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.hardware.fingerprint.FingerprintManager; 20import android.security.GateKeeper; 21import android.security.KeyStore; 22import android.security.keymaster.KeymasterArguments; 23import android.security.keymaster.KeymasterDefs; 24 25import java.security.ProviderException; 26 27/** 28 * @hide 29 */ 30public abstract class KeymasterUtils { 31 32 private KeymasterUtils() {} 33 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 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 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 */ 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 */ 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 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