Home | History | Annotate | Download | only in keystore
      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 
     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      * @return 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