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