diff options
Diffstat (limited to 'keystore/java/android/security')
16 files changed, 881 insertions, 498 deletions
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java index 3b25ba6..3f29c6a 100644 --- a/keystore/java/android/security/AndroidKeyPairGenerator.java +++ b/keystore/java/android/security/AndroidKeyPairGenerator.java @@ -54,13 +54,13 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { public static class RSA extends AndroidKeyPairGenerator { public RSA() { - super("RSA"); + super(KeyStoreKeyProperties.Algorithm.RSA); } } public static class EC extends AndroidKeyPairGenerator { public EC() { - super("EC"); + super(KeyStoreKeyProperties.Algorithm.EC); } } @@ -83,15 +83,15 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { private android.security.KeyStore mKeyStore; private KeyPairGeneratorSpec mSpec; - private String mKeyAlgorithm; + private @KeyStoreKeyProperties.AlgorithmEnum String mKeyAlgorithm; private int mKeyType; private int mKeySize; - protected AndroidKeyPairGenerator(String algorithm) { + protected AndroidKeyPairGenerator(@KeyStoreKeyProperties.AlgorithmEnum String algorithm) { mAlgorithm = algorithm; } - public String getAlgorithm() { + public @KeyStoreKeyProperties.AlgorithmEnum String getAlgorithm() { return mAlgorithm; } @@ -197,7 +197,7 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { return certGen.generate(privateKey); } - private String getKeyAlgorithm(KeyPairGeneratorSpec spec) { + private @KeyStoreKeyProperties.AlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) { String result = spec.getKeyType(); if (result != null) { return result; @@ -248,10 +248,11 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { } } - private static String getDefaultSignatureAlgorithmForKeyAlgorithm(String algorithm) { - if ("RSA".equalsIgnoreCase(algorithm)) { + private static String getDefaultSignatureAlgorithmForKeyAlgorithm( + @KeyStoreKeyProperties.AlgorithmEnum String algorithm) { + if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(algorithm)) { return "sha256WithRSA"; - } else if ("EC".equalsIgnoreCase(algorithm)) { + } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(algorithm)) { return "sha256WithECDSA"; } else { throw new IllegalArgumentException("Unsupported key type " + algorithm); @@ -287,7 +288,7 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { } KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params; - String keyAlgorithm = getKeyAlgorithm(spec); + @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithm = getKeyAlgorithm(spec); int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm); if (keyType == -1) { throw new InvalidAlgorithmParameterException( diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index 72cb062..e82ff6a 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -128,10 +128,11 @@ public class AndroidKeyStore extends KeyStoreSpi { keymasterDigest = keymasterDigests.get(0); } - String keyAlgorithmString; + @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithmString; try { - keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm( - keymasterAlgorithm, keymasterDigest); + keyAlgorithmString = + KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm( + keymasterAlgorithm, keymasterDigest); } catch (IllegalArgumentException e) { throw (UnrecoverableKeyException) new UnrecoverableKeyException("Unsupported secret key type").initCause(e); @@ -451,10 +452,10 @@ public class AndroidKeyStore extends KeyStoreSpi { int keymasterAlgorithm; int keymasterDigest; try { - keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm( + keymasterAlgorithm = KeyStoreKeyProperties.Algorithm.toKeymasterSecretKeyAlgorithm( keyAlgorithmString); keymasterDigest = - KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString); + KeyStoreKeyProperties.Algorithm.toKeymasterDigest(keyAlgorithmString); } catch (IllegalArgumentException e) { throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString); } @@ -465,8 +466,7 @@ public class AndroidKeyStore extends KeyStoreSpi { int[] keymasterDigests; if (params.isDigestsSpecified()) { // Digest(s) specified in parameters - keymasterDigests = - KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests()); + keymasterDigests = KeyStoreKeyProperties.Digest.allToKeymaster(params.getDigests()); if (keymasterDigest != -1) { // Digest also specified in the JCA key algorithm name. if (!com.android.internal.util.ArrayUtils.contains( @@ -494,8 +494,8 @@ public class AndroidKeyStore extends KeyStoreSpi { } @KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes(); - int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes( - params.getBlockModes()); + int[] keymasterBlockModes = + KeyStoreKeyProperties.BlockMode.allToKeymaster(params.getBlockModes()); if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) && (params.isRandomizedEncryptionRequired())) { for (int keymasterBlockMode : keymasterBlockModes) { @@ -503,8 +503,7 @@ public class AndroidKeyStore extends KeyStoreSpi { throw new KeyStoreException( "Randomized encryption (IND-CPA) required but may be violated by block" + " mode: " - + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode( - keymasterBlockMode) + + KeyStoreKeyProperties.BlockMode.fromKeymaster(keymasterBlockMode) + ". See KeyStoreParameter documentation."); } } @@ -513,11 +512,11 @@ public class AndroidKeyStore extends KeyStoreSpi { args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); } args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); - int[] keymasterPaddings = ArrayUtils.concat( - KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings( - params.getEncryptionPaddings()), - KeymasterUtils.getKeymasterPaddingsFromJcaSignaturePaddings( - params.getSignaturePaddings())); + if (params.getSignaturePaddings().length > 0) { + throw new KeyStoreException("Signature paddings not supported for symmetric keys"); + } + int[] keymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster( + params.getEncryptionPaddings()); args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); KeymasterUtils.addUserAuthArgs(args, params.getContext(), diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index e9c24dd..8e27dc3 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -262,7 +262,8 @@ public final class KeyChain { * unavailable. */ public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response, - String[] keyTypes, Principal[] issuers, String host, int port, String alias) { + @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers, + String host, int port, String alias) { choosePrivateKeyAlias(activity, response, keyTypes, issuers, host, port, null, alias); } @@ -306,9 +307,8 @@ public final class KeyChain { * unavailable. */ public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response, - String[] keyTypes, Principal[] issuers, - String host, int port, String url, - String alias) { + @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers, + String host, int port, String url, String alias) { /* * TODO currently keyTypes, issuers are unused. They are meant * to follow the semantics and purpose of X509KeyManager @@ -431,9 +431,11 @@ public final class KeyChain { * specific {@code PrivateKey} type indicated by {@code algorithm} (e.g., * "RSA"). */ - public static boolean isKeyAlgorithmSupported(String algorithm) { + public static boolean isKeyAlgorithmSupported( + @KeyStoreKeyProperties.AlgorithmEnum String algorithm) { final String algUpper = algorithm.toUpperCase(Locale.US); - return "EC".equals(algUpper) || "RSA".equals(algUpper); + return KeyStoreKeyProperties.Algorithm.EC.equals(algUpper) + || KeyStoreKeyProperties.Algorithm.RSA.equals(algUpper); } /** @@ -443,7 +445,8 @@ public final class KeyChain { * hardware support that can be used to bind keys to the device in a way * that makes it non-exportable. */ - public static boolean isBoundKeyAlgorithm(String algorithm) { + public static boolean isBoundKeyAlgorithm( + @KeyStoreKeyProperties.AlgorithmEnum String algorithm) { if (!isKeyAlgorithmSupported(algorithm)) { return false; } diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 8f135a6..729646d 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -48,8 +48,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; private final @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private final String[] mEncryptionPaddings; - private final String[] mBlockModes; + private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; + private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes; private final boolean mRandomizedEncryptionRequired; private final boolean mUserAuthenticationRequired; private final int mUserAuthenticationValidityDurationSeconds; @@ -63,8 +63,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, @KeyStoreKeyProperties.PurposeEnum int purposes, - String[] encryptionPaddings, - String[] blockModes, + @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings, + @KeyStoreKeyProperties.BlockModeEnum String[] blockModes, boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds) { @@ -160,14 +160,14 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. */ - public String[] getEncryptionPaddings() { + public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() { return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** * Gets the set of block modes with which the key can be used. */ - public String[] getBlockModes() { + public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() { return ArrayUtils.cloneIfNotEmpty(mBlockModes); } @@ -195,10 +195,12 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the duration of time (seconds) for which this key can be used after the user is - * successfully authenticated. + * successfully authenticated. This has effect only if user authentication is required. * - * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication - * is required for every use of the key. + * @return duration in seconds or {@code -1} if authentication is required for every use of the + * key. + * + * @see #isUserAuthenticationRequired() */ public int getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; @@ -220,8 +222,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private Date mKeyValidityForOriginationEnd; private Date mKeyValidityForConsumptionEnd; private @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private String[] mEncryptionPaddings; - private String[] mBlockModes; + private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; + private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; private boolean mUserAuthenticationRequired; private int mUserAuthenticationValidityDurationSeconds = -1; @@ -346,7 +348,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * <p>This must be specified for keys which are used for encryption/decryption. */ - public Builder setEncryptionPaddings(String... paddings) { + public Builder setEncryptionPaddings( + @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) { mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } @@ -357,7 +360,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * <p>This must be specified for encryption/decryption keys. */ - public Builder setBlockModes(String... blockModes) { + public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) { mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; } @@ -425,7 +428,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * <p>By default, the user needs to authenticate for every use of the key. * - * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for + * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for * every use of the key. * * @see #setUserAuthenticationRequired(boolean) diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index d6d3789..25c61fd 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -85,13 +85,13 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private final @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private final String[] mDigests; + private final @KeyStoreKeyProperties.DigestEnum String[] mDigests; - private final String[] mEncryptionPaddings; + private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; - private final String[] mSignaturePaddings; + private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; - private final String[] mBlockModes; + private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes; private final boolean mRandomizedEncryptionRequired; @@ -138,10 +138,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, @KeyStoreKeyProperties.PurposeEnum int purposes, - String[] digests, - String[] encryptionPaddings, - String[] signaturePaddings, - String[] blockModes, + @KeyStoreKeyProperties.DigestEnum String[] digests, + @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings, + @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings, + @KeyStoreKeyProperties.BlockModeEnum String[] blockModes, boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds) { @@ -246,7 +246,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Returns the key type (e.g., "EC", "RSA") specified by this parameter. */ - public String getKeyType() { + public @KeyStoreKeyProperties.AlgorithmEnum String getKeyType() { return mKeyType; } @@ -352,28 +352,28 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of digest algorithms with which the key can be used. */ - public String[] getDigests() { + public @KeyStoreKeyProperties.DigestEnum String[] getDigests() { return ArrayUtils.cloneIfNotEmpty(mDigests); } /** * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. */ - public String[] getEncryptionPaddings() { + public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() { return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** * Gets the set of padding schemes with which the key can be used when signing/verifying. */ - public String[] getSignaturePaddings() { + public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() { return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } /** * Gets the set of block modes with which the key can be used. */ - public String[] getBlockModes() { + public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() { return ArrayUtils.cloneIfNotEmpty(mBlockModes); } @@ -403,14 +403,14 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** - * Gets the duration of time (seconds) for which the private key can be used after the user - * is successfully authenticated. + * Gets the duration of time (seconds) for which this key can be used after the user is + * successfully authenticated. This has effect only if user authentication is required. * * <p>This restriction applies only to private key operations. Public key operations are not * restricted. * - * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication - * is required for every use of the key. + * @return duration in seconds or {@code -1} if authentication is required for every use of the + * key. * * @see #isUserAuthenticationRequired() */ @@ -468,13 +468,13 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private String[] mDigests; + private @KeyStoreKeyProperties.DigestEnum String[] mDigests; - private String[] mEncryptionPaddings; + private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; - private String[] mSignaturePaddings; + private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; - private String[] mBlockModes; + private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; @@ -511,7 +511,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the key type (e.g., EC, RSA) of the keypair to be created. */ - public Builder setKeyType(String keyType) throws NoSuchAlgorithmException { + public Builder setKeyType(@KeyStoreKeyProperties.AlgorithmEnum String keyType) + throws NoSuchAlgorithmException { if (keyType == null) { throw new NullPointerException("keyType == null"); } else { @@ -628,6 +629,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p>By default, the key is valid at any instant. * + * <p><b>NOTE: This has currently no effect. + * * @see #setKeyValidityEnd(Date) */ public Builder setKeyValidityStart(Date startDate) { @@ -640,6 +643,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p>By default, the key is valid at any instant. * + * <p><b>NOTE: This has currently no effect. + * * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) @@ -655,6 +660,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p>By default, the key is valid at any instant. * + * <p><b>NOTE: This has currently no effect. + * * @see #setKeyValidityForConsumptionEnd(Date) */ public Builder setKeyValidityForOriginationEnd(Date endDate) { @@ -668,6 +675,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p>By default, the key is valid at any instant. * + * <p><b>NOTE: This has currently no effect. + * * @see #setKeyValidityForOriginationEnd(Date) */ public Builder setKeyValidityForConsumptionEnd(Date endDate) { @@ -679,6 +688,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Sets the set of purposes for which the key can be used. * * <p>This must be specified for all keys. There is no default. + * + * <p><b>NOTE: This has currently no effect. */ public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; @@ -690,8 +701,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * to use the key with any other digest will be rejected. * * <p>This must be specified for keys which are used for signing/verification. + * + * <p><b>NOTE: This has currently no effect. */ - public Builder setDigests(String... digests) { + public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) { mDigests = ArrayUtils.cloneIfNotEmpty(digests); return this; } @@ -702,8 +715,11 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * rejected. * * <p>This must be specified for keys which are used for encryption/decryption. + * + * <p><b>NOTE: This has currently no effect. */ - public Builder setEncryptionPaddings(String... paddings) { + public Builder setEncryptionPaddings( + @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) { mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } @@ -714,8 +730,11 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * rejected. * * <p>This must be specified for RSA keys which are used for signing/verification. + * + * <p><b>NOTE: This has currently no effect. */ - public Builder setSignaturePaddings(String... paddings) { + public Builder setSignaturePaddings( + @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) { mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } @@ -725,8 +744,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Attempts to use the key with any other block modes will be rejected. * * <p>This must be specified for encryption/decryption keys. + * + * <p><b>NOTE: This has currently no effect. */ - public Builder setBlockModes(String... blockModes) { + public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) { mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; } @@ -750,6 +771,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * <li>If you are using RSA encryption without padding, consider switching to padding * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li> * </ul> + * + * <p><b>NOTE: This has currently no effect. */ public Builder setRandomizedEncryptionRequired(boolean required) { mRandomizedEncryptionRequired = required; @@ -772,6 +795,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * <p>This restriction applies only to private key operations. Public key operations are not * restricted. * + * <p><b>NOTE: This has currently no effect. + * * @see #setUserAuthenticationValidityDurationSeconds(int) */ public Builder setUserAuthenticationRequired(boolean required) { @@ -788,7 +813,9 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * <p>This restriction applies only to private key operations. Public key operations are not * restricted. * - * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for + * <p><b>NOTE: This has currently no effect. + * + * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for * every use of the key. * * @see #setUserAuthenticationRequired(boolean) diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 82d328b..304d277 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -115,10 +115,10 @@ public class KeyStore { return mToken; } - static int getKeyTypeForAlgorithm(String keyType) { - if ("RSA".equalsIgnoreCase(keyType)) { + static int getKeyTypeForAlgorithm(@KeyStoreKeyProperties.AlgorithmEnum String keyType) { + if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(keyType)) { return NativeConstants.EVP_PKEY_RSA; - } else if ("EC".equalsIgnoreCase(keyType)) { + } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(keyType)) { return NativeConstants.EVP_PKEY_EC; } else { return -1; diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java index 094aa75..bd601bc 100644 --- a/keystore/java/android/security/KeyStoreCipherSpi.java +++ b/keystore/java/android/security/KeyStoreCipherSpi.java @@ -27,6 +27,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidParameterSpecException; @@ -315,15 +316,15 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } else if (e instanceof InvalidAlgorithmParameterException) { throw (InvalidAlgorithmParameterException) e; } else { - throw new RuntimeException("Unexpected exception type", e); + throw new ProviderException("Unexpected exception type", e); } } if (mOperationToken == null) { - throw new IllegalStateException("Keystore returned null operation token"); + throw new ProviderException("Keystore returned null operation token"); } if (mOperationHandle == 0) { - throw new IllegalStateException("Keystore returned invalid operation handle"); + throw new ProviderException("Keystore returned invalid operation handle"); } loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs); @@ -494,13 +495,14 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } if ((mIv != null) && (mIv.length > 0)) { try { - AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); + AlgorithmParameters params = + AlgorithmParameters.getInstance(KeyStoreKeyProperties.Algorithm.AES); params.init(new IvParameterSpec(mIv)); return params; } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Failed to obtain AES AlgorithmParameters", e); + throw new ProviderException("Failed to obtain AES AlgorithmParameters", e); } catch (InvalidParameterSpecException e) { - throw new RuntimeException( + throw new ProviderException( "Failed to initialize AES AlgorithmParameters with an IV", e); } } @@ -633,10 +635,9 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry if ((mIv == null) && (mEncrypting)) { // IV was not provided by the caller and thus will be generated by keymaster. // Mix in some additional entropy from the provided SecureRandom. - if (mRng != null) { - mAdditionalEntropyForBegin = new byte[mBlockSizeBytes]; - mRng.nextBytes(mAdditionalEntropyForBegin); - } + mAdditionalEntropyForBegin = + KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( + mRng, mBlockSizeBytes); } } } @@ -668,11 +669,11 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry if (mIv == null) { mIv = returnedIv; } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { - throw new IllegalStateException("IV in use differs from provided IV"); + throw new ProviderException("IV in use differs from provided IV"); } } else { if (returnedIv != null) { - throw new IllegalStateException( + throw new ProviderException( "IV in use despite IV not being used by this transformation"); } } diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java index 1aa3aec..885f1f7 100644 --- a/keystore/java/android/security/KeyStoreConnectException.java +++ b/keystore/java/android/security/KeyStoreConnectException.java @@ -16,12 +16,14 @@ package android.security; +import java.security.ProviderException; + /** * Indicates a communications error with keystore service. * * @hide */ -public class KeyStoreConnectException extends IllegalStateException { +public class KeyStoreConnectException extends ProviderException { public KeyStoreConnectException() { super("Failed to communicate with keystore service"); } diff --git a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java index e5933ad..311278b 100644 --- a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java +++ b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java @@ -21,6 +21,7 @@ import android.security.keymaster.KeymasterDefs; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.SecureRandom; /** * Assorted utility methods for implementing crypto operations on top of KeyStore. @@ -28,6 +29,9 @@ import java.security.InvalidKeyException; * @hide */ abstract class KeyStoreCryptoOperationUtils { + + private static volatile SecureRandom sRng; + private KeyStoreCryptoOperationUtils() {} /** @@ -81,4 +85,28 @@ abstract class KeyStoreCryptoOperationUtils { // General cases return getInvalidKeyExceptionForInit(keyStore, key, beginOpResultCode); } + + /** + * Returns the requested number of random bytes to mix into keystore/keymaster RNG. + * + * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default + * RNG. + */ + static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) { + if (rng == null) { + rng = getRng(); + } + byte[] result = new byte[sizeBytes]; + rng.nextBytes(result); + return result; + } + + private static SecureRandom getRng() { + // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is + // required to be thread-safe. + if (sRng == null) { + sRng = new SecureRandom(); + } + return sRng; + } } diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java index 0dbe788..5089a25 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -24,6 +24,7 @@ import android.security.keymaster.OperationResult; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; +import java.security.ProviderException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.MacSpi; @@ -185,10 +186,10 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp } if (mOperationToken == null) { - throw new IllegalStateException("Keystore returned null operation token"); + throw new ProviderException("Keystore returned null operation token"); } if (mOperationHandle == 0) { - throw new IllegalStateException("Keystore returned invalid operation handle"); + throw new ProviderException("Keystore returned invalid operation handle"); } mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer( @@ -206,17 +207,17 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp try { ensureKeystoreOperationInitialized(); } catch (InvalidKeyException e) { - throw new IllegalStateException("Failed to reinitialize MAC", e); + throw new ProviderException("Failed to reinitialize MAC", e); } byte[] output; try { output = mChunkedStreamer.update(input, offset, len); } catch (KeyStoreException e) { - throw new IllegalStateException("Keystore operation failed", e); + throw new ProviderException("Keystore operation failed", e); } if ((output != null) && (output.length != 0)) { - throw new IllegalStateException("Update operation unexpectedly produced output"); + throw new ProviderException("Update operation unexpectedly produced output"); } } @@ -225,14 +226,14 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp try { ensureKeystoreOperationInitialized(); } catch (InvalidKeyException e) { - throw new IllegalStateException("Failed to reinitialize MAC", e); + throw new ProviderException("Failed to reinitialize MAC", e); } byte[] result; try { result = mChunkedStreamer.doFinal(null, 0, 0); } catch (KeyStoreException e) { - throw new IllegalStateException("Keystore operation failed", e); + throw new ProviderException("Keystore operation failed", e); } resetWhilePreservingInitState(); diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java index 68b5751..4b914c2 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -21,6 +21,7 @@ import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterDefs; import java.security.InvalidAlgorithmParameterException; +import java.security.ProviderException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.Date; @@ -39,6 +40,17 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { public AES() { super(KeymasterDefs.KM_ALGORITHM_AES, 128); } + + @Override + protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + super.engineInit(params, random); + if ((mKeySizeBits != 128) && (mKeySizeBits != 192) && (mKeySizeBits != 256)) { + throw new InvalidAlgorithmParameterException( + "Unsupported key size: " + mKeySizeBits + + ". Supported: 128, 192, 256."); + } + } } protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi { @@ -87,6 +99,11 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { private KeyGeneratorSpec mSpec; private SecureRandom mRng; + protected int mKeySizeBits; + private int[] mKeymasterPurposes; + private int[] mKeymasterBlockModes; + private int[] mKeymasterPaddings; + protected KeyStoreKeyGeneratorSpi( int keymasterAlgorithm, int defaultKeySizeBits) { @@ -100,6 +117,97 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { mKeymasterAlgorithm = keymasterAlgorithm; mKeymasterDigest = keymasterDigest; mDefaultKeySizeBits = defaultKeySizeBits; + if (mDefaultKeySizeBits <= 0) { + throw new IllegalArgumentException("Default key size must be positive"); + } + + if ((mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) && (mKeymasterDigest == -1)) { + throw new IllegalArgumentException( + "Digest algorithm must be specified for HMAC key"); + } + } + + @Override + protected void engineInit(SecureRandom random) { + throw new UnsupportedOperationException("Cannot initialize without an " + + KeyGeneratorSpec.class.getName() + " parameter"); + } + + @Override + protected void engineInit(int keySize, SecureRandom random) { + throw new UnsupportedOperationException("Cannot initialize without a " + + KeyGeneratorSpec.class.getName() + " parameter"); + } + + @Override + protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + resetAll(); + + boolean success = false; + try { + if ((params == null) || (!(params instanceof KeyGeneratorSpec))) { + throw new InvalidAlgorithmParameterException("Cannot initialize without an " + + KeyGeneratorSpec.class.getName() + " parameter"); + } + KeyGeneratorSpec spec = (KeyGeneratorSpec) params; + if (spec.getKeystoreAlias() == null) { + throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided"); + } + + mRng = random; + mSpec = spec; + + mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits; + if (mKeySizeBits <= 0) { + throw new InvalidAlgorithmParameterException( + "Key size must be positive: " + mKeySizeBits); + } else if ((mKeySizeBits % 8) != 0) { + throw new InvalidAlgorithmParameterException( + "Key size in must be a multiple of 8: " + mKeySizeBits); + } + + try { + mKeymasterPurposes = + KeyStoreKeyProperties.Purpose.allToKeymaster(spec.getPurposes()); + mKeymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster( + spec.getEncryptionPaddings()); + mKeymasterBlockModes = + KeyStoreKeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()); + if (((spec.getPurposes() & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + && (spec.isRandomizedEncryptionRequired())) { + for (int keymasterBlockMode : mKeymasterBlockModes) { + if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible( + keymasterBlockMode)) { + throw new InvalidAlgorithmParameterException( + "Randomized encryption (IND-CPA) required but may be violated" + + " by block mode: " + + KeyStoreKeyProperties.BlockMode.fromKeymaster( + keymasterBlockMode) + + ". See " + KeyGeneratorSpec.class.getName() + + " documentation."); + } + } + } + } catch (IllegalArgumentException e) { + throw new InvalidAlgorithmParameterException(e); + } + + success = true; + } finally { + if (!success) { + resetAll(); + } + } + } + + private void resetAll() { + mSpec = null; + mRng = null; + mKeySizeBits = -1; + mKeymasterPurposes = null; + mKeymasterPaddings = null; + mKeymasterBlockModes = null; } @Override @@ -117,43 +225,14 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } KeymasterArguments args = new KeymasterArguments(); + args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits); args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); if (mKeymasterDigest != -1) { args.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); } - if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { - if (mKeymasterDigest == -1) { - throw new IllegalStateException("Digest algorithm must be specified for HMAC key"); - } - } - int keySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits; - args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits); - @KeyStoreKeyProperties.PurposeEnum int purposes = spec.getPurposes(); - int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes( - spec.getBlockModes()); - if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) - && (spec.isRandomizedEncryptionRequired())) { - for (int keymasterBlockMode : keymasterBlockModes) { - if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) { - throw new IllegalStateException( - "Randomized encryption (IND-CPA) required but may be violated by block" - + " mode: " - + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode( - keymasterBlockMode) - + ". See KeyGeneratorSpec documentation."); - } - } - } - - for (int keymasterPurpose : - KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) { - args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); - } - args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); - args.addInts( - KeymasterDefs.KM_TAG_PADDING, - KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings( - spec.getEncryptionPaddings())); + args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes); + args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes); + args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings); KeymasterUtils.addUserAuthArgs(args, spec.getContext(), spec.isUserAuthenticationRequired(), @@ -168,57 +247,31 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { (spec.getKeyValidityForConsumptionEnd() != null) ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE)); - if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + if (((spec.getPurposes() & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) && (!spec.isRandomizedEncryptionRequired())) { // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); } - byte[] additionalEntropy = null; - SecureRandom rng = mRng; - if (rng != null) { - additionalEntropy = new byte[(keySizeBits + 7) / 8]; - rng.nextBytes(additionalEntropy); - } - + byte[] additionalEntropy = + KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( + mRng, (mKeySizeBits + 7) / 8); int flags = spec.getFlags(); String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias(); + KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); int errorCode = mKeyStore.generateKey( - keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics()); + keyAliasInKeystore, args, additionalEntropy, flags, resultingKeyCharacteristics); if (errorCode != KeyStore.NO_ERROR) { - throw new IllegalStateException( + throw new ProviderException( "Keystore operation failed", KeyStore.getKeyStoreException(errorCode)); } - String keyAlgorithmJCA = - KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest); - return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA); - } - - @Override - protected void engineInit(SecureRandom random) { - throw new UnsupportedOperationException("Cannot initialize without an " - + KeyGeneratorSpec.class.getName() + " parameter"); - } - - @Override - protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) - throws InvalidAlgorithmParameterException { - if ((params == null) || (!(params instanceof KeyGeneratorSpec))) { - throw new InvalidAlgorithmParameterException("Cannot initialize without an " - + KeyGeneratorSpec.class.getName() + " parameter"); - } - KeyGeneratorSpec spec = (KeyGeneratorSpec) params; - if (spec.getKeystoreAlias() == null) { - throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided"); + String keyAlgorithmJCA; + try { + keyAlgorithmJCA = KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm( + mKeymasterAlgorithm, mKeymasterDigest); + } catch (IllegalArgumentException e) { + throw new ProviderException("Failed to obtain JCA secret key algorithm name", e); } - - mSpec = spec; - mRng = random; - } - - @Override - protected void engineInit(int keySize, SecureRandom random) { - throw new UnsupportedOperationException("Cannot initialize without a " - + KeyGeneratorSpec.class.getName() + " parameter"); + return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA); } } diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java index b85ec53..1c3e300 100644 --- a/keystore/java/android/security/KeyStoreKeyProperties.java +++ b/keystore/java/android/security/KeyStoreKeyProperties.java @@ -17,13 +17,23 @@ package android.security; import android.annotation.IntDef; +import android.annotation.StringDef; import android.security.keymaster.KeymasterDefs; import libcore.util.EmptyArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPairGenerator; import java.util.Collection; +import java.util.Locale; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.SecretKeyFactory; /** * Properties of {@code AndroidKeyStore} keys. @@ -37,7 +47,7 @@ public abstract class KeyStoreKeyProperties { public @interface PurposeEnum {} /** - * Purpose of key. + * Purposes of key. */ public static abstract class Purpose { private Purpose() {} @@ -122,6 +132,514 @@ public abstract class KeyStoreKeyProperties { } @Retention(RetentionPolicy.SOURCE) + @StringDef({ + Algorithm.RSA, + Algorithm.EC, + Algorithm.AES, + Algorithm.HMAC_SHA1, + Algorithm.HMAC_SHA224, + Algorithm.HMAC_SHA256, + Algorithm.HMAC_SHA384, + Algorithm.HMAC_SHA512, + }) + public @interface AlgorithmEnum {} + + /** + * Key algorithms. + * + * <p>These are standard names which can be used to obtain instances of {@link KeyGenerator}, + * {@link KeyPairGenerator}, {@link Cipher} (as part of the transformation string), {@link Mac}, + * {@link KeyFactory}, {@link SecretKeyFactory}. These are also the names used by + * {@link Key#getAlgorithm()}. + */ + public static abstract class Algorithm { + private Algorithm() {} + + /** Rivest Shamir Adleman (RSA) key. */ + public static final String RSA = "RSA"; + + /** Elliptic Curve (EC) key. */ + public static final String EC = "EC"; + + /** Advanced Encryption Standard (AES) key. */ + public static final String AES = "AES"; + + /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */ + public static final String HMAC_SHA1 = "HmacSHA1"; + + /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */ + public static final String HMAC_SHA224 = "HmacSHA224"; + + /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */ + public static final String HMAC_SHA256 = "HmacSHA256"; + + /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */ + public static final String HMAC_SHA384 = "HmacSHA384"; + + /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */ + public static final String HMAC_SHA512 = "HmacSHA512"; + + /** + * @hide + */ + static int toKeymasterSecretKeyAlgorithm(@AlgorithmEnum String algorithm) { + if (AES.equalsIgnoreCase(algorithm)) { + return KeymasterDefs.KM_ALGORITHM_AES; + } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) { + return KeymasterDefs.KM_ALGORITHM_HMAC; + } else { + throw new IllegalArgumentException( + "Unsupported secret key algorithm: " + algorithm); + } + } + + /** + * @hide + */ + static @AlgorithmEnum String fromKeymasterSecretKeyAlgorithm( + int keymasterAlgorithm, int keymasterDigest) { + switch (keymasterAlgorithm) { + case KeymasterDefs.KM_ALGORITHM_AES: + if (keymasterDigest != -1) { + throw new IllegalArgumentException("Digest not supported for AES key: " + + Digest.fromKeymaster(keymasterDigest)); + } + return AES; + case KeymasterDefs.KM_ALGORITHM_HMAC: + switch (keymasterDigest) { + case KeymasterDefs.KM_DIGEST_SHA1: + return HMAC_SHA1; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return HMAC_SHA224; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return HMAC_SHA256; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return HMAC_SHA384; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return HMAC_SHA512; + default: + throw new IllegalArgumentException("Unsupported HMAC digest: " + + Digest.fromKeymaster(keymasterDigest)); + } + default: + throw new IllegalArgumentException( + "Unsupported algorithm: " + keymasterAlgorithm); + } + } + + /** + * @hide + * + * @return keymaster digest or {@code -1} if the algorithm does not involve a digest. + */ + static int toKeymasterDigest(@AlgorithmEnum String algorithm) { + String algorithmUpper = algorithm.toUpperCase(Locale.US); + if (algorithmUpper.startsWith("HMAC")) { + String digestUpper = algorithmUpper.substring("HMAC".length()); + switch (digestUpper) { + case "SHA1": + return KeymasterDefs.KM_DIGEST_SHA1; + case "SHA224": + return KeymasterDefs.KM_DIGEST_SHA_2_224; + case "SHA256": + return KeymasterDefs.KM_DIGEST_SHA_2_256; + case "SHA384": + return KeymasterDefs.KM_DIGEST_SHA_2_384; + case "SHA512": + return KeymasterDefs.KM_DIGEST_SHA_2_512; + default: + throw new IllegalArgumentException( + "Unsupported HMAC digest: " + digestUpper); + } + } else { + return -1; + } + } + } + + @Retention(RetentionPolicy.SOURCE) + @StringDef({ + BlockMode.ECB, + BlockMode.CBC, + BlockMode.CTR, + BlockMode.GCM, + }) + public @interface BlockModeEnum {} + + /** + * Block modes that can be used when encrypting/decrypting using a key. + */ + public static abstract class BlockMode { + private BlockMode() {} + + /** Electronic Codebook (ECB) block mode. */ + public static final String ECB = "ECB"; + + /** Cipher Block Chaining (CBC) block mode. */ + public static final String CBC = "CBC"; + + /** Counter (CTR) block mode. */ + public static final String CTR = "CTR"; + + /** Galois/Counter Mode (GCM) block mode. */ + public static final String GCM = "GCM"; + + /** + * @hide + */ + static int toKeymaster(@BlockModeEnum String blockMode) { + if (ECB.equalsIgnoreCase(blockMode)) { + return KeymasterDefs.KM_MODE_ECB; + } else if (CBC.equalsIgnoreCase(blockMode)) { + return KeymasterDefs.KM_MODE_CBC; + } else if (CTR.equalsIgnoreCase(blockMode)) { + return KeymasterDefs.KM_MODE_CTR; + } else if (GCM.equalsIgnoreCase(blockMode)) { + return KeymasterDefs.KM_MODE_GCM; + } else { + throw new IllegalArgumentException("Unsupported block mode: " + blockMode); + } + } + + /** + * @hide + */ + static @BlockModeEnum String fromKeymaster(int blockMode) { + switch (blockMode) { + case KeymasterDefs.KM_MODE_ECB: + return ECB; + case KeymasterDefs.KM_MODE_CBC: + return CBC; + case KeymasterDefs.KM_MODE_CTR: + return CTR; + case KeymasterDefs.KM_MODE_GCM: + return GCM; + default: + throw new IllegalArgumentException("Unsupported block mode: " + blockMode); + } + } + + /** + * @hide + */ + static @BlockModeEnum String[] allFromKeymaster(Collection<Integer> blockModes) { + if ((blockModes == null) || (blockModes.isEmpty())) { + return EmptyArray.STRING; + } + @BlockModeEnum String[] result = new String[blockModes.size()]; + int offset = 0; + for (int blockMode : blockModes) { + result[offset] = fromKeymaster(blockMode); + offset++; + } + return result; + } + + /** + * @hide + */ + static int[] allToKeymaster(@BlockModeEnum String[] blockModes) { + if ((blockModes == null) || (blockModes.length == 0)) { + return EmptyArray.INT; + } + int[] result = new int[blockModes.length]; + for (int i = 0; i < blockModes.length; i++) { + result[i] = toKeymaster(blockModes[i]); + } + return result; + } + } + + @Retention(RetentionPolicy.SOURCE) + @StringDef({ + EncryptionPadding.NONE, + EncryptionPadding.PKCS7, + EncryptionPadding.RSA_PKCS1, + EncryptionPadding.RSA_OAEP, + }) + public @interface EncryptionPaddingEnum {} + + /** + * Padding schemes for encryption/decryption. + */ + public static abstract class EncryptionPadding { + private EncryptionPadding() {} + + /** + * No padding. + */ + public static final String NONE = "NoPadding"; + + /** + * PKCS#7 padding. + */ + public static final String PKCS7 = "PKCS7Padding"; + + /** + * RSA PKCS#1 v1.5 padding for encryption/decryption. + */ + public static final String RSA_PKCS1 = "PKCS1Padding"; + + /** + * RSA Optimal Asymmetric Encryption Padding (OAEP). + */ + public static final String RSA_OAEP = "OAEPPadding"; + + /** + * @hide + */ + static int toKeymaster(@EncryptionPaddingEnum String padding) { + if (NONE.equalsIgnoreCase(padding)) { + return KeymasterDefs.KM_PAD_NONE; + } else if (PKCS7.equalsIgnoreCase(padding)) { + return KeymasterDefs.KM_PAD_PKCS7; + } else if (RSA_PKCS1.equalsIgnoreCase(padding)) { + return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT; + } else if (RSA_OAEP.equalsIgnoreCase(padding)) { + return KeymasterDefs.KM_PAD_RSA_OAEP; + } else { + throw new IllegalArgumentException( + "Unsupported encryption padding scheme: " + padding); + } + } + + /** + * @hide + */ + static @EncryptionPaddingEnum String fromKeymaster(int padding) { + switch (padding) { + case KeymasterDefs.KM_PAD_NONE: + return NONE; + case KeymasterDefs.KM_PAD_PKCS7: + return PKCS7; + case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT: + return RSA_PKCS1; + case KeymasterDefs.KM_PAD_RSA_OAEP: + return RSA_OAEP; + default: + throw new IllegalArgumentException( + "Unsupported encryption padding: " + padding); + } + } + + /** + * @hide + */ + static int[] allToKeymaster(@EncryptionPaddingEnum String[] paddings) { + if ((paddings == null) || (paddings.length == 0)) { + return EmptyArray.INT; + } + int[] result = new int[paddings.length]; + for (int i = 0; i < paddings.length; i++) { + result[i] = toKeymaster(paddings[i]); + } + return result; + } + } + + @Retention(RetentionPolicy.SOURCE) + @StringDef({ + SignaturePadding.RSA_PKCS1, + SignaturePadding.RSA_PSS, + }) + public @interface SignaturePaddingEnum {} + + /** + * Padding schemes for signing/verification. + */ + public static abstract class SignaturePadding { + private SignaturePadding() {} + + /** + * RSA PKCS#1 v1.5 padding for signatures. + */ + public static final String RSA_PKCS1 = "PKCS1"; + + /** + * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding. + */ + public static final String RSA_PSS = "PSS"; + + /** + * @hide + */ + static int toKeymaster(@SignaturePaddingEnum String padding) { + switch (padding.toUpperCase(Locale.US)) { + case RSA_PKCS1: + return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN; + case RSA_PSS: + return KeymasterDefs.KM_PAD_RSA_PSS; + default: + throw new IllegalArgumentException( + "Unsupported signature padding scheme: " + padding); + } + } + + /** + * @hide + */ + static @SignaturePaddingEnum String fromKeymaster(int padding) { + switch (padding) { + case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN: + return RSA_PKCS1; + case KeymasterDefs.KM_PAD_RSA_PSS: + return RSA_PSS; + default: + throw new IllegalArgumentException("Unsupported signature padding: " + padding); + } + } + + /** + * @hide + */ + static int[] allToKeymaster(@SignaturePaddingEnum String[] paddings) { + if ((paddings == null) || (paddings.length == 0)) { + return EmptyArray.INT; + } + int[] result = new int[paddings.length]; + for (int i = 0; i < paddings.length; i++) { + result[i] = toKeymaster(paddings[i]); + } + return result; + } + } + + @Retention(RetentionPolicy.SOURCE) + @StringDef({ + Digest.NONE, + Digest.MD5, + Digest.SHA1, + Digest.SHA224, + Digest.SHA256, + Digest.SHA384, + Digest.SHA512, + }) + public @interface DigestEnum {} + + /** + * Digests that can be used with a key when signing or generating Message Authentication + * Codes (MACs). + */ + public static abstract class Digest { + private Digest() {} + + /** + * No digest: sign/authenticate the raw message. + */ + public static final String NONE = "NONE"; + + /** + * MD5 digest. + */ + public static final String MD5 = "MD5"; + + /** + * SHA-1 digest. + */ + public static final String SHA1 = "SHA-1"; + + /** + * SHA-2 224 (aka SHA-224) digest. + */ + public static final String SHA224 = "SHA-224"; + + /** + * SHA-2 256 (aka SHA-256) digest. + */ + public static final String SHA256 = "SHA-256"; + + /** + * SHA-2 384 (aka SHA-384) digest. + */ + public static final String SHA384 = "SHA-384"; + + /** + * SHA-2 512 (aka SHA-512) digest. + */ + public static final String SHA512 = "SHA-512"; + + /** + * @hide + */ + static int toKeymaster(@DigestEnum String digest) { + switch (digest.toUpperCase(Locale.US)) { + case SHA1: + return KeymasterDefs.KM_DIGEST_SHA1; + case SHA224: + return KeymasterDefs.KM_DIGEST_SHA_2_224; + case SHA256: + return KeymasterDefs.KM_DIGEST_SHA_2_256; + case SHA384: + return KeymasterDefs.KM_DIGEST_SHA_2_384; + case SHA512: + return KeymasterDefs.KM_DIGEST_SHA_2_512; + case NONE: + return KeymasterDefs.KM_DIGEST_NONE; + case MD5: + return KeymasterDefs.KM_DIGEST_MD5; + default: + throw new IllegalArgumentException("Unsupported digest algorithm: " + digest); + } + } + + /** + * @hide + */ + static @DigestEnum String fromKeymaster(int digest) { + switch (digest) { + case KeymasterDefs.KM_DIGEST_NONE: + return NONE; + case KeymasterDefs.KM_DIGEST_MD5: + return MD5; + case KeymasterDefs.KM_DIGEST_SHA1: + return SHA1; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return SHA224; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return SHA256; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return SHA384; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return SHA512; + default: + throw new IllegalArgumentException("Unsupported digest algorithm: " + digest); + } + } + + /** + * @hide + */ + static @DigestEnum String[] allFromKeymaster(Collection<Integer> digests) { + if (digests.isEmpty()) { + return EmptyArray.STRING; + } + String[] result = new String[digests.size()]; + int offset = 0; + for (int digest : digests) { + result[offset] = fromKeymaster(digest); + offset++; + } + return result; + } + + /** + * @hide + */ + static int[] allToKeymaster(@DigestEnum String[] digests) { + if ((digests == null) || (digests.length == 0)) { + return EmptyArray.INT; + } + int[] result = new int[digests.length]; + int offset = 0; + for (@DigestEnum String digest : digests) { + result[offset] = toKeymaster(digest); + offset++; + } + return result; + } + } + + @Retention(RetentionPolicy.SOURCE) @IntDef({Origin.GENERATED, Origin.IMPORTED, Origin.UNKNOWN}) public @interface OriginEnum {} diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java index 96d58d8..7533bdc 100644 --- a/keystore/java/android/security/KeyStoreKeySpec.java +++ b/keystore/java/android/security/KeyStoreKeySpec.java @@ -32,10 +32,10 @@ public class KeyStoreKeySpec implements KeySpec { private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; private final @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private final String[] mEncryptionPaddings; - private final String[] mSignaturePaddings; - private final String[] mDigests; - private final String[] mBlockModes; + private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; + private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; + private final @KeyStoreKeyProperties.DigestEnum String[] mDigests; + private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes; private final boolean mUserAuthenticationRequired; private final int mUserAuthenticationValidityDurationSeconds; private final boolean mUserAuthenticationRequirementTeeEnforced; @@ -51,10 +51,10 @@ public class KeyStoreKeySpec implements KeySpec { Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, @KeyStoreKeyProperties.PurposeEnum int purposes, - String[] encryptionPaddings, - String[] signaturePaddings, - String[] digests, - String[] blockModes, + @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings, + @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings, + @KeyStoreKeyProperties.DigestEnum String[] digests, + @KeyStoreKeyProperties.BlockModeEnum String[] blockModes, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, boolean userAuthenticationRequirementTeeEnforced) { @@ -143,28 +143,28 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the set of block modes with which the key can be used. */ - public String[] getBlockModes() { + public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() { return ArrayUtils.cloneIfNotEmpty(mBlockModes); } /** * Gets the set of padding modes with which the key can be used when encrypting/decrypting. */ - public String[] getEncryptionPaddings() { + public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() { return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** * Gets the set of padding modes with which the key can be used when signing/verifying. */ - public String[] getSignaturePaddings() { + public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() { return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } /** * Gets the set of digest algorithms with which the key can be used. */ - public String[] getDigests() { + public @KeyStoreKeyProperties.DigestEnum String[] getDigests() { return ArrayUtils.cloneIfNotEmpty(mDigests); } @@ -179,10 +179,10 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the duration of time (seconds) for which this key can be used after the user is - * successfully authenticated. + * successfully authenticated. This has effect only if user authentication is required. * - * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication - * is required for every use of the key. + * @return duration in seconds or {@code -1} if authentication is required for every use of the + * key. * * @see #isUserAuthenticationRequired() */ diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index b4747e9..8d7a19f 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -45,10 +45,10 @@ public final class KeyStoreParameter implements ProtectionParameter { private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; private final @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private final String[] mEncryptionPaddings; - private final String[] mSignaturePaddings; - private final String[] mDigests; - private final String[] mBlockModes; + private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; + private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; + private final @KeyStoreKeyProperties.DigestEnum String[] mDigests; + private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes; private final boolean mRandomizedEncryptionRequired; private final boolean mUserAuthenticationRequired; private final int mUserAuthenticationValidityDurationSeconds; @@ -60,10 +60,10 @@ public final class KeyStoreParameter implements ProtectionParameter { Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, @KeyStoreKeyProperties.PurposeEnum int purposes, - String[] encryptionPaddings, - String[] signaturePaddings, - String[] digests, - String[] blockModes, + @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings, + @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings, + @KeyStoreKeyProperties.DigestEnum String[] digests, + @KeyStoreKeyProperties.BlockModeEnum String[] blockModes, boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds) { @@ -151,7 +151,7 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. */ - public String[] getEncryptionPaddings() { + public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() { return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } @@ -159,7 +159,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * Gets the set of padding schemes with which the key can be used when signing or verifying * signatures. */ - public String[] getSignaturePaddings() { + public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() { return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } @@ -170,7 +170,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see #isDigestsSpecified() */ - public String[] getDigests() { + public @KeyStoreKeyProperties.DigestEnum String[] getDigests() { if (mDigests == null) { throw new IllegalStateException("Digests not specified"); } @@ -190,7 +190,7 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Gets the set of block modes with which the key can be used. */ - public String[] getBlockModes() { + public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() { return ArrayUtils.cloneIfNotEmpty(mBlockModes); } @@ -218,10 +218,12 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Gets the duration of time (seconds) for which this key can be used after the user is - * successfully authenticated. + * successfully authenticated. This has effect only if user authentication is required. * - * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication - * is required for every use of the key. + * @return duration in seconds or {@code -1} if authentication is required for every use of the + * key. + * + * @see #isUserAuthenticationRequired() */ public int getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; @@ -251,10 +253,10 @@ public final class KeyStoreParameter implements ProtectionParameter { private Date mKeyValidityForOriginationEnd; private Date mKeyValidityForConsumptionEnd; private @KeyStoreKeyProperties.PurposeEnum int mPurposes; - private String[] mEncryptionPaddings; - private String[] mSignaturePaddings; - private String[] mDigests; - private String[] mBlockModes; + private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings; + private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings; + private @KeyStoreKeyProperties.DigestEnum String[] mDigests; + private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; private boolean mUserAuthenticationRequired; private int mUserAuthenticationValidityDurationSeconds = -1; @@ -292,6 +294,8 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p>By default, the key is valid at any instant. * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. + * * @see #setKeyValidityEnd(Date) */ public Builder setKeyValidityStart(Date startDate) { @@ -304,6 +308,8 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p>By default, the key is valid at any instant. * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. + * * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) @@ -319,6 +325,8 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p>By default, the key is valid at any instant. * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. + * * @see #setKeyValidityForConsumptionEnd(Date) */ public Builder setKeyValidityForOriginationEnd(Date endDate) { @@ -332,6 +340,8 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p>By default, the key is valid at any instant. * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. + * * @see #setKeyValidityForOriginationEnd(Date) */ public Builder setKeyValidityForConsumptionEnd(Date endDate) { @@ -343,6 +353,8 @@ public final class KeyStoreParameter implements ProtectionParameter { * Sets the set of purposes for which the key can be used. * * <p>This must be specified for all keys. There is no default. + * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; @@ -355,8 +367,11 @@ public final class KeyStoreParameter implements ProtectionParameter { * rejected. * * <p>This must be specified for keys which are used for encryption/decryption. + * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ - public Builder setEncryptionPaddings(String... paddings) { + public Builder setEncryptionPaddings( + @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) { mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } @@ -367,8 +382,11 @@ public final class KeyStoreParameter implements ProtectionParameter { * rejected. * * <p>This must be specified for RSA keys which are used for signing/verification. + * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ - public Builder setSignaturePaddings(String... paddings) { + public Builder setSignaturePaddings( + @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) { mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } @@ -380,8 +398,10 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p>For HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()}. For * asymmetric signing keys this constraint must be specified. + * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ - public Builder setDigests(String... digests) { + public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) { mDigests = ArrayUtils.cloneIfNotEmpty(digests); return this; } @@ -391,8 +411,10 @@ public final class KeyStoreParameter implements ProtectionParameter { * Attempts to use the key with any other block modes will be rejected. * * <p>This must be specified for encryption/decryption keys. + * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ - public Builder setBlockModes(String... blockModes) { + public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) { mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; } @@ -430,6 +452,8 @@ public final class KeyStoreParameter implements ProtectionParameter { * <li>If you are using RSA encryption without padding, consider switching to padding * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li> * </ul> + * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ public Builder setRandomizedEncryptionRequired(boolean required) { mRandomizedEncryptionRequired = required; @@ -449,6 +473,8 @@ public final class KeyStoreParameter implements ProtectionParameter { * <a href="{@docRoot}training/articles/keystore.html#UserAuthentication">More * information</a>. * + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. + * * @see #setUserAuthenticationValidityDurationSeconds(int) */ public Builder setUserAuthenticationRequired(boolean required) { @@ -462,7 +488,9 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p>By default, the user needs to authenticate for every use of the key. * - * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for + * <p><b>NOTE: This has currently no effect on asymmetric key pairs. + * + * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for * every use of the key. * * @see #setUserAuthenticationRequired(boolean) diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java index bfe09e3..ff79b7a 100644 --- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java @@ -79,8 +79,8 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { int keySize; @KeyStoreKeyProperties.PurposeEnum int purposes; String[] encryptionPaddings; - String[] digests; - String[] blockModes; + @KeyStoreKeyProperties.DigestEnum String[] digests; + @KeyStoreKeyProperties.BlockModeEnum String[] blockModes; int keymasterSwEnforcedUserAuthenticators; int keymasterHwEnforcedUserAuthenticators; try { @@ -105,10 +105,10 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { List<String> encryptionPaddingsList = new ArrayList<String>(); for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) { - String jcaPadding; + @KeyStoreKeyProperties.EncryptionPaddingEnum String jcaPadding; try { - jcaPadding = KeymasterUtils.getJcaEncryptionPaddingFromKeymasterPadding( - keymasterPadding); + jcaPadding = + KeyStoreKeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding); } catch (IllegalArgumentException e) { throw new InvalidKeySpecException( "Unsupported encryption padding: " + keymasterPadding); @@ -118,9 +118,9 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { encryptionPaddings = encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); - digests = KeymasterUtils.getJcaDigestAlgorithmsFromKeymasterDigests( + digests = KeyStoreKeyProperties.Digest.allFromKeymaster( keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST)); - blockModes = KeymasterUtils.getJcaBlockModesFromKeymasterBlockModes( + blockModes = KeyStoreKeyProperties.BlockMode.allFromKeymaster( keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE)); keymasterSwEnforcedUserAuthenticators = keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java index aa44ecd..df67ae7 100644 --- a/keystore/java/android/security/KeymasterUtils.java +++ b/keystore/java/android/security/KeymasterUtils.java @@ -21,11 +21,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterDefs; -import libcore.util.EmptyArray; - -import java.util.Collection; -import java.util.Locale; - /** * @hide */ @@ -33,152 +28,6 @@ public abstract class KeymasterUtils { private KeymasterUtils() {} - public static int getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) { - if ("AES".equalsIgnoreCase(jcaKeyAlgorithm)) { - return KeymasterDefs.KM_ALGORITHM_AES; - } else if (jcaKeyAlgorithm.toUpperCase(Locale.US).startsWith("HMAC")) { - return KeymasterDefs.KM_ALGORITHM_HMAC; - } else { - throw new IllegalArgumentException( - "Unsupported secret key algorithm: " + jcaKeyAlgorithm); - } - } - - public static String getJcaSecretKeyAlgorithm(int keymasterAlgorithm, int keymasterDigest) { - switch (keymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_AES: - if (keymasterDigest != -1) { - throw new IllegalArgumentException( - "Digest not supported for AES key: " + keymasterDigest); - } - return "AES"; - case KeymasterDefs.KM_ALGORITHM_HMAC: - switch (keymasterDigest) { - case KeymasterDefs.KM_DIGEST_SHA1: - return "HmacSHA1"; - case KeymasterDefs.KM_DIGEST_SHA_2_224: - return "HmacSHA224"; - case KeymasterDefs.KM_DIGEST_SHA_2_256: - return "HmacSHA256"; - case KeymasterDefs.KM_DIGEST_SHA_2_384: - return "HmacSHA384"; - case KeymasterDefs.KM_DIGEST_SHA_2_512: - return "HmacSHA512"; - default: - throw new IllegalArgumentException( - "Unsupported HMAC digest: " + keymasterDigest); - } - default: - throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm); - } - } - - public static String getJcaKeyPairAlgorithmFromKeymasterAlgorithm(int keymasterAlgorithm) { - switch (keymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_RSA: - return "RSA"; - case KeymasterDefs.KM_ALGORITHM_EC: - return "EC"; - default: - throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm); - } - } - - public static int getKeymasterDigestfromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) { - String algorithmUpper = jcaKeyAlgorithm.toUpperCase(Locale.US); - if (algorithmUpper.startsWith("HMAC")) { - String digestUpper = algorithmUpper.substring("HMAC".length()); - switch (digestUpper) { - case "MD5": - return KeymasterDefs.KM_DIGEST_MD5; - case "SHA1": - return KeymasterDefs.KM_DIGEST_SHA1; - case "SHA224": - return KeymasterDefs.KM_DIGEST_SHA_2_224; - case "SHA256": - return KeymasterDefs.KM_DIGEST_SHA_2_256; - case "SHA384": - return KeymasterDefs.KM_DIGEST_SHA_2_384; - case "SHA512": - return KeymasterDefs.KM_DIGEST_SHA_2_512; - default: - throw new IllegalArgumentException("Unsupported HMAC digest: " + digestUpper); - } - } else { - return -1; - } - } - - public static int getKeymasterDigestFromJcaDigestAlgorithm(String jcaDigestAlgorithm) { - if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-1")) { - return KeymasterDefs.KM_DIGEST_SHA1; - } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-224")) { - return KeymasterDefs.KM_DIGEST_SHA_2_224; - } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-256")) { - return KeymasterDefs.KM_DIGEST_SHA_2_256; - } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-384")) { - return KeymasterDefs.KM_DIGEST_SHA_2_384; - } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-512")) { - return KeymasterDefs.KM_DIGEST_SHA_2_512; - } else if (jcaDigestAlgorithm.equalsIgnoreCase("NONE")) { - return KeymasterDefs.KM_DIGEST_NONE; - } else if (jcaDigestAlgorithm.equalsIgnoreCase("MD5")) { - return KeymasterDefs.KM_DIGEST_MD5; - } else { - throw new IllegalArgumentException( - "Unsupported digest algorithm: " + jcaDigestAlgorithm); - } - } - - public static String getJcaDigestAlgorithmFromKeymasterDigest(int keymasterDigest) { - switch (keymasterDigest) { - case KeymasterDefs.KM_DIGEST_NONE: - return "NONE"; - case KeymasterDefs.KM_DIGEST_MD5: - return "MD5"; - case KeymasterDefs.KM_DIGEST_SHA1: - return "SHA-1"; - case KeymasterDefs.KM_DIGEST_SHA_2_224: - return "SHA-224"; - case KeymasterDefs.KM_DIGEST_SHA_2_256: - return "SHA-256"; - case KeymasterDefs.KM_DIGEST_SHA_2_384: - return "SHA-384"; - case KeymasterDefs.KM_DIGEST_SHA_2_512: - return "SHA-512"; - default: - throw new IllegalArgumentException( - "Unsupported digest algorithm: " + keymasterDigest); - } - } - - public static String[] getJcaDigestAlgorithmsFromKeymasterDigests( - Collection<Integer> keymasterDigests) { - if (keymasterDigests.isEmpty()) { - return EmptyArray.STRING; - } - String[] result = new String[keymasterDigests.size()]; - int offset = 0; - for (int keymasterDigest : keymasterDigests) { - result[offset] = getJcaDigestAlgorithmFromKeymasterDigest(keymasterDigest); - offset++; - } - return result; - } - - public static int[] getKeymasterDigestsFromJcaDigestAlgorithms(String[] jcaDigestAlgorithms) { - if ((jcaDigestAlgorithms == null) || (jcaDigestAlgorithms.length == 0)) { - return EmptyArray.INT; - } - int[] result = new int[jcaDigestAlgorithms.length]; - int offset = 0; - for (String jcaDigestAlgorithm : jcaDigestAlgorithms) { - result[offset] = getKeymasterDigestFromJcaDigestAlgorithm(jcaDigestAlgorithm); - offset++; - } - return result; - } - public static int getDigestOutputSizeBits(int keymasterDigest) { switch (keymasterDigest) { case KeymasterDefs.KM_DIGEST_NONE: @@ -200,60 +49,6 @@ public abstract class KeymasterUtils { } } - public static int getKeymasterBlockModeFromJcaBlockMode(String jcaBlockMode) { - if ("ECB".equalsIgnoreCase(jcaBlockMode)) { - return KeymasterDefs.KM_MODE_ECB; - } else if ("CBC".equalsIgnoreCase(jcaBlockMode)) { - return KeymasterDefs.KM_MODE_CBC; - } else if ("CTR".equalsIgnoreCase(jcaBlockMode)) { - return KeymasterDefs.KM_MODE_CTR; - } else if ("GCM".equalsIgnoreCase(jcaBlockMode)) { - return KeymasterDefs.KM_MODE_GCM; - } else { - throw new IllegalArgumentException("Unsupported block mode: " + jcaBlockMode); - } - } - - public static String getJcaBlockModeFromKeymasterBlockMode(int keymasterBlockMode) { - switch (keymasterBlockMode) { - case KeymasterDefs.KM_MODE_ECB: - return "ECB"; - case KeymasterDefs.KM_MODE_CBC: - return "CBC"; - case KeymasterDefs.KM_MODE_CTR: - return "CTR"; - case KeymasterDefs.KM_MODE_GCM: - return "GCM"; - default: - throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode); - } - } - - public static String[] getJcaBlockModesFromKeymasterBlockModes( - Collection<Integer> keymasterBlockModes) { - if ((keymasterBlockModes == null) || (keymasterBlockModes.isEmpty())) { - return EmptyArray.STRING; - } - String[] result = new String[keymasterBlockModes.size()]; - int offset = 0; - for (int keymasterBlockMode : keymasterBlockModes) { - result[offset] = getJcaBlockModeFromKeymasterBlockMode(keymasterBlockMode); - offset++; - } - return result; - } - - public static int[] getKeymasterBlockModesFromJcaBlockModes(String[] jcaBlockModes) { - if ((jcaBlockModes == null) || (jcaBlockModes.length == 0)) { - return EmptyArray.INT; - } - int[] result = new int[jcaBlockModes.length]; - for (int i = 0; i < jcaBlockModes.length; i++) { - result[i] = getKeymasterBlockModeFromJcaBlockMode(jcaBlockModes[i]); - } - return result; - } - public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) { switch (keymasterBlockMode) { case KeymasterDefs.KM_MODE_ECB: @@ -267,82 +62,6 @@ public abstract class KeymasterUtils { } } - public static int getKeymasterPaddingFromJcaEncryptionPadding(String jcaPadding) { - if ("NoPadding".equalsIgnoreCase(jcaPadding)) { - return KeymasterDefs.KM_PAD_NONE; - } else if ("PKCS7Padding".equalsIgnoreCase(jcaPadding)) { - return KeymasterDefs.KM_PAD_PKCS7; - } else if ("PKCS1Padding".equalsIgnoreCase(jcaPadding)) { - return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT; - } else if ("OEAPPadding".equalsIgnoreCase(jcaPadding)) { - return KeymasterDefs.KM_PAD_RSA_OAEP; - } else { - throw new IllegalArgumentException( - "Unsupported encryption padding scheme: " + jcaPadding); - } - } - - public static String getJcaEncryptionPaddingFromKeymasterPadding(int keymasterPadding) { - switch (keymasterPadding) { - case KeymasterDefs.KM_PAD_NONE: - return "NoPadding"; - case KeymasterDefs.KM_PAD_PKCS7: - return "PKCS7Padding"; - case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT: - return "PKCS1Padding"; - case KeymasterDefs.KM_PAD_RSA_OAEP: - return "OEAPPadding"; - default: - throw new IllegalArgumentException( - "Unsupported encryption padding: " + keymasterPadding); - } - } - - public static int getKeymasterPaddingFromJcaSignaturePadding(String jcaPadding) { - if ("PKCS#1".equalsIgnoreCase(jcaPadding)) { - return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN; - } if ("PSS".equalsIgnoreCase(jcaPadding)) { - return KeymasterDefs.KM_PAD_RSA_PSS; - } else { - throw new IllegalArgumentException( - "Unsupported signature padding scheme: " + jcaPadding); - } - } - - public static String getJcaSignaturePaddingFromKeymasterPadding(int keymasterPadding) { - switch (keymasterPadding) { - case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN: - return "PKCS#1"; - case KeymasterDefs.KM_PAD_RSA_PSS: - return "PSS"; - default: - throw new IllegalArgumentException( - "Unsupported signature padding: " + keymasterPadding); - } - } - - public static int[] getKeymasterPaddingsFromJcaEncryptionPaddings(String[] jcaPaddings) { - if ((jcaPaddings == null) || (jcaPaddings.length == 0)) { - return EmptyArray.INT; - } - int[] result = new int[jcaPaddings.length]; - for (int i = 0; i < jcaPaddings.length; i++) { - result[i] = getKeymasterPaddingFromJcaEncryptionPadding(jcaPaddings[i]); - } - return result; - } - - public static int[] getKeymasterPaddingsFromJcaSignaturePaddings(String[] jcaPaddings) { - if ((jcaPaddings == null) || (jcaPaddings.length == 0)) { - return EmptyArray.INT; - } - int[] result = new int[jcaPaddings.length]; - for (int i = 0; i < jcaPaddings.length; i++) { - result[i] = getKeymasterPaddingFromJcaSignaturePadding(jcaPaddings[i]); - } - return result; - } - /** * Adds keymaster arguments to express the key's authorization policy supported by user * authentication. |
