diff options
author | 2023-08-30 12:12:29 +0100 | |
---|---|---|
committer | 2023-09-22 12:09:58 +0000 | |
commit | d61656c5002d58816e2b6e1e3dcd46dce1551f35 (patch) | |
tree | 8c3cbcadb4ef18a1571af3a97c19b39ab0f1e77b | |
parent | 5f42c65ee497d03ebe7dbc89deb0c88cb413c81f (diff) |
MGF1 Digest: Add separate setter
Add a separate setter for the digests used by the MGF1 mask generation
function (for RSA OAEP operations).
Previously the MGF1 digests were specified according to the primary
digests specification, which is not accurate enough.
With the new setter:
* If the user does not explicitly specify MGF1 digests, then the
default (SHA-1) will be specified in the tag passed to Keystore.
* If the user does explicitly specify MGF1 digests, only those
digests will be specified in the tag passed to Keystore.
The SHA-1 digest will not be added.
Bug: 284140060
Test: atest android.security.keystore.KeyGenParameterSpecTest android.security.ParcelableKeyGenParameterSpecTest
Test: atest CtsKeystoreTestCases:android.keystore.cts.CipherTest#testKatBasicWithDifferentProviders
Change-Id: I1521e9b4399ece33c2d17b79133543d490d3b377
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)); + } } |