AndroidKeyStoreECDSASignatureSpi.java revision 3876b1be27e3aefde9a72eb2e4f856e94fc5f946
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.security.keystore;
18
19import android.annotation.NonNull;
20import android.os.IBinder;
21import android.security.KeyStore;
22import android.security.KeyStoreException;
23import android.security.keymaster.KeyCharacteristics;
24import android.security.keymaster.KeymasterArguments;
25import android.security.keymaster.KeymasterDefs;
26
27import libcore.util.EmptyArray;
28
29import java.io.ByteArrayOutputStream;
30import java.security.InvalidKeyException;
31import java.security.SignatureSpi;
32
33/**
34 * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
35 *
36 * @hide
37 */
38abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
39
40    public final static class NONE extends AndroidKeyStoreECDSASignatureSpi {
41        public NONE() {
42            super(KeymasterDefs.KM_DIGEST_NONE);
43        }
44
45        @Override
46        protected KeyStoreCryptoOperationStreamer createMainDataStreamer(KeyStore keyStore,
47                IBinder operationToken) {
48            return new TruncateToFieldSizeMessageStreamer(
49                    super.createMainDataStreamer(keyStore, operationToken),
50                    getGroupSizeBits());
51        }
52
53        /**
54         * Streamer which buffers all input, then truncates it to field size, and then sends it into
55         * KeyStore via the provided delegate streamer.
56         */
57        private static class TruncateToFieldSizeMessageStreamer
58                implements KeyStoreCryptoOperationStreamer {
59
60            private final KeyStoreCryptoOperationStreamer mDelegate;
61            private final int mGroupSizeBits;
62            private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
63            private long mConsumedInputSizeBytes;
64
65            private TruncateToFieldSizeMessageStreamer(
66                    KeyStoreCryptoOperationStreamer delegate,
67                    int groupSizeBits) {
68                mDelegate = delegate;
69                mGroupSizeBits = groupSizeBits;
70            }
71
72            @Override
73            public byte[] update(byte[] input, int inputOffset, int inputLength)
74                    throws KeyStoreException {
75                if (inputLength > 0) {
76                    mInputBuffer.write(input, inputOffset, inputLength);
77                    mConsumedInputSizeBytes += inputLength;
78                }
79                return EmptyArray.BYTE;
80            }
81
82            @Override
83            public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature,
84                    byte[] additionalEntropy) throws KeyStoreException {
85                if (inputLength > 0) {
86                    mConsumedInputSizeBytes += inputLength;
87                    mInputBuffer.write(input, inputOffset, inputLength);
88                }
89
90                byte[] bufferedInput = mInputBuffer.toByteArray();
91                mInputBuffer.reset();
92                // Truncate input at field size (bytes)
93                return mDelegate.doFinal(bufferedInput,
94                        0,
95                        Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)),
96                        signature, additionalEntropy);
97            }
98
99            @Override
100            public long getConsumedInputSizeBytes() {
101                return mConsumedInputSizeBytes;
102            }
103
104            @Override
105            public long getProducedOutputSizeBytes() {
106                return mDelegate.getProducedOutputSizeBytes();
107            }
108        }
109    }
110
111    public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi {
112        public SHA1() {
113            super(KeymasterDefs.KM_DIGEST_SHA1);
114        }
115    }
116
117    public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi {
118        public SHA224() {
119            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
120        }
121    }
122
123    public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi {
124        public SHA256() {
125            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
126        }
127    }
128
129    public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi {
130        public SHA384() {
131            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
132        }
133    }
134
135    public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi {
136        public SHA512() {
137            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
138        }
139    }
140
141    private final int mKeymasterDigest;
142
143    private int mGroupSizeBits = -1;
144
145    AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) {
146        mKeymasterDigest = keymasterDigest;
147    }
148
149    @Override
150    protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
151        if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) {
152            throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
153                    + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
154        }
155
156        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
157        int errorCode = getKeyStore().getKeyCharacteristics(
158                key.getAlias(), null, null, key.getUid(), keyCharacteristics);
159        if (errorCode != KeyStore.NO_ERROR) {
160            throw getKeyStore().getInvalidKeyException(key.getAlias(), key.getUid(), errorCode);
161        }
162        long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
163        if (keySizeBits == -1) {
164            throw new InvalidKeyException("Size of key not known");
165        } else if (keySizeBits > Integer.MAX_VALUE) {
166            throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
167        }
168        mGroupSizeBits = (int) keySizeBits;
169
170        super.initKey(key);
171    }
172
173    @Override
174    protected final void resetAll() {
175        mGroupSizeBits = -1;
176        super.resetAll();
177    }
178
179    @Override
180    protected final void resetWhilePreservingInitState() {
181        super.resetWhilePreservingInitState();
182    }
183
184    @Override
185    protected final void addAlgorithmSpecificParametersToBegin(
186            @NonNull KeymasterArguments keymasterArgs) {
187        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
188        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
189    }
190
191    @Override
192    protected final int getAdditionalEntropyAmountForSign() {
193        return (mGroupSizeBits + 7) / 8;
194    }
195
196    protected final int getGroupSizeBits() {
197        if (mGroupSizeBits == -1) {
198            throw new IllegalStateException("Not initialized");
199        }
200        return mGroupSizeBits;
201    }
202}
203