diff options
Diffstat (limited to 'keystore/java/android/security/KeyStoreKeyGeneratorSpi.java')
-rw-r--r-- | keystore/java/android/security/KeyStoreKeyGeneratorSpi.java | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java new file mode 100644 index 0000000..86950dd --- /dev/null +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -0,0 +1,183 @@ +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, + 256); + } + } + + 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)); + } + 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"); + } +} |