summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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();
+ }
+ }
}