diff options
Diffstat (limited to 'keystore')
7 files changed, 145 insertions, 63 deletions
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index ed91d70..72cb062 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -486,16 +486,6 @@ public class AndroidKeyStore extends KeyStoreSpi { } } args.addInts(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests); - if (keymasterDigests.length > 0) { - // TODO: Remove MAC length constraint once Keymaster API no longer requires it. - // This code will blow up if mode than one digest is specified. - int digestOutputSizeBytes = - KeymasterUtils.getDigestOutputSizeBytes(keymasterDigests[0]); - if (digestOutputSizeBytes != -1) { - // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster - args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes); - } - } if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { if (keymasterDigests.length == 0) { throw new KeyStoreException("At least one digest algorithm must be specified" diff --git a/keystore/java/android/security/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/AndroidKeyStoreBCWorkaroundProvider.java new file mode 100644 index 0000000..45329cf --- /dev/null +++ b/keystore/java/android/security/AndroidKeyStoreBCWorkaroundProvider.java @@ -0,0 +1,83 @@ +/* + * 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; + +import java.security.Provider; + +/** + * {@link Provider} of JCA crypto operations operating on Android KeyStore keys. + * + * <p>This provider was separated out of {@link AndroidKeyStoreProvider} to work around the issue + * that Bouncy Castle provider incorrectly declares that it accepts arbitrary keys (incl. Android + * KeyStore ones). This causes JCA to select the Bouncy Castle's implementation of JCA crypto + * operations for Android KeyStore keys unless Android KeyStore's own implementations are installed + * as higher-priority than Bouncy Castle ones. The purpose of this provider is to do just that: to + * offer crypto operations operating on Android KeyStore keys and to be installed at higher priority + * than the Bouncy Castle provider. + * + * <p>Once Bouncy Castle provider is fixed, this provider can be merged into the + * {@code AndroidKeyStoreProvider}. + * + * @hide + */ +class AndroidKeyStoreBCWorkaroundProvider extends Provider { + + // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these + // classes when this provider is instantiated and installed early on during each app's + // initialization process. + + private static final String PACKAGE_NAME = "android.security"; + private static final String KEYSTORE_SECRET_KEY_CLASS_NAME = + PACKAGE_NAME + ".KeyStoreSecretKey"; + + AndroidKeyStoreBCWorkaroundProvider() { + super("AndroidKeyStoreBCWorkaround", + 1.0, + "Android KeyStore security provider to work around Bouncy Castle"); + + // javax.crypto.Mac + putMacImpl("HmacSHA1", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA1"); + putMacImpl("HmacSHA224", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA224"); + putMacImpl("HmacSHA256", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA256"); + putMacImpl("HmacSHA384", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA384"); + putMacImpl("HmacSHA512", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA512"); + + // javax.crypto.Cipher + putSymmetricCipherImpl("AES/ECB/NoPadding", + PACKAGE_NAME + ".KeyStoreCipherSpi$AES$ECB$NoPadding"); + putSymmetricCipherImpl("AES/ECB/PKCS7Padding", + PACKAGE_NAME + ".KeyStoreCipherSpi$AES$ECB$PKCS7Padding"); + + putSymmetricCipherImpl("AES/CBC/NoPadding", + PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CBC$NoPadding"); + putSymmetricCipherImpl("AES/CBC/PKCS7Padding", + PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CBC$PKCS7Padding"); + + putSymmetricCipherImpl("AES/CTR/NoPadding", + PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CTR$NoPadding"); + } + + private void putMacImpl(String algorithm, String implClass) { + put("Mac." + algorithm, implClass); + put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME); + } + + private void putSymmetricCipherImpl(String transformation, String implClass) { + put("Cipher." + transformation, implClass); + put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME); + } +} diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java index 43f3b30..518067b 100644 --- a/keystore/java/android/security/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/AndroidKeyStoreProvider.java @@ -17,6 +17,7 @@ package android.security; import java.security.Provider; +import java.security.Security; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -32,10 +33,12 @@ public class AndroidKeyStoreProvider extends Provider { // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these // classes when this provider is instantiated and installed early on during each app's // initialization process. + // + // Crypto operations operating on the AndroidKeyStore keys must not be offered by this provider. + // Instead, they need to be offered by AndroidKeyStoreBCWorkaroundProvider. See its Javadoc + // for details. private static final String PACKAGE_NAME = "android.security"; - private static final String KEYSTORE_SECRET_KEY_CLASS_NAME = - PACKAGE_NAME + ".KeyStoreSecretKey"; public AndroidKeyStoreProvider() { super(PROVIDER_NAME, 1.0, "Android KeyStore security provider"); @@ -62,43 +65,39 @@ public class AndroidKeyStoreProvider extends Provider { putSecretKeyFactoryImpl("HmacSHA256"); putSecretKeyFactoryImpl("HmacSHA384"); putSecretKeyFactoryImpl("HmacSHA512"); + } - // javax.crypto.Mac - putMacImpl("HmacSHA1", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA1"); - putMacImpl("HmacSHA224", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA224"); - putMacImpl("HmacSHA256", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA256"); - putMacImpl("HmacSHA384", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA384"); - putMacImpl("HmacSHA512", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA512"); - - // javax.crypto.Cipher - putSymmetricCipherImpl("AES/ECB/NoPadding", - PACKAGE_NAME + ".KeyStoreCipherSpi$AES$ECB$NoPadding"); - putSymmetricCipherImpl("AES/ECB/PKCS7Padding", - PACKAGE_NAME + ".KeyStoreCipherSpi$AES$ECB$PKCS7Padding"); - - putSymmetricCipherImpl("AES/CBC/NoPadding", - PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CBC$NoPadding"); - putSymmetricCipherImpl("AES/CBC/PKCS7Padding", - PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CBC$PKCS7Padding"); + /** + * Installs a new instance of this provider (and the + * {@link AndroidKeyStoreBCWorkaroundProvider}). + */ + public static void install() { + Provider[] providers = Security.getProviders(); + int bcProviderPosition = -1; + for (int position = 0; position < providers.length; position++) { + Provider provider = providers[position]; + if ("BC".equals(provider.getName())) { + bcProviderPosition = position; + break; + } + } - putSymmetricCipherImpl("AES/CTR/NoPadding", - PACKAGE_NAME + ".KeyStoreCipherSpi$AES$CTR$NoPadding"); + Security.addProvider(new AndroidKeyStoreProvider()); + Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider(); + if (bcProviderPosition != -1) { + // Bouncy Castle provider found -- install the workaround provider above it. + Security.insertProviderAt(workaroundProvider, bcProviderPosition); + } else { + // Bouncy Castle provider not found -- install the workaround provider at lowest + // priority. + Security.addProvider(workaroundProvider); + } } private void putSecretKeyFactoryImpl(String algorithm) { put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".KeyStoreSecretKeyFactorySpi"); } - private void putMacImpl(String algorithm, String implClass) { - put("Mac." + algorithm, implClass); - put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME); - } - - private void putSymmetricCipherImpl(String transformation, String implClass) { - put("Cipher." + transformation, implClass); - put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME); - } - /** * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto * primitive. diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 1563863..5b0e74a 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -16,6 +16,8 @@ package android.security; +import android.app.ActivityThread; +import android.app.Application; import com.android.org.conscrypt.NativeConstants; import android.content.Context; @@ -625,18 +627,31 @@ public class KeyStore { return 0; } + String opPackageName = getMyOpPackageName(); + try { long deviceId = 0; // TODO: plumb hardware id to FPMS - if (!service.isHardwareDetected(deviceId)) { + if (!service.isHardwareDetected(deviceId, opPackageName)) { return 0; } - return service.getAuthenticatorId(); + return service.getAuthenticatorId(opPackageName); } catch (RemoteException e) { throw new IllegalStateException("Failed to communicate with fingerprint service", e); } } + private static String getMyOpPackageName() { + ActivityThread activityThread = ActivityThread.currentActivityThread(); + if (activityThread != null) { + Application application = activityThread.getApplication(); + if (application != null) { + return application.getOpPackageName(); + } + } + throw new IllegalStateException("Cannot create AudioRecord outside of an app"); + } + /** * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error * code. diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java index 4590b9c..c52f61b 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -67,7 +67,7 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp private final KeyStore mKeyStore = KeyStore.getInstance(); private final int mKeymasterDigest; - private final int mMacSizeBytes; + private final int mMacSizeBits; // Fields below are populated by engineInit and should be preserved after engineDoFinal. private KeyStoreSecretKey mKey; @@ -79,12 +79,12 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp protected KeyStoreHmacSpi(int keymasterDigest) { mKeymasterDigest = keymasterDigest; - mMacSizeBytes = KeymasterUtils.getDigestOutputSizeBytes(keymasterDigest); + mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest); } @Override protected int engineGetMacLength() { - return mMacSizeBytes; + return (mMacSizeBits + 7) / 8; } @Override @@ -158,14 +158,16 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp KeymasterArguments keymasterArgs = new KeymasterArguments(); keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC); keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); + keymasterArgs.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits); + KeymasterArguments keymasterOutputArgs = new KeymasterArguments(); OperationResult opResult = mKeyStore.begin( mKey.getAlias(), KeymasterDefs.KM_PURPOSE_SIGN, true, keymasterArgs, - null, - new KeymasterArguments()); + null, // no additional entropy needed for HMAC because it's deterministic + keymasterOutputArgs); if (opResult == null) { throw new KeyStoreConnectException(); } else if ((opResult.resultCode != KeyStore.NO_ERROR) diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java index 20f6042..68b5751 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -45,7 +45,7 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { protected HmacBase(int keymasterDigest) { super(KeymasterDefs.KM_ALGORITHM_HMAC, keymasterDigest, - KeymasterUtils.getDigestOutputSizeBytes(keymasterDigest) * 8); + KeymasterUtils.getDigestOutputSizeBits(keymasterDigest)); } } @@ -120,13 +120,6 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); if (mKeymasterDigest != -1) { args.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); - int digestOutputSizeBytes = - KeymasterUtils.getDigestOutputSizeBytes(mKeymasterDigest); - if (digestOutputSizeBytes != -1) { - // 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); - } } if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { if (mKeymasterDigest == -1) { diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java index 3ccb588..aa44ecd 100644 --- a/keystore/java/android/security/KeymasterUtils.java +++ b/keystore/java/android/security/KeymasterUtils.java @@ -179,22 +179,22 @@ public abstract class KeymasterUtils { return result; } - public static int getDigestOutputSizeBytes(int keymasterDigest) { + public static int getDigestOutputSizeBits(int keymasterDigest) { switch (keymasterDigest) { case KeymasterDefs.KM_DIGEST_NONE: return -1; case KeymasterDefs.KM_DIGEST_MD5: - return 128 / 8; + return 128; case KeymasterDefs.KM_DIGEST_SHA1: - return 160 / 8; + return 160; case KeymasterDefs.KM_DIGEST_SHA_2_224: - return 224 / 8; + return 224; case KeymasterDefs.KM_DIGEST_SHA_2_256: - return 256 / 8; + return 256; case KeymasterDefs.KM_DIGEST_SHA_2_384: - return 384 / 8; + return 384; case KeymasterDefs.KM_DIGEST_SHA_2_512: - return 512 / 8; + return 512; default: throw new IllegalArgumentException("Unknown digest: " + keymasterDigest); } |