Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4545933d authored by Janis Danisevskis's avatar Janis Danisevskis
Browse files

Keystore 2.0 SPI: Evolve the Crypto SPI.

This patch evolves the Crypto SPI to use the new Keystore 2.0 shim.
The main changes are:
 * The SPI uses the AIDL defined KeyParameter instead of
   KeymasterArguments.
 * Operations are created directly from the KeystoreSecurityLevel that
   is part of the AndroidKeyStoreKey object.

Also this patch deletes the DeletatingX509Certificate class. This is no
longer needed, because public key operations are no longer performed by
Keystore 2.0. We can delegate public certificate operations simply by
wrapping such certificates into public keys that are understood by other
providers, such as BouncyCastle.

Bug: 159476414
Test: None
Change-Id: Ice874a8121d80bf788da059b4e8420c7dd799d81
parent 4be5005c
Loading
Loading
Loading
Loading
+32 −19
Original line number Original line Diff line number Diff line
@@ -16,10 +16,11 @@


package android.security.keystore2;
package android.security.keystore2;


import android.security.keymaster.KeymasterArguments;
import android.annotation.NonNull;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProperties;
import android.system.keystore2.KeyParameter;


import java.security.AlgorithmParameters;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidAlgorithmParameterException;
@@ -30,6 +31,7 @@ import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
import java.util.Arrays;
import java.util.List;


