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.hardware.fingerprint.FingerprintManager;
20 import android.security.GateKeeper;
21 import android.security.KeyStore;
22 import android.security.keymaster.KeymasterArguments;
23 import android.security.keymaster.KeymasterDefs;
24 
25 import java.security.ProviderException;
26 
27 /**
28  * @hide
29  */
30 public abstract class KeymasterUtils {
31 
KeymasterUtils()32     private KeymasterUtils() {}
33 
getDigestOutputSizeBits(int keymasterDigest)34     public static int getDigestOutputSizeBits(int keymasterDigest) {
35         switch (keymasterDigest) {
36             case KeymasterDefs.KM_DIGEST_NONE:
37                 return -1;
38             case KeymasterDefs.KM_DIGEST_MD5:
39                 return 128;
40             case KeymasterDefs.KM_DIGEST_SHA1:
41                 return 160;
42             case KeymasterDefs.KM_DIGEST_SHA_2_224:
43                 return 224;
44             case KeymasterDefs.KM_DIGEST_SHA_2_256:
45                 return 256;
46             case KeymasterDefs.KM_DIGEST_SHA_2_384:
47                 return 384;
48             case KeymasterDefs.KM_DIGEST_SHA_2_512:
49                 return 512;
50             default:
51                 throw new IllegalArgumentException("Unknown digest: " + keymasterDigest);
52         }
53     }
54 
isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( int keymasterBlockMode)55     public static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
56             int keymasterBlockMode) {
57         switch (keymasterBlockMode) {
58             case KeymasterDefs.KM_MODE_ECB:
59                 return false;
60             case KeymasterDefs.KM_MODE_CBC:
61             case KeymasterDefs.KM_MODE_CTR:
62             case KeymasterDefs.KM_MODE_GCM:
63                 return true;
64             default:
65                 throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode);
66         }
67     }
68 
isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( int keymasterPadding)69     public static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
70             int keymasterPadding) {
71         switch (keymasterPadding) {
72             case KeymasterDefs.KM_PAD_NONE:
73                 return false;
74             case KeymasterDefs.KM_PAD_RSA_OAEP:
75             case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
76                 return true;
77             default:
78                 throw new IllegalArgumentException(
79                         "Unsupported asymmetric encryption padding scheme: " + keymasterPadding);
80         }
81     }
82 
83     /**
84      * Adds keymaster arguments to express the key's authorization policy supported by user
85      * authentication.
86      *
87      * @param userAuthenticationRequired whether user authentication is required to authorize the
88      *        use of the key.
89      * @param userAuthenticationValidityDurationSeconds duration of time (seconds) for which user
90      *        authentication is valid as authorization for using the key or {@code -1} if every
91      *        use of the key needs authorization.
92      * @param boundToSpecificSecureUserId if non-zero, specify which SID the key will be bound to,
93      *        overriding the default logic in this method where the key is bound to either the root
94      *        SID of the current user, or the fingerprint SID if explicit fingerprint authorization
95      *        is requested.
96      * @throws IllegalStateException if user authentication is required but the system is in a wrong
97      *         state (e.g., secure lock screen not set up) for generating or importing keys that
98      *         require user authentication.
99      */
addUserAuthArgs(KeymasterArguments args, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, long boundToSpecificSecureUserId)100     public static void addUserAuthArgs(KeymasterArguments args,
101             boolean userAuthenticationRequired,
102             int userAuthenticationValidityDurationSeconds,
103             boolean userAuthenticationValidWhileOnBody,
104             boolean invalidatedByBiometricEnrollment,
105             long boundToSpecificSecureUserId) {
106         if (!userAuthenticationRequired) {
107             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
108             return;
109         }
110 
111         if (userAuthenticationValidityDurationSeconds == -1) {
112             // Every use of this key needs to be authorized by the user. This currently means
113             // fingerprint-only auth.
114             FingerprintManager fingerprintManager =
115                     KeyStore.getApplicationContext().getSystemService(FingerprintManager.class);
116             // TODO: Restore USE_FINGERPRINT permission check in
117             // FingerprintManager.getAuthenticatorId once the ID is no longer needed here.
118             long fingerprintOnlySid =
119                     (fingerprintManager != null) ? fingerprintManager.getAuthenticatorId() : 0;
120             if (fingerprintOnlySid == 0) {
121                 throw new IllegalStateException(
122                         "At least one fingerprint must be enrolled to create keys requiring user"
123                         + " authentication for every use");
124             }
125 
126             long sid;
127             if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
128                 sid = boundToSpecificSecureUserId;
129             } else if (invalidatedByBiometricEnrollment) {
130                 // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
131                 // enrolled fingerprints, invalidating the key.
132                 sid = fingerprintOnlySid;
133             } else {
134                 // The root SID will *not* change on fingerprint enrollment, or removal of all
135                 // enrolled fingerprints, allowing the key to remain valid.
136                 sid = getRootSid();
137             }
138 
139             args.addUnsignedLong(
140                     KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
141             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
142             if (userAuthenticationValidWhileOnBody) {
143                 throw new ProviderException("Key validity extension while device is on-body is not "
144                         + "supported for keys requiring fingerprint authentication");
145             }
146         } else {
147             long sid;
148             if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
149                 sid = boundToSpecificSecureUserId;
150             } else {
151                 // The key is authorized for use for the specified amount of time after the user has
152                 // authenticated. Whatever unlocks the secure lock screen should authorize this key.
153                 sid = getRootSid();
154             }
155             args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
156                     KeymasterArguments.toUint64(sid));
157             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
158                     KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
159             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
160                     userAuthenticationValidityDurationSeconds);
161             if (userAuthenticationValidWhileOnBody) {
162                 args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
163             }
164         }
165     }
166 
167     /**
168      * Adds {@code KM_TAG_MIN_MAC_LENGTH} tag, if necessary, to the keymaster arguments for
169      * generating or importing a key. This tag may only be needed for symmetric keys (e.g., HMAC,
170      * AES-GCM).
171      */
addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, int keymasterAlgorithm, int[] keymasterBlockModes, int[] keymasterDigests)172     public static void addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args,
173             int keymasterAlgorithm,
174             int[] keymasterBlockModes,
175             int[] keymasterDigests) {
176         switch (keymasterAlgorithm) {
177             case KeymasterDefs.KM_ALGORITHM_AES:
178                 if (com.android.internal.util.ArrayUtils.contains(
179                         keymasterBlockModes, KeymasterDefs.KM_MODE_GCM)) {
180                     // AES GCM key needs the minimum length of AEAD tag specified.
181                     args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
182                             AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
183                                     .MIN_SUPPORTED_TAG_LENGTH_BITS);
184                 }
185                 break;
186             case KeymasterDefs.KM_ALGORITHM_HMAC:
187                 // HMAC key needs the minimum length of MAC set to the output size of the associated
188                 // digest. This is because we do not offer a way to generate shorter MACs and
189                 // don't offer a way to verify MACs (other than by generating them).
190                 if (keymasterDigests.length != 1) {
191                     throw new ProviderException(
192                             "Unsupported number of authorized digests for HMAC key: "
193                                     + keymasterDigests.length
194                                     + ". Exactly one digest must be authorized");
195                 }
196                 int keymasterDigest = keymasterDigests[0];
197                 int digestOutputSizeBits = getDigestOutputSizeBits(keymasterDigest);
198                 if (digestOutputSizeBits == -1) {
199                     throw new ProviderException(
200                             "HMAC key authorized for unsupported digest: "
201                                     + KeyProperties.Digest.fromKeymaster(keymasterDigest));
202                 }
203                 args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits);
204                 break;
205         }
206     }
207 
getRootSid()208     private static long getRootSid() {
209         long rootSid = GateKeeper.getSecureUserId();
210         if (rootSid == 0) {
211             throw new IllegalStateException("Secure lock screen must be enabled"
212                     + " to create keys requiring user authentication");
213         }
214         return rootSid;
215     }
216 }
217