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