import javax.crypto.CipherSpi;
import javax.crypto.CipherSpi;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.IvParameterSpec;
@@ -67,15 +69,13 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
            super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
            super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
        }
        }


        public static class NoPadding extends
        public static class NoPadding extends ECB {
                AndroidKeyStore3DESCipherSpi.ECB {
            public NoPadding() {
            public NoPadding() {
                super(KeymasterDefs.KM_PAD_NONE);
                super(KeymasterDefs.KM_PAD_NONE);
            }
            }
        }
        }


        public static class PKCS7Padding extends
        public static class PKCS7Padding extends ECB {
                AndroidKeyStore3DESCipherSpi.ECB {
            public PKCS7Padding() {
            public PKCS7Padding() {
                super(KeymasterDefs.KM_PAD_PKCS7);
                super(KeymasterDefs.KM_PAD_PKCS7);
            }
            }
@@ -87,15 +87,13 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
            super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
            super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
        }
        }


        public static class NoPadding extends
        public static class NoPadding extends CBC {
                AndroidKeyStore3DESCipherSpi.CBC {
            public NoPadding() {
            public NoPadding() {
                super(KeymasterDefs.KM_PAD_NONE);
                super(KeymasterDefs.KM_PAD_NONE);
            }
            }
        }
        }


        public static class PKCS7Padding extends
        public static class PKCS7Padding extends CBC {
                AndroidKeyStore3DESCipherSpi.CBC {
            public PKCS7Padding() {
            public PKCS7Padding() {
                super(KeymasterDefs.KM_PAD_PKCS7);
                super(KeymasterDefs.KM_PAD_PKCS7);
            }
            }
@@ -254,7 +252,7 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
    }
    }


    @Override
    @Override
    protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) {
    protected void addAlgorithmSpecificParametersToBegin(@NonNull List<KeyParameter> parameters) {
        if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
        if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
            // IV is being reused for encryption: this violates security best practices.
            // IV is being reused for encryption: this violates security best practices.
            throw new IllegalStateException(
            throw new IllegalStateException(
@@ -262,23 +260,38 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
                            + " practices.");
                            + " practices.");
        }
        }


        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_3DES);
        parameters.add(KeyStore2ParameterUtils.makeEnum(
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
                KeymasterDefs.KM_TAG_ALGORITHM,
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
                KeymasterDefs.KM_ALGORITHM_3DES
        if ((mIvRequired) && (mIv != null)) {
        ));
            keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_BLOCK_MODE,
                mKeymasterBlockMode
        ));
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_PADDING,
                mKeymasterPadding
        ));

        if (mIvRequired && (mIv != null)) {
            parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv));
        }
        }
    }
    }


    @Override
    @Override
    protected void loadAlgorithmSpecificParametersFromBeginResult(
    protected void loadAlgorithmSpecificParametersFromBeginResult(
            KeymasterArguments keymasterArgs) {
            KeyParameter[] parameters) {
        mIvHasBeenUsed = true;
        mIvHasBeenUsed = true;


        // NOTE: Keymaster doesn't always return an IV, even if it's used.
        // NOTE: Keymaster doesn't always return an IV, even if it's used.
        byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
        byte[] returnedIv = null;
        if ((returnedIv != null) && (returnedIv.length == 0)) {
        if (parameters != null) {
            returnedIv = null;
            for (KeyParameter p : parameters) {
                if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
                    returnedIv = p.blob;
                    break;
                }
            }
        }
        }


        if (mIvRequired) {
        if (mIvRequired) {
+50 −65
Original line number Original line Diff line number Diff line
@@ -18,15 +18,13 @@ package android.security.keystore2;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.os.IBinder;
import android.security.KeyStore;
import android.security.KeyStoreException;
import android.security.KeyStoreException;
import android.security.keymaster.KeymasterArguments;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
import android.security.keystore.ArrayUtils;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProperties;
import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream;
import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream;
import android.system.keystore2.KeyParameter;


import libcore.util.EmptyArray;
import libcore.util.EmptyArray;


@@ -41,6 +39,7 @@ import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
import java.util.Arrays;
import java.util.List;


import javax.crypto.CipherSpi;
import javax.crypto.CipherSpi;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.GCMParameterSpec;
@@ -175,26 +174,25 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
        @NonNull
        @NonNull
        @Override
        @Override
        protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
        protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
                KeyStore keyStore, IBinder operationToken) {
                KeyStoreOperation operation) {
            KeyStoreCryptoOperationStreamer
            KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer(
                    streamer = new KeyStoreCryptoOperationChunkedStreamer(
                    new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
                    new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
                            keyStore, operationToken), 0);
                            operation), 0);
            if (isEncrypting()) {
            if (isEncrypting()) {
                return streamer;
                return streamer;
            } else {
            } else {
                // When decrypting, to avoid leaking unauthenticated plaintext, do not return any
                // When decrypting, to avoid leaking unauthenticated plaintext, do not return any
                // plaintext before ciphertext is authenticated by KeyStore.finish.
                // plaintext before ciphertext is authenticated by KeyStore.finish.
                return new AndroidKeyStoreAuthenticatedAESCipherSpi.BufferAllOutputUntilDoFinalStreamer(streamer);
                return new BufferAllOutputUntilDoFinalStreamer(streamer);
            }
            }
        }
        }


        @NonNull
        @NonNull
        @Override
        @Override
        protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
        protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
                KeyStore keyStore, IBinder operationToken) {
                KeyStoreOperation operation) {
            return new KeyStoreCryptoOperationChunkedStreamer(
            return new KeyStoreCryptoOperationChunkedStreamer(
                    new AndroidKeyStoreAuthenticatedAESCipherSpi.AdditionalAuthenticationDataStream(keyStore, operationToken), 0);
                    new AdditionalAuthenticationDataStream(operation), 0);
        }
        }


        @Override
        @Override
