AndroidKeyStoreKeyGeneratorSpi.java revision 512c132f49fc6e8e4fc119f4cf167d33b2393509
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 } catch (IllegalStateException | IllegalArgumentException e) { 239 throw new InvalidAlgorithmParameterException(e); 240 } 241 242 success = true; 243 } finally { 244 if (!success) { 245 resetAll(); 246 } 247 } 248 } 249 250 private void resetAll() { 251 mSpec = null; 252 mRng = null; 253 mKeySizeBits = -1; 254 mKeymasterPurposes = null; 255 mKeymasterPaddings = null; 256 mKeymasterBlockModes = null; 257 } 258 259 @Override 260 protected SecretKey engineGenerateKey() { 261 KeyGenParameterSpec spec = mSpec; 262 if (spec == null) { 263 throw new IllegalStateException("Not initialized"); 264 } 265 266 KeymasterArguments args = new KeymasterArguments(); 267 args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits); 268 args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); 269 args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes); 270 args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes); 271 args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings); 272 args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); 273 KeymasterUtils.addUserAuthArgs(args, 274 spec.isUserAuthenticationRequired(), 275 spec.getUserAuthenticationValidityDurationSeconds(), 276 spec.isUserAuthenticationValidWhileOnBody()); 277 KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( 278 args, 279 mKeymasterAlgorithm, 280 mKeymasterBlockModes, 281 mKeymasterDigests); 282 args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); 283 args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, 284 spec.getKeyValidityForOriginationEnd()); 285 args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, 286 spec.getKeyValidityForConsumptionEnd()); 287 288 if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) 289 && (!spec.isRandomizedEncryptionRequired())) { 290 // Permit caller-provided IV when encrypting with this key 291 args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); 292 } 293 294 byte[] additionalEntropy = 295 KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( 296 mRng, (mKeySizeBits + 7) / 8); 297 int flags = 0; 298 String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias(); 299 KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); 300 boolean success = false; 301 try { 302 Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias(), spec.getUid()); 303 int errorCode = mKeyStore.generateKey( 304 keyAliasInKeystore, 305 args, 306 additionalEntropy, 307 spec.getUid(), 308 flags, 309 resultingKeyCharacteristics); 310 if (errorCode != KeyStore.NO_ERROR) { 311 throw new ProviderException( 312 "Keystore operation failed", KeyStore.getKeyStoreException(errorCode)); 313 } 314 @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA; 315 try { 316 keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( 317 mKeymasterAlgorithm, mKeymasterDigest); 318 } catch (IllegalArgumentException e) { 319 throw new ProviderException("Failed to obtain JCA secret key algorithm name", e); 320 } 321 SecretKey result = new AndroidKeyStoreSecretKey( 322 keyAliasInKeystore, spec.getUid(), keyAlgorithmJCA); 323 success = true; 324 return result; 325 } finally { 326 if (!success) { 327 Credentials.deleteAllTypesForAlias( 328 mKeyStore, spec.getKeystoreAlias(), spec.getUid()); 329 } 330 } 331 } 332} 333