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