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