blob: f1f9436c54a86d990a9b8aa7ae73d32269b033b3 [file] [log] [blame]
package android.security;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import java.security.InvalidAlgorithmParameterException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KeyGeneratorSpi;
import javax.crypto.SecretKey;
/**
* {@link KeyGeneratorSpi} backed by Android KeyStore.
*
* @hide
*/
public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
public static class AES extends KeyStoreKeyGeneratorSpi {
public AES() {
super(KeyStoreKeyConstraints.Algorithm.AES, 128);
}
}
public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi {
public HmacSHA256() {
super(KeyStoreKeyConstraints.Algorithm.HMAC,
KeyStoreKeyConstraints.Digest.SHA256,
KeyStoreKeyConstraints.Digest.getOutputSizeBytes(
KeyStoreKeyConstraints.Digest.SHA256) * 8);
}
}
private final KeyStore mKeyStore = KeyStore.getInstance();
private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mDigest;
private final int mDefaultKeySizeBits;
private KeyGeneratorSpec mSpec;
private SecureRandom mRng;
protected KeyStoreKeyGeneratorSpi(
@KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
int defaultKeySizeBits) {
this(algorithm, null, defaultKeySizeBits);
}
protected KeyStoreKeyGeneratorSpi(
@KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
@KeyStoreKeyConstraints.DigestEnum Integer digest,
int defaultKeySizeBits) {
mAlgorithm = algorithm;
mDigest = digest;
mDefaultKeySizeBits = defaultKeySizeBits;
}
@Override
protected SecretKey engineGenerateKey() {
KeyGeneratorSpec spec = mSpec;
if (spec == null) {
throw new IllegalStateException("Not initialized");
}
if ((spec.isEncryptionRequired())
&& (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
throw new IllegalStateException(
"Android KeyStore must be in initialized and unlocked state if encryption is"
+ " required");
}
KeymasterArguments args = new KeymasterArguments();
args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
KeyStoreKeyConstraints.Algorithm.toKeymaster(mAlgorithm));
if (mDigest != null) {
args.addInt(KeymasterDefs.KM_TAG_DIGEST,
KeyStoreKeyConstraints.Digest.toKeymaster(mDigest));
}
if (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
if (mDigest == null) {
throw new IllegalStateException("Digest algorithm must be specified for key"
+ " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm));
}
Integer digestOutputSizeBytes =
KeyStoreKeyConstraints.Digest.getOutputSizeBytes(mDigest);
if (digestOutputSizeBytes != null) {
// TODO: Remove MAC length constraint once Keymaster API no longer requires it.
// TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
}
}
int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
@KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null)
? spec.getPurposes()
: (KeyStoreKeyConstraints.Purpose.ENCRYPT
| KeyStoreKeyConstraints.Purpose.DECRYPT
| KeyStoreKeyConstraints.Purpose.SIGN
| KeyStoreKeyConstraints.Purpose.VERIFY);
for (int keymasterPurpose :
KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
}
if (spec.getBlockMode() != null) {
args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
KeyStoreKeyConstraints.BlockMode.toKeymaster(spec.getBlockMode()));
}
if (spec.getPadding() != null) {
args.addInt(KeymasterDefs.KM_TAG_PADDING,
KeyStoreKeyConstraints.Padding.toKeymaster(spec.getPadding()));
}
if (spec.getMaxUsesPerBoot() != null) {
args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, spec.getMaxUsesPerBoot());
}
if (spec.getMinSecondsBetweenOperations() != null) {
args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
spec.getMinSecondsBetweenOperations());
}
if (spec.getUserAuthenticators().isEmpty()) {
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
} else {
// TODO: Pass-in user authenticator IDs once the Keymaster API has stabilized
// for (int userAuthenticatorId : spec.getUserAuthenticators()) {
// args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_ID, userAuthenticatorId);
// }
}
if (spec.getUserAuthenticationValidityDurationSeconds() != null) {
args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
spec.getUserAuthenticationValidityDurationSeconds());
}
if (spec.getKeyValidityStart() != null) {
args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart());
}
if (spec.getKeyValidityForOriginationEnd() != null) {
args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
spec.getKeyValidityForOriginationEnd());
}
if (spec.getKeyValidityForConsumptionEnd() != null) {
args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
spec.getKeyValidityForConsumptionEnd());
}
if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
|| ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
// Permit caller-specified IV. This is needed due to the Cipher abstraction.
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
}
byte[] additionalEntropy = null;
SecureRandom rng = mRng;
if (rng != null) {
additionalEntropy = new byte[(keySizeBits + 7) / 8];
rng.nextBytes(additionalEntropy);
}
int flags = spec.getFlags();
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
int errorCode = mKeyStore.generateKey(
keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
if (errorCode != KeyStore.NO_ERROR) {
throw new CryptoOperationException("Failed to generate key",
KeymasterUtils.getExceptionForKeymasterError(errorCode));
}
String keyAlgorithmJCA =
KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest);
return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
}
@Override
protected void engineInit(SecureRandom random) {
throw new UnsupportedOperationException("Cannot initialize without an "
+ KeyGeneratorSpec.class.getName() + " parameter");
}
@Override
protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
throws InvalidAlgorithmParameterException {
if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
throw new InvalidAlgorithmParameterException("Cannot initialize without an "
+ KeyGeneratorSpec.class.getName() + " parameter");
}
KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
if (spec.getKeystoreAlias() == null) {
throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
}
mSpec = spec;
mRng = random;
}
@Override
protected void engineInit(int keySize, SecureRandom random) {
throw new UnsupportedOperationException("Cannot initialize without a "
+ KeyGeneratorSpec.class.getName() + " parameter");
}
}