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