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:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user