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 * 93 * @throws IllegalStateException if user authentication is required but the system is in a wrong 94 * state (e.g., secure lock screen not set up) for generating or importing keys that 95 * require user authentication. 96 */ addUserAuthArgs(KeymasterArguments args, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment)97 public static void addUserAuthArgs(KeymasterArguments args, 98 boolean userAuthenticationRequired, 99 int userAuthenticationValidityDurationSeconds, 100 boolean userAuthenticationValidWhileOnBody, 101 boolean invalidatedByBiometricEnrollment) { 102 if (!userAuthenticationRequired) { 103 args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); 104 return; 105 } 106 107 if (userAuthenticationValidityDurationSeconds == -1) { 108 // Every use of this key needs to be authorized by the user. This currently means 109 // fingerprint-only auth. 110 FingerprintManager fingerprintManager = 111 KeyStore.getApplicationContext().getSystemService(FingerprintManager.class); 112 // TODO: Restore USE_FINGERPRINT permission check in 113 // FingerprintManager.getAuthenticatorId once the ID is no longer needed here. 114 long fingerprintOnlySid = 115 (fingerprintManager != null) ? fingerprintManager.getAuthenticatorId() : 0; 116 if (fingerprintOnlySid == 0) { 117 throw new IllegalStateException( 118 "At least one fingerprint must be enrolled to create keys requiring user" 119 + " authentication for every use"); 120 } 121 122 long sid; 123 if (invalidatedByBiometricEnrollment) { 124 // The fingerprint-only SID will change on fingerprint enrollment or removal of all, 125 // enrolled fingerprints, invalidating the key. 126 sid = fingerprintOnlySid; 127 } else { 128 // The root SID will *not* change on fingerprint enrollment, or removal of all 129 // enrolled fingerprints, allowing the key to remain valid. 130 sid = getRootSid(); 131 } 132 133 args.addUnsignedLong( 134 KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid)); 135 args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT); 136 if (userAuthenticationValidWhileOnBody) { 137 throw new ProviderException("Key validity extension while device is on-body is not " 138 + "supported for keys requiring fingerprint authentication"); 139 } 140 } else { 141 // The key is authorized for use for the specified amount of time after the user has 142 // authenticated. Whatever unlocks the secure lock screen should authorize this key. 143 long rootSid = getRootSid(); 144 args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, 145 KeymasterArguments.toUint64(rootSid)); 146 args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 147 KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT); 148 args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, 149 userAuthenticationValidityDurationSeconds); 150 if (userAuthenticationValidWhileOnBody) { 151 args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY); 152 } 153 } 154 } 155 156 /** 157 * Adds {@code KM_TAG_MIN_MAC_LENGTH} tag, if necessary, to the keymaster arguments for 158 * generating or importing a key. This tag may only be needed for symmetric keys (e.g., HMAC, 159 * AES-GCM). 160 */ addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, int keymasterAlgorithm, int[] keymasterBlockModes, int[] keymasterDigests)161 public static void addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, 162 int keymasterAlgorithm, 163 int[] keymasterBlockModes, 164 int[] keymasterDigests) { 165 switch (keymasterAlgorithm) { 166 case KeymasterDefs.KM_ALGORITHM_AES: 167 if (com.android.internal.util.ArrayUtils.contains( 168 keymasterBlockModes, KeymasterDefs.KM_MODE_GCM)) { 169 // AES GCM key needs the minimum length of AEAD tag specified. 170 args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, 171 AndroidKeyStoreAuthenticatedAESCipherSpi.GCM 172 .MIN_SUPPORTED_TAG_LENGTH_BITS); 173 } 174 break; 175 case KeymasterDefs.KM_ALGORITHM_HMAC: 176 // HMAC key needs the minimum length of MAC set to the output size of the associated 177 // digest. This is because we do not offer a way to generate shorter MACs and 178 // don't offer a way to verify MACs (other than by generating them). 179 if (keymasterDigests.length != 1) { 180 throw new ProviderException( 181 "Unsupported number of authorized digests for HMAC key: " 182 + keymasterDigests.length 183 + ". Exactly one digest must be authorized"); 184 } 185 int keymasterDigest = keymasterDigests[0]; 186 int digestOutputSizeBits = getDigestOutputSizeBits(keymasterDigest); 187 if (digestOutputSizeBits == -1) { 188 throw new ProviderException( 189 "HMAC key authorized for unsupported digest: " 190 + KeyProperties.Digest.fromKeymaster(keymasterDigest)); 191 } 192 args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits); 193 break; 194 } 195 } 196 getRootSid()197 private static long getRootSid() { 198 long rootSid = GateKeeper.getSecureUserId(); 199 if (rootSid == 0) { 200 throw new IllegalStateException("Secure lock screen must be enabled" 201 + " to create keys requiring user authentication"); 202 } 203 return rootSid; 204 } 205 } 206