OSDN Git Service

Integrate weaver into authentication flow
[android-x86/frameworks-base.git] / services / core / java / com / android / server / SyntheticPasswordManager.java
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.hardware.weaver.V1_0.IWeaver;
21 import android.hardware.weaver.V1_0.WeaverConfig;
22 import android.hardware.weaver.V1_0.WeaverReadResponse;
23 import android.hardware.weaver.V1_0.WeaverReadStatus;
24 import android.hardware.weaver.V1_0.WeaverStatus;
25 import android.os.RemoteException;
26 import android.security.GateKeeper;
27 import android.service.gatekeeper.GateKeeperResponse;
28 import android.service.gatekeeper.IGateKeeperService;
29 import android.util.ArrayMap;
30 import android.util.Log;
31 import android.util.Slog;
32
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.internal.util.ArrayUtils;
35 import com.android.internal.widget.LockPatternUtils;
36 import com.android.internal.widget.VerifyCredentialResponse;
37
38 import libcore.util.HexEncoding;
39
40 import java.nio.ByteBuffer;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.SecureRandom;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Collections;
46 import java.util.HashSet;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.NoSuchElementException;
50 import java.util.Set;
51
52
53 /**
54  * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
55  * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
56  * synthetic password blobs which are wrapped by user credentials or escrow tokens.
57  *
58  * Here is the assumptions it makes:
59  *   Each user has one single synthetic password at any time.
60  *   The SP has an associated password handle, which binds to the SID for that user. The password
61  *   handle is persisted by SyntheticPasswordManager internally.
62  *   If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
63  *
64  * Information persisted on disk:
65  *   for each user (stored under DEFAULT_HANDLE):
66  *     SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
67  *                     credential exists, cleared when user clears their credential.
68  *     SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combining with escrow
69  *                     tokens. Destroyed when escrow support is turned off for the given user.
70  *
71  *     for each SP blob under the user (stored under the corresponding handle):
72  *       SP_BLOB_NAME: The encrypted synthetic password. Always exists.
73  *       PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
74  *       SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME in order
75  *                            to facilitate secure deletion. Exists if this is a non-weaver SP
76  *                            (both password and token based), or it's a token-based SP under weaver.
77  *       WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver.
78  *
79  *
80  */
81 public class SyntheticPasswordManager {
82     private static final String SP_BLOB_NAME = "spblob";
83     private static final String SP_E0_NAME = "e0";
84     private static final String SP_P1_NAME = "p1";
85     private static final String SP_HANDLE_NAME = "handle";
86     private static final String SECDISCARDABLE_NAME = "secdis";
87     private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
88     private static final String PASSWORD_DATA_NAME = "pwd";
89     private static final String WEAVER_SLOT_NAME = "weaver";
90
91     public static final long DEFAULT_HANDLE = 0L;
92     private static final String DEFAULT_PASSWORD = "default-password";
93
94     private static final byte WEAVER_VERSION = 1;
95     private static final int INVALID_WEAVER_SLOT = -1;
96
97     private static final byte SYNTHETIC_PASSWORD_VERSION = 1;
98     private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
99     private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
100
101     // 256-bit synthetic password
102     private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
103
104     private static final int PASSWORD_SCRYPT_N = 13;
105     private static final int PASSWORD_SCRYPT_R = 3;
106     private static final int PASSWORD_SCRYPT_P = 1;
107     private static final int PASSWORD_SALT_LENGTH = 16;
108     private static final int PASSWORD_TOKEN_LENGTH = 32;
109     private static final String TAG = "SyntheticPasswordManager";
110
111     private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
112     private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
113     private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
114     private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
115     private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
116     private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
117     private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
118     private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
119     private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
120     private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
121
122     static class AuthenticationResult {
123         public AuthenticationToken authToken;
124         public VerifyCredentialResponse gkResponse;
125     }
126
127     static class AuthenticationToken {
128         /*
129          * Here is the relationship between all three fields:
130          * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
131          * syntheticPassword = hash(P0 || P1)
132          * E0 = P0 encrypted under syntheticPassword, stored on disk.
133          */
134         private @Nullable byte[] E0;
135         private @Nullable byte[] P1;
136         private @NonNull String syntheticPassword;
137
138         public String deriveKeyStorePassword() {
139             return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
140                     PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
141         }
142
143         public byte[] deriveGkPassword() {
144             return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
145                     syntheticPassword.getBytes());
146         }
147
148         public byte[] deriveDiskEncryptionKey() {
149             return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
150                     syntheticPassword.getBytes());
151         }
152
153         private void initialize(byte[] P0, byte[] P1) {
154             this.P1 = P1;
155             this.syntheticPassword = String.valueOf(HexEncoding.encode(
156                     SyntheticPasswordCrypto.personalisedHash(
157                             PERSONALIZATION_SP_SPLIT, P0, P1)));
158             this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
159                     PERSONALIZATION_E0, P0);
160         }
161
162         public void recreate(byte[] secret) {
163             initialize(secret, this.P1);
164         }
165
166         protected static AuthenticationToken create() {
167             AuthenticationToken result = new AuthenticationToken();
168             result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
169                     secureRandom(SYNTHETIC_PASSWORD_LENGTH));
170             return result;
171         }
172
173         public byte[] computeP0() {
174             if (E0 == null) {
175                 return null;
176             }
177             return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
178                     E0);
179         }
180     }
181
182     static class PasswordData {
183         byte scryptN;
184         byte scryptR;
185         byte scryptP;
186         public int passwordType;
187         byte[] salt;
188         // For GateKeeper-based credential, this is the password handle returned by GK,
189         // for weaver-based credential, this is empty.
190         public byte[] passwordHandle;
191
192         public static PasswordData create(int passwordType) {
193             PasswordData result = new PasswordData();
194             result.scryptN = PASSWORD_SCRYPT_N;
195             result.scryptR = PASSWORD_SCRYPT_R;
196             result.scryptP = PASSWORD_SCRYPT_P;
197             result.passwordType = passwordType;
198             result.salt = secureRandom(PASSWORD_SALT_LENGTH);
199             return result;
200         }
201
202         public static PasswordData fromBytes(byte[] data) {
203             PasswordData result = new PasswordData();
204             ByteBuffer buffer = ByteBuffer.allocate(data.length);
205             buffer.put(data, 0, data.length);
206             buffer.flip();
207             result.passwordType = buffer.getInt();
208             result.scryptN = buffer.get();
209             result.scryptR = buffer.get();
210             result.scryptP = buffer.get();
211             int saltLen = buffer.getInt();
212             result.salt = new byte[saltLen];
213             buffer.get(result.salt);
214             int handleLen = buffer.getInt();
215             if (handleLen > 0) {
216                 result.passwordHandle = new byte[handleLen];
217                 buffer.get(result.passwordHandle);
218             } else {
219                 result.passwordHandle = null;
220             }
221             return result;
222         }
223
224         public byte[] toBytes() {
225
226             ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
227                     + Integer.BYTES + salt.length + Integer.BYTES +
228                     (passwordHandle != null ? passwordHandle.length : 0));
229             buffer.putInt(passwordType);
230             buffer.put(scryptN);
231             buffer.put(scryptR);
232             buffer.put(scryptP);
233             buffer.putInt(salt.length);
234             buffer.put(salt);
235             if (passwordHandle != null && passwordHandle.length > 0) {
236                 buffer.putInt(passwordHandle.length);
237                 buffer.put(passwordHandle);
238             } else {
239                 buffer.putInt(0);
240             }
241             return buffer.array();
242         }
243     }
244
245     static class TokenData {
246         byte[] secdiscardableOnDisk;
247         byte[] weaverSecret;
248         byte[] aggregatedSecret;
249     }
250
251     private LockSettingsStorage mStorage;
252     private IWeaver mWeaver;
253     private WeaverConfig mWeaverConfig;
254
255     public SyntheticPasswordManager(LockSettingsStorage storage) {
256         mStorage = storage;
257     }
258
259     @VisibleForTesting
260     protected IWeaver getWeaverService() throws RemoteException {
261         try {
262             return IWeaver.getService();
263         } catch (NoSuchElementException e) {
264             Slog.i(TAG, "Device does not support weaver");
265             return null;
266         }
267     }
268
269     public synchronized void initWeaverService() {
270         if (mWeaver != null) {
271             return;
272         }
273         try {
274             mWeaverConfig = null;
275             mWeaver = getWeaverService();
276             if (mWeaver != null) {
277                 mWeaver.getConfig((int status, WeaverConfig config) -> {
278                     if (status == WeaverStatus.OK && config.slots > 0) {
279                         mWeaverConfig = config;
280                     } else {
281                         Slog.e(TAG, "Failed to get weaver config, status " + status
282                                 + " slots: " + config.slots);
283                         mWeaver = null;
284                     }
285                 });
286             }
287         } catch (RemoteException e) {
288             Slog.e(TAG, "Failed to get weaver service", e);
289         }
290     }
291
292     private synchronized boolean isWeaverAvailable() {
293         if (mWeaver == null) {
294             //Re-initializing weaver in case there was a transient error preventing access to it.
295             initWeaverService();
296         }
297         return mWeaver != null && mWeaverConfig.slots > 0;
298     }
299
300     /**
301      * Enroll the given key value pair into the specified weaver slot. if the given key is null,
302      * a default all-zero key is used. If the value is not specified, a fresh random secret is
303      * generated as the value.
304      *
305      * @return the value stored in the weaver slot
306      * @throws RemoteException
307      */
308     private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
309             throws RemoteException {
310         if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
311             throw new RuntimeException("Invalid slot for weaver");
312         }
313         if (key == null) {
314             key = new byte[mWeaverConfig.keySize];
315         } else if (key.length != mWeaverConfig.keySize) {
316             throw new RuntimeException("Invalid key size for weaver");
317         }
318         if (value == null) {
319             value = secureRandom(mWeaverConfig.valueSize);
320         }
321         int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
322         if (writeStatus != WeaverStatus.OK) {
323             Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
324             return null;
325         }
326         return value;
327     }
328
329     /**
330      * Verify the supplied key against a weaver slot, returning a response indicating whether
331      * the verification is successful, throttled or failed. If successful, the bound secret
332      * is also returned.
333      * @throws RemoteException
334      */
335     private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
336         if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
337             throw new RuntimeException("Invalid slot for weaver");
338         }
339         if (key == null) {
340             key = new byte[mWeaverConfig.keySize];
341         } else if (key.length != mWeaverConfig.keySize) {
342             throw new RuntimeException("Invalid key size for weaver");
343         }
344         final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
345         mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
346             switch (status) {
347                 case WeaverReadStatus.OK:
348                     response[0] = new VerifyCredentialResponse(
349                             fromByteArrayList(readResponse.value));
350                     break;
351                 case WeaverReadStatus.THROTTLE:
352                     response[0] = new VerifyCredentialResponse(readResponse.timeout);
353                     Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
354                     break;
355                 case WeaverReadStatus.INCORRECT_KEY:
356                     if (readResponse.timeout == 0) {
357                         response[0] = VerifyCredentialResponse.ERROR;
358                         Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
359                     } else {
360                         response[0] = new VerifyCredentialResponse(readResponse.timeout);
361                         Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
362                     }
363                     break;
364                 case WeaverReadStatus.FAILED:
365                     response[0] = VerifyCredentialResponse.ERROR;
366                     Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
367                     break;
368                default:
369                    response[0] = VerifyCredentialResponse.ERROR;
370                    Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
371                    break;
372             }
373         });
374         return response[0];
375     }
376
377     public void removeUser(int userId) {
378         if (isWeaverAvailable()) {
379             for (long handle : mStorage.listSyntheticPasswordHandlesForUser(WEAVER_SLOT_NAME,
380                     userId)) {
381                 destroyWeaverSlot(handle, userId);
382             }
383         }
384     }
385
386     public int getCredentialType(long handle, int userId) {
387         byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
388         if (passwordData == null) {
389             Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
390             return LockPatternUtils.CREDENTIAL_TYPE_NONE;
391         }
392         return PasswordData.fromBytes(passwordData).passwordType;
393     }
394
395     /**
396      * Initializing a new Authentication token, possibly from an existing credential and hash.
397      *
398      * The authentication token would bear a randomly-generated synthetic password.
399      *
400      * This method has the side effect of rebinding the SID of the given user to the
401      * newly-generated SP.
402      *
403      * If the existing credential hash is non-null, the existing SID mill be migrated so
404      * the synthetic password in the authentication token will produce the same SID
405      * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
406      * in a per-user data storage.)
407      *
408      * If the existing credential hash is null, it means the given user should have no SID so
409      * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
410      * the supplied credential parameter is also ignored.
411      *
412      * Also saves the escrow information necessary to re-generate the synthetic password under
413      * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
414      * password escrow should be disabled completely on the given user.
415      *
416      */
417     public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
418             byte[] hash, String credential, int userId) throws RemoteException {
419         AuthenticationToken result = AuthenticationToken.create();
420         GateKeeperResponse response;
421         if (hash != null) {
422             response = gatekeeper.enroll(userId, hash, credential.getBytes(),
423                     result.deriveGkPassword());
424             if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
425                 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
426                 clearSidForUser(userId);
427             } else {
428                 saveSyntheticPasswordHandle(response.getPayload(), userId);
429             }
430         } else {
431             clearSidForUser(userId);
432         }
433         saveEscrowData(result, userId);
434         return result;
435     }
436
437     /**
438      * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
439      * Used when adding password to previously-unsecured devices.
440      */
441     public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
442             int userId) throws RemoteException {
443         GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
444                 authToken.deriveGkPassword());
445         if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
446             Log.e(TAG, "Fail to create new SID for user " + userId);
447             return;
448         }
449         saveSyntheticPasswordHandle(response.getPayload(), userId);
450     }
451
452     // Nuke the SP handle (and as a result, its SID) for the given user.
453     public void clearSidForUser(int userId) {
454         destroyState(SP_HANDLE_NAME, true, DEFAULT_HANDLE, userId);
455     }
456
457     public boolean hasSidForUser(int userId) {
458         return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
459     }
460
461     // if null, it means there is no SID associated with the user
462     // This can happen if the user is migrated to SP but currently
463     // do not have a lockscreen password.
464     private byte[] loadSyntheticPasswordHandle(int userId) {
465         return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
466     }
467
468     private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
469         saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
470     }
471
472     private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
473         authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
474         authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
475         return authToken.E0 != null && authToken.P1 != null;
476     }
477
478     private void saveEscrowData(AuthenticationToken authToken, int userId) {
479         saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
480         saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
481     }
482
483     public boolean hasEscrowData(int userId) {
484         return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
485                 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
486     }
487
488     public void destroyEscrowData(int userId) {
489         destroyState(SP_E0_NAME, true, DEFAULT_HANDLE, userId);
490         destroyState(SP_P1_NAME, true, DEFAULT_HANDLE, userId);
491     }
492
493     private int loadWeaverSlot(long handle, int userId) {
494         final int LENGTH = Byte.BYTES + Integer.BYTES;
495         byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
496         if (data == null || data.length != LENGTH) {
497             return INVALID_WEAVER_SLOT;
498         }
499         ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
500         buffer.put(data, 0, data.length);
501         buffer.flip();
502         if (buffer.get() != WEAVER_VERSION) {
503             Log.e(TAG, "Invalid weaver slot version of handle " + handle);
504             return INVALID_WEAVER_SLOT;
505         }
506         return buffer.getInt();
507     }
508
509     private void saveWeaverSlot(int slot, long handle, int userId) {
510         ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
511         buffer.put(WEAVER_VERSION);
512         buffer.putInt(slot);
513         saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
514     }
515
516     private void destroyWeaverSlot(long handle, int userId) {
517         int slot = loadWeaverSlot(handle, userId);
518         if (slot != INVALID_WEAVER_SLOT) {
519             try {
520                 weaverEnroll(slot, null, null);
521             } catch (RemoteException e) {
522                 Log.w(TAG, "Failed to destroy slot", e);
523             }
524         }
525         destroyState(WEAVER_SLOT_NAME, true, handle, userId);
526     }
527
528     private int getNextAvailableWeaverSlot() {
529         Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
530                 WEAVER_SLOT_NAME);
531         HashSet<Integer> slots = new HashSet<>();
532         for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
533             for (Long handle : entry.getValue()) {
534                 int slot = loadWeaverSlot(handle, entry.getKey());
535                 slots.add(slot);
536             }
537         }
538         for (int i = 0; i < mWeaverConfig.slots; i++) {
539             if (!slots.contains(i)) {
540                 return i;
541             }
542         }
543         throw new RuntimeException("Run out of weaver slots.");
544     }
545
546     /**
547      * Create a new password based SP blob based on the supplied authentication token, such that
548      * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
549      * in the same authentication token.
550      *
551      * This method only creates SP blob wrapping around the given synthetic password and does not
552      * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
553      * is consistent with the device state by calling other APIs in this class.
554      *
555      * @see #newSidForUser
556      * @see #clearSidForUser
557      */
558     public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
559             String credential, int credentialType, AuthenticationToken authToken, int userId)
560                     throws RemoteException {
561         if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
562             credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
563             credential = DEFAULT_PASSWORD;
564         }
565
566         long handle = generateHandle();
567         PasswordData pwd = PasswordData.create(credentialType);
568         byte[] pwdToken = computePasswordToken(credential, pwd);
569         final long sid;
570         final byte[] applicationId;
571
572         if (isWeaverAvailable()) {
573             // Weaver based user password
574             int weaverSlot = getNextAvailableWeaverSlot();
575             byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
576             if (weaverSecret == null) {
577                 Log.e(TAG, "Fail to enroll user password under weaver " + userId);
578                 return DEFAULT_HANDLE;
579             }
580             saveWeaverSlot(weaverSlot, handle, userId);
581
582             pwd.passwordHandle = null;
583             sid = GateKeeper.INVALID_SECURE_USER_ID;
584             applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
585         } else {
586             // GateKeeper based user password
587             GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
588                     passwordTokenToGkInput(pwdToken));
589             if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
590                 Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
591                 return DEFAULT_HANDLE;
592             }
593             pwd.passwordHandle = response.getPayload();
594             sid = sidFromPasswordHandle(pwd.passwordHandle);
595             applicationId = transformUnderSecdiscardable(pwdToken,
596                     createSecdiscardable(handle, userId));
597         }
598         saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
599
600         createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
601                 applicationId, sid, userId);
602         return handle;
603     }
604
605     private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
606
607     public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
608         long handle = generateHandle();
609         if (!tokenMap.containsKey(userId)) {
610             tokenMap.put(userId, new ArrayMap<>());
611         }
612         TokenData tokenData = new TokenData();
613         final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
614         if (isWeaverAvailable()) {
615             tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
616             tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
617                             PERSONALISATION_WEAVER_TOKEN, secdiscardable);
618         } else {
619             tokenData.secdiscardableOnDisk = secdiscardable;
620             tokenData.weaverSecret = null;
621         }
622         tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
623
624         tokenMap.get(userId).put(handle, tokenData);
625         return handle;
626     }
627
628     public Set<Long> getPendingTokensForUser(int userId) {
629         if (!tokenMap.containsKey(userId)) {
630             return Collections.emptySet();
631         }
632         return tokenMap.get(userId).keySet();
633     }
634
635     public boolean removePendingToken(long handle, int userId) {
636         if (!tokenMap.containsKey(userId)) {
637             return false;
638         }
639         return tokenMap.get(userId).remove(handle) != null;
640     }
641
642     public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
643             int userId) {
644         if (!tokenMap.containsKey(userId)) {
645             return false;
646         }
647         TokenData tokenData = tokenMap.get(userId).get(handle);
648         if (tokenData == null) {
649             return false;
650         }
651         if (!loadEscrowData(authToken, userId)) {
652             Log.w(TAG, "User is not escrowable");
653             return false;
654         }
655         if (isWeaverAvailable()) {
656             int slot = getNextAvailableWeaverSlot();
657             try {
658                 weaverEnroll(slot, null, tokenData.weaverSecret);
659             } catch (RemoteException e) {
660                 Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
661                 return false;
662             }
663             saveWeaverSlot(slot, handle, userId);
664         }
665         saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
666         createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
667                 tokenData.aggregatedSecret, 0L, userId);
668         tokenMap.get(userId).remove(handle);
669         return true;
670     }
671
672     private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
673             byte[] applicationId, long sid, int userId) {
674         final byte[] secret;
675         if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
676             secret = authToken.computeP0();
677         } else {
678             secret = authToken.syntheticPassword.getBytes();
679         }
680         byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
681         byte[] blob = new byte[content.length + 1 + 1];
682         blob[0] = SYNTHETIC_PASSWORD_VERSION;
683         blob[1] = type;
684         System.arraycopy(content, 0, blob, 2, content.length);
685         saveState(SP_BLOB_NAME, blob, handle, userId);
686     }
687
688     /**
689      * Decrypt a synthetic password by supplying the user credential and corresponding password
690      * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
691      * verification to referesh the SID & Auth token maintained by the system.
692      */
693     public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
694             long handle, String credential, int userId) throws RemoteException {
695         if (credential == null) {
696             credential = DEFAULT_PASSWORD;
697         }
698         AuthenticationResult result = new AuthenticationResult();
699         PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
700         byte[] pwdToken = computePasswordToken(credential, pwd);
701
702         final byte[] applicationId;
703         int weaverSlot = loadWeaverSlot(handle, userId);
704         if (weaverSlot != INVALID_WEAVER_SLOT) {
705             // Weaver based user password
706             if (!isWeaverAvailable()) {
707                 Log.e(TAG, "No weaver service to unwrap password based SP");
708                 result.gkResponse = VerifyCredentialResponse.ERROR;
709                 return result;
710             }
711             result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
712             if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
713                 return result;
714             }
715             applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
716         } else {
717             byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
718             GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
719                     pwd.passwordHandle, gkPwdToken);
720             int responseCode = response.getResponseCode();
721             if (responseCode == GateKeeperResponse.RESPONSE_OK) {
722                 result.gkResponse = VerifyCredentialResponse.OK;
723                 if (response.getShouldReEnroll()) {
724                     GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
725                             pwd.passwordHandle, gkPwdToken, gkPwdToken);
726                     if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
727                         pwd.passwordHandle = reenrollResponse.getPayload();
728                         saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
729                     } else {
730                         Log.w(TAG, "Fail to re-enroll user password for user " + userId);
731                         // continue the flow anyway
732                     }
733                 }
734             } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
735                 result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
736                 return result;
737             } else  {
738                 result.gkResponse = VerifyCredentialResponse.ERROR;
739                 return result;
740             }
741             applicationId = transformUnderSecdiscardable(pwdToken,
742                     loadSecdiscardable(handle, userId));
743         }
744
745         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
746                 applicationId, userId);
747
748         // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
749         result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
750         return result;
751     }
752
753     /**
754      * Decrypt a synthetic password by supplying an escrow token and corresponding token
755      * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
756      * verification to referesh the SID & Auth token maintained by the system.
757      */
758     public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
759             IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
760                     throws RemoteException {
761         AuthenticationResult result = new AuthenticationResult();
762         byte[] secdiscardable = loadSecdiscardable(handle, userId);
763         int slotId = loadWeaverSlot(handle, userId);
764         if (slotId != INVALID_WEAVER_SLOT) {
765             if (!isWeaverAvailable()) {
766                 Log.e(TAG, "No weaver service to unwrap token based SP");
767                 result.gkResponse = VerifyCredentialResponse.ERROR;
768                 return result;
769             }
770             VerifyCredentialResponse response = weaverVerify(slotId, null);
771             if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
772                     response.getPayload() == null) {
773                 Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
774                 result.gkResponse = VerifyCredentialResponse.ERROR;
775                 return result;
776             }
777             secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
778                     PERSONALISATION_WEAVER_TOKEN, secdiscardable);
779         }
780         byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
781         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
782                 applicationId, userId);
783         if (result.authToken != null) {
784             result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
785             if (result.gkResponse == null) {
786                 // The user currently has no password. return OK with null payload so null
787                 // is propagated to unlockUser()
788                 result.gkResponse = VerifyCredentialResponse.OK;
789             }
790         } else {
791             result.gkResponse = VerifyCredentialResponse.ERROR;
792         }
793         return result;
794     }
795
796     private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
797             byte[] applicationId, int userId) {
798         byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
799         if (blob == null) {
800             return null;
801         }
802         if (blob[0] != SYNTHETIC_PASSWORD_VERSION) {
803             throw new RuntimeException("Unknown blob version");
804         }
805         if (blob[1] != type) {
806             throw new RuntimeException("Invalid blob type");
807         }
808         byte[] secret = decryptSPBlob(getHandleName(handle),
809                 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
810         if (secret == null) {
811             Log.e(TAG, "Fail to decrypt SP for user " + userId);
812             return null;
813         }
814         AuthenticationToken result = new AuthenticationToken();
815         if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
816             if (!loadEscrowData(result, userId)) {
817                 Log.e(TAG, "User is not escrowable: " + userId);
818                 return null;
819             }
820             result.recreate(secret);
821         } else {
822             result.syntheticPassword = new String(secret);
823         }
824         return result;
825     }
826
827     /**
828      * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
829      * if required.
830      *
831      * Normally performing verifyChallenge with an AuthenticationToken should always return
832      * RESPONSE_OK, since user authentication failures are detected earlier when trying to
833      * decrypt SP.
834      */
835     public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
836             @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
837         byte[] spHandle = loadSyntheticPasswordHandle(userId);
838         if (spHandle == null) {
839             // There is no password handle associated with the given user, i.e. the user is not
840             // secured by lockscreen and has no SID, so just return here;
841             return null;
842         }
843         VerifyCredentialResponse result;
844         GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
845                 spHandle, auth.deriveGkPassword());
846         int responseCode = response.getResponseCode();
847         if (responseCode == GateKeeperResponse.RESPONSE_OK) {
848             result = new VerifyCredentialResponse(response.getPayload());
849             if (response.getShouldReEnroll()) {
850                 response = gatekeeper.enroll(userId, spHandle,
851                         spHandle, auth.deriveGkPassword());
852                 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
853                     spHandle = response.getPayload();
854                     saveSyntheticPasswordHandle(spHandle, userId);
855                     // Call self again to re-verify with updated handle
856                     return verifyChallenge(gatekeeper, auth, challenge, userId);
857                 } else {
858                     Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
859                     // Fall through, return existing handle
860                 }
861             }
862         } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
863             result = new VerifyCredentialResponse(response.getTimeout());
864         } else {
865             result = VerifyCredentialResponse.ERROR;
866         }
867         return result;
868     }
869
870     public boolean existsHandle(long handle, int userId) {
871         return hasState(SP_BLOB_NAME, handle, userId);
872     }
873
874     public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
875         destroySyntheticPassword(handle, userId);
876         destroyState(SECDISCARDABLE_NAME, true, handle, userId);
877     }
878
879     public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
880         destroySyntheticPassword(handle, userId);
881         destroyState(SECDISCARDABLE_NAME, true, handle, userId);
882         destroyState(PASSWORD_DATA_NAME, true, handle, userId);
883     }
884
885     private void destroySyntheticPassword(long handle, int userId) {
886         destroyState(SP_BLOB_NAME, true, handle, userId);
887         destroySPBlobKey(getHandleName(handle));
888         if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
889             destroyWeaverSlot(handle, userId);
890         }
891     }
892
893     private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
894         byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
895                 PERSONALISATION_WEAVER_PASSWORD, secret);
896         byte[] result = new byte[data.length + weaverSecret.length];
897         System.arraycopy(data, 0, result, 0, data.length);
898         System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
899         return result;
900     }
901
902     private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
903         byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
904                 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
905         byte[] result = new byte[data.length + secdiscardable.length];
906         System.arraycopy(data, 0, result, 0, data.length);
907         System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
908         return result;
909     }
910
911     private byte[] createSecdiscardable(long handle, int userId) {
912         byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
913         saveSecdiscardable(handle, data, userId);
914         return data;
915     }
916
917     private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
918         saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
919     }
920
921     private byte[] loadSecdiscardable(long handle, int userId) {
922         return loadState(SECDISCARDABLE_NAME, handle, userId);
923     }
924
925     private boolean hasState(String stateName, long handle, int userId) {
926         return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
927     }
928
929     private byte[] loadState(String stateName, long handle, int userId) {
930         return mStorage.readSyntheticPasswordState(userId, handle, stateName);
931     }
932
933     private void saveState(String stateName, byte[] data, long handle, int userId) {
934         mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
935     }
936
937     private void destroyState(String stateName, boolean secure, long handle, int userId) {
938         mStorage.deleteSyntheticPasswordState(userId, handle, stateName, secure);
939     }
940
941     protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
942         return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
943     }
944
945     protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
946         return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
947     }
948
949     protected void destroySPBlobKey(String keyAlias) {
950         SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
951     }
952
953     public static long generateHandle() {
954         SecureRandom rng = new SecureRandom();
955         long result;
956         do {
957             result = rng.nextLong();
958         } while (result == DEFAULT_HANDLE);
959         return result;
960     }
961
962     private int fakeUid(int uid) {
963         return 100000 + uid;
964     }
965
966     protected static byte[] secureRandom(int length) {
967         try {
968             return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
969         } catch (NoSuchAlgorithmException e) {
970             e.printStackTrace();
971             return null;
972         }
973     }
974
975     private String getHandleName(long handle) {
976         return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
977     }
978
979     private byte[] computePasswordToken(String password, PasswordData data) {
980         return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
981                 PASSWORD_TOKEN_LENGTH);
982     }
983
984     private byte[] passwordTokenToGkInput(byte[] token) {
985         return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
986     }
987
988     private byte[] passwordTokenToWeaverKey(byte[] token) {
989         byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
990         if (key.length < mWeaverConfig.keySize) {
991             throw new RuntimeException("weaver key length too small");
992         }
993         return Arrays.copyOf(key, mWeaverConfig.keySize);
994     }
995
996     protected long sidFromPasswordHandle(byte[] handle) {
997         return nativeSidFromPasswordHandle(handle);
998     }
999
1000     protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
1001         return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
1002     }
1003
1004     native long nativeSidFromPasswordHandle(byte[] handle);
1005     native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
1006
1007     protected static ArrayList<Byte> toByteArrayList(byte[] data) {
1008         ArrayList<Byte> result = new ArrayList<Byte>(data.length);
1009         for (int i = 0; i < data.length; i++) {
1010             result.add(data[i]);
1011         }
1012         return result;
1013     }
1014
1015     protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
1016         byte[] result = new byte[data.size()];
1017         for (int i = 0; i < data.size(); i++) {
1018             result[i] = data.get(i);
1019         }
1020         return result;
1021     }
1022
1023     final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
1024     public static String bytesToHex(byte[] bytes) {
1025         if (bytes == null) {
1026             return "null";
1027         }
1028         char[] hexChars = new char[bytes.length * 2];
1029         for ( int j = 0; j < bytes.length; j++ ) {
1030             int v = bytes[j] & 0xFF;
1031             hexChars[j * 2] = hexArray[v >>> 4];
1032             hexChars[j * 2 + 1] = hexArray[v & 0x0F];
1033         }
1034         return new String(hexChars);
1035     }
1036 }