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
17package android.security.keystore;
18
19import android.annotation.CallSuper;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.os.IBinder;
23import android.security.KeyStore;
24import android.security.KeyStoreException;
25import android.security.keymaster.KeymasterArguments;
26import android.security.keymaster.KeymasterDefs;
27import android.security.keymaster.OperationResult;
28
29import libcore.util.EmptyArray;
30
31import java.nio.BufferOverflowException;
32import java.nio.ByteBuffer;
33import java.security.AlgorithmParameters;
34import java.security.GeneralSecurityException;
35import java.security.InvalidAlgorithmParameterException;
36import java.security.InvalidKeyException;
37import java.security.InvalidParameterException;
38import java.security.Key;
39import java.security.KeyFactory;
40import java.security.NoSuchAlgorithmException;
41import java.security.PrivateKey;
42import java.security.ProviderException;
43import java.security.PublicKey;
44import java.security.SecureRandom;
45import java.security.spec.AlgorithmParameterSpec;
46import java.security.spec.InvalidKeySpecException;
47import java.security.spec.PKCS8EncodedKeySpec;
48import java.security.spec.X509EncodedKeySpec;
49
50import javax.crypto.AEADBadTagException;
51import javax.crypto.BadPaddingException;
52import javax.crypto.Cipher;
53import javax.crypto.CipherSpi;
54import javax.crypto.IllegalBlockSizeException;
55import javax.crypto.NoSuchPaddingException;
56import javax.crypto.SecretKey;
57import javax.crypto.SecretKeyFactory;
58import javax.crypto.ShortBufferException;
59import javax.crypto.spec.SecretKeySpec;
60
61/**
62 * Base class for {@link CipherSpi} implementations of Android KeyStore backed ciphers.
63 *
64 * @hide
65 */
66abstract 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
95    AndroidKeyStoreCipherSpiBase() {
96        mKeyStore = KeyStore.getInstance();
97    }
98
99    @Override
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
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
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
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
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
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
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
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     * @returns stream or {@code null} if AAD is not supported by this cipher.
311     */
312    @Nullable
313    protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
314            @SuppressWarnings("unused") KeyStore keyStore,
315            @SuppressWarnings("unused") IBinder operationToken) {
316        return null;
317    }
318
319    @Override
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
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
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
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
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
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
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
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
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
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
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
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
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
734    protected final int engineGetKeySize(Key key) throws InvalidKeyException {
735        throw new UnsupportedOperationException();
736    }
737
738    @CallSuper
739    @Override
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
752    public final long getOperationHandle() {
753        return mOperationHandle;
754    }
755
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     */
763    protected final void setKeymasterPurposeOverride(int keymasterPurpose) {
764        mKeymasterPurposeOverride = keymasterPurpose;
765    }
766
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     */
775    protected final boolean isEncrypting() {
776        return mEncrypting;
777    }
778
779    @NonNull
780    protected final KeyStore getKeyStore() {
781        return mKeyStore;
782    }
783
784    protected final long getConsumedInputSizeBytes() {
785        if (mMainDataStreamer == null) {
786            throw new IllegalStateException("Not initialized");
787        }
788        return mMainDataStreamer.getConsumedInputSizeBytes();
789    }
790
791    protected final long getProducedOutputSizeBytes() {
792        if (mMainDataStreamer == null) {
793            throw new IllegalStateException("Not initialized");
794        }
795        return mMainDataStreamer.getProducedOutputSizeBytes();
796    }
797
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
918    protected abstract void loadAlgorithmSpecificParametersFromBeginResult(
919            @NonNull KeymasterArguments keymasterArgs);
920}
921