• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.server;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.os.RemoteException;
21 import android.service.gatekeeper.GateKeeperResponse;
22 import android.service.gatekeeper.IGateKeeperService;
23 import android.util.ArrayMap;
24 import android.util.Log;
25 
26 import com.android.internal.util.ArrayUtils;
27 import com.android.internal.widget.LockPatternUtils;
28 import com.android.internal.widget.VerifyCredentialResponse;
29 
30 import libcore.util.HexEncoding;
31 
32 import java.nio.ByteBuffer;
33 import java.security.MessageDigest;
34 import java.security.NoSuchAlgorithmException;
35 import java.security.SecureRandom;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.Set;
39 
40 
41 /**
42  * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
43  * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
44  * synthetic password blobs which are wrapped by user credentials or escrow tokens.
45  *
46  * Here is the assumptions it makes:
47  *   Each user has one single synthetic password at any time.
48  *   The SP has an associated password handle, which binds to the SID for that user. The password
49  *   handle is persisted by SyntheticPasswordManager internally.
50  *   If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
51  *
52  * Information persisted on disk:
53  *   for each user (stored under DEFAULT_HANDLE):
54  *     SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
55  *                     credential exists, cleared when user clears their credential.
56  *     SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow
57  *                     tokens. Destroyed when escrow support is turned off for the given user.
58  *
59  *     for each SP blob under the user (stored under the corresponding handle):
60  *       SP_BLOB_NAME: The encrypted synthetic password. Always exists.
61  *       PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
62  *       SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the
63  *                            purpose of secure deletion.
64  *
65  */
66 public class SyntheticPasswordManager {
67     private static final String SP_BLOB_NAME = "spblob";
68     private static final String SP_E0_NAME = "e0";
69     private static final String SP_P1_NAME = "p1";
70     private static final String SP_HANDLE_NAME = "handle";
71     private static final String SECDISCARDABLE_NAME = "secdis";
72     private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
73     private static final String PASSWORD_DATA_NAME = "pwd";
74 
75     public static final long DEFAULT_HANDLE = 0;
76     private static final String DEFAULT_PASSWORD = "default-password";
77 
78     private static final byte SYNTHETIC_PASSWORD_VERSION = 1;
79     private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
80     private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
81 
82     // 256-bit synthetic password
83     private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
84 
85     private static final int PASSWORD_SCRYPT_N = 11;
86     private static final int PASSWORD_SCRYPT_R = 3;
87     private static final int PASSWORD_SCRYPT_P = 1;
88     private static final int PASSWORD_SALT_LENGTH = 16;
89     private static final int PASSWORD_TOKEN_LENGTH = 32;
90     private static final String TAG = "SyntheticPasswordManager";
91 
92     private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
93     private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
94     private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
95     private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
96     private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
97     private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
98     private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
99 
100     static class AuthenticationResult {
101         public AuthenticationToken authToken;
102         public VerifyCredentialResponse gkResponse;
103     }
104 
105     static class AuthenticationToken {
106         /*
107          * Here is the relationship between all three fields:
108          * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
109          * syntheticPassword = hash(P0 || P1)
110          * E0 = P0 encrypted under syntheticPassword, stored on disk.
111          */
112         private @Nullable byte[] E0;
113         private @Nullable byte[] P1;
114         private @NonNull String syntheticPassword;
115 
deriveKeyStorePassword()116         public String deriveKeyStorePassword() {
117             return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
118                     PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
119         }
120 
deriveGkPassword()121         public byte[] deriveGkPassword() {
122             return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
123                     syntheticPassword.getBytes());
124         }
125 
deriveDiskEncryptionKey()126         public byte[] deriveDiskEncryptionKey() {
127             return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
128                     syntheticPassword.getBytes());
129         }
130 
initialize(byte[] P0, byte[] P1)131         private void initialize(byte[] P0, byte[] P1) {
132             this.P1 = P1;
133             this.syntheticPassword = String.valueOf(HexEncoding.encode(
134                     SyntheticPasswordCrypto.personalisedHash(
135                             PERSONALIZATION_SP_SPLIT, P0, P1)));
136             this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
137                     PERSONALIZATION_E0, P0);
138         }
139 
recreate(byte[] secret)140         public void recreate(byte[] secret) {
141             initialize(secret, this.P1);
142         }
143 
create()144         protected static AuthenticationToken create() {
145             AuthenticationToken result = new AuthenticationToken();
146             result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
147                     secureRandom(SYNTHETIC_PASSWORD_LENGTH));
148             return result;
149         }
150 
computeP0()151         public byte[] computeP0() {
152             if (E0 == null) {
153                 return null;
154             }
155             return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
156                     E0);
157         }
158     }
159 
160     static class PasswordData {
161         byte scryptN;
162         byte scryptR;
163         byte scryptP;
164         public int passwordType;
165         byte[] salt;
166         public byte[] passwordHandle;
167 
create(int passwordType)168         public static PasswordData create(int passwordType) {
169             PasswordData result = new PasswordData();
170             result.scryptN = PASSWORD_SCRYPT_N;
171             result.scryptR = PASSWORD_SCRYPT_R;
172             result.scryptP = PASSWORD_SCRYPT_P;
173             result.passwordType = passwordType;
174             result.salt = secureRandom(PASSWORD_SALT_LENGTH);
175             return result;
176         }
177 
fromBytes(byte[] data)178         public static PasswordData fromBytes(byte[] data) {
179             PasswordData result = new PasswordData();
180             ByteBuffer buffer = ByteBuffer.allocate(data.length);
181             buffer.put(data, 0, data.length);
182             buffer.flip();
183             result.passwordType = buffer.getInt();
184             result.scryptN = buffer.get();
185             result.scryptR = buffer.get();
186             result.scryptP = buffer.get();
187             int saltLen = buffer.getInt();
188             result.salt = new byte[saltLen];
189             buffer.get(result.salt);
190             int handleLen = buffer.getInt();
191             result.passwordHandle = new byte[handleLen];
192             buffer.get(result.passwordHandle);
193             return result;
194         }
195 
toBytes()196         public byte[] toBytes() {
197             ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
198                     + Integer.BYTES + salt.length + Integer.BYTES + passwordHandle.length);
199             buffer.putInt(passwordType);
200             buffer.put(scryptN);
201             buffer.put(scryptR);
202             buffer.put(scryptP);
203             buffer.putInt(salt.length);
204             buffer.put(salt);
205             buffer.putInt(passwordHandle.length);
206             buffer.put(passwordHandle);
207             return buffer.array();
208         }
209     }
210 
211     private LockSettingsStorage mStorage;
212 
SyntheticPasswordManager(LockSettingsStorage storage)213     public SyntheticPasswordManager(LockSettingsStorage storage) {
214         mStorage = storage;
215     }
216 
217 
getCredentialType(long handle, int userId)218     public int getCredentialType(long handle, int userId) {
219         byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
220         if (passwordData == null) {
221             Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
222             return LockPatternUtils.CREDENTIAL_TYPE_NONE;
223         }
224         return PasswordData.fromBytes(passwordData).passwordType;
225     }
226 
227     /**
228      * Initializing a new Authentication token, possibly from an existing credential and hash.
229      *
230      * The authentication token would bear a randomly-generated synthetic password.
231      *
232      * This method has the side effect of rebinding the SID of the given user to the
233      * newly-generated SP.
234      *
235      * If the existing credential hash is non-null, the existing SID mill be migrated so
236      * the synthetic password in the authentication token will produce the same SID
237      * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
238      * in a per-user data storage.)
239      *
240      * If the existing credential hash is null, it means the given user should have no SID so
241      * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
242      * the supplied credential parameter is also ignored.
243      *
244      * Also saves the escrow information necessary to re-generate the synthetic password under
245      * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
246      * password escrow should be disabled completely on the given user.
247      *
248      */
newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, byte[] hash, String credential, int userId)249     public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
250             byte[] hash, String credential, int userId) throws RemoteException {
251         AuthenticationToken result = AuthenticationToken.create();
252         GateKeeperResponse response;
253         if (hash != null) {
254             response = gatekeeper.enroll(userId, hash, credential.getBytes(),
255                     result.deriveGkPassword());
256             if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
257                 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
258                 clearSidForUser(userId);
259             } else {
260                 saveSyntheticPasswordHandle(response.getPayload(), userId);
261             }
262         } else {
263             clearSidForUser(userId);
264         }
265         saveEscrowData(result, userId);
266         return result;
267     }
268 
269     /**
270      * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
271      * Used when adding password to previously-unsecured devices.
272      */
newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken, int userId)273     public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
274             int userId) throws RemoteException {
275         GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
276                 authToken.deriveGkPassword());
277         if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
278             Log.e(TAG, "Fail to create new SID for user " + userId);
279             return;
280         }
281         saveSyntheticPasswordHandle(response.getPayload(), userId);
282     }
283 
284     // Nuke the SP handle (and as a result, its SID) for the given user.
clearSidForUser(int userId)285     public void clearSidForUser(int userId) {
286         destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
287     }
288 
hasSidForUser(int userId)289     public boolean hasSidForUser(int userId) {
290         return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
291     }
292 
293     // if null, it means there is no SID associated with the user
294     // This can happen if the user is migrated to SP but currently
295     // do not have a lockscreen password.
loadSyntheticPasswordHandle(int userId)296     private byte[] loadSyntheticPasswordHandle(int userId) {
297         return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
298     }
299 
saveSyntheticPasswordHandle(byte[] spHandle, int userId)300     private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
301         saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
302     }
303 
loadEscrowData(AuthenticationToken authToken, int userId)304     private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
305         authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
306         authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
307         return authToken.E0 != null && authToken.P1 != null;
308     }
309 
saveEscrowData(AuthenticationToken authToken, int userId)310     private void saveEscrowData(AuthenticationToken authToken, int userId) {
311         saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
312         saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
313     }
314 
hasEscrowData(int userId)315     public boolean hasEscrowData(int userId) {
316         return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
317                 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
318     }
319 
destroyEscrowData(int userId)320     public void destroyEscrowData(int userId) {
321         destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
322         destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
323     }
324 
325     /**
326      * Create a new password based SP blob based on the supplied authentication token, such that
327      * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
328      * in the same authentication token.
329      *
330      * This method only creates SP blob wrapping around the given synthetic password and does not
331      * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
332      * is consistent with the device state by calling other APIs in this class.
333      *
334      * @see #newSidForUser
335      * @see #clearSidForUser
336      */
createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, String credential, int credentialType, AuthenticationToken authToken, int userId)337     public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
338             String credential, int credentialType, AuthenticationToken authToken, int userId)
339                     throws RemoteException {
340         if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
341             credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
342             credential = DEFAULT_PASSWORD;
343         }
344 
345         long handle = generateHandle();
346         PasswordData pwd = PasswordData.create(credentialType);
347         byte[] pwdToken = computePasswordToken(credential, pwd);
348 
349         // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
350         // to prevent them from accumulating and causing problems.
351         gatekeeper.clearSecureUserId(fakeUid(userId));
352         GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
353                 passwordTokenToGkInput(pwdToken));
354         if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
355             Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
356             return DEFAULT_HANDLE;
357         }
358         pwd.passwordHandle = response.getPayload();
359         long sid = sidFromPasswordHandle(pwd.passwordHandle);
360         saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
361 
362         byte[] applicationId = transformUnderSecdiscardable(pwdToken,
363                 createSecdiscardable(handle, userId));
364         createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
365                 applicationId, sid, userId);
366         return handle;
367     }
368 
369     private ArrayMap<Integer, ArrayMap<Long, byte[]>> tokenMap = new ArrayMap<>();
370 
createTokenBasedSyntheticPassword(byte[] token, int userId)371     public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
372         long handle = generateHandle();
373         byte[] applicationId = transformUnderSecdiscardable(token,
374                 createSecdiscardable(handle, userId));
375         if (!tokenMap.containsKey(userId)) {
376             tokenMap.put(userId, new ArrayMap<>());
377         }
378         tokenMap.get(userId).put(handle, applicationId);
379         return handle;
380     }
381 
getPendingTokensForUser(int userId)382     public Set<Long> getPendingTokensForUser(int userId) {
383         if (!tokenMap.containsKey(userId)) {
384             return Collections.emptySet();
385         }
386         return tokenMap.get(userId).keySet();
387     }
388 
removePendingToken(long handle, int userId)389     public boolean removePendingToken(long handle, int userId) {
390         if (!tokenMap.containsKey(userId)) {
391             return false;
392         }
393         return tokenMap.get(userId).remove(handle) != null;
394     }
395 
activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken, int userId)396     public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
397             int userId) {
398         if (!tokenMap.containsKey(userId)) {
399             return false;
400         }
401         byte[] applicationId = tokenMap.get(userId).get(handle);
402         if (applicationId == null) {
403             return false;
404         }
405         if (!loadEscrowData(authToken, userId)) {
406             Log.w(TAG, "User is not escrowable");
407             return false;
408         }
409         createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
410                 applicationId, 0L, userId);
411         tokenMap.get(userId).remove(handle);
412         return true;
413     }
414 
createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken, byte[] applicationId, long sid, int userId)415     private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
416             byte[] applicationId, long sid, int userId) {
417         final byte[] secret;
418         if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
419             secret = authToken.computeP0();
420         } else {
421             secret = authToken.syntheticPassword.getBytes();
422         }
423         byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
424         byte[] blob = new byte[content.length + 1 + 1];
425         blob[0] = SYNTHETIC_PASSWORD_VERSION;
426         blob[1] = type;
427         System.arraycopy(content, 0, blob, 2, content.length);
428         saveState(SP_BLOB_NAME, blob, handle, userId);
429     }
430 
431     /**
432      * Decrypt a synthetic password by supplying the user credential and corresponding password
433      * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
434      * verification to referesh the SID & Auth token maintained by the system.
435      */
unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, long handle, String credential, int userId)436     public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
437             long handle, String credential, int userId) throws RemoteException {
438         if (credential == null) {
439             credential = DEFAULT_PASSWORD;
440         }
441         AuthenticationResult result = new AuthenticationResult();
442         PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
443         byte[] pwdToken = computePasswordToken(credential, pwd);
444         byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
445 
446         GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
447                 pwd.passwordHandle, gkPwdToken);
448         int responseCode = response.getResponseCode();
449         if (responseCode == GateKeeperResponse.RESPONSE_OK) {
450             result.gkResponse = VerifyCredentialResponse.OK;
451             if (response.getShouldReEnroll()) {
452                 GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
453                         pwd.passwordHandle, gkPwdToken, gkPwdToken);
454                 if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
455                     pwd.passwordHandle = reenrollResponse.getPayload();
456                     saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
457                 } else {
458                     Log.w(TAG, "Fail to re-enroll user password for user " + userId);
459                     // continue the flow anyway
460                 }
461             }
462         } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
463             result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
464             return result;
465         } else  {
466             result.gkResponse = VerifyCredentialResponse.ERROR;
467             return result;
468         }
469 
470 
471         byte[] applicationId = transformUnderSecdiscardable(pwdToken,
472                 loadSecdiscardable(handle, userId));
473         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
474                 applicationId, userId);
475 
476         // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
477         result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
478         return result;
479     }
480 
481     /**
482      * Decrypt a synthetic password by supplying an escrow token and corresponding token
483      * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
484      * verification to referesh the SID & Auth token maintained by the system.
485      */
unwrapTokenBasedSyntheticPassword( IGateKeeperService gatekeeper, long handle, byte[] token, int userId)486     public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
487             IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
488                     throws RemoteException {
489         AuthenticationResult result = new AuthenticationResult();
490         byte[] applicationId = transformUnderSecdiscardable(token,
491                 loadSecdiscardable(handle, userId));
492         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
493                 applicationId, userId);
494         if (result.authToken != null) {
495             result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
496             if (result.gkResponse == null) {
497                 // The user currently has no password. return OK with null payload so null
498                 // is propagated to unlockUser()
499                 result.gkResponse = VerifyCredentialResponse.OK;
500             }
501         } else {
502             result.gkResponse = VerifyCredentialResponse.ERROR;
503         }
504         return result;
505     }
506 
unwrapSyntheticPasswordBlob(long handle, byte type, byte[] applicationId, int userId)507     private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
508             byte[] applicationId, int userId) {
509         byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
510         if (blob == null) {
511             return null;
512         }
513         if (blob[0] != SYNTHETIC_PASSWORD_VERSION) {
514             throw new RuntimeException("Unknown blob version");
515         }
516         if (blob[1] != type) {
517             throw new RuntimeException("Invalid blob type");
518         }
519         byte[] secret = decryptSPBlob(getHandleName(handle),
520                 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
521         if (secret == null) {
522             Log.e(TAG, "Fail to decrypt SP for user " + userId);
523             return null;
524         }
525         AuthenticationToken result = new AuthenticationToken();
526         if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
527             if (!loadEscrowData(result, userId)) {
528                 Log.e(TAG, "User is not escrowable: " + userId);
529                 return null;
530             }
531             result.recreate(secret);
532         } else {
533             result.syntheticPassword = new String(secret);
534         }
535         return result;
536     }
537 
538     /**
539      * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
540      * if required.
541      *
542      * Normally performing verifyChallenge with an AuthenticationToken should always return
543      * RESPONSE_OK, since user authentication failures are detected earlier when trying to
544      * decrypt SP.
545      */
verifyChallenge(IGateKeeperService gatekeeper, @NonNull AuthenticationToken auth, long challenge, int userId)546     public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
547             @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
548         byte[] spHandle = loadSyntheticPasswordHandle(userId);
549         if (spHandle == null) {
550             // There is no password handle associated with the given user, i.e. the user is not
551             // secured by lockscreen and has no SID, so just return here;
552             return null;
553         }
554         VerifyCredentialResponse result;
555         GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
556                 spHandle, auth.deriveGkPassword());
557         int responseCode = response.getResponseCode();
558         if (responseCode == GateKeeperResponse.RESPONSE_OK) {
559             result = new VerifyCredentialResponse(response.getPayload());
560             if (response.getShouldReEnroll()) {
561                 response = gatekeeper.enroll(userId, spHandle,
562                         spHandle, auth.deriveGkPassword());
563                 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
564                     spHandle = response.getPayload();
565                     saveSyntheticPasswordHandle(spHandle, userId);
566                     // Call self again to re-verify with updated handle
567                     return verifyChallenge(gatekeeper, auth, challenge, userId);
568                 } else {
569                     Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
570                     // Fall through, return existing handle
571                 }
572             }
573         } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
574             result = new VerifyCredentialResponse(response.getTimeout());
575         } else {
576             result = VerifyCredentialResponse.ERROR;
577         }
578         return result;
579     }
580 
existsHandle(long handle, int userId)581     public boolean existsHandle(long handle, int userId) {
582         return hasState(SP_BLOB_NAME, handle, userId);
583     }
584 
destroyTokenBasedSyntheticPassword(long handle, int userId)585     public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
586         destroySyntheticPassword(handle, userId);
587         destroyState(SECDISCARDABLE_NAME, handle, userId);
588     }
589 
destroyPasswordBasedSyntheticPassword(long handle, int userId)590     public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
591         destroySyntheticPassword(handle, userId);
592         destroyState(SECDISCARDABLE_NAME, handle, userId);
593         destroyState(PASSWORD_DATA_NAME, handle, userId);
594     }
595 
destroySyntheticPassword(long handle, int userId)596     private void destroySyntheticPassword(long handle, int userId) {
597         destroyState(SP_BLOB_NAME, handle, userId);
598         destroySPBlobKey(getHandleName(handle));
599     }
600 
transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable)601     private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
602         byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
603                 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
604         byte[] result = new byte[data.length + secdiscardable.length];
605         System.arraycopy(data, 0, result, 0, data.length);
606         System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
607         return result;
608     }
609 
createSecdiscardable(long handle, int userId)610     private byte[] createSecdiscardable(long handle, int userId) {
611         byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
612         saveState(SECDISCARDABLE_NAME, data, handle, userId);
613         return data;
614     }
615 
loadSecdiscardable(long handle, int userId)616     private byte[] loadSecdiscardable(long handle, int userId) {
617         return loadState(SECDISCARDABLE_NAME, handle, userId);
618     }
619 
hasState(String stateName, long handle, int userId)620     private boolean hasState(String stateName, long handle, int userId) {
621         return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
622     }
623 
loadState(String stateName, long handle, int userId)624     private byte[] loadState(String stateName, long handle, int userId) {
625         return mStorage.readSyntheticPasswordState(userId, handle, stateName);
626     }
627 
saveState(String stateName, byte[] data, long handle, int userId)628     private void saveState(String stateName, byte[] data, long handle, int userId) {
629         mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
630     }
631 
destroyState(String stateName, long handle, int userId)632     private void destroyState(String stateName, long handle, int userId) {
633         mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
634     }
635 
decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId)636     protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
637         return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
638     }
639 
createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid)640     protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
641         return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
642     }
643 
destroySPBlobKey(String keyAlias)644     protected void destroySPBlobKey(String keyAlias) {
645         SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
646     }
647 
generateHandle()648     public static long generateHandle() {
649         SecureRandom rng = new SecureRandom();
650         long result;
651         do {
652             result = rng.nextLong();
653         } while (result == DEFAULT_HANDLE);
654         return result;
655     }
656 
fakeUid(int uid)657     private int fakeUid(int uid) {
658         return 100000 + uid;
659     }
660 
secureRandom(int length)661     protected static byte[] secureRandom(int length) {
662         try {
663             return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
664         } catch (NoSuchAlgorithmException e) {
665             e.printStackTrace();
666             return null;
667         }
668     }
669 
getHandleName(long handle)670     private String getHandleName(long handle) {
671         return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
672     }
673 
computePasswordToken(String password, PasswordData data)674     private byte[] computePasswordToken(String password, PasswordData data) {
675         return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
676                 PASSWORD_TOKEN_LENGTH);
677     }
678 
passwordTokenToGkInput(byte[] token)679     private byte[] passwordTokenToGkInput(byte[] token) {
680         return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
681     }
682 
sidFromPasswordHandle(byte[] handle)683     protected long sidFromPasswordHandle(byte[] handle) {
684         return nativeSidFromPasswordHandle(handle);
685     }
686 
scrypt(String password, byte[] salt, int N, int r, int p, int outLen)687     protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
688         return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
689     }
690 
nativeSidFromPasswordHandle(byte[] handle)691     native long nativeSidFromPasswordHandle(byte[] handle);
nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen)692     native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
693 
694     final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
bytesToHex(byte[] bytes)695     public static String bytesToHex(byte[] bytes) {
696         if (bytes == null) {
697             return "null";
698         }
699         char[] hexChars = new char[bytes.length * 2];
700         for ( int j = 0; j < bytes.length; j++ ) {
701             int v = bytes[j] & 0xFF;
702             hexChars[j * 2] = hexArray[v >>> 4];
703             hexChars[j * 2 + 1] = hexArray[v & 0x0F];
704         }
705         return new String(hexChars);
706     }
707 }
708