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

Commit d23dc502 authored by Alex Klyubin's avatar Alex Klyubin
Browse files

Make NONEwithECDSA truncate input when necessary.

Keymaster's implementation of ECDSA with digest NONE rejects input
longer than group size in bytes. RI's NONEwithECDSA accepts inputs
of arbitrary length by truncating them to the above size. This CL
makes Android Keystore's NONEwithECDSA do the truncation to keep
the JCA and Keymaster happy.

The change is inside AndroidKeyStoreECDSASignatureSpi$NONE. All other
small modifications are for supporting that change by making it
possible for AndroidKeyStoreSignatureSpiBase to pass in the signature
being verified into KeyStoreCryptoOperationStreamer. This in turn is
needed to make it possible for NONEwithECDSA implementation to provide
a wrapper streamer which truncates input.

Bug: 22030217
Change-Id: I26064f6df37ef8c631d70a36a356aa0b76a9ad29
parent cede20a7
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -363,8 +363,9 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC

        @Override
        public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
                byte[] additionalEntropy) throws KeyStoreException {
            byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, additionalEntropy);
                byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
            byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature,
                    additionalEntropy);
            if (output != null) {
                try {
                    mBufferedOutput.write(output);
@@ -425,7 +426,7 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC
        }

        @Override
        public OperationResult finish(byte[] additionalEntropy) {
        public OperationResult finish(byte[] signature, byte[] additionalEntropy) {
            if ((additionalEntropy != null) && (additionalEntropy.length > 0)) {
                throw new ProviderException("AAD stream does not support additional entropy");
            }
+5 −1
Original line number Diff line number Diff line
@@ -353,6 +353,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
            try {
                output = mAdditionalAuthenticationDataStreamer.doFinal(
                        EmptyArray.BYTE, 0, 0,
                        null, // no signature
                        null // no additional entropy needed flushing AAD
                        );
            } finally {
@@ -469,7 +470,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
            byte[] additionalEntropy =
                    KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
                            mRng, getAdditionalEntropyAmountForFinish());
            output = mMainDataStreamer.doFinal(input, inputOffset, inputLen, additionalEntropy);
            output = mMainDataStreamer.doFinal(
                    input, inputOffset, inputLen,
                    null, // no signature involved
                    additionalEntropy);
        } catch (KeyStoreException e) {
            switch (e.getErrorCode()) {
                case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
+83 −6
Original line number Diff line number Diff line
@@ -17,11 +17,16 @@
package android.security.keystore;

import android.annotation.NonNull;
import android.os.IBinder;
import android.security.KeyStore;
import android.security.KeyStoreException;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;

import libcore.util.EmptyArray;

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

@@ -36,6 +41,71 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
        public NONE() {
            super(KeymasterDefs.KM_DIGEST_NONE);
        }

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

        /**
         * Streamer which buffers all input, then truncates it to field size, and then sends it into
         * KeyStore via the provided delegate streamer.
         */
        private static class TruncateToFieldSizeMessageStreamer
                implements KeyStoreCryptoOperationStreamer {

            private final KeyStoreCryptoOperationStreamer mDelegate;
            private final int mGroupSizeBits;
            private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
            private long mConsumedInputSizeBytes;

            private TruncateToFieldSizeMessageStreamer(
                    KeyStoreCryptoOperationStreamer delegate,
                    int groupSizeBits) {
                mDelegate = delegate;
                mGroupSizeBits = groupSizeBits;
            }

            @Override
            public byte[] update(byte[] input, int inputOffset, int inputLength)
                    throws KeyStoreException {
                if (inputLength > 0) {
                    mInputBuffer.write(input, inputOffset, inputLength);
                    mConsumedInputSizeBytes += inputLength;
                }
                return EmptyArray.BYTE;
            }

            @Override
            public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature,
                    byte[] additionalEntropy) throws KeyStoreException {
                if (inputLength > 0) {
                    mConsumedInputSizeBytes += inputLength;
                    mInputBuffer.write(input, inputOffset, inputLength);
                }

                byte[] bufferedInput = mInputBuffer.toByteArray();
                mInputBuffer.reset();
                // Truncate input at field size (bytes)
                return mDelegate.doFinal(bufferedInput,
                        0,
                        Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)),
                        signature, additionalEntropy);
            }

            @Override
            public long getConsumedInputSizeBytes() {
                return mConsumedInputSizeBytes;
            }

            @Override
            public long getProducedOutputSizeBytes() {
                return mDelegate.getProducedOutputSizeBytes();
            }
        }
    }

    public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi {
@@ -70,7 +140,7 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature

    private final int mKeymasterDigest;

    private int mGroupSizeBytes = -1;
    private int mGroupSizeBits = -1;

    AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) {
        mKeymasterDigest = keymasterDigest;
@@ -95,14 +165,14 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
        } else if (keySizeBits > Integer.MAX_VALUE) {
            throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
        }
        mGroupSizeBytes = (int) ((keySizeBits + 7) / 8);
        mGroupSizeBits = (int) keySizeBits;

        super.initKey(key);
    }

    @Override
    protected final void resetAll() {
        mGroupSizeBytes = -1;
        mGroupSizeBits = -1;
        super.resetAll();
    }

@@ -112,14 +182,21 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
    }

    @Override
    protected void addAlgorithmSpecificParametersToBegin(
    protected final void addAlgorithmSpecificParametersToBegin(
            @NonNull KeymasterArguments keymasterArgs) {
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
    }

    @Override
    protected int getAdditionalEntropyAmountForSign() {
        return mGroupSizeBytes;
    protected final int getAdditionalEntropyAmountForSign() {
        return (mGroupSizeBits + 7) / 8;
    }

    protected final int getGroupSizeBits() {
        if (mGroupSizeBits == -1) {
            throw new IllegalStateException("Not initialized");
        }
        return mGroupSizeBits;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -234,6 +234,7 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
        try {
            result = mChunkedStreamer.doFinal(
                    null, 0, 0,
                    null, // no signature provided -- this invocation will generate one
                    null // no additional entropy needed -- HMAC is deterministic
                    );
        } catch (KeyStoreException e) {
+3 −3
Original line number Diff line number Diff line
@@ -150,8 +150,7 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase

            @Override
            public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
                    byte[] additionalEntropy)
                    throws KeyStoreException {
                    byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
                if (inputLength > 0) {
                    mConsumedInputSizeBytes += inputLength;
                    mInputBuffer.write(input, inputOffset, inputLength);
@@ -174,7 +173,8 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
                            "Message size (" + bufferedInput.length + " bytes) must be smaller than"
                            + " modulus (" + mModulusSizeBytes + " bytes)");
                }
                return mDelegate.doFinal(paddedInput, 0, paddedInput.length, additionalEntropy);
                return mDelegate.doFinal(paddedInput, 0, paddedInput.length, signature,
                        additionalEntropy);
            }

            @Override
Loading