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