From 52886ca77d4d93f6faf5d2a6836625a421e32403 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Tue, 31 Mar 2015 20:55:10 -0700 Subject: [PATCH] A way to obtain KeyStore operation handle from crypto primitives. This adds AndroidKeyStore.getKeyStoreOperationHandle method which can be used to obtain the KeyStore operation handle corresponding to the provided JCA cryto primitive (provided it's backed by AndroidKeyStore). Bug: 18088752 Change-Id: Iaa3b6f9b2281b2ec2de8fd5946d353dc7fdb3d2d --- .../security/AndroidKeyStoreProvider.java | 42 +++++++++++++++++++ .../android/security/KeyStoreCipherSpi.java | 22 +++++++++- .../security/KeyStoreCryptoOperation.java | 31 ++++++++++++++ .../android/security/KeyStoreHmacSpi.java | 11 ++++- 4 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 keystore/java/android/security/KeyStoreCryptoOperation.java diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java index 39f9d9c6174c6..a7c2ddbbe0370 100644 --- a/keystore/java/android/security/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/AndroidKeyStoreProvider.java @@ -16,8 +16,12 @@ package android.security; +import java.lang.reflect.Method; import java.security.Provider; +import javax.crypto.Cipher; +import javax.crypto.Mac; + /** * A provider focused on providing JCA interfaces for the Android KeyStore. * @@ -71,4 +75,42 @@ public class AndroidKeyStoreProvider extends Provider { put("Cipher." + transformation, implClass); put("Cipher." + transformation + " SupportedKeyClasses", KeyStoreSecretKey.class.getName()); } + + /** + * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto + * primitive. + * + *

The following primitives are supported: {@link Cipher} and {@link Mac}. + * + * @return KeyStore operation handle or {@code null} if the provided primitive's KeyStore + * operation is not in progress. + * + * @throws IllegalArgumentException if the provided primitive is not supported or is not backed + * by AndroidKeyStore provider. + */ + public static Long getKeyStoreOperationHandle(Object cryptoPrimitive) { + if (cryptoPrimitive == null) { + throw new NullPointerException(); + } + if ((!(cryptoPrimitive instanceof Mac)) && (!(cryptoPrimitive instanceof Cipher))) { + throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive); + } + Object spi; + // TODO: Replace this Reflection based codewith direct invocations once the libcore changes + // are in. + try { + Method getSpiMethod = cryptoPrimitive.getClass().getDeclaredMethod("getSpi"); + getSpiMethod.setAccessible(true); + spi = getSpiMethod.invoke(cryptoPrimitive); + } catch (ReflectiveOperationException e) { + throw new IllegalArgumentException( + "Unsupported crypto primitive: " + cryptoPrimitive, e); + } + if (!(spi instanceof KeyStoreCryptoOperation)) { + throw new IllegalArgumentException( + "Crypto primitive not backed by Android KeyStore: " + cryptoPrimitive + + ", spi: " + spi); + } + return ((KeyStoreCryptoOperation) spi).getOperationHandle(); + } } diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java index 87f0d8e3dcc8a..bdaa8129d7711 100644 --- a/keystore/java/android/security/KeyStoreCipherSpi.java +++ b/keystore/java/android/security/KeyStoreCipherSpi.java @@ -45,7 +45,7 @@ import javax.crypto.spec.IvParameterSpec; * * @hide */ -public abstract class KeyStoreCipherSpi extends CipherSpi { +public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCryptoOperation { public abstract static class AES extends KeyStoreCipherSpi { protected AES(@KeyStoreKeyConstraints.BlockModeEnum int blockMode, @@ -129,6 +129,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi { * error conditions in between. */ private IBinder mOperationToken; + private Long mOperationHandle; private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer; protected KeyStoreCipherSpi( @@ -192,6 +193,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi { mOperationToken = null; mKeyStore.abort(operationToken); } + mOperationHandle = null; mMainDataStreamer = null; mAdditionalEntropyForBegin = null; } @@ -230,6 +232,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi { throw new CryptoOperationException("Keystore returned null operation token"); } mOperationToken = opResult.token; + mOperationHandle = opResult.operationHandle; loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs); mFirstOperationInitiated = true; mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer( @@ -348,6 +351,23 @@ public abstract class KeyStoreCipherSpi extends CipherSpi { throw new UnsupportedOperationException(); } + @Override + public void finalize() throws Throwable { + try { + IBinder operationToken = mOperationToken; + if (operationToken != null) { + mKeyStore.abort(operationToken); + } + } finally { + super.finalize(); + } + } + + @Override + public Long getOperationHandle() { + return mOperationHandle; + } + // The methods below may need to be overridden by subclasses that use algorithm-specific // parameters. diff --git a/keystore/java/android/security/KeyStoreCryptoOperation.java b/keystore/java/android/security/KeyStoreCryptoOperation.java new file mode 100644 index 0000000000000..19abd05553bfb --- /dev/null +++ b/keystore/java/android/security/KeyStoreCryptoOperation.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * Cryptographic operation backed by {@link KeyStore}. + * + * @hide + */ +public interface KeyStoreCryptoOperation { + /** + * Gets the KeyStore operation handle of this crypto operation. + * + * @return handle or {@code null} if the KeyStore operation is not in progress. + */ + Long getOperationHandle(); +} diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java index 939b41cdda19f..841b90d20cfb9 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -33,7 +33,7 @@ import javax.crypto.MacSpi; * * @hide */ -public abstract class KeyStoreHmacSpi extends MacSpi { +public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation { public static class HmacSHA256 extends KeyStoreHmacSpi { public HmacSHA256() { @@ -50,6 +50,7 @@ public abstract class KeyStoreHmacSpi extends MacSpi { // The fields below are reset by the engineReset operation. private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer; private IBinder mOperationToken; + private Long mOperationHandle; protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) { mDigest = digest; @@ -87,6 +88,7 @@ public abstract class KeyStoreHmacSpi extends MacSpi { mOperationToken = null; mKeyStore.abort(operationToken); } + mOperationHandle = null; mChunkedStreamer = null; KeymasterArguments keymasterArgs = new KeymasterArguments(); @@ -108,6 +110,7 @@ public abstract class KeyStoreHmacSpi extends MacSpi { if (mOperationToken == null) { throw new CryptoOperationException("Keystore returned null operation token"); } + mOperationHandle = opResult.operationHandle; mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer( new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( mKeyStore, mOperationToken)); @@ -157,11 +160,15 @@ public abstract class KeyStoreHmacSpi extends MacSpi { try { IBinder operationToken = mOperationToken; if (operationToken != null) { - mOperationToken = null; mKeyStore.abort(operationToken); } } finally { super.finalize(); } } + + @Override + public Long getOperationHandle() { + return mOperationHandle; + } }