diff options
14 files changed, 519 insertions, 5 deletions
diff --git a/api/current.txt b/api/current.txt index 82dd59069c87..6ae25bca858f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -37856,6 +37856,7 @@ package android.security.keystore { method public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); method public boolean isRandomizedEncryptionRequired(); + method public boolean isStrongBoxBacked(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); } @@ -37873,6 +37874,7 @@ package android.security.keystore { method public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...); method public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...); method public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean); + method public android.security.keystore.KeyGenParameterSpec.Builder setIsStrongBoxBacked(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setKeySize(int); method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityEnd(java.util.Date); method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date); @@ -37933,6 +37935,7 @@ package android.security.keystore { field public static final java.lang.String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding"; field public static final java.lang.String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding"; field public static final java.lang.String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding"; + field public static final deprecated java.lang.String KEY_ALGORITHM_3DES = "DESede"; field public static final java.lang.String KEY_ALGORITHM_AES = "AES"; field public static final java.lang.String KEY_ALGORITHM_EC = "EC"; field public static final java.lang.String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1"; @@ -37943,11 +37946,13 @@ package android.security.keystore { field public static final java.lang.String KEY_ALGORITHM_RSA = "RSA"; field public static final int ORIGIN_GENERATED = 1; // 0x1 field public static final int ORIGIN_IMPORTED = 2; // 0x2 + field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8 field public static final int ORIGIN_UNKNOWN = 4; // 0x4 field public static final int PURPOSE_DECRYPT = 2; // 0x2 field public static final int PURPOSE_ENCRYPT = 1; // 0x1 field public static final int PURPOSE_SIGN = 4; // 0x4 field public static final int PURPOSE_VERIFY = 8; // 0x8 + field public static final int PURPOSE_WRAP_KEY = 32; // 0x20 field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1"; field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS"; } @@ -37987,12 +37992,24 @@ package android.security.keystore { method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int); } + public class StrongBoxUnavailableException extends java.security.ProviderException { + ctor public StrongBoxUnavailableException(); + } + public class UserNotAuthenticatedException extends java.security.InvalidKeyException { ctor public UserNotAuthenticatedException(); ctor public UserNotAuthenticatedException(java.lang.String); ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable); } + public class WrappedKeyEntry implements java.security.KeyStore.Entry { + ctor public WrappedKeyEntry(byte[], java.lang.String, java.lang.String, java.security.spec.AlgorithmParameterSpec); + method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec(); + method public java.lang.String getTransformation(); + method public byte[] getWrappedKeyBytes(); + method public java.lang.String getWrappingKeyAlias(); + } + } package android.service.autofill { diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 72afbb8414f6..346437032845 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -102,6 +102,7 @@ public final class KeymasterDefs { public static final int KM_ALGORITHM_RSA = 1; public static final int KM_ALGORITHM_EC = 3; public static final int KM_ALGORITHM_AES = 32; + public static final int KM_ALGORITHM_3DES = 33; public static final int KM_ALGORITHM_HMAC = 128; // Block modes. @@ -131,6 +132,7 @@ public final class KeymasterDefs { public static final int KM_ORIGIN_GENERATED = 0; public static final int KM_ORIGIN_IMPORTED = 2; public static final int KM_ORIGIN_UNKNOWN = 3; + public static final int KM_ORIGIN_SECURELY_IMPORTED = 4; // Key usability requirements. public static final int KM_BLOB_STANDALONE = 0; @@ -141,6 +143,7 @@ public final class KeymasterDefs { public static final int KM_PURPOSE_DECRYPT = 1; public static final int KM_PURPOSE_SIGN = 2; public static final int KM_PURPOSE_VERIFY = 3; + public static final int KM_PURPOSE_WRAP = 5; // Key formats. public static final int KM_KEY_FORMAT_X509 = 0; diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 1690e8ce9309..e25386baf969 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -510,6 +510,19 @@ public class KeyStore { return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics); } + public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey, + String wrappingKeyAlias, + byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid, + KeyCharacteristics outCharacteristics) { + try { + return mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias, + maskingKey, args, rootSid, fingerprintSid, outCharacteristics); + } catch (RemoteException e) { + Log.w(TAG, "Cannot connect to keystore", e); + return SYSTEM_ERROR; + } + } + public ExportResult exportKey(String alias, int format, KeymasterBlob clientId, KeymasterBlob appId, int uid) { try { diff --git a/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java new file mode 100644 index 000000000000..01fd0624572c --- /dev/null +++ b/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2018 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.keystore; + +import android.security.keymaster.KeymasterArguments; +import android.security.keymaster.KeymasterDefs; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.util.Arrays; + +import javax.crypto.CipherSpi; +import javax.crypto.spec.IvParameterSpec; + +/** + * Base class for Android Keystore 3DES {@link CipherSpi} implementations. + * + * @hide + */ +public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase { + + private static final int BLOCK_SIZE_BYTES = 8; + + private final int mKeymasterBlockMode; + private final int mKeymasterPadding; + /** Whether this transformation requires an IV. */ + private final boolean mIvRequired; + + private byte[] mIv; + + /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */ + private boolean mIvHasBeenUsed; + + AndroidKeyStore3DESCipherSpi( + int keymasterBlockMode, + int keymasterPadding, + boolean ivRequired) { + mKeymasterBlockMode = keymasterBlockMode; + mKeymasterPadding = keymasterPadding; + mIvRequired = ivRequired; + } + + abstract static class ECB extends AndroidKeyStore3DESCipherSpi { + protected ECB(int keymasterPadding) { + super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false); + } + + public static class NoPadding extends ECB { + public NoPadding() { + super(KeymasterDefs.KM_PAD_NONE); + } + } + + public static class PKCS7Padding extends ECB { + public PKCS7Padding() { + super(KeymasterDefs.KM_PAD_PKCS7); + } + } + } + + abstract static class CBC extends AndroidKeyStore3DESCipherSpi { + protected CBC(int keymasterPadding) { + super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true); + } + + public static class NoPadding extends CBC { + public NoPadding() { + super(KeymasterDefs.KM_PAD_NONE); + } + } + + public static class PKCS7Padding extends CBC { + public PKCS7Padding() { + super(KeymasterDefs.KM_PAD_PKCS7); + } + } + } + + @Override + protected void initKey(int i, Key key) throws InvalidKeyException { + if (!(key instanceof AndroidKeyStoreSecretKey)) { + throw new InvalidKeyException( + "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null")); + } + if (!KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(key.getAlgorithm())) { + throw new InvalidKeyException( + "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " + + KeyProperties.KEY_ALGORITHM_3DES + " supported"); + } + setKey((AndroidKeyStoreSecretKey) key); + } + + @Override + protected int engineGetBlockSize() { + return BLOCK_SIZE_BYTES; + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return inputLen + 3 * BLOCK_SIZE_BYTES; + } + + @Override + protected final byte[] engineGetIV() { + return ArrayUtils.cloneIfNotEmpty(mIv); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + if (!mIvRequired) { + return null; + } + if ((mIv != null) && (mIv.length > 0)) { + try { + AlgorithmParameters params = AlgorithmParameters.getInstance("DESede"); + params.init(new IvParameterSpec(mIv)); + return params; + } catch (NoSuchAlgorithmException e) { + throw new ProviderException( + "Failed to obtain 3DES AlgorithmParameters", e); + } catch (InvalidParameterSpecException e) { + throw new ProviderException( + "Failed to initialize 3DES AlgorithmParameters with an IV", + e); + } + } + return null; + } + + @Override + protected void initAlgorithmSpecificParameters() throws InvalidKeyException { + if (!mIvRequired) { + return; + } + + // IV is used + if (!isEncrypting()) { + throw new InvalidKeyException("IV required when decrypting" + + ". Use IvParameterSpec or AlgorithmParameters to provide it."); + } + } + + @Override + protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (!mIvRequired) { + if (params != null) { + throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); + } + return; + } + + // IV is used + if (params == null) { + if (!isEncrypting()) { + // IV must be provided by the caller + throw new InvalidAlgorithmParameterException( + "IvParameterSpec must be provided when decrypting"); + } + return; + } + if (!(params instanceof IvParameterSpec)) { + throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported"); + } + mIv = ((IvParameterSpec) params).getIV(); + if (mIv == null) { + throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec"); + } + } + + @Override + protected void initAlgorithmSpecificParameters(AlgorithmParameters params) + throws InvalidAlgorithmParameterException { + if (!mIvRequired) { + if (params != null) { + throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params); + } + return; + } + + // IV is used + if (params == null) { + if (!isEncrypting()) { + // IV must be provided by the caller + throw new InvalidAlgorithmParameterException("IV required when decrypting" + + ". Use IvParameterSpec or AlgorithmParameters to provide it."); + } + return; + } + + if (!"DESede".equalsIgnoreCase(params.getAlgorithm())) { + throw new InvalidAlgorithmParameterException( + "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm() + + ". Supported: DESede"); + } + + IvParameterSpec ivSpec; + try { + ivSpec = params.getParameterSpec(IvParameterSpec.class); + } catch (InvalidParameterSpecException e) { + if (!isEncrypting()) { + // IV must be provided by the caller + throw new InvalidAlgorithmParameterException("IV required when decrypting" + + ", but not found in parameters: " + params, e); + } + mIv = null; + return; + } + mIv = ivSpec.getIV(); + if (mIv == null) { + throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters"); + } + } + + @Override + protected final int getAdditionalEntropyAmountForBegin() { + if ((mIvRequired) && (mIv == null) && (isEncrypting())) { + // IV will need to be generated + return BLOCK_SIZE_BYTES; + } + + return 0; + } + + @Override + protected int getAdditionalEntropyAmountForFinish() { + return 0; + } + + @Override + protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) { + if ((isEncrypting()) && (mIvRequired) && (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."); + } + + 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); + } + } + + @Override + protected void loadAlgorithmSpecificParametersFromBeginResult( + KeymasterArguments keymasterArgs) { + 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; + } + + if (mIvRequired) { + if (mIv == null) { + mIv = returnedIv; + } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) { + throw new ProviderException("IV in use differs from provided IV"); + } + } else { + if (returnedIv != null) { + throw new ProviderException( + "IV in use despite IV not being used by this transformation"); + } + } + } + + @Override + protected final void resetAll() { + mIv = null; + mIvHasBeenUsed = false; + super.resetAll(); + } +} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java index be390ffca2d9..e4cf84afcb8f 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java @@ -93,6 +93,16 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider { putSymmetricCipherImpl("AES/CTR/NoPadding", PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding"); + putSymmetricCipherImpl("DESede/CBC/NoPadding", + PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$NoPadding"); + putSymmetricCipherImpl("DESede/CBC/PKCS7Padding", + PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$PKCS7Padding"); + + putSymmetricCipherImpl("DESede/ECB/NoPadding", + PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$NoPadding"); + putSymmetricCipherImpl("DESede/ECB/PKCS7Padding", + PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$PKCS7Padding"); + putSymmetricCipherImpl("AES/GCM/NoPadding", PACKAGE_NAME + ".AndroidKeyStoreAuthenticatedAESCipherSpi$GCM$NoPadding"); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java index fdebf3793f12..5bcb34a67a54 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java @@ -307,7 +307,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor * * <p>This implementation returns {@code null}. * - * @returns stream or {@code null} if AAD is not supported by this cipher. + * @return stream or {@code null} if AAD is not supported by this cipher. */ @Nullable protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer( diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index f1d1e1665387..379e17701c8f 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -60,6 +60,12 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } } + public static class DESede extends AndroidKeyStoreKeyGeneratorSpi { + public DESede() { + super(KeymasterDefs.KM_ALGORITHM_3DES, 168); + } + } + protected static abstract class HmacBase extends AndroidKeyStoreKeyGeneratorSpi { protected HmacBase(int keymasterDigest) { super(KeymasterDefs.KM_ALGORITHM_HMAC, diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index 55e6519d805d..10189263e98f 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -80,6 +80,7 @@ public class AndroidKeyStoreProvider extends Provider { // javax.crypto.KeyGenerator put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); + put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede"); put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1"); put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224"); put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256"); @@ -88,6 +89,7 @@ public class AndroidKeyStoreProvider extends Provider { // java.security.SecretKeyFactory putSecretKeyFactoryImpl("AES"); + putSecretKeyFactoryImpl("DESede"); putSecretKeyFactoryImpl("HmacSHA1"); putSecretKeyFactoryImpl("HmacSHA224"); putSecretKeyFactoryImpl("HmacSHA256"); @@ -348,7 +350,8 @@ public class AndroidKeyStoreProvider extends Provider { } if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC || - keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES) { + keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES || + keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) { return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid, keyCharacteristics); } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA || diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index d73a9e29bb1b..440e0863fbb1 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -18,6 +18,7 @@ package android.security.keystore; import libcore.util.EmptyArray; import android.security.Credentials; +import android.security.GateKeeper; import android.security.KeyStore; import android.security.KeyStoreParameter; import android.security.keymaster.KeyCharacteristics; @@ -25,6 +26,7 @@ import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; +import android.security.keystore.WrappedKeyEntry; import android.util.Log; import java.io.ByteArrayInputStream; @@ -744,6 +746,31 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { } } + private void setWrappedKeyEntry(String alias, byte[] wrappedKeyBytes, String wrappingKeyAlias, + java.security.KeyStore.ProtectionParameter param) throws KeyStoreException { + if (param != null) { + throw new KeyStoreException("Protection parameters are specified inside wrapped keys"); + } + + byte[] maskingKey = new byte[32]; + KeymasterArguments args = new KeymasterArguments(); // TODO: populate wrapping key args. + + int errorCode = mKeyStore.importWrappedKey( + Credentials.USER_SECRET_KEY + alias, + wrappedKeyBytes, + Credentials.USER_PRIVATE_KEY + wrappingKeyAlias, + maskingKey, + args, + GateKeeper.getSecureUserId(), + 0, // FIXME fingerprint id? + mUid, + new KeyCharacteristics()); + if (errorCode != KeyStore.NO_ERROR) { + throw new KeyStoreException("Failed to import wrapped key. Keystore error code: " + + errorCode); + } + } + @Override public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain) throws KeyStoreException { @@ -974,6 +1001,9 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { } else if (entry instanceof SecretKeyEntry) { SecretKeyEntry secE = (SecretKeyEntry) entry; setSecretKeyEntry(alias, secE.getSecretKey(), param); + } else if (entry instanceof WrappedKeyEntry) { + WrappedKeyEntry wke = (WrappedKeyEntry) entry; + setWrappedKeyEntry(alias, wke.getWrappedKeyBytes(), wke.getWrappingKeyAlias(), param); } else { throw new KeyStoreException( "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry" diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 1238d8774e58..1e2b873c7fdd 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -262,6 +262,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private final boolean mUniqueIdIncluded; private final boolean mUserAuthenticationValidWhileOnBody; private final boolean mInvalidatedByBiometricEnrollment; + private final boolean mIsStrongBoxBacked; /** * @hide should be built with Builder @@ -289,7 +290,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { byte[] attestationChallenge, boolean uniqueIdIncluded, boolean userAuthenticationValidWhileOnBody, - boolean invalidatedByBiometricEnrollment) { + boolean invalidatedByBiometricEnrollment, + boolean isStrongBoxBacked) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -335,6 +337,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mUniqueIdIncluded = uniqueIdIncluded; mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; + mIsStrongBoxBacked = isStrongBoxBacked; } /** @@ -625,6 +628,13 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * Returns {@code true} if the key is protected by a Strongbox security chip. + */ + public boolean isStrongBoxBacked() { + return mIsStrongBoxBacked; + } + + /** * Builder of {@link KeyGenParameterSpec} instances. */ public final static class Builder { @@ -652,6 +662,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private boolean mUniqueIdIncluded = false; private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; + private boolean mIsStrongBoxBacked = false; /** * Creates a new instance of the {@code Builder}. @@ -1177,6 +1188,15 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * Sets whether this key should be protected by a StrongBox security chip. + */ + @NonNull + public Builder setIsStrongBoxBacked(boolean isStrongBoxBacked) { + mIsStrongBoxBacked = isStrongBoxBacked; + return this; + } + + /** * Builds an instance of {@code KeyGenParameterSpec}. */ @NonNull @@ -1204,7 +1224,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mAttestationChallenge, mUniqueIdIncluded, mUserAuthenticationValidWhileOnBody, - mInvalidatedByBiometricEnrollment); + mInvalidatedByBiometricEnrollment, + mIsStrongBoxBacked); } } } diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index a250d1f01ba1..f54b6decc5c4 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -44,6 +44,7 @@ public abstract class KeyProperties { PURPOSE_DECRYPT, PURPOSE_SIGN, PURPOSE_VERIFY, + PURPOSE_WRAP_KEY, }) public @interface PurposeEnum {} @@ -68,6 +69,11 @@ public abstract class KeyProperties { public static final int PURPOSE_VERIFY = 1 << 3; /** + * Purpose of key: wrapping and unwrapping wrapped keys for secure import. + */ + public static final int PURPOSE_WRAP_KEY = 1 << 5; + + /** * @hide */ public static abstract class Purpose { @@ -83,6 +89,8 @@ public abstract class KeyProperties { return KeymasterDefs.KM_PURPOSE_SIGN; case PURPOSE_VERIFY: return KeymasterDefs.KM_PURPOSE_VERIFY; + case PURPOSE_WRAP_KEY: + return KeymasterDefs.KM_PURPOSE_WRAP; default: throw new IllegalArgumentException("Unknown purpose: " + purpose); } @@ -98,6 +106,8 @@ public abstract class KeyProperties { return PURPOSE_SIGN; case KeymasterDefs.KM_PURPOSE_VERIFY: return PURPOSE_VERIFY; + case KeymasterDefs.KM_PURPOSE_WRAP: + return PURPOSE_WRAP_KEY; default: throw new IllegalArgumentException("Unknown purpose: " + purpose); } @@ -146,6 +156,15 @@ public abstract class KeyProperties { /** Advanced Encryption Standard (AES) key. */ public static final String KEY_ALGORITHM_AES = "AES"; + /** + * Triple Data Encryption Algorithm (3DES) key. + * + * @deprecated Included for interoperability with legacy systems. Prefer {@link + * KeyProperties#KEY_ALGORITHM_AES} for new development. + */ + @Deprecated + public static final String KEY_ALGORITHM_3DES = "DESede"; + /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */ public static final String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1"; @@ -196,6 +215,8 @@ public abstract class KeyProperties { @NonNull @KeyAlgorithmEnum String algorithm) { if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) { return KeymasterDefs.KM_ALGORITHM_AES; + } else if (KEY_ALGORITHM_3DES.equalsIgnoreCase(algorithm)) { + return KeymasterDefs.KM_ALGORITHM_3DES; } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) { return KeymasterDefs.KM_ALGORITHM_HMAC; } else { @@ -210,6 +231,8 @@ public abstract class KeyProperties { switch (keymasterAlgorithm) { case KeymasterDefs.KM_ALGORITHM_AES: return KEY_ALGORITHM_AES; + case KeymasterDefs.KM_ALGORITHM_3DES: + return KEY_ALGORITHM_3DES; case KeymasterDefs.KM_ALGORITHM_HMAC: switch (keymasterDigest) { case KeymasterDefs.KM_DIGEST_SHA1: @@ -666,6 +689,10 @@ public abstract class KeyProperties { */ public static final int ORIGIN_UNKNOWN = 1 << 2; + /** Key was imported into the AndroidKeyStore in an encrypted wrapper */ + public static final int ORIGIN_SECURELY_IMPORTED = 1 << 3; + + /** * @hide */ @@ -680,6 +707,8 @@ public abstract class KeyProperties { return ORIGIN_IMPORTED; case KeymasterDefs.KM_ORIGIN_UNKNOWN: return ORIGIN_UNKNOWN; + case KeymasterDefs.KM_ORIGIN_SECURELY_IMPORTED: + return ORIGIN_SECURELY_IMPORTED; default: throw new IllegalArgumentException("Unknown origin: " + origin); } diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index 2eb06631c228..dbacb9c53dd6 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -488,9 +488,9 @@ public final class KeyProtection implements ProtectionParameter { private int mUserAuthenticationValidityDurationSeconds = -1; private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; - private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; private boolean mCriticalToDeviceEncryption = false; + /** * Creates a new instance of the {@code Builder}. * diff --git a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java new file mode 100644 index 000000000000..ad41a58eb76f --- /dev/null +++ b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 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.keystore; + +import java.security.ProviderException; + +/** + * Indicates that an operation could not be performed because the requested security hardware + * is not available. + */ +public class StrongBoxUnavailableException extends ProviderException { + +} + diff --git a/keystore/java/android/security/keystore/WrappedKeyEntry.java b/keystore/java/android/security/keystore/WrappedKeyEntry.java new file mode 100644 index 000000000000..a8f4afe7a201 --- /dev/null +++ b/keystore/java/android/security/keystore/WrappedKeyEntry.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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.keystore; + +import java.security.KeyStore.Entry; +import java.security.spec.AlgorithmParameterSpec; + +/** + * An {@link Entry} that holds a wrapped key. + */ +public class WrappedKeyEntry implements Entry { + + private final byte[] mWrappedKeyBytes; + private final String mWrappingKeyAlias; + private final String mTransformation; + private final AlgorithmParameterSpec mAlgorithmParameterSpec; + + public WrappedKeyEntry(byte[] wrappedKeyBytes, String wrappingKeyAlias, String transformation, + AlgorithmParameterSpec algorithmParameterSpec) { + mWrappedKeyBytes = wrappedKeyBytes; + mWrappingKeyAlias = wrappingKeyAlias; + mTransformation = transformation; + mAlgorithmParameterSpec = algorithmParameterSpec; + } + + public byte[] getWrappedKeyBytes() { + return mWrappedKeyBytes; + } + + public String getWrappingKeyAlias() { + return mWrappingKeyAlias; + } + + public String getTransformation() { + return mTransformation; + } + + public AlgorithmParameterSpec getAlgorithmParameterSpec() { + return mAlgorithmParameterSpec; + } +} |