@@ -214,17 +212,19 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC


        @Override
        @Override
        protected final void addAlgorithmSpecificParametersToBegin(
        protected final void addAlgorithmSpecificParametersToBegin(
                @NonNull KeymasterArguments keymasterArgs) {
                @NonNull List<KeyParameter> parameters) {
            super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
            super.addAlgorithmSpecificParametersToBegin(parameters);
            keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits);
            parameters.add(KeyStore2ParameterUtils.makeInt(
                    KeymasterDefs.KM_TAG_MAC_LENGTH,
                    mTagLengthBits
            ));
        }
        }


        protected final int getTagLengthBits() {
        protected final int getTagLengthBits() {
            return mTagLengthBits;
            return mTagLengthBits;
        }
        }


        public static final class NoPadding extends
        public static final class NoPadding extends GCM {
                AndroidKeyStoreAuthenticatedAESCipherSpi.GCM {
            public NoPadding() {
            public NoPadding() {
                super(KeymasterDefs.KM_PAD_NONE);
                super(KeymasterDefs.KM_PAD_NONE);
            }
            }
@@ -290,31 +290,45 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC


    @Override
    @Override
    protected void addAlgorithmSpecificParametersToBegin(
    protected void addAlgorithmSpecificParametersToBegin(
            @NonNull KeymasterArguments keymasterArgs) {
            @NonNull List<KeyParameter> parameters) {
        if ((isEncrypting()) && (mIvHasBeenUsed)) {
        if ((isEncrypting()) && (mIvHasBeenUsed)) {
            // IV is being reused for encryption: this violates security best practices.
            // IV is being reused for encryption: this violates security best practices.
            throw new IllegalStateException(
            throw new IllegalStateException(
                    "IV has already been used. Reusing IV in encryption mode violates security best"
                    "IV has already been used. Reusing IV in encryption mode violates security best"
                    + " practices.");
                    + " practices.");
        }
        }
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_ALGORITHM,
                KeymasterDefs.KM_ALGORITHM_AES
        ));
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_BLOCK_MODE,
                mKeymasterBlockMode
        ));
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_PADDING,
                mKeymasterPadding
        ));


        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
        if (mIv != null) {
        if (mIv != null) {
            keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
            parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv));
        }
        }
    }
    }


    @Override
    @Override
    protected final void loadAlgorithmSpecificParametersFromBeginResult(
    protected final void loadAlgorithmSpecificParametersFromBeginResult(
            @NonNull KeymasterArguments keymasterArgs) {
            KeyParameter[] parameters) {
        mIvHasBeenUsed = true;
        mIvHasBeenUsed = true;


        // NOTE: Keymaster doesn't always return an IV, even if it's used.
        // NOTE: Keymaster doesn't always return an IV, even if it's used.
        byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
        byte[] returnedIv = null;
        if ((returnedIv != null) && (returnedIv.length == 0)) {
        if (parameters != null) {
            returnedIv = null;
            for (KeyParameter p : parameters) {
                if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
                    returnedIv = p.blob;
                    break;
                }
            }
        }
        }


        if (mIv == null) {
        if (mIv == null) {
@@ -353,8 +367,7 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
        private ByteArrayOutputStream mBufferedOutput = new ByteArrayOutputStream();
        private ByteArrayOutputStream mBufferedOutput = new ByteArrayOutputStream();
        private long mProducedOutputSizeBytes;
        private long mProducedOutputSizeBytes;


        private BufferAllOutputUntilDoFinalStreamer(
        private BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate) {
                KeyStoreCryptoOperationStreamer delegate) {
            mDelegate = delegate;
            mDelegate = delegate;
        }
        }


@@ -374,9 +387,8 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC


        @Override
        @Override
        public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
        public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
                byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
                byte[] signature) throws KeyStoreException {
            byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature,
            byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature);
                    additionalEntropy);
            if (output != null) {
            if (output != null) {
                try {
                try {
                    mBufferedOutput.write(output);
                    mBufferedOutput.write(output);
@@ -407,48 +419,21 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
     */
     */
    private static class AdditionalAuthenticationDataStream implements Stream {
    private static class AdditionalAuthenticationDataStream implements Stream {


        private final KeyStore mKeyStore;
        private final KeyStoreOperation mOperation;
        private final IBinder mOperationToken;


        private AdditionalAuthenticationDataStream(KeyStore keyStore, IBinder operationToken) {
        private AdditionalAuthenticationDataStream(KeyStoreOperation operation) {
            mKeyStore = keyStore;
            mOperation = operation;
            mOperationToken = operationToken;
        }
        }


        @Override
        @Override
        public OperationResult update(byte[] input) {
        public byte[] update(byte[] input) throws KeyStoreException {
            KeymasterArguments keymasterArgs = new KeymasterArguments();
            mOperation.updateAad(input);
            keymasterArgs.addBytes(KeymasterDefs.KM_TAG_ASSOCIATED_DATA, input);
            return null;

            // KeyStore does not reflect AAD in inputConsumed, but users of Stream rely on this
            // field. We fix this discrepancy here. KeyStore.update contract is that all of AAD
            // has been consumed if the method succeeds.
            OperationResult result = mKeyStore.update(mOperationToken, keymasterArgs, null);
            if (result.resultCode == KeyStore.NO_ERROR) {
                result = new OperationResult(
                        result.resultCode,
                        result.token,
                        result.operationHandle,
                        input.length, // inputConsumed
                        result.output,
                        result.outParams);
            }
            return result;
        }
        }


        @Override
        @Override
        public OperationResult finish(byte[] input, byte[] signature, byte[] additionalEntropy) {
        public byte[] finish(byte[] input, byte[] signature) {
            if ((additionalEntropy != null) && (additionalEntropy.length > 0)) {
            return null;
                throw new ProviderException("AAD stream does not support additional entropy");
            }
            return new OperationResult(
                    KeyStore.NO_ERROR,
                    mOperationToken,
                    0, // operation handle -- nobody cares about this being returned from finish
                    0, // inputConsumed
                    EmptyArray.BYTE, // output
                    new KeymasterArguments() // additional params returned by finish
                    );
        }
        }
    }
    }
}
}
 No newline at end of file
+76 −94

File changed.

Preview size limit exceeded, changes collapsed.

+23 −19
Original line number Original line Diff line number Diff line
@@ -17,19 +17,19 @@
package android.security.keystore2;
package android.security.keystore2;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.os.IBinder;
import android.security.KeyStore;
import android.security.KeyStoreException;
import android.security.KeyStoreException;
import android.security.keymaster.KeyCharacteristics;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProperties;
import android.system.keystore2.Authorization;
import android.system.keystore2.KeyParameter;


import libcore.util.EmptyArray;
import libcore.util.EmptyArray;


import java.io.ByteArrayOutputStream;
import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.InvalidKeyException;
import java.security.SignatureSpi;
import java.security.SignatureSpi;
import java.util.List;


/**
/**
 * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
 * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
@@ -44,10 +44,10 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
        }
        }


        @Override
        @Override
        protected KeyStoreCryptoOperationStreamer createMainDataStreamer(KeyStore keyStore,
        protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
                IBinder operationToken) {
                KeyStoreOperation operation) {
            return new TruncateToFieldSizeMessageStreamer(
            return new TruncateToFieldSizeMessageStreamer(
                    super.createMainDataStreamer(keyStore, operationToken),
                    super.createMainDataStreamer(operation),
                    getGroupSizeBits());
                    getGroupSizeBits());
        }
        }


@@ -81,8 +81,8 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
            }
            }


            @Override
            @Override
            public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature,
            public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature)
                    byte[] additionalEntropy) throws KeyStoreException {
                    throws KeyStoreException {
                if (inputLength > 0) {
                if (inputLength > 0) {
                    mConsumedInputSizeBytes += inputLength;
                    mConsumedInputSizeBytes += inputLength;
                    mInputBuffer.write(input, inputOffset, inputLength);
                    mInputBuffer.write(input, inputOffset, inputLength);
@@ -94,7 +94,7 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
                return mDelegate.doFinal(bufferedInput,
                return mDelegate.doFinal(bufferedInput,
                        0,
                        0,
                        Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)),
                        Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)),
                        signature, additionalEntropy);
                        signature);
            }
            }


            @Override
            @Override
@@ -154,13 +154,13 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
                    + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
                    + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
        }
        }


        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
        long keySizeBits = -1;
        int errorCode = getKeyStore().getKeyCharacteristics(
        for (Authorization a : key.getAuthorizations()) {
                key.getAlias(), null, null, key.getUid(), keyCharacteristics);
            if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) {
        if (errorCode != KeyStore.NO_ERROR) {
                keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a);
            throw getKeyStore().getInvalidKeyException(key.getAlias(), key.getUid(), errorCode);
            }
        }
        }
        long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);

        if (keySizeBits == -1) {
        if (keySizeBits == -1) {
            throw new InvalidKeyException("Size of key not known");
            throw new InvalidKeyException("Size of key not known");
        } else if (keySizeBits > Integer.MAX_VALUE) {
        } else if (keySizeBits > Integer.MAX_VALUE) {
@@ -184,9 +184,13 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature


    @Override
    @Override
    protected final void addAlgorithmSpecificParametersToBegin(
    protected final void addAlgorithmSpecificParametersToBegin(
            @NonNull KeymasterArguments keymasterArgs) {
            @NonNull List<KeyParameter> parameters) {
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
        parameters.add(KeyStore2ParameterUtils.makeEnum(
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
                KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC
        ));
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
        ));
    }
    }


    @Override
    @Override
+52 −61
Original line number Original line Diff line number Diff line
@@ -16,21 +16,20 @@


package android.security.keystore2;
package android.security.keystore2;


import android.os.IBinder;
import android.security.KeyStore;
import android.security.KeyStoreException;
import android.security.KeyStoreException;
import android.security.keymaster.KeymasterArguments;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
import android.security.keystore.KeyStoreConnectException;
import android.security.keystore.KeyStoreCryptoOperation;
import android.security.keystore.KeyStoreCryptoOperation;
import android.security.keystore.KeymasterUtils;
import android.security.keystore.KeymasterUtils;
import android.system.keystore2.KeyParameter;


import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.Key;
import java.security.ProviderException;
import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.util.ArrayList;
import java.util.List;


import javax.crypto.MacSpi;
import javax.crypto.MacSpi;


@@ -41,6 +40,8 @@ import javax.crypto.MacSpi;
 */
 */
public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {


    private static final String TAG = "AndroidKeyStoreHmacSpi";

    public static class HmacSHA1 extends AndroidKeyStoreHmacSpi {
    public static class HmacSHA1 extends AndroidKeyStoreHmacSpi {
        public HmacSHA1() {
        public HmacSHA1() {
            super(KeymasterDefs.KM_DIGEST_SHA1);
            super(KeymasterDefs.KM_DIGEST_SHA1);
@@ -71,7 +72,6 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
        }
        }
    }
    }


    private final KeyStore mKeyStore = KeyStore.getInstance();
    private final int mKeymasterDigest;
    private final int mKeymasterDigest;
    private final int mMacSizeBits;
    private final int mMacSizeBits;


@@ -80,12 +80,16 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC


    // Fields below are reset when engineDoFinal succeeds.
    // Fields below are reset when engineDoFinal succeeds.
    private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
    private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
    private IBinder mOperationToken;
    private KeyStoreOperation mOperation;
    private long mOperationHandle;
    private long mOperationChallenge;


    protected AndroidKeyStoreHmacSpi(int keymasterDigest) {
    protected AndroidKeyStoreHmacSpi(int keymasterDigest) {
        mKeymasterDigest = keymasterDigest;
        mKeymasterDigest = keymasterDigest;
        mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
        mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
        mOperation = null;
        mOperationChallenge = 0;
        mKey = null;
        mChunkedStreamer = null;
    }
    }


    @Override
    @Override
@@ -127,24 +131,21 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC


    }
    }


    private void abortOperation() {
        KeyStoreCryptoOperationUtils.abortOperation(mOperation);
        mOperation = null;
    }

    private void resetAll() {
    private void resetAll() {
        abortOperation();
        mOperationChallenge = 0;
        mKey = null;
        mKey = null;
        IBinder operationToken = mOperationToken;
        if (operationToken != null) {
            mKeyStore.abort(operationToken);
        }
        mOperationToken = null;
        mOperationHandle = 0;
        mChunkedStreamer = null;
        mChunkedStreamer = null;
    }
    }


    private void resetWhilePreservingInitState() {
    private void resetWhilePreservingInitState() {
        IBinder operationToken = mOperationToken;
        abortOperation();
        if (operationToken != null) {
        mOperationChallenge = 0;
            mKeyStore.abort(operationToken);
        }
        mOperationToken = null;
        mOperationHandle = 0;
        mChunkedStreamer = null;
        mChunkedStreamer = null;
    }
    }


@@ -161,45 +162,40 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
            throw new IllegalStateException("Not initialized");
            throw new IllegalStateException("Not initialized");
        }
        }


        KeymasterArguments keymasterArgs = new KeymasterArguments();
        List<KeyParameter> parameters = new ArrayList<>();
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
        parameters.add(KeyStore2ParameterUtils.makeEnum(
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
                KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC
        keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits);
        ));

        parameters.add(KeyStore2ParameterUtils.makeEnum(
        OperationResult opResult = mKeyStore.begin(
                KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
                mKey.getAlias(),
        ));
                KeymasterDefs.KM_PURPOSE_SIGN,
        parameters.add(KeyStore2ParameterUtils.makeInt(
                true,
                KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits
                keymasterArgs,
        ));
                null, // no additional entropy needed for HMAC because it's deterministic
                mKey.getUid());

        if (opResult == null) {
            throw new KeyStoreConnectException();
        }

        // Store operation token and handle regardless of the error code returned by KeyStore to
        // ensure that the operation gets aborted immediately if the code below throws an exception.
        mOperationToken = opResult.token;
        mOperationHandle = opResult.operationHandle;


        try {
            mOperation = mKey.getSecurityLevel().createOperation(
                    mKey.getKeyIdDescriptor(),
                    parameters
            );
        } catch (KeyStoreException keyStoreException) {
            // If necessary, throw an exception due to KeyStore operation having failed.
            // If necessary, throw an exception due to KeyStore operation having failed.
        InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
            InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyException(
                mKeyStore, mKey, opResult.resultCode);
                    mKey, keyStoreException);
            if (e != null) {
            if (e != null) {
                throw e;
                throw e;
            }
            }

        if (mOperationToken == null) {
            throw new ProviderException("Keystore returned null operation token");
        }
        if (mOperationHandle == 0) {
            throw new ProviderException("Keystore returned invalid operation handle");
        }
        }


        // Now we check if we got an operation challenge. This indicates that user authorization
        // is required. And if we got a challenge we check if the authorization can possibly
        // succeed.
        mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(
                mOperation, mKey);

        mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
        mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
                        mKeyStore, mOperationToken));
                        mOperation));
    }
    }


    @Override
    @Override
@@ -238,9 +234,7 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
        try {
        try {
            result = mChunkedStreamer.doFinal(
            result = mChunkedStreamer.doFinal(
                    null, 0, 0,
                    null, 0, 0,
                    null, // no signature provided -- this invocation will generate one
                    null); // no signature provided -- this invocation will generate one
                    null // no additional entropy needed -- HMAC is deterministic
                    );
        } catch (KeyStoreException e) {
        } catch (KeyStoreException e) {
            throw new ProviderException("Keystore operation failed", e);
            throw new ProviderException("Keystore operation failed", e);
        }
        }
@@ -252,10 +246,7 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
    @Override
    @Override
    public void finalize() throws Throwable {
    public void finalize() throws Throwable {
        try {
        try {
            IBinder operationToken = mOperationToken;
            abortOperation();
            if (operationToken != null) {
                mKeyStore.abort(operationToken);
            }
        } finally {
        } finally {
            super.finalize();
            super.finalize();
        }
        }
@@ -263,6 +254,6 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC


    @Override
    @Override
    public long getOperationHandle() {
    public long getOperationHandle() {
        return mOperationHandle;
        return mOperationChallenge;
    }
    }
}
}
Loading