diff options
9 files changed, 258 insertions, 28 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index cba935fadb55..d399e349de0c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -37935,6 +37935,7 @@ package android.security.keystore { method @Nullable public java.util.Date getKeyValidityStart(); method @NonNull public String getKeystoreAlias(); method public int getMaxUsageCount(); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests(); method public int getPurposes(); method @NonNull public String[] getSignaturePaddings(); method public int getUserAuthenticationType(); @@ -37942,6 +37943,7 @@ package android.security.keystore { method public boolean isDevicePropertiesAttestationIncluded(); method @NonNull public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified(); method public boolean isRandomizedEncryptionRequired(); method public boolean isStrongBoxBacked(); method public boolean isUnlockedDeviceRequired(); @@ -37973,6 +37975,7 @@ package android.security.keystore { method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@Nullable java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean); @@ -38077,12 +38080,14 @@ package android.security.keystore { method @Nullable public java.util.Date getKeyValidityForOriginationEnd(); method @Nullable public java.util.Date getKeyValidityStart(); method public int getMaxUsageCount(); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests(); method public int getPurposes(); method @NonNull public String[] getSignaturePaddings(); method public int getUserAuthenticationType(); method public int getUserAuthenticationValidityDurationSeconds(); method public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified(); method public boolean isRandomizedEncryptionRequired(); method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); @@ -38104,6 +38109,7 @@ package android.security.keystore { method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date); method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date); method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int); + method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...); method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean); method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...); method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean); diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 9eed904d52be..6db1832d0508 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -16,6 +16,7 @@ package android.security.keystore; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,7 +35,10 @@ import java.security.KeyPairGenerator; import java.security.Signature; import java.security.cert.Certificate; import java.security.spec.AlgorithmParameterSpec; +import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.Set; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; @@ -300,6 +304,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final Date mKeyValidityForConsumptionEnd; private final @KeyProperties.PurposeEnum int mPurposes; private final @KeyProperties.DigestEnum String[] mDigests; + private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests; private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; private final @KeyProperties.BlockModeEnum String[] mBlockModes; @@ -343,6 +348,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu Date keyValidityForConsumptionEnd, @KeyProperties.PurposeEnum int purposes, @KeyProperties.DigestEnum String[] digests, + @KeyProperties.DigestEnum Set<String> mgf1Digests, @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings, @KeyProperties.SignaturePaddingEnum String[] signaturePaddings, @KeyProperties.BlockModeEnum String[] blockModes, @@ -401,6 +407,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); mPurposes = purposes; mDigests = ArrayUtils.cloneIfNotEmpty(digests); + // No need to copy the input parameter because the Builder class passes in an immutable + // collection. + mMgf1Digests = mgf1Digests != null ? mgf1Digests : Collections.emptySet(); mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); @@ -562,7 +571,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu /** * Returns the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384} with which the - * key can be used or {@code null} if not specified. + * key can be used. * * <p>See {@link KeyProperties}.{@code DIGEST} constants. * @@ -590,6 +599,40 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Returns the set of digests that can be used by the MGF1 mask generation function + * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP} + * scheme. + * If not explicitly specified during key generation, the default {@code SHA-1} digest is + * used and may be specified when using the key. + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. + * + * @throws IllegalStateException if this set has not been specified. + * + * @see #isMgf1DigestsSpecified() + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public @KeyProperties.DigestEnum Set<String> getMgf1Digests() { + if (mMgf1Digests.isEmpty()) { + throw new IllegalStateException("Mask generation function (MGF) not specified"); + } + return new HashSet(mMgf1Digests); + } + + /** + * Returns {@code true} if the set of digests for the MGF1 mask generation function, + * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme. + * + * @see #getMgf1Digests() + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public boolean isMgf1DigestsSpecified() { + return !mMgf1Digests.isEmpty(); + } + + /** * Returns the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OEAPPadding}, * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when * encrypting/decrypting. Attempts to use the key with any other padding scheme will be @@ -899,6 +942,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private Date mKeyValidityForOriginationEnd; private Date mKeyValidityForConsumptionEnd; private @KeyProperties.DigestEnum String[] mDigests; + private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests = + Collections.emptySet(); private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; private @KeyProperties.BlockModeEnum String[] mBlockModes; @@ -968,6 +1013,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu if (sourceSpec.isDigestsSpecified()) { mDigests = sourceSpec.getDigests(); } + if (sourceSpec.isMgf1DigestsSpecified()) { + mMgf1Digests = sourceSpec.getMgf1Digests(); + } mEncryptionPaddings = sourceSpec.getEncryptionPaddings(); mSignaturePaddings = sourceSpec.getSignaturePaddings(); mBlockModes = sourceSpec.getBlockModes(); @@ -1214,6 +1262,30 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be + * used by the mask generation function MGF1 (which is used for certain operations with + * the key). Attempts to use the key with any other digest for the mask generation + * function will be rejected. + * + * <p>This can only be specified for signing/verification keys and RSA encryption/decryption + * keys used with RSA OAEP padding scheme because these operations involve a mask generation + * function (MGF1) with a digest. + * The default digest for MGF1 is {@code SHA-1}, which will be specified during key creation + * time if no digests have been explicitly provided. + * When using the key, the caller may not specify any digests that were not provided during + * key creation time. The caller may specify the default digest, {@code SHA-1}, if no + * digests were explicitly provided during key creation (but it is not necessary to do so). + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) { + mMgf1Digests = Set.of(mgf1Digests); + return this; + } + + /** * Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OAEPPadding}, * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when * encrypting/decrypting. Attempts to use the key with any other padding scheme will be @@ -1745,6 +1817,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mKeyValidityForConsumptionEnd, mPurposes, mDigests, + mMgf1Digests, mEncryptionPaddings, mSignaturePaddings, mBlockModes, diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index c14c3c534cf4..1262a5303f38 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -16,6 +16,7 @@ package android.security.keystore; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -30,7 +31,10 @@ import java.security.Key; import java.security.KeyStore.ProtectionParameter; import java.security.Signature; import java.security.cert.Certificate; +import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.Set; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -223,6 +227,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; private final @KeyProperties.DigestEnum String[] mDigests; + private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests; private final @KeyProperties.BlockModeEnum String[] mBlockModes; private final boolean mRandomizedEncryptionRequired; private final boolean mUserAuthenticationRequired; @@ -246,6 +251,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings, @KeyProperties.SignaturePaddingEnum String[] signaturePaddings, @KeyProperties.DigestEnum String[] digests, + @KeyProperties.DigestEnum Set<String> mgf1Digests, @KeyProperties.BlockModeEnum String[] blockModes, boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, @@ -269,6 +275,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); mDigests = ArrayUtils.cloneIfNotEmpty(digests); + mMgf1Digests = mgf1Digests; mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticationRequired = userAuthenticationRequired; @@ -378,6 +385,40 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Returns the set of digests that can be used by the MGF1 mask generation function + * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP} + * scheme. + * If not explicitly specified during key generation, the default {@code SHA-1} digest is + * used and may be specified. + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. + * + * @throws IllegalStateException if this set has not been specified. + * + * @see #isMgf1DigestsSpecified() + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public @KeyProperties.DigestEnum Set<String> getMgf1Digests() { + if (mMgf1Digests.isEmpty()) { + throw new IllegalStateException("Mask generation function (MGF) not specified"); + } + return new HashSet(mMgf1Digests); + } + + /** + * Returns {@code true} if the set of digests for the MGF1 mask generation function, + * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme. + * + * @see #getMgf1Digests() + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public boolean isMgf1DigestsSpecified() { + return !mMgf1Digests.isEmpty(); + } + + /** * Gets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be used * when encrypting/decrypting. Attempts to use the key with any other block modes will be * rejected. @@ -574,6 +615,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; private @KeyProperties.DigestEnum String[] mDigests; + private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests = + Collections.emptySet(); private @KeyProperties.BlockModeEnum String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; private boolean mUserAuthenticationRequired; @@ -724,6 +767,30 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be + * used by the mask generation function MGF1 (which is used for certain operations with + * the key). Attempts to use the key with any other digest for the mask generation + * function will be rejected. + * + * <p>This can only be specified for signing/verification keys and RSA encryption/decryption + * keys used with RSA OAEP padding scheme because these operations involve a mask generation + * function (MGF1) with a digest. + * The default digest for MGF1 is {@code SHA-1}, which will be specified during key import + * time if no digests have been explicitly provided. + * When using the key, the caller may not specify any digests that were not provided during + * key import time. The caller may specify the default digest, {@code SHA-1}, if no + * digests were explicitly provided during key import (but it is not necessary to do so). + * + * <p>See {@link KeyProperties}.{@code DIGEST} constants. + */ + @NonNull + @FlaggedApi("MGF1_DIGEST_SETTER") + public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) { + mMgf1Digests = Set.of(mgf1Digests); + return this; + } + + /** * Sets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be * used when encrypting/decrypting. Attempts to use the key with any other block modes will * be rejected. @@ -1111,6 +1178,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mEncryptionPaddings, mSignaturePaddings, mDigests, + mMgf1Digests, mBlockModes, mRandomizedEncryptionRequired, mUserAuthenticationRequired, diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index a6e33664f2b1..30b3b5ce0ead 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -23,7 +23,11 @@ import java.math.BigInteger; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; import java.security.spec.RSAKeyGenParameterSpec; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.List; +import java.util.Set; import javax.security.auth.x500.X500Principal; @@ -91,6 +95,11 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { } else { out.writeStringArray(null); } + if (mSpec.isMgf1DigestsSpecified()) { + out.writeStringList(List.copyOf(mSpec.getMgf1Digests())); + } else { + out.writeStringList(null); + } out.writeStringArray(mSpec.getEncryptionPaddings()); out.writeStringArray(mSpec.getSignaturePaddings()); out.writeStringArray(mSpec.getBlockModes()); @@ -152,6 +161,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { final Date keyValidityForOriginationEnd = readDateOrNull(in); final Date keyValidityForConsumptionEnd = readDateOrNull(in); final String[] digests = in.createStringArray(); + final ArrayList<String> mgf1Digests = in.createStringArrayList(); final String[] encryptionPaddings = in.createStringArray(); final String[] signaturePaddings = in.createStringArray(); final String[] blockModes = in.createStringArray(); @@ -189,6 +199,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { keyValidityForConsumptionEnd, purposes, digests, + mgf1Digests != null ? Set.copyOf(mgf1Digests) : Collections.emptySet(), encryptionPaddings, signaturePaddings, blockModes, diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java index 9ac0f6d304f6..101a10e3d312 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java @@ -24,6 +24,7 @@ import android.os.StrictMode; import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; +import android.security.keystore.KeyProperties; import android.security.keystore.KeyStoreCryptoOperation; import android.system.keystore2.Authorization; @@ -71,7 +72,7 @@ import javax.crypto.spec.SecretKeySpec; */ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation { private static final String TAG = "AndroidKeyStoreCipherSpiBase"; - public static final String DEFAULT_MGF1_DIGEST = "SHA-1"; + public static final String DEFAULT_MGF1_DIGEST = KeyProperties.DIGEST_SHA1; // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after // doFinal finishes. diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 1398da3f5ef2..ed4b485f3927 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -188,6 +188,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private int[] mKeymasterEncryptionPaddings; private int[] mKeymasterSignaturePaddings; private int[] mKeymasterDigests; + private int[] mKeymasterMgf1Digests; private Long mRSAPublicExponent; @@ -323,6 +324,21 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } else { mKeymasterDigests = EmptyArray.INT; } + if (spec.isMgf1DigestsSpecified()) { + // User-specified digests: Add all of them and do _not_ add the SHA-1 + // digest by default (stick to what the user provided). + Set<String> mgfDigests = spec.getMgf1Digests(); + mKeymasterMgf1Digests = new int[mgfDigests.size()]; + int offset = 0; + for (String digest : mgfDigests) { + mKeymasterMgf1Digests[offset] = KeyProperties.Digest.toKeymaster(digest); + offset++; + } + } else { + // No user-specified digests: Add the SHA-1 default. + mKeymasterMgf1Digests = new int[]{ + KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)}; + } // Check that user authentication related parameters are acceptable. This method // will throw an IllegalStateException if there are issues (e.g., secure lock screen @@ -544,6 +560,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mKeymasterEncryptionPaddings = null; mKeymasterSignaturePaddings = null; mKeymasterDigests = null; + mKeymasterMgf1Digests = null; mKeySizeBits = 0; mSpec = null; mRSAPublicExponent = null; @@ -831,24 +848,11 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato KeymasterDefs.KM_TAG_PADDING, padding )); if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) { - final boolean[] hasDefaultMgf1DigestBeenAdded = {false}; - ArrayUtils.forEach(mKeymasterDigests, (digest) -> { + ArrayUtils.forEach(mKeymasterMgf1Digests, (mgf1Digest) -> { params.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest + KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mgf1Digest )); - hasDefaultMgf1DigestBeenAdded[0] |= - digest.equals(KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)); }); - /* Because of default MGF1 digest is SHA-1. It has to be added in Key - * characteristics. Otherwise, crypto operations will fail with Incompatible - * MGF1 digest. - */ - if (!hasDefaultMgf1DigestBeenAdded[0]) { - params.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, - KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST) - )); - } } }); ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index eef817902ade..d3d970153cf3 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -526,25 +526,22 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { padding )); if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) { - if (spec.isDigestsSpecified()) { - boolean hasDefaultMgf1DigestBeenAdded = false; - for (String digest : spec.getDigests()) { + if (spec.isMgf1DigestsSpecified()) { + for (String mgf1Digest : spec.getMgf1Digests()) { importArgs.add(KeyStore2ParameterUtils.makeEnum( KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, - KeyProperties.Digest.toKeymaster(digest) + KeyProperties.Digest.toKeymaster(mgf1Digest) )); - hasDefaultMgf1DigestBeenAdded |= digest.equals(DEFAULT_MGF1_DIGEST); } + } else { /* Because of default MGF1 digest is SHA-1. It has to be added in Key * characteristics. Otherwise, crypto operations will fail with Incompatible * MGF1 digest. */ - if (!hasDefaultMgf1DigestBeenAdded) { - importArgs.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, - KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST) - )); - } + importArgs.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, + KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST) + )); } } } diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java index 2ae61ab3b38d..d4e2dbc81509 100644 --- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java @@ -17,6 +17,7 @@ package android.security; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -101,6 +102,7 @@ public final class ParcelableKeyGenParameterSpecTest { assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END)); assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END)); assertThat(spec.getDigests(), is(new String[] {DIGEST})); + assertThat(spec.isMgf1DigestsSpecified(), is(false)); assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING})); assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING})); assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE})); @@ -189,4 +191,19 @@ public final class ParcelableKeyGenParameterSpecTest { ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec(); assertEquals(parcelSpec.getName(), ecSpec.getName()); } + + @Test + public void testParcelingMgf1Digests() { + String[] mgf1Digests = + new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256}; + + ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec( + new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) + .setMgf1Digests(mgf1Digests) + .build()); + Parcel parcel = parcelForReading(spec); + KeyGenParameterSpec fromParcel = + ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec(); + assertArrayEquals(fromParcel.getMgf1Digests().toArray(), mgf1Digests); + } } diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java index ddbb1d8c097c..da5e8bf84191 100644 --- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java @@ -16,9 +16,12 @@ package android.security.keystore; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; import android.security.ParcelableKeyGenParameterSpecTest; @@ -61,4 +64,54 @@ public final class KeyGenParameterSpecTest { assertEquals(copiedSpec.getAttestationChallenge(), null); } + + @Test + public void testMgf1DigestsNotSpecifiedByDefault() { + KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec(); + assertThat(spec.isMgf1DigestsSpecified(), is(false)); + assertThrows(IllegalStateException.class, () -> { + spec.getMgf1Digests(); + }); + } + + @Test + public void testMgf1DigestsCanBeSpecified() { + String[] mgf1Digests = + new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256}; + KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) + .setMgf1Digests(mgf1Digests) + .build(); + assertThat(spec.isMgf1DigestsSpecified(), is(true)); + assertThat(spec.getMgf1Digests(), containsInAnyOrder(mgf1Digests)); + + KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build(); + assertThat(copiedSpec.isMgf1DigestsSpecified(), is(true)); + assertThat(copiedSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests)); + } + + @Test + public void testMgf1DigestsAreNotModified() { + String[] mgf1Digests = + new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256}; + KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) + .setMgf1Digests(mgf1Digests); + + KeyGenParameterSpec firstSpec = builder.build(); + assertArrayEquals(mgf1Digests, firstSpec.getMgf1Digests().toArray()); + + String[] otherDigests = new String[] {KeyProperties.DIGEST_SHA224}; + KeyGenParameterSpec secondSpec = builder.setMgf1Digests(otherDigests).build(); + assertThat(secondSpec.getMgf1Digests(), containsInAnyOrder(otherDigests)); + + // Now check that the first spec created hasn't changed. + assertThat(firstSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests)); + } + + @Test + public void testEmptyMgf1DigestsCanBeSet() { + KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) + .setMgf1Digests(new String[] {}).build(); + + assertThat(spec.isMgf1DigestsSpecified(), is(false)); + } } |