Loading core/java/android/service/gatekeeper/GateKeeperResponse.java +2 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ public final class GateKeeperResponse implements Parcelable { public static final int RESPONSE_OK = 0; public static final int RESPONSE_RETRY = 1; public static final GateKeeperResponse ERROR = createGenericResponse(RESPONSE_ERROR); private final int mResponseCode; private int mTimeout; Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +135 −139 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +140 −86 Original line number Diff line number Diff line Loading @@ -349,11 +349,9 @@ public class SyntheticPasswordManager { * a default all-zero key is used. If the value is not specified, a fresh random secret is * generated as the value. * * @return the value stored in the weaver slot * @throws RemoteException * @return the value stored in the weaver slot, or null if the operation fails */ private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) throws RemoteException { private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) { if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) { throw new RuntimeException("Invalid slot for weaver"); } Loading @@ -365,11 +363,16 @@ public class SyntheticPasswordManager { if (value == null) { value = secureRandom(mWeaverConfig.valueSize); } try { int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value)); if (writeStatus != WeaverStatus.OK) { Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus); return null; } } catch (RemoteException e) { Log.e(TAG, "weaver write failed", e); return null; } return value; } Loading @@ -377,9 +380,8 @@ public class SyntheticPasswordManager { * Verify the supplied key against a weaver slot, returning a response indicating whether * the verification is successful, throttled or failed. If successful, the bound secret * is also returned. * @throws RemoteException */ private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException { private VerifyCredentialResponse weaverVerify(int slot, byte[] key) { if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) { throw new RuntimeException("Invalid slot for weaver"); } Loading @@ -389,7 +391,9 @@ public class SyntheticPasswordManager { throw new RuntimeException("Invalid key size for weaver"); } final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1]; mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> { try { mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> { switch (status) { case WeaverReadStatus.OK: response[0] = new VerifyCredentialResponse( Loading @@ -405,7 +409,8 @@ public class SyntheticPasswordManager { Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot); } else { response[0] = new VerifyCredentialResponse(readResponse.timeout); Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot); Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot); } break; case WeaverReadStatus.FAILED: Loading @@ -418,6 +423,10 @@ public class SyntheticPasswordManager { break; } }); } catch (RemoteException e) { response[0] = VerifyCredentialResponse.ERROR; Log.e(TAG, "weaver read failed, slot: " + slot, e); } return response[0]; } Loading Loading @@ -460,12 +469,15 @@ public class SyntheticPasswordManager { * */ public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, byte[] hash, byte[] credential, int userId) throws RemoteException { byte[] hash, byte[] credential, int userId) { AuthenticationToken result = AuthenticationToken.create(); GateKeeperResponse response; if (hash != null) { response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword()); try { response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword()); } catch (RemoteException e) { throw new IllegalStateException("Failed to enroll credential duing SP init", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId); clearSidForUser(userId); Loading @@ -484,9 +496,13 @@ public class SyntheticPasswordManager { * Used when adding password to previously-unsecured devices. */ public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken, int userId) throws RemoteException { GateKeeperResponse response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword()); int userId) { GateKeeperResponse response; try { response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword()); } catch (RemoteException e) { throw new IllegalStateException("Failed to create new SID for user", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { Log.e(TAG, "Fail to create new SID for user " + userId); return; Loading Loading @@ -565,12 +581,8 @@ public class SyntheticPasswordManager { Set<Integer> usedSlots = getUsedWeaverSlots(); if (!usedSlots.contains(slot)) { Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId); try { weaverEnroll(slot, null, null); mPasswordSlotManager.markSlotDeleted(slot); } catch (RemoteException e) { Log.w(TAG, "Failed to destroy slot", e); } } else { Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId); } Loading Loading @@ -622,11 +634,12 @@ public class SyntheticPasswordManager { * * @see #newSidForUser * @see #clearSidForUser * @return a new password handle for the wrapped SP blob * @throw IllegalStateException if creation fails. */ public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, byte[] credential, int credentialType, AuthenticationToken authToken, int requestedQuality, int userId) throws RemoteException { int requestedQuality, int userId) { if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE; credential = DEFAULT_PASSWORD; Loading @@ -642,10 +655,11 @@ public class SyntheticPasswordManager { // Weaver based user password int weaverSlot = getNextAvailableWeaverSlot(); Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId); byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null); byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null); if (weaverSecret == null) { Log.e(TAG, "Fail to enroll user password under weaver " + userId); return DEFAULT_HANDLE; throw new IllegalStateException( "Fail to enroll user password under weaver " + userId); } saveWeaverSlot(weaverSlot, handle, userId); mPasswordSlotManager.markSlotInUse(weaverSlot); Loading @@ -657,13 +671,22 @@ public class SyntheticPasswordManager { } else { // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them // to prevent them from accumulating and causing problems. try { gatekeeper.clearSecureUserId(fakeUid(userId)); } catch (RemoteException ignore) { Log.w(TAG, "Failed to clear SID from gatekeeper"); } // GateKeeper based user password GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null, GateKeeperResponse response; try { response = gatekeeper.enroll(fakeUid(userId), null, null, passwordTokenToGkInput(pwdToken)); } catch (RemoteException e) { throw new IllegalStateException("Failed to enroll password for new SP blob", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId); return DEFAULT_HANDLE; throw new IllegalStateException( "Fail to enroll user password when creating SP for user " + userId); } pwd.passwordHandle = response.getPayload(); sid = sidFromPasswordHandle(pwd.passwordHandle); Loading @@ -680,14 +703,20 @@ public class SyntheticPasswordManager { public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper, byte[] userCredential, int credentialType, ICheckCredentialProgressCallback progressCallback) throws RemoteException { ICheckCredentialProgressCallback progressCallback) { PersistentData persistentData = mStorage.readPersistentDataBlock(); if (persistentData.type == PersistentData.TYPE_SP) { PasswordData pwd = PasswordData.fromBytes(persistentData.payload); byte[] pwdToken = computePasswordToken(userCredential, pwd); GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId), GateKeeperResponse response; try { response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId), 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken)); } catch (RemoteException e) { Log.e(TAG, "FRP verifyChallenge failed", e); return VerifyCredentialResponse.ERROR; } return VerifyCredentialResponse.fromGateKeeperResponse(response); } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) { PasswordData pwd = PasswordData.fromBytes(persistentData.payload); Loading Loading @@ -805,11 +834,9 @@ public class SyntheticPasswordManager { } if (isWeaverAvailable()) { int slot = getNextAvailableWeaverSlot(); try { Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId); weaverEnroll(slot, null, tokenData.weaverSecret); } catch (RemoteException e) { Log.e(TAG, "Failed to enroll weaver secret when activating token", e); if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) { Log.e(TAG, "Failed to enroll weaver secret when activating token"); return false; } saveWeaverSlot(slot, handle, userId); Loading Loading @@ -859,7 +886,7 @@ public class SyntheticPasswordManager { */ public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, long handle, byte[] credential, int userId, ICheckCredentialProgressCallback progressCallback) throws RemoteException { ICheckCredentialProgressCallback progressCallback) { if (credential == null) { credential = DEFAULT_PASSWORD; } Loading @@ -886,14 +913,28 @@ public class SyntheticPasswordManager { applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload()); } else { byte[] gkPwdToken = passwordTokenToGkInput(pwdToken); GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L, GateKeeperResponse response; try { response = gatekeeper.verifyChallenge(fakeUid(userId), 0L, pwd.passwordHandle, gkPwdToken); } catch (RemoteException e) { Log.e(TAG, "gatekeeper verify failed", e); result.gkResponse = VerifyCredentialResponse.ERROR; return result; } int responseCode = response.getResponseCode(); if (responseCode == GateKeeperResponse.RESPONSE_OK) { result.gkResponse = VerifyCredentialResponse.OK; if (response.getShouldReEnroll()) { GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId), GateKeeperResponse reenrollResponse; try { reenrollResponse = gatekeeper.enroll(fakeUid(userId), pwd.passwordHandle, gkPwdToken, gkPwdToken); } catch (RemoteException e) { Log.w(TAG, "Fail to invoke gatekeeper.enroll", e); reenrollResponse = GateKeeperResponse.ERROR; // continue the flow anyway } if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { pwd.passwordHandle = reenrollResponse.getPayload(); saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId); Loading Loading @@ -922,7 +963,11 @@ public class SyntheticPasswordManager { // Supplied credential passes first stage weaver/gatekeeper check so it should be correct. // Notify the callback so the keyguard UI can proceed immediately. if (progressCallback != null) { try { progressCallback.onCredentialVerified(); } catch (RemoteException e) { Log.w(TAG, "progressCallback throws exception", e); } } result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, applicationId, sid, userId); Loading @@ -938,8 +983,7 @@ public class SyntheticPasswordManager { * verification to referesh the SID & Auth token maintained by the system. */ public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword( IGateKeeperService gatekeeper, long handle, byte[] token, int userId) throws RemoteException { IGateKeeperService gatekeeper, long handle, byte[] token, int userId) { AuthenticationResult result = new AuthenticationResult(); byte[] secdiscardable = loadSecdiscardable(handle, userId); int slotId = loadWeaverSlot(handle, userId); Loading Loading @@ -1028,38 +1072,48 @@ public class SyntheticPasswordManager { * decrypt SP. */ public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper, @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException { @NonNull AuthenticationToken auth, long challenge, int userId) { byte[] spHandle = loadSyntheticPasswordHandle(userId); if (spHandle == null) { // There is no password handle associated with the given user, i.e. the user is not // secured by lockscreen and has no SID, so just return here; return null; } VerifyCredentialResponse result; GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge, GateKeeperResponse response; try { response = gatekeeper.verifyChallenge(userId, challenge, spHandle, auth.deriveGkPassword()); } catch (RemoteException e) { Log.e(TAG, "Fail to verify with gatekeeper " + userId, e); return VerifyCredentialResponse.ERROR; } int responseCode = response.getResponseCode(); if (responseCode == GateKeeperResponse.RESPONSE_OK) { result = new VerifyCredentialResponse(response.getPayload()); VerifyCredentialResponse result = new VerifyCredentialResponse(response.getPayload()); if (response.getShouldReEnroll()) { response = gatekeeper.enroll(userId, spHandle, spHandle, auth.deriveGkPassword()); try { response = gatekeeper.enroll(userId, spHandle, spHandle, auth.deriveGkPassword()); } catch (RemoteException e) { Log.e(TAG, "Failed to invoke gatekeeper.enroll", e); response = GateKeeperResponse.ERROR; } if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { spHandle = response.getPayload(); saveSyntheticPasswordHandle(spHandle, userId); // Call self again to re-verify with updated handle return verifyChallenge(gatekeeper, auth, challenge, userId); } else { // Fall through, return result from the previous verification attempt. Log.w(TAG, "Fail to re-enroll SP handle for user " + userId); // Fall through, return existing handle } } return result; } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { result = new VerifyCredentialResponse(response.getTimeout()); return new VerifyCredentialResponse(response.getTimeout()); } else { result = VerifyCredentialResponse.ERROR; return VerifyCredentialResponse.ERROR; } return result; } public boolean existsHandle(long handle, int userId) { Loading Loading
core/java/android/service/gatekeeper/GateKeeperResponse.java +2 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ public final class GateKeeperResponse implements Parcelable { public static final int RESPONSE_OK = 0; public static final int RESPONSE_RETRY = 1; public static final GateKeeperResponse ERROR = createGenericResponse(RESPONSE_ERROR); private final int mResponseCode; private int mTimeout; Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +135 −139 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +140 −86 Original line number Diff line number Diff line Loading @@ -349,11 +349,9 @@ public class SyntheticPasswordManager { * a default all-zero key is used. If the value is not specified, a fresh random secret is * generated as the value. * * @return the value stored in the weaver slot * @throws RemoteException * @return the value stored in the weaver slot, or null if the operation fails */ private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) throws RemoteException { private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) { if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) { throw new RuntimeException("Invalid slot for weaver"); } Loading @@ -365,11 +363,16 @@ public class SyntheticPasswordManager { if (value == null) { value = secureRandom(mWeaverConfig.valueSize); } try { int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value)); if (writeStatus != WeaverStatus.OK) { Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus); return null; } } catch (RemoteException e) { Log.e(TAG, "weaver write failed", e); return null; } return value; } Loading @@ -377,9 +380,8 @@ public class SyntheticPasswordManager { * Verify the supplied key against a weaver slot, returning a response indicating whether * the verification is successful, throttled or failed. If successful, the bound secret * is also returned. * @throws RemoteException */ private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException { private VerifyCredentialResponse weaverVerify(int slot, byte[] key) { if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) { throw new RuntimeException("Invalid slot for weaver"); } Loading @@ -389,7 +391,9 @@ public class SyntheticPasswordManager { throw new RuntimeException("Invalid key size for weaver"); } final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1]; mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> { try { mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> { switch (status) { case WeaverReadStatus.OK: response[0] = new VerifyCredentialResponse( Loading @@ -405,7 +409,8 @@ public class SyntheticPasswordManager { Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot); } else { response[0] = new VerifyCredentialResponse(readResponse.timeout); Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot); Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot); } break; case WeaverReadStatus.FAILED: Loading @@ -418,6 +423,10 @@ public class SyntheticPasswordManager { break; } }); } catch (RemoteException e) { response[0] = VerifyCredentialResponse.ERROR; Log.e(TAG, "weaver read failed, slot: " + slot, e); } return response[0]; } Loading Loading @@ -460,12 +469,15 @@ public class SyntheticPasswordManager { * */ public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, byte[] hash, byte[] credential, int userId) throws RemoteException { byte[] hash, byte[] credential, int userId) { AuthenticationToken result = AuthenticationToken.create(); GateKeeperResponse response; if (hash != null) { response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword()); try { response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword()); } catch (RemoteException e) { throw new IllegalStateException("Failed to enroll credential duing SP init", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId); clearSidForUser(userId); Loading @@ -484,9 +496,13 @@ public class SyntheticPasswordManager { * Used when adding password to previously-unsecured devices. */ public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken, int userId) throws RemoteException { GateKeeperResponse response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword()); int userId) { GateKeeperResponse response; try { response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword()); } catch (RemoteException e) { throw new IllegalStateException("Failed to create new SID for user", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { Log.e(TAG, "Fail to create new SID for user " + userId); return; Loading Loading @@ -565,12 +581,8 @@ public class SyntheticPasswordManager { Set<Integer> usedSlots = getUsedWeaverSlots(); if (!usedSlots.contains(slot)) { Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId); try { weaverEnroll(slot, null, null); mPasswordSlotManager.markSlotDeleted(slot); } catch (RemoteException e) { Log.w(TAG, "Failed to destroy slot", e); } } else { Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId); } Loading Loading @@ -622,11 +634,12 @@ public class SyntheticPasswordManager { * * @see #newSidForUser * @see #clearSidForUser * @return a new password handle for the wrapped SP blob * @throw IllegalStateException if creation fails. */ public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, byte[] credential, int credentialType, AuthenticationToken authToken, int requestedQuality, int userId) throws RemoteException { int requestedQuality, int userId) { if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE; credential = DEFAULT_PASSWORD; Loading @@ -642,10 +655,11 @@ public class SyntheticPasswordManager { // Weaver based user password int weaverSlot = getNextAvailableWeaverSlot(); Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId); byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null); byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null); if (weaverSecret == null) { Log.e(TAG, "Fail to enroll user password under weaver " + userId); return DEFAULT_HANDLE; throw new IllegalStateException( "Fail to enroll user password under weaver " + userId); } saveWeaverSlot(weaverSlot, handle, userId); mPasswordSlotManager.markSlotInUse(weaverSlot); Loading @@ -657,13 +671,22 @@ public class SyntheticPasswordManager { } else { // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them // to prevent them from accumulating and causing problems. try { gatekeeper.clearSecureUserId(fakeUid(userId)); } catch (RemoteException ignore) { Log.w(TAG, "Failed to clear SID from gatekeeper"); } // GateKeeper based user password GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null, GateKeeperResponse response; try { response = gatekeeper.enroll(fakeUid(userId), null, null, passwordTokenToGkInput(pwdToken)); } catch (RemoteException e) { throw new IllegalStateException("Failed to enroll password for new SP blob", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId); return DEFAULT_HANDLE; throw new IllegalStateException( "Fail to enroll user password when creating SP for user " + userId); } pwd.passwordHandle = response.getPayload(); sid = sidFromPasswordHandle(pwd.passwordHandle); Loading @@ -680,14 +703,20 @@ public class SyntheticPasswordManager { public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper, byte[] userCredential, int credentialType, ICheckCredentialProgressCallback progressCallback) throws RemoteException { ICheckCredentialProgressCallback progressCallback) { PersistentData persistentData = mStorage.readPersistentDataBlock(); if (persistentData.type == PersistentData.TYPE_SP) { PasswordData pwd = PasswordData.fromBytes(persistentData.payload); byte[] pwdToken = computePasswordToken(userCredential, pwd); GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId), GateKeeperResponse response; try { response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId), 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken)); } catch (RemoteException e) { Log.e(TAG, "FRP verifyChallenge failed", e); return VerifyCredentialResponse.ERROR; } return VerifyCredentialResponse.fromGateKeeperResponse(response); } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) { PasswordData pwd = PasswordData.fromBytes(persistentData.payload); Loading Loading @@ -805,11 +834,9 @@ public class SyntheticPasswordManager { } if (isWeaverAvailable()) { int slot = getNextAvailableWeaverSlot(); try { Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId); weaverEnroll(slot, null, tokenData.weaverSecret); } catch (RemoteException e) { Log.e(TAG, "Failed to enroll weaver secret when activating token", e); if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) { Log.e(TAG, "Failed to enroll weaver secret when activating token"); return false; } saveWeaverSlot(slot, handle, userId); Loading Loading @@ -859,7 +886,7 @@ public class SyntheticPasswordManager { */ public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, long handle, byte[] credential, int userId, ICheckCredentialProgressCallback progressCallback) throws RemoteException { ICheckCredentialProgressCallback progressCallback) { if (credential == null) { credential = DEFAULT_PASSWORD; } Loading @@ -886,14 +913,28 @@ public class SyntheticPasswordManager { applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload()); } else { byte[] gkPwdToken = passwordTokenToGkInput(pwdToken); GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L, GateKeeperResponse response; try { response = gatekeeper.verifyChallenge(fakeUid(userId), 0L, pwd.passwordHandle, gkPwdToken); } catch (RemoteException e) { Log.e(TAG, "gatekeeper verify failed", e); result.gkResponse = VerifyCredentialResponse.ERROR; return result; } int responseCode = response.getResponseCode(); if (responseCode == GateKeeperResponse.RESPONSE_OK) { result.gkResponse = VerifyCredentialResponse.OK; if (response.getShouldReEnroll()) { GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId), GateKeeperResponse reenrollResponse; try { reenrollResponse = gatekeeper.enroll(fakeUid(userId), pwd.passwordHandle, gkPwdToken, gkPwdToken); } catch (RemoteException e) { Log.w(TAG, "Fail to invoke gatekeeper.enroll", e); reenrollResponse = GateKeeperResponse.ERROR; // continue the flow anyway } if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { pwd.passwordHandle = reenrollResponse.getPayload(); saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId); Loading Loading @@ -922,7 +963,11 @@ public class SyntheticPasswordManager { // Supplied credential passes first stage weaver/gatekeeper check so it should be correct. // Notify the callback so the keyguard UI can proceed immediately. if (progressCallback != null) { try { progressCallback.onCredentialVerified(); } catch (RemoteException e) { Log.w(TAG, "progressCallback throws exception", e); } } result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, applicationId, sid, userId); Loading @@ -938,8 +983,7 @@ public class SyntheticPasswordManager { * verification to referesh the SID & Auth token maintained by the system. */ public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword( IGateKeeperService gatekeeper, long handle, byte[] token, int userId) throws RemoteException { IGateKeeperService gatekeeper, long handle, byte[] token, int userId) { AuthenticationResult result = new AuthenticationResult(); byte[] secdiscardable = loadSecdiscardable(handle, userId); int slotId = loadWeaverSlot(handle, userId); Loading Loading @@ -1028,38 +1072,48 @@ public class SyntheticPasswordManager { * decrypt SP. */ public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper, @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException { @NonNull AuthenticationToken auth, long challenge, int userId) { byte[] spHandle = loadSyntheticPasswordHandle(userId); if (spHandle == null) { // There is no password handle associated with the given user, i.e. the user is not // secured by lockscreen and has no SID, so just return here; return null; } VerifyCredentialResponse result; GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge, GateKeeperResponse response; try { response = gatekeeper.verifyChallenge(userId, challenge, spHandle, auth.deriveGkPassword()); } catch (RemoteException e) { Log.e(TAG, "Fail to verify with gatekeeper " + userId, e); return VerifyCredentialResponse.ERROR; } int responseCode = response.getResponseCode(); if (responseCode == GateKeeperResponse.RESPONSE_OK) { result = new VerifyCredentialResponse(response.getPayload()); VerifyCredentialResponse result = new VerifyCredentialResponse(response.getPayload()); if (response.getShouldReEnroll()) { response = gatekeeper.enroll(userId, spHandle, spHandle, auth.deriveGkPassword()); try { response = gatekeeper.enroll(userId, spHandle, spHandle, auth.deriveGkPassword()); } catch (RemoteException e) { Log.e(TAG, "Failed to invoke gatekeeper.enroll", e); response = GateKeeperResponse.ERROR; } if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { spHandle = response.getPayload(); saveSyntheticPasswordHandle(spHandle, userId); // Call self again to re-verify with updated handle return verifyChallenge(gatekeeper, auth, challenge, userId); } else { // Fall through, return result from the previous verification attempt. Log.w(TAG, "Fail to re-enroll SP handle for user " + userId); // Fall through, return existing handle } } return result; } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { result = new VerifyCredentialResponse(response.getTimeout()); return new VerifyCredentialResponse(response.getTimeout()); } else { result = VerifyCredentialResponse.ERROR; return VerifyCredentialResponse.ERROR; } return result; } public boolean existsHandle(long handle, int userId) { Loading