AndroidKeyStoreSignatureSpiBase.java revision 4a0ff7ca984d29bd34b02e54441957cad65e8b53
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.CallSuper;
20import android.annotation.NonNull;
21import android.os.IBinder;
22import android.security.KeyStore;
23import android.security.KeyStoreException;
24import android.security.keymaster.KeymasterArguments;
25import android.security.keymaster.KeymasterDefs;
26import android.security.keymaster.OperationResult;
27
28import libcore.util.EmptyArray;
29
30import java.nio.ByteBuffer;
31import java.security.InvalidKeyException;
32import java.security.InvalidParameterException;
33import java.security.PrivateKey;
34import java.security.ProviderException;
35import java.security.PublicKey;
36import java.security.SecureRandom;
37import java.security.SignatureException;
38import java.security.SignatureSpi;
39
40/**
41 * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers.
42 *
43 * @hide
44 */
45abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
46        implements KeyStoreCryptoOperation {
47    private final KeyStore mKeyStore;
48
49    // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin
50    // and should be preserved after SignatureSpi.engineSign/engineVerify finishes.
51    private boolean mSigning;
52    private AndroidKeyStoreKey mKey;
53
54    /**
55     * Token referencing this operation inside keystore service. It is initialized by
56     * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when
57     * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between.
58     */
59    private IBinder mOperationToken;
60    private long mOperationHandle;
61    private KeyStoreCryptoOperationChunkedStreamer mMessageStreamer;
62
63    /**
64     * Encountered exception which could not be immediately thrown because it was encountered inside
65     * a method that does not throw checked exception. This exception will be thrown from
66     * {@code engineSign} or {@code engineVerify}. Once such an exception is encountered,
67     * {@code engineUpdate} starts ignoring input data.
68     */
69    private Exception mCachedException;
70
71    AndroidKeyStoreSignatureSpiBase() {
72        mKeyStore = KeyStore.getInstance();
73    }
74
75    @Override
76    protected final void engineInitSign(PrivateKey key) throws InvalidKeyException {
77        engineInitSign(key, null);
78    }
79
80    @Override
81    protected final void engineInitSign(PrivateKey privateKey, SecureRandom random)
82            throws InvalidKeyException {
83        resetAll();
84
85        boolean success = false;
86        try {
87            if (privateKey == null) {
88                throw new InvalidKeyException("Unsupported key: null");
89            }
90            AndroidKeyStoreKey keystoreKey;
91            if (privateKey instanceof AndroidKeyStorePrivateKey) {
92                keystoreKey = (AndroidKeyStoreKey) privateKey;
93            } else {
94                throw new InvalidKeyException("Unsupported private key type: " + privateKey);
95            }
96            mSigning = true;
97            initKey(keystoreKey);
98            appRandom = random;
99            ensureKeystoreOperationInitialized();
100            success = true;
101        } finally {
102            if (!success) {
103                resetAll();
104            }
105        }
106    }
107
108    @Override
109    protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
110        resetAll();
111
112        boolean success = false;
113        try {
114            if (publicKey == null) {
115                throw new InvalidKeyException("Unsupported key: null");
116            }
117            AndroidKeyStoreKey keystoreKey;
118            if (publicKey instanceof AndroidKeyStorePublicKey) {
119                keystoreKey = (AndroidKeyStorePublicKey) publicKey;
120            } else {
121                throw new InvalidKeyException("Unsupported public key type: " + publicKey);
122            }
123            mSigning = false;
124            initKey(keystoreKey);
125            appRandom = null;
126            ensureKeystoreOperationInitialized();
127            success = true;
128        } finally {
129            if (!success) {
130                resetAll();
131            }
132        }
133    }
134
135    /**
136     * Configures this signature instance to use the provided key.
137     *
138     * @throws InvalidKeyException if the {@code key} is not suitable.
139     */
140    @CallSuper
141    protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
142        mKey = key;
143    }
144
145    /**
146     * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
147     * cipher instance.
148     *
149     * <p>Subclasses storing additional state should override this method, reset the additional
150     * state, and then chain to superclass.
151     */
152    @CallSuper
153    protected void resetAll() {
154        IBinder operationToken = mOperationToken;
155        if (operationToken != null) {
156            mOperationToken = null;
157            mKeyStore.abort(operationToken);
158        }
159        mSigning = false;
160        mKey = null;
161        appRandom = null;
162        mOperationToken = null;
163        mOperationHandle = 0;
164        mMessageStreamer = null;
165        mCachedException = null;
166    }
167
168    /**
169     * Resets this cipher while preserving the initialized state. This must be equivalent to
170     * rolling back the cipher's state to just after the most recent {@code engineInit} completed
171     * successfully.
172     *
173     * <p>Subclasses storing additional post-init state should override this method, reset the
174     * additional state, and then chain to superclass.
175     */
176    @CallSuper
177    protected void resetWhilePreservingInitState() {
178        IBinder operationToken = mOperationToken;
179        if (operationToken != null) {
180            mOperationToken = null;
181            mKeyStore.abort(operationToken);
182        }
183        mOperationHandle = 0;
184        mMessageStreamer = null;
185        mCachedException = null;
186    }
187
188    private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
189        if (mMessageStreamer != null) {
190            return;
191        }
192        if (mCachedException != null) {
193            return;
194        }
195        if (mKey == null) {
196            throw new IllegalStateException("Not initialized");
197        }
198
199        KeymasterArguments keymasterInputArgs = new KeymasterArguments();
200        addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
201        byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
202                appRandom, getAdditionalEntropyAmountForBegin());
203
204        OperationResult opResult = mKeyStore.begin(
205                mKey.getAlias(),
206                mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
207                true, // permit aborting this operation if keystore runs out of resources
208                keymasterInputArgs,
209                additionalEntropy);
210        if (opResult == null) {
211            throw new KeyStoreConnectException();
212        }
213
214        // Store operation token and handle regardless of the error code returned by KeyStore to
215        // ensure that the operation gets aborted immediately if the code below throws an exception.
216        mOperationToken = opResult.token;
217        mOperationHandle = opResult.operationHandle;
218
219        // If necessary, throw an exception due to KeyStore operation having failed.
220        InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
221                mKeyStore, mKey, opResult.resultCode);
222        if (e != null) {
223            throw e;
224        }
225
226        if (mOperationToken == null) {
227            throw new ProviderException("Keystore returned null operation token");
228        }
229        if (mOperationHandle == 0) {
230            throw new ProviderException("Keystore returned invalid operation handle");
231        }
232
233        mMessageStreamer = new KeyStoreCryptoOperationChunkedStreamer(
234                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
235                        mKeyStore, opResult.token));
236    }
237
238    @Override
239    public final long getOperationHandle() {
240        return mOperationHandle;
241    }
242
243    @Override
244    protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
245        if (mCachedException != null) {
246            throw new SignatureException(mCachedException);
247        }
248
249        try {
250            ensureKeystoreOperationInitialized();
251        } catch (InvalidKeyException e) {
252            throw new SignatureException(e);
253        }
254
255        if (len == 0) {
256            return;
257        }
258
259        byte[] output;
260        try {
261            output = mMessageStreamer.update(b, off, len);
262        } catch (KeyStoreException e) {
263            throw new SignatureException(e);
264        }
265
266        if (output.length != 0) {
267            throw new ProviderException(
268                    "Update operation unexpectedly produced output: " + output.length + " bytes");
269        }
270    }
271
272    @Override
273    protected final void engineUpdate(byte b) throws SignatureException {
274        engineUpdate(new byte[] {b}, 0, 1);
275    }
276
277    @Override
278    protected final void engineUpdate(ByteBuffer input) {
279        byte[] b;
280        int off;
281        int len = input.remaining();
282        if (input.hasArray()) {
283            b = input.array();
284            off = input.arrayOffset() + input.position();
285            input.position(input.limit());
286        } else {
287            b = new byte[len];
288            off = 0;
289            input.get(b);
290        }
291
292        try {
293            engineUpdate(b, off, len);
294        } catch (SignatureException e) {
295            mCachedException = e;
296        }
297    }
298
299    @Override
300    protected final int engineSign(byte[] out, int outOffset, int outLen)
301            throws SignatureException {
302        return super.engineSign(out, outOffset, outLen);
303    }
304
305    @Override
306    protected final byte[] engineSign() throws SignatureException {
307        if (mCachedException != null) {
308            throw new SignatureException(mCachedException);
309        }
310
311        byte[] signature;
312        try {
313            ensureKeystoreOperationInitialized();
314            signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0);
315        } catch (InvalidKeyException | KeyStoreException e) {
316            throw new SignatureException(e);
317        }
318
319        resetWhilePreservingInitState();
320        return signature;
321    }
322
323    @Override
324    protected final boolean engineVerify(byte[] signature) throws SignatureException {
325        if (mCachedException != null) {
326            throw new SignatureException(mCachedException);
327        }
328
329        boolean result;
330        try {
331            ensureKeystoreOperationInitialized();
332            mMessageStreamer.flush();
333            OperationResult opResult = mKeyStore.finish(mOperationToken, null, signature);
334            if (opResult == null) {
335                throw new KeyStoreConnectException();
336            }
337            switch (opResult.resultCode) {
338                case KeyStore.NO_ERROR:
339                    result = true;
340                    break;
341                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
342                    result = false;
343                    break;
344                default:
345                    throw new SignatureException(
346                            KeyStore.getKeyStoreException(opResult.resultCode));
347            }
348        } catch (InvalidKeyException | KeyStoreException e) {
349            throw new SignatureException(e);
350        }
351
352        resetWhilePreservingInitState();
353        return result;
354    }
355
356    @Override
357    protected final boolean engineVerify(byte[] sigBytes, int offset, int len)
358            throws SignatureException {
359        return engineVerify(ArrayUtils.subarray(sigBytes, offset, len));
360    }
361
362    @Deprecated
363    @Override
364    protected final Object engineGetParameter(String param) throws InvalidParameterException {
365        throw new InvalidParameterException();
366    }
367
368    @Deprecated
369    @Override
370    protected final void engineSetParameter(String param, Object value)
371            throws InvalidParameterException {
372        throw new InvalidParameterException();
373    }
374
375    protected final KeyStore getKeyStore() {
376        return mKeyStore;
377    }
378
379    /**
380     * Returns {@code true} if this signature is initialized for signing, {@code false} if this
381     * signature is initialized for verification.
382     */
383    protected final boolean isSigning() {
384        return mSigning;
385    }
386
387    // The methods below need to be implemented by subclasses.
388
389    /**
390     * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
391     * {@code begin} operation.
392     *
393     * <p>For signature verification, this should be {@code 0} because verification should not be
394     * consuming any entropy. For signature generation, this value should match (or exceed) the
395     * amount of Shannon entropy of the produced signature assuming the key and the message are
396     * known. For example, for ECDSA signature this should be the size of {@code R}, whereas for the
397     * RSA signature with PKCS#1 padding this should be {@code 0}.
398     */
399    protected abstract int getAdditionalEntropyAmountForBegin();
400
401    /**
402     * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
403     *
404     * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
405     *        parameters.
406     */
407    protected abstract void addAlgorithmSpecificParametersToBegin(
408            @NonNull KeymasterArguments keymasterArgs);
409}
410