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 
17 package android.security.keystore2;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.hardware.security.keymint.KeyParameter;
23 import android.security.KeyStoreException;
24 import android.security.KeyStoreOperation;
25 import android.security.keymaster.KeymasterDefs;
26 import android.security.keystore.KeyStoreCryptoOperation;
27 import android.system.keystore2.Authorization;
28 
29 import libcore.util.EmptyArray;
30 
31 import java.nio.BufferOverflowException;
32 import java.nio.ByteBuffer;
33 import java.security.AlgorithmParameters;
34 import java.security.GeneralSecurityException;
35 import java.security.InvalidAlgorithmParameterException;
36 import java.security.InvalidKeyException;
37 import java.security.InvalidParameterException;
38 import java.security.Key;
39 import java.security.KeyFactory;
40 import java.security.NoSuchAlgorithmException;
41 import java.security.PrivateKey;
42 import java.security.ProviderException;
43 import java.security.PublicKey;
44 import java.security.SecureRandom;
45 import java.security.spec.AlgorithmParameterSpec;
46 import java.security.spec.InvalidKeySpecException;
47 import java.security.spec.MGF1ParameterSpec;
48 import java.security.spec.PKCS8EncodedKeySpec;
49 import java.security.spec.X509EncodedKeySpec;
50 import java.util.ArrayList;
51 import java.util.List;
52 
53 import javax.crypto.AEADBadTagException;
54 import javax.crypto.BadPaddingException;
55 import javax.crypto.Cipher;
56 import javax.crypto.CipherSpi;
57 import javax.crypto.IllegalBlockSizeException;
58 import javax.crypto.NoSuchPaddingException;
59 import javax.crypto.SecretKey;
60 import javax.crypto.SecretKeyFactory;
61 import javax.crypto.ShortBufferException;
62 import javax.crypto.spec.OAEPParameterSpec;
63 import javax.crypto.spec.PSource;
64 import javax.crypto.spec.SecretKeySpec;
65 
66 /**
67  * Base class for {@link CipherSpi} implementations of Android KeyStore backed ciphers.
68  *
69  * @hide
70  */
71 abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation {
72     private static final String TAG = "AndroidKeyStoreCipherSpiBase";
73     public static final String DEFAULT_MGF1_DIGEST = "SHA-1";
74 
75     // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
76     // doFinal finishes.
77     private boolean mEncrypting;
78     private int mKeymasterPurposeOverride = -1;
79     private AndroidKeyStoreKey mKey;
80     private SecureRandom mRng;
81 
82     /**
83      * Object representing this operation inside keystore service. It is initialized
84      * by {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some
85      * error conditions in between.
86      */
87     private KeyStoreOperation mOperation;
88     /**
89      * The operation challenge is required when an operation needs user authorization.
90      * The challenge is subjected to an authenticator, e.g., Gatekeeper or a biometric
91      * authenticator, and included in the authentication token minted by this authenticator.
92      * It may be null, if the operation does not require authorization.
93      */
94     private long mOperationChallenge;
95     private KeyStoreCryptoOperationStreamer mMainDataStreamer;
96     private KeyStoreCryptoOperationStreamer mAdditionalAuthenticationDataStreamer;
97     private boolean mAdditionalAuthenticationDataStreamerClosed;
98 
99     /**
100      * Encountered exception which could not be immediately thrown because it was encountered inside
101      * a method that does not throw checked exception. This exception will be thrown from
102      * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and
103      * {@code engineDoFinal} start ignoring input data.
104      */
105     private Exception mCachedException;
106 
107     private Cipher mCipher;
108 
AndroidKeyStoreCipherSpiBase()109     AndroidKeyStoreCipherSpiBase() {
110         mOperation = null;
111         mEncrypting = false;
112         mKeymasterPurposeOverride = -1;
113         mKey = null;
114         mRng = null;
115         mOperationChallenge = 0;
116         mMainDataStreamer = null;
117         mAdditionalAuthenticationDataStreamer = null;
118         mAdditionalAuthenticationDataStreamerClosed = false;
119         mCachedException = null;
120         mCipher = null;
121     }
122 
getKeyCharacteristics(Key key)123     private Authorization[] getKeyCharacteristics(Key key) {
124         if (!(key instanceof AndroidKeyStoreKey)) {
125             return new Authorization[] {};
126         }
127 
128         return ((AndroidKeyStoreKey) key).getAuthorizations();
129     }
130 
131     @Override
engineInit(int opmode, Key key, SecureRandom random)132     protected final void engineInit(int opmode, Key key, SecureRandom random)
133             throws InvalidKeyException {
134         resetAll();
135 
136         // Public key operations get diverted to the default provider.
137         if (!(key instanceof AndroidKeyStorePrivateKey)
138                 && (key instanceof PrivateKey || key instanceof PublicKey)) {
139             try {
140                 mCipher = Cipher.getInstance(getTransform());
141                 String transform = getTransform();
142 
143                 if ("RSA/ECB/OAEPWithSHA-224AndMGF1Padding".equals(transform)) {
144                     OAEPParameterSpec spec =
145                             new OAEPParameterSpec("SHA-224", "MGF1",
146                                     new MGF1ParameterSpec(DEFAULT_MGF1_DIGEST),
147                                     PSource.PSpecified.DEFAULT);
148                     mCipher.init(opmode, key, spec, random);
149                 } else if ("RSA/ECB/OAEPWithSHA-256AndMGF1Padding".equals(transform)) {
150                     OAEPParameterSpec spec =
151                             new OAEPParameterSpec("SHA-256", "MGF1",
152                                     new MGF1ParameterSpec(DEFAULT_MGF1_DIGEST),
153                                     PSource.PSpecified.DEFAULT);
154                     mCipher.init(opmode, key, spec, random);
155 
156                 } else if ("RSA/ECB/OAEPWithSHA-384AndMGF1Padding".equals(transform)) {
157                     OAEPParameterSpec spec =
158                             new OAEPParameterSpec("SHA-384", "MGF1",
159                                     new MGF1ParameterSpec(DEFAULT_MGF1_DIGEST),
160                                     PSource.PSpecified.DEFAULT);
161                     mCipher.init(opmode, key, spec, random);
162 
163                 } else if ("RSA/ECB/OAEPWithSHA-512AndMGF1Padding".equals(transform)) {
164                     OAEPParameterSpec spec =
165                             new OAEPParameterSpec("SHA-512", "MGF1",
166                                     new MGF1ParameterSpec(DEFAULT_MGF1_DIGEST),
167                                     PSource.PSpecified.DEFAULT);
168                     mCipher.init(opmode, key, spec, random);
169                 } else {
170                     mCipher.init(opmode, key, random);
171                 }
172                 return;
173             } catch (NoSuchAlgorithmException
174                     | NoSuchPaddingException
175                     | InvalidAlgorithmParameterException e) {
176                 throw new InvalidKeyException(e);
177             }
178         }
179 
180         boolean success = false;
181         try {
182             init(opmode, key, random);
183             initAlgorithmSpecificParameters();
184             try {
185                 ensureKeystoreOperationInitialized(getKeyCharacteristics(key));
186             } catch (InvalidAlgorithmParameterException e) {
187                 throw new InvalidKeyException(e);
188             }
189             success = true;
190         } finally {
191             if (!success) {
192                 resetAll();
193             }
194         }
195     }
196 
197     @Override
engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)198     protected final void engineInit(int opmode, Key key, AlgorithmParameters params,
199             SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
200         resetAll();
201 
202         // Public key operations get diverted to the default provider.
203         if (!(key instanceof AndroidKeyStorePrivateKey)
204                 && (key instanceof PrivateKey || key instanceof PublicKey)) {
205             try {
206                 mCipher = Cipher.getInstance(getTransform());
207                 mCipher.init(opmode, key, params, random);
208                 return;
209             } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
210                 throw new InvalidKeyException(e);
211             }
212         }
213 
214         boolean success = false;
215         try {
216             init(opmode, key, random);
217             initAlgorithmSpecificParameters(params);
218             ensureKeystoreOperationInitialized(getKeyCharacteristics(key));
219             success = true;
220         } finally {
221             if (!success) {
222                 resetAll();
223             }
224         }
225     }
226 
227     @Override
engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)228     protected final void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
229             SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
230         resetAll();
231 
232         // Public key operations get diverted to the default provider.
233         if (!(key instanceof AndroidKeyStorePrivateKey)
234                 && (key instanceof PrivateKey || key instanceof PublicKey)) {
235             try {
236                 mCipher = Cipher.getInstance(getTransform());
237                 mCipher.init(opmode, key, params, random);
238                 return;
239             } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
240                 throw new InvalidKeyException(e);
241             }
242         }
243 
244         boolean success = false;
245         try {
246             init(opmode, key, random);
247             initAlgorithmSpecificParameters(params);
248             ensureKeystoreOperationInitialized(getKeyCharacteristics(key));
249             success = true;
250         } finally {
251             if (!success) {
252                 resetAll();
253             }
254         }
255     }
256 
init(int opmode, Key key, SecureRandom random)257     private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
258         switch (opmode) {
259             case Cipher.ENCRYPT_MODE:
260             case Cipher.WRAP_MODE:
261                 mEncrypting = true;
262                 break;
263             case Cipher.DECRYPT_MODE:
264             case Cipher.UNWRAP_MODE:
265                 mEncrypting = false;
266                 break;
267             default:
268                 throw new InvalidParameterException("Unsupported opmode: " + opmode);
269         }
270         initKey(opmode, key);
271         if (mKey == null) {
272             throw new ProviderException("initKey did not initialize the key");
273         }
274         mRng = random;
275     }
276 
abortOperation()277     private void abortOperation() {
278         KeyStoreCryptoOperationUtils.abortOperation(mOperation);
279         mOperation = null;
280     }
281 
282     /**
283      * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
284      * cipher instance.
285      *
286      * <p>Subclasses storing additional state should override this method, reset the additional
287      * state, and then chain to superclass.
288      */
289     @CallSuper
resetAll()290     protected void resetAll() {
291         abortOperation();
292         mEncrypting = false;
293         mKeymasterPurposeOverride = -1;
294         mKey = null;
295         mRng = null;
296         mOperationChallenge = 0;
297         mMainDataStreamer = null;
298         mAdditionalAuthenticationDataStreamer = null;
299         mAdditionalAuthenticationDataStreamerClosed = false;
300         mCachedException = null;
301         mCipher = null;
302     }
303 
304     /**
305      * Resets this cipher while preserving the initialized state. This must be equivalent to
306      * rolling back the cipher's state to just after the most recent {@code engineInit} completed
307      * successfully.
308      *
309      * <p>Subclasses storing additional post-init state should override this method, reset the
310      * additional state, and then chain to superclass.
311      */
312     @CallSuper
resetWhilePreservingInitState()313     protected void resetWhilePreservingInitState() {
314         abortOperation();
315         mOperationChallenge = 0;
316         mMainDataStreamer = null;
317         mAdditionalAuthenticationDataStreamer = null;
318         mAdditionalAuthenticationDataStreamerClosed = false;
319         mCachedException = null;
320     }
321 
ensureKeystoreOperationInitialized(Authorization[] keyCharacteristics)322     private void ensureKeystoreOperationInitialized(Authorization[] keyCharacteristics)
323             throws InvalidKeyException,
324             InvalidAlgorithmParameterException {
325         if (mMainDataStreamer != null) {
326             return;
327         }
328         if (mCachedException != null) {
329             return;
330         }
331         if (mKey == null) {
332             throw new IllegalStateException("Not initialized");
333         }
334 
335         List<KeyParameter> parameters = new ArrayList<>();
336         addAlgorithmSpecificParametersToBegin(parameters, keyCharacteristics);
337 
338         int purpose;
339         if (mKeymasterPurposeOverride != -1) {
340             purpose = mKeymasterPurposeOverride;
341         } else {
342             purpose = mEncrypting
343                     ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT;
344         }
345 
346         parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
347 
348         try {
349             mOperation = mKey.getSecurityLevel().createOperation(
350                     mKey.getKeyIdDescriptor(),
351                     parameters
352             );
353         } catch (KeyStoreException keyStoreException) {
354             GeneralSecurityException e = KeyStoreCryptoOperationUtils.getExceptionForCipherInit(
355                     mKey, keyStoreException);
356             if (e != null) {
357                 if (e instanceof InvalidKeyException) {
358                     throw (InvalidKeyException) e;
359                 } else if (e instanceof InvalidAlgorithmParameterException) {
360                     throw (InvalidAlgorithmParameterException) e;
361                 } else {
362                     throw new ProviderException("Unexpected exception type", e);
363                 }
364             }
365         }
366 
367         // Now we check if we got an operation challenge. This indicates that user authorization
368         // is required. And if we got a challenge we check if the authorization can possibly
369         // succeed.
370         mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(
371                 mOperation, mKey);
372 
373         loadAlgorithmSpecificParametersFromBeginResult(mOperation.getParameters());
374         mMainDataStreamer = createMainDataStreamer(mOperation);
375         mAdditionalAuthenticationDataStreamer =
376                 createAdditionalAuthenticationDataStreamer(mOperation);
377         mAdditionalAuthenticationDataStreamerClosed = false;
378     }
379 
380     /**
381      * Creates a streamer which sends plaintext/ciphertext into the provided KeyStore and receives
382      * the corresponding ciphertext/plaintext from the KeyStore.
383      *
384      * <p>This implementation returns a working streamer.
385      */
386     @NonNull
createMainDataStreamer( KeyStoreOperation operation)387     protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
388             KeyStoreOperation operation) {
389         return new KeyStoreCryptoOperationChunkedStreamer(
390                 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
391                         operation), 0);
392     }
393 
394     /**
395      * Creates a streamer which sends Additional Authentication Data (AAD) into the KeyStore.
396      *
397      * <p>This implementation returns {@code null}.
398      *
399      * @return stream or {@code null} if AAD is not supported by this cipher.
400      */
401     @Nullable
createAdditionalAuthenticationDataStreamer( @uppressWarningsR) KeyStoreOperation operation)402     protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
403             @SuppressWarnings("unused") KeyStoreOperation operation) {
404         return null;
405     }
406 
407     @Override
engineUpdate(byte[] input, int inputOffset, int inputLen)408     protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
409         if (mCipher != null) {
410             return mCipher.update(input, inputOffset, inputLen);
411         }
412 
413         if (mCachedException != null) {
414             return null;
415         }
416         try {
417             ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey));
418         } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
419             mCachedException = e;
420             return null;
421         }
422 
423         if (inputLen == 0) {
424             return null;
425         }
426 
427         byte[] output;
428         try {
429             flushAAD();
430             output = mMainDataStreamer.update(input, inputOffset, inputLen);
431         } catch (KeyStoreException e) {
432             mCachedException = e;
433             return null;
434         }
435 
436         if (output.length == 0) {
437             return null;
438         }
439 
440         return output;
441     }
442 
flushAAD()443     private void flushAAD() throws KeyStoreException {
444         if ((mAdditionalAuthenticationDataStreamer != null)
445                 && (!mAdditionalAuthenticationDataStreamerClosed)) {
446             byte[] output;
447             try {
448                 output = mAdditionalAuthenticationDataStreamer.doFinal(
449                         EmptyArray.BYTE, 0, 0,
450                         null); // no signature
451             } finally {
452                 mAdditionalAuthenticationDataStreamerClosed = true;
453             }
454             if ((output != null) && (output.length > 0)) {
455                 throw new ProviderException(
456                         "AAD update unexpectedly returned data: " + output.length + " bytes");
457             }
458         }
459     }
460 
461     @Override
engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)462     protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
463             int outputOffset) throws ShortBufferException {
464         if (mCipher != null) {
465             return mCipher.update(input, inputOffset, inputLen, output);
466         }
467         byte[] outputCopy = engineUpdate(input, inputOffset, inputLen);
468         if (outputCopy == null) {
469             return 0;
470         }
471         int outputAvailable = output.length - outputOffset;
472         if (outputCopy.length > outputAvailable) {
473             throw new ShortBufferException("Output buffer too short. Produced: "
474                     + outputCopy.length + ", available: " + outputAvailable);
475         }
476         System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
477         return outputCopy.length;
478     }
479 
480     @Override
engineUpdate(ByteBuffer input, ByteBuffer output)481     protected final int engineUpdate(ByteBuffer input, ByteBuffer output)
482             throws ShortBufferException {
483         if (mCipher != null) {
484             return mCipher.update(input, output);
485         }
486 
487         if (input == null) {
488             throw new NullPointerException("input == null");
489         }
490         if (output == null) {
491             throw new NullPointerException("output == null");
492         }
493 
494         int inputSize = input.remaining();
495         byte[] outputArray;
496         if (input.hasArray()) {
497             outputArray =
498                     engineUpdate(
499                             input.array(), input.arrayOffset() + input.position(), inputSize);
500             input.position(input.position() + inputSize);
501         } else {
502             byte[] inputArray = new byte[inputSize];
503             input.get(inputArray);
504             outputArray = engineUpdate(inputArray, 0, inputSize);
505         }
506 
507         int outputSize = (outputArray != null) ? outputArray.length : 0;
508         if (outputSize > 0) {
509             int outputBufferAvailable = output.remaining();
510             try {
511                 output.put(outputArray);
512             } catch (BufferOverflowException e) {
513                 throw new ShortBufferException(
514                         "Output buffer too small. Produced: " + outputSize + ", available: "
515                                 + outputBufferAvailable);
516             }
517         }
518         return outputSize;
519     }
520 
521     @Override
engineUpdateAAD(byte[] input, int inputOffset, int inputLen)522     protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
523         if (mCipher != null) {
524             mCipher.updateAAD(input, inputOffset, inputLen);
525             return;
526         }
527 
528         if (mCachedException != null) {
529             return;
530         }
531 
532         try {
533             ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey));
534         } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
535             mCachedException = e;
536             return;
537         }
538 
539         if (mAdditionalAuthenticationDataStreamerClosed) {
540             throw new IllegalStateException(
541                     "AAD can only be provided before Cipher.update is invoked");
542         }
543 
544         if (mAdditionalAuthenticationDataStreamer == null) {
545             throw new IllegalStateException("This cipher does not support AAD");
546         }
547 
548         byte[] output;
549         try {
550             output = mAdditionalAuthenticationDataStreamer.update(input, inputOffset, inputLen);
551         } catch (KeyStoreException e) {
552             mCachedException = e;
553             return;
554         }
555 
556         if ((output != null) && (output.length > 0)) {
557             throw new ProviderException("AAD update unexpectedly produced output: "
558                     + output.length + " bytes");
559         }
560     }
561 
562     @Override
engineUpdateAAD(ByteBuffer src)563     protected final void engineUpdateAAD(ByteBuffer src) {
564         if (mCipher != null) {
565             mCipher.updateAAD(src);
566             return;
567         }
568 
569         if (src == null) {
570             throw new IllegalArgumentException("src == null");
571         }
572         if (!src.hasRemaining()) {
573             return;
574         }
575 
576         byte[] input;
577         int inputOffset;
578         int inputLen;
579         if (src.hasArray()) {
580             input = src.array();
581             inputOffset = src.arrayOffset() + src.position();
582             inputLen = src.remaining();
583             src.position(src.limit());
584         } else {
585             input = new byte[src.remaining()];
586             inputOffset = 0;
587             inputLen = input.length;
588             src.get(input);
589         }
590         engineUpdateAAD(input, inputOffset, inputLen);
591     }
592 
593     @Override
engineDoFinal(byte[] input, int inputOffset, int inputLen)594     protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
595             throws IllegalBlockSizeException, BadPaddingException {
596         if (mCipher != null) {
597             if (input == null && inputLen == 0) {
598                 return mCipher.doFinal();
599             } else {
600                 return mCipher.doFinal(input, inputOffset, inputLen);
601             }
602         }
603 
604         if (mCachedException != null) {
605             throw (IllegalBlockSizeException)
606                     new IllegalBlockSizeException().initCause(mCachedException);
607         }
608 
609         try {
610             ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey));
611         } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
612             throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
613         }
614 
615         byte[] output;
616         try {
617             flushAAD();
618             output = mMainDataStreamer.doFinal(
619                     input, inputOffset, inputLen,
620                     null); // no signature involved
621         } catch (KeyStoreException e) {
622             switch (e.getErrorCode()) {
623                 case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
624                     throw (BadPaddingException) new BadPaddingException().initCause(e);
625                 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
626                     throw (AEADBadTagException) new AEADBadTagException().initCause(e);
627                 default:
628                     throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
629             }
630         }
631 
632         resetWhilePreservingInitState();
633         return output;
634     }
635 
636     @Override
engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)637     protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
638             int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
639             BadPaddingException {
640         if (mCipher != null) {
641             return mCipher.doFinal(input, inputOffset, inputLen, output);
642         }
643 
644         byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen);
645         if (outputCopy == null) {
646             return 0;
647         }
648         int outputAvailable = output.length - outputOffset;
649         if (outputCopy.length > outputAvailable) {
650             throw new ShortBufferException("Output buffer too short. Produced: "
651                     + outputCopy.length + ", available: " + outputAvailable);
652         }
653         System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
654         return outputCopy.length;
655     }
656 
657     @Override
engineDoFinal(ByteBuffer input, ByteBuffer output)658     protected final int engineDoFinal(ByteBuffer input, ByteBuffer output)
659             throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
660         if (mCipher != null) {
661             return mCipher.doFinal(input, output);
662         }
663 
664         if (input == null) {
665             throw new NullPointerException("input == null");
666         }
667         if (output == null) {
668             throw new NullPointerException("output == null");
669         }
670 
671         int inputSize = input.remaining();
672         byte[] outputArray;
673         if (input.hasArray()) {
674             outputArray =
675                     engineDoFinal(
676                             input.array(), input.arrayOffset() + input.position(), inputSize);
677             input.position(input.position() + inputSize);
678         } else {
679             byte[] inputArray = new byte[inputSize];
680             input.get(inputArray);
681             outputArray = engineDoFinal(inputArray, 0, inputSize);
682         }
683 
684         int outputSize = (outputArray != null) ? outputArray.length : 0;
685         if (outputSize > 0) {
686             int outputBufferAvailable = output.remaining();
687             try {
688                 output.put(outputArray);
689             } catch (BufferOverflowException e) {
690                 throw new ShortBufferException(
691                         "Output buffer too small. Produced: " + outputSize + ", available: "
692                                 + outputBufferAvailable);
693             }
694         }
695         return outputSize;
696     }
697 
698     @Override
engineWrap(Key key)699     protected final byte[] engineWrap(Key key)
700             throws IllegalBlockSizeException, InvalidKeyException {
701         if (mCipher != null) {
702             return mCipher.wrap(key);
703         }
704 
705         if (mKey == null) {
706             throw new IllegalStateException("Not initilized");
707         }
708 
709         if (!isEncrypting()) {
710             throw new IllegalStateException(
711                     "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
712         }
713 
714         if (key == null) {
715             throw new NullPointerException("key == null");
716         }
717         byte[] encoded = null;
718         if (key instanceof SecretKey) {
719             if ("RAW".equalsIgnoreCase(key.getFormat())) {
720                 encoded = key.getEncoded();
721             }
722             if (encoded == null) {
723                 try {
724                     SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(key.getAlgorithm());
725                     SecretKeySpec spec =
726                             (SecretKeySpec) keyFactory.getKeySpec(
727                                     (SecretKey) key, SecretKeySpec.class);
728                     encoded = spec.getEncoded();
729                 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
730                     throw new InvalidKeyException(
731                             "Failed to wrap key because it does not export its key material",
732                             e);
733                 }
734             }
735         } else if (key instanceof PrivateKey) {
736             if ("PKCS8".equalsIgnoreCase(key.getFormat())) {
737                 encoded = key.getEncoded();
738             }
739             if (encoded == null) {
740                 try {
741                     KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
742                     PKCS8EncodedKeySpec spec =
743                             keyFactory.getKeySpec(key, PKCS8EncodedKeySpec.class);
744                     encoded = spec.getEncoded();
745                 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
746                     throw new InvalidKeyException(
747                             "Failed to wrap key because it does not export its key material",
748                             e);
749                 }
750             }
751         } else if (key instanceof PublicKey) {
752             if ("X.509".equalsIgnoreCase(key.getFormat())) {
753                 encoded = key.getEncoded();
754             }
755             if (encoded == null) {
756                 try {
757                     KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
758                     X509EncodedKeySpec spec =
759                             keyFactory.getKeySpec(key, X509EncodedKeySpec.class);
760                     encoded = spec.getEncoded();
761                 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
762                     throw new InvalidKeyException(
763                             "Failed to wrap key because it does not export its key material",
764                             e);
765                 }
766             }
767         } else {
768             throw new InvalidKeyException("Unsupported key type: " + key.getClass().getName());
769         }
770 
771         if (encoded == null) {
772             throw new InvalidKeyException(
773                     "Failed to wrap key because it does not export its key material");
774         }
775 
776         try {
777             return engineDoFinal(encoded, 0, encoded.length);
778         } catch (BadPaddingException e) {
779             throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
780         }
781     }
782 
783     @Override
engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)784     protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
785             int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
786         if (mCipher != null) {
787             return mCipher.unwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType);
788         }
789 
790         if (mKey == null) {
791             throw new IllegalStateException("Not initilized");
792         }
793 
794         if (isEncrypting()) {
795             throw new IllegalStateException(
796                     "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
797         }
798 
799         if (wrappedKey == null) {
800             throw new NullPointerException("wrappedKey == null");
801         }
802 
803         byte[] encoded;
804         try {
805             encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
806         } catch (IllegalBlockSizeException | BadPaddingException e) {
807             throw new InvalidKeyException("Failed to unwrap key", e);
808         }
809 
810         switch (wrappedKeyType) {
811             case Cipher.SECRET_KEY:
812             {
813                 return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
814                 // break;
815             }
816             case Cipher.PRIVATE_KEY:
817             {
818                 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
819                 try {
820                     return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
821                 } catch (InvalidKeySpecException e) {
822                     throw new InvalidKeyException(
823                             "Failed to create private key from its PKCS#8 encoded form", e);
824                 }
825                 // break;
826             }
827             case Cipher.PUBLIC_KEY:
828             {
829                 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
830                 try {
831                     return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
832                 } catch (InvalidKeySpecException e) {
833                     throw new InvalidKeyException(
834                             "Failed to create public key from its X.509 encoded form", e);
835                 }
836                 // break;
837             }
838             default:
839                 throw new InvalidParameterException(
840                         "Unsupported wrappedKeyType: " + wrappedKeyType);
841         }
842     }
843 
844     @Override
engineSetMode(String mode)845     protected final void engineSetMode(String mode) throws NoSuchAlgorithmException {
846         // This should never be invoked because all algorithms registered with the AndroidKeyStore
847         // provide explicitly specify block mode.
848         throw new UnsupportedOperationException();
849     }
850 
851     @Override
engineSetPadding(String arg0)852     protected final void engineSetPadding(String arg0) throws NoSuchPaddingException {
853         // This should never be invoked because all algorithms registered with the AndroidKeyStore
854         // provide explicitly specify padding mode.
855         throw new UnsupportedOperationException();
856     }
857 
858     @Override
engineGetKeySize(Key key)859     protected final int engineGetKeySize(Key key) throws InvalidKeyException {
860         throw new UnsupportedOperationException();
861     }
862 
863     @CallSuper
864     @Override
finalize()865     public void finalize() throws Throwable {
866         try {
867             abortOperation();
868         } finally {
869             super.finalize();
870         }
871     }
872 
873     @Override
getOperationHandle()874     public final long getOperationHandle() {
875         return mOperationChallenge;
876     }
877 
setKey(@onNull AndroidKeyStoreKey key)878     protected final void setKey(@NonNull AndroidKeyStoreKey key) {
879         mKey = key;
880     }
881 
882     /**
883      * Overrides the default purpose/type of the crypto operation.
884      */
setKeymasterPurposeOverride(int keymasterPurpose)885     protected final void setKeymasterPurposeOverride(int keymasterPurpose) {
886         mKeymasterPurposeOverride = keymasterPurpose;
887     }
888 
getKeymasterPurposeOverride()889     protected final int getKeymasterPurposeOverride() {
890         return mKeymasterPurposeOverride;
891     }
892 
893     /**
894      * Returns {@code true} if this cipher is initialized for encryption, {@code false} if this
895      * cipher is initialized for decryption.
896      */
isEncrypting()897     protected final boolean isEncrypting() {
898         return mEncrypting;
899     }
900 
getConsumedInputSizeBytes()901     protected final long getConsumedInputSizeBytes() {
902         if (mMainDataStreamer == null) {
903             throw new IllegalStateException("Not initialized");
904         }
905         return mMainDataStreamer.getConsumedInputSizeBytes();
906     }
907 
getProducedOutputSizeBytes()908     protected final long getProducedOutputSizeBytes() {
909         if (mMainDataStreamer == null) {
910             throw new IllegalStateException("Not initialized");
911         }
912         return mMainDataStreamer.getProducedOutputSizeBytes();
913     }
914 
opmodeToString(int opmode)915     static String opmodeToString(int opmode) {
916         switch (opmode) {
917             case Cipher.ENCRYPT_MODE:
918                 return "ENCRYPT_MODE";
919             case Cipher.DECRYPT_MODE:
920                 return "DECRYPT_MODE";
921             case Cipher.WRAP_MODE:
922                 return "WRAP_MODE";
923             case Cipher.UNWRAP_MODE:
924                 return "UNWRAP_MODE";
925             default:
926                 return String.valueOf(opmode);
927         }
928     }
929 
930     // The methods below need to be implemented by subclasses.
931 
932     /**
933      * Initializes this cipher with the provided key.
934      *
935      * @throws InvalidKeyException if the {@code key} is not suitable for this cipher in the
936      *         specified {@code opmode}.
937      *
938      * @see #setKey(AndroidKeyStoreKey)
939      */
initKey(int opmode, @Nullable Key key)940     protected abstract void initKey(int opmode, @Nullable Key key) throws InvalidKeyException;
941 
942     /**
943      * Returns algorithm-specific parameters used by this cipher or {@code null} if no
944      * algorithm-specific parameters are used.
945      */
946     @Nullable
947     @Override
engineGetParameters()948     protected abstract AlgorithmParameters engineGetParameters();
949 
950     /**
951      * Invoked by {@code engineInit} to initialize algorithm-specific parameters when no additional
952      * initialization parameters were provided.
953      *
954      * @throws InvalidKeyException if this cipher cannot be configured based purely on the provided
955      *         key and needs additional parameters to be provided to {@code Cipher.init}.
956      */
initAlgorithmSpecificParameters()957     protected abstract void initAlgorithmSpecificParameters() throws InvalidKeyException;
958 
959     /**
960      * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional
961      * parameters were provided.
962      *
963      * @param params additional algorithm parameters or {@code null} if not specified.
964      *
965      * @throws InvalidAlgorithmParameterException if there is insufficient information to configure
966      *         this cipher or if the provided parameters are not suitable for this cipher.
967      */
initAlgorithmSpecificParameters( @ullable AlgorithmParameterSpec params)968     protected abstract void initAlgorithmSpecificParameters(
969             @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException;
970 
971     /**
972      * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional
973      * parameters were provided.
974      *
975      * @param params additional algorithm parameters or {@code null} if not specified.
976      *
977      * @throws InvalidAlgorithmParameterException if there is insufficient information to configure
978      *         this cipher or if the provided parameters are not suitable for this cipher.
979      */
initAlgorithmSpecificParameters(@ullable AlgorithmParameters params)980     protected abstract void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
981             throws InvalidAlgorithmParameterException;
982 
983     /**
984      * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
985      * {@code begin} operation. This amount of entropy is typically what's consumed to generate
986      * random parameters, such as IV.
987      *
988      * <p>For decryption, the return value should be {@code 0} because decryption should not be
989      * consuming any entropy. For encryption, the value combined with
990      * {@link #getAdditionalEntropyAmountForFinish()} should match (or exceed) the amount of Shannon
991      * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all
992      * explicitly provided parameters to {@code Cipher.init} are known. For example, for AES CBC
993      * encryption with an explicitly provided IV the return value should be {@code 0}, whereas for
994      * the case where IV is generated by the KeyStore's {@code begin} operation it should be
995      * {@code 16}.
996      */
getAdditionalEntropyAmountForBegin()997     protected abstract int getAdditionalEntropyAmountForBegin();
998 
999     /**
1000      * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
1001      * {@code finish} operation. This amount of entropy is typically what's consumed by encryption
1002      * padding scheme.
1003      *
1004      * <p>For decryption, the return value should be {@code 0} because decryption should not be
1005      * consuming any entropy. For encryption, the value combined with
1006      * {@link #getAdditionalEntropyAmountForBegin()} should match (or exceed) the amount of Shannon
1007      * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all
1008      * explicitly provided parameters to {@code Cipher.init} are known. For example, for RSA with
1009      * OAEP the return value should be the size of the OAEP hash output. For RSA with PKCS#1 padding
1010      * the return value should be the size of the padding string or could be raised (for simplicity)
1011      * to the size of the modulus.
1012      */
getAdditionalEntropyAmountForFinish()1013     protected abstract int getAdditionalEntropyAmountForFinish();
1014 
1015     /**
1016      * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
1017      *
1018      * @param parameters keystore/keymaster arguments to be populated with algorithm-specific
1019      *        parameters.
1020      */
addAlgorithmSpecificParametersToBegin( @onNull List<KeyParameter> parameters)1021     protected abstract void addAlgorithmSpecificParametersToBegin(
1022             @NonNull List<KeyParameter> parameters);
1023 
1024     /**
1025      * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation,
1026      * including the key characteristics. This is useful in case the parameters to {@code begin}
1027      * depend on how the key was generated.
1028      * The default implementation provided here simply ignores these key characteristics because
1029      * they are not be needed for most engines.
1030      *
1031      * @param parameters keystore/keymaster arguments to be populated with algorithm-specific
1032      *                   parameters.
1033      * @param keyCharacteristics The key's characteristics.
1034      */
addAlgorithmSpecificParametersToBegin( @onNull List<KeyParameter> parameters, Authorization[] keyCharacteristics)1035     protected void addAlgorithmSpecificParametersToBegin(
1036             @NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) {
1037         addAlgorithmSpecificParametersToBegin(parameters);
1038     }
1039 
1040     /**
1041      * Invoked to obtain algorithm-specific parameters from the result of the KeyStore's
1042      * {@code begin} operation.
1043      *
1044      * <p>Some parameters, such as IV, are not required to be provided to {@code Cipher.init}. Such
1045      * parameters, if not provided, must be generated by KeyStore and returned to the user of
1046      * {@code Cipher} and potentially reused after {@code doFinal}.
1047      *
1048      * @param parameters keystore/keymaster arguments returned by KeyStore {@code createOperation}.
1049      */
loadAlgorithmSpecificParametersFromBeginResult( KeyParameter[] parameters)1050     protected abstract void loadAlgorithmSpecificParametersFromBeginResult(
1051             KeyParameter[] parameters);
1052 
getTransform()1053     protected abstract String getTransform();
1054 }
1055