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