AndroidKeyStoreSignatureSpiBase.java revision a72b55195c23fc06d1600efe8f6aac85290c7f8f
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
202        OperationResult opResult = mKeyStore.begin(
203                mKey.getAlias(),
204                mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
205                true, // permit aborting this operation if keystore runs out of resources
206                keymasterInputArgs,
207                null // no additional entropy for begin -- only finish might need some
208                );
209        if (opResult == null) {
210            throw new KeyStoreConnectException();
211        }
212
213        // Store operation token and handle regardless of the error code returned by KeyStore to
214        // ensure that the operation gets aborted immediately if the code below throws an exception.
215        mOperationToken = opResult.token;
216        mOperationHandle = opResult.operationHandle;
217
218        // If necessary, throw an exception due to KeyStore operation having failed.
219        InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
220                mKeyStore, mKey, opResult.resultCode);
221        if (e != null) {
222            throw e;
223        }
224
225        if (mOperationToken == null) {
226            throw new ProviderException("Keystore returned null operation token");
227        }
228        if (mOperationHandle == 0) {
229            throw new ProviderException("Keystore returned invalid operation handle");
230        }
231
232        mMessageStreamer = new KeyStoreCryptoOperationChunkedStreamer(
233                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
234                        mKeyStore, opResult.token));
235    }
236
237    @Override
238    public final long getOperationHandle() {
239        return mOperationHandle;
240    }
241
242    @Override
243    protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
244        if (mCachedException != null) {
245            throw new SignatureException(mCachedException);
246        }
247
248        try {
249            ensureKeystoreOperationInitialized();
250        } catch (InvalidKeyException e) {
251            throw new SignatureException(e);
252        }
253
254        if (len == 0) {
255            return;
256        }
257
258        byte[] output;
259        try {
260            output = mMessageStreamer.update(b, off, len);
261        } catch (KeyStoreException e) {
262            throw new SignatureException(e);
263        }
264
265        if (output.length != 0) {
266            throw new ProviderException(
267                    "Update operation unexpectedly produced output: " + output.length + " bytes");
268        }
269    }
270
271    @Override
272    protected final void engineUpdate(byte b) throws SignatureException {
273        engineUpdate(new byte[] {b}, 0, 1);
274    }
275
276    @Override
277    protected final void engineUpdate(ByteBuffer input) {
278        byte[] b;
279        int off;
280        int len = input.remaining();
281        if (input.hasArray()) {
282            b = input.array();
283            off = input.arrayOffset() + input.position();
284            input.position(input.limit());
285        } else {
286            b = new byte[len];
287            off = 0;
288            input.get(b);
289        }
290
291        try {
292            engineUpdate(b, off, len);
293        } catch (SignatureException e) {
294            mCachedException = e;
295        }
296    }
297
298    @Override
299    protected final int engineSign(byte[] out, int outOffset, int outLen)
300            throws SignatureException {
301        return super.engineSign(out, outOffset, outLen);
302    }
303
304    @Override
305    protected final byte[] engineSign() throws SignatureException {
306        if (mCachedException != null) {
307            throw new SignatureException(mCachedException);
308        }
309
310        byte[] signature;
311        try {
312            ensureKeystoreOperationInitialized();
313
314            byte[] additionalEntropy =
315                    KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
316                            appRandom, getAdditionalEntropyAmountForSign());
317            signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0, additionalEntropy);
318        } catch (InvalidKeyException | KeyStoreException e) {
319            throw new SignatureException(e);
320        }
321
322        resetWhilePreservingInitState();
323        return signature;
324    }
325
326    @Override
327    protected final boolean engineVerify(byte[] signature) throws SignatureException {
328        if (mCachedException != null) {
329            throw new SignatureException(mCachedException);
330        }
331
332        boolean result;
333        try {
334            ensureKeystoreOperationInitialized();
335            mMessageStreamer.flush();
336            OperationResult opResult = mKeyStore.finish(mOperationToken, null, signature);
337            if (opResult == null) {
338                throw new KeyStoreConnectException();
339            }
340            switch (opResult.resultCode) {
341                case KeyStore.NO_ERROR:
342                    result = true;
343                    break;
344                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
345                    result = false;
346                    break;
347                default:
348                    throw new SignatureException(
349                            KeyStore.getKeyStoreException(opResult.resultCode));
350            }
351        } catch (InvalidKeyException | KeyStoreException e) {
352            throw new SignatureException(e);
353        }
354
355        resetWhilePreservingInitState();
356        return result;
357    }
358
359    @Override
360    protected final boolean engineVerify(byte[] sigBytes, int offset, int len)
361            throws SignatureException {
362        return engineVerify(ArrayUtils.subarray(sigBytes, offset, len));
363    }
364
365    @Deprecated
366    @Override
367    protected final Object engineGetParameter(String param) throws InvalidParameterException {
368        throw new InvalidParameterException();
369    }
370
371    @Deprecated
372    @Override
373    protected final void engineSetParameter(String param, Object value)
374            throws InvalidParameterException {
375        throw new InvalidParameterException();
376    }
377
378    protected final KeyStore getKeyStore() {
379        return mKeyStore;
380    }
381
382    /**
383     * Returns {@code true} if this signature is initialized for signing, {@code false} if this
384     * signature is initialized for verification.
385     */
386    protected final boolean isSigning() {
387        return mSigning;
388    }
389
390    // The methods below need to be implemented by subclasses.
391
392    /**
393     * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
394     * {@code finish} operation when generating a signature.
395     *
396     * <p>This value should match (or exceed) the amount of Shannon entropy of the produced
397     * signature assuming the key and the message are known. For example, for ECDSA signature this
398     * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this
399     * should be {@code 0}.
400     */
401    protected abstract int getAdditionalEntropyAmountForSign();
402
403    /**
404     * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
405     *
406     * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
407     *        parameters.
408     */
409    protected abstract void addAlgorithmSpecificParametersToBegin(
410            @NonNull KeymasterArguments keymasterArgs);
411}
412