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