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