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