diff options
Diffstat (limited to 'keystore')
25 files changed, 1818 insertions, 1408 deletions
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index ed690de..c259c25 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -19,6 +19,8 @@ package android.security; import com.android.org.conscrypt.OpenSSLEngine; import com.android.org.conscrypt.OpenSSLKeyHolder; +import libcore.util.EmptyArray; + import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterDefs; @@ -46,12 +48,14 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; import javax.crypto.SecretKey; @@ -112,34 +116,22 @@ public class AndroidKeyStore extends KeyStoreSpi { if (keymasterAlgorithm == -1) { throw new UnrecoverableKeyException("Key algorithm unknown"); } - @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm; - try { - keyAlgorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(keymasterAlgorithm); - } catch (IllegalArgumentException e) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Unsupported key algorithm").initCause(e); - } - int keymasterDigest = - keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1); - if (keymasterDigest == -1) { - keymasterDigest = - keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1); - } - @KeyStoreKeyConstraints.DigestEnum Integer digest = null; - if (keymasterDigest != -1) { - try { - digest = KeyStoreKeyConstraints.Digest.fromKeymaster(keymasterDigest); - } catch (IllegalArgumentException e) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Unsupported digest").initCause(e); - } + List<Integer> keymasterDigests = + keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST); + int keymasterDigest; + if (keymasterDigests.isEmpty()) { + keymasterDigest = -1; + } else { + // More than one digest can be permitted for this key. Use the first one to form the + // JCA key algorithm name. + keymasterDigest = keymasterDigests.get(0); } String keyAlgorithmString; try { - keyAlgorithmString = KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm( - keyAlgorithm, digest); + keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm( + keymasterAlgorithm, keymasterDigest); } catch (IllegalArgumentException e) { throw (UnrecoverableKeyException) new UnrecoverableKeyException("Unsupported secret key type").initCause(e); @@ -456,80 +448,99 @@ public class AndroidKeyStore extends KeyStoreSpi { } String keyAlgorithmString = key.getAlgorithm(); - @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm; - @KeyStoreKeyConstraints.DigestEnum Integer digest; + int keymasterAlgorithm; + int keymasterDigest; try { - keyAlgorithm = - KeyStoreKeyConstraints.Algorithm.fromJCASecretKeyAlgorithm(keyAlgorithmString); - digest = KeyStoreKeyConstraints.Digest.fromJCASecretKeyAlgorithm(keyAlgorithmString); + keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm( + keyAlgorithmString); + keymasterDigest = + KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString); } catch (IllegalArgumentException e) { throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString); } KeymasterArguments args = new KeymasterArguments(); - args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, - KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm)); + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm); - @KeyStoreKeyConstraints.DigestEnum int digests; + int[] keymasterDigests; if (params.isDigestsSpecified()) { // Digest(s) specified in parameters - if (digest != null) { + keymasterDigests = + KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests()); + if (keymasterDigest != -1) { // Digest also specified in the JCA key algorithm name. - if ((params.getDigests() & digest) != digest) { + if (!com.android.internal.util.ArrayUtils.contains( + keymasterDigests, keymasterDigest)) { throw new KeyStoreException("Key digest mismatch" + ". Key: " + keyAlgorithmString - + ", parameter spec: " - + KeyStoreKeyConstraints.Digest.allToString(params.getDigests())); + + ", parameter spec: " + Arrays.asList(params.getDigests())); } } - digests = params.getDigests(); } else { // No digest specified in parameters - if (digest != null) { + if (keymasterDigest != -1) { // Digest specified in the JCA key algorithm name. - digests = digest; + keymasterDigests = new int[] {keymasterDigest}; } else { - digests = 0; + keymasterDigests = EmptyArray.INT; } } - for (int keymasterDigest : KeyStoreKeyConstraints.Digest.allToKeymaster(digests)) { - args.addInt(KeymasterDefs.KM_TAG_DIGEST, keymasterDigest); - } - if (digests != 0) { + args.addInts(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests); + if (keymasterDigests.length > 0) { // TODO: Remove MAC length constraint once Keymaster API no longer requires it. // This code will blow up if mode than one digest is specified. - Integer digestOutputSizeBytes = - KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest); - if (digestOutputSizeBytes != null) { + int digestOutputSizeBytes = + KeymasterUtils.getDigestOutputSizeBytes(keymasterDigests[0]); + if (digestOutputSizeBytes != -1) { // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes); } } - if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) { - if (digests == 0) { + if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { + if (keymasterDigests.length == 0) { throw new KeyStoreException("At least one digest algorithm must be specified" + " for key algorithm " + keyAlgorithmString); } } - int purposes = params.getPurposes(); - for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { - args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); - } - for (int keymasterBlockMode : - KeyStoreKeyConstraints.BlockMode.allToKeymaster(params.getBlockModes())) { - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode); + @KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes(); + int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes( + params.getBlockModes()); + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + && (params.isRandomizedEncryptionRequired())) { + for (int keymasterBlockMode : keymasterBlockModes) { + if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) { + throw new KeyStoreException( + "Randomized encryption (IND-CPA) required but may be violated by block" + + " mode: " + + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode( + keymasterBlockMode) + + ". See KeyStoreParameter documentation."); + } + } } - for (int keymasterPadding : - KeyStoreKeyConstraints.Padding.allToKeymaster(params.getPaddings())) { - args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); + for (int keymasterPurpose : KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) { + 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())); + args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); if (params.getUserAuthenticators() == 0) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); } else { args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, - KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster( + KeyStoreKeyProperties.UserAuthenticator.allToKeymaster( params.getUserAuthenticators())); + long secureUserId = GateKeeper.getSecureUserId(); + if (secureUserId == 0) { + throw new IllegalStateException("Secure lock screen must be enabled" + + " to import keys requiring user authentication"); + } + args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, secureUserId); } if (params.isInvalidatedOnNewFingerprintEnrolled()) { // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports @@ -552,9 +563,9 @@ public class AndroidKeyStore extends KeyStoreSpi { // TODO: Remove this once keymaster does not require us to specify the size of imported key. args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8); - if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) - || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) { - // Permit caller-specified IV. This is needed for the Cipher abstraction. + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + && (!params.isRandomizedEncryptionRequired())) { + // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); } diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java index a59927d..43f3b30 100644 --- a/keystore/java/android/security/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/AndroidKeyStoreProvider.java @@ -49,14 +49,26 @@ public class AndroidKeyStoreProvider extends Provider { // javax.crypto.KeyGenerator put("KeyGenerator.AES", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$AES"); + put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA1"); + put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA224"); put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA256"); + put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA384"); + put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".KeyStoreKeyGeneratorSpi$HmacSHA512"); // java.security.SecretKeyFactory - put("SecretKeyFactory.AES", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi"); - put("SecretKeyFactory.HmacSHA256", PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi"); + putSecretKeyFactoryImpl("AES"); + putSecretKeyFactoryImpl("HmacSHA1"); + putSecretKeyFactoryImpl("HmacSHA224"); + putSecretKeyFactoryImpl("HmacSHA256"); + putSecretKeyFactoryImpl("HmacSHA384"); + putSecretKeyFactoryImpl("HmacSHA512"); // javax.crypto.Mac + putMacImpl("HmacSHA1", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA1"); + putMacImpl("HmacSHA224", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA224"); putMacImpl("HmacSHA256", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA256"); + putMacImpl("HmacSHA384", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA384"); + putMacImpl("HmacSHA512", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA512"); // javax.crypto.Cipher putSymmetricCipherImpl("AES/ECB/NoPadding", @@ -73,6 +85,10 @@ public class AndroidKeyStoreProvider extends Provider { PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CTR$NoPadding"); } + private void putSecretKeyFactoryImpl(String algorithm) { + put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi"); + } + private void putMacImpl(String algorithm, String implClass) { put("Mac." + algorithm, implClass); put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME); diff --git a/keystore/java/android/security/ArrayUtils.java b/keystore/java/android/security/ArrayUtils.java new file mode 100644 index 0000000..2047d3f --- /dev/null +++ b/keystore/java/android/security/ArrayUtils.java @@ -0,0 +1,62 @@ +package android.security; + +import libcore.util.EmptyArray; + +/** + * @hide + */ +abstract class ArrayUtils { + private ArrayUtils() {} + + public static String[] nullToEmpty(String[] array) { + return (array != null) ? array : EmptyArray.STRING; + } + + public static String[] cloneIfNotEmpty(String[] array) { + return ((array != null) && (array.length > 0)) ? array.clone() : array; + } + + public static byte[] concat(byte[] arr1, byte[] arr2) { + return concat(arr1, 0, (arr1 != null) ? arr1.length : 0, + arr2, 0, (arr2 != null) ? arr2.length : 0); + } + + public static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2, + int len2) { + if (len1 == 0) { + return subarray(arr2, offset2, len2); + } else if (len2 == 0) { + return subarray(arr1, offset1, len1); + } else { + byte[] result = new byte[len1 + len2]; + System.arraycopy(arr1, offset1, result, 0, len1); + System.arraycopy(arr2, offset2, result, len1, len2); + return result; + } + } + + public static byte[] subarray(byte[] arr, int offset, int len) { + if (len == 0) { + return EmptyArray.BYTE; + } + if ((offset == 0) && (len == arr.length)) { + return arr; + } + byte[] result = new byte[len]; + System.arraycopy(arr, offset, result, 0, len); + return result; + } + + public static int[] concat(int[] arr1, int[] arr2) { + if ((arr1 == null) || (arr1.length == 0)) { + return arr2; + } else if ((arr2 == null) || (arr2.length == 0)) { + return arr1; + } else { + int[] result = new int[arr1.length + arr2.length]; + System.arraycopy(arr1, 0, result, 0, arr1.length); + System.arraycopy(arr2, 0, result, arr1.length, arr2.length); + return result; + } + } +} diff --git a/keystore/java/android/security/CryptoOperationException.java b/keystore/java/android/security/CryptoOperationException.java index 00c142f..1c9d005 100644 --- a/keystore/java/android/security/CryptoOperationException.java +++ b/keystore/java/android/security/CryptoOperationException.java @@ -25,8 +25,6 @@ package android.security; * permitted to throw a checked exception during operation. Because crypto operations can fail * for a variety of reasons after initialization, this base class provides type-safety for unchecked * exceptions that may be thrown in those cases. - * - * @hide */ public class CryptoOperationException extends RuntimeException { diff --git a/keystore/java/android/security/EcIesParameterSpec.java b/keystore/java/android/security/EcIesParameterSpec.java new file mode 100644 index 0000000..a3e5aec --- /dev/null +++ b/keystore/java/android/security/EcIesParameterSpec.java @@ -0,0 +1,262 @@ +package android.security; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.Cipher; +import javax.crypto.Mac; + +/** + * {@link AlgorithmParameterSpec} for ECIES (Integrated Encryption Scheme using Elliptic Curve + * cryptography) based on {@code ISO/IEC 18033-2}. + * + * <p>ECIES is a hybrid authenticated encryption scheme. Encryption is performed using an Elliptic + * Curve (EC) public key. The resulting ciphertext can be decrypted only using the corresponding EC + * private key. The scheme is called hybrid because the EC key is only used to securely encapsulate + * symmetric key material. Encryption of plaintext and authentication of the corresponding + * ciphertext is performed using symmetric cryptography. + * + * <p>Encryption using ECIES consists of two stages: + * <ol> + * <li>Key Encapsulation Mechanism (KEM) randomly generates symmetric key material and securely + * encapsulates it in the output so that it can be extracted by the KEM when decrypting. + * Encapsulated key material is represented in the output as an EC point.</li> + * <li>The above symmetric key material is used by Data Encapsulation Mechanism (DEM) to encrypt the + * provided plaintext and authenticate the ciphertext. The resulting authenticated ciphertext is + * then output. When decrypting, the DEM first authenticates the ciphertext and, only if it + * authenticates, decrypts the ciphertext and outputs the plaintext.</li> + * </ol> + * + * <p>Details of KEM: + * <ul> + * <li>Only curves with cofactor of {@code 1} are supported.</li> + * <li>{@code CheckMode}, {@code OldCofactorMode}, {@code CofactorMode}, and {@code SingleHashMode} + * are {@code 0}. + * <li>Point format is specified by {@link #getKemPointFormat()}.</li> + * <li>KDF algorithm is specified by {@link #getKemKdfAlgorithm()}.</li> + * </ul> + * + * <p>Details of DEM: + * <ul> + * <li>Only DEM1-like mechanism is supported, with its symmetric cipher (SC) specified by + * {@link #getDemCipherTransformation()} (e.g., {@code AES/CBC/NoPadding} for standard DEM1) and + * MAC algorithm specified by {@link #getDemMacAlgorithm()} (e.g., {@code HmacSHA1} for standard + * DEM1).</li> + * </ul> + */ +public class EcIesParameterSpec implements AlgorithmParameterSpec { + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = {PointFormat.UNCOMPRESSED, PointFormat.COMPRESSED}) + public @interface PointFormatEnum {} + + /** + * Wire format of the EC point. + */ + public static abstract class PointFormat { + + private PointFormat() {} + + /** Unspecified point format. */ + public static final int UNSPECIFIED = -1; + + /** + * Uncompressed point format: both coordinates are stored separately. + * + * <p>The wire format is byte {@code 0x04} followed by binary representation of the + * {@code x} coordinate followed by binary representation of the {@code y} coordinate. See + * {@code ISO 18033-2} section {@code 5.4.3}. + */ + public static final int UNCOMPRESSED = 0; + + /** + * Compressed point format: only one coordinate is stored. + * + * <p>The wire format is byte {@code 0x02} or {@code 0x03} (depending on the value of the + * stored coordinate) followed by the binary representation of the {@code x} coordinate. + * See {@code ISO 18033-2} section {@code 5.4.3}. + */ + public static final int COMPRESSED = 1; + } + + /** + * Default parameter spec: compressed point format, {@code HKDFwithSHA256}, DEM uses 128-bit AES + * GCM. + */ + public static final EcIesParameterSpec DEFAULT = new EcIesParameterSpec( + PointFormat.COMPRESSED, + "HKDFwithSHA256", + "AES/GCM/NoPadding", + 128, + null, + 0); + + private final @PointFormatEnum int mKemPointFormat; + private final String mKemKdfAlgorithm; + private final String mDemCipherTransformation; + private final int mDemCipherKeySize; + private final String mDemMacAlgorithm; + private final int mDemMacKeySize; + + private EcIesParameterSpec( + @PointFormatEnum int kemPointFormat, + String kemKdfAlgorithm, + String demCipherTransformation, + int demCipherKeySize, + String demMacAlgorithm, + int demMacKeySize) { + mKemPointFormat = kemPointFormat; + mKemKdfAlgorithm = kemKdfAlgorithm; + mDemCipherTransformation = demCipherTransformation; + mDemCipherKeySize = demCipherKeySize; + mDemMacAlgorithm = demMacAlgorithm; + mDemMacKeySize = demMacKeySize; + } + + /** + * Returns KEM EC point wire format or {@link PointFormat#UNSPECIFIED} if not specified. + */ + public @PointFormatEnum int getKemPointFormat() { + return mKemPointFormat; + } + + /** + * Returns KEM KDF algorithm (e.g., {@code HKDFwithSHA256} or {@code KDF1withSHA1}) or + * {@code null} if not specified. + */ + public String getKemKdfAlgorithm() { + return mKemKdfAlgorithm; + } + + /** + * Returns DEM {@link Cipher} transformation (e.g., {@code AES/GCM/NoPadding} or + * {@code AES/CBC/PKCS7Padding}) or {@code null} if not specified. + * + * @see Cipher#getInstance(String) + * @see #getDemCipherKeySize() + */ + public String getDemCipherTransformation() { + return mDemCipherTransformation; + } + + /** + * Returns DEM {@link Cipher} key size in bits. + * + * @see #getDemCipherTransformation() + */ + public int getDemCipherKeySize() { + return mDemCipherKeySize; + } + + /** + * Returns DEM {@link Mac} algorithm (e.g., {@code HmacSHA256} or {@code HmacSHA1}) or + * {@code null} if not specified. + * + * @see Mac#getInstance(String) + * @see #getDemMacKeySize() + */ + public String getDemMacAlgorithm() { + return mDemMacAlgorithm; + } + + /** + * Returns DEM {@link Mac} key size in bits. + * + * @see #getDemCipherTransformation() + */ + public int getDemMacKeySize() { + return mDemMacKeySize; + } + + /** + * Builder of {@link EcIesParameterSpec}. + */ + public static class Builder { + private @PointFormatEnum int mKemPointFormat = PointFormat.UNSPECIFIED; + private String mKemKdfAlgorithm; + private String mDemCipherTransformation; + private int mDemCipherKeySize = 128; + private String mDemMacAlgorithm; + private int mDemMacKeySize = -1; + + /** + * Sets KEM EC point wire format. + */ + public Builder setKemPointFormat(@PointFormatEnum int pointFormat) { + mKemPointFormat = pointFormat; + return this; + } + + /** + * Sets KEM KDF algorithm. For example, {@code HKDFwithSHA256}, {@code KDF2withSHA256}, or + * {@code KDF1withSHA1}. + */ + public Builder setKemKdfAlgorithm(String algorithm) { + mKemKdfAlgorithm = algorithm; + return this; + } + + /** + * Sets DEM {@link Cipher} transformation. For example, {@code AES/GCM/NoPadding}, + * {@code AES/CBC/PKCS7Padding} or {@code AES/CTR/NoPadding}. + * + * @see Cipher#getInstance(String) + */ + public Builder setDemCipherTransformation(String transformation) { + mDemCipherTransformation = transformation; + return this; + } + + /** + * Returns DEM {@link Cipher} key size in bits. + * + * <p>The default value is {@code 128} bits. + * + * @see #setDemCipherTransformation(String) + */ + public Builder setDemCipherKeySize(int sizeBits) { + mDemCipherKeySize = sizeBits; + return this; + } + + /** + * Sets DEM {@link Mac} algorithm. For example, {@code HmacSHA256} or {@code HmacSHA1}. + * + * @see Mac#getInstance(String) + */ + public Builder setDemMacAlgorithm(String algorithm) { + mDemMacAlgorithm = algorithm; + return this; + } + + /** + * Sets DEM {@link Mac} key size in bits. + * + * <p>By default, {@code Mac} key size is the same as the {@code Cipher} key size. + * + * @see #setDemCipherKeySize(int) + */ + public Builder setDemMacKeySize(int sizeBits) { + mDemMacKeySize = sizeBits; + return this; + } + + /** + * Returns a new {@link EcIesParameterSpec} based on the current state of this builder. + */ + public EcIesParameterSpec build() { + int demMacKeySize = (mDemMacKeySize != -1) ? mDemMacKeySize : mDemCipherKeySize; + return new EcIesParameterSpec( + mKemPointFormat, + mKemKdfAlgorithm, + mDemCipherTransformation, + mDemCipherKeySize, + mDemMacAlgorithm, + demMacKeySize + ); + } + } +} diff --git a/keystore/java/android/security/GateKeeper.java b/keystore/java/android/security/GateKeeper.java new file mode 100644 index 0000000..c9f06e9 --- /dev/null +++ b/keystore/java/android/security/GateKeeper.java @@ -0,0 +1,30 @@ +package android.security; + +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.service.gatekeeper.IGateKeeperService; + +/** + * Convenience class for accessing the gatekeeper service. + * + * @hide + */ +public abstract class GateKeeper { + + private GateKeeper() {} + + public static IGateKeeperService getService() { + return IGateKeeperService.Stub.asInterface( + ServiceManager.getService("android.service.gatekeeper.IGateKeeperService")); + } + + public static long getSecureUserId() throws IllegalStateException { + try { + return GateKeeper.getService().getSecureUserId(UserHandle.myUserId()); + } catch (RemoteException e) { + throw new IllegalStateException( + "Failed to obtain secure user ID from gatekeeper", e); + } + } +} diff --git a/keystore/java/android/security/KeyExpiredException.java b/keystore/java/android/security/KeyExpiredException.java index 35a5acc..a02dc33 100644 --- a/keystore/java/android/security/KeyExpiredException.java +++ b/keystore/java/android/security/KeyExpiredException.java @@ -19,8 +19,6 @@ package android.security; /** * Indicates that a cryptographic operation failed because the employed key's validity end date * is in the past. - * - * @hide */ public class KeyExpiredException extends CryptoOperationException { diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 0e490cd..7ecc47e 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -22,6 +22,7 @@ import android.text.TextUtils; import java.security.spec.AlgorithmParameterSpec; import java.util.Date; +import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -36,22 +37,21 @@ import javax.crypto.SecretKey; * <p>After generation, the {@code keyStoreAlias} is used with the * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)} * interface to retrieve the {@link SecretKey}. - * - * @hide */ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private final Context mContext; private final String mKeystoreAlias; private final int mFlags; - private final Integer mKeySize; + private final int mKeySize; private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; - private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; - private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; - private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; - private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private final String[] mEncryptionPaddings; + private final String[] mBlockModes; + private final boolean mRandomizedEncryptionRequired; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; private final boolean mInvalidatedOnNewFingerprintEnrolled; @@ -59,14 +59,15 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { Context context, String keyStoreAlias, int flags, - Integer keySize, + int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum int purposes, - @KeyStoreKeyConstraints.PaddingEnum int paddings, - @KeyStoreKeyConstraints.BlockModeEnum int blockModes, - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] encryptionPaddings, + String[] blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds, boolean invalidatedOnNewFingerprintEnrolled) { if (context == null) { @@ -87,8 +88,10 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; mPurposes = purposes; - mPaddings = paddings; - mBlockModes = blockModes; + mEncryptionPaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); + mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled; @@ -117,9 +120,9 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** - * Gets the requested key size or {@code null} if the default size should be used. + * Returns the requested key size or {@code -1} if default size should be used. */ - public Integer getKeySize() { + public int getKeySize() { return mKeySize; } @@ -153,22 +156,35 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of purposes for which the key can be used. */ - public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { + public @KeyStoreKeyProperties.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the set of padding schemes to which the key is restricted. + * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. + */ + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); + } + + /** + * Gets the set of block modes with which the key can be used. */ - public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { - return mPaddings; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(mBlockModes); } /** - * Gets the set of block modes to which the key is restricted. + * Returns {@code true} if encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic property + * being required is <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. This property is important because it mitigates several classes of + * weaknesses due to which ciphertext may leak information about plaintext. For example, if a + * given plaintext always produces the same ciphertext, an attacker may see the repeated + * ciphertexts and be able to deduce something about the plaintext. */ - public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { - return mBlockModes; + public boolean isRandomizedEncryptionRequired() { + return mRandomizedEncryptionRequired; } /** @@ -177,7 +193,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @return user authenticators or {@code 0} if the key can be used without user authentication. */ - public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + public @KeyStoreKeyProperties.UserAuthenticatorEnum int getUserAuthenticators() { return mUserAuthenticators; } @@ -198,8 +214,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * authenticators protecting access to this key. * * @see #getUserAuthenticators() - * - * @hide */ public boolean isInvalidatedOnNewFingerprintEnrolled() { return mInvalidatedOnNewFingerprintEnrolled; @@ -216,14 +230,15 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { private final Context mContext; private String mKeystoreAlias; private int mFlags; - private Integer mKeySize; + private int mKeySize = -1; private Date mKeyValidityStart; private Date mKeyValidityForOriginationEnd; private Date mKeyValidityForConsumptionEnd; - private @KeyStoreKeyConstraints.PurposeEnum int mPurposes; - private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; - private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; - private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private String[] mEncryptionPaddings; + private String[] mBlockModes; + private boolean mRandomizedEncryptionRequired = true; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; private boolean mInvalidatedOnNewFingerprintEnrolled; @@ -281,7 +296,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant before which the key is not yet valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityEnd(Date) */ @@ -293,7 +308,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant after which the key is no longer valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) @@ -308,7 +323,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant after which the key is no longer valid for encryption and signing. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForConsumptionEnd(Date) */ @@ -321,7 +336,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * Sets the time instant after which the key is no longer valid for decryption and * verification. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForOriginationEnd(Date) */ @@ -331,34 +346,72 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** - * Restricts the key to being used only for the provided set of purposes. + * Sets the set of purposes for which the key can be used. * - * <p>This restriction must be specified. There is no default. + * <p>This must be specified for all keys. There is no default. */ - public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { + public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; } /** - * Restricts the key to being used only with the provided padding schemes. Attempts to use - * the key with any other padding will be rejected. + * Sets the set of padding schemes with which the key can be used when + * encrypting/decrypting. Attempts to use the key with any other padding scheme will be + * rejected. + * + * <p>This must be specified for keys which are used for encryption/decryption. + */ + public Builder setEncryptionPaddings(String... paddings) { + mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); + return this; + } + + /** + * Sets the set of block modes with which the key can be used when encrypting/decrypting. + * Attempts to use the key with any other block modes will be rejected. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. + * <p>This must be specified for encryption/decryption keys. */ - public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) { - mPaddings = paddings; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; } /** - * Restricts the key to being used only with the provided block modes. Attempts to use the - * key with any other block modes will be rejected. + * Sets whether encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic + * property being required is <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. This property is important because it mitigates several classes + * of weaknesses due to which ciphertext may leak information about plaintext. For example, + * if a given plaintext always produces the same ciphertext, an attacker may see the + * repeated ciphertexts and be able to deduce something about the plaintext. + * + * <p>By default, {@code IND-CPA} is required. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. + * <p>When {@code IND-CPA} is required: + * <ul> + * <li>block modes which do not offer {@code IND-CPA}, such as {@code ECB}, are prohibited; + * </li> + * <li>in block modes which use an IV, such as {@code CBC}, {@code CTR}, and {@code GCM}, + * caller-provided IVs are rejected when encrypting, to ensure that only random IVs are + * used.</li> + * + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <li>If you are generating a random IV for encryption and then initializing a {@code} + * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV + * instead. This will occur if the {@code Cipher} is initialized for encryption without an + * IV. The IV can then be queried via {@link Cipher#getIV()}.</li> + * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully + * random, such as the name of the file being encrypted, or transaction ID, or password, + * or a device identifier), consider changing your design to use a random IV which will then + * be provided in addition to the ciphertext to the entities which need to decrypt the + * ciphertext.</li> + * </ul> */ - public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { - mBlockModes = blockModes; + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; return this; } @@ -374,7 +427,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @see #setUserAuthenticationValidityDurationSeconds(int) */ public Builder setUserAuthenticators( - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { mUserAuthenticators = userAuthenticators; return this; } @@ -403,8 +456,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * <p>By default, enrolling a new fingerprint does not invalidate the key. * * @see #setUserAuthenticators(Set) - * - * @hide */ public Builder setInvalidatedOnNewFingerprintEnrolled(boolean invalidated) { mInvalidatedOnNewFingerprintEnrolled = invalidated; @@ -425,8 +476,9 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, - mPaddings, + mEncryptionPaddings, mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds, mInvalidatedOnNewFingerprintEnrolled); diff --git a/keystore/java/android/security/KeyNotYetValidException.java b/keystore/java/android/security/KeyNotYetValidException.java index f1c2cac..964cd7e 100644 --- a/keystore/java/android/security/KeyNotYetValidException.java +++ b/keystore/java/android/security/KeyNotYetValidException.java @@ -19,8 +19,6 @@ package android.security; /** * Indicates that a cryptographic operation failed because the employed key's validity start date * is in the future. - * - * @hide */ public class KeyNotYetValidException extends CryptoOperationException { diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index 52b7097..5e5cf37 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -52,6 +52,11 @@ import javax.security.auth.x500.X500Principal; */ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { + private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake"); + private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1"); + private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970 + private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048 + private final Context mContext; private final String mKeystoreAlias; @@ -78,15 +83,19 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private final Date mKeyValidityForConsumptionEnd; - private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; + private final @KeyStoreKeyProperties.PurposeEnum int mPurposes; + + private final String[] mDigests; + + private final String[] mEncryptionPaddings; - private final @KeyStoreKeyConstraints.DigestEnum int mDigests; + private final String[] mSignaturePaddings; - private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private final String[] mBlockModes; - private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final boolean mRandomizedEncryptionRequired; - private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; @@ -130,33 +139,42 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum int purposes, - @KeyStoreKeyConstraints.DigestEnum int digests, - @KeyStoreKeyConstraints.PaddingEnum int paddings, - @KeyStoreKeyConstraints.BlockModeEnum int blockModes, - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] digests, + String[] encryptionPaddings, + String[] signaturePaddings, + String[] blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds, boolean invalidatedOnNewFingerprintEnrolled) { if (context == null) { throw new IllegalArgumentException("context == null"); } else if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); - } else if (subjectDN == null) { - throw new IllegalArgumentException("subjectDN == null"); - } else if (serialNumber == null) { - throw new IllegalArgumentException("serialNumber == null"); - } else if (startDate == null) { - throw new IllegalArgumentException("startDate == null"); - } else if (endDate == null) { - throw new IllegalArgumentException("endDate == null"); - } else if (endDate.before(startDate)) { - throw new IllegalArgumentException("endDate < startDate"); } else if ((userAuthenticationValidityDurationSeconds < 0) && (userAuthenticationValidityDurationSeconds != -1)) { throw new IllegalArgumentException( "userAuthenticationValidityDurationSeconds must not be negative"); } + if (subjectDN == null) { + subjectDN = DEFAULT_CERT_SUBJECT; + } + if (startDate == null) { + startDate = DEFAULT_CERT_NOT_BEFORE; + } + if (endDate == null) { + endDate = DEFAULT_CERT_NOT_AFTER; + } + if (serialNumber == null) { + serialNumber = DEFAULT_CERT_SERIAL_NUMBER; + } + + if (endDate.before(startDate)) { + throw new IllegalArgumentException("endDate < startDate"); + } + mContext = context; mKeystoreAlias = keyStoreAlias; mKeyType = keyType; @@ -171,9 +189,12 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; mPurposes = purposes; - mDigests = digests; - mPaddings = paddings; - mBlockModes = blockModes; + mDigests = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(digests)); + mEncryptionPaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); + mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); + mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled; @@ -186,8 +207,30 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize, AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate, int flags) { - this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate, - endDate, flags, startDate, endDate, endDate, 0, 0, 0, 0, 0, -1, false); + + this(context, + keyStoreAlias, + keyType, + keySize, + spec, + subjectDN, + serialNumber, + startDate, + endDate, + flags, + startDate, + endDate, + endDate, + 0, // purposes + null, // digests + null, // encryption paddings + null, // signature paddings + null, // block modes + false, // randomized encryption required + 0, // user authenticators + -1, // user authentication validity duration (seconds) + false // invalidate on new fingerprint enrolled + ); } /** @@ -280,8 +323,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Gets the time instant before which the key pair is not yet valid. * * @return instant or {@code null} if not restricted. - * - * @hide */ public Date getKeyValidityStart() { return mKeyValidityStart; @@ -292,8 +333,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * verification. * * @return instant or {@code null} if not restricted. - * - * @hide */ public Date getKeyValidityForConsumptionEnd() { return mKeyValidityForConsumptionEnd; @@ -303,8 +342,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Gets the time instant after which the key pair is no longer valid for encryption and signing. * * @return instant or {@code null} if not restricted. - * - * @hide */ public Date getKeyValidityForOriginationEnd() { return mKeyValidityForOriginationEnd; @@ -312,38 +349,50 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of purposes for which the key can be used. - * - * @hide */ - public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { + public @KeyStoreKeyProperties.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the set of digests to which the key is restricted. - * - * @hide + * Gets the set of digest algorithms with which the key can be used. */ - public @KeyStoreKeyConstraints.DigestEnum int getDigests() { - return mDigests; + public String[] getDigests() { + return ArrayUtils.cloneIfNotEmpty(mDigests); } /** - * Gets the set of padding schemes to which the key is restricted. - * - * @hide + * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. */ - public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { - return mPaddings; + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** - * Gets the set of block modes to which the key is restricted. - * - * @hide + * Gets the set of padding schemes with which the key can be used when signing/verifying. + */ + public String[] getSignaturePaddings() { + return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); + } + + /** + * Gets the set of block modes with which the key can be used. */ - public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { - return mBlockModes; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(mBlockModes); + } + + /** + * Returns {@code true} if encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic property + * being required is <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. This property is important because it mitigates several classes of + * weaknesses due to which ciphertext may leak information about plaintext. For example, if a + * given plaintext always produces the same ciphertext, an attacker may see the repeated + * ciphertexts and be able to deduce something about the plaintext. + */ + public boolean isRandomizedEncryptionRequired() { + return mRandomizedEncryptionRequired; } /** @@ -354,10 +403,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * restricted. * * @return user authenticators or {@code 0} if the key can be used without user authentication. - * - * @hide */ - public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + public @KeyStoreKeyProperties.UserAuthenticatorEnum int getUserAuthenticators() { return mUserAuthenticators; } @@ -370,8 +417,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication * is required for every use of the key. - * - * @hide */ public int getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; @@ -383,8 +428,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * authenticators protecting access to this key. * * @see #getUserAuthenticators() - * - * @hide */ public boolean isInvalidatedOnNewFingerprintEnrolled() { return mInvalidatedOnNewFingerprintEnrolled; @@ -438,15 +481,19 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private Date mKeyValidityForConsumptionEnd; - private @KeyStoreKeyConstraints.PurposeEnum int mPurposes; + private @KeyStoreKeyProperties.PurposeEnum int mPurposes; + + private String[] mDigests; + + private String[] mEncryptionPaddings; - private @KeyStoreKeyConstraints.DigestEnum int mDigests; + private String[] mSignaturePaddings; - private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private String[] mBlockModes; - private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private boolean mRandomizedEncryptionRequired = true; - private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; @@ -521,6 +568,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the subject used for the self-signed certificate of the * generated key pair. + * + * <p>The subject must be specified on API Level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On + * newer platforms the subject defaults to {@code CN=fake} if not specified. */ public Builder setSubject(X500Principal subject) { if (subject == null) { @@ -533,6 +584,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the serial number used for the self-signed certificate of the * generated key pair. + * + * <p>The serial number must be specified on API Level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On + * newer platforms the serial number defaults to {@code 1} if not specified. */ public Builder setSerialNumber(BigInteger serialNumber) { if (serialNumber == null) { @@ -545,6 +600,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the start of the validity period for the self-signed certificate * of the generated key pair. + * + * <p>The date must be specified on API Level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On + * newer platforms the date defaults to {@code Jan 1 1970} if not specified. */ public Builder setStartDate(Date startDate) { if (startDate == null) { @@ -557,6 +616,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the end of the validity period for the self-signed certificate * of the generated key pair. + * + * <p>The date must be specified on API Level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On + * newer platforms the date defaults to {@code Jan 1 2048} if not specified. */ public Builder setEndDate(Date endDate) { if (endDate == null) { @@ -580,11 +643,9 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant before which the key is not yet valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityEnd(Date) - * - * @hide */ public Builder setKeyValidityStart(Date startDate) { mKeyValidityStart = startDate; @@ -594,13 +655,11 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant after which the key is no longer valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityEnd(Date endDate) { setKeyValidityForOriginationEnd(endDate); @@ -611,11 +670,9 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the time instant after which the key is no longer valid for encryption and signing. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForConsumptionEnd(Date) - * - * @hide */ public Builder setKeyValidityForOriginationEnd(Date endDate) { mKeyValidityForOriginationEnd = endDate; @@ -626,11 +683,9 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Sets the time instant after which the key is no longer valid for decryption and * verification. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityForConsumptionEnd(Date endDate) { mKeyValidityForConsumptionEnd = endDate; @@ -638,53 +693,83 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** - * Restricts the key to being used only for the provided set of purposes. + * Sets the set of purposes for which the key can be used. * - * <p>This restriction must be specified. There is no default. - * - * @hide + * <p>This must be specified for all keys. There is no default. */ - public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { + public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; } /** - * Restricts the key to being used only with the provided digests. Attempts to use the key - * with any other digests be rejected. - * - * <p>This restriction must be specified for keys which are used for signing/verification. + * Sets the set of digests with which the key can be used when signing/verifying. Attempts + * to use the key with any other digest will be rejected. * - * @hide + * <p>This must be specified for keys which are used for signing/verification. */ - public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) { - mDigests = digests; + public Builder setDigests(String... digests) { + mDigests = ArrayUtils.cloneIfNotEmpty(digests); return this; } /** - * Restricts the key to being used only with the provided padding schemes. Attempts to use - * the key with any other padding will be rejected. + * Sets the set of padding schemes with which the key can be used when + * encrypting/decrypting. Attempts to use the key with any other padding scheme will be + * rejected. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. - * - * @hide + * <p>This must be specified for keys which are used for encryption/decryption. */ - public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) { - mPaddings = paddings; + public Builder setEncryptionPaddings(String... paddings) { + mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } /** - * Restricts the key to being used only with the provided block mode when encrypting or - * decrypting. Attempts to use the key with any other block modes will be rejected. + * Sets the set of padding schemes with which the key can be used when + * signing/verifying. Attempts to use the key with any other padding scheme will be + * rejected. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. + * <p>This must be specified for RSA keys which are used for signing/verification. + */ + public Builder setSignaturePaddings(String... paddings) { + mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); + return this; + } + + /** + * Sets the set of block modes with which the key can be used when encrypting/decrypting. + * Attempts to use the key with any other block modes will be rejected. * - * @hide + * <p>This must be specified for encryption/decryption keys. */ - public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { - mBlockModes = blockModes; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); + return this; + } + + /** + * Sets whether encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic + * property being required is <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. This property is important because it mitigates several classes + * of weaknesses due to which ciphertext may leak information about plaintext. For example, + * if a given plaintext always produces the same ciphertext, an attacker may see the + * repeated ciphertexts and be able to deduce something about the plaintext. + * + * <p>By default, {@code IND-CPA} is required. + * + * <p>When {@code IND-CPA} is required, encryption/decryption transformations which do not + * offer {@code IND-CPA}, such as RSA without padding, are prohibited. + * + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <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> + */ + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; return this; } @@ -701,11 +786,9 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * without user authentication. * * @see #setUserAuthenticationValidityDurationSeconds(int) - * - * @hide */ public Builder setUserAuthenticators( - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { mUserAuthenticators = userAuthenticators; return this; } @@ -723,8 +806,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * every use of the key. * * @see #setUserAuthenticators(int) - * - * @hide */ public Builder setUserAuthenticationValidityDurationSeconds(int seconds) { mUserAuthenticationValidityDurationSeconds = seconds; @@ -739,8 +820,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * <p>By default, enrolling a new fingerprint does not invalidate the key. * * @see #setUserAuthenticators(Set) - * - * @hide */ public Builder setInvalidatedOnNewFingerprintEnrolled(boolean invalidated) { mInvalidatedOnNewFingerprintEnrolled = invalidated; @@ -769,8 +848,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForConsumptionEnd, mPurposes, mDigests, - mPaddings, + mEncryptionPaddings, + mSignaturePaddings, mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds, mInvalidatedOnNewFingerprintEnrolled); diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java index ec358d6..37e00b2 100644 --- a/keystore/java/android/security/KeyStoreCipherSpi.java +++ b/keystore/java/android/security/KeyStoreCipherSpi.java @@ -48,70 +48,71 @@ import javax.crypto.spec.IvParameterSpec; public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCryptoOperation { public abstract static class AES extends KeyStoreCipherSpi { - protected AES(@KeyStoreKeyConstraints.BlockModeEnum int blockMode, - @KeyStoreKeyConstraints.PaddingEnum int padding, boolean ivUsed) { - super(KeyStoreKeyConstraints.Algorithm.AES, - blockMode, - padding, + protected AES(int keymasterBlockMode, int keymasterPadding, boolean ivUsed) { + super(KeymasterDefs.KM_ALGORITHM_AES, + keymasterBlockMode, + keymasterPadding, 16, ivUsed); } public abstract static class ECB extends AES { - protected ECB(@KeyStoreKeyConstraints.PaddingEnum int padding) { - super(KeyStoreKeyConstraints.BlockMode.ECB, padding, false); + protected ECB(int keymasterPadding) { + super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false); } public static class NoPadding extends ECB { public NoPadding() { - super(KeyStoreKeyConstraints.Padding.NONE); + super(KeymasterDefs.KM_PAD_NONE); } } public static class PKCS7Padding extends ECB { public PKCS7Padding() { - super(KeyStoreKeyConstraints.Padding.PKCS7); + super(KeymasterDefs.KM_PAD_PKCS7); } } } public abstract static class CBC extends AES { - protected CBC(@KeyStoreKeyConstraints.BlockModeEnum int padding) { - super(KeyStoreKeyConstraints.BlockMode.CBC, padding, true); + protected CBC(int keymasterPadding) { + super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true); } public static class NoPadding extends CBC { public NoPadding() { - super(KeyStoreKeyConstraints.Padding.NONE); + super(KeymasterDefs.KM_PAD_NONE); } } public static class PKCS7Padding extends CBC { public PKCS7Padding() { - super(KeyStoreKeyConstraints.Padding.PKCS7); + super(KeymasterDefs.KM_PAD_PKCS7); } } } public abstract static class CTR extends AES { - protected CTR(@KeyStoreKeyConstraints.BlockModeEnum int padding) { - super(KeyStoreKeyConstraints.BlockMode.CTR, padding, true); + protected CTR(int keymasterPadding) { + super(KeymasterDefs.KM_MODE_CTR, keymasterPadding, true); } public static class NoPadding extends CTR { public NoPadding() { - super(KeyStoreKeyConstraints.Padding.NONE); + super(KeymasterDefs.KM_PAD_NONE); } } } } private final KeyStore mKeyStore; - private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm; - private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockMode; - private final @KeyStoreKeyConstraints.PaddingEnum int mPadding; + private final int mKeymasterAlgorithm; + private final int mKeymasterBlockMode; + private final int mKeymasterPadding; private final int mBlockSizeBytes; - private final boolean mIvUsed; + + /** Whether this transformation requires an IV. */ + private final boolean mIvRequired; // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after // doFinal finishes. @@ -119,10 +120,13 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry private KeyStoreSecretKey mKey; private SecureRandom mRng; private boolean mFirstOperationInitiated; - byte[] mIv; + private byte[] mIv; + /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */ + private boolean mIvHasBeenUsed; - // Fields below must be reset + // Fields below must be reset after doFinal private byte[] mAdditionalEntropyForBegin; + /** * Token referencing this operation inside keystore service. It is initialized by * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and one some @@ -133,17 +137,17 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer; protected KeyStoreCipherSpi( - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, - @KeyStoreKeyConstraints.BlockModeEnum int blockMode, - @KeyStoreKeyConstraints.PaddingEnum int padding, + int keymasterAlgorithm, + int keymasterBlockMode, + int keymasterPadding, int blockSizeBytes, boolean ivUsed) { mKeyStore = KeyStore.getInstance(); - mAlgorithm = algorithm; - mBlockMode = blockMode; - mPadding = padding; + mKeymasterAlgorithm = keymasterAlgorithm; + mKeymasterBlockMode = keymasterBlockMode; + mKeymasterPadding = keymasterPadding; mBlockSizeBytes = blockSizeBytes; - mIvUsed = ivUsed; + mIvRequired = ivUsed; } @Override @@ -170,7 +174,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { - reset(); + resetAll(); if (!(key instanceof KeyStoreSecretKey)) { throw new InvalidKeyException( "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null")); @@ -187,7 +191,25 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry mEncrypting = opmode == Cipher.ENCRYPT_MODE; } - private void reset() { + private void resetAll() { + IBinder operationToken = mOperationToken; + if (operationToken != null) { + mOperationToken = null; + mKeyStore.abort(operationToken); + } + mEncrypting = false; + mKey = null; + mRng = null; + mFirstOperationInitiated = false; + mIv = null; + mIvHasBeenUsed = false; + mAdditionalEntropyForBegin = null; + mOperationToken = null; + mOperationHandle = null; + mMainDataStreamer = null; + } + + private void resetWhilePreservingInitState() { IBinder operationToken = mOperationToken; if (operationToken != null) { mOperationToken = null; @@ -205,11 +227,17 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry if (mKey == null) { throw new IllegalStateException("Not initialized"); } + if ((mEncrypting) && (mIvRequired) && (mIvHasBeenUsed)) { + // IV is being reused for encryption: this violates security best practices. + throw new IllegalStateException( + "IV has already been used. Reusing IV in encryption mode violates security best" + + " practices."); + } KeymasterArguments keymasterInputArgs = new KeymasterArguments(); - keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mAlgorithm); - keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mBlockMode); - keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mPadding); + keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); + keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode); + keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); addAlgorithmSpecificParametersToBegin(keymasterInputArgs); KeymasterArguments keymasterOutputArgs = new KeymasterArguments(); @@ -234,6 +262,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry mOperationHandle = opResult.operationHandle; loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs); mFirstOperationInitiated = true; + mIvHasBeenUsed = true; mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer( new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( mKeyStore, opResult.token)); @@ -298,7 +327,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } } - reset(); + resetWhilePreservingInitState(); return output; } @@ -376,7 +405,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry */ @Override protected AlgorithmParameters engineGetParameters() { - if (!mIvUsed) { + if (!mIvRequired) { return null; } if ((mIv != null) && (mIv.length > 0)) { @@ -408,7 +437,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry */ protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { - if (!mIvUsed) { + if (!mIvRequired) { if (params != null) { throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); } @@ -447,7 +476,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry */ protected void initAlgorithmSpecificParameters(AlgorithmParameters params) throws InvalidAlgorithmParameterException { - if (!mIvUsed) { + if (!mIvRequired) { if (params != null) { throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); } @@ -492,7 +521,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry * and thus {@code Cipher.init} needs to be invoked with explicitly provided parameters. */ protected void initAlgorithmSpecificParameters() throws InvalidKeyException { - if (!mIvUsed) { + if (!mIvRequired) { return; } @@ -515,26 +544,20 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry if (!mFirstOperationInitiated) { // First begin operation -- see if we need to provide additional entropy for IV // generation. - if (mIvUsed) { + if (mIvRequired) { // IV is needed if ((mIv == null) && (mEncrypting)) { - // TODO: Switch to keymaster-generated IV code below once keymaster supports - // that. - // IV is needed but was not provided by the caller -- generate an IV. - mIv = new byte[mBlockSizeBytes]; - SecureRandom rng = (mRng != null) ? mRng : new SecureRandom(); - rng.nextBytes(mIv); -// // 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); -// } + // 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); + } } } } - if ((mIvUsed) && (mIv != null)) { + if ((mIvRequired) && (mIv != null)) { keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv); } } @@ -557,7 +580,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry returnedIv = null; } - if (mIvUsed) { + if (mIvRequired) { if (mIv == null) { mIv = returnedIv; } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java index 1f8b7e4..aafd2fa 100644 --- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java @@ -19,6 +19,8 @@ package android.security; import android.os.IBinder; import android.security.keymaster.OperationResult; +import libcore.util.EmptyArray; + import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -95,7 +97,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { // Too much input for one chunk -- extract one max-sized chunk and feed it into the // update operation. inputBytesInChunk = mMaxChunkSize - mBufferedLength; - chunk = concat(mBuffered, mBufferedOffset, mBufferedLength, + chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength, input, inputOffset, inputBytesInChunk); } else { // All of available input fits into one chunk. @@ -108,7 +110,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { } else { // Need to combine buffered data with input data into one array. inputBytesInChunk = inputLength; - chunk = concat(mBuffered, mBufferedOffset, mBufferedLength, + chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength, input, inputOffset, inputBytesInChunk); } } @@ -197,7 +199,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { // Flush all buffered input and provided input into keystore/keymaster. byte[] output = update(input, inputOffset, inputLength); - output = concat(output, flush()); + output = ArrayUtils.concat(output, flush()); OperationResult opResult = mKeyStoreStream.finish(); if (opResult == null) { @@ -206,7 +208,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { throw KeyStore.getKeyStoreException(opResult.resultCode); } - return concat(output, opResult.output); + return ArrayUtils.concat(output, opResult.output); } /** @@ -215,11 +217,11 @@ public class KeyStoreCryptoOperationChunkedStreamer { */ public byte[] flush() throws KeyStoreException { if (mBufferedLength <= 0) { - return EMPTY_BYTE_ARRAY; + return EmptyArray.BYTE; } - byte[] chunk = subarray(mBuffered, mBufferedOffset, mBufferedLength); - mBuffered = EMPTY_BYTE_ARRAY; + byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength); + mBuffered = EmptyArray.BYTE; mBufferedLength = 0; mBufferedOffset = 0; @@ -238,46 +240,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed); } - return (opResult.output != null) ? opResult.output : EMPTY_BYTE_ARRAY; - } - - private static byte[] concat(byte[] arr1, byte[] arr2) { - if ((arr1 == null) || (arr1.length == 0)) { - return arr2; - } else if ((arr2 == null) || (arr2.length == 0)) { - return arr1; - } else { - byte[] result = new byte[arr1.length + arr2.length]; - System.arraycopy(arr1, 0, result, 0, arr1.length); - System.arraycopy(arr2, 0, result, arr1.length, arr2.length); - return result; - } - } - - private static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2, - int len2) { - if (len1 == 0) { - return subarray(arr2, offset2, len2); - } else if (len2 == 0) { - return subarray(arr1, offset1, len1); - } else { - byte[] result = new byte[len1 + len2]; - System.arraycopy(arr1, offset1, result, 0, len1); - System.arraycopy(arr2, offset2, result, len1, len2); - return result; - } - } - - private static byte[] subarray(byte[] arr, int offset, int len) { - if (len == 0) { - return EMPTY_BYTE_ARRAY; - } - if ((offset == 0) && (len == arr.length)) { - return arr; - } - byte[] result = new byte[len]; - System.arraycopy(arr, offset, result, 0, len); - return result; + return (opResult.output != null) ? opResult.output : EmptyArray.BYTE; } /** diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java index a5864a4..a19bbda 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -35,14 +35,38 @@ import javax.crypto.MacSpi; */ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation { + public static class HmacSHA1 extends KeyStoreHmacSpi { + public HmacSHA1() { + super(KeymasterDefs.KM_DIGEST_SHA1); + } + } + + public static class HmacSHA224 extends KeyStoreHmacSpi { + public HmacSHA224() { + super(KeymasterDefs.KM_DIGEST_SHA_2_224); + } + } + public static class HmacSHA256 extends KeyStoreHmacSpi { public HmacSHA256() { - super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8); + super(KeymasterDefs.KM_DIGEST_SHA_2_256); + } + } + + public static class HmacSHA384 extends KeyStoreHmacSpi { + public HmacSHA384() { + super(KeymasterDefs.KM_DIGEST_SHA_2_384); + } + } + + public static class HmacSHA512 extends KeyStoreHmacSpi { + public HmacSHA512() { + super(KeymasterDefs.KM_DIGEST_SHA_2_512); } } private final KeyStore mKeyStore = KeyStore.getInstance(); - private final @KeyStoreKeyConstraints.DigestEnum int mDigest; + private final int mKeymasterDigest; private final int mMacSizeBytes; private String mKeyAliasInKeyStore; @@ -52,9 +76,9 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp private IBinder mOperationToken; private Long mOperationHandle; - protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) { - mDigest = digest; - mMacSizeBytes = macSizeBytes; + protected KeyStoreHmacSpi(int keymasterDigest) { + mKeymasterDigest = keymasterDigest; + mMacSizeBytes = KeymasterUtils.getDigestOutputSizeBytes(keymasterDigest); } @Override @@ -105,8 +129,8 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp } KeymasterArguments keymasterArgs = new KeymasterArguments(); - keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeyStoreKeyConstraints.Algorithm.HMAC); - keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mDigest); + keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC); + keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore, KeymasterDefs.KM_PURPOSE_SIGN, diff --git a/keystore/java/android/security/KeyStoreKeyCharacteristics.java b/keystore/java/android/security/KeyStoreKeyCharacteristics.java deleted file mode 100644 index 543b5d8..0000000 --- a/keystore/java/android/security/KeyStoreKeyCharacteristics.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security; - -import android.annotation.IntDef; -import android.security.keymaster.KeymasterDefs; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Characteristics of {@code AndroidKeyStore} keys. - * - * @hide - */ -public abstract class KeyStoreKeyCharacteristics { - private KeyStoreKeyCharacteristics() {} - - @Retention(RetentionPolicy.SOURCE) - @IntDef({Origin.GENERATED_INSIDE_TEE, Origin.GENERATED_OUTSIDE_OF_TEE, Origin.IMPORTED}) - public @interface OriginEnum {} - - /** - * Origin of the key. - */ - public static abstract class Origin { - private Origin() {} - - /** Key was generated inside a TEE. */ - public static final int GENERATED_INSIDE_TEE = 1; - - /** Key was generated outside of a TEE. */ - public static final int GENERATED_OUTSIDE_OF_TEE = 2; - - /** Key was imported. */ - public static final int IMPORTED = 0; - - /** - * @hide - */ - public static @OriginEnum int fromKeymaster(int origin) { - switch (origin) { - case KeymasterDefs.KM_ORIGIN_HARDWARE: - return GENERATED_INSIDE_TEE; - case KeymasterDefs.KM_ORIGIN_SOFTWARE: - return GENERATED_OUTSIDE_OF_TEE; - case KeymasterDefs.KM_ORIGIN_IMPORTED: - return IMPORTED; - default: - throw new IllegalArgumentException("Unknown origin: " + origin); - } - } - } -} diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java deleted file mode 100644 index 7137a9a..0000000 --- a/keystore/java/android/security/KeyStoreKeyConstraints.java +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security; - -import android.annotation.IntDef; -import android.security.keymaster.KeymasterDefs; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Collection; -import java.util.Locale; - -/** - * Constraints for {@code AndroidKeyStore} keys. - * - * @hide - */ -public abstract class KeyStoreKeyConstraints { - private KeyStoreKeyConstraints() {} - - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = {Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY}) - public @interface PurposeEnum {} - - /** - * Purpose of key. - */ - public static abstract class Purpose { - private Purpose() {} - - /** - * Purpose: encryption. - */ - public static final int ENCRYPT = 1 << 0; - - /** - * Purpose: decryption. - */ - public static final int DECRYPT = 1 << 1; - - /** - * Purpose: signing. - */ - public static final int SIGN = 1 << 2; - - /** - * Purpose: signature verification. - */ - public static final int VERIFY = 1 << 3; - - /** - * @hide - */ - public static int toKeymaster(@PurposeEnum int purpose) { - switch (purpose) { - case ENCRYPT: - return KeymasterDefs.KM_PURPOSE_ENCRYPT; - case DECRYPT: - return KeymasterDefs.KM_PURPOSE_DECRYPT; - case SIGN: - return KeymasterDefs.KM_PURPOSE_SIGN; - case VERIFY: - return KeymasterDefs.KM_PURPOSE_VERIFY; - default: - throw new IllegalArgumentException("Unknown purpose: " + purpose); - } - } - - /** - * @hide - */ - public static @PurposeEnum int fromKeymaster(int purpose) { - switch (purpose) { - case KeymasterDefs.KM_PURPOSE_ENCRYPT: - return ENCRYPT; - case KeymasterDefs.KM_PURPOSE_DECRYPT: - return DECRYPT; - case KeymasterDefs.KM_PURPOSE_SIGN: - return SIGN; - case KeymasterDefs.KM_PURPOSE_VERIFY: - return VERIFY; - default: - throw new IllegalArgumentException("Unknown purpose: " + purpose); - } - } - - /** - * @hide - */ - public static int[] allToKeymaster(@PurposeEnum int purposes) { - int[] result = getSetFlags(purposes); - for (int i = 0; i < result.length; i++) { - result[i] = toKeymaster(result[i]); - } - return result; - } - - /** - * @hide - */ - public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) { - @PurposeEnum int result = 0; - for (int keymasterPurpose : purposes) { - result |= fromKeymaster(keymasterPurpose); - } - return result; - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({Algorithm.AES, Algorithm.HMAC}) - public @interface AlgorithmEnum {} - - /** - * Key algorithm. - */ - public static abstract class Algorithm { - private Algorithm() {} - - /** - * Key algorithm: AES. - */ - public static final int AES = 0; - - /** - * Key algorithm: HMAC. - */ - public static final int HMAC = 1; - - /** - * @hide - */ - public static int toKeymaster(@AlgorithmEnum int algorithm) { - switch (algorithm) { - case AES: - return KeymasterDefs.KM_ALGORITHM_AES; - case HMAC: - return KeymasterDefs.KM_ALGORITHM_HMAC; - default: - throw new IllegalArgumentException("Unknown algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static @AlgorithmEnum int fromKeymaster(int algorithm) { - switch (algorithm) { - case KeymasterDefs.KM_ALGORITHM_AES: - return AES; - case KeymasterDefs.KM_ALGORITHM_HMAC: - return HMAC; - default: - throw new IllegalArgumentException("Unknown algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static String toString(@AlgorithmEnum int algorithm) { - switch (algorithm) { - case AES: - return "AES"; - case HMAC: - return "HMAC"; - default: - throw new IllegalArgumentException("Unknown algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static @AlgorithmEnum int fromJCASecretKeyAlgorithm(String algorithm) { - if (algorithm == null) { - throw new NullPointerException("algorithm == null"); - } else if ("AES".equalsIgnoreCase(algorithm)) { - return AES; - } else if (algorithm.toLowerCase(Locale.US).startsWith("hmac")) { - return HMAC; - } else { - throw new IllegalArgumentException( - "Unsupported secret key algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static String toJCASecretKeyAlgorithm(@AlgorithmEnum int algorithm, - @DigestEnum Integer digest) { - switch (algorithm) { - case AES: - return "AES"; - case HMAC: - if (digest == null) { - throw new IllegalArgumentException("HMAC digest not specified"); - } - switch (digest) { - case Digest.SHA256: - return "HmacSHA256"; - default: - throw new IllegalArgumentException( - "Unsupported HMAC digest: " + digest); - } - default: - throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = {Padding.NONE, Padding.PKCS7}) - public @interface PaddingEnum {} - - /** - * Padding for signing and encryption. - */ - public static abstract class Padding { - private Padding() {} - - /** - * No padding. - */ - public static final int NONE = 1 << 0; - - /** - * PKCS#7 padding. - */ - public static final int PKCS7 = 1 << 1; - - /** - * @hide - */ - public static int toKeymaster(int padding) { - switch (padding) { - case NONE: - return KeymasterDefs.KM_PAD_NONE; - case PKCS7: - return KeymasterDefs.KM_PAD_PKCS7; - default: - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - - /** - * @hide - */ - public static @PaddingEnum int fromKeymaster(int padding) { - switch (padding) { - case KeymasterDefs.KM_PAD_NONE: - return NONE; - case KeymasterDefs.KM_PAD_PKCS7: - return PKCS7; - default: - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - - /** - * @hide - */ - public static String toString(@PaddingEnum int padding) { - switch (padding) { - case NONE: - return "NONE"; - case PKCS7: - return "PKCS#7"; - default: - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - - /** - * @hide - */ - public static @PaddingEnum int fromJCAPadding(String padding) { - String paddingLower = padding.toLowerCase(Locale.US); - if ("nopadding".equals(paddingLower)) { - return NONE; - } else if ("pkcs7padding".equals(paddingLower)) { - return PKCS7; - } else { - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - - /** - * @hide - */ - public static int[] allToKeymaster(@PaddingEnum int paddings) { - int[] result = getSetFlags(paddings); - for (int i = 0; i < result.length; i++) { - result[i] = toKeymaster(result[i]); - } - return result; - } - - /** - * @hide - */ - public static @PaddingEnum int allFromKeymaster(Collection<Integer> paddings) { - @PaddingEnum int result = 0; - for (int keymasterPadding : paddings) { - result |= fromKeymaster(keymasterPadding); - } - return result; - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = {Digest.NONE, Digest.SHA256}) - 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 int NONE = 1 << 0; - - /** - * SHA-256 digest. - */ - public static final int SHA256 = 1 << 1; - - /** - * @hide - */ - public static String toString(@DigestEnum int digest) { - switch (digest) { - case NONE: - return "NONE"; - case SHA256: - return "SHA256"; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static String[] allToString(@DigestEnum int digests) { - int[] values = getSetFlags(digests); - String[] result = new String[values.length]; - for (int i = 0; i < values.length; i++) { - result[i] = toString(values[i]); - } - return result; - } - - /** - * @hide - */ - public static int toKeymaster(@DigestEnum int digest) { - switch (digest) { - case NONE: - return KeymasterDefs.KM_DIGEST_NONE; - case SHA256: - return KeymasterDefs.KM_DIGEST_SHA_2_256; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static @DigestEnum int fromKeymaster(int digest) { - switch (digest) { - case KeymasterDefs.KM_DIGEST_NONE: - return NONE; - case KeymasterDefs.KM_DIGEST_SHA_2_256: - return SHA256; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static int[] allToKeymaster(@DigestEnum int digests) { - int[] result = getSetFlags(digests); - for (int i = 0; i < result.length; i++) { - result[i] = toKeymaster(result[i]); - } - return result; - } - - /** - * @hide - */ - public static @DigestEnum int allFromKeymaster(Collection<Integer> digests) { - @DigestEnum int result = 0; - for (int keymasterDigest : digests) { - result |= fromKeymaster(keymasterDigest); - } - return result; - } - - /** - * @hide - */ - public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) { - String algorithmLower = algorithm.toLowerCase(Locale.US); - if (algorithmLower.startsWith("hmac")) { - if ("hmacsha256".equals(algorithmLower)) { - return SHA256; - } else { - throw new IllegalArgumentException("Unsupported digest: " - + algorithmLower.substring("hmac".length())); - } - } else { - return null; - } - } - - /** - * @hide - */ - public static String toJCASignatureAlgorithmDigest(@DigestEnum int digest) { - switch (digest) { - case NONE: - return "NONE"; - case SHA256: - return "SHA256"; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static Integer getOutputSizeBytes(@DigestEnum int digest) { - switch (digest) { - case NONE: - return null; - case SHA256: - return 256 / 8; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR}) - 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 int ECB = 1 << 0; - - /** Cipher Block Chaining (CBC) block mode. */ - public static final int CBC = 1 << 1; - - /** Counter (CTR) block mode. */ - public static final int CTR = 1 << 2; - - /** - * @hide - */ - public static int toKeymaster(@BlockModeEnum int mode) { - switch (mode) { - case ECB: - return KeymasterDefs.KM_MODE_ECB; - case CBC: - return KeymasterDefs.KM_MODE_CBC; - case CTR: - return KeymasterDefs.KM_MODE_CTR; - default: - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - - /** - * @hide - */ - public static @BlockModeEnum int fromKeymaster(int mode) { - switch (mode) { - case KeymasterDefs.KM_MODE_ECB: - return ECB; - case KeymasterDefs.KM_MODE_CBC: - return CBC; - case KeymasterDefs.KM_MODE_CTR: - return CTR; - default: - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - - /** - * @hide - */ - public static int[] allToKeymaster(@BlockModeEnum int modes) { - int[] result = getSetFlags(modes); - for (int i = 0; i < result.length; i++) { - result[i] = toKeymaster(result[i]); - } - return result; - } - - /** - * @hide - */ - public static @BlockModeEnum int allFromKeymaster(Collection<Integer> modes) { - @BlockModeEnum int result = 0; - for (int keymasterMode : modes) { - result |= fromKeymaster(keymasterMode); - } - return result; - } - - /** - * @hide - */ - public static String toString(@BlockModeEnum int mode) { - switch (mode) { - case ECB: - return "ECB"; - case CBC: - return "CBC"; - case CTR: - return "CTR"; - default: - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - - /** - * @hide - */ - public static @BlockModeEnum int fromJCAMode(String mode) { - String modeLower = mode.toLowerCase(Locale.US); - if ("ecb".equals(modeLower)) { - return ECB; - } else if ("cbc".equals(modeLower)) { - return CBC; - } else if ("ctr".equals(modeLower)) { - return CTR; - } else { - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = {UserAuthenticator.LOCK_SCREEN}) - public @interface UserAuthenticatorEnum {} - - /** - * User authenticators which can be used to restrict/protect access to keys. - */ - public static abstract class UserAuthenticator { - private UserAuthenticator() {} - - /** Lock screen. */ - public static final int LOCK_SCREEN = 1 << 0; - - /** Fingerprint reader/sensor. */ - public static final int FINGERPRINT_READER = 1 << 1; - - /** - * @hide - */ - public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) { - switch (userAuthenticator) { - case LOCK_SCREEN: - return KeymasterDefs.HW_AUTH_PASSWORD; - case FINGERPRINT_READER: - return KeymasterDefs.HW_AUTH_FINGERPRINT; - default: - throw new IllegalArgumentException( - "Unknown user authenticator: " + userAuthenticator); - } - } - - /** - * @hide - */ - public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) { - switch (userAuthenticator) { - case KeymasterDefs.HW_AUTH_PASSWORD: - return LOCK_SCREEN; - case FINGERPRINT_READER: - return FINGERPRINT_READER; - default: - throw new IllegalArgumentException( - "Unknown user authenticator: " + userAuthenticator); - } - } - - /** - * @hide - */ - public static int allToKeymaster(@UserAuthenticatorEnum int userAuthenticators) { - int result = 0; - int userAuthenticator = 1; - while (userAuthenticators != 0) { - if ((userAuthenticators & 1) != 0) { - result |= toKeymaster(userAuthenticator); - } - userAuthenticators >>>= 1; - userAuthenticator <<= 1; - } - return result; - } - - /** - * @hide - */ - public static @UserAuthenticatorEnum int allFromKeymaster(int userAuthenticators) { - @UserAuthenticatorEnum int result = 0; - int userAuthenticator = 1; - while (userAuthenticators != 0) { - if ((userAuthenticators & 1) != 0) { - result |= fromKeymaster(userAuthenticator); - } - userAuthenticators >>>= 1; - userAuthenticator <<= 1; - } - return result; - } - - /** - * @hide - */ - public static String toString(@UserAuthenticatorEnum int userAuthenticator) { - switch (userAuthenticator) { - case LOCK_SCREEN: - return "LOCK_SCREEN"; - case FINGERPRINT_READER: - return "FINGERPRINT_READER"; - default: - throw new IllegalArgumentException( - "Unknown user authenticator: " + userAuthenticator); - } - } - } - - private static final int[] EMPTY_INT_ARRAY = new int[0]; - - private static int[] getSetFlags(int flags) { - if (flags == 0) { - return EMPTY_INT_ARRAY; - } - int result[] = new int[getSetBitCount(flags)]; - int resultOffset = 0; - int flag = 1; - while (flags != 0) { - if ((flags & 1) != 0) { - result[resultOffset] = flag; - resultOffset++; - } - flags >>>= 1; - flag <<= 1; - } - return result; - } - - private static int getSetBitCount(int value) { - if (value == 0) { - return 0; - } - int result = 0; - while (value != 0) { - if ((value & 1) != 0) { - result++; - } - value >>>= 1; - } - return result; - } -} diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java index abce32d..d1abe12 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -37,39 +37,68 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { public static class AES extends KeyStoreKeyGeneratorSpi { public AES() { - super(KeyStoreKeyConstraints.Algorithm.AES, 128); + super(KeymasterDefs.KM_ALGORITHM_AES, 128); } } - public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi { + protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi { + protected HmacBase(int keymasterDigest) { + super(KeymasterDefs.KM_ALGORITHM_HMAC, + keymasterDigest, + KeymasterUtils.getDigestOutputSizeBytes(keymasterDigest) * 8); + } + } + + public static class HmacSHA1 extends HmacBase { + public HmacSHA1() { + super(KeymasterDefs.KM_DIGEST_SHA1); + } + } + + public static class HmacSHA224 extends HmacBase { + public HmacSHA224() { + super(KeymasterDefs.KM_DIGEST_SHA_2_224); + } + } + + public static class HmacSHA256 extends HmacBase { public HmacSHA256() { - super(KeyStoreKeyConstraints.Algorithm.HMAC, - KeyStoreKeyConstraints.Digest.SHA256, - KeyStoreKeyConstraints.Digest.getOutputSizeBytes( - KeyStoreKeyConstraints.Digest.SHA256) * 8); + super(KeymasterDefs.KM_DIGEST_SHA_2_256); + } + } + + public static class HmacSHA384 extends HmacBase { + public HmacSHA384() { + super(KeymasterDefs.KM_DIGEST_SHA_2_384); + } + } + + public static class HmacSHA512 extends HmacBase { + public HmacSHA512() { + super(KeymasterDefs.KM_DIGEST_SHA_2_512); } } private final KeyStore mKeyStore = KeyStore.getInstance(); - private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm; - private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest; + private final int mKeymasterAlgorithm; + private final int mKeymasterDigest; private final int mDefaultKeySizeBits; private KeyGeneratorSpec mSpec; private SecureRandom mRng; protected KeyStoreKeyGeneratorSpi( - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, + int keymasterAlgorithm, int defaultKeySizeBits) { - this(algorithm, null, defaultKeySizeBits); + this(keymasterAlgorithm, -1, defaultKeySizeBits); } protected KeyStoreKeyGeneratorSpi( - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, - @KeyStoreKeyConstraints.DigestEnum Integer digest, + int keymasterAlgorithm, + int keymasterDigest, int defaultKeySizeBits) { - mAlgorithm = algorithm; - mDigest = digest; + mKeymasterAlgorithm = keymasterAlgorithm; + mKeymasterDigest = keymasterDigest; mDefaultKeySizeBits = defaultKeySizeBits; } @@ -88,46 +117,62 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } KeymasterArguments args = new KeymasterArguments(); - args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, - KeyStoreKeyConstraints.Algorithm.toKeymaster(mAlgorithm)); - if (mDigest != null) { - args.addInt(KeymasterDefs.KM_TAG_DIGEST, - KeyStoreKeyConstraints.Digest.toKeymaster(mDigest)); - Integer digestOutputSizeBytes = - KeyStoreKeyConstraints.Digest.getOutputSizeBytes(mDigest); - if (digestOutputSizeBytes != null) { + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); + if (mKeymasterDigest != -1) { + args.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); + int digestOutputSizeBytes = + KeymasterUtils.getDigestOutputSizeBytes(mKeymasterDigest); + if (digestOutputSizeBytes != -1) { // TODO: Remove MAC length constraint once Keymaster API no longer requires it. // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes); } } - if (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) { - if (mDigest == null) { - throw new IllegalStateException("Digest algorithm must be specified for key" - + " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm)); + if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { + if (mKeymasterDigest == -1) { + throw new IllegalStateException("Digest algorithm must be specified for HMAC key"); } } - int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits; + int keySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits; args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits); - int purposes = spec.getPurposes(); + @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 : - KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { + KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) { args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); } - for (int keymasterBlockMode : - KeyStoreKeyConstraints.BlockMode.allToKeymaster(spec.getBlockModes())) { - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode); - } - for (int keymasterPadding : - KeyStoreKeyConstraints.Padding.allToKeymaster(spec.getPaddings())) { - args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); - } + args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes); + args.addInts( + KeymasterDefs.KM_TAG_PADDING, + KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings( + spec.getEncryptionPaddings())); if (spec.getUserAuthenticators() == 0) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); } else { args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, - KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster( + KeyStoreKeyProperties.UserAuthenticator.allToKeymaster( spec.getUserAuthenticators())); + long secureUserId = GateKeeper.getSecureUserId(); + if (secureUserId == 0) { + throw new IllegalStateException("Secure lock screen must be enabled" + + " to generate keys requiring user authentication"); + } + args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, secureUserId); } if (spec.isInvalidatedOnNewFingerprintEnrolled()) { // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports @@ -147,9 +192,9 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { (spec.getKeyValidityForConsumptionEnd() != null) ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE)); - if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) - || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) { - // Permit caller-specified IV. This is needed due to the Cipher abstraction. + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) + && (!spec.isRandomizedEncryptionRequired())) { + // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); } @@ -168,7 +213,7 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { throw KeyStore.getCryptoOperationException(errorCode); } String keyAlgorithmJCA = - KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest); + KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest); return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA); } diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java new file mode 100644 index 0000000..206103f --- /dev/null +++ b/keystore/java/android/security/KeyStoreKeyProperties.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +import android.annotation.IntDef; +import android.security.keymaster.KeymasterDefs; + +import libcore.util.EmptyArray; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collection; + +/** + * Properties of {@code AndroidKeyStore} keys. + */ +public abstract class KeyStoreKeyProperties { + private KeyStoreKeyProperties() {} + + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, + value = {Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY}) + public @interface PurposeEnum {} + + /** + * Purpose of key. + */ + public static abstract class Purpose { + private Purpose() {} + + /** + * Purpose: encryption. + */ + public static final int ENCRYPT = 1 << 0; + + /** + * Purpose: decryption. + */ + public static final int DECRYPT = 1 << 1; + + /** + * Purpose: signing. + */ + public static final int SIGN = 1 << 2; + + /** + * Purpose: signature verification. + */ + public static final int VERIFY = 1 << 3; + + /** + * @hide + */ + public static int toKeymaster(@PurposeEnum int purpose) { + switch (purpose) { + case ENCRYPT: + return KeymasterDefs.KM_PURPOSE_ENCRYPT; + case DECRYPT: + return KeymasterDefs.KM_PURPOSE_DECRYPT; + case SIGN: + return KeymasterDefs.KM_PURPOSE_SIGN; + case VERIFY: + return KeymasterDefs.KM_PURPOSE_VERIFY; + default: + throw new IllegalArgumentException("Unknown purpose: " + purpose); + } + } + + /** + * @hide + */ + public static @PurposeEnum int fromKeymaster(int purpose) { + switch (purpose) { + case KeymasterDefs.KM_PURPOSE_ENCRYPT: + return ENCRYPT; + case KeymasterDefs.KM_PURPOSE_DECRYPT: + return DECRYPT; + case KeymasterDefs.KM_PURPOSE_SIGN: + return SIGN; + case KeymasterDefs.KM_PURPOSE_VERIFY: + return VERIFY; + default: + throw new IllegalArgumentException("Unknown purpose: " + purpose); + } + } + + /** + * @hide + */ + public static int[] allToKeymaster(@PurposeEnum int purposes) { + int[] result = getSetFlags(purposes); + for (int i = 0; i < result.length; i++) { + result[i] = toKeymaster(result[i]); + } + return result; + } + + /** + * @hide + */ + public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) { + @PurposeEnum int result = 0; + for (int keymasterPurpose : purposes) { + result |= fromKeymaster(keymasterPurpose); + } + return result; + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, + value = {UserAuthenticator.LOCK_SCREEN, UserAuthenticator.FINGERPRINT_READER}) + public @interface UserAuthenticatorEnum {} + + /** + * User authenticators which can be used to restrict/protect access to keys. + */ + public static abstract class UserAuthenticator { + private UserAuthenticator() {} + + /** Lock screen. */ + public static final int LOCK_SCREEN = 1 << 0; + + /** Fingerprint reader/sensor. */ + public static final int FINGERPRINT_READER = 1 << 1; + + /** + * @hide + */ + public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) { + switch (userAuthenticator) { + case LOCK_SCREEN: + return KeymasterDefs.HW_AUTH_PASSWORD; + case FINGERPRINT_READER: + return KeymasterDefs.HW_AUTH_FINGERPRINT; + default: + throw new IllegalArgumentException( + "Unknown user authenticator: " + userAuthenticator); + } + } + + /** + * @hide + */ + public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) { + switch (userAuthenticator) { + case KeymasterDefs.HW_AUTH_PASSWORD: + return LOCK_SCREEN; + case KeymasterDefs.HW_AUTH_FINGERPRINT: + return FINGERPRINT_READER; + default: + throw new IllegalArgumentException( + "Unknown user authenticator: " + userAuthenticator); + } + } + + /** + * @hide + */ + public static int allToKeymaster(@UserAuthenticatorEnum int userAuthenticators) { + int result = 0; + int userAuthenticator = 1; + while (userAuthenticators != 0) { + if ((userAuthenticators & 1) != 0) { + result |= toKeymaster(userAuthenticator); + } + userAuthenticators >>>= 1; + userAuthenticator <<= 1; + } + return result; + } + + /** + * @hide + */ + public static @UserAuthenticatorEnum int allFromKeymaster(int userAuthenticators) { + @UserAuthenticatorEnum int result = 0; + int userAuthenticator = 1; + while (userAuthenticators != 0) { + if ((userAuthenticators & 1) != 0) { + result |= fromKeymaster(userAuthenticator); + } + userAuthenticators >>>= 1; + userAuthenticator <<= 1; + } + return result; + } + + /** + * @hide + */ + public static String toString(@UserAuthenticatorEnum int userAuthenticator) { + switch (userAuthenticator) { + case LOCK_SCREEN: + return "LOCK_SCREEN"; + case FINGERPRINT_READER: + return "FINGERPRINT_READER"; + default: + throw new IllegalArgumentException( + "Unknown user authenticator: " + userAuthenticator); + } + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({Origin.GENERATED, Origin.IMPORTED, Origin.UNKNOWN}) + public @interface OriginEnum {} + + /** + * Origin of the key. + */ + public static abstract class Origin { + private Origin() {} + + /** Key was generated inside AndroidKeyStore. */ + public static final int GENERATED = 1 << 0; + + /** Key was imported into AndroidKeyStore. */ + public static final int IMPORTED = 1 << 1; + + /** + * Origin of the key is unknown. This can occur only for keys backed by an old TEE + * implementation which does not record origin information. + */ + public static final int UNKNOWN = 1 << 2; + + /** + * @hide + */ + public static @OriginEnum int fromKeymaster(int origin) { + switch (origin) { + case KeymasterDefs.KM_ORIGIN_GENERATED: + return GENERATED; + case KeymasterDefs.KM_ORIGIN_IMPORTED: + return IMPORTED; + case KeymasterDefs.KM_ORIGIN_UNKNOWN: + return UNKNOWN; + default: + throw new IllegalArgumentException("Unknown origin: " + origin); + } + } + } + + private static int[] getSetFlags(int flags) { + if (flags == 0) { + return EmptyArray.INT; + } + int result[] = new int[getSetBitCount(flags)]; + int resultOffset = 0; + int flag = 1; + while (flags != 0) { + if ((flags & 1) != 0) { + result[resultOffset] = flag; + resultOffset++; + } + flags >>>= 1; + flag <<= 1; + } + return result; + } + + private static int getSetBitCount(int value) { + if (value == 0) { + return 0; + } + int result = 0; + while (value != 0) { + if ((value & 1) != 0) { + result++; + } + value >>>= 1; + } + return result; + } +} diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java index 256d9b3..a89e4dd 100644 --- a/keystore/java/android/security/KeyStoreKeySpec.java +++ b/keystore/java/android/security/KeyStoreKeySpec.java @@ -22,23 +22,22 @@ import java.util.Date; /** * Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android * KeyStore</a>. - * - * @hide */ public class KeyStoreKeySpec implements KeySpec { private final String mKeystoreAlias; private final int mKeySize; - private final @KeyStoreKeyCharacteristics.OriginEnum int mOrigin; + private final boolean mTeeBacked; + private final @KeyStoreKeyProperties.OriginEnum int mOrigin; private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; - private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; - private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm; - private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; - private final @KeyStoreKeyConstraints.DigestEnum int mDigests; - private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; - private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; - private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mTeeEnforcedUserAuthenticators; + 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.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mTeeEnforcedUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; private final boolean mInvalidatedOnNewFingerprintEnrolled; @@ -46,31 +45,35 @@ public class KeyStoreKeySpec implements KeySpec { * @hide */ KeyStoreKeySpec(String keystoreKeyAlias, - @KeyStoreKeyCharacteristics.OriginEnum int origin, + boolean teeBacked, + @KeyStoreKeyProperties.OriginEnum int origin, int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum int purposes, - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm, - @KeyStoreKeyConstraints.PaddingEnum int paddings, - @KeyStoreKeyConstraints.DigestEnum int digests, - @KeyStoreKeyConstraints.BlockModeEnum int blockModes, - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, - @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators, + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] encryptionPaddings, + String[] signaturePaddings, + String[] digests, + String[] blockModes, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyProperties.UserAuthenticatorEnum int teeEnforcedUserAuthenticators, int userAuthenticationValidityDurationSeconds, boolean invalidatedOnNewFingerprintEnrolled) { mKeystoreAlias = keystoreKeyAlias; + mTeeBacked = teeBacked; mOrigin = origin; mKeySize = keySize; mKeyValidityStart = keyValidityStart; mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; mPurposes = purposes; - mAlgorithm = algorithm; - mPaddings = paddings; - mDigests = digests; - mBlockModes = blockModes; + mEncryptionPaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); + mSignaturePaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); + mDigests = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(digests)); + mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); mUserAuthenticators = userAuthenticators; mTeeEnforcedUserAuthenticators = teeEnforcedUserAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; @@ -85,9 +88,17 @@ public class KeyStoreKeySpec implements KeySpec { } /** + * Returns {@code true} if the key is TEE-backed. Key material of TEE-backed keys is available + * in plaintext only inside the TEE. + */ + public boolean isTeeBacked() { + return mTeeBacked; + } + + /** * Gets the origin of the key. */ - public @KeyStoreKeyCharacteristics.OriginEnum int getOrigin() { + public @KeyStoreKeyProperties.OriginEnum int getOrigin() { return mOrigin; } @@ -128,36 +139,36 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the set of purposes for which the key can be used. */ - public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { + public @KeyStoreKeyProperties.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the algorithm of the key. + * Gets the set of block modes with which the key can be used. */ - public @KeyStoreKeyConstraints.AlgorithmEnum int getAlgorithm() { - return mAlgorithm; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(mBlockModes); } /** - * Gets the set of block modes with which the key can be used. + * Gets the set of padding modes with which the key can be used when encrypting/decrypting. */ - public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { - return mBlockModes; + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** - * Gets the set of padding modes with which the key can be used. + * Gets the set of padding modes with which the key can be used when signing/verifying. */ - public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { - return mPaddings; + public String[] getSignaturePaddings() { + return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } /** * Gets the set of digest algorithms with which the key can be used. */ - public @KeyStoreKeyConstraints.DigestEnum int getDigests() { - return mDigests; + public String[] getDigests() { + return ArrayUtils.cloneIfNotEmpty(mDigests); } /** @@ -166,7 +177,7 @@ public class KeyStoreKeySpec implements KeySpec { * * @return user authenticators or {@code 0} if the key can be used without user authentication. */ - public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + public @KeyStoreKeyProperties.UserAuthenticatorEnum int getUserAuthenticators() { return mUserAuthenticators; } @@ -175,7 +186,7 @@ public class KeyStoreKeySpec implements KeySpec { * key. This is a subset of the user authentications returned by * {@link #getUserAuthenticators()}. */ - public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getTeeEnforcedUserAuthenticators() { + public @KeyStoreKeyProperties.UserAuthenticatorEnum int getTeeEnforcedUserAuthenticators() { return mTeeEnforcedUserAuthenticators; } diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index 0b2f9b6..c24b74f 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -19,13 +19,14 @@ package android.security; import android.content.Context; import java.security.Key; -import java.security.KeyPairGenerator; import java.security.KeyStore.ProtectionParameter; import java.util.Date; +import javax.crypto.Cipher; + /** - * This provides the optional parameters that can be specified for - * {@code KeyStore} entries that work with + * Parameters specifying how to secure and restrict the use of a key being + * imported into the * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore * facility</a>. The Android KeyStore facility is accessed through a * {@link java.security.KeyStore} API using the {@code AndroidKeyStore} @@ -36,23 +37,19 @@ import java.util.Date; * there is only one logical instance of the {@code KeyStore} per application * UID so apps using the {@code sharedUid} facility will also share a * {@code KeyStore}. - * <p> - * Keys may be generated using the {@link KeyPairGenerator} facility with a - * {@link KeyPairGeneratorSpec} to specify the entry's {@code alias}. A - * self-signed X.509 certificate will be attached to generated entries, but that - * may be replaced at a later time by a certificate signed by a real Certificate - * Authority. */ public final class KeyStoreParameter implements ProtectionParameter { private int mFlags; private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; - private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes; - private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; - private final @KeyStoreKeyConstraints.DigestEnum Integer mDigests; - private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; - private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private final String[] mEncryptionPaddings; + private final String[] mSignaturePaddings; + private final String[] mDigests; + private final String[] mBlockModes; + private final boolean mRandomizedEncryptionRequired; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; private final boolean mInvalidatedOnNewFingerprintEnrolled; @@ -60,11 +57,13 @@ public final class KeyStoreParameter implements ProtectionParameter { Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum int purposes, - @KeyStoreKeyConstraints.PaddingEnum int paddings, - @KeyStoreKeyConstraints.DigestEnum Integer digests, - @KeyStoreKeyConstraints.BlockModeEnum int blockModes, - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] encryptionPaddings, + String[] signaturePaddings, + String[] digests, + String[] blockModes, + boolean randomizedEncryptionRequired, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds, boolean invalidatedOnNewFingerprintEnrolled) { if ((userAuthenticationValidityDurationSeconds < 0) @@ -78,9 +77,13 @@ public final class KeyStoreParameter implements ProtectionParameter { mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; mPurposes = purposes; - mPaddings = paddings; - mDigests = digests; - mBlockModes = blockModes; + mEncryptionPaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); + mSignaturePaddings = + ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings)); + mDigests = ArrayUtils.cloneIfNotEmpty(digests); + mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled; @@ -105,7 +108,6 @@ public final class KeyStoreParameter implements ProtectionParameter { * Gets the time instant before which the key is not yet valid. * * @return instant or {@code null} if not restricted. - * @hide */ public Date getKeyValidityStart() { return mKeyValidityStart; @@ -115,8 +117,6 @@ public final class KeyStoreParameter implements ProtectionParameter { * Gets the time instant after which the key is no long valid for decryption and verification. * * @return instant or {@code null} if not restricted. - * - * @hide */ public Date getKeyValidityForConsumptionEnd() { return mKeyValidityForConsumptionEnd; @@ -126,8 +126,6 @@ public final class KeyStoreParameter implements ProtectionParameter { * Gets the time instant after which the key is no long valid for encryption and signing. * * @return instant or {@code null} if not restricted. - * - * @hide */ public Date getKeyValidityForOriginationEnd() { return mKeyValidityForOriginationEnd; @@ -135,56 +133,68 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Gets the set of purposes for which the key can be used. - * - * @hide */ - public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { + public @KeyStoreKeyProperties.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the set of padding schemes to which the key is restricted. - * - * @hide + * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. + */ + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); + } + + /** + * Gets the set of padding schemes with which the key can be used when signing or verifying + * signatures. */ - public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { - return mPaddings; + public String[] getSignaturePaddings() { + return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } /** - * Gets the set of digests to which the key is restricted. + * Gets the set of digest algorithms with which the key can be used. * - * @throws IllegalStateException if this restriction has not been specified. + * @throws IllegalStateException if this set has not been specified. * * @see #isDigestsSpecified() - * - * @hide */ - public @KeyStoreKeyConstraints.DigestEnum int getDigests() { + public String[] getDigests() { if (mDigests == null) { throw new IllegalStateException("Digests not specified"); } - return mDigests; + return ArrayUtils.cloneIfNotEmpty(mDigests); } /** - * Returns {@code true} if digest restrictions have been specified. + * Returns {@code true} if the set of digest algorithms with which the key can be used has been + * specified. * * @see #getDigests() - * - * @hide */ public boolean isDigestsSpecified() { return mDigests != null; } /** - * Gets the set of block modes to which the key is restricted. - * - * @hide + * Gets the set of block modes with which the key can be used. + */ + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(mBlockModes); + } + + /** + * Returns {@code true} if encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic property + * being required is <em>indistinguishability under chosen-plaintext attack ({@code + * IND-CPA})</em>. This property is important because it mitigates several classes of + * weaknesses due to which ciphertext may leak information about plaintext. For example, if a + * given plaintext always produces the same ciphertext, an attacker may see the repeated + * ciphertexts and be able to deduce something about the plaintext. */ - public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { - return mBlockModes; + public boolean isRandomizedEncryptionRequired() { + return mRandomizedEncryptionRequired; } /** @@ -192,10 +202,8 @@ public final class KeyStoreParameter implements ProtectionParameter { * used iff the user has authenticated to at least one of these user authenticators. * * @return user authenticators or {@code 0} if the key can be used without user authentication. - * - * @hide */ - public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + public @KeyStoreKeyProperties.UserAuthenticatorEnum int getUserAuthenticators() { return mUserAuthenticators; } @@ -205,8 +213,6 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication * is required for every use of the key. - * - * @hide */ public int getUserAuthenticationValidityDurationSeconds() { return mUserAuthenticationValidityDurationSeconds; @@ -218,8 +224,6 @@ public final class KeyStoreParameter implements ProtectionParameter { * authenticators protecting access to this key. * * @see #getUserAuthenticators() - * - * @hide */ public boolean isInvalidatedOnNewFingerprintEnrolled() { return mInvalidatedOnNewFingerprintEnrolled; @@ -247,11 +251,13 @@ public final class KeyStoreParameter implements ProtectionParameter { private Date mKeyValidityStart; private Date mKeyValidityForOriginationEnd; private Date mKeyValidityForConsumptionEnd; - private @KeyStoreKeyConstraints.PurposeEnum int mPurposes; - private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; - private @KeyStoreKeyConstraints.DigestEnum Integer mDigests; - private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; - private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private String[] mEncryptionPaddings; + private String[] mSignaturePaddings; + private String[] mDigests; + private String[] mBlockModes; + private boolean mRandomizedEncryptionRequired = true; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; private boolean mInvalidatedOnNewFingerprintEnrolled; @@ -287,11 +293,9 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Sets the time instant before which the key is not yet valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityEnd(Date) - * - * @hide */ public Builder setKeyValidityStart(Date startDate) { mKeyValidityStart = startDate; @@ -301,13 +305,11 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Sets the time instant after which the key is no longer valid. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityEnd(Date endDate) { setKeyValidityForOriginationEnd(endDate); @@ -318,11 +320,9 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Sets the time instant after which the key is no longer valid for encryption and signing. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForConsumptionEnd(Date) - * - * @hide */ public Builder setKeyValidityForOriginationEnd(Date endDate) { mKeyValidityForOriginationEnd = endDate; @@ -333,11 +333,9 @@ public final class KeyStoreParameter implements ProtectionParameter { * Sets the time instant after which the key is no longer valid for decryption and * verification. * - * <b>By default, the key is valid at any instant. + * <p>By default, the key is valid at any instant. * * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityForConsumptionEnd(Date endDate) { mKeyValidityForConsumptionEnd = endDate; @@ -345,55 +343,99 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * Restricts the key to being used only for the provided set of purposes. - * - * <p>This restriction must be specified. There is no default. + * Sets the set of purposes for which the key can be used. * - * @hide + * <p>This must be specified for all keys. There is no default. */ - public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) { + public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; } /** - * Restricts the key to being used only with the provided padding schemes. Attempts to use - * the key with any other padding will be rejected. + * Sets the set of padding schemes with which the key can be used when + * encrypting/decrypting. Attempts to use the key with any other padding scheme will be + * rejected. * - * <p>This restriction must be specified for keys which are used for encryption/decryption. - * - * @hide + * <p>This must be specified for keys which are used for encryption/decryption. */ - public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) { - mPaddings = paddings; + public Builder setEncryptionPaddings(String... paddings) { + mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } /** - * Restricts the key to being used only with the provided digests when generating signatures - * or HMACs. Attempts to use the key with any other digest will be rejected. - * - * <p>For HMAC keys, the default is to restrict to the digest specified in - * {@link Key#getAlgorithm()}. For asymmetric signing keys this constraint must be specified - * because there is no default. + * Sets the set of padding schemes with which the key can be used when + * signing/verifying. Attempts to use the key with any other padding scheme will be + * rejected. * - * @hide + * <p>This must be specified for RSA keys which are used for signing/verification. */ - public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) { - mDigests = digests; + public Builder setSignaturePaddings(String... paddings) { + mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); return this; } + /** - * Restricts the key to being used only with the provided block modes. Attempts to use the - * key with any other block modes will be rejected. + * Sets the set of digests with which the key can be used when signing/verifying or + * generating MACs. Attempts to use the key with any other digest will be rejected. * - * <p>This restriction must be specified for symmetric encryption/decryption keys. + * <p>For HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()}. For + * asymmetric signing keys this constraint must be specified. + */ + public Builder setDigests(String... digests) { + mDigests = ArrayUtils.cloneIfNotEmpty(digests); + return this; + } + + /** + * Sets the set of block modes with which the key can be used when encrypting/decrypting. + * Attempts to use the key with any other block modes will be rejected. * - * @hide + * <p>This must be specified for encryption/decryption keys. */ - public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { - mBlockModes = blockModes; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); + return this; + } + + /** + * Sets whether encryption using this key must be sufficiently randomized to produce + * different ciphertexts for the same plaintext every time. The formal cryptographic + * property being required is <em>indistinguishability under chosen-plaintext attack + * ({@code IND-CPA})</em>. This property is important because it mitigates several classes + * of weaknesses due to which ciphertext may leak information about plaintext. For example, + * if a given plaintext always produces the same ciphertext, an attacker may see the + * repeated ciphertexts and be able to deduce something about the plaintext. + * + * <p>By default, {@code IND-CPA} is required. + * + * <p>When {@code IND-CPA} is required: + * <ul> + * <li>transformation which do not offer {@code IND-CPA}, such as symmetric ciphers using + * {@code ECB} mode or RSA encryption without padding, are prohibited;</li> + * <li>in transformations which use an IV, such as symmetric ciphers in {@code CBC}, + * {@code CTR}, and {@code GCM} block modes, caller-provided IVs are rejected when + * encrypting, to ensure that only random IVs are used.</li> + * + * <p>Before disabling this requirement, consider the following approaches instead: + * <ul> + * <li>If you are generating a random IV for encryption and then initializing a {@code} + * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV + * instead. This will occur if the {@code Cipher} is initialized for encryption without an + * IV. The IV can then be queried via {@link Cipher#getIV()}.</li> + * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully + * random, such as the name of the file being encrypted, or transaction ID, or password, + * or a device identifier), consider changing your design to use a random IV which will then + * be provided in addition to the ciphertext to the entities which need to decrypt the + * ciphertext.</li> + * <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> + */ + public Builder setRandomizedEncryptionRequired(boolean required) { + mRandomizedEncryptionRequired = required; return this; } @@ -407,11 +449,9 @@ public final class KeyStoreParameter implements ProtectionParameter { * without user authentication. * * @see #setUserAuthenticationValidityDurationSeconds(int) - * - * @hide */ public Builder setUserAuthenticators( - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { mUserAuthenticators = userAuthenticators; return this; } @@ -426,8 +466,6 @@ public final class KeyStoreParameter implements ProtectionParameter { * every use of the key. * * @see #setUserAuthenticators(int) - * - * @hide */ public Builder setUserAuthenticationValidityDurationSeconds(int seconds) { mUserAuthenticationValidityDurationSeconds = seconds; @@ -442,8 +480,6 @@ public final class KeyStoreParameter implements ProtectionParameter { * <p>By default, enrolling a new fingerprint does not invalidate the key. * * @see #setUserAuthenticators(Set) - * - * @hide */ public Builder setInvalidatedOnNewFingerprintEnrolled(boolean invalidated) { mInvalidatedOnNewFingerprintEnrolled = invalidated; @@ -462,9 +498,11 @@ public final class KeyStoreParameter implements ProtectionParameter { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, - mPaddings, + mEncryptionPaddings, + mSignaturePaddings, mDigests, mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds, mInvalidatedOnNewFingerprintEnrolled); diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java index 8bf228a..4be0638 100644 --- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java @@ -19,10 +19,14 @@ package android.security; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterDefs; +import libcore.util.EmptyArray; + import java.security.InvalidKeyException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactorySpi; @@ -70,97 +74,110 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { + " Keystore error: " + errorCode); } - @KeyStoreKeyCharacteristics.OriginEnum Integer origin; + boolean teeBacked; + @KeyStoreKeyProperties.OriginEnum int origin; int keySize; - @KeyStoreKeyConstraints.PurposeEnum int purposes; - @KeyStoreKeyConstraints.AlgorithmEnum int algorithm; - @KeyStoreKeyConstraints.PaddingEnum int paddings; - @KeyStoreKeyConstraints.DigestEnum int digests; - @KeyStoreKeyConstraints.BlockModeEnum int blockModes; - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators; - @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators; + @KeyStoreKeyProperties.PurposeEnum int purposes; + String[] encryptionPaddings; + String[] digests; + String[] blockModes; + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators; + @KeyStoreKeyProperties.UserAuthenticatorEnum int teeEnforcedUserAuthenticators; try { - origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN); - if (origin == null) { + if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { + teeBacked = true; + origin = KeyStoreKeyProperties.Origin.fromKeymaster( + keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); + } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { + teeBacked = false; + origin = KeyStoreKeyProperties.Origin.fromKeymaster( + keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); + } else { throw new InvalidKeySpecException("Key origin not available"); } - origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(origin); - Integer keySizeInteger = - KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_KEY_SIZE); + Integer keySizeInteger = keyCharacteristics.getInteger(KeymasterDefs.KM_TAG_KEY_SIZE); if (keySizeInteger == null) { throw new InvalidKeySpecException("Key size not available"); } keySize = keySizeInteger; - purposes = KeyStoreKeyConstraints.Purpose.allFromKeymaster( - KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PURPOSE)); - Integer alg = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ALGORITHM); - if (alg == null) { - throw new InvalidKeySpecException("Key algorithm not available"); + purposes = KeyStoreKeyProperties.Purpose.allFromKeymaster( + keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PURPOSE)); + + List<String> encryptionPaddingsList = new ArrayList<String>(); + for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) { + String jcaPadding; + try { + jcaPadding = KeymasterUtils.getJcaEncryptionPaddingFromKeymasterPadding( + keymasterPadding); + } catch (IllegalArgumentException e) { + throw new InvalidKeySpecException( + "Unsupported encryption padding: " + keymasterPadding); + } + encryptionPaddingsList.add(jcaPadding); } - algorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(alg); - paddings = KeyStoreKeyConstraints.Padding.allFromKeymaster( - KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING)); - digests = KeyStoreKeyConstraints.Digest.allFromKeymaster( - KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST)); - blockModes = KeyStoreKeyConstraints.BlockMode.allFromKeymaster( - KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE)); - - @KeyStoreKeyConstraints.UserAuthenticatorEnum + encryptionPaddings = + encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); + + digests = KeymasterUtils.getJcaDigestAlgorithmsFromKeymasterDigests( + keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST)); + blockModes = KeymasterUtils.getJcaBlockModesFromKeymasterBlockModes( + keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE)); + + @KeyStoreKeyProperties.UserAuthenticatorEnum int swEnforcedKeymasterUserAuthenticators = keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); - @KeyStoreKeyConstraints.UserAuthenticatorEnum + @KeyStoreKeyProperties.UserAuthenticatorEnum int hwEnforcedKeymasterUserAuthenticators = keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); - @KeyStoreKeyConstraints.UserAuthenticatorEnum + @KeyStoreKeyProperties.UserAuthenticatorEnum int keymasterUserAuthenticators = swEnforcedKeymasterUserAuthenticators | hwEnforcedKeymasterUserAuthenticators; - userAuthenticators = KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster( + userAuthenticators = KeyStoreKeyProperties.UserAuthenticator.allFromKeymaster( keymasterUserAuthenticators); teeEnforcedUserAuthenticators = - KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster( + KeyStoreKeyProperties.UserAuthenticator.allFromKeymaster( hwEnforcedKeymasterUserAuthenticators); } catch (IllegalArgumentException e) { throw new InvalidKeySpecException("Unsupported key characteristic", e); } - Date keyValidityStart = - KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME); + Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME); if ((keyValidityStart != null) && (keyValidityStart.getTime() <= 0)) { keyValidityStart = null; } - Date keyValidityForOriginationEnd = KeymasterUtils.getDate(keyCharacteristics, - KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); + Date keyValidityForOriginationEnd = + keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); if ((keyValidityForOriginationEnd != null) && (keyValidityForOriginationEnd.getTime() == Long.MAX_VALUE)) { keyValidityForOriginationEnd = null; } - Date keyValidityForConsumptionEnd = KeymasterUtils.getDate(keyCharacteristics, - KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); + Date keyValidityForConsumptionEnd = + keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); if ((keyValidityForConsumptionEnd != null) && (keyValidityForConsumptionEnd.getTime() == Long.MAX_VALUE)) { keyValidityForConsumptionEnd = null; } - Integer userAuthenticationValidityDurationSeconds = - KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT); + int userAuthenticationValidityDurationSeconds = + keyCharacteristics.getInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1); // TODO: Populate the value below from key characteristics once Keymaster is ready. boolean invalidatedOnNewFingerprintEnrolled = false; return new KeyStoreKeySpec(entryAlias, + teeBacked, origin, keySize, keyValidityStart, keyValidityForOriginationEnd, keyValidityForConsumptionEnd, purposes, - algorithm, - paddings, + encryptionPaddings, + EmptyArray.STRING, // no signature paddings -- this is symmetric crypto digests, blockModes, userAuthenticators, teeEnforcedUserAuthenticators, - ((userAuthenticationValidityDurationSeconds != null) - ? userAuthenticationValidityDurationSeconds : -1), + userAuthenticationValidityDurationSeconds, invalidatedOnNewFingerprintEnrolled); } diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java index 3143d4d..67f75c2 100644 --- a/keystore/java/android/security/KeymasterUtils.java +++ b/keystore/java/android/security/KeymasterUtils.java @@ -16,48 +16,327 @@ package android.security; -import android.security.keymaster.KeyCharacteristics; +import android.security.keymaster.KeymasterDefs; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import libcore.util.EmptyArray; + +import java.util.Collection; +import java.util.Locale; /** * @hide */ public abstract class KeymasterUtils { + private KeymasterUtils() {} - public static Integer getInt(KeyCharacteristics keyCharacteristics, int tag) { - if (keyCharacteristics.hwEnforced.containsTag(tag)) { - return keyCharacteristics.hwEnforced.getInt(tag, -1); - } else if (keyCharacteristics.swEnforced.containsTag(tag)) { - return keyCharacteristics.swEnforced.getInt(tag, -1); + 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 { - return null; + 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 List<Integer> getInts(KeyCharacteristics keyCharacteristics, int tag) { - List<Integer> result = new ArrayList<Integer>(); - result.addAll(keyCharacteristics.hwEnforced.getInts(tag)); - result.addAll(keyCharacteristics.swEnforced.getInts(tag)); + 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 Date getDate(KeyCharacteristics keyCharacteristics, int tag) { - Date result = keyCharacteristics.hwEnforced.getDate(tag, null); - if (result == null) { - result = keyCharacteristics.swEnforced.getDate(tag, null); + 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 boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) { - if (keyCharacteristics.hwEnforced.containsTag(tag)) { - return keyCharacteristics.hwEnforced.getBoolean(tag, false); + public static int getDigestOutputSizeBytes(int keymasterDigest) { + switch (keymasterDigest) { + case KeymasterDefs.KM_DIGEST_NONE: + return -1; + case KeymasterDefs.KM_DIGEST_MD5: + return 128 / 8; + case KeymasterDefs.KM_DIGEST_SHA1: + return 160 / 8; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return 224 / 8; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return 256 / 8; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return 384 / 8; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return 512 / 8; + default: + throw new IllegalArgumentException("Unknown digest: " + keymasterDigest); + } + } + + 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 { - return keyCharacteristics.swEnforced.getBoolean(tag, false); + 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: + return false; + case KeymasterDefs.KM_MODE_CBC: + case KeymasterDefs.KM_MODE_CTR: + case KeymasterDefs.KM_MODE_GCM: + return true; + default: + throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode); + } + } + + 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; + } } diff --git a/keystore/java/android/security/NewFingerprintEnrolledException.java b/keystore/java/android/security/NewFingerprintEnrolledException.java index 6da4a2a..806b214 100644 --- a/keystore/java/android/security/NewFingerprintEnrolledException.java +++ b/keystore/java/android/security/NewFingerprintEnrolledException.java @@ -19,8 +19,6 @@ package android.security; /** * Indicates that a cryptographic operation could not be performed because the key used by the * operation is permanently invalid because a new fingerprint was enrolled. - * - * @hide */ public class NewFingerprintEnrolledException extends CryptoOperationException { diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java index e6342ef..f5f5f41 100644 --- a/keystore/java/android/security/UserNotAuthenticatedException.java +++ b/keystore/java/android/security/UserNotAuthenticatedException.java @@ -19,8 +19,6 @@ package android.security; /** * Indicates that a cryptographic operation could not be performed because the user has not been * authenticated recently enough. - * - * @hide */ public class UserNotAuthenticatedException extends CryptoOperationException { diff --git a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java index bc8dd13..681a9ff 100644 --- a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java +++ b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java @@ -24,6 +24,11 @@ import java.util.Date; import javax.security.auth.x500.X500Principal; public class KeyPairGeneratorSpecTest extends AndroidTestCase { + private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake"); + private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1"); + private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1980 + private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048 + private static final String TEST_ALIAS_1 = "test1"; private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1"); @@ -105,46 +110,37 @@ public class KeyPairGeneratorSpecTest extends AndroidTestCase { } } - public void testConstructor_NullSubjectDN_Failure() throws Exception { - try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW, - NOW_PLUS_10_YEARS, 0); - fail("Should throw IllegalArgumentException when subjectDN is null"); - } catch (IllegalArgumentException success) { - } + public void testConstructor_NullSubjectDN_Success() throws Exception { + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec( + getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW, + NOW_PLUS_10_YEARS, 0); + assertEquals(DEFAULT_CERT_SUBJECT, spec.getSubjectDN()); } - public void testConstructor_NullSerial_Failure() throws Exception { - try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW, - NOW_PLUS_10_YEARS, 0); - fail("Should throw IllegalArgumentException when startDate is null"); - } catch (IllegalArgumentException success) { - } + public void testConstructor_NullSerial_Success() throws Exception { + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec( + getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW, + NOW_PLUS_10_YEARS, 0); + assertEquals(DEFAULT_CERT_SERIAL_NUMBER, spec.getSerialNumber()); } - public void testConstructor_NullStartDate_Failure() throws Exception { - try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, - null, NOW_PLUS_10_YEARS, 0); - fail("Should throw IllegalArgumentException when startDate is null"); - } catch (IllegalArgumentException success) { - } + public void testConstructor_NullStartDate_Success() throws Exception { + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec( + getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, null, + NOW_PLUS_10_YEARS, 0); + assertEquals(DEFAULT_CERT_NOT_BEFORE, spec.getStartDate()); } - public void testConstructor_NullEndDate_Failure() throws Exception { - try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, - NOW, null, 0); - fail("Should throw IllegalArgumentException when keystoreAlias is null"); - } catch (IllegalArgumentException success) { - } + public void testConstructor_NullEndDate_Success() throws Exception { + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec( + getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, NOW, null, 0); + assertEquals(DEFAULT_CERT_NOT_AFTER, spec.getEndDate()); } public void testConstructor_EndBeforeStart_Failure() throws Exception { try { - new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, - NOW_PLUS_10_YEARS, NOW, 0); + new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, + SERIAL_1, NOW_PLUS_10_YEARS, NOW, 0); fail("Should throw IllegalArgumentException when end is before start"); } catch (IllegalArgumentException success) { } diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java index c9a140c..1a5552a 100644 --- a/keystore/tests/src/android/security/KeyStoreTest.java +++ b/keystore/tests/src/android/security/KeyStoreTest.java @@ -294,14 +294,14 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { public void testSaw_ungrantedUid_Bluetooth() throws Exception { String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID); - assertNull(results1); + assertEquals(0, results1.length); mKeyStore.password(TEST_PASSWD); mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED); mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED); String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID); - assertNull(results2); + assertEquals(0, results2.length); } public void testSaw_grantedUid_Wifi() throws Exception { @@ -798,7 +798,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { // TODO: Verify we have an RSA public key that's well formed. } - public void testAesOcbEncryptSuccess() throws Exception { + public void testAesGcmEncryptSuccess() throws Exception { String name = "test"; KeymasterArguments args = new KeymasterArguments(); args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT); @@ -806,7 +806,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256); - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB); + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM); args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096); args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16); args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); @@ -903,9 +903,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256); - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB); - args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096); - args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16); + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR); args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); KeyCharacteristics outCharacteristics = new KeyCharacteristics(); @@ -935,11 +933,9 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT); args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT); args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); - args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); + args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7); args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256); - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB); - args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096); - args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16); + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB); args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1); KeyCharacteristics outCharacteristics = new KeyCharacteristics(); |
