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.os.IBinder;
     22 import android.security.KeyStore;
     23 import android.security.KeyStoreException;
     24 import android.security.keymaster.KeymasterArguments;
     25 import android.security.keymaster.KeymasterDefs;
     26 import android.security.keymaster.OperationResult;
     27 
     28 import libcore.util.EmptyArray;
     29 
     30 import java.nio.ByteBuffer;
     31 import java.security.InvalidKeyException;
     32 import java.security.InvalidParameterException;
     33 import java.security.PrivateKey;
     34 import java.security.ProviderException;
     35 import java.security.PublicKey;
     36 import java.security.SecureRandom;
     37 import java.security.SignatureException;
     38 import java.security.SignatureSpi;
     39 
     40 /**
     41  * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers.
     42  *
     43  * @hide
     44  */
     45 abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
     46         implements KeyStoreCryptoOperation {
     47     private final KeyStore mKeyStore;
     48 
     49     // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin
     50     // and should be preserved after SignatureSpi.engineSign/engineVerify finishes.
     51     private boolean mSigning;
     52     private AndroidKeyStoreKey mKey;
     53 
     54     /**
     55      * Token referencing this operation inside keystore service. It is initialized by
     56      * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when
     57      * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between.
     58      */
     59     private IBinder mOperationToken;
     60     private long mOperationHandle;
     61     private KeyStoreCryptoOperationStreamer mMessageStreamer;
     62 
     63     /**
     64      * Encountered exception which could not be immediately thrown because it was encountered inside
     65      * a method that does not throw checked exception. This exception will be thrown from
     66      * {@code engineSign} or {@code engineVerify}. Once such an exception is encountered,
     67      * {@code engineUpdate} starts ignoring input data.
     68      */
     69     private Exception mCachedException;
     70 
     71     AndroidKeyStoreSignatureSpiBase() {
     72         mKeyStore = KeyStore.getInstance();
     73     }
     74 
     75     @Override
     76     protected final void engineInitSign(PrivateKey key) throws InvalidKeyException {
     77         engineInitSign(key, null);
     78     }
     79 
     80     @Override
     81     protected final void engineInitSign(PrivateKey privateKey, SecureRandom random)
     82             throws InvalidKeyException {
     83         resetAll();
     84 
     85         boolean success = false;
     86         try {
     87             if (privateKey == null) {
     88                 throw new InvalidKeyException("Unsupported key: null");
     89             }
     90             AndroidKeyStoreKey keystoreKey;
     91             if (privateKey instanceof AndroidKeyStorePrivateKey) {
     92                 keystoreKey = (AndroidKeyStoreKey) privateKey;
     93             } else {
     94                 throw new InvalidKeyException("Unsupported private key type: " + privateKey);
     95             }
     96             mSigning = true;
     97             initKey(keystoreKey);
     98             appRandom = random;
     99             ensureKeystoreOperationInitialized();
    100             success = true;
    101         } finally {
    102             if (!success) {
    103                 resetAll();
    104             }
    105         }
    106     }
    107 
    108     @Override
    109     protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
    110         resetAll();
    111 
    112         boolean success = false;
    113         try {
    114             if (publicKey == null) {
    115                 throw new InvalidKeyException("Unsupported key: null");
    116             }
    117             AndroidKeyStoreKey keystoreKey;
    118             if (publicKey instanceof AndroidKeyStorePublicKey) {
    119                 keystoreKey = (AndroidKeyStorePublicKey) publicKey;
    120             } else {
    121                 throw new InvalidKeyException("Unsupported public key type: " + publicKey);
    122             }
    123             mSigning = false;
    124             initKey(keystoreKey);
    125             appRandom = null;
    126             ensureKeystoreOperationInitialized();
    127             success = true;
    128         } finally {
    129             if (!success) {
    130                 resetAll();
    131             }
    132         }
    133     }
    134 
    135     /**
    136      * Configures this signature instance to use the provided key.
    137      *
    138      * @throws InvalidKeyException if the {@code key} is not suitable.
    139      */
    140     @CallSuper
    141     protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
    142         mKey = key;
    143     }
    144 
    145     /**
    146      * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
    147      * cipher instance.
    148      *
    149      * <p>Subclasses storing additional state should override this method, reset the additional
    150      * state, and then chain to superclass.
    151      */
    152     @CallSuper
    153     protected void resetAll() {
    154         IBinder operationToken = mOperationToken;
    155         if (operationToken != null) {
    156             mOperationToken = null;
    157             mKeyStore.abort(operationToken);
    158         }
    159         mSigning = false;
    160         mKey = null;
    161         appRandom = null;
    162         mOperationToken = null;
    163         mOperationHandle = 0;
    164         mMessageStreamer = null;
    165         mCachedException = null;
    166     }
    167 
    168     /**
    169      * Resets this cipher while preserving the initialized state. This must be equivalent to
    170      * rolling back the cipher's state to just after the most recent {@code engineInit} completed
    171      * successfully.
    172      *
    173      * <p>Subclasses storing additional post-init state should override this method, reset the
    174      * additional state, and then chain to superclass.
    175      */
    176     @CallSuper
    177     protected void resetWhilePreservingInitState() {
    178         IBinder operationToken = mOperationToken;
    179         if (operationToken != null) {
    180             mOperationToken = null;
    181             mKeyStore.abort(operationToken);
    182         }
    183         mOperationHandle = 0;
    184         mMessageStreamer = null;
    185         mCachedException = null;
    186     }
    187 
    188     private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
    189         if (mMessageStreamer != null) {
    190             return;
    191         }
    192         if (mCachedException != null) {
    193             return;
    194         }
    195         if (mKey == null) {
    196             throw new IllegalStateException("Not initialized");
    197         }
    198 
    199         KeymasterArguments keymasterInputArgs = new KeymasterArguments();
    200         addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
    201 
    202         OperationResult opResult = mKeyStore.begin(
    203                 mKey.getAlias(),
    204                 mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
    205                 true, // permit aborting this operation if keystore runs out of resources
    206                 keymasterInputArgs,
    207                 null, // no additional entropy for begin -- only finish might need some
    208                 mKey.getUid());
    209         if (opResult == null) {
    210             throw new KeyStoreConnectException();
    211         }
    212 
    213         // Store operation token and handle regardless of the error code returned by KeyStore to
    214         // ensure that the operation gets aborted immediately if the code below throws an exception.
    215         mOperationToken = opResult.token;
    216         mOperationHandle = opResult.operationHandle;
    217 
    218         // If necessary, throw an exception due to KeyStore operation having failed.
    219         InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
    220                 mKeyStore, mKey, opResult.resultCode);
    221         if (e != null) {
    222             throw e;
    223         }
    224 
    225         if (mOperationToken == null) {
    226             throw new ProviderException("Keystore returned null operation token");
    227         }
    228         if (mOperationHandle == 0) {
    229             throw new ProviderException("Keystore returned invalid operation handle");
    230         }
    231 
    232         mMessageStreamer = createMainDataStreamer(mKeyStore, opResult.token);
    233     }
    234 
    235     /**
    236      * Creates a streamer which sends the message to be signed/verified into the provided KeyStore
    237      *
    238      * <p>This implementation returns a working streamer.
    239      */
    240     @NonNull
    241     protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
    242             KeyStore keyStore, IBinder operationToken) {
    243         return new KeyStoreCryptoOperationChunkedStreamer(
    244                 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
    245                         keyStore, operationToken));
    246     }
    247 
    248     @Override
    249     public final long getOperationHandle() {
    250         return mOperationHandle;
    251     }
    252 
    253     @Override
    254     protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
    255         if (mCachedException != null) {
    256             throw new SignatureException(mCachedException);
    257         }
    258 
    259         try {
    260             ensureKeystoreOperationInitialized();
    261         } catch (InvalidKeyException e) {
    262             throw new SignatureException(e);
    263         }
    264 
    265         if (len == 0) {
    266             return;
    267         }
    268 
    269         byte[] output;
    270         try {
    271             output = mMessageStreamer.update(b, off, len);
    272         } catch (KeyStoreException e) {
    273             throw new SignatureException(e);
    274         }
    275 
    276         if (output.length != 0) {
    277             throw new ProviderException(
    278                     "Update operation unexpectedly produced output: " + output.length + " bytes");
    279         }
    280     }
    281 
    282     @Override
    283     protected final void engineUpdate(byte b) throws SignatureException {
    284         engineUpdate(new byte[] {b}, 0, 1);
    285     }
    286 
    287     @Override
    288     protected final void engineUpdate(ByteBuffer input) {
    289         byte[] b;
    290         int off;
    291         int len = input.remaining();
    292         if (input.hasArray()) {
    293             b = input.array();
    294             off = input.arrayOffset() + input.position();
    295             input.position(input.limit());
    296         } else {
    297             b = new byte[len];
    298             off = 0;
    299             input.get(b);
    300         }
    301 
    302         try {
    303             engineUpdate(b, off, len);
    304         } catch (SignatureException e) {
    305             mCachedException = e;
    306         }
    307     }
    308 
    309     @Override
    310     protected final int engineSign(byte[] out, int outOffset, int outLen)
    311             throws SignatureException {
    312         return super.engineSign(out, outOffset, outLen);
    313     }
    314 
    315     @Override
    316     protected final byte[] engineSign() throws SignatureException {
    317         if (mCachedException != null) {
    318             throw new SignatureException(mCachedException);
    319         }
    320 
    321         byte[] signature;
    322         try {
    323             ensureKeystoreOperationInitialized();
    324 
    325             byte[] additionalEntropy =
    326                     KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
    327                             appRandom, getAdditionalEntropyAmountForSign());
    328             signature = mMessageStreamer.doFinal(
    329                     EmptyArray.BYTE, 0, 0,
    330                     null, // no signature provided -- it'll be generated by this invocation
    331                     additionalEntropy);
    332         } catch (InvalidKeyException | KeyStoreException e) {
    333             throw new SignatureException(e);
    334         }
    335 
    336         resetWhilePreservingInitState();
    337         return signature;
    338     }
    339 
    340     @Override
    341     protected final boolean engineVerify(byte[] signature) throws SignatureException {
    342         if (mCachedException != null) {
    343             throw new SignatureException(mCachedException);
    344         }
    345 
    346         try {
    347             ensureKeystoreOperationInitialized();
    348         } catch (InvalidKeyException e) {
    349             throw new SignatureException(e);
    350         }
    351 
    352         boolean verified;
    353         try {
    354             byte[] output = mMessageStreamer.doFinal(
    355                     EmptyArray.BYTE, 0, 0,
    356                     signature,
    357                     null // no additional entropy needed -- verification is deterministic
    358                     );
    359             if (output.length != 0) {
    360                 throw new ProviderException(
    361                         "Signature verification unexpected produced output: " + output.length
    362                         + " bytes");
    363             }
    364             verified = true;
    365         } catch (KeyStoreException e) {
    366             switch (e.getErrorCode()) {
    367                 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
    368                     verified = false;
    369                     break;
    370                 default:
    371                     throw new SignatureException(e);
    372             }
    373         }
    374 
    375         resetWhilePreservingInitState();
    376         return verified;
    377     }
    378 
    379     @Override
    380     protected final boolean engineVerify(byte[] sigBytes, int offset, int len)
    381             throws SignatureException {
    382         return engineVerify(ArrayUtils.subarray(sigBytes, offset, len));
    383     }
    384 
    385     @Deprecated
    386     @Override
    387     protected final Object engineGetParameter(String param) throws InvalidParameterException {
    388         throw new InvalidParameterException();
    389     }
    390 
    391     @Deprecated
    392     @Override
    393     protected final void engineSetParameter(String param, Object value)
    394             throws InvalidParameterException {
    395         throw new InvalidParameterException();
    396     }
    397 
    398     protected final KeyStore getKeyStore() {
    399         return mKeyStore;
    400     }
    401 
    402     /**
    403      * Returns {@code true} if this signature is initialized for signing, {@code false} if this
    404      * signature is initialized for verification.
    405      */
    406     protected final boolean isSigning() {
    407         return mSigning;
    408     }
    409 
    410     // The methods below need to be implemented by subclasses.
    411 
    412     /**
    413      * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
    414      * {@code finish} operation when generating a signature.
    415      *
    416      * <p>This value should match (or exceed) the amount of Shannon entropy of the produced
    417      * signature assuming the key and the message are known. For example, for ECDSA signature this
    418      * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this
    419      * should be {@code 0}.
    420      */
    421     protected abstract int getAdditionalEntropyAmountForSign();
    422 
    423     /**
    424      * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
    425      *
    426      * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
    427      *        parameters.
    428      */
    429     protected abstract void addAlgorithmSpecificParametersToBegin(
    430             @NonNull KeymasterArguments keymasterArgs);
    431 }
    432