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