blob: 3ccb588951f61f1c1552db9f2bba15e40e7af8fc [file] [log] [blame]
Alex Klyubincc21bb32015-03-31 16:50:37 -07001/*
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
Alex Klyubind23a1f72015-03-27 14:39:28 -070017package android.security;
18
Alex Klyubin1eda77a2015-04-28 14:21:01 -070019import android.content.Context;
20import android.hardware.fingerprint.FingerprintManager;
Alex Klyubin1eda77a2015-04-28 14:21:01 -070021import android.security.keymaster.KeymasterArguments;
Alex Klyubin5927c9f2015-04-10 13:28:03 -070022import android.security.keymaster.KeymasterDefs;
Alex Klyubind23a1f72015-03-27 14:39:28 -070023
Alex Klyubin5927c9f2015-04-10 13:28:03 -070024import libcore.util.EmptyArray;
25
26import java.util.Collection;
27import java.util.Locale;
Alex Klyubinacc835f2015-03-31 15:26:56 -070028
Alex Klyubind23a1f72015-03-27 14:39:28 -070029/**
30 * @hide
31 */
32public abstract class KeymasterUtils {
Alex Klyubin5927c9f2015-04-10 13:28:03 -070033
Alex Klyubind23a1f72015-03-27 14:39:28 -070034 private KeymasterUtils() {}
35
Alex Klyubin5927c9f2015-04-10 13:28:03 -070036 public static int getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) {
37 if ("AES".equalsIgnoreCase(jcaKeyAlgorithm)) {
38 return KeymasterDefs.KM_ALGORITHM_AES;
39 } else if (jcaKeyAlgorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
40 return KeymasterDefs.KM_ALGORITHM_HMAC;
Alex Klyubinacc835f2015-03-31 15:26:56 -070041 } else {
Alex Klyubin5927c9f2015-04-10 13:28:03 -070042 throw new IllegalArgumentException(
43 "Unsupported secret key algorithm: " + jcaKeyAlgorithm);
Alex Klyubinacc835f2015-03-31 15:26:56 -070044 }
45 }
46
Alex Klyubin5927c9f2015-04-10 13:28:03 -070047 public static String getJcaSecretKeyAlgorithm(int keymasterAlgorithm, int keymasterDigest) {
48 switch (keymasterAlgorithm) {
49 case KeymasterDefs.KM_ALGORITHM_AES:
50 if (keymasterDigest != -1) {
51 throw new IllegalArgumentException(
52 "Digest not supported for AES key: " + keymasterDigest);
53 }
54 return "AES";
55 case KeymasterDefs.KM_ALGORITHM_HMAC:
56 switch (keymasterDigest) {
57 case KeymasterDefs.KM_DIGEST_SHA1:
58 return "HmacSHA1";
59 case KeymasterDefs.KM_DIGEST_SHA_2_224:
60 return "HmacSHA224";
61 case KeymasterDefs.KM_DIGEST_SHA_2_256:
62 return "HmacSHA256";
63 case KeymasterDefs.KM_DIGEST_SHA_2_384:
64 return "HmacSHA384";
65 case KeymasterDefs.KM_DIGEST_SHA_2_512:
66 return "HmacSHA512";
67 default:
68 throw new IllegalArgumentException(
69 "Unsupported HMAC digest: " + keymasterDigest);
70 }
71 default:
72 throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm);
73 }
Alex Klyubinacc835f2015-03-31 15:26:56 -070074 }
75
Alex Klyubin5927c9f2015-04-10 13:28:03 -070076 public static String getJcaKeyPairAlgorithmFromKeymasterAlgorithm(int keymasterAlgorithm) {
77 switch (keymasterAlgorithm) {
78 case KeymasterDefs.KM_ALGORITHM_RSA:
79 return "RSA";
80 case KeymasterDefs.KM_ALGORITHM_EC:
81 return "EC";
82 default:
83 throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm);
84 }
85 }
86
87 public static int getKeymasterDigestfromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) {
88 String algorithmUpper = jcaKeyAlgorithm.toUpperCase(Locale.US);
89 if (algorithmUpper.startsWith("HMAC")) {
90 String digestUpper = algorithmUpper.substring("HMAC".length());
91 switch (digestUpper) {
92 case "MD5":
93 return KeymasterDefs.KM_DIGEST_MD5;
94 case "SHA1":
95 return KeymasterDefs.KM_DIGEST_SHA1;
96 case "SHA224":
97 return KeymasterDefs.KM_DIGEST_SHA_2_224;
98 case "SHA256":
99 return KeymasterDefs.KM_DIGEST_SHA_2_256;
100 case "SHA384":
101 return KeymasterDefs.KM_DIGEST_SHA_2_384;
102 case "SHA512":
103 return KeymasterDefs.KM_DIGEST_SHA_2_512;
104 default:
105 throw new IllegalArgumentException("Unsupported HMAC digest: " + digestUpper);
106 }
107 } else {
108 return -1;
109 }
110 }
111
112 public static int getKeymasterDigestFromJcaDigestAlgorithm(String jcaDigestAlgorithm) {
113 if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-1")) {
114 return KeymasterDefs.KM_DIGEST_SHA1;
115 } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-224")) {
116 return KeymasterDefs.KM_DIGEST_SHA_2_224;
117 } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-256")) {
118 return KeymasterDefs.KM_DIGEST_SHA_2_256;
119 } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-384")) {
120 return KeymasterDefs.KM_DIGEST_SHA_2_384;
121 } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-512")) {
122 return KeymasterDefs.KM_DIGEST_SHA_2_512;
123 } else if (jcaDigestAlgorithm.equalsIgnoreCase("NONE")) {
124 return KeymasterDefs.KM_DIGEST_NONE;
125 } else if (jcaDigestAlgorithm.equalsIgnoreCase("MD5")) {
126 return KeymasterDefs.KM_DIGEST_MD5;
127 } else {
128 throw new IllegalArgumentException(
129 "Unsupported digest algorithm: " + jcaDigestAlgorithm);
130 }
131 }
132
133 public static String getJcaDigestAlgorithmFromKeymasterDigest(int keymasterDigest) {
134 switch (keymasterDigest) {
135 case KeymasterDefs.KM_DIGEST_NONE:
136 return "NONE";
137 case KeymasterDefs.KM_DIGEST_MD5:
138 return "MD5";
139 case KeymasterDefs.KM_DIGEST_SHA1:
140 return "SHA-1";
141 case KeymasterDefs.KM_DIGEST_SHA_2_224:
142 return "SHA-224";
143 case KeymasterDefs.KM_DIGEST_SHA_2_256:
144 return "SHA-256";
145 case KeymasterDefs.KM_DIGEST_SHA_2_384:
146 return "SHA-384";
147 case KeymasterDefs.KM_DIGEST_SHA_2_512:
148 return "SHA-512";
149 default:
150 throw new IllegalArgumentException(
151 "Unsupported digest algorithm: " + keymasterDigest);
152 }
153 }
154
155 public static String[] getJcaDigestAlgorithmsFromKeymasterDigests(
156 Collection<Integer> keymasterDigests) {
157 if (keymasterDigests.isEmpty()) {
158 return EmptyArray.STRING;
159 }
160 String[] result = new String[keymasterDigests.size()];
161 int offset = 0;
162 for (int keymasterDigest : keymasterDigests) {
163 result[offset] = getJcaDigestAlgorithmFromKeymasterDigest(keymasterDigest);
164 offset++;
Alex Klyubinacc835f2015-03-31 15:26:56 -0700165 }
166 return result;
167 }
168
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700169 public static int[] getKeymasterDigestsFromJcaDigestAlgorithms(String[] jcaDigestAlgorithms) {
170 if ((jcaDigestAlgorithms == null) || (jcaDigestAlgorithms.length == 0)) {
171 return EmptyArray.INT;
Alex Klyubinacc835f2015-03-31 15:26:56 -0700172 }
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700173 int[] result = new int[jcaDigestAlgorithms.length];
174 int offset = 0;
175 for (String jcaDigestAlgorithm : jcaDigestAlgorithms) {
176 result[offset] = getKeymasterDigestFromJcaDigestAlgorithm(jcaDigestAlgorithm);
177 offset++;
178 }
179 return result;
180 }
181
182 public static int getDigestOutputSizeBytes(int keymasterDigest) {
183 switch (keymasterDigest) {
184 case KeymasterDefs.KM_DIGEST_NONE:
185 return -1;
186 case KeymasterDefs.KM_DIGEST_MD5:
187 return 128 / 8;
188 case KeymasterDefs.KM_DIGEST_SHA1:
189 return 160 / 8;
190 case KeymasterDefs.KM_DIGEST_SHA_2_224:
191 return 224 / 8;
192 case KeymasterDefs.KM_DIGEST_SHA_2_256:
193 return 256 / 8;
194 case KeymasterDefs.KM_DIGEST_SHA_2_384:
195 return 384 / 8;
196 case KeymasterDefs.KM_DIGEST_SHA_2_512:
197 return 512 / 8;
198 default:
199 throw new IllegalArgumentException("Unknown digest: " + keymasterDigest);
200 }
201 }
202
203 public static int getKeymasterBlockModeFromJcaBlockMode(String jcaBlockMode) {
204 if ("ECB".equalsIgnoreCase(jcaBlockMode)) {
205 return KeymasterDefs.KM_MODE_ECB;
206 } else if ("CBC".equalsIgnoreCase(jcaBlockMode)) {
207 return KeymasterDefs.KM_MODE_CBC;
208 } else if ("CTR".equalsIgnoreCase(jcaBlockMode)) {
209 return KeymasterDefs.KM_MODE_CTR;
210 } else if ("GCM".equalsIgnoreCase(jcaBlockMode)) {
211 return KeymasterDefs.KM_MODE_GCM;
212 } else {
213 throw new IllegalArgumentException("Unsupported block mode: " + jcaBlockMode);
214 }
215 }
216
217 public static String getJcaBlockModeFromKeymasterBlockMode(int keymasterBlockMode) {
218 switch (keymasterBlockMode) {
219 case KeymasterDefs.KM_MODE_ECB:
220 return "ECB";
221 case KeymasterDefs.KM_MODE_CBC:
222 return "CBC";
223 case KeymasterDefs.KM_MODE_CTR:
224 return "CTR";
225 case KeymasterDefs.KM_MODE_GCM:
226 return "GCM";
227 default:
228 throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode);
229 }
230 }
231
232 public static String[] getJcaBlockModesFromKeymasterBlockModes(
233 Collection<Integer> keymasterBlockModes) {
234 if ((keymasterBlockModes == null) || (keymasterBlockModes.isEmpty())) {
235 return EmptyArray.STRING;
236 }
237 String[] result = new String[keymasterBlockModes.size()];
238 int offset = 0;
239 for (int keymasterBlockMode : keymasterBlockModes) {
240 result[offset] = getJcaBlockModeFromKeymasterBlockMode(keymasterBlockMode);
241 offset++;
242 }
243 return result;
244 }
245
246 public static int[] getKeymasterBlockModesFromJcaBlockModes(String[] jcaBlockModes) {
247 if ((jcaBlockModes == null) || (jcaBlockModes.length == 0)) {
248 return EmptyArray.INT;
249 }
250 int[] result = new int[jcaBlockModes.length];
251 for (int i = 0; i < jcaBlockModes.length; i++) {
252 result[i] = getKeymasterBlockModeFromJcaBlockMode(jcaBlockModes[i]);
253 }
254 return result;
255 }
256
257 public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) {
258 switch (keymasterBlockMode) {
259 case KeymasterDefs.KM_MODE_ECB:
260 return false;
261 case KeymasterDefs.KM_MODE_CBC:
262 case KeymasterDefs.KM_MODE_CTR:
263 case KeymasterDefs.KM_MODE_GCM:
264 return true;
265 default:
266 throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode);
267 }
268 }
269
270 public static int getKeymasterPaddingFromJcaEncryptionPadding(String jcaPadding) {
271 if ("NoPadding".equalsIgnoreCase(jcaPadding)) {
272 return KeymasterDefs.KM_PAD_NONE;
273 } else if ("PKCS7Padding".equalsIgnoreCase(jcaPadding)) {
274 return KeymasterDefs.KM_PAD_PKCS7;
275 } else if ("PKCS1Padding".equalsIgnoreCase(jcaPadding)) {
276 return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
277 } else if ("OEAPPadding".equalsIgnoreCase(jcaPadding)) {
278 return KeymasterDefs.KM_PAD_RSA_OAEP;
279 } else {
280 throw new IllegalArgumentException(
281 "Unsupported encryption padding scheme: " + jcaPadding);
282 }
283 }
284
285 public static String getJcaEncryptionPaddingFromKeymasterPadding(int keymasterPadding) {
286 switch (keymasterPadding) {
287 case KeymasterDefs.KM_PAD_NONE:
288 return "NoPadding";
289 case KeymasterDefs.KM_PAD_PKCS7:
290 return "PKCS7Padding";
291 case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
292 return "PKCS1Padding";
293 case KeymasterDefs.KM_PAD_RSA_OAEP:
294 return "OEAPPadding";
295 default:
296 throw new IllegalArgumentException(
297 "Unsupported encryption padding: " + keymasterPadding);
298 }
299 }
300
301 public static int getKeymasterPaddingFromJcaSignaturePadding(String jcaPadding) {
302 if ("PKCS#1".equalsIgnoreCase(jcaPadding)) {
303 return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
304 } if ("PSS".equalsIgnoreCase(jcaPadding)) {
305 return KeymasterDefs.KM_PAD_RSA_PSS;
306 } else {
307 throw new IllegalArgumentException(
308 "Unsupported signature padding scheme: " + jcaPadding);
309 }
310 }
311
312 public static String getJcaSignaturePaddingFromKeymasterPadding(int keymasterPadding) {
313 switch (keymasterPadding) {
314 case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
315 return "PKCS#1";
316 case KeymasterDefs.KM_PAD_RSA_PSS:
317 return "PSS";
318 default:
319 throw new IllegalArgumentException(
320 "Unsupported signature padding: " + keymasterPadding);
321 }
322 }
323
324 public static int[] getKeymasterPaddingsFromJcaEncryptionPaddings(String[] jcaPaddings) {
325 if ((jcaPaddings == null) || (jcaPaddings.length == 0)) {
326 return EmptyArray.INT;
327 }
328 int[] result = new int[jcaPaddings.length];
329 for (int i = 0; i < jcaPaddings.length; i++) {
330 result[i] = getKeymasterPaddingFromJcaEncryptionPadding(jcaPaddings[i]);
331 }
332 return result;
333 }
334
335 public static int[] getKeymasterPaddingsFromJcaSignaturePaddings(String[] jcaPaddings) {
336 if ((jcaPaddings == null) || (jcaPaddings.length == 0)) {
337 return EmptyArray.INT;
338 }
339 int[] result = new int[jcaPaddings.length];
340 for (int i = 0; i < jcaPaddings.length; i++) {
341 result[i] = getKeymasterPaddingFromJcaSignaturePadding(jcaPaddings[i]);
342 }
343 return result;
Alex Klyubinacc835f2015-03-31 15:26:56 -0700344 }
Alex Klyubin1eda77a2015-04-28 14:21:01 -0700345
Alex Klyubin1eda77a2015-04-28 14:21:01 -0700346 /**
347 * Adds keymaster arguments to express the key's authorization policy supported by user
348 * authentication.
349 *
350 * @param userAuthenticationRequired whether user authentication is required to authorize the
351 * use of the key.
352 * @param userAuthenticationValidityDurationSeconds duration of time (seconds) for which user
353 * authentication is valid as authorization for using the key or {@code -1} if every
354 * use of the key needs authorization.
355 */
356 public static void addUserAuthArgs(KeymasterArguments args,
357 Context context,
358 boolean userAuthenticationRequired,
359 int userAuthenticationValidityDurationSeconds) {
360 if (!userAuthenticationRequired) {
361 args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
362 return;
363 }
364
365 if (userAuthenticationValidityDurationSeconds == -1) {
366 // Every use of this key needs to be authorized by the user. This currently means
367 // fingerprint-only auth.
368 FingerprintManager fingerprintManager =
369 context.getSystemService(FingerprintManager.class);
370 if ((fingerprintManager == null) || (!fingerprintManager.isHardwareDetected())) {
371 throw new IllegalStateException(
372 "This device does not support keys which require authentication for every"
373 + " use -- this requires fingerprint authentication which is not"
374 + " available on this device");
375 }
376 long fingerprintOnlySid = fingerprintManager.getAuthenticatorId();
377 if (fingerprintOnlySid == 0) {
378 throw new IllegalStateException(
379 "At least one fingerprint must be enrolled to create keys requiring user"
380 + " authentication for every use");
381 }
382 args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, fingerprintOnlySid);
383 args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
384 } else {
385 // The key is authorized for use for the specified amount of time after the user has
386 // authenticated. Whatever unlocks the secure lock screen should authorize this key.
Alex Klyubin708fc9402015-04-28 18:58:47 -0700387 long rootSid = GateKeeper.getSecureUserId();
Alex Klyubin1eda77a2015-04-28 14:21:01 -0700388 if (rootSid == 0) {
389 throw new IllegalStateException("Secure lock screen must be enabled"
390 + " to create keys requiring user authentication");
391 }
392 args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, rootSid);
393 args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
394 KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
395 args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
396 userAuthenticationValidityDurationSeconds);
397 }
398 }
Alex Klyubind23a1f72015-03-27 14:39:28 -0700399}