diff options
17 files changed, 1151 insertions, 1446 deletions
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java index b803a1b..b3a3aad 100644 --- a/core/java/android/security/keymaster/KeyCharacteristics.java +++ b/core/java/android/security/keymaster/KeyCharacteristics.java @@ -19,6 +19,8 @@ package android.security.keymaster; import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.Date; import java.util.List; /** @@ -30,10 +32,12 @@ public class KeyCharacteristics implements Parcelable { public static final Parcelable.Creator<KeyCharacteristics> CREATOR = new Parcelable.Creator<KeyCharacteristics>() { + @Override public KeyCharacteristics createFromParcel(Parcel in) { return new KeyCharacteristics(in); } + @Override public KeyCharacteristics[] newArray(int length) { return new KeyCharacteristics[length]; } @@ -50,6 +54,7 @@ public class KeyCharacteristics implements Parcelable { return 0; } + @Override public void writeToParcel(Parcel out, int flags) { swEnforced.writeToParcel(out, flags); hwEnforced.writeToParcel(out, flags); @@ -59,5 +64,53 @@ public class KeyCharacteristics implements Parcelable { swEnforced = KeymasterArguments.CREATOR.createFromParcel(in); hwEnforced = KeymasterArguments.CREATOR.createFromParcel(in); } + + public Integer getInteger(int tag) { + if (hwEnforced.containsTag(tag)) { + return hwEnforced.getInt(tag, -1); + } else if (swEnforced.containsTag(tag)) { + return swEnforced.getInt(tag, -1); + } else { + return null; + } + } + + public int getInt(int tag, int defaultValue) { + Integer result = getInteger(tag); + return (result != null) ? result : defaultValue; + } + + public List<Integer> getInts(int tag) { + List<Integer> result = new ArrayList<Integer>(); + result.addAll(hwEnforced.getInts(tag)); + result.addAll(swEnforced.getInts(tag)); + return result; + } + + public Date getDate(int tag) { + Date result = hwEnforced.getDate(tag, null); + if (result == null) { + result = swEnforced.getDate(tag, null); + } + return result; + } + + public Date getDate(int tag, Date defaultValue) { + if (hwEnforced.containsTag(tag)) { + return hwEnforced.getDate(tag, null); + } else if (hwEnforced.containsTag(tag)) { + return swEnforced.getDate(tag, null); + } else { + return defaultValue; + } + } + + public boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) { + if (keyCharacteristics.hwEnforced.containsTag(tag)) { + return keyCharacteristics.hwEnforced.getBoolean(tag, false); + } else { + return keyCharacteristics.swEnforced.getBoolean(tag, false); + } + } } diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java index b5fd4bd..8ed288c 100644 --- a/core/java/android/security/keymaster/KeymasterArguments.java +++ b/core/java/android/security/keymaster/KeymasterArguments.java @@ -34,9 +34,12 @@ public class KeymasterArguments implements Parcelable { public static final Parcelable.Creator<KeymasterArguments> CREATOR = new Parcelable.Creator<KeymasterArguments>() { + @Override public KeymasterArguments createFromParcel(Parcel in) { return new KeymasterArguments(in); } + + @Override public KeymasterArguments[] newArray(int size) { return new KeymasterArguments[size]; } @@ -54,6 +57,12 @@ public class KeymasterArguments implements Parcelable { mArguments.add(new KeymasterIntArgument(tag, value)); } + public void addInts(int tag, int... values) { + for (int value : values) { + addInt(tag, value); + } + } + public void addBoolean(int tag) { mArguments.add(new KeymasterBooleanArgument(tag)); } diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index 55a8b4f..fcee9fc 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,6 +48,7 @@ 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; @@ -112,13 +115,6 @@ 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); @@ -126,20 +122,11 @@ public class AndroidKeyStore extends KeyStoreSpi { 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); - } - } 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,90 +443,92 @@ 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); } } - @KeyStoreKeyConstraints.PurposeEnum int purposes = params.getPurposes(); - @KeyStoreKeyConstraints.BlockModeEnum int blockModes = params.getBlockModes(); - if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) + @KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes(); + int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes( + params.getBlockModes()); + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) && (params.isRandomizedEncryptionRequired())) { - @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes = - blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES; - if (incompatibleBlockModes != 0) { - throw new KeyStoreException("Randomized encryption (IND-CPA) required but may be" - + " violated by block mode(s): " - + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes) - + ". See KeyStoreParameter documentation."); + 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 keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) { + for (int keymasterPurpose : KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) { args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose); } - for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) { - args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode); - } - for (int keymasterPadding : - KeyStoreKeyConstraints.Padding.allToKeymaster(params.getPaddings())) { - args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); - } + 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())); } if (params.getUserAuthenticationValidityDurationSeconds() != -1) { @@ -559,7 +548,7 @@ 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) + 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/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/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 4dcabd0..a22c31c 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -49,11 +49,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { 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 @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private final String[] mEncryptionPaddings; + private final String[] mBlockModes; private final boolean mRandomizedEncryptionRequired; - private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; private KeyGeneratorSpec( @@ -64,11 +64,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum int purposes, - @KeyStoreKeyConstraints.PaddingEnum int paddings, - @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] encryptionPaddings, + String[] blockModes, boolean randomizedEncryptionRequired, - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds) { if (context == null) { throw new IllegalArgumentException("context == null"); @@ -88,8 +88,9 @@ 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; @@ -154,22 +155,22 @@ 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 @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. + * 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); } /** @@ -191,7 +192,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; } @@ -221,11 +222,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { 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 @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private String[] mEncryptionPaddings; + private String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; - private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; /** @@ -332,34 +333,35 @@ 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 restriction must be specified for keys which are used for encryption/decryption. + * <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 modes. Attempts to use the - * key with any other block modes will be rejected. + * 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 setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { - mBlockModes = blockModes; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; } @@ -412,7 +414,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @see #setUserAuthenticationValidityDurationSeconds(int) */ public Builder setUserAuthenticators( - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { mUserAuthenticators = userAuthenticators; return this; } @@ -447,7 +449,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, - mPaddings, + mEncryptionPaddings, mBlockModes, mRandomizedEncryptionRequired, mUserAuthenticators, diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index db310ea..9d6701a 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -78,17 +78,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 @KeyStoreKeyConstraints.DigestEnum int mDigests; + private final String[] mDigests; - private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private final String[] mEncryptionPaddings; - private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final String[] mSignaturePaddings; + + private final String[] mBlockModes; private final boolean mRandomizedEncryptionRequired; - private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; @@ -130,12 +132,13 @@ 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, + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] digests, + String[] encryptionPaddings, + String[] signaturePaddings, + String[] blockModes, boolean randomizedEncryptionRequired, - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds) { if (context == null) { throw new IllegalArgumentException("context == null"); @@ -171,9 +174,11 @@ 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; @@ -199,13 +204,15 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { startDate, endDate, endDate, - 0, - 0, - 0, - 0, - true, - 0, - -1); + 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) + ); } /** @@ -333,35 +340,44 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @hide */ - public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() { + public @KeyStoreKeyProperties.PurposeEnum int getPurposes() { return mPurposes; } /** - * Gets the set of digests to which the key is restricted. + * Gets the set of digest algorithms with which the key can be used. + * + * @hide + */ + public String[] getDigests() { + return ArrayUtils.cloneIfNotEmpty(mDigests); + } + + /** + * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. * * @hide */ - public @KeyStoreKeyConstraints.DigestEnum int getDigests() { - return mDigests; + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** - * 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 signing/verifying. * * @hide */ - public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { - return mPaddings; + public String[] getSignaturePaddings() { + return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } /** - * Gets the set of block modes to which the key is restricted. + * Gets the set of block modes with which the key can be used. * * @hide */ - public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { - return mBlockModes; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(mBlockModes); } /** @@ -390,7 +406,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @hide */ - public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + public @KeyStoreKeyProperties.UserAuthenticatorEnum int getUserAuthenticators() { return mUserAuthenticators; } @@ -458,17 +474,19 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private Date mKeyValidityForConsumptionEnd; - private @KeyStoreKeyConstraints.PurposeEnum int mPurposes; + private @KeyStoreKeyProperties.PurposeEnum int mPurposes; + + private String[] mDigests; - private @KeyStoreKeyConstraints.DigestEnum int mDigests; + private String[] mEncryptionPaddings; - private @KeyStoreKeyConstraints.PaddingEnum int mPaddings; + private String[] mSignaturePaddings; - private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; - private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; @@ -658,53 +676,68 @@ 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. + * <p>This must be specified for all keys. There is no default. * * @hide */ - 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. + * 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. + * + * <p>This must be specified for keys which are used for signing/verification. + * + * @hide + */ + public Builder setDigests(String... digests) { + mDigests = ArrayUtils.cloneIfNotEmpty(digests); + return this; + } + + /** + * 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 signing/verification. + * <p>This must be specified for keys which are used for encryption/decryption. * * @hide */ - public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) { - mDigests = digests; + public Builder setEncryptionPaddings(String... paddings) { + mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); 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 + * 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. * * @hide */ - public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) { - mPaddings = paddings; + public Builder setSignaturePaddings(String... paddings) { + mSignaturePaddings = 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 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. * * @hide */ - public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { - mBlockModes = blockModes; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; } @@ -752,7 +785,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * @hide */ public Builder setUserAuthenticators( - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { mUserAuthenticators = userAuthenticators; return this; } @@ -800,7 +833,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mKeyValidityForConsumptionEnd, mPurposes, mDigests, - mPaddings, + mEncryptionPaddings, + mSignaturePaddings, mBlockModes, mRandomizedEncryptionRequired, mUserAuthenticators, diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java index 487eac0..7bc6378 100644 --- a/keystore/java/android/security/KeyStoreCipherSpi.java +++ b/keystore/java/android/security/KeyStoreCipherSpi.java @@ -48,68 +48,67 @@ 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; /** Whether this transformation requires an IV. */ @@ -138,15 +137,15 @@ 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; mIvRequired = ivUsed; } @@ -236,9 +235,9 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry } 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(); 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 f69e7d1..a19bbda 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -37,36 +37,36 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp public static class HmacSHA1 extends KeyStoreHmacSpi { public HmacSHA1() { - super(KeyStoreKeyConstraints.Digest.SHA1); + super(KeymasterDefs.KM_DIGEST_SHA1); } } public static class HmacSHA224 extends KeyStoreHmacSpi { public HmacSHA224() { - super(KeyStoreKeyConstraints.Digest.SHA224); + super(KeymasterDefs.KM_DIGEST_SHA_2_224); } } public static class HmacSHA256 extends KeyStoreHmacSpi { public HmacSHA256() { - super(KeyStoreKeyConstraints.Digest.SHA256); + super(KeymasterDefs.KM_DIGEST_SHA_2_256); } } public static class HmacSHA384 extends KeyStoreHmacSpi { public HmacSHA384() { - super(KeyStoreKeyConstraints.Digest.SHA384); + super(KeymasterDefs.KM_DIGEST_SHA_2_384); } } public static class HmacSHA512 extends KeyStoreHmacSpi { public HmacSHA512() { - super(KeyStoreKeyConstraints.Digest.SHA512); + 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; @@ -76,9 +76,9 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp private IBinder mOperationToken; private Long mOperationHandle; - protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest) { - mDigest = digest; - mMacSizeBytes = KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest); + protected KeyStoreHmacSpi(int keymasterDigest) { + mKeymasterDigest = keymasterDigest; + mMacSizeBytes = KeymasterUtils.getDigestOutputSizeBytes(keymasterDigest); } @Override @@ -129,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 1f5d400..0000000 --- a/keystore/java/android/security/KeyStoreKeyCharacteristics.java +++ /dev/null @@ -1,63 +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, Origin.IMPORTED}) - 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; - - /** - * @hide - */ - public static @OriginEnum int fromKeymaster(int origin) { - switch (origin) { - case KeymasterDefs.KM_ORIGIN_HARDWARE: - return GENERATED; - 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 055b702..0000000 --- a/keystore/java/android/security/KeyStoreKeyConstraints.java +++ /dev/null @@ -1,937 +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, Algorithm.RSA, Algorithm.EC}) - public @interface AlgorithmEnum {} - - /** - * Key algorithm. - */ - public static abstract class Algorithm { - private Algorithm() {} - - /** - * Key algorithm: AES. - */ - public static final int AES = 1 << 0; - - /** - * Key algorithm: HMAC. - */ - public static final int HMAC = 1 << 1; - - /** - * Key algorithm: RSA. - */ - public static final int RSA = 1 << 2; - - /** - * Key algorithm: EC. - */ - public static final int EC = 1 << 3; - - /** - * @hide - */ - public static int toKeymaster(@AlgorithmEnum int algorithm) { - switch (algorithm) { - case AES: - return KeymasterDefs.KM_ALGORITHM_AES; - case HMAC: - return KeymasterDefs.KM_ALGORITHM_HMAC; - case RSA: - return KeymasterDefs.KM_ALGORITHM_RSA; - case EC: - return KeymasterDefs.KM_ALGORITHM_EC; - 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; - case KeymasterDefs.KM_ALGORITHM_RSA: - return RSA; - case KeymasterDefs.KM_ALGORITHM_EC: - return EC; - 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"; - case RSA: - return "RSA"; - case EC: - return "EC"; - 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.MD5: - return "HmacMD5"; - case Digest.SHA1: - return "HmacSHA1"; - case Digest.SHA224: - return "HmacSHA224"; - case Digest.SHA256: - return "HmacSHA256"; - case Digest.SHA384: - return "HmacSHA384"; - case Digest.SHA512: - return "HmacSHA512"; - default: - throw new IllegalArgumentException( - "Unsupported HMAC digest: " + digest); - } - default: - throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm); - } - } - - /** - * @hide - */ - public static String toJCAKeyPairAlgorithm(@AlgorithmEnum int algorithm) { - switch (algorithm) { - case RSA: - return "RSA"; - case EC: - return "EC"; - default: - throw new IllegalArgumentException("Unsupported key alorithm: " + algorithm); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = { - Padding.NONE, - Padding.PKCS7, - Padding.RSA_PKCS1_ENCRYPTION, - Padding.RSA_PKCS1_SIGNATURE, - Padding.RSA_OAEP, - Padding.RSA_PSS, - }) - 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; - - /** - * RSA PKCS#1 v1.5 padding for encryption/decryption. - */ - public static final int RSA_PKCS1_ENCRYPTION = 1 << 2; - - /** - * RSA PKCS#1 v1.5 padding for signatures. - */ - public static final int RSA_PKCS1_SIGNATURE = 1 << 3; - - /** - * RSA Optimal Asymmetric Encryption Padding (OAEP). - */ - public static final int RSA_OAEP = 1 << 4; - - /** - * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding. - */ - public static final int RSA_PSS = 1 << 5; - - /** - * @hide - */ - public static int toKeymaster(int padding) { - switch (padding) { - case NONE: - return KeymasterDefs.KM_PAD_NONE; - case PKCS7: - return KeymasterDefs.KM_PAD_PKCS7; - case RSA_PKCS1_ENCRYPTION: - return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT; - case RSA_PKCS1_SIGNATURE: - return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN; - case RSA_OAEP: - return KeymasterDefs.KM_PAD_RSA_OAEP; - case RSA_PSS: - return KeymasterDefs.KM_PAD_RSA_PSS; - 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; - case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT: - return RSA_PKCS1_ENCRYPTION; - case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN: - return RSA_PKCS1_SIGNATURE; - case KeymasterDefs.KM_PAD_RSA_OAEP: - return RSA_OAEP; - case KeymasterDefs.KM_PAD_RSA_PSS: - return RSA_PSS; - 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"; - case RSA_PKCS1_ENCRYPTION: - return "RSA PKCS#1 (encryption)"; - case RSA_PKCS1_SIGNATURE: - return "RSA PKCS#1 (signature)"; - case RSA_OAEP: - return "RSA OAEP"; - case RSA_PSS: - return "RSA PSS"; - default: - throw new IllegalArgumentException("Unknown padding: " + padding); - } - } - - /** - * @hide - */ - public static @PaddingEnum int fromJCACipherPadding(String padding) { - String paddingLower = padding.toLowerCase(Locale.US); - if ("nopadding".equals(paddingLower)) { - return NONE; - } else if ("pkcs7padding".equals(paddingLower)) { - return PKCS7; - } else if ("pkcs1padding".equals(paddingLower)) { - return RSA_PKCS1_ENCRYPTION; - } else if (("oaeppadding".equals(paddingLower)) - || ((paddingLower.startsWith("oaepwith")) - && (paddingLower.endsWith("padding")))) { - return RSA_OAEP; - } 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.MD5, - Digest.SHA1, - Digest.SHA224, - Digest.SHA256, - Digest.SHA384, - Digest.SHA512, - }) - public @interface DigestEnum {} - - /** - * Digests that can be used with a key when signing or generating Message Authentication - * Codes (MACs). - */ - public static abstract class Digest { - private Digest() {} - - /** - * No digest: sign/authenticate the raw message. - */ - public static final int NONE = 1 << 0; - - /** - * MD5 digest. - */ - public static final int MD5 = 1 << 1; - - /** - * SHA-1 digest. - */ - public static final int SHA1 = 1 << 2; - - /** - * SHA-2 224 (aka SHA-224) digest. - */ - public static final int SHA224 = 1 << 3; - - /** - * SHA-2 256 (aka SHA-256) digest. - */ - public static final int SHA256 = 1 << 4; - - /** - * SHA-2 384 (aka SHA-384) digest. - */ - public static final int SHA384 = 1 << 5; - - /** - * SHA-2 512 (aka SHA-512) digest. - */ - public static final int SHA512 = 1 << 6; - - /** - * @hide - */ - public static String toString(@DigestEnum int digest) { - switch (digest) { - case NONE: - return "NONE"; - case MD5: - return "MD5"; - case SHA1: - return "SHA-1"; - case SHA224: - return "SHA-224"; - case SHA256: - return "SHA-256"; - case SHA384: - return "SHA-384"; - case SHA512: - return "SHA-512"; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static String allToString(@DigestEnum int digests) { - StringBuilder result = new StringBuilder("["); - boolean firstValue = true; - for (@DigestEnum int digest : getSetFlags(digests)) { - if (firstValue) { - firstValue = false; - } else { - result.append(", "); - } - result.append(toString(digest)); - } - result.append(']'); - return result.toString(); - } - - /** - * @hide - */ - public static int toKeymaster(@DigestEnum int digest) { - switch (digest) { - case NONE: - return KeymasterDefs.KM_DIGEST_NONE; - 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("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static @DigestEnum int fromKeymaster(int digest) { - switch (digest) { - case KeymasterDefs.KM_DIGEST_NONE: - return NONE; - case KeymasterDefs.KM_DIGEST_MD5: - return MD5; - case KeymasterDefs.KM_DIGEST_SHA1: - return SHA1; - case KeymasterDefs.KM_DIGEST_SHA_2_224: - return SHA224; - case KeymasterDefs.KM_DIGEST_SHA_2_256: - return SHA256; - case KeymasterDefs.KM_DIGEST_SHA_2_384: - return SHA384; - case KeymasterDefs.KM_DIGEST_SHA_2_512: - return SHA512; - default: - throw new IllegalArgumentException("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")) { - String digestLower = algorithmLower.substring("hmac".length()); - if ("md5".equals(digestLower)) { - return MD5; - } else if ("sha1".equals(digestLower)) { - return SHA1; - } else if ("sha224".equals(digestLower)) { - return SHA224; - } else if ("sha256".equals(digestLower)) { - return SHA256; - } else if ("sha384".equals(digestLower)) { - return SHA384; - } else if ("sha512".equals(digestLower)) { - return SHA512; - } else { - throw new IllegalArgumentException("Unsupported digest: " + digestLower); - } - } else { - return null; - } - } - - /** - * @hide - */ - public static String toJCASignatureAlgorithmDigest(@DigestEnum int digest) { - switch (digest) { - case NONE: - return "NONE"; - case MD5: - return "MD5"; - case SHA1: - return "SHA1"; - case SHA224: - return "SHA224"; - case SHA256: - return "SHA256"; - case SHA384: - return "SHA384"; - case SHA512: - return "SHA512"; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - - /** - * @hide - */ - public static Integer getOutputSizeBytes(@DigestEnum int digest) { - switch (digest) { - case NONE: - return null; - case MD5: - return 128 / 8; - case SHA1: - return 160 / 8; - case SHA224: - return 224 / 8; - case SHA256: - return 256 / 8; - case SHA384: - return 384 / 8; - case SHA512: - return 512 / 8; - default: - throw new IllegalArgumentException("Unknown digest: " + digest); - } - } - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR, BlockMode.GCM}) - public @interface BlockModeEnum {} - - /** - * Block modes that can be used when encrypting/decrypting using a key. - */ - public static abstract class BlockMode { - private BlockMode() {} - - /** Electronic Codebook (ECB) block mode. */ - public static final 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; - - /** Galois/Counter Mode (GCM) block mode. */ - public static final int GCM = 1 << 3; - - /** - * Set of block modes compatible with IND-CPA if used correctly. - * - * @hide - */ - public static final @BlockModeEnum int IND_CPA_COMPATIBLE_MODES = - CBC | CTR | GCM; - - /** - * @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; - case GCM: - return KeymasterDefs.KM_MODE_GCM; - 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; - case KeymasterDefs.KM_MODE_GCM: - return GCM; - 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"; - case GCM: - return "GCM"; - default: - throw new IllegalArgumentException("Unknown block mode: " + mode); - } - } - - /** - * @hide - */ - public static String allToString(@BlockModeEnum int modes) { - StringBuilder result = new StringBuilder("["); - boolean firstValue = true; - for (@BlockModeEnum int mode : getSetFlags(modes)) { - if (firstValue) { - firstValue = false; - } else { - result.append(", "); - } - result.append(toString(mode)); - } - result.append(']'); - return result.toString(); - } - - /** - * @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 if ("gcm".equals(modeLower)) { - return GCM; - } 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; - - /** - * @hide - */ - public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) { - switch (userAuthenticator) { - case LOCK_SCREEN: - return KeymasterDefs.HW_AUTH_PASSWORD; - 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; - 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"; - 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 a500786..87e7ee6 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -37,68 +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); } } protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi { - protected HmacBase(@KeyStoreKeyConstraints.DigestEnum int digest) { - super(KeyStoreKeyConstraints.Algorithm.HMAC, - digest, - KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest) * 8); + protected HmacBase(int keymasterDigest) { + super(KeymasterDefs.KM_ALGORITHM_HMAC, + keymasterDigest, + KeymasterUtils.getDigestOutputSizeBytes(keymasterDigest) * 8); } } public static class HmacSHA1 extends HmacBase { public HmacSHA1() { - super(KeyStoreKeyConstraints.Digest.SHA1); + super(KeymasterDefs.KM_DIGEST_SHA1); } } public static class HmacSHA224 extends HmacBase { public HmacSHA224() { - super(KeyStoreKeyConstraints.Digest.SHA224); + super(KeymasterDefs.KM_DIGEST_SHA_2_224); } } public static class HmacSHA256 extends HmacBase { public HmacSHA256() { - super(KeyStoreKeyConstraints.Digest.SHA256); + super(KeymasterDefs.KM_DIGEST_SHA_2_256); } } public static class HmacSHA384 extends HmacBase { public HmacSHA384() { - super(KeyStoreKeyConstraints.Digest.SHA384); + super(KeymasterDefs.KM_DIGEST_SHA_2_384); } } public static class HmacSHA512 extends HmacBase { public HmacSHA512() { - super(KeyStoreKeyConstraints.Digest.SHA512); + 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; } @@ -117,58 +117,55 @@ 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; args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits); - @KeyStoreKeyConstraints.PurposeEnum int purposes = spec.getPurposes(); - @KeyStoreKeyConstraints.BlockModeEnum int blockModes = spec.getBlockModes(); - if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) + @KeyStoreKeyProperties.PurposeEnum int purposes = spec.getPurposes(); + int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes( + spec.getBlockModes()); + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) && (spec.isRandomizedEncryptionRequired())) { - @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes = - blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES; - if (incompatibleBlockModes != 0) { - throw new IllegalStateException( - "Randomized encryption (IND-CPA) required but may be violated by block" - + " mode(s): " - + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes) - + ". See KeyGeneratorSpec documentation."); + 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(blockModes)) { - 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())); } if (spec.getUserAuthenticationValidityDurationSeconds() != -1) { @@ -185,7 +182,7 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { (spec.getKeyValidityForConsumptionEnd() != null) ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE)); - if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0) + if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0) && (!spec.isRandomizedEncryptionRequired())) { // Permit caller-provided IV when encrypting with this key args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); @@ -206,7 +203,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..d8ad1d3 --- /dev/null +++ b/keystore/java/android/security/KeyStoreKeyProperties.java @@ -0,0 +1,274 @@ +/* + * 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. + * + * @hide + */ +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}) + 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; + + /** + * @hide + */ + public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) { + switch (userAuthenticator) { + case LOCK_SCREEN: + return KeymasterDefs.HW_AUTH_PASSWORD; + 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; + 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"; + default: + throw new IllegalArgumentException( + "Unknown user authenticator: " + userAuthenticator); + } + } + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({Origin.GENERATED, Origin.IMPORTED}) + 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; + + /** + * @hide + */ + public static @OriginEnum int fromKeymaster(int origin) { + switch (origin) { + case KeymasterDefs.KM_ORIGIN_HARDWARE: + return GENERATED; + case KeymasterDefs.KM_ORIGIN_IMPORTED: + return IMPORTED; + 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 27b444e..861ed34 100644 --- a/keystore/java/android/security/KeyStoreKeySpec.java +++ b/keystore/java/android/security/KeyStoreKeySpec.java @@ -29,17 +29,17 @@ public class KeyStoreKeySpec implements KeySpec { private final String mKeystoreAlias; private final int mKeySize; private final boolean mTeeBacked; - private final @KeyStoreKeyCharacteristics.OriginEnum int mOrigin; + 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; @@ -48,18 +48,18 @@ public class KeyStoreKeySpec implements KeySpec { */ KeyStoreKeySpec(String keystoreKeyAlias, boolean teeBacked, - @KeyStoreKeyCharacteristics.OriginEnum int origin, + @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) { mKeystoreAlias = keystoreKeyAlias; mTeeBacked = teeBacked; @@ -69,10 +69,12 @@ public class KeyStoreKeySpec implements KeySpec { 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; @@ -96,7 +98,7 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the origin of the key. */ - public @KeyStoreKeyCharacteristics.OriginEnum int getOrigin() { + public @KeyStoreKeyProperties.OriginEnum int getOrigin() { return mOrigin; } @@ -137,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); } /** @@ -175,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; } @@ -184,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 fb534b4..9fce177 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -43,24 +43,26 @@ public final class KeyStoreParameter implements ProtectionParameter { 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 @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 @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; private KeyStoreParameter(int flags, Date keyValidityStart, Date keyValidityForOriginationEnd, Date keyValidityForConsumptionEnd, - @KeyStoreKeyConstraints.PurposeEnum int purposes, - @KeyStoreKeyConstraints.PaddingEnum int paddings, - @KeyStoreKeyConstraints.DigestEnum Integer digests, - @KeyStoreKeyConstraints.BlockModeEnum int blockModes, + @KeyStoreKeyProperties.PurposeEnum int purposes, + String[] encryptionPaddings, + String[] signaturePaddings, + String[] digests, + String[] blockModes, boolean randomizedEncryptionRequired, - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators, + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators, int userAuthenticationValidityDurationSeconds) { if ((userAuthenticationValidityDurationSeconds < 0) && (userAuthenticationValidityDurationSeconds != -1)) { @@ -73,9 +75,12 @@ 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; @@ -133,37 +138,48 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @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. + * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. * * @hide */ - public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { - return mPaddings; + public String[] getEncryptionPaddings() { + return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } /** - * Gets the set of digests to which the key is restricted. + * Gets the set of padding schemes with which the key can be used when signing or verifying + * signatures. * - * @throws IllegalStateException if this restriction has not been specified. + * @hide + */ + public String[] getSignaturePaddings() { + return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); + } + + /** + * Gets the set of digest algorithms with which the key can be used. + * + * @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() * @@ -174,12 +190,12 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * Gets the set of block modes to which the key is restricted. + * Gets the set of block modes with which the key can be used. * * @hide */ - public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { - return mBlockModes; + public String[] getBlockModes() { + return ArrayUtils.cloneIfNotEmpty(mBlockModes); } /** @@ -205,7 +221,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @hide */ - public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + public @KeyStoreKeyProperties.UserAuthenticatorEnum int getUserAuthenticators() { return mUserAuthenticators; } @@ -244,12 +260,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 @KeyStoreKeyProperties.PurposeEnum int mPurposes; + private String[] mEncryptionPaddings; + private String[] mSignaturePaddings; + private String[] mDigests; + private String[] mBlockModes; private boolean mRandomizedEncryptionRequired = true; - private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; + private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; /** @@ -342,55 +359,70 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * 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. * * @hide */ - 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. + * <p>This must be specified for keys which are used for encryption/decryption. * * @hide */ - 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. + * 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 must be specified for RSA keys which are used for signing/verification. + * + * @hide + */ + public Builder setSignaturePaddings(String... paddings) { + mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); + return this; + } + + + /** + * 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>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. + * <p>For HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()}. For + * asymmetric signing keys this constraint must be specified. * * @hide */ - 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 block modes. Attempts to use the - * key with any other block modes will be rejected. + * 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 symmetric encryption/decryption keys. + * <p>This must be specified for encryption/decryption keys. * * @hide */ - public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) { - mBlockModes = blockModes; + public Builder setBlockModes(String... blockModes) { + mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; } @@ -449,7 +481,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * @hide */ public Builder setUserAuthenticators( - @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) { + @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) { mUserAuthenticators = userAuthenticators; return this; } @@ -484,7 +516,8 @@ public final class KeyStoreParameter implements ProtectionParameter { mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd, mPurposes, - mPaddings, + mEncryptionPaddings, + mSignaturePaddings, mDigests, mBlockModes, mRandomizedEncryptionRequired, diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java index 99a8168..33073a4 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; @@ -71,84 +75,90 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { } boolean teeBacked; - @KeyStoreKeyCharacteristics.OriginEnum int origin; + @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 { if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { teeBacked = true; - origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster( + origin = KeyStoreKeyProperties.Origin.fromKeymaster( keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { teeBacked = false; - origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster( + origin = KeyStoreKeyProperties.Origin.fromKeymaster( keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); } else { throw new InvalidKeySpecException("Key origin not available"); } - 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); return new KeyStoreKeySpec(entryAlias, teeBacked, @@ -158,14 +168,13 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { keyValidityForOriginationEnd, keyValidityForConsumptionEnd, purposes, - algorithm, - paddings, + encryptionPaddings, + EmptyArray.STRING, // no signature paddings -- this is symmetric crypto digests, blockModes, userAuthenticators, teeEnforcedUserAuthenticators, - ((userAuthenticationValidityDurationSeconds != null) - ? userAuthenticationValidityDurationSeconds : -1)); + userAuthenticationValidityDurationSeconds); } @Override 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; + } } |