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.security.Credentials;
     20 import android.security.GateKeeper;
     21 import android.security.KeyStore;
     22 import android.security.keymaster.KeyCharacteristics;
     23 import android.security.keymaster.KeymasterArguments;
     24 import android.security.keymaster.KeymasterDefs;
     25 import android.security.keystore.KeyGenParameterSpec;
     26 import android.security.keystore.KeyProperties;
     27 
     28 import libcore.util.EmptyArray;
     29 
     30 import java.security.InvalidAlgorithmParameterException;
     31 import java.security.ProviderException;
     32 import java.security.SecureRandom;
     33 import java.security.spec.AlgorithmParameterSpec;
     34 import java.util.Arrays;
     35 
     36 import javax.crypto.KeyGeneratorSpi;
     37 import javax.crypto.SecretKey;
     38 
     39 /**
     40  * {@link KeyGeneratorSpi} backed by Android KeyStore.
     41  *
     42  * @hide
     43  */
     44 public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
     45 
     46     public static class AES extends AndroidKeyStoreKeyGeneratorSpi {
     47         public AES() {
     48             super(KeymasterDefs.KM_ALGORITHM_AES, 128);
     49         }
     50 
     51         @Override
     52         protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
     53                 throws InvalidAlgorithmParameterException {
     54             super.engineInit(params, random);
     55             if ((mKeySizeBits != 128) && (mKeySizeBits != 192) && (mKeySizeBits != 256)) {
     56                 throw new InvalidAlgorithmParameterException(
     57                         "Unsupported key size: " + mKeySizeBits
     58                         + ". Supported: 128, 192, 256.");
     59             }
     60         }
     61     }
     62 
     63     public static class DESede extends AndroidKeyStoreKeyGeneratorSpi {
     64         public DESede() {
     65             super(KeymasterDefs.KM_ALGORITHM_3DES, 168);
     66         }
     67     }
     68 
     69     protected static abstract class HmacBase extends AndroidKeyStoreKeyGeneratorSpi {
     70         protected HmacBase(int keymasterDigest) {
     71             super(KeymasterDefs.KM_ALGORITHM_HMAC,
     72                     keymasterDigest,
     73                     KeymasterUtils.getDigestOutputSizeBits(keymasterDigest));
     74         }
     75     }
     76 
     77     public static class HmacSHA1 extends HmacBase {
     78         public HmacSHA1() {
     79             super(KeymasterDefs.KM_DIGEST_SHA1);
     80         }
     81     }
     82 
     83     public static class HmacSHA224 extends HmacBase {
     84         public HmacSHA224() {
     85             super(KeymasterDefs.KM_DIGEST_SHA_2_224);
     86         }
     87     }
     88 
     89     public static class HmacSHA256 extends HmacBase {
     90         public HmacSHA256() {
     91             super(KeymasterDefs.KM_DIGEST_SHA_2_256);
     92         }
     93     }
     94 
     95     public static class HmacSHA384 extends HmacBase {
     96         public HmacSHA384() {
     97             super(KeymasterDefs.KM_DIGEST_SHA_2_384);
     98         }
     99     }
    100 
    101     public static class HmacSHA512 extends HmacBase {
    102         public HmacSHA512() {
    103             super(KeymasterDefs.KM_DIGEST_SHA_2_512);
    104         }
    105     }
    106 
    107     private final KeyStore mKeyStore = KeyStore.getInstance();
    108     private final int mKeymasterAlgorithm;
    109     private final int mKeymasterDigest;
    110     private final int mDefaultKeySizeBits;
    111 
    112     private KeyGenParameterSpec mSpec;
    113     private SecureRandom mRng;
    114 
    115     protected int mKeySizeBits;
    116     private int[] mKeymasterPurposes;
    117     private int[] mKeymasterBlockModes;
    118     private int[] mKeymasterPaddings;
    119     private int[] mKeymasterDigests;
    120 
    121     protected AndroidKeyStoreKeyGeneratorSpi(
    122             int keymasterAlgorithm,
    123             int defaultKeySizeBits) {
    124         this(keymasterAlgorithm, -1, defaultKeySizeBits);
    125     }
    126 
    127     protected AndroidKeyStoreKeyGeneratorSpi(
    128             int keymasterAlgorithm,
    129             int keymasterDigest,
    130             int defaultKeySizeBits) {
    131         mKeymasterAlgorithm = keymasterAlgorithm;
    132         mKeymasterDigest = keymasterDigest;
    133         mDefaultKeySizeBits = defaultKeySizeBits;
    134         if (mDefaultKeySizeBits <= 0) {
    135             throw new IllegalArgumentException("Default key size must be positive");
    136         }
    137 
    138         if ((mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) && (mKeymasterDigest == -1)) {
    139             throw new IllegalArgumentException(
    140                     "Digest algorithm must be specified for HMAC key");
    141         }
    142     }
    143 
    144     @Override
    145     protected void engineInit(SecureRandom random) {
    146         throw new UnsupportedOperationException("Cannot initialize without a "
    147                 + KeyGenParameterSpec.class.getName() + " parameter");
    148     }
    149 
    150     @Override
    151     protected void engineInit(int keySize, SecureRandom random) {
    152         throw new UnsupportedOperationException("Cannot initialize without a "
    153                 + KeyGenParameterSpec.class.getName() + " parameter");
    154     }
    155 
    156     @Override
    157     protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
    158             throws InvalidAlgorithmParameterException {
    159         resetAll();
    160 
    161         boolean success = false;
    162         try {
    163             if ((params == null) || (!(params instanceof KeyGenParameterSpec))) {
    164                 throw new InvalidAlgorithmParameterException("Cannot initialize without a "
    165                         + KeyGenParameterSpec.class.getName() + " parameter");
    166             }
    167             KeyGenParameterSpec spec = (KeyGenParameterSpec) params;
    168             if (spec.getKeystoreAlias() == null) {
    169                 throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
    170             }
    171 
    172             mRng = random;
    173             mSpec = spec;
    174 
    175             mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
    176             if (mKeySizeBits <= 0) {
    177                 throw new InvalidAlgorithmParameterException(
    178                         "Key size must be positive: " + mKeySizeBits);
    179             } else if ((mKeySizeBits % 8) != 0) {
    180                 throw new InvalidAlgorithmParameterException(
    181                         "Key size must be a multiple of 8: " + mKeySizeBits);
    182             }
    183 
    184             try {
    185                 mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
    186                 mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
    187                         spec.getEncryptionPaddings());
    188                 if (spec.getSignaturePaddings().length > 0) {
    189                     throw new InvalidAlgorithmParameterException(
    190                             "Signature paddings not supported for symmetric key algorithms");
    191                 }
    192                 mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
    193                 if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
    194                         && (spec.isRandomizedEncryptionRequired())) {
    195                     for (int keymasterBlockMode : mKeymasterBlockModes) {
    196                         if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
    197                                 keymasterBlockMode)) {
    198                             throw new InvalidAlgorithmParameterException(
    199                                     "Randomized encryption (IND-CPA) required but may be violated"
    200                                     + " by block mode: "
    201                                     + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
    202                                     + ". See " + KeyGenParameterSpec.class.getName()
    203                                     + " documentation.");
    204                         }
    205                     }
    206                 }
    207 
    208                 if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
    209                     if (mKeySizeBits < 64) {
    210                         throw new InvalidAlgorithmParameterException(
    211                             "HMAC key size must be at least 64 bits.");
    212                     }
    213 
    214                     // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
    215                     // implies SHA-256 digest). Because keymaster HMAC key is authorized only for
    216                     // one digest, we don't let algorithm parameter spec override the digest implied
    217                     // by the key. If the spec specifies digests at all, it must specify only one
    218                     // digest, the only implied by key algorithm.
    219                     mKeymasterDigests = new int[] {mKeymasterDigest};
    220                     if (spec.isDigestsSpecified()) {
    221                         // Digest(s) explicitly specified in the spec. Check that the list
    222                         // consists of exactly one digest, the one implied by key algorithm.
    223                         int[] keymasterDigestsFromSpec =
    224                                 KeyProperties.Digest.allToKeymaster(spec.getDigests());
    225                         if ((keymasterDigestsFromSpec.length != 1)
    226                                 || (keymasterDigestsFromSpec[0] != mKeymasterDigest)) {
    227                             throw new InvalidAlgorithmParameterException(
    228                                     "Unsupported digests specification: "
    229                                     + Arrays.asList(spec.getDigests()) + ". Only "
    230                                     + KeyProperties.Digest.fromKeymaster(mKeymasterDigest)
    231                                     + " supported for this HMAC key algorithm");
    232                         }
    233                     }
    234                 } else {
    235                     // Key algorithm does not imply a digest.
    236                     if (spec.isDigestsSpecified()) {
    237                         mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
    238                     } else {
    239                         mKeymasterDigests = EmptyArray.INT;
    240                     }
    241                 }
    242 
    243                 // Check that user authentication related parameters are acceptable. This method
    244                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
    245                 // not set up).
    246                 KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
    247             } catch (IllegalStateException | IllegalArgumentException e) {
    248                 throw new InvalidAlgorithmParameterException(e);
    249             }
    250 
    251             success = true;
    252         } finally {
    253             if (!success) {
    254                 resetAll();
    255             }
    256         }
    257     }
    258 
    259     private void resetAll() {
    260         mSpec = null;
    261         mRng = null;
    262         mKeySizeBits = -1;
    263         mKeymasterPurposes = null;
    264         mKeymasterPaddings = null;
    265         mKeymasterBlockModes = null;
    266     }
    267 
    268     @Override
    269     protected SecretKey engineGenerateKey() {
    270         KeyGenParameterSpec spec = mSpec;
    271         if (spec == null) {
    272             throw new IllegalStateException("Not initialized");
    273         }
    274 
    275         KeymasterArguments args = new KeymasterArguments();
    276         args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
    277         args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
    278         args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
    279         args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
    280         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
    281         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
    282         KeymasterUtils.addUserAuthArgs(args, spec);
    283         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
    284                 args,
    285                 mKeymasterAlgorithm,
    286                 mKeymasterBlockModes,
    287                 mKeymasterDigests);
    288         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart());
    289         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
    290                 spec.getKeyValidityForOriginationEnd());
    291         args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
    292                 spec.getKeyValidityForConsumptionEnd());
    293 
    294         if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
    295                 && (!spec.isRandomizedEncryptionRequired())) {
    296             // Permit caller-provided IV when encrypting with this key
    297             args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
    298         }
    299 
    300         byte[] additionalEntropy =
    301                 KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
    302                         mRng, (mKeySizeBits + 7) / 8);
    303         int flags = 0;
    304         String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias();
    305         KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
    306         boolean success = false;
    307         try {
    308             Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias(), spec.getUid());
    309             int errorCode = mKeyStore.generateKey(
    310                     keyAliasInKeystore,
    311                     args,
    312                     additionalEntropy,
    313                     spec.getUid(),
    314                     flags,
    315                     resultingKeyCharacteristics);
    316             if (errorCode != KeyStore.NO_ERROR) {
    317                 throw new ProviderException(
    318                         "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
    319             }
    320             @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
    321             try {
    322                 keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
    323                         mKeymasterAlgorithm, mKeymasterDigest);
    324             } catch (IllegalArgumentException e) {
    325                 throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
    326             }
    327             SecretKey result = new AndroidKeyStoreSecretKey(
    328                     keyAliasInKeystore, spec.getUid(), keyAlgorithmJCA);
    329             success = true;
    330             return result;
    331         } finally {
    332             if (!success) {
    333                 Credentials.deleteAllTypesForAlias(
    334                         mKeyStore, spec.getKeystoreAlias(), spec.getUid());
    335             }
    336         }
    337     }
    338 }
    339