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