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_PRIVATE_KEY)) {
68            entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
69        } else if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)){
70            // key has legacy prefix
71            entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
72        } else {
73            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
74        }
75
76        return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid());
77    }
78
79    static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore,
80            int keyUid) {
81        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
82        int errorCode = keyStore.getKeyCharacteristics(
83                keyAliasInKeystore, null, null, keyUid, keyCharacteristics);
84        if (errorCode != KeyStore.NO_ERROR) {
85            throw new ProviderException("Failed to obtain information about key."
86                    + " Keystore error: " + errorCode);
87        }
88
89        boolean insideSecureHardware;
90        @KeyProperties.OriginEnum int origin;
91        int keySize;
92        @KeyProperties.PurposeEnum int purposes;
93        String[] encryptionPaddings;
94        String[] signaturePaddings;
95        @KeyProperties.DigestEnum String[] digests;
96        @KeyProperties.BlockModeEnum String[] blockModes;
97        int keymasterSwEnforcedUserAuthenticators;
98        int keymasterHwEnforcedUserAuthenticators;
99        List<BigInteger> keymasterSecureUserIds;
100        try {
101            if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
102                insideSecureHardware = true;
103                origin = KeyProperties.Origin.fromKeymaster(
104                        keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
105            } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
106                insideSecureHardware = false;
107                origin = KeyProperties.Origin.fromKeymaster(
108                        keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
109            } else {
110                throw new ProviderException("Key origin not available");
111            }
112            long keySizeUnsigned =
113                    keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
114            if (keySizeUnsigned == -1) {
115                throw new ProviderException("Key size not available");
116            } else if (keySizeUnsigned > Integer.MAX_VALUE) {
117                throw new ProviderException("Key too large: " + keySizeUnsigned + " bits");
118            }
119            keySize = (int) keySizeUnsigned;
120            purposes = KeyProperties.Purpose.allFromKeymaster(
121                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE));
122
123            List<String> encryptionPaddingsList = new ArrayList<String>();
124            List<String> signaturePaddingsList = new ArrayList<String>();
125            // Keymaster stores both types of paddings in the same array -- we split it into two.
126            for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) {
127                try {
128                    @KeyProperties.EncryptionPaddingEnum String jcaPadding =
129                            KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
130                    encryptionPaddingsList.add(jcaPadding);
131                } catch (IllegalArgumentException e) {
132                    try {
133                        @KeyProperties.SignaturePaddingEnum String padding =
134                                KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding);
135                        signaturePaddingsList.add(padding);
136                    } catch (IllegalArgumentException e2) {
137                        throw new ProviderException(
138                                "Unsupported encryption padding: " + keymasterPadding);
139                    }
140                }
141
142            }
143            encryptionPaddings =
144                    encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
145            signaturePaddings =
146                    signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);
147
148            digests = KeyProperties.Digest.allFromKeymaster(
149                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST));
150            blockModes = KeyProperties.BlockMode.allFromKeymaster(
151                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE));
152            keymasterSwEnforcedUserAuthenticators =
153                    keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
154            keymasterHwEnforcedUserAuthenticators =
155                    keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
156            keymasterSecureUserIds =
157                keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID);
158        } catch (IllegalArgumentException e) {
159            throw new ProviderException("Unsupported key characteristic", e);
160        }
161
162        Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
163        Date keyValidityForOriginationEnd =
164                keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
165        Date keyValidityForConsumptionEnd =
166                keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
167        boolean userAuthenticationRequired =
168                !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
169        long userAuthenticationValidityDurationSeconds =
170                keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1);
171        if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
172            throw new ProviderException("User authentication timeout validity too long: "
173                    + userAuthenticationValidityDurationSeconds + " seconds");
174        }
175        boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
176                && (keymasterHwEnforcedUserAuthenticators != 0)
177                && (keymasterSwEnforcedUserAuthenticators == 0);
178        boolean userAuthenticationValidWhileOnBody =
179                keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
180        boolean trustedUserPresenceRequred =
181                keyCharacteristics.hwEnforced.getBoolean(
182                    KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
183
184        boolean invalidatedByBiometricEnrollment = false;
185        if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT
186            || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT) {
187            // Fingerprint-only key; will be invalidated if the root SID isn't in the SID list.
188            invalidatedByBiometricEnrollment = keymasterSecureUserIds != null
189                    && !keymasterSecureUserIds.isEmpty()
190                    && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
191        }
192
193        boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
194
195        return new KeyInfo(entryAlias,
196                insideSecureHardware,
197                origin,
198                keySize,
199                keyValidityStart,
200                keyValidityForOriginationEnd,
201                keyValidityForConsumptionEnd,
202                purposes,
203                encryptionPaddings,
204                signaturePaddings,
205                digests,
206                blockModes,
207                userAuthenticationRequired,
208                (int) userAuthenticationValidityDurationSeconds,
209                userAuthenticationRequirementEnforcedBySecureHardware,
210                userAuthenticationValidWhileOnBody,
211                trustedUserPresenceRequred,
212                invalidatedByBiometricEnrollment,
213                userConfirmationRequired);
214    }
215
216    private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
217    	try {
218    		return BigInteger.valueOf(GateKeeper.getSecureUserId());
219    	} catch (IllegalStateException e) {
220    		throw new ProviderException("Failed to get GateKeeper secure user ID", e);
221    	}
222    }
223
224    @Override
225    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
226        throw new InvalidKeySpecException(
227                "To generate secret key in Android Keystore, use KeyGenerator initialized with "
228                        + KeyGenParameterSpec.class.getName());
229    }
230
231    @Override
232    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
233        if (key == null) {
234            throw new InvalidKeyException("key == null");
235        } else if (!(key instanceof AndroidKeyStoreSecretKey)) {
236            throw new InvalidKeyException(
237                    "To import a secret key into Android Keystore, use KeyStore.setEntry");
238        }
239
240        return key;
241    }
242}
243