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