• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.KeyStore;
21 import android.security.keymaster.KeyCharacteristics;
22 import android.security.keymaster.KeymasterArguments;
23 import android.security.keymaster.KeymasterDefs;
24 import android.security.keystore.KeyGenParameterSpec;
25 import android.security.keystore.KeyProperties;
26 
27 import libcore.util.EmptyArray;
28 
29 import java.security.InvalidAlgorithmParameterException;
30 import java.security.ProviderException;
31 import java.security.SecureRandom;
32 import java.security.spec.AlgorithmParameterSpec;
33 import java.util.Arrays;
34 
35 import javax.crypto.KeyGeneratorSpi;
36 import javax.crypto.SecretKey;
37 
38 /**
39  * {@link KeyGeneratorSpi} backed by Android KeyStore.
40  *
41  * @hide
42  */
43 public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
44 
45     public static class AES extends AndroidKeyStoreKeyGeneratorSpi {
AES()46         public AES() {
47             super(KeymasterDefs.KM_ALGORITHM_AES, 128);
48         }
49 
50         @Override
engineInit(AlgorithmParameterSpec params, SecureRandom random)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 {
HmacBase(int keymasterDigest)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 {
HmacSHA1()71         public HmacSHA1() {
72             super(KeymasterDefs.KM_DIGEST_SHA1);
73         }
74     }
75 
76     public static class HmacSHA224 extends HmacBase {
HmacSHA224()77         public HmacSHA224() {
78             super(KeymasterDefs.KM_DIGEST_SHA_2_224);
79         }
80     }
81 
82     public static class HmacSHA256 extends HmacBase {
HmacSHA256()83         public HmacSHA256() {
84             super(KeymasterDefs.KM_DIGEST_SHA_2_256);
85         }
86     }
87 
88     public static class HmacSHA384 extends HmacBase {
HmacSHA384()89         public HmacSHA384() {
90             super(KeymasterDefs.KM_DIGEST_SHA_2_384);
91         }
92     }
93 
94     public static class HmacSHA512 extends HmacBase {
HmacSHA512()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 
AndroidKeyStoreKeyGeneratorSpi( int keymasterAlgorithm, int defaultKeySizeBits)114     protected AndroidKeyStoreKeyGeneratorSpi(
115             int keymasterAlgorithm,
116             int defaultKeySizeBits) {
117         this(keymasterAlgorithm, -1, defaultKeySizeBits);
118     }
119 
AndroidKeyStoreKeyGeneratorSpi( int keymasterAlgorithm, int keymasterDigest, int defaultKeySizeBits)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
engineInit(SecureRandom random)138     protected void engineInit(SecureRandom random) {
139         throw new UnsupportedOperationException("Cannot initialize without a "
140                 + KeyGenParameterSpec.class.getName() + " parameter");
141     }
142 
143     @Override
engineInit(int keySize, SecureRandom random)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
engineInit(AlgorithmParameterSpec params, SecureRandom random)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 
resetAll()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
engineGenerateKey()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