summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/hardware/face/FaceManager.java6
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java37
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl5
-rw-r--r--core/java/com/android/internal/widget/LockPatternChecker.java53
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java86
-rw-r--r--core/java/com/android/internal/widget/LockscreenCredential.java2
-rw-r--r--core/java/com/android/internal/widget/VerifyCredentialResponse.java166
7 files changed, 192 insertions, 163 deletions
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 885d137dd2fe..19cb13c7e114 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -1073,12 +1073,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
/**
* @hide
*/
- public abstract static class GenerateChallengeCallback {
- public abstract void onGenerateChallengeResult(long challenge);
+ public interface GenerateChallengeCallback {
+ void onGenerateChallengeResult(long challenge);
}
private abstract static class InternalGenerateChallengeCallback
- extends GenerateChallengeCallback {}
+ implements GenerateChallengeCallback {}
private class OnEnrollCancelListener implements OnCancelListener {
@Override
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index e384da7574ad..71598eb6394f 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -377,12 +377,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
/**
* @hide
*/
- public abstract static class GenerateChallengeCallback {
- public abstract void onChallengeGenerated(long challenge);
+ public interface GenerateChallengeCallback {
+ void onChallengeGenerated(long challenge);
}
private abstract static class InternalGenerateChallengeCallback
- extends GenerateChallengeCallback {}
+ implements GenerateChallengeCallback {}
/**
* Request authentication of a crypto object. This call warms up the fingerprint hardware
@@ -581,37 +581,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
- * Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the
- * TEE/hardware operation is complete.
- * @return challenge generated in the TEE/hardware
- * @hide
- */
- @RequiresPermission(MANAGE_FINGERPRINT)
- public long generateChallengeBlocking() {
- final AtomicReference<Long> result = new AtomicReference<>();
- final CountDownLatch latch = new CountDownLatch(1);
- final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() {
- @Override
- public void onChallengeGenerated(long challenge) {
- result.set(challenge);
- latch.countDown();
- }
- };
-
- generateChallenge(callback);
-
- try {
- latch.await(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- Slog.e(TAG, "Interrupted while generatingChallenge", e);
- e.printStackTrace();
- }
-
- return result.get();
- }
-
-
- /**
* Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a
* HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification.
* The HardwareAuthenticationToken can then be sent to the biometric HAL together with a
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index e35fda1ee76d..d5d635de81d8 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -47,8 +47,9 @@ interface ILockSettings {
void resetKeyStore(int userId);
VerifyCredentialResponse checkCredential(in LockscreenCredential credential, int userId,
in ICheckCredentialProgressCallback progressCallback);
- VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, long challenge, int userId);
- VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, long challenge, int userId);
+ VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, int userId, int flags);
+ VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, int userId, int flags);
+ VerifyCredentialResponse verifyGatekeeperPassword(in byte[] gatekeeperPassword, long challenge, int userId);
boolean checkVoldPassword(int userId);
int getCredentialType(int userId);
byte[] getHashFactor(in LockscreenCredential currentCredential, int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 85a45fd8e0c0..5adbc583140f 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -1,5 +1,6 @@
package com.android.internal.widget;
+import android.annotation.NonNull;
import android.os.AsyncTask;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
@@ -41,11 +42,11 @@ public final class LockPatternChecker {
/**
* Invoked when a security verification is finished.
*
- * @param attestation The attestation that the challenge was verified, or null.
+ * @param response The response, optionally containing Gatekeeper HAT or Gatekeeper Password
* @param throttleTimeoutMs The amount of time in ms to wait before reattempting
- * the call. Only non-0 if attestation is null.
+ * the call. Only non-0 if the response is {@link VerifyCredentialResponse#RESPONSE_RETRY}.
*/
- void onVerified(byte[] attestation, int throttleTimeoutMs);
+ void onVerified(@NonNull VerifyCredentialResponse response, int throttleTimeoutMs);
}
/**
@@ -53,33 +54,27 @@ public final class LockPatternChecker {
*
* @param utils The LockPatternUtils instance to use.
* @param credential The credential to check.
- * @param challenge The challenge to verify against the credential.
* @param userId The user to check against the credential.
+ * @param flags See {@link LockPatternUtils.VerifyFlag}
* @param callback The callback to be invoked with the verification result.
*/
public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils,
final LockscreenCredential credential,
- final long challenge,
final int userId,
+ final @LockPatternUtils.VerifyFlag int flags,
final OnVerifyCallback callback) {
// Create a copy of the credential since checking credential is asynchrounous.
final LockscreenCredential credentialCopy = credential.duplicate();
- AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
- private int mThrottleTimeout;
-
+ AsyncTask<Void, Void, VerifyCredentialResponse> task =
+ new AsyncTask<Void, Void, VerifyCredentialResponse>() {
@Override
- protected byte[] doInBackground(Void... args) {
- try {
- return utils.verifyCredential(credentialCopy, challenge, userId);
- } catch (RequestThrottledException ex) {
- mThrottleTimeout = ex.getTimeoutMs();
- return null;
- }
+ protected VerifyCredentialResponse doInBackground(Void... args) {
+ return utils.verifyCredential(credentialCopy, userId, flags);
}
@Override
- protected void onPostExecute(byte[] result) {
- callback.onVerified(result, mThrottleTimeout);
+ protected void onPostExecute(@NonNull VerifyCredentialResponse result) {
+ callback.onVerified(result, result.getTimeout());
credentialCopy.zeroize();
}
@@ -141,33 +136,27 @@ public final class LockPatternChecker {
*
* @param utils The LockPatternUtils instance to use.
* @param credential The credential to check.
- * @param challenge The challenge to verify against the credential.
* @param userId The user to check against the credential.
+ * @param flags See {@link LockPatternUtils.VerifyFlag}
* @param callback The callback to be invoked with the verification result.
*/
public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils,
final LockscreenCredential credential,
- final long challenge,
final int userId,
+ final @LockPatternUtils.VerifyFlag int flags,
final OnVerifyCallback callback) {
- // Create a copy of the credential since checking credential is asynchrounous.
+ // Create a copy of the credential since checking credential is asynchronous.
final LockscreenCredential credentialCopy = credential.duplicate();
- AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
- private int mThrottleTimeout;
-
+ AsyncTask<Void, Void, VerifyCredentialResponse> task =
+ new AsyncTask<Void, Void, VerifyCredentialResponse>() {
@Override
- protected byte[] doInBackground(Void... args) {
- try {
- return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId);
- } catch (RequestThrottledException ex) {
- mThrottleTimeout = ex.getTimeoutMs();
- return null;
- }
+ protected VerifyCredentialResponse doInBackground(Void... args) {
+ return utils.verifyTiedProfileChallenge(credentialCopy, userId, flags);
}
@Override
- protected void onPostExecute(byte[] result) {
- callback.onVerified(result, mThrottleTimeout);
+ protected void onPostExecute(@NonNull VerifyCredentialResponse response) {
+ callback.onVerified(response, response.getTimeout());
credentialCopy.zeroize();
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 93690cdfc811..f7370d6a22f9 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -130,6 +130,18 @@ public class LockPatternUtils {
public @interface CredentialType {}
/**
+ * Flag provided to {@link #verifyCredential(LockscreenCredential, long, int, int)} . If set,
+ * the method will return the Gatekeeper Password in the {@link VerifyCredentialResponse}.
+ */
+ public static final int VERIFY_FLAG_RETURN_GK_PW = 1 << 0;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ VERIFY_FLAG_RETURN_GK_PW
+ })
+ public @interface VerifyFlag {}
+
+ /**
* Special user id for triggering the FRP verification flow.
*/
public static final int USER_FRP = UserHandle.USER_NULL + 1;
@@ -374,29 +386,46 @@ public class LockPatternUtils {
* If credential matches, return an opaque attestation that the challenge was verified.
*
* @param credential The credential to check.
- * @param challenge The challenge to verify against the credential
* @param userId The user whose credential is being verified
- * @return the attestation that the challenge was verified, or null
- * @throws RequestThrottledException if credential verification is being throttled due to
- * to many incorrect attempts.
+ * @param flags See {@link VerifyFlag}
* @throws IllegalStateException if called on the main thread.
*/
- public byte[] verifyCredential(@NonNull LockscreenCredential credential, long challenge,
- int userId) throws RequestThrottledException {
+ @NonNull
+ public VerifyCredentialResponse verifyCredential(@NonNull LockscreenCredential credential,
+ int userId, @VerifyFlag int flags) {
throwIfCalledOnMainThread();
try {
- VerifyCredentialResponse response = getLockSettings().verifyCredential(
- credential, challenge, userId);
- if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
- return response.getPayload();
- } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
- throw new RequestThrottledException(response.getTimeout());
+ final VerifyCredentialResponse response = getLockSettings().verifyCredential(
+ credential, userId, flags);
+ if (response == null) {
+ return VerifyCredentialResponse.ERROR;
} else {
- return null;
+ return response;
}
} catch (RemoteException re) {
Log.e(TAG, "failed to verify credential", re);
- return null;
+ return VerifyCredentialResponse.ERROR;
+ }
+ }
+
+ /**
+ * With the Gatekeeper Password returned via {@link #verifyCredential(LockscreenCredential,
+ * int, int)}, request Gatekeeper to create a HardwareAuthToken wrapping the given
+ * challenge.
+ */
+ @NonNull
+ public VerifyCredentialResponse verifyGatekeeperPassword(@NonNull byte[] gatekeeperPassword,
+ long challenge, int userId) {
+ try {
+ final VerifyCredentialResponse response = getLockSettings().verifyGatekeeperPassword(
+ gatekeeperPassword, challenge, userId);
+ if (response == null) {
+ return VerifyCredentialResponse.ERROR;
+ }
+ return response;
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to verify gatekeeper password", e);
+ return VerifyCredentialResponse.ERROR;
}
}
@@ -418,8 +447,9 @@ public class LockPatternUtils {
try {
VerifyCredentialResponse response = getLockSettings().checkCredential(
credential, userId, wrapCallback(progressCallback));
-
- if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+ if (response == null) {
+ return false;
+ } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return true;
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
throw new RequestThrottledException(response.getTimeout());
@@ -439,30 +469,26 @@ public class LockPatternUtils {
* verified.
*
* @param credential The parent user's credential to check.
- * @param challenge The challenge to verify against the credential
* @return the attestation that the challenge was verified, or null
* @param userId The managed profile user id
- * @throws RequestThrottledException if credential verification is being throttled due to
- * to many incorrect attempts.
+ * @param flags See {@link VerifyFlag}
* @throws IllegalStateException if called on the main thread.
*/
- public byte[] verifyTiedProfileChallenge(@NonNull LockscreenCredential credential,
- long challenge, int userId) throws RequestThrottledException {
+ @NonNull
+ public VerifyCredentialResponse verifyTiedProfileChallenge(
+ @NonNull LockscreenCredential credential, int userId, @VerifyFlag int flags) {
throwIfCalledOnMainThread();
try {
- VerifyCredentialResponse response =
- getLockSettings().verifyTiedProfileChallenge(credential, challenge, userId);
-
- if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
- return response.getPayload();
- } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
- throw new RequestThrottledException(response.getTimeout());
+ final VerifyCredentialResponse response = getLockSettings()
+ .verifyTiedProfileChallenge(credential, userId, flags);
+ if (response == null) {
+ return VerifyCredentialResponse.ERROR;
} else {
- return null;
+ return response;
}
} catch (RemoteException re) {
Log.e(TAG, "failed to verify tied profile credential", re);
- return null;
+ return VerifyCredentialResponse.ERROR;
}
}
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 55f30fb89253..a488449db019 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -48,7 +48,7 @@ import java.util.Objects;
* // Process the credential in some way
* }
* </pre>
- * With this construct, we can garantee that there will be no copies of the password left in
+ * With this construct, we can guarantee that there will be no copies of the password left in
* memory when the credential goes out of scope. This should help mitigate certain class of
* attacks where the attcker gains read-only access to full device memory (cold boot attack,
* unsecured software/hardware memory dumping interfaces such as JTAG).
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index 7d1c70647092..e09eb4228219 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -16,11 +16,16 @@
package com.android.internal.widget;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.gatekeeper.GateKeeperResponse;
import android.util.Slog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Response object for a ILockSettings credential verification request.
* @hide
@@ -30,78 +35,114 @@ public final class VerifyCredentialResponse implements Parcelable {
public static final int RESPONSE_ERROR = -1;
public static final int RESPONSE_OK = 0;
public static final int RESPONSE_RETRY = 1;
-
- public static final VerifyCredentialResponse OK = new VerifyCredentialResponse();
- public static final VerifyCredentialResponse ERROR
- = new VerifyCredentialResponse(RESPONSE_ERROR, 0, null);
+ @IntDef({RESPONSE_ERROR,
+ RESPONSE_OK,
+ RESPONSE_RETRY})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ResponseCode {}
+
+ public static final VerifyCredentialResponse OK = new VerifyCredentialResponse.Builder()
+ .build();
+ public static final VerifyCredentialResponse ERROR = fromError();
private static final String TAG = "VerifyCredentialResponse";
- private int mResponseCode;
- private byte[] mPayload;
- private int mTimeout;
+ private final @ResponseCode int mResponseCode;
+ private final int mTimeout;
+ @Nullable private final byte[] mGatekeeperHAT;
+ @Nullable private final byte[] mGatekeeperPw;
public static final Parcelable.Creator<VerifyCredentialResponse> CREATOR
= new Parcelable.Creator<VerifyCredentialResponse>() {
@Override
public VerifyCredentialResponse createFromParcel(Parcel source) {
- int responseCode = source.readInt();
- VerifyCredentialResponse response = new VerifyCredentialResponse(responseCode, 0, null);
- if (responseCode == RESPONSE_RETRY) {
- response.setTimeout(source.readInt());
- } else if (responseCode == RESPONSE_OK) {
- int size = source.readInt();
- if (size > 0) {
- byte[] payload = new byte[size];
- source.readByteArray(payload);
- response.setPayload(payload);
- }
- }
- return response;
+ final @ResponseCode int responseCode = source.readInt();
+ final int timeout = source.readInt();
+ final byte[] gatekeeperHAT = source.createByteArray();
+ final byte[] gatekeeperPassword = source.createByteArray();
+
+ return new VerifyCredentialResponse(responseCode, timeout, gatekeeperHAT,
+ gatekeeperPassword);
}
@Override
public VerifyCredentialResponse[] newArray(int size) {
return new VerifyCredentialResponse[size];
}
-
};
- public VerifyCredentialResponse() {
- mResponseCode = RESPONSE_OK;
- mPayload = null;
- }
+ public static class Builder {
+ @Nullable private byte[] mGatekeeperHAT;
+ @Nullable private byte[] mGatekeeperPassword;
+ /**
+ * @param gatekeeperHAT Gatekeeper HardwareAuthToken, minted upon successful authentication.
+ */
+ public Builder setGatekeeperHAT(byte[] gatekeeperHAT) {
+ mGatekeeperHAT = gatekeeperHAT;
+ return this;
+ }
- public VerifyCredentialResponse(byte[] payload) {
- mPayload = payload;
- mResponseCode = RESPONSE_OK;
+ public Builder setGatekeeperPassword(byte[] gatekeeperPassword) {
+ mGatekeeperPassword = gatekeeperPassword;
+ return this;
+ }
+
+ /**
+ * Builds a VerifyCredentialResponse with {@link #RESPONSE_OK} and any other parameters
+ * that were preveiously set.
+ * @return
+ */
+ public VerifyCredentialResponse build() {
+ return new VerifyCredentialResponse(RESPONSE_OK,
+ 0 /* timeout */,
+ mGatekeeperHAT,
+ mGatekeeperPassword);
+ }
}
- public VerifyCredentialResponse(int timeout) {
- mTimeout = timeout;
- mResponseCode = RESPONSE_RETRY;
- mPayload = null;
+ /**
+ * Since timeouts are always an error, provide a way to create the VerifyCredentialResponse
+ * object directly. None of the other fields (Gatekeeper HAT, Gatekeeper Password, etc)
+ * are valid in this case. Similarly, the response code will always be
+ * {@link #RESPONSE_RETRY}.
+ */
+ public static VerifyCredentialResponse fromTimeout(int timeout) {
+ return new VerifyCredentialResponse(RESPONSE_RETRY,
+ timeout,
+ null /* gatekeeperHAT */,
+ null /* gatekeeperPassword */);
+ }
+
+ /**
+ * Since error (incorrect password) should never result in any of the other fields from
+ * being populated, provide a default method to return a VerifyCredentialResponse.
+ */
+ public static VerifyCredentialResponse fromError() {
+ return new VerifyCredentialResponse(RESPONSE_ERROR,
+ 0 /* timeout */,
+ null /* gatekeeperHAT */,
+ null /* gatekeeperPassword */);
}
- private VerifyCredentialResponse(int responseCode, int timeout, byte[] payload) {
+ private VerifyCredentialResponse(@ResponseCode int responseCode, int timeout,
+ @Nullable byte[] gatekeeperHAT, @Nullable byte[] gatekeeperPassword) {
mResponseCode = responseCode;
mTimeout = timeout;
- mPayload = payload;
+ mGatekeeperHAT = gatekeeperHAT;
+ mGatekeeperPw = gatekeeperPassword;
+ }
+
+ public VerifyCredentialResponse stripPayload() {
+ return new VerifyCredentialResponse(mResponseCode, mTimeout,
+ null /* gatekeeperHAT */, null /* gatekeeperPassword */);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mResponseCode);
- if (mResponseCode == RESPONSE_RETRY) {
- dest.writeInt(mTimeout);
- } else if (mResponseCode == RESPONSE_OK) {
- if (mPayload != null) {
- dest.writeInt(mPayload.length);
- dest.writeByteArray(mPayload);
- } else {
- dest.writeInt(0);
- }
- }
+ dest.writeInt(mTimeout);
+ dest.writeByteArray(mGatekeeperHAT);
+ dest.writeByteArray(mGatekeeperPw);
}
@Override
@@ -109,48 +150,51 @@ public final class VerifyCredentialResponse implements Parcelable {
return 0;
}
- public byte[] getPayload() {
- return mPayload;
+ @Nullable
+ public byte[] getGatekeeperHAT() {
+ return mGatekeeperHAT;
+ }
+
+ @Nullable
+ public byte[] getGatekeeperPw() {
+ return mGatekeeperPw;
}
public int getTimeout() {
return mTimeout;
}
- public int getResponseCode() {
+ public @ResponseCode int getResponseCode() {
return mResponseCode;
}
- private void setTimeout(int timeout) {
- mTimeout = timeout;
- }
-
- private void setPayload(byte[] payload) {
- mPayload = payload;
+ public boolean isMatched() {
+ return mResponseCode == RESPONSE_OK;
}
- public VerifyCredentialResponse stripPayload() {
- return new VerifyCredentialResponse(mResponseCode, mTimeout, new byte[0]);
+ @Override
+ public String toString() {
+ return "Response: " + mResponseCode
+ + ", GK HAT: " + (mGatekeeperHAT != null)
+ + ", GK PW: " + (mGatekeeperPw != null);
}
public static VerifyCredentialResponse fromGateKeeperResponse(
GateKeeperResponse gateKeeperResponse) {
- VerifyCredentialResponse response;
int responseCode = gateKeeperResponse.getResponseCode();
if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
- response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
+ return fromTimeout(gateKeeperResponse.getTimeout());
} else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
byte[] token = gateKeeperResponse.getPayload();
if (token == null) {
// something's wrong if there's no payload with a challenge
Slog.e(TAG, "verifyChallenge response had no associated payload");
- response = VerifyCredentialResponse.ERROR;
+ return fromError();
} else {
- response = new VerifyCredentialResponse(token);
+ return new VerifyCredentialResponse.Builder().setGatekeeperHAT(token).build();
}
} else {
- response = VerifyCredentialResponse.ERROR;
+ return fromError();
}
- return response;
}
}