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.GateKeeper;
21import android.security.KeyStore;
22import android.security.keymaster.KeyCharacteristics;
23import android.security.keymaster.KeymasterDefs;
24
25import java.math.BigInteger;
26import java.security.InvalidKeyException;
27import java.security.ProviderException;
28import java.security.spec.InvalidKeySpecException;
29import java.security.spec.KeySpec;
30import java.util.ArrayList;
31import java.util.Date;
32import java.util.List;
33
34import javax.crypto.SecretKey;
35import javax.crypto.SecretKeyFactorySpi;
36import javax.crypto.spec.SecretKeySpec;
37
38/**
39 * {@link SecretKeyFactorySpi} backed by Android Keystore.
40 *
41 * @hide
42 */
43public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
44
45    private final KeyStore mKeyStore = KeyStore.getInstance();
46
47    @Override
48    protected KeySpec engineGetKeySpec(SecretKey key,
49            @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
50        if (keySpecClass == null) {
51            throw new InvalidKeySpecException("keySpecClass == null");
52        }
53        if (!(key instanceof AndroidKeyStoreSecretKey)) {
54            throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " +
55                    ((key != null) ? key.getClass().getName() : "null"));
56        }
57        if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) {
58            throw new InvalidKeySpecException(
59                    "Key material export of Android KeyStore keys is not supported");
60        }
61        if (!KeyInfo.class.equals(keySpecClass)) {
62            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
63        }
64        AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
65        String keyAliasInKeystore = keystoreKey.getAlias();
66        String entryAlias;
67        if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
68            entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
69        } else {
70            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
71        }
72
73        return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid());
74    }
75
76    static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore,
77            int keyUid) {
78        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
79        int errorCode = keyStore.getKeyCharacteristics(
80                keyAliasInKeystore, null, null, keyUid, keyCharacteristics);
81        if (errorCode != KeyStore.NO_ERROR) {
82            throw new ProviderException("Failed to obtain information about key."
83                    + " Keystore error: " + errorCode);
84        }
85
86        boolean insideSecureHardware;
87        @KeyProperties.OriginEnum int origin;
88        int keySize;
89        @KeyProperties.PurposeEnum int purposes;
90        String[] encryptionPaddings;
91        String[] signaturePaddings;
92        @KeyProperties.DigestEnum String[] digests;
93        @KeyProperties.BlockModeEnum String[] blockModes;
94        int keymasterSwEnforcedUserAuthenticators;
95        int keymasterHwEnforcedUserAuthenticators;
96        List<BigInteger> keymasterSecureUserIds;
97        try {
98            if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
99                insideSecureHardware = true;
100                origin = KeyProperties.Origin.fromKeymaster(
101                        keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
102            } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
103                insideSecureHardware = false;
104                origin = KeyProperties.Origin.fromKeymaster(
105                        keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
106            } else {
107                throw new ProviderException("Key origin not available");
108            }
109            long keySizeUnsigned =
110                    keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
111            if (keySizeUnsigned == -1) {
112                throw new ProviderException("Key size not available");
113            } else if (keySizeUnsigned > Integer.MAX_VALUE) {
114                throw new ProviderException("Key too large: " + keySizeUnsigned + " bits");
115            }
116            keySize = (int) keySizeUnsigned;
117            purposes = KeyProperties.Purpose.allFromKeymaster(
118                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE));
119
120            List<String> encryptionPaddingsList = new ArrayList<String>();
121            List<String> signaturePaddingsList = new ArrayList<String>();
122            // Keymaster stores both types of paddings in the same array -- we split it into two.
123            for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) {
124                try {
125                    @KeyProperties.EncryptionPaddingEnum String jcaPadding =
126                            KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
127                    encryptionPaddingsList.add(jcaPadding);
128                } catch (IllegalArgumentException e) {
129                    try {
130                        @KeyProperties.SignaturePaddingEnum String padding =
131                                KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding);
132                        signaturePaddingsList.add(padding);
133                    } catch (IllegalArgumentException e2) {
134                        throw new ProviderException(
135                                "Unsupported encryption padding: " + keymasterPadding);
136                    }
137                }
138
139            }
140            encryptionPaddings =
141                    encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
142            signaturePaddings =
143                    signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);
144
145            digests = KeyProperties.Digest.allFromKeymaster(
146                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST));
147            blockModes = KeyProperties.BlockMode.allFromKeymaster(
148                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE));
149            keymasterSwEnforcedUserAuthenticators =
150                    keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
151            keymasterHwEnforcedUserAuthenticators =
152                    keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
153            keymasterSecureUserIds =
154                keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID);
155        } catch (IllegalArgumentException e) {
156            throw new ProviderException("Unsupported key characteristic", e);
157        }
158
159        Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
160        Date keyValidityForOriginationEnd =
161                keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
162        Date keyValidityForConsumptionEnd =
163                keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
164        boolean userAuthenticationRequired =
165                !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
166        long userAuthenticationValidityDurationSeconds =
167                keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1);
168        if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
169            throw new ProviderException("User authentication timeout validity too long: "
170                    + userAuthenticationValidityDurationSeconds + " seconds");
171        }
172        boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
173                && (keymasterHwEnforcedUserAuthenticators != 0)
174                && (keymasterSwEnforcedUserAuthenticators == 0);
175        boolean userAuthenticationValidWhileOnBody =
176                keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
177
178        boolean invalidatedByBiometricEnrollment = false;
179        if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT
180            || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT) {
181            // Fingerprint-only key; will be invalidated if the root SID isn't in the SID list.
182            invalidatedByBiometricEnrollment = keymasterSecureUserIds != null
183                    && !keymasterSecureUserIds.isEmpty()
184                    && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
185        }
186
187        return new KeyInfo(entryAlias,
188                insideSecureHardware,
189                origin,
190                keySize,
191                keyValidityStart,
192                keyValidityForOriginationEnd,
193                keyValidityForConsumptionEnd,
194                purposes,
195                encryptionPaddings,
196                signaturePaddings,
197                digests,
198                blockModes,
199                userAuthenticationRequired,
200                (int) userAuthenticationValidityDurationSeconds,
201                userAuthenticationRequirementEnforcedBySecureHardware,
202                userAuthenticationValidWhileOnBody,
203                invalidatedByBiometricEnrollment);
204    }
205
206    private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
207    	try {
208    		return BigInteger.valueOf(GateKeeper.getSecureUserId());
209    	} catch (IllegalStateException e) {
210    		throw new ProviderException("Failed to get GateKeeper secure user ID", e);
211    	}
212    }
213
214    @Override
215    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
216        throw new InvalidKeySpecException(
217                "To generate secret key in Android Keystore, use KeyGenerator initialized with "
218                        + KeyGenParameterSpec.class.getName());
219    }
220
221    @Override
222    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
223        if (key == null) {
224            throw new InvalidKeyException("key == null");
225        } else if (!(key instanceof AndroidKeyStoreSecretKey)) {
226            throw new InvalidKeyException(
227                    "To import a secret key into Android Keystore, use KeyStore.setEntry");
228        }
229
230        return key;
231    }
232}
233