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