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