Keystore 2.0 SPI: Evolve the Crypto SPI.

This patch evolves the Crypto SPI to use the new Keystore 2.0 shim.
The main changes are:
 * The SPI uses the AIDL defined KeyParameter instead of
   KeymasterArguments.
 * Operations are created directly from the KeystoreSecurityLevel that
   is part of the AndroidKeyStoreKey object.

Also this patch deletes the DeletatingX509Certificate class. This is no
longer needed, because public key operations are no longer performed by
Keystore 2.0. We can delegate public certificate operations simply by
wrapping such certificates into public keys that are understood by other
providers, such as BouncyCastle.

Bug: 159476414
Test: None
Change-Id: Ice874a8121d80bf788da059b4e8420c7dd799d81
This commit is contained in:
Janis Danisevskis
2020-10-09 14:23:17 -07:00
parent 4be5005c05
commit 4545933da5
13 changed files with 959 additions and 1093 deletions

View File

@@ -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) {

View File

@@ -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;
}
}
}

View File

@@ -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;
parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
// 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);
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);
}

View File

@@ -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

View File

@@ -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);
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
));
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();
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;
}
}
// 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.
InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
mKeyStore, mKey, opResult.resultCode);
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;
}
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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
));
}
}

View File

@@ -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);
List<KeyParameter> parameters = new ArrayList<>();
addAlgorithmSpecificParametersToBegin(parameters);
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();
int purpose = mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY;
parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
try {
mOperation = mKey.getSecurityLevel().createOperation(
mKey.getKeyIdDescriptor(),
parameters);
} catch (KeyStoreException keyStoreException) {
throw KeyStoreCryptoOperationUtils.getInvalidKeyException(
mKey, keyStoreException);
}
// 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;
// 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);
// If necessary, throw an exception due to KeyStore operation having failed.
InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
mKeyStore, mKey, opResult.resultCode);
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");
}
mMessageStreamer = createMainDataStreamer(mKeyStore, opResult.token);
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);
}

View File

@@ -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) {

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}