1 /* 2 * Copyright (C) 2021 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.keystore2; 18 19 import android.security.keymaster.KeymasterArguments; 20 import android.security.keymaster.KeymasterDefs; 21 import android.security.keystore.KeyProperties; 22 23 import java.security.AlgorithmParameters; 24 import java.security.NoSuchAlgorithmException; 25 import java.security.ProviderException; 26 import java.security.spec.ECGenParameterSpec; 27 import java.security.spec.ECParameterSpec; 28 import java.security.spec.InvalidParameterSpecException; 29 30 /** 31 * @hide 32 */ 33 public abstract class KeymasterUtils { 34 KeymasterUtils()35 private KeymasterUtils() {} 36 37 /** @hide */ getDigestOutputSizeBits(int keymasterDigest)38 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 59 /** @hide */ isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( int keymasterBlockMode)60 static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( 61 int keymasterBlockMode) { 62 switch (keymasterBlockMode) { 63 case KeymasterDefs.KM_MODE_ECB: 64 return false; 65 case KeymasterDefs.KM_MODE_CBC: 66 case KeymasterDefs.KM_MODE_CTR: 67 case KeymasterDefs.KM_MODE_GCM: 68 return true; 69 default: 70 throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode); 71 } 72 } 73 74 /** @hide */ isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( int keymasterPadding)75 static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( 76 int keymasterPadding) { 77 switch (keymasterPadding) { 78 case KeymasterDefs.KM_PAD_NONE: 79 return false; 80 case KeymasterDefs.KM_PAD_RSA_OAEP: 81 case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT: 82 return true; 83 default: 84 throw new IllegalArgumentException( 85 "Unsupported asymmetric encryption padding scheme: " + keymasterPadding); 86 } 87 } 88 89 /** 90 * Adds {@code KM_TAG_MIN_MAC_LENGTH} tag, if necessary, to the keymaster arguments for 91 * generating or importing a key. This tag may only be needed for symmetric keys (e.g., HMAC, 92 * AES-GCM). 93 */ addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, int keymasterAlgorithm, int[] keymasterBlockModes, int[] keymasterDigests)94 public static void addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, 95 int keymasterAlgorithm, 96 int[] keymasterBlockModes, 97 int[] keymasterDigests) { 98 switch (keymasterAlgorithm) { 99 case KeymasterDefs.KM_ALGORITHM_AES: 100 if (com.android.internal.util.ArrayUtils.contains( 101 keymasterBlockModes, KeymasterDefs.KM_MODE_GCM)) { 102 // AES GCM key needs the minimum length of AEAD tag specified. 103 args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, 104 AndroidKeyStoreAuthenticatedAESCipherSpi.GCM 105 .MIN_SUPPORTED_TAG_LENGTH_BITS); 106 } 107 break; 108 case KeymasterDefs.KM_ALGORITHM_HMAC: 109 // HMAC key needs the minimum length of MAC set to the output size of the associated 110 // digest. This is because we do not offer a way to generate shorter MACs and 111 // don't offer a way to verify MACs (other than by generating them). 112 if (keymasterDigests.length != 1) { 113 throw new ProviderException( 114 "Unsupported number of authorized digests for HMAC key: " 115 + keymasterDigests.length 116 + ". Exactly one digest must be authorized"); 117 } 118 int keymasterDigest = keymasterDigests[0]; 119 int digestOutputSizeBits = getDigestOutputSizeBits(keymasterDigest); 120 if (digestOutputSizeBits == -1) { 121 throw new ProviderException( 122 "HMAC key authorized for unsupported digest: " 123 + KeyProperties.Digest.fromKeymaster(keymasterDigest)); 124 } 125 args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits); 126 break; 127 } 128 } 129 getEcCurveFromKeymaster(int ecCurve)130 static String getEcCurveFromKeymaster(int ecCurve) { 131 switch (ecCurve) { 132 case android.hardware.security.keymint.EcCurve.P_224: 133 return "secp224r1"; 134 case android.hardware.security.keymint.EcCurve.P_256: 135 return "secp256r1"; 136 case android.hardware.security.keymint.EcCurve.P_384: 137 return "secp384r1"; 138 case android.hardware.security.keymint.EcCurve.P_521: 139 return "secp521r1"; 140 } 141 return ""; 142 } 143 getKeymasterEcCurve(String ecCurveName)144 static int getKeymasterEcCurve(String ecCurveName) { 145 if (ecCurveName.equals("secp224r1")) { 146 return android.hardware.security.keymint.EcCurve.P_224; 147 } else if (ecCurveName.equals("secp256r1")) { 148 return android.hardware.security.keymint.EcCurve.P_256; 149 } else if (ecCurveName.equals("secp384r1")) { 150 return android.hardware.security.keymint.EcCurve.P_384; 151 } else if (ecCurveName.equals("secp521r1")) { 152 return android.hardware.security.keymint.EcCurve.P_521; 153 } 154 return -1; 155 } 156 getCurveSpec(String name)157 static ECParameterSpec getCurveSpec(String name) 158 throws NoSuchAlgorithmException, InvalidParameterSpecException { 159 AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC"); 160 parameters.init(new ECGenParameterSpec(name)); 161 return parameters.getParameterSpec(ECParameterSpec.class); 162 } 163 getCurveName(ECParameterSpec spec)164 static String getCurveName(ECParameterSpec spec) { 165 if (KeymasterUtils.isECParameterSpecOfCurve(spec, "secp224r1")) { 166 return "secp224r1"; 167 } else if (KeymasterUtils.isECParameterSpecOfCurve(spec, "secp256r1")) { 168 return "secp256r1"; 169 } else if (KeymasterUtils.isECParameterSpecOfCurve(spec, "secp384r1")) { 170 return "secp384r1"; 171 } else if (KeymasterUtils.isECParameterSpecOfCurve(spec, "secp521r1")) { 172 return "secp521r1"; 173 } 174 return null; 175 } 176 isECParameterSpecOfCurve(ECParameterSpec spec, String curveName)177 private static boolean isECParameterSpecOfCurve(ECParameterSpec spec, String curveName) { 178 try { 179 ECParameterSpec curveSpec = KeymasterUtils.getCurveSpec(curveName); 180 if (curveSpec.getCurve().equals(spec.getCurve()) 181 && curveSpec.getOrder().equals(spec.getOrder()) 182 && curveSpec.getGenerator().equals(spec.getGenerator())) { 183 return true; 184 } 185 } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { 186 return false; 187 } 188 return false; 189 } 190 } 191