Merge "MGF1 Digest: Add separate setter" into main am: 32b5cf1bac am: 3b167877a1 am: 51b238eac7 am: 60bef2a8e0
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2733037
Change-Id: I0204c03165d15a42fc16140896f4a22f8932c011
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/core/api/current.txt b/core/api/current.txt
index 02be772..5e03a81 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -39528,6 +39528,7 @@
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();
@@ -39535,6 +39536,7 @@
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();
@@ -39566,6 +39568,7 @@
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);
@@ -39670,12 +39673,14 @@
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();
@@ -39697,6 +39702,7 @@
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 3956241..96c257b 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.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 @@
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;
@@ -345,6 +350,7 @@
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,
@@ -404,6 +410,9 @@
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));
@@ -566,7 +575,7 @@
/**
* 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.
*
@@ -594,6 +603,40 @@
}
/**
+ * 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
@@ -913,6 +956,8 @@
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;
@@ -983,6 +1028,9 @@
if (sourceSpec.isDigestsSpecified()) {
mDigests = sourceSpec.getDigests();
}
+ if (sourceSpec.isMgf1DigestsSpecified()) {
+ mMgf1Digests = sourceSpec.getMgf1Digests();
+ }
mEncryptionPaddings = sourceSpec.getEncryptionPaddings();
mSignaturePaddings = sourceSpec.getSignaturePaddings();
mBlockModes = sourceSpec.getBlockModes();
@@ -1230,6 +1278,30 @@
}
/**
+ * 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
@@ -1782,6 +1854,7 @@
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 5ab21bc..c1e3bab 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.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 @@
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;
@@ -247,6 +252,7 @@
@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,
@@ -271,6 +277,7 @@
mSignaturePaddings =
ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+ mMgf1Digests = mgf1Digests;
mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
mUserAuthenticationRequired = userAuthenticationRequired;
@@ -381,6 +388,40 @@
}
/**
+ * 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.
@@ -588,6 +629,8 @@
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;
@@ -739,6 +782,30 @@
}
/**
+ * 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.
@@ -1141,6 +1208,7 @@
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 9356eb8..ceba04e 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -23,7 +23,11 @@
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 @@
} 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());
@@ -153,6 +162,7 @@
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();
@@ -191,6 +201,7 @@
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 9ac0f6d..101a10e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -24,6 +24,7 @@
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 @@
*/
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 1398da3..ed4b485 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -188,6 +188,7 @@
private int[] mKeymasterEncryptionPaddings;
private int[] mKeymasterSignaturePaddings;
private int[] mKeymasterDigests;
+ private int[] mKeymasterMgf1Digests;
private Long mRSAPublicExponent;
@@ -323,6 +324,21 @@
} 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 @@
mKeymasterEncryptionPaddings = null;
mKeymasterSignaturePaddings = null;
mKeymasterDigests = null;
+ mKeymasterMgf1Digests = null;
mKeySizeBits = 0;
mSpec = null;
mRSAPublicExponent = null;
@@ -831,24 +848,11 @@
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 273dff1..ddbd93e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -526,25 +526,22 @@
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 2ae61ab..d4e2dbc 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 @@
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 @@
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 ddbb1d8..da5e8bf 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 @@
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));
+ }
}