summaryrefslogtreecommitdiff
path: root/keystore/java/android/security/keystore2
diff options
context:
space:
mode:
authorJanis Danisevskis <jdanis@google.com>2020-10-09 14:23:17 -0700
committerJanis Danisevskis <jdanis@google.com>2020-11-13 19:55:40 -0800
commit4545933da590ad92faaf075623a0d00a75debfdd (patch)
tree2625680b5bea8c5dce8a52da52c01f893bc48cb0 /keystore/java/android/security/keystore2
parent4be5005c05a261ad9cdfb72d968871b2ba648798 (diff)
Keystore 2.0 SPI: Evolve the Crypto SPI.
This patch evolves the Crypto SPI to use the new Keystore 2.0 shim. The main changes are: * The SPI uses the AIDL defined KeyParameter instead of KeymasterArguments. * Operations are created directly from the KeystoreSecurityLevel that is part of the AndroidKeyStoreKey object. Also this patch deletes the DeletatingX509Certificate class. This is no longer needed, because public key operations are no longer performed by Keystore 2.0. We can delegate public certificate operations simply by wrapping such certificates into public keys that are understood by other providers, such as BouncyCastle. Bug: 159476414 Test: None Change-Id: Ice874a8121d80bf788da059b4e8420c7dd799d81
Diffstat (limited to 'keystore/java/android/security/keystore2')
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java51
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java115
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java170
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java42
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java113
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java13
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java50
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java20
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java125
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java1052
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java49
-rw-r--r--keystore/java/android/security/keystore2/DelegatingX509Certificate.java212
-rw-r--r--keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java34
13 files changed, 956 insertions, 1090 deletions
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
index 275dcef8a78c..70713a47ad6d 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -16,10 +16,11 @@
package android.security.keystore2;
-import android.security.keymaster.KeymasterArguments;
+import android.annotation.NonNull;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
+import android.system.keystore2.KeyParameter;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
@@ -30,6 +31,7 @@ import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
+import java.util.List;
import javax.crypto.CipherSpi;
import javax.crypto.spec.IvParameterSpec;
@@ -67,15 +69,13 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
}
- public static class NoPadding extends
- AndroidKeyStore3DESCipherSpi.ECB {
+ public static class NoPadding extends ECB {
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
}
- public static class PKCS7Padding extends
- AndroidKeyStore3DESCipherSpi.ECB {
+ public static class PKCS7Padding extends ECB {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
@@ -87,15 +87,13 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
}
- public static class NoPadding extends
- AndroidKeyStore3DESCipherSpi.CBC {
+ public static class NoPadding extends CBC {
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
}
- public static class PKCS7Padding extends
- AndroidKeyStore3DESCipherSpi.CBC {
+ public static class PKCS7Padding extends CBC {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
@@ -254,7 +252,7 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
}
@Override
- protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) {
+ protected void addAlgorithmSpecificParametersToBegin(@NonNull List<KeyParameter> parameters) {
if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
// IV is being reused for encryption: this violates security best practices.
throw new IllegalStateException(
@@ -262,23 +260,38 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+ " practices.");
}
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_3DES);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
- if ((mIvRequired) && (mIv != null)) {
- keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM,
+ KeymasterDefs.KM_ALGORITHM_3DES
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_BLOCK_MODE,
+ mKeymasterBlockMode
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING,
+ mKeymasterPadding
+ ));
+
+ if (mIvRequired && (mIv != null)) {
+ parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv));
}
}
@Override
protected void loadAlgorithmSpecificParametersFromBeginResult(
- KeymasterArguments keymasterArgs) {
+ KeyParameter[] parameters) {
mIvHasBeenUsed = true;
// NOTE: Keymaster doesn't always return an IV, even if it's used.
- byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
- if ((returnedIv != null) && (returnedIv.length == 0)) {
- returnedIv = null;
+ byte[] returnedIv = null;
+ if (parameters != null) {
+ for (KeyParameter p : parameters) {
+ if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
+ returnedIv = p.blob;
+ break;
+ }
+ }
}
if (mIvRequired) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index 43381c0078d9..dd094b7a5fd0 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -18,15 +18,13 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.IBinder;
-import android.security.KeyStore;
import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterArguments;
+import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream;
+import android.system.keystore2.KeyParameter;
import libcore.util.EmptyArray;
@@ -41,6 +39,7 @@ import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
+import java.util.List;
import javax.crypto.CipherSpi;
import javax.crypto.spec.GCMParameterSpec;
@@ -175,26 +174,25 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
@NonNull
@Override
protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
- KeyStore keyStore, IBinder operationToken) {
- KeyStoreCryptoOperationStreamer
- streamer = new KeyStoreCryptoOperationChunkedStreamer(
+ KeyStoreOperation operation) {
+ KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- keyStore, operationToken), 0);
+ operation), 0);
if (isEncrypting()) {
return streamer;
} else {
// When decrypting, to avoid leaking unauthenticated plaintext, do not return any
// plaintext before ciphertext is authenticated by KeyStore.finish.
- return new AndroidKeyStoreAuthenticatedAESCipherSpi.BufferAllOutputUntilDoFinalStreamer(streamer);
+ return new BufferAllOutputUntilDoFinalStreamer(streamer);
}
}
@NonNull
@Override
protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
- KeyStore keyStore, IBinder operationToken) {
+ KeyStoreOperation operation) {
return new KeyStoreCryptoOperationChunkedStreamer(
- new AndroidKeyStoreAuthenticatedAESCipherSpi.AdditionalAuthenticationDataStream(keyStore, operationToken), 0);
+ new AdditionalAuthenticationDataStream(operation), 0);
}
@Override
@@ -214,17 +212,19 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
@Override
protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
- keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits);
+ @NonNull List<KeyParameter> parameters) {
+ super.addAlgorithmSpecificParametersToBegin(parameters);
+ parameters.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_MAC_LENGTH,
+ mTagLengthBits
+ ));
}
protected final int getTagLengthBits() {
return mTagLengthBits;
}
- public static final class NoPadding extends
- AndroidKeyStoreAuthenticatedAESCipherSpi.GCM {
+ public static final class NoPadding extends GCM {
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
@@ -290,31 +290,45 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
@Override
protected void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
+ @NonNull List<KeyParameter> parameters) {
if ((isEncrypting()) && (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.");
}
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM,
+ KeymasterDefs.KM_ALGORITHM_AES
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_BLOCK_MODE,
+ mKeymasterBlockMode
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING,
+ mKeymasterPadding
+ ));
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
if (mIv != null) {
- keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
+ parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv));
}
}
@Override
protected final void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs) {
+ KeyParameter[] parameters) {
mIvHasBeenUsed = true;
// NOTE: Keymaster doesn't always return an IV, even if it's used.
- byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
- if ((returnedIv != null) && (returnedIv.length == 0)) {
- returnedIv = null;
+ byte[] returnedIv = null;
+ if (parameters != null) {
+ for (KeyParameter p : parameters) {
+ if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
+ returnedIv = p.blob;
+ break;
+ }
+ }
}
if (mIv == null) {
@@ -353,8 +367,7 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
private ByteArrayOutputStream mBufferedOutput = new ByteArrayOutputStream();
private long mProducedOutputSizeBytes;
- private BufferAllOutputUntilDoFinalStreamer(
- KeyStoreCryptoOperationStreamer delegate) {
+ private BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate) {
mDelegate = delegate;
}
@@ -374,9 +387,8 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
@Override
public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
- byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
- byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature,
- additionalEntropy);
+ byte[] signature) throws KeyStoreException {
+ byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature);
if (output != null) {
try {
mBufferedOutput.write(output);
@@ -407,48 +419,21 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
*/
private static class AdditionalAuthenticationDataStream implements Stream {
- private final KeyStore mKeyStore;
- private final IBinder mOperationToken;
+ private final KeyStoreOperation mOperation;
- private AdditionalAuthenticationDataStream(KeyStore keyStore, IBinder operationToken) {
- mKeyStore = keyStore;
- mOperationToken = operationToken;
+ private AdditionalAuthenticationDataStream(KeyStoreOperation operation) {
+ mOperation = operation;
}
@Override
- public OperationResult update(byte[] input) {
- KeymasterArguments keymasterArgs = new KeymasterArguments();
- keymasterArgs.addBytes(KeymasterDefs.KM_TAG_ASSOCIATED_DATA, input);
-
- // KeyStore does not reflect AAD in inputConsumed, but users of Stream rely on this
- // field. We fix this discrepancy here. KeyStore.update contract is that all of AAD
- // has been consumed if the method succeeds.
- OperationResult result = mKeyStore.update(mOperationToken, keymasterArgs, null);
- if (result.resultCode == KeyStore.NO_ERROR) {
- result = new OperationResult(
- result.resultCode,
- result.token,
- result.operationHandle,
- input.length, // inputConsumed
- result.output,
- result.outParams);
- }
- return result;
+ public byte[] update(byte[] input) throws KeyStoreException {
+ mOperation.updateAad(input);
+ return null;
}
@Override
- public OperationResult finish(byte[] input, byte[] signature, byte[] additionalEntropy) {
- if ((additionalEntropy != null) && (additionalEntropy.length > 0)) {
- throw new ProviderException("AAD stream does not support additional entropy");
- }
- return new OperationResult(
- KeyStore.NO_ERROR,
- mOperationToken,
- 0, // operation handle -- nobody cares about this being returned from finish
- 0, // inputConsumed
- EmptyArray.BYTE, // output
- new KeymasterArguments() // additional params returned by finish
- );
+ public byte[] finish(byte[] input, byte[] signature) {
+ return null;
}
}
} \ No newline at end of file
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 94b5c4d23afe..b785ee5c6966 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -19,14 +19,11 @@ package android.security.keystore2;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.IBinder;
-import android.security.KeyStore;
import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterArguments;
+import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
-import android.security.keystore.KeyStoreConnectException;
import android.security.keystore.KeyStoreCryptoOperation;
+import android.system.keystore2.KeyParameter;
import libcore.util.EmptyArray;
@@ -48,6 +45,8 @@ import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.List;
import javax.crypto.AEADBadTagException;
import javax.crypto.BadPaddingException;
@@ -66,7 +65,7 @@ import javax.crypto.spec.SecretKeySpec;
* @hide
*/
abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation {
- private final KeyStore mKeyStore;
+ private static final String TAG = "AndroidKeyStoreCipherSpiBase";
// Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
// doFinal finishes.
@@ -76,15 +75,20 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
private SecureRandom mRng;
/**
- * Token referencing this operation inside keystore service. It is initialized by
- * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some error
- * conditions in between.
+ * Object representing this operation inside keystore service. It is initialized
+ * by {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some
+ * error conditions in between.
*/
- private IBinder mOperationToken;
- private long mOperationHandle;
+ private KeyStoreOperation mOperation;
+ /**
+ * The operation challenge is required when an operation needs user authorization.
+ * The challenge is subjected to an authenticator, e.g., Gatekeeper or a biometric
+ * authenticator, and included in the authentication token minted by this authenticator.
+ * It may be null, if the operation does not require authorization.
+ */
+ private long mOperationChallenge;
private KeyStoreCryptoOperationStreamer mMainDataStreamer;
- private KeyStoreCryptoOperationStreamer
- mAdditionalAuthenticationDataStreamer;
+ private KeyStoreCryptoOperationStreamer mAdditionalAuthenticationDataStreamer;
private boolean mAdditionalAuthenticationDataStreamerClosed;
/**
@@ -96,7 +100,16 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
private Exception mCachedException;
AndroidKeyStoreCipherSpiBase() {
- mKeyStore = KeyStore.getInstance();
+ mOperation = null;
+ mEncrypting = false;
+ mKeymasterPurposeOverride = -1;
+ mKey = null;
+ mRng = null;
+ mOperationChallenge = 0;
+ mMainDataStreamer = null;
+ mAdditionalAuthenticationDataStreamer = null;
+ mAdditionalAuthenticationDataStreamerClosed = false;
+ mCachedException = null;
}
@Override
@@ -177,6 +190,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
mRng = random;
}
+ private void abortOperation() {
+ KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+ mOperation = null;
+ }
+
/**
* Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
* cipher instance.
@@ -186,16 +204,12 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
*/
@CallSuper
protected void resetAll() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
+ abortOperation();
mEncrypting = false;
mKeymasterPurposeOverride = -1;
mKey = null;
mRng = null;
- mOperationToken = null;
- mOperationHandle = 0;
+ mOperationChallenge = 0;
mMainDataStreamer = null;
mAdditionalAuthenticationDataStreamer = null;
mAdditionalAuthenticationDataStreamerClosed = false;
@@ -212,12 +226,8 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
*/
@CallSuper
protected void resetWhilePreservingInitState() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- mOperationToken = null;
- mOperationHandle = 0;
+ abortOperation();
+ mOperationChallenge = 0;
mMainDataStreamer = null;
mAdditionalAuthenticationDataStreamer = null;
mAdditionalAuthenticationDataStreamerClosed = false;
@@ -236,10 +246,8 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
throw new IllegalStateException("Not initialized");
}
- KeymasterArguments keymasterInputArgs = new KeymasterArguments();
- addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
- byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
- mRng, getAdditionalEntropyAmountForBegin());
+ List<KeyParameter> parameters = new ArrayList<>();
+ addAlgorithmSpecificParametersToBegin(parameters);
int purpose;
if (mKeymasterPurposeOverride != -1) {
@@ -248,46 +256,38 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
purpose = mEncrypting
? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT;
}
- OperationResult opResult = mKeyStore.begin(
- mKey.getAlias(),
- purpose,
- true, // permit aborting this operation if keystore runs out of resources
- keymasterInputArgs,
- additionalEntropy,
- mKey.getUid());
- if (opResult == null) {
- throw new KeyStoreConnectException();
- }
-
- // Store operation token and handle regardless of the error code returned by KeyStore to
- // ensure that the operation gets aborted immediately if the code below throws an exception.
- mOperationToken = opResult.token;
- mOperationHandle = opResult.operationHandle;
-
- // If necessary, throw an exception due to KeyStore operation having failed.
- GeneralSecurityException e = KeyStoreCryptoOperationUtils.getExceptionForCipherInit(
- mKeyStore, mKey, opResult.resultCode);
- if (e != null) {
- if (e instanceof InvalidKeyException) {
- throw (InvalidKeyException) e;
- } else if (e instanceof InvalidAlgorithmParameterException) {
- throw (InvalidAlgorithmParameterException) e;
- } else {
- throw new ProviderException("Unexpected exception type", e);
+
+ parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
+
+ try {
+ mOperation = mKey.getSecurityLevel().createOperation(
+ mKey.getKeyIdDescriptor(),
+ parameters
+ );
+ } catch (KeyStoreException keyStoreException) {
+ GeneralSecurityException e = KeyStoreCryptoOperationUtils.getExceptionForCipherInit(
+ mKey, keyStoreException);
+ if (e != null) {
+ if (e instanceof InvalidKeyException) {
+ throw (InvalidKeyException) e;
+ } else if (e instanceof InvalidAlgorithmParameterException) {
+ throw (InvalidAlgorithmParameterException) e;
+ } else {
+ throw new ProviderException("Unexpected exception type", e);
+ }
}
}
- if (mOperationToken == null) {
- throw new ProviderException("Keystore returned null operation token");
- }
- if (mOperationHandle == 0) {
- throw new ProviderException("Keystore returned invalid operation handle");
- }
+ // Now we check if we got an operation challenge. This indicates that user authorization
+ // is required. And if we got a challenge we check if the authorization can possibly
+ // succeed.
+ mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(
+ mOperation, mKey);
- loadAlgorithmSpecificParametersFromBeginResult(opResult.outParams);
- mMainDataStreamer = createMainDataStreamer(mKeyStore, opResult.token);
+ loadAlgorithmSpecificParametersFromBeginResult(mOperation.getParameters());
+ mMainDataStreamer = createMainDataStreamer(mOperation);
mAdditionalAuthenticationDataStreamer =
- createAdditionalAuthenticationDataStreamer(mKeyStore, opResult.token);
+ createAdditionalAuthenticationDataStreamer(mOperation);
mAdditionalAuthenticationDataStreamerClosed = false;
}
@@ -299,10 +299,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
*/
@NonNull
protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
- KeyStore keyStore, IBinder operationToken) {
+ KeyStoreOperation operation) {
return new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- keyStore, operationToken), 0);
+ operation), 0);
}
/**
@@ -314,8 +314,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
*/
@Nullable
protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
- @SuppressWarnings("unused") KeyStore keyStore,
- @SuppressWarnings("unused") IBinder operationToken) {
+ @SuppressWarnings("unused") KeyStoreOperation operation) {
return null;
}
@@ -358,9 +357,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
try {
output = mAdditionalAuthenticationDataStreamer.doFinal(
EmptyArray.BYTE, 0, 0,
- null, // no signature
- null // no additional entropy needed flushing AAD
- );
+ null); // no signature
} finally {
mAdditionalAuthenticationDataStreamerClosed = true;
}
@@ -503,17 +500,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
byte[] output;
try {
flushAAD();
- byte[] additionalEntropy =
- KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
- mRng, getAdditionalEntropyAmountForFinish());
output = mMainDataStreamer.doFinal(
input, inputOffset, inputLen,
- null, // no signature involved
- additionalEntropy);
+ null); // no signature involved
} catch (KeyStoreException e) {
switch (e.getErrorCode()) {
- case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
- throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
throw (BadPaddingException) new BadPaddingException().initCause(e);
case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
@@ -742,10 +733,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
public void finalize() throws Throwable {
try {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
+ abortOperation();
} finally {
super.finalize();
}
@@ -753,7 +741,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
@Override
public final long getOperationHandle() {
- return mOperationHandle;
+ return mOperationChallenge;
}
protected final void setKey(@NonNull AndroidKeyStoreKey key) {
@@ -779,11 +767,6 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
return mEncrypting;
}
- @NonNull
- protected final KeyStore getKeyStore() {
- return mKeyStore;
- }
-
protected final long getConsumedInputSizeBytes() {
if (mMainDataStreamer == null) {
throw new IllegalStateException("Not initialized");
@@ -901,11 +884,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
/**
* Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
*
- * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
+ * @param parameters keystore/keymaster arguments to be populated with algorithm-specific
* parameters.
*/
protected abstract void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs);
+ @NonNull List<KeyParameter> parameters);
/**
* Invoked to obtain algorithm-specific parameters from the result of the KeyStore's
@@ -915,9 +898,8 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
* parameters, if not provided, must be generated by KeyStore and returned to the user of
* {@code Cipher} and potentially reused after {@code doFinal}.
*
- * @param keymasterArgs keystore/keymaster arguments returned by KeyStore {@code begin}
- * operation.
+ * @param parameters keystore/keymaster arguments returned by KeyStore {@code createOperation}.
*/
protected abstract void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs);
+ KeyParameter[] parameters);
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index 95e4c52591e0..9f7f2383a416 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -17,19 +17,19 @@
package android.security.keystore2;
import android.annotation.NonNull;
-import android.os.IBinder;
-import android.security.KeyStore;
import android.security.KeyStoreException;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
+import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyParameter;
import libcore.util.EmptyArray;
import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.SignatureSpi;
+import java.util.List;
/**
* Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
@@ -44,10 +44,10 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
}
@Override
- protected KeyStoreCryptoOperationStreamer createMainDataStreamer(KeyStore keyStore,
- IBinder operationToken) {
+ protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+ KeyStoreOperation operation) {
return new TruncateToFieldSizeMessageStreamer(
- super.createMainDataStreamer(keyStore, operationToken),
+ super.createMainDataStreamer(operation),
getGroupSizeBits());
}
@@ -81,8 +81,8 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
}
@Override
- public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature,
- byte[] additionalEntropy) throws KeyStoreException {
+ public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature)
+ throws KeyStoreException {
if (inputLength > 0) {
mConsumedInputSizeBytes += inputLength;
mInputBuffer.write(input, inputOffset, inputLength);
@@ -94,7 +94,7 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
return mDelegate.doFinal(bufferedInput,
0,
Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)),
- signature, additionalEntropy);
+ signature);
}
@Override
@@ -154,13 +154,13 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
+ ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
}
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = getKeyStore().getKeyCharacteristics(
- key.getAlias(), null, null, key.getUid(), keyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw getKeyStore().getInvalidKeyException(key.getAlias(), key.getUid(), errorCode);
+ long keySizeBits = -1;
+ for (Authorization a : key.getAuthorizations()) {
+ if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) {
+ keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a);
+ }
}
- long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+
if (keySizeBits == -1) {
throw new InvalidKeyException("Size of key not known");
} else if (keySizeBits > Integer.MAX_VALUE) {
@@ -184,9 +184,13 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
@Override
protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ @NonNull List<KeyParameter> parameters) {
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
+ ));
}
@Override
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index f7155b09750c..3dde2e592259 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -16,21 +16,20 @@
package android.security.keystore2;
-import android.os.IBinder;
-import android.security.KeyStore;
import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterArguments;
+import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
-import android.security.keystore.KeyStoreConnectException;
import android.security.keystore.KeyStoreCryptoOperation;
import android.security.keystore.KeymasterUtils;
+import android.system.keystore2.KeyParameter;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
import javax.crypto.MacSpi;
@@ -41,6 +40,8 @@ import javax.crypto.MacSpi;
*/
public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
+ private static final String TAG = "AndroidKeyStoreHmacSpi";
+
public static class HmacSHA1 extends AndroidKeyStoreHmacSpi {
public HmacSHA1() {
super(KeymasterDefs.KM_DIGEST_SHA1);
@@ -71,7 +72,6 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
}
}
- private final KeyStore mKeyStore = KeyStore.getInstance();
private final int mKeymasterDigest;
private final int mMacSizeBits;
@@ -80,12 +80,16 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
// Fields below are reset when engineDoFinal succeeds.
private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
- private IBinder mOperationToken;
- private long mOperationHandle;
+ private KeyStoreOperation mOperation;
+ private long mOperationChallenge;
protected AndroidKeyStoreHmacSpi(int keymasterDigest) {
mKeymasterDigest = keymasterDigest;
mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+ mOperation = null;
+ mOperationChallenge = 0;
+ mKey = null;
+ mChunkedStreamer = null;
}
@Override
@@ -127,24 +131,21 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
}
+ private void abortOperation() {
+ KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+ mOperation = null;
+ }
+
private void resetAll() {
+ abortOperation();
+ mOperationChallenge = 0;
mKey = null;
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- mOperationToken = null;
- mOperationHandle = 0;
mChunkedStreamer = null;
}
private void resetWhilePreservingInitState() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- mOperationToken = null;
- mOperationHandle = 0;
+ abortOperation();
+ mOperationChallenge = 0;
mChunkedStreamer = null;
}
@@ -161,45 +162,40 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
throw new IllegalStateException("Not initialized");
}
- KeymasterArguments keymasterArgs = new KeymasterArguments();
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits);
-
- OperationResult opResult = mKeyStore.begin(
- mKey.getAlias(),
- KeymasterDefs.KM_PURPOSE_SIGN,
- true,
- keymasterArgs,
- null, // no additional entropy needed for HMAC because it's deterministic
- mKey.getUid());
-
- if (opResult == null) {
- throw new KeyStoreConnectException();
- }
-
- // Store operation token and handle regardless of the error code returned by KeyStore to
- // ensure that the operation gets aborted immediately if the code below throws an exception.
- mOperationToken = opResult.token;
- mOperationHandle = opResult.operationHandle;
+ List<KeyParameter> parameters = new ArrayList<>();
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits
+ ));
- // If necessary, throw an exception due to KeyStore operation having failed.
- InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
- mKeyStore, mKey, opResult.resultCode);
- if (e != null) {
- throw e;
+ try {
+ mOperation = mKey.getSecurityLevel().createOperation(
+ mKey.getKeyIdDescriptor(),
+ parameters
+ );
+ } catch (KeyStoreException keyStoreException) {
+ // If necessary, throw an exception due to KeyStore operation having failed.
+ InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyException(
+ mKey, keyStoreException);
+ if (e != null) {
+ throw e;
+ }
}
- if (mOperationToken == null) {
- throw new ProviderException("Keystore returned null operation token");
- }
- if (mOperationHandle == 0) {
- throw new ProviderException("Keystore returned invalid operation handle");
- }
+ // Now we check if we got an operation challenge. This indicates that user authorization
+ // is required. And if we got a challenge we check if the authorization can possibly
+ // succeed.
+ mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(
+ mOperation, mKey);
mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- mKeyStore, mOperationToken));
+ mOperation));
}
@Override
@@ -238,9 +234,7 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
try {
result = mChunkedStreamer.doFinal(
null, 0, 0,
- null, // no signature provided -- this invocation will generate one
- null // no additional entropy needed -- HMAC is deterministic
- );
+ null); // no signature provided -- this invocation will generate one
} catch (KeyStoreException e) {
throw new ProviderException("Keystore operation failed", e);
}
@@ -252,10 +246,7 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
@Override
public void finalize() throws Throwable {
try {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
+ abortOperation();
} finally {
super.finalize();
}
@@ -263,6 +254,6 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
@Override
public long getOperationHandle() {
- return mOperationHandle;
+ return mOperationChallenge;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
index 38db36020fb7..afb10547411b 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
@@ -19,12 +19,15 @@ package android.security.keystore2;
import java.security.KeyStore;
import java.security.KeyStore.ProtectionParameter;
+/**
+ * @hide
+ */
class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
- private final int mUid;
+ private final int mNamespace;
- AndroidKeyStoreLoadStoreParameter(int uid) {
- mUid = uid;
+ AndroidKeyStoreLoadStoreParameter(int namespace) {
+ mNamespace = namespace;
}
@Override
@@ -32,7 +35,7 @@ class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
return null;
}
- int getUid() {
- return mUid;
+ int getNamespace() {
+ return mNamespace;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index c9c0b0de3463..a6ea9723db24 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -18,12 +18,11 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.security.KeyStore;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeymasterUtils;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyParameter;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
@@ -35,6 +34,7 @@ import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.MGF1ParameterSpec;
+import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
@@ -294,15 +294,17 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
@Override
protected final void addAlgorithmSpecificParametersToBegin(
- KeymasterArguments keymasterArgs) {
- super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ @NonNull List<KeyParameter> parameters) {
+ super.addAlgorithmSpecificParametersToBegin(parameters);
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
+ ));
}
@Override
protected final void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs) {
- super.loadAlgorithmSpecificParametersFromBeginResult(keymasterArgs);
+ KeyParameter[] parameters) {
+ super.loadAlgorithmSpecificParametersFromBeginResult(parameters);
}
@Override
@@ -415,14 +417,13 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
}
}
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = getKeyStore().getKeyCharacteristics(
- keystoreKey.getAlias(), null, null, keystoreKey.getUid(), keyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw getKeyStore().getInvalidKeyException(
- keystoreKey.getAlias(), keystoreKey.getUid(), errorCode);
+ long keySizeBits = -1;
+ for (Authorization a : keystoreKey.getAuthorizations()) {
+ if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) {
+ keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a);
+ }
}
- long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+
if (keySizeBits == -1) {
throw new InvalidKeyException("Size of key not known");
} else if (keySizeBits > Integer.MAX_VALUE) {
@@ -459,25 +460,32 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
@Override
protected void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ @NonNull List<KeyParameter> parameters) {
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA
+ ));
int keymasterPadding = getKeymasterPaddingOverride();
if (keymasterPadding == -1) {
keymasterPadding = mKeymasterPadding;
}
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING, keymasterPadding
+ ));
int purposeOverride = getKeymasterPurposeOverride();
if ((purposeOverride != -1)
&& ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN)
|| (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) {
- // Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE.
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
+ // Keymaster sign/verify requires digest to be specified.
+ // For raw sign/verify it's NONE.
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE
+ ));
}
}
@Override
protected void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs) {
+ KeyParameter[] parameters) {
}
@Override
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
index 6b2c098810e4..5f1b9c0586a1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -17,20 +17,20 @@
package android.security.keystore2;
import android.annotation.NonNull;
-import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
+import android.system.keystore2.KeyParameter;
import java.security.InvalidKeyException;
import java.security.SignatureSpi;
+import java.util.List;
/**
* Base class for {@link SignatureSpi} providing Android KeyStore backed RSA signatures.
*
* @hide
*/
-abstract class AndroidKeyStoreRSASignatureSpi extends
- AndroidKeyStoreSignatureSpiBase {
+abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
abstract static class PKCS1Padding extends AndroidKeyStoreRSASignatureSpi {
PKCS1Padding(int keymasterDigest) {
@@ -158,9 +158,15 @@ abstract class AndroidKeyStoreRSASignatureSpi extends
@Override
protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ @NonNull List<KeyParameter> parameters) {
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding
+ ));
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
index 23818a784f89..55414b70d403 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
@@ -18,15 +18,12 @@ package android.security.keystore2;
import android.annotation.CallSuper;
import android.annotation.NonNull;
-import android.os.IBinder;
-import android.security.KeyStore;
import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterArguments;
+import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
import android.security.keystore.ArrayUtils;
-import android.security.keystore.KeyStoreConnectException;
import android.security.keystore.KeyStoreCryptoOperation;
+import android.system.keystore2.KeyParameter;
import libcore.util.EmptyArray;
@@ -39,6 +36,8 @@ import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.security.SignatureSpi;
+import java.util.ArrayList;
+import java.util.List;
/**
* Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers.
@@ -47,7 +46,7 @@ import java.security.SignatureSpi;
*/
abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
implements KeyStoreCryptoOperation {
- private final KeyStore mKeyStore;
+ private static final String TAG = "AndroidKeyStoreSignatureSpiBase";
// Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin
// and should be preserved after SignatureSpi.engineSign/engineVerify finishes.
@@ -55,12 +54,18 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
private AndroidKeyStoreKey mKey;
/**
- * Token referencing this operation inside keystore service. It is initialized by
- * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when
- * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between.
+ * Object representing this operation inside keystore service. It is initialized
+ * by {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some
+ * error conditions in between.
*/
- private IBinder mOperationToken;
- private long mOperationHandle;
+ private KeyStoreOperation mOperation;
+ /**
+ * The operation challenge is required when an operation needs user authorization.
+ * The challenge is subjected to an authenticator, e.g., Gatekeeper or a biometric
+ * authenticator, and included in the authentication token minted by this authenticator.
+ * It may be null, if the operation does not require authorization.
+ */
+ private long mOperationChallenge;
private KeyStoreCryptoOperationStreamer mMessageStreamer;
/**
@@ -72,7 +77,13 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
private Exception mCachedException;
AndroidKeyStoreSignatureSpiBase() {
- mKeyStore = KeyStore.getInstance();
+ mOperation = null;
+ mOperationChallenge = 0;
+ mSigning = false;
+ mKey = null;
+ appRandom = null;
+ mMessageStreamer = null;
+ mCachedException = null;
}
@Override
@@ -145,6 +156,11 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
mKey = key;
}
+ private void abortOperation() {
+ KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+ mOperation = null;
+ }
+
/**
* Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
* cipher instance.
@@ -154,16 +170,11 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
*/
@CallSuper
protected void resetAll() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mOperationToken = null;
- mKeyStore.abort(operationToken);
- }
+ abortOperation();
+ mOperationChallenge = 0;
mSigning = false;
mKey = null;
appRandom = null;
- mOperationToken = null;
- mOperationHandle = 0;
mMessageStreamer = null;
mCachedException = null;
}
@@ -178,12 +189,8 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
*/
@CallSuper
protected void resetWhilePreservingInitState() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mOperationToken = null;
- mKeyStore.abort(operationToken);
- }
- mOperationHandle = 0;
+ abortOperation();
+ mOperationChallenge = 0;
mMessageStreamer = null;
mCachedException = null;
}
@@ -199,40 +206,29 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
throw new IllegalStateException("Not initialized");
}
- KeymasterArguments keymasterInputArgs = new KeymasterArguments();
- addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
-
- OperationResult opResult = mKeyStore.begin(
- mKey.getAlias(),
- mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
- true, // permit aborting this operation if keystore runs out of resources
- keymasterInputArgs,
- null, // no additional entropy for begin -- only finish might need some
- mKey.getUid());
- if (opResult == null) {
- throw new KeyStoreConnectException();
- }
+ List<KeyParameter> parameters = new ArrayList<>();
+ addAlgorithmSpecificParametersToBegin(parameters);
- // Store operation token and handle regardless of the error code returned by KeyStore to
- // ensure that the operation gets aborted immediately if the code below throws an exception.
- mOperationToken = opResult.token;
- mOperationHandle = opResult.operationHandle;
+ int purpose = mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY;
- // If necessary, throw an exception due to KeyStore operation having failed.
- InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
- mKeyStore, mKey, opResult.resultCode);
- if (e != null) {
- throw e;
- }
+ parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
- if (mOperationToken == null) {
- throw new ProviderException("Keystore returned null operation token");
- }
- if (mOperationHandle == 0) {
- throw new ProviderException("Keystore returned invalid operation handle");
+ try {
+ mOperation = mKey.getSecurityLevel().createOperation(
+ mKey.getKeyIdDescriptor(),
+ parameters);
+ } catch (KeyStoreException keyStoreException) {
+ throw KeyStoreCryptoOperationUtils.getInvalidKeyException(
+ mKey, keyStoreException);
}
- mMessageStreamer = createMainDataStreamer(mKeyStore, opResult.token);
+ // Now we check if we got an operation challenge. This indicates that user authorization
+ // is required. And if we got a challenge we check if the authorization can possibly
+ // succeed.
+ mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(
+ mOperation, mKey);
+
+ mMessageStreamer = createMainDataStreamer(mOperation);
}
/**
@@ -242,15 +238,15 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
*/
@NonNull
protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
- KeyStore keyStore, IBinder operationToken) {
+ @NonNull KeyStoreOperation operation) {
return new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- keyStore, operationToken));
+ operation));
}
@Override
public final long getOperationHandle() {
- return mOperationHandle;
+ return mOperationChallenge;
}
@Override
@@ -330,8 +326,7 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
appRandom, getAdditionalEntropyAmountForSign());
signature = mMessageStreamer.doFinal(
EmptyArray.BYTE, 0, 0,
- null, // no signature provided -- it'll be generated by this invocation
- additionalEntropy);
+ null); // no signature provided -- it'll be generated by this invocation
} catch (InvalidKeyException | KeyStoreException e) {
throw new SignatureException(e);
}
@@ -356,9 +351,7 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
try {
byte[] output = mMessageStreamer.doFinal(
EmptyArray.BYTE, 0, 0,
- signature,
- null // no additional entropy needed -- verification is deterministic
- );
+ signature);
if (output.length != 0) {
throw new ProviderException(
"Signature verification unexpected produced output: " + output.length
@@ -398,10 +391,6 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
throw new InvalidParameterException();
}
- protected final KeyStore getKeyStore() {
- return mKeyStore;
- }
-
/**
* Returns {@code true} if this signature is initialized for signing, {@code false} if this
* signature is initialized for verification.
@@ -426,9 +415,9 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
/**
* Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
*
- * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
+ * @param parameters keystore/keymaster arguments to be populated with algorithm-specific
* parameters.
*/
protected abstract void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs);
+ @NonNull List<KeyParameter> parameters);
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 96cfe411e73a..4c26864cb02b 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -16,23 +16,31 @@
package android.security.keystore2;
-import android.security.Credentials;
+import android.annotation.NonNull;
+import android.hardware.biometrics.BiometricManager;
import android.security.GateKeeper;
-import android.security.KeyStore;
+import android.security.KeyStore2;
import android.security.KeyStoreParameter;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
+import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.KeymasterUtils;
import android.security.keystore.SecureKeyImportUnavailableException;
import android.security.keystore.WrappedKeyEntry;
+import android.system.keystore2.AuthenticatorSpec;
+import android.system.keystore2.Domain;
+import android.system.keystore2.IKeystoreSecurityLevel;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyEntryResponse;
+import android.system.keystore2.KeyMetadata;
+import android.system.keystore2.KeyParameter;
+import android.system.keystore2.ResponseCode;
+import android.system.keystore2.SecurityLevel;
import android.util.Log;
-import libcore.util.EmptyArray;
-
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -48,7 +56,6 @@ import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.ProviderException;
-import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@@ -63,6 +70,7 @@ 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;
@@ -87,52 +95,88 @@ import javax.crypto.SecretKey;
* @hide
*/
public class AndroidKeyStoreSpi extends KeyStoreSpi {
+ public static final String TAG = "AndroidKeyStoreSpi";
public static final String NAME = "AndroidKeyStore";
- private KeyStore mKeyStore;
- private int mUid = KeyStore.UID_SELF;
+ private KeyStore2 mKeyStore;
+ private int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
UnrecoverableKeyException {
- String userKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- AndroidKeyStoreKey key;
- if (!mKeyStore.contains(userKeyAlias, mUid)) {
- // try legacy prefix for backward compatibility
- userKeyAlias = Credentials.USER_SECRET_KEY + alias;
- if (!mKeyStore.contains(userKeyAlias, mUid)) return null;
- }
try {
- key = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore,
- userKeyAlias,
- mUid);
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore,
+ alias,
+ mNamespace);
} catch (KeyPermanentlyInvalidatedException e) {
throw new UnrecoverableKeyException(e.getMessage());
+ } catch (UnrecoverableKeyException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof android.security.KeyStoreException) {
+ if (((android.security.KeyStoreException) cause).getErrorCode()
+ == ResponseCode.KEY_NOT_FOUND) {
+ return null;
+ }
+ }
+ throw e;
}
- return key;
}
- @Override
- public Certificate[] engineGetCertificateChain(String alias) {
+ /**
+ * Make a key descriptor from the given alias and the mNamespace member.
+ * If mNamespace is -1 it sets the domain field to {@link Domain#APP} and {@link Domain#SELINUX}
+ * otherwise. The blob field is always set to null and the alias field to {@code alias}
+ * @param alias The alias of the new key descriptor.
+ * @return A new key descriptor.
+ */
+ private KeyDescriptor makeKeyDescriptor(@NonNull String alias) {
+ KeyDescriptor descriptor = new KeyDescriptor();
+ descriptor.domain = getTargetDomain();
+ descriptor.nspace = mNamespace; // ignored if Domain.App;
+ descriptor.alias = alias;
+ descriptor.blob = null;
+ return descriptor;
+ }
+
+ private @Domain int getTargetDomain() {
+ return mNamespace == KeyProperties.NAMESPACE_APPLICATION
+ ? Domain.APP
+ : Domain.SELINUX;
+ }
+ private KeyEntryResponse getKeyMetadata(String alias) {
if (alias == null) {
throw new NullPointerException("alias == null");
}
- final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
+ KeyDescriptor descriptor = makeKeyDescriptor(alias);
+
+ try {
+ return mKeyStore.getKeyEntry(descriptor);
+ } catch (android.security.KeyStoreException e) {
+ if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
+ Log.w(TAG, "Could not get key metadata from Keystore.", e);
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public Certificate[] engineGetCertificateChain(String alias) {
+ KeyEntryResponse response = getKeyMetadata(alias);
+
+ if (response == null || response.metadata.certificate == null) {
+ return null;
+ }
+
+ final X509Certificate leaf = (X509Certificate) toCertificate(response.metadata.certificate);
if (leaf == null) {
return null;
}
final Certificate[] caList;
- // Suppress the key not found warning for this call. It seems that this error is exclusively
- // being thrown when there is a self signed certificate chain, so when the keystore service
- // attempts to query for the CA details, it obviously fails to find them and returns a
- // key not found exception. This is WAI, and throwing a stack trace here can be very
- // misleading since the trace is not clear.
- final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias,
- mUid,
- true /* suppressKeyNotFoundWarning */);
+ final byte[] caBytes = response.metadata.certificateChain;
+
if (caBytes != null) {
final Collection<X509Certificate> caChain = toCertificates(caBytes);
@@ -154,80 +198,26 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
@Override
public Certificate engineGetCertificate(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
+ KeyEntryResponse response = getKeyMetadata(alias);
+
+ if (response == null) {
+ return null;
}
- byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
+ byte[] encodedCert = response.metadata.certificate;
if (encodedCert != null) {
- return getCertificateForPrivateKeyEntry(alias, encodedCert);
+ return toCertificate(encodedCert);
}
- encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
+ encodedCert = response.metadata.certificateChain;
if (encodedCert != null) {
- return getCertificateForTrustedCertificateEntry(encodedCert);
+ return toCertificate(encodedCert);
}
// This entry/alias does not contain a certificate.
return null;
}
- private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) {
- // For this certificate there shouldn't be a private key in this KeyStore entry. Thus,
- // there's no need to wrap this certificate as opposed to the certificate associated with
- // a private key entry.
- return toCertificate(encodedCert);
- }
-
- private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) {
- // All crypto algorithms offered by Android Keystore for its private keys must also
- // be offered for the corresponding public keys stored in the Android Keystore. The
- // complication is that the underlying keystore service operates only on full key pairs,
- // rather than just public keys or private keys. As a result, Android Keystore-backed
- // crypto can only be offered for public keys for which keystore contains the
- // corresponding private key. This is not the case for certificate-only entries (e.g.,
- // trusted certificates).
- //
- // getCertificate().getPublicKey() is the only way to obtain the public key
- // corresponding to the private key stored in the KeyStore. Thus, we need to make sure
- // that the returned public key points to the underlying key pair / private key
- // when available.
-
- X509Certificate cert = toCertificate(encodedCert);
- if (cert == null) {
- // Failed to parse the certificate.
- return null;
- }
-
- String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- if (mKeyStore.contains(privateKeyAlias, mUid)) {
- // As expected, keystore contains the private key corresponding to this public key. Wrap
- // the certificate so that its getPublicKey method returns an Android Keystore
- // PublicKey. This key will delegate crypto operations involving this public key to
- // Android Keystore when higher-priority providers do not offer these crypto
- // operations for this key.
- return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert);
- } else {
- // This KeyStore entry/alias is supposed to contain the private key corresponding to
- // the public key in this certificate, but it does not for some reason. It's probably a
- // bug. Let other providers handle crypto operations involving the public key returned
- // by this certificate's getPublicKey.
- return cert;
- }
- }
-
- /**
- * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
- * returned by the certificate contains information about the alias of the private key in
- * keystore. This is needed so that Android Keystore crypto operations using public keys can
- * find out which key alias to use. These operations cannot work without an alias.
- */
- private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
- String privateKeyAlias, int uid, X509Certificate certificate) {
- return (certificate != null)
- ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null;
- }
-
private static X509Certificate toCertificate(byte[] bytes) {
try {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
@@ -251,37 +241,21 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
}
- private Date getModificationDate(String alias) {
- final long epochMillis = mKeyStore.getmtime(alias, mUid);
- if (epochMillis == -1L) {
- return null;
- }
-
- return new Date(epochMillis);
- }
-
@Override
public Date engineGetCreationDate(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
+ KeyEntryResponse response = getKeyMetadata(alias);
- Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
- if (d != null) {
- return d;
+ if (response == null) {
+ return null;
}
- d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
- if (d != null) {
- return d;
- }
- d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
- if (d != null) {
- return d;
- }
-
- return getModificationDate(Credentials.CA_CERTIFICATE + alias);
+ // TODO add modification time to key metadata.
+ return null;
+ // if (response.metadata.modificationTime == -1) {
+ // return null;
+ // }
+ // return new Date(response.metadata.modificationTime);
}
@Override
@@ -354,7 +328,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
- ProtectionParameter param) throws KeyStoreException {
+ java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
+ @SecurityLevel int securitylevel = SecurityLevel.TRUSTED_ENVIRONMENT;
int flags = 0;
KeyProtection spec;
if (param == null) {
@@ -362,17 +337,20 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
} else if (param instanceof KeyStoreParameter) {
spec = getLegacyKeyProtectionParameter(key);
KeyStoreParameter legacySpec = (KeyStoreParameter) param;
- if (legacySpec.isEncryptionRequired()) {
- flags = KeyStore.FLAG_ENCRYPTED;
- }
} else if (param instanceof KeyProtection) {
spec = (KeyProtection) param;
if (spec.isCriticalToDeviceEncryption()) {
- flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
+ // This key is should not be bound to the LSKF even if it is auth bound.
+ // This indicates that this key is used in the derivation for of the
+ // master key, that is used for the LSKF binding of other auth bound
+ // keys. This breaks up a circular dependency while retaining logical
+ // authentication binding of the key.
+ flags |= IKeystoreSecurityLevel
+ .KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
}
if (spec.isStrongBoxBacked()) {
- flags |= KeyStore.FLAG_STRONGBOX;
+ securitylevel = SecurityLevel.STRONGBOX;
}
} else {
throw new KeyStoreException(
@@ -447,147 +425,169 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
chainBytes = null;
}
- final String pkeyAlias;
+ @Domain int targetDomain = getTargetDomain();
+
+ // If the given key is an AndroidKeyStorePrivateKey, we attempt to update
+ // its subcomponents with the given certificate and certificate chain.
if (key instanceof AndroidKeyStorePrivateKey) {
- pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
- } else {
- pkeyAlias = null;
- }
+ AndroidKeyStoreKey ksKey = (AndroidKeyStoreKey) key;
+ KeyDescriptor descriptor = ksKey.getUserKeyDescriptor();
- byte[] pkcs8EncodedPrivateKeyBytes;
- KeymasterArguments importArgs;
- final boolean shouldReplacePrivateKey;
- if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
- final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
- if (!alias.equals(keySubalias)) {
- throw new KeyStoreException("Can only replace keys with same alias: " + alias
- + " != " + keySubalias);
- }
- shouldReplacePrivateKey = false;
- importArgs = null;
- pkcs8EncodedPrivateKeyBytes = null;
- } else {
- shouldReplacePrivateKey = true;
- // Make sure the PrivateKey format is the one we support.
- final String keyFormat = key.getFormat();
- if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
- throw new KeyStoreException(
- "Unsupported private key export format: " + keyFormat
- + ". Only private keys which export their key material in PKCS#8 format are"
- + " supported.");
- }
+ // This throws if the request cannot replace the entry.
+ assertCanReplace(alias, targetDomain, mNamespace, descriptor);
- // Make sure we can actually encode the key.
- pkcs8EncodedPrivateKeyBytes = key.getEncoded();
- if (pkcs8EncodedPrivateKeyBytes == null) {
- throw new KeyStoreException("Private key did not export any key material");
+ try {
+ mKeyStore.updateSubcomponents(
+ ((AndroidKeyStorePrivateKey) key).getKeyIdDescriptor(),
+ userCertBytes, chainBytes);
+ } catch (android.security.KeyStoreException e) {
+ throw new KeyStoreException("Failed to store certificate and certificate chain", e);
}
+ return;
+ }
- importArgs = new KeymasterArguments();
- try {
- importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
- KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
- key.getAlgorithm()));
- @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
- importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
- KeyProperties.Purpose.allToKeymaster(purposes));
- if (spec.isDigestsSpecified()) {
- importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
- KeyProperties.Digest.allToKeymaster(spec.getDigests()));
- }
+ // Make sure the PrivateKey format is the one we support.
+ final String keyFormat = key.getFormat();
+ if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
+ throw new KeyStoreException(
+ "Unsupported private key export format: " + keyFormat
+ + ". Only private keys which export their key material in PKCS#8 format are"
+ + " supported.");
+ }
- importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
- KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
- int[] keymasterEncryptionPaddings =
- KeyProperties.EncryptionPadding.allToKeymaster(
- spec.getEncryptionPaddings());
- if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (spec.isRandomizedEncryptionRequired())) {
- for (int keymasterPadding : keymasterEncryptionPaddings) {
- if (!KeymasterUtils
- .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
- keymasterPadding)) {
- throw new KeyStoreException(
- "Randomized encryption (IND-CPA) required but is violated by"
- + " encryption padding mode: "
- + KeyProperties.EncryptionPadding.fromKeymaster(
- keymasterPadding)
- + ". See KeyProtection documentation.");
- }
- }
- }
- importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
- importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
- KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
- KeymasterUtils.addUserAuthArgs(importArgs, spec);
- importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
- spec.getKeyValidityStart());
- importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
- spec.getKeyValidityForOriginationEnd());
- importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
- spec.getKeyValidityForConsumptionEnd());
- } catch (IllegalArgumentException | IllegalStateException e) {
- throw new KeyStoreException(e);
- }
+ // Make sure we can actually encode the key.
+ byte[] pkcs8EncodedPrivateKeyBytes = key.getEncoded();
+ if (pkcs8EncodedPrivateKeyBytes == null) {
+ throw new KeyStoreException("Private key did not export any key material");
}
+ final List<KeyParameter> importArgs = new ArrayList<>();
- boolean success = false;
try {
- // Store the private key, if necessary
- if (shouldReplacePrivateKey) {
- // Delete the stored private key and any related entries before importing the
- // provided key
- Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
- KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
- int errorCode = mKeyStore.importKey(
- Credentials.USER_PRIVATE_KEY + alias,
- importArgs,
- KeymasterDefs.KM_KEY_FORMAT_PKCS8,
- pkcs8EncodedPrivateKeyBytes,
- mUid,
- flags,
- resultingKeyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to store private key",
- KeyStore.getKeyStoreException(errorCode));
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM,
+ KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+ key.getAlgorithm()))
+ );
+ KeyStore2ParameterUtils.forEachSetFlag(spec.getPurposes(), (purpose) -> {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PURPOSE,
+ KeyProperties.Purpose.toKeymaster(purpose)
+ ));
+ });
+ if (spec.isDigestsSpecified()) {
+ for (String digest : spec.getDigests()) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST,
+ KeyProperties.Digest.toKeymaster(digest)
+ ));
}
- } else {
- // Keep the stored private key around -- delete all other entry types
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
- Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
}
-
- // Store the leaf certificate
- int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
- mUid, flags);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to store certificate #0",
- KeyStore.getKeyStoreException(errorCode));
+ for (String blockMode : spec.getBlockModes()) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_BLOCK_MODE,
+ KeyProperties.BlockMode.toKeymaster(blockMode)
+ ));
}
-
- // Store the certificate chain
- errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
- mUid, flags);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to store certificate chain",
- KeyStore.getKeyStoreException(errorCode));
- }
- success = true;
- } finally {
- if (!success) {
- if (shouldReplacePrivateKey) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
- } else {
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
- Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
+ int[] keymasterEncryptionPaddings =
+ KeyProperties.EncryptionPadding.allToKeymaster(
+ spec.getEncryptionPaddings());
+ if (((spec.getPurposes() & KeyProperties.PURPOSE_DECRYPT) != 0)
+ && (spec.isRandomizedEncryptionRequired())) {
+ for (int keymasterPadding : keymasterEncryptionPaddings) {
+ if (!KeymasterUtils
+ .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+ keymasterPadding)) {
+ throw new KeyStoreException(
+ "Randomized encryption (IND-CPA) required but is violated by"
+ + " encryption padding mode: "
+ + KeyProperties.EncryptionPadding.fromKeymaster(
+ keymasterPadding)
+ + ". See KeyProtection documentation.");
+ }
}
}
+ for (int padding : keymasterEncryptionPaddings) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING,
+ padding
+ ));
+ }
+ for (String padding : spec.getSignaturePaddings()) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING,
+ KeyProperties.SignaturePadding.toKeymaster(padding)
+ ));
+ }
+ KeyStore2ParameterUtils.addUserAuthArgs(importArgs, spec);
+ if (spec.getKeyValidityStart() != null) {
+ importArgs.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()
+ ));
+ }
+ if (spec.getKeyValidityForOriginationEnd() != null) {
+ importArgs.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+ spec.getKeyValidityForOriginationEnd()
+ ));
+ }
+ if (spec.getKeyValidityForConsumptionEnd() != null) {
+ importArgs.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+ spec.getKeyValidityForConsumptionEnd()
+ ));
+ }
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ throw new KeyStoreException(e);
+ }
+
+ try {
+ KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
+ securitylevel);
+
+ KeyDescriptor descriptor = makeKeyDescriptor(alias);
+
+ KeyMetadata metadata = securityLevelInterface.importKey(descriptor, null,
+ importArgs, flags, pkcs8EncodedPrivateKeyBytes);
+
+ try {
+ mKeyStore.updateSubcomponents(metadata.key, userCertBytes, chainBytes);
+ } catch (android.security.KeyStoreException e) {
+ mKeyStore.deleteKey(metadata.key);
+ throw new KeyStoreException("Failed to store certificate and certificate chain", e);
+ }
+
+ } catch (android.security.KeyStoreException e) {
+ throw new KeyStoreException("Failed to store private key", e);
+ }
+ }
+
+ private static void assertCanReplace(String alias, @Domain int targetDomain,
+ int targetNamespace, KeyDescriptor descriptor)
+ throws KeyStoreException {
+ // If
+ // * the alias does not match, or
+ // * the domain does not match, or
+ // * the domain is Domain.SELINUX and the namespaces don not match,
+ // then the designated key location is not equivalent to the location of the
+ // given key parameter and cannot be updated.
+ //
+ // Note: mNamespace == KeyProperties.NAMESPACE_APPLICATION implies that the target domain
+ // is Domain.APP and Domain.SELINUX is the target domain otherwise.
+ if (alias != descriptor.alias
+ || descriptor.domain != targetDomain
+ || (descriptor.domain == Domain.SELINUX && descriptor.nspace != targetNamespace)) {
+ throw new KeyStoreException("Can only replace keys with same alias: " + alias
+ + " != " + descriptor.alias + " in the same target domain: " + targetDomain
+ + " != " + descriptor.domain
+ + (targetDomain == Domain.SELINUX ? " in the same target namespace: "
+ + targetNamespace + " != " + descriptor.nspace : "")
+ );
}
}
- private void setSecretKeyEntry(String entryAlias, SecretKey key,
- ProtectionParameter param)
+ private void setSecretKeyEntry(String alias, SecretKey key,
+ java.security.KeyStore.ProtectionParameter param)
throws KeyStoreException {
if ((param != null) && (!(param instanceof KeyProtection))) {
throw new KeyStoreException(
@@ -596,28 +596,19 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
KeyProtection params = (KeyProtection) param;
+ @SecurityLevel int securityLevel = params.isStrongBoxBacked() ? SecurityLevel.STRONGBOX :
+ SecurityLevel.TRUSTED_ENVIRONMENT;
+ @Domain int targetDomain = (getTargetDomain());
+
if (key instanceof AndroidKeyStoreSecretKey) {
- // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
- // overwrite its own entry.
- String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
- if (keyAliasInKeystore == null) {
- throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
- }
- String keyAliasPrefix = Credentials.USER_PRIVATE_KEY;
- if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
- // try legacy prefix
- keyAliasPrefix = Credentials.USER_SECRET_KEY;
- if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
- throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
- + keyAliasInKeystore);
- }
- }
- String keyEntryAlias =
- keyAliasInKeystore.substring(keyAliasPrefix.length());
- if (!entryAlias.equals(keyEntryAlias)) {
- throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
- + " alias: " + entryAlias + " != " + keyEntryAlias);
- }
+ String keyAliasInKeystore =
+ ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor().alias;
+
+ KeyDescriptor descriptor = ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor();
+
+ // This throws if the request cannot replace the existing key.
+ assertCanReplace(alias, targetDomain, mNamespace, descriptor);
+
// This is the entry where this key is already stored. No need to do anything.
if (params != null) {
throw new KeyStoreException("Modifying KeyStore-backed key using protection"
@@ -646,13 +637,18 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
+ " RAW format export");
}
- KeymasterArguments args = new KeymasterArguments();
+ final List<KeyParameter> importArgs = new ArrayList<>();
+
try {
int keymasterAlgorithm =
- KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm());
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
+ KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(
+ key.getAlgorithm());
+
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM,
+ keymasterAlgorithm
+ ));
- int[] keymasterDigests;
if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
// JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
// implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
@@ -666,7 +662,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
"HMAC key algorithm digest unknown for key algorithm "
+ key.getAlgorithm());
}
- keymasterDigests = new int[] {keymasterImpliedDigest};
+
if (params.isDigestsSpecified()) {
// Digest(s) explicitly specified in params -- check that the list consists of
// exactly one digest, the one implied by key algorithm.
@@ -676,172 +672,235 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|| (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
throw new KeyStoreException(
"Unsupported digests specification: "
- + Arrays.asList(params.getDigests()) + ". Only "
- + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
- + " supported for HMAC key algorithm " + key.getAlgorithm());
+ + Arrays.asList(params.getDigests()) + ". Only "
+ + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
+ + " supported for HMAC key algorithm "
+ + key.getAlgorithm());
}
}
+ int outputBits = KeymasterUtils.getDigestOutputSizeBits(keymasterImpliedDigest);
+ if (outputBits == -1) {
+ throw new ProviderException(
+ "HMAC key authorized for unsupported digest: "
+ + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest));
+ }
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST, keymasterImpliedDigest
+ ));
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, outputBits
+ ));
} else {
- // Key algorithm does not imply a digest.
if (params.isDigestsSpecified()) {
- keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
+ for (String digest : params.getDigests()) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST,
+ KeyProperties.Digest.toKeymaster(digest)
+ ));
+ }
+ }
+ }
+
+ KeyStore2ParameterUtils.forEachSetFlag(params.getPurposes(), (purpose) -> {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PURPOSE,
+ KeyProperties.Purpose.toKeymaster(purpose)
+ ));
+ });
+
+ boolean indCpa = false;
+ if ((params.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) {
+ if (((KeyProtection) param).isRandomizedEncryptionRequired()) {
+ indCpa = true;
} else {
- keymasterDigests = EmptyArray.INT;
+ importArgs.add(KeyStore2ParameterUtils.makeBool(
+ KeymasterDefs.KM_TAG_CALLER_NONCE
+ ));
}
}
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
-
- @KeyProperties.PurposeEnum int purposes = params.getPurposes();
- int[] keymasterBlockModes =
- KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
- if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (params.isRandomizedEncryptionRequired())) {
- for (int keymasterBlockMode : keymasterBlockModes) {
- if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
- keymasterBlockMode)) {
- throw new KeyStoreException(
- "Randomized encryption (IND-CPA) required but may be violated by"
- + " block mode: "
- + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
- + ". See KeyProtection documentation.");
- }
+
+ for (String blockMode : params.getBlockModes()) {
+ int keymasterBlockMode = KeyProperties.BlockMode.toKeymaster(blockMode);
+ if (indCpa
+ && !KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+ keymasterBlockMode)) {
+ throw new KeyStoreException(
+ "Randomized encryption (IND-CPA) required but may be violated by"
+ + " block mode: " + blockMode
+ + ". See KeyProtection documentation.");
+
}
+ if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES
+ && keymasterBlockMode == KeymasterDefs.KM_MODE_GCM) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
+ AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
+ .MIN_SUPPORTED_TAG_LENGTH_BITS
+ ));
+ }
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_BLOCK_MODE,
+ keymasterBlockMode
+ ));
}
- args.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
- KeyProperties.Purpose.allToKeymaster(purposes));
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
+
if (params.getSignaturePaddings().length > 0) {
throw new KeyStoreException("Signature paddings not supported for symmetric keys");
}
- int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
- params.getEncryptionPaddings());
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
- KeymasterUtils.addUserAuthArgs(args, params);
- KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
- args,
- keymasterAlgorithm,
- keymasterBlockModes,
- keymasterDigests);
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
- params.getKeyValidityStart());
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
- params.getKeyValidityForOriginationEnd());
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
- params.getKeyValidityForConsumptionEnd());
-
- if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (!params.isRandomizedEncryptionRequired())) {
- // Permit caller-provided IV when encrypting with this key
- args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
+
+ for (String padding : params.getEncryptionPaddings()) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING,
+ KeyProperties.EncryptionPadding.toKeymaster(padding)
+ ));
+ }
+
+ KeyStore2ParameterUtils.addUserAuthArgs(importArgs, params);
+
+ if (params.getKeyValidityStart() != null) {
+ importArgs.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart()
+ ));
+ }
+ if (params.getKeyValidityForOriginationEnd() != null) {
+ importArgs.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+ params.getKeyValidityForOriginationEnd()
+ ));
+ }
+ if (params.getKeyValidityForConsumptionEnd() != null) {
+ importArgs.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+ params.getKeyValidityForConsumptionEnd()
+ ));
}
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
+
int flags = 0;
if (params.isCriticalToDeviceEncryption()) {
- flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
- }
- if (params.isStrongBoxBacked()) {
- flags |= KeyStore.FLAG_STRONGBOX;
+ flags |= IKeystoreSecurityLevel.KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
}
- Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
- String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + entryAlias;
- int errorCode = mKeyStore.importKey(
- keyAliasInKeystore,
- args,
- KeymasterDefs.KM_KEY_FORMAT_RAW,
- keyMaterial,
- mUid,
- flags,
- new KeyCharacteristics());
- if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to import secret key. Keystore error code: "
- + errorCode);
+ try {
+ KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
+ securityLevel);
+
+ KeyDescriptor descriptor = makeKeyDescriptor(alias);
+
+ securityLevelInterface.importKey(descriptor, null /* TODO attestationKey */,
+ importArgs, flags, keyMaterial);
+ } catch (android.security.KeyStoreException e) {
+ throw new KeyStoreException("Failed to import secret key.", e);
}
}
private void setWrappedKeyEntry(String alias, WrappedKeyEntry entry,
- ProtectionParameter param) throws KeyStoreException {
+ java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
if (param != null) {
throw new KeyStoreException("Protection parameters are specified inside wrapped keys");
}
byte[] maskingKey = new byte[32];
-
- KeymasterArguments args = new KeymasterArguments();
String[] parts = entry.getTransformation().split("/");
+ List<KeyParameter> args = new ArrayList<>();
+
String algorithm = parts[0];
if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
- } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ args.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM,
+ KeymasterDefs.KM_ALGORITHM_RSA
+ ));
+ } else {
+ throw new KeyStoreException("Algorithm \"" + algorithm + "\" not supported for "
+ + "wrapping. Only RSA wrapping keys are supported.");
}
if (parts.length > 1) {
String mode = parts[1];
- if (KeyProperties.BLOCK_MODE_ECB.equalsIgnoreCase(mode)) {
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
- } else if (KeyProperties.BLOCK_MODE_CBC.equalsIgnoreCase(mode)) {
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CBC);
- } else if (KeyProperties.BLOCK_MODE_CTR.equalsIgnoreCase(mode)) {
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
- } else if (KeyProperties.BLOCK_MODE_GCM.equalsIgnoreCase(mode)) {
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
- }
+ args.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_BLOCK_MODE,
+ KeyProperties.BlockMode.toKeymaster(mode)
+ ));
}
if (parts.length > 2) {
- String padding = parts[2];
- if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
- // Noop
- } else if (KeyProperties.ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7);
- } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
- args.addEnums(KeymasterDefs.KM_TAG_PADDING,
- KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
- } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_OAEP);
+ @KeyProperties.EncryptionPaddingEnum int padding =
+ KeyProperties.EncryptionPadding.toKeymaster(parts[2]);
+ if (padding != KeymasterDefs.KM_PAD_NONE) {
+ args.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING,
+ padding
+ ));
}
}
KeyGenParameterSpec spec = (KeyGenParameterSpec) entry.getAlgorithmParameterSpec();
if (spec.isDigestsSpecified()) {
- String digest = spec.getDigests()[0];
- if (KeyProperties.DIGEST_NONE.equalsIgnoreCase(digest)) {
- // Noop
- } else if (KeyProperties.DIGEST_MD5.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_MD5);
- } else if (KeyProperties.DIGEST_SHA1.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA1);
- } else if (KeyProperties.DIGEST_SHA224.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_224);
- } else if (KeyProperties.DIGEST_SHA256.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_256);
- } else if (KeyProperties.DIGEST_SHA384.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_384);
- } else if (KeyProperties.DIGEST_SHA512.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_512);
+ @KeyProperties.DigestEnum int digest =
+ KeyProperties.Digest.toKeymaster(spec.getDigests()[0]);
+ if (digest != KeymasterDefs.KM_DIGEST_NONE) {
+ args.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_DIGEST,
+ digest
+ ));
}
}
- int errorCode = mKeyStore.importWrappedKey(
- Credentials.USER_PRIVATE_KEY + alias,
- entry.getWrappedKeyBytes(),
- Credentials.USER_PRIVATE_KEY + entry.getWrappingKeyAlias(),
- maskingKey,
- args,
- GateKeeper.getSecureUserId(),
- 0, // FIXME fingerprint id?
- mUid,
- new KeyCharacteristics());
- if (errorCode == KeymasterDefs.KM_ERROR_UNIMPLEMENTED) {
- throw new SecureKeyImportUnavailableException("Could not import wrapped key");
- } else if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
- + errorCode);
+ KeyDescriptor wrappingkey = makeKeyDescriptor(entry.getWrappingKeyAlias());
+
+ KeyEntryResponse response = null;
+ try {
+ response = mKeyStore.getKeyEntry(wrappingkey);
+ } catch (android.security.KeyStoreException e) {
+ throw new KeyStoreException("Failed to load wrapping key.", e);
+ }
+
+ KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
+
+ KeyStoreSecurityLevel securityLevel = new KeyStoreSecurityLevel(response.iSecurityLevel);
+
+ final BiometricManager bm = android.app.AppGlobals.getInitialApplication()
+ .getSystemService(BiometricManager.class);
+
+ long[] biometricSids = bm.getAuthenticatorIds();
+
+ List<AuthenticatorSpec> authenticatorSpecs = new ArrayList<>();
+
+ AuthenticatorSpec authenticatorSpec = new AuthenticatorSpec();
+ // TODO Replace with HardwareAuthenticatorType.PASSWORD when KeyMint AIDL spec has landed.
+ authenticatorSpec.authenticatorType = 1; // HardwareAuthenticatorType.PASSWORD
+ authenticatorSpec.authenticatorId = GateKeeper.getSecureUserId();
+ authenticatorSpecs.add(authenticatorSpec);
+
+ for (long sid : biometricSids) {
+ AuthenticatorSpec authSpec = new AuthenticatorSpec();
+ // TODO Replace with HardwareAuthenticatorType.FINGERPRINT when KeyMint AIDL spec has
+ // landed.
+ authSpec.authenticatorType = 2; // HardwareAuthenticatorType.FINGERPRINT
+ authSpec.authenticatorId = sid;
+ authenticatorSpecs.add(authSpec);
+ }
+
+ try {
+ securityLevel.importWrappedKey(
+ wrappedKey, wrappingkey,
+ entry.getWrappedKeyBytes(),
+ null /* masking key is set to 32 bytes if null is given here */,
+ args,
+ authenticatorSpecs.toArray(new AuthenticatorSpec[0]));
+ } catch (android.security.KeyStoreException e) {
+ switch (e.getErrorCode()) {
+ case KeymasterDefs.KM_ERROR_UNIMPLEMENTED: {
+ throw new SecureKeyImportUnavailableException("Could not import wrapped key");
+ }
+ default:
+ throw new KeyStoreException("Failed to import wrapped key. Keystore error "
+ + "code: " + e.getErrorCode(), e);
+ }
}
}
@@ -851,6 +910,23 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
throw new KeyStoreException("Operation not supported because key encoding is unknown");
}
+ /**
+ * This function sets a trusted certificate entry. It fails if the given
+ * alias is already taken by an actual key entry. However, if the entry is a
+ * trusted certificate it will get silently replaced.
+ * @param alias the alias name
+ * @param cert the certificate
+ *
+ * @throws KeyStoreException if the alias is already taken by a secret or private
+ * key entry.
+ * @throws KeyStoreException with a nested {@link CertificateEncodingException}
+ * if the {@code cert.getEncoded()} throws.
+ * @throws KeyStoreException with a nested {@link android.security.KeyStoreException} if
+ * something went wrong while inserting the certificate into keystore.
+ * @throws NullPointerException if cert or alias is null.
+ *
+ * @hide
+ */
@Override
public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
if (isKeyEntry(alias)) {
@@ -869,36 +945,43 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
throw new KeyStoreException(e);
}
- if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) {
- throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
+ try {
+ mKeyStore.updateSubcomponents(makeKeyDescriptor(alias),
+ null /* publicCert - unused when used as pure certificate store. */,
+ encoded);
+ } catch (android.security.KeyStoreException e) {
+ throw new KeyStoreException("Couldn't insert certificate.", e);
}
}
@Override
public void engineDeleteEntry(String alias) throws KeyStoreException {
- if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) {
- throw new KeyStoreException("Failed to delete entry: " + alias);
+ KeyDescriptor descriptor = makeKeyDescriptor(alias);
+ try {
+ mKeyStore.deleteKey(descriptor);
+ } catch (android.security.KeyStoreException e) {
+ if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
+ throw new KeyStoreException("Failed to delete entry: " + alias, e);
+ }
}
}
private Set<String> getUniqueAliases() {
- final String[] rawAliases = mKeyStore.list("", mUid);
- if (rawAliases == null) {
- return new HashSet<String>();
- }
- final Set<String> aliases = new HashSet<String>(rawAliases.length);
- for (String alias : rawAliases) {
- final int idx = alias.indexOf('_');
- if ((idx == -1) || (alias.length() <= idx)) {
- Log.e(NAME, "invalid alias: " + alias);
- continue;
+ try {
+ final KeyDescriptor[] keys = mKeyStore.list(
+ getTargetDomain(),
+ mNamespace
+ );
+ final Set<String> aliases = new HashSet<>(keys.length);
+ for (KeyDescriptor d : keys) {
+ aliases.add(d.alias);
}
-
- aliases.add(new String(alias.substring(idx + 1)));
+ return aliases;
+ } catch (android.security.KeyStoreException e) {
+ Log.e(TAG, "Failed to list keystore entries.", e);
+ return null;
}
-
- return aliases;
}
@Override
@@ -912,10 +995,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid)
- || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid)
- || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid)
- || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
+ return getKeyMetadata(alias) != null;
}
@Override
@@ -929,22 +1009,30 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
private boolean isKeyEntry(String alias) {
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) ||
- mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
- }
-
-
- private boolean isCertificateEntry(String alias) {
if (alias == null) {
throw new NullPointerException("alias == null");
}
- return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
+ KeyEntryResponse response = getKeyMetadata(alias);
+ // If response is null, there is no such entry.
+ // If response.iSecurityLevel is null, there is no private or secret key material stored.
+ return response != null && response.iSecurityLevel != null;
}
+
@Override
public boolean engineIsCertificateEntry(String alias) {
- return !isKeyEntry(alias) && isCertificateEntry(alias);
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+ KeyEntryResponse response = getKeyMetadata(alias);
+ // If response == null there is no such entry.
+ // If there is no certificateChain, then this is not a certificate entry.
+ // If there is a private key entry, this is the certificate chain for that
+ // key entry and not a CA certificate entry.
+ return response != null
+ && response.metadata.certificateChain != null
+ && response.iSecurityLevel == null;
}
@Override
@@ -953,66 +1041,55 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
return null;
}
if (!"X.509".equalsIgnoreCase(cert.getType())) {
- // Only X.509 certificates supported
+ Log.e(TAG, "In engineGetCertificateAlias: only X.509 certificates are supported.");
return null;
}
byte[] targetCertBytes;
try {
targetCertBytes = cert.getEncoded();
} catch (CertificateEncodingException e) {
+ Log.e(TAG, "While trying to get the alias for a certificate.", e);
return null;
}
if (targetCertBytes == null) {
return null;
}
- final Set<String> nonCaEntries = new HashSet<String>();
-
- /*
- * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
- * says to only compare the first certificate in the chain which is
- * equivalent to the USER_CERTIFICATE prefix for the Android keystore
- * convention.
- */
- final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid);
- if (certAliases != null) {
- for (String alias : certAliases) {
- final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
- if (certBytes == null) {
- continue;
- }
-
- nonCaEntries.add(alias);
-
- if (Arrays.equals(certBytes, targetCertBytes)) {
- return alias;
- }
+ KeyDescriptor[] keyDescriptors = null;
+ try {
+ keyDescriptors = mKeyStore.list(
+ getTargetDomain(),
+ mNamespace
+ );
+ } catch (android.security.KeyStoreException e) {
+ Log.w(TAG, "Failed to get list of keystore entries.", e);
+ }
+
+ String caAlias = null;
+ for (KeyDescriptor d : keyDescriptors) {
+ KeyEntryResponse response = getKeyMetadata(d.alias);
+ if (response == null) {
+ continue;
}
- }
-
- /*
- * Look at all the TrustedCertificateEntry types. Skip all the
- * PrivateKeyEntry we looked at above.
- */
- final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid);
- if (certAliases != null) {
- for (String alias : caAliases) {
- if (nonCaEntries.contains(alias)) {
- continue;
- }
-
- final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
- if (certBytes == null) {
- continue;
+ /*
+ * The KeyStoreSpi documentation says to only compare the first certificate in the
+ * chain which is equivalent to the {@code response.metadata.certificate} field.
+ * So we look for a hit in this field first. For pure CA certificate entries,
+ * we check the {@code response.metadata.certificateChain} field. But we only
+ * return a CA alias if there was no hit in the certificate field of any other
+ * entry.
+ */
+ if (response.metadata.certificate != null) {
+ if (Arrays.equals(response.metadata.certificate, targetCertBytes)) {
+ return d.alias;
}
-
- if (Arrays.equals(certBytes, targetCertBytes)) {
- return alias;
+ } else if (response.metadata.certificateChain != null && caAlias == null) {
+ if (Arrays.equals(response.metadata.certificateChain, targetCertBytes)) {
+ caAlias = d.alias;
}
}
}
-
- return null;
+ return caAlias;
}
@Override
@@ -1033,24 +1110,24 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
}
// Unfortunate name collision.
- mKeyStore = KeyStore.getInstance();
- mUid = KeyStore.UID_SELF;
+ mKeyStore = KeyStore2.getInstance();
+ mNamespace = KeyProperties.NAMESPACE_APPLICATION;
}
@Override
public void engineLoad(LoadStoreParameter param) throws IOException,
NoSuchAlgorithmException, CertificateException {
- int uid = KeyStore.UID_SELF;
+ int namespace = KeyProperties.NAMESPACE_APPLICATION;
if (param != null) {
if (param instanceof AndroidKeyStoreLoadStoreParameter) {
- uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid();
+ namespace = ((AndroidKeyStoreLoadStoreParameter) param).getNamespace();
} else {
throw new IllegalArgumentException(
"Unsupported param type: " + param.getClass());
}
}
- mKeyStore = KeyStore.getInstance();
- mUid = uid;
+ mKeyStore = KeyStore2.getInstance();
+ mNamespace = namespace;
}
@Override
@@ -1060,11 +1137,14 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
throw new KeyStoreException("entry == null");
}
- Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
-
if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
java.security.KeyStore.TrustedCertificateEntry trE =
(java.security.KeyStore.TrustedCertificateEntry) entry;
+ // engineSetCertificateEntry does not overwrite if the existing entry
+ // is a key entry, but the semantic of engineSetEntry is such that it
+ // overwrites any existing entry. Thus we delete any possible existing
+ // entry by this alias.
+ engineDeleteEntry(alias);
engineSetCertificateEntry(alias, trE.getTrustedCertificate());
return;
}
@@ -1080,34 +1160,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
setWrappedKeyEntry(alias, wke, param);
} else {
throw new KeyStoreException(
- "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
- + "; was " + entry);
- }
- }
-
- /**
- * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
- * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
- * can find out which keystore private key entry to use. This is needed so that Android Keystore
- * crypto operations using public keys can find out which key alias to use. These operations
- * require an alias.
- */
- static class KeyStoreX509Certificate extends DelegatingX509Certificate {
- private final String mPrivateKeyAlias;
- private final int mPrivateKeyUid;
- KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid,
- X509Certificate delegate) {
- super(delegate);
- mPrivateKeyAlias = privateKeyAlias;
- mPrivateKeyUid = privateKeyUid;
- }
-
- @Override
- public PublicKey getPublicKey() {
- PublicKey original = super.getPublicKey();
- return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
- mPrivateKeyAlias, mPrivateKeyUid,
- original.getAlgorithm(), original.getEncoded());
+ "Entry must be a PrivateKeyEntry, SecretKeyEntry, WrappedKeyEntry "
+ + "or TrustedCertificateEntry; was " + entry);
}
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index 65678a37b938..3d5a8f63e7f9 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -18,10 +18,10 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
+import android.system.keystore2.KeyParameter;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
@@ -32,6 +32,7 @@ import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
+import java.util.List;
import javax.crypto.CipherSpi;
import javax.crypto.spec.IvParameterSpec;
@@ -48,15 +49,13 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
}
- public static class NoPadding extends
- AndroidKeyStoreUnauthenticatedAESCipherSpi.ECB {
+ public static class NoPadding extends ECB {
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
}
- public static class PKCS7Padding extends
- AndroidKeyStoreUnauthenticatedAESCipherSpi.ECB {
+ public static class PKCS7Padding extends ECB {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
@@ -68,15 +67,13 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
}
- public static class NoPadding extends
- AndroidKeyStoreUnauthenticatedAESCipherSpi.CBC {
+ public static class NoPadding extends CBC {
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
}
- public static class PKCS7Padding extends
- AndroidKeyStoreUnauthenticatedAESCipherSpi.CBC {
+ public static class PKCS7Padding extends CBC {
public PKCS7Padding() {
super(KeymasterDefs.KM_PAD_PKCS7);
}
@@ -88,8 +85,7 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
super(KeymasterDefs.KM_MODE_CTR, keymasterPadding, true);
}
- public static class NoPadding extends
- AndroidKeyStoreUnauthenticatedAESCipherSpi.CTR {
+ public static class NoPadding extends CTR {
public NoPadding() {
super(KeymasterDefs.KM_PAD_NONE);
}
@@ -245,7 +241,7 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
@Override
protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
+ @NonNull List<KeyParameter> parameters) {
if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
// IV is being reused for encryption: this violates security best practices.
throw new IllegalStateException(
@@ -253,23 +249,36 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
+ " practices.");
}
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding
+ ));
if ((mIvRequired) && (mIv != null)) {
- keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
+ parameters.add(KeyStore2ParameterUtils.makeBytes(
+ KeymasterDefs.KM_TAG_NONCE, mIv
+ ));
}
}
@Override
protected final void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs) {
+ KeyParameter[] parameters) {
mIvHasBeenUsed = true;
// NOTE: Keymaster doesn't always return an IV, even if it's used.
- byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
- if ((returnedIv != null) && (returnedIv.length == 0)) {
- returnedIv = null;
+ byte[] returnedIv = null;
+ if (parameters != null) {
+ for (KeyParameter p : parameters) {
+ if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
+ returnedIv = p.blob;
+ break;
+ }
+ }
}
if (mIvRequired) {
diff --git a/keystore/java/android/security/keystore2/DelegatingX509Certificate.java b/keystore/java/android/security/keystore2/DelegatingX509Certificate.java
deleted file mode 100644
index 9dfee8ce098c..000000000000
--- a/keystore/java/android/security/keystore2/DelegatingX509Certificate.java
+++ /dev/null
@@ -1,212 +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.keystore2;
-
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Principal;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Set;
-
-import javax.security.auth.x500.X500Principal;
-
-class DelegatingX509Certificate extends X509Certificate {
- private final X509Certificate mDelegate;
-
- DelegatingX509Certificate(X509Certificate delegate) {
- mDelegate = delegate;
- }
-
- @Override
- public Set<String> getCriticalExtensionOIDs() {
- return mDelegate.getCriticalExtensionOIDs();
- }
-
- @Override
- public byte[] getExtensionValue(String oid) {
- return mDelegate.getExtensionValue(oid);
- }
-
- @Override
- public Set<String> getNonCriticalExtensionOIDs() {
- return mDelegate.getNonCriticalExtensionOIDs();
- }
-
- @Override
- public boolean hasUnsupportedCriticalExtension() {
- return mDelegate.hasUnsupportedCriticalExtension();
- }
-
- @Override
- public void checkValidity() throws CertificateExpiredException,
- CertificateNotYetValidException {
- mDelegate.checkValidity();
- }
-
- @Override
- public void checkValidity(Date date) throws CertificateExpiredException,
- CertificateNotYetValidException {
- mDelegate.checkValidity(date);
- }
-
- @Override
- public int getBasicConstraints() {
- return mDelegate.getBasicConstraints();
- }
-
- @Override
- public Principal getIssuerDN() {
- return mDelegate.getIssuerDN();
- }
-
- @Override
- public boolean[] getIssuerUniqueID() {
- return mDelegate.getIssuerUniqueID();
- }
-
- @Override
- public boolean[] getKeyUsage() {
- return mDelegate.getKeyUsage();
- }
-
- @Override
- public Date getNotAfter() {
- return mDelegate.getNotAfter();
- }
-
- @Override
- public Date getNotBefore() {
- return mDelegate.getNotBefore();
- }
-
- @Override
- public BigInteger getSerialNumber() {
- return mDelegate.getSerialNumber();
- }
-
- @Override
- public String getSigAlgName() {
- return mDelegate.getSigAlgName();
- }
-
- @Override
- public String getSigAlgOID() {
- return mDelegate.getSigAlgOID();
- }
-
- @Override
- public byte[] getSigAlgParams() {
- return mDelegate.getSigAlgParams();
- }
-
- @Override
- public byte[] getSignature() {
- return mDelegate.getSignature();
- }
-
- @Override
- public Principal getSubjectDN() {
- return mDelegate.getSubjectDN();
- }
-
- @Override
- public boolean[] getSubjectUniqueID() {
- return mDelegate.getSubjectUniqueID();
- }
-
- @Override
- public byte[] getTBSCertificate() throws CertificateEncodingException {
- return mDelegate.getTBSCertificate();
- }
-
- @Override
- public int getVersion() {
- return mDelegate.getVersion();
- }
-
- @Override
- public byte[] getEncoded() throws CertificateEncodingException {
- return mDelegate.getEncoded();
- }
-
- @Override
- public PublicKey getPublicKey() {
- return mDelegate.getPublicKey();
- }
-
- @Override
- public String toString() {
- return mDelegate.toString();
- }
-
- @Override
- public void verify(PublicKey key)
- throws CertificateException,
- NoSuchAlgorithmException,
- InvalidKeyException,
- NoSuchProviderException,
- SignatureException {
- mDelegate.verify(key);
- }
-
- @Override
- public void verify(PublicKey key, String sigProvider)
- throws CertificateException,
- NoSuchAlgorithmException,
- InvalidKeyException,
- NoSuchProviderException,
- SignatureException {
- mDelegate.verify(key, sigProvider);
- }
-
- @Override
- public List<String> getExtendedKeyUsage() throws CertificateParsingException {
- return mDelegate.getExtendedKeyUsage();
- }
-
- @Override
- public Collection<List<?>> getIssuerAlternativeNames() throws CertificateParsingException {
- return mDelegate.getIssuerAlternativeNames();
- }
-
- @Override
- public X500Principal getIssuerX500Principal() {
- return mDelegate.getIssuerX500Principal();
- }
-
- @Override
- public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
- return mDelegate.getSubjectAlternativeNames();
- }
-
- @Override
- public X500Principal getSubjectX500Principal() {
- return mDelegate.getSubjectX500Principal();
- }
-}
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 4b48bb749133..3b11854bf7cb 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -21,6 +21,7 @@ import android.hardware.biometrics.BiometricManager;
import android.security.GateKeeper;
import android.security.KeyStore;
import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyExpiredException;
import android.security.keystore.KeyNotYetValidException;
@@ -28,6 +29,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.UserNotAuthenticatedException;
import android.system.keystore2.Authorization;
import android.system.keystore2.ResponseCode;
+import android.util.Log;
import libcore.util.EmptyArray;
@@ -174,4 +176,36 @@ abstract class KeyStoreCryptoOperationUtils {
}
return sRng;
}
+
+ static void abortOperation(KeyStoreOperation operation) {
+ if (operation != null) {
+ try {
+ operation.abort();
+ } catch (KeyStoreException e) {
+ // We log this error, but we can afford to ignore it. Dropping the reference
+ // to the KeyStoreOperation is enough to clean up all related resources even
+ // in the Keystore daemon. We log it anyway, because it may indicate some
+ // underlying problem that is worth debugging.
+ Log.w(
+ "KeyStoreCryptoOperationUtils",
+ "Encountered error trying to abort a keystore operation.",
+ e
+ );
+ }
+ }
+ }
+
+ static long getOrMakeOperationChallenge(KeyStoreOperation operation, AndroidKeyStoreKey key)
+ throws KeyPermanentlyInvalidatedException {
+ if (operation.getChallenge() != null) {
+ if (!KeyStoreCryptoOperationUtils.canUserAuthorizationSucceed(key)) {
+ throw new KeyPermanentlyInvalidatedException();
+ }
+ return operation.getChallenge();
+ } else {
+ // Keystore won't give us an operation challenge if the operation doesn't
+ // need user authorization. So we make our own.
+ return Math.randomLongInternal();
+ }
+ }
}