Bug 1862132 - Part 1. Move data class to WebAuthnUtils. r=jschanck,geckoview-reviewers,owlish
I would like to move data class from WebAuthnTokenManager to new class since WebAuthnTokenManager may becomes complex when we add Credential Manager support. Differential Revision: https://phabricator.services.mozilla.com/D207500
This commit is contained in:
@@ -14,9 +14,8 @@ namespace mozilla::jni {
|
|||||||
template <>
|
template <>
|
||||||
RefPtr<dom::WebAuthnRegisterResult> Java2Native(
|
RefPtr<dom::WebAuthnRegisterResult> Java2Native(
|
||||||
mozilla::jni::Object::Param aData, JNIEnv* aEnv) {
|
mozilla::jni::Object::Param aData, JNIEnv* aEnv) {
|
||||||
MOZ_ASSERT(
|
MOZ_ASSERT(aData.IsInstanceOf<java::WebAuthnUtils::MakeCredentialResponse>());
|
||||||
aData.IsInstanceOf<java::WebAuthnTokenManager::MakeCredentialResponse>());
|
java::WebAuthnUtils::MakeCredentialResponse::LocalRef response(aData);
|
||||||
java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef response(aData);
|
|
||||||
RefPtr<dom::WebAuthnRegisterResult> result =
|
RefPtr<dom::WebAuthnRegisterResult> result =
|
||||||
new dom::WebAuthnRegisterResult(response);
|
new dom::WebAuthnRegisterResult(response);
|
||||||
return result;
|
return result;
|
||||||
@@ -25,9 +24,8 @@ RefPtr<dom::WebAuthnRegisterResult> Java2Native(
|
|||||||
template <>
|
template <>
|
||||||
RefPtr<dom::WebAuthnSignResult> Java2Native(mozilla::jni::Object::Param aData,
|
RefPtr<dom::WebAuthnSignResult> Java2Native(mozilla::jni::Object::Param aData,
|
||||||
JNIEnv* aEnv) {
|
JNIEnv* aEnv) {
|
||||||
MOZ_ASSERT(
|
MOZ_ASSERT(aData.IsInstanceOf<java::WebAuthnUtils::GetAssertionResponse>());
|
||||||
aData.IsInstanceOf<java::WebAuthnTokenManager::GetAssertionResponse>());
|
java::WebAuthnUtils::GetAssertionResponse::LocalRef response(aData);
|
||||||
java::WebAuthnTokenManager::GetAssertionResponse::LocalRef response(aData);
|
|
||||||
RefPtr<dom::WebAuthnSignResult> result =
|
RefPtr<dom::WebAuthnSignResult> result =
|
||||||
new dom::WebAuthnSignResult(response);
|
new dom::WebAuthnSignResult(response);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
|
|
||||||
#ifdef MOZ_WIDGET_ANDROID
|
#ifdef MOZ_WIDGET_ANDROID
|
||||||
# include "mozilla/java/WebAuthnTokenManagerNatives.h"
|
# include "mozilla/java/WebAuthnUtilsNatives.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
@@ -44,8 +44,7 @@ class WebAuthnRegisterResult final : public nsIWebAuthnRegisterResult {
|
|||||||
|
|
||||||
#ifdef MOZ_WIDGET_ANDROID
|
#ifdef MOZ_WIDGET_ANDROID
|
||||||
explicit WebAuthnRegisterResult(
|
explicit WebAuthnRegisterResult(
|
||||||
const java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef&
|
const java::WebAuthnUtils::MakeCredentialResponse::LocalRef& aResponse) {
|
||||||
aResponse) {
|
|
||||||
mAttestationObject.AppendElements(
|
mAttestationObject.AppendElements(
|
||||||
reinterpret_cast<uint8_t*>(
|
reinterpret_cast<uint8_t*>(
|
||||||
aResponse->AttestationObject()->GetElements().Elements()),
|
aResponse->AttestationObject()->GetElements().Elements()),
|
||||||
@@ -167,8 +166,7 @@ class WebAuthnSignResult final : public nsIWebAuthnSignResult {
|
|||||||
|
|
||||||
#ifdef MOZ_WIDGET_ANDROID
|
#ifdef MOZ_WIDGET_ANDROID
|
||||||
explicit WebAuthnSignResult(
|
explicit WebAuthnSignResult(
|
||||||
const java::WebAuthnTokenManager::GetAssertionResponse::LocalRef&
|
const java::WebAuthnUtils::GetAssertionResponse::LocalRef& aResponse) {
|
||||||
aResponse) {
|
|
||||||
mAuthenticatorData.AppendElements(
|
mAuthenticatorData.AppendElements(
|
||||||
reinterpret_cast<uint8_t*>(
|
reinterpret_cast<uint8_t*>(
|
||||||
aResponse->AuthData()->GetElements().Elements()),
|
aResponse->AuthData()->GetElements().Elements()),
|
||||||
|
|||||||
@@ -0,0 +1,263 @@
|
|||||||
|
/* -*- Mode: Java; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil; -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=100: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.util;
|
||||||
|
|
||||||
|
import android.util.Base64;
|
||||||
|
import com.google.android.gms.fido.common.Transport;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.mozilla.gecko.annotation.WrapForJNI;
|
||||||
|
|
||||||
|
public class WebAuthnUtils {
|
||||||
|
private static final String LOGTAG = "WebAuthnUtils";
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
// from dom/webauthn/WebAuthnTransportIdentifiers.h
|
||||||
|
private static final byte AUTHENTICATOR_TRANSPORT_USB = 1;
|
||||||
|
private static final byte AUTHENTICATOR_TRANSPORT_NFC = 2;
|
||||||
|
private static final byte AUTHENTICATOR_TRANSPORT_BLE = 4;
|
||||||
|
private static final byte AUTHENTICATOR_TRANSPORT_INTERNAL = 8;
|
||||||
|
|
||||||
|
// From WebAuthentication.webidl
|
||||||
|
public enum AttestationPreference {
|
||||||
|
NONE,
|
||||||
|
INDIRECT,
|
||||||
|
DIRECT,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Transport> getTransportsForByte(final byte transports) {
|
||||||
|
final ArrayList<Transport> result = new ArrayList<Transport>();
|
||||||
|
if ((transports & AUTHENTICATOR_TRANSPORT_USB) == AUTHENTICATOR_TRANSPORT_USB) {
|
||||||
|
result.add(Transport.USB);
|
||||||
|
}
|
||||||
|
if ((transports & AUTHENTICATOR_TRANSPORT_NFC) == AUTHENTICATOR_TRANSPORT_NFC) {
|
||||||
|
result.add(Transport.NFC);
|
||||||
|
}
|
||||||
|
if ((transports & AUTHENTICATOR_TRANSPORT_BLE) == AUTHENTICATOR_TRANSPORT_BLE) {
|
||||||
|
result.add(Transport.BLUETOOTH_LOW_ENERGY);
|
||||||
|
}
|
||||||
|
if ((transports & AUTHENTICATOR_TRANSPORT_INTERNAL) == AUTHENTICATOR_TRANSPORT_INTERNAL) {
|
||||||
|
result.add(Transport.INTERNAL);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WebAuthnPublicCredential {
|
||||||
|
public final byte[] id;
|
||||||
|
public final byte transports;
|
||||||
|
|
||||||
|
public WebAuthnPublicCredential(final byte[] aId, final byte aTransports) {
|
||||||
|
this.id = aId;
|
||||||
|
this.transports = aTransports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<WebAuthnPublicCredential> CombineBuffers(
|
||||||
|
final Object[] idObjectList, final ByteBuffer transportList) {
|
||||||
|
if (idObjectList.length != transportList.remaining()) {
|
||||||
|
throw new RuntimeException("Couldn't extract allowed list!");
|
||||||
|
}
|
||||||
|
|
||||||
|
final ArrayList<WebAuthnPublicCredential> credList =
|
||||||
|
new ArrayList<WebAuthnPublicCredential>();
|
||||||
|
|
||||||
|
final byte[] transportBytes = new byte[transportList.remaining()];
|
||||||
|
transportList.get(transportBytes);
|
||||||
|
|
||||||
|
for (int i = 0; i < idObjectList.length; i++) {
|
||||||
|
final ByteBuffer id = (ByteBuffer) idObjectList[i];
|
||||||
|
final byte[] idBytes = new byte[id.remaining()];
|
||||||
|
id.get(idBytes);
|
||||||
|
|
||||||
|
credList.add(new WebAuthnPublicCredential(idBytes, transportBytes[i]));
|
||||||
|
}
|
||||||
|
return credList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapForJNI
|
||||||
|
public static class MakeCredentialResponse {
|
||||||
|
public final byte[] clientDataJson;
|
||||||
|
public final byte[] keyHandle;
|
||||||
|
public final byte[] attestationObject;
|
||||||
|
public final String[] transports;
|
||||||
|
public final String authenticatorAttachment;
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
private byte[] mClientDataJson;
|
||||||
|
private byte[] mKeyHandle;
|
||||||
|
private byte[] mAttestationObject;
|
||||||
|
private String[] mTransports;
|
||||||
|
private String mAuthenticatorAttachment;
|
||||||
|
|
||||||
|
public Builder() {}
|
||||||
|
|
||||||
|
public Builder setClientDataJson(final byte[] clientDataJson) {
|
||||||
|
this.mClientDataJson = clientDataJson;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setKeyHandle(final byte[] keyHandle) {
|
||||||
|
this.mKeyHandle = keyHandle;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setAttestationObject(final byte[] attestationObject) {
|
||||||
|
this.mAttestationObject = attestationObject;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setTransports(final String[] transports) {
|
||||||
|
this.mTransports = transports;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setAuthenticatorAttachment(final String authenticatorAttachment) {
|
||||||
|
this.mAuthenticatorAttachment = authenticatorAttachment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MakeCredentialResponse build() {
|
||||||
|
return new MakeCredentialResponse(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapForJNI(skip = true)
|
||||||
|
protected MakeCredentialResponse(final Builder builder) {
|
||||||
|
this.clientDataJson = builder.mClientDataJson;
|
||||||
|
this.keyHandle = builder.mKeyHandle;
|
||||||
|
this.attestationObject = builder.mAttestationObject;
|
||||||
|
this.transports = builder.mTransports;
|
||||||
|
this.authenticatorAttachment = builder.mAuthenticatorAttachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapForJNI(skip = true)
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("{");
|
||||||
|
sb.append("clientDataJson=")
|
||||||
|
.append(
|
||||||
|
Base64.encodeToString(
|
||||||
|
this.clientDataJson, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING))
|
||||||
|
.append(", keyHandle=")
|
||||||
|
.append(
|
||||||
|
Base64.encodeToString(
|
||||||
|
this.keyHandle, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING))
|
||||||
|
.append(", attestationObject=")
|
||||||
|
.append(
|
||||||
|
Base64.encodeToString(
|
||||||
|
this.attestationObject, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING))
|
||||||
|
.append(", transports=")
|
||||||
|
.append(String.join(", ", this.transports))
|
||||||
|
.append(", authenticatorAttachment=")
|
||||||
|
.append(this.authenticatorAttachment)
|
||||||
|
.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapForJNI
|
||||||
|
public static class GetAssertionResponse {
|
||||||
|
public final byte[] clientDataJson;
|
||||||
|
public final byte[] keyHandle;
|
||||||
|
public final byte[] authData;
|
||||||
|
public final byte[] signature;
|
||||||
|
public final byte[] userHandle;
|
||||||
|
public final String authenticatorAttachment;
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
private byte[] mClientDataJson;
|
||||||
|
private byte[] mKeyHandle;
|
||||||
|
private byte[] mAuthData;
|
||||||
|
private byte[] mSignature;
|
||||||
|
private byte[] mUserHandle;
|
||||||
|
private String mAuthenticatorAttachment;
|
||||||
|
|
||||||
|
public Builder() {}
|
||||||
|
|
||||||
|
public Builder setClientDataJson(final byte[] clientDataJson) {
|
||||||
|
this.mClientDataJson = clientDataJson;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setKeyHandle(final byte[] keyHandle) {
|
||||||
|
this.mKeyHandle = keyHandle;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setAuthData(final byte[] authData) {
|
||||||
|
this.mAuthData = authData;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setSignature(final byte[] signature) {
|
||||||
|
this.mSignature = signature;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setUserHandle(final byte[] userHandle) {
|
||||||
|
this.mUserHandle = userHandle;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setAuthenticatorAttachment(final String authenticatorAttachment) {
|
||||||
|
this.mAuthenticatorAttachment = authenticatorAttachment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetAssertionResponse build() {
|
||||||
|
return new GetAssertionResponse(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapForJNI(skip = true)
|
||||||
|
protected GetAssertionResponse(final Builder builder) {
|
||||||
|
this.clientDataJson = builder.mClientDataJson;
|
||||||
|
this.keyHandle = builder.mKeyHandle;
|
||||||
|
this.authData = builder.mAuthData;
|
||||||
|
this.signature = builder.mSignature;
|
||||||
|
if (builder.mUserHandle == null) {
|
||||||
|
this.userHandle = new byte[0];
|
||||||
|
} else {
|
||||||
|
this.userHandle = builder.mUserHandle;
|
||||||
|
}
|
||||||
|
this.authenticatorAttachment = builder.mAuthenticatorAttachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapForJNI(skip = true)
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("{");
|
||||||
|
sb.append("clientDataJson=")
|
||||||
|
.append(
|
||||||
|
Base64.encodeToString(
|
||||||
|
this.clientDataJson, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING))
|
||||||
|
.append(", keyHandle=")
|
||||||
|
.append(
|
||||||
|
Base64.encodeToString(
|
||||||
|
this.keyHandle, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING))
|
||||||
|
.append(", authData=")
|
||||||
|
.append(
|
||||||
|
Base64.encodeToString(
|
||||||
|
this.authData, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING))
|
||||||
|
.append(", signature=")
|
||||||
|
.append(
|
||||||
|
Base64.encodeToString(
|
||||||
|
this.signature, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING))
|
||||||
|
.append(", userHandle=")
|
||||||
|
.append(
|
||||||
|
userHandle.length > 0
|
||||||
|
? Base64.encodeToString(
|
||||||
|
this.userHandle, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING)
|
||||||
|
: "(empty)")
|
||||||
|
.append(", authenticatorAttachment=")
|
||||||
|
.append(this.authenticatorAttachment)
|
||||||
|
.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,10 +7,8 @@ package org.mozilla.geckoview;
|
|||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Base64;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.android.gms.fido.Fido;
|
import com.google.android.gms.fido.Fido;
|
||||||
import com.google.android.gms.fido.common.Transport;
|
|
||||||
import com.google.android.gms.fido.fido2.Fido2ApiClient;
|
import com.google.android.gms.fido.fido2.Fido2ApiClient;
|
||||||
import com.google.android.gms.fido.fido2.Fido2PrivilegedApiClient;
|
import com.google.android.gms.fido.fido2.Fido2PrivilegedApiClient;
|
||||||
import com.google.android.gms.fido.fido2.api.common.Algorithm;
|
import com.google.android.gms.fido.fido2.api.common.Algorithm;
|
||||||
@@ -43,16 +41,11 @@ import java.util.List;
|
|||||||
import org.mozilla.gecko.GeckoAppShell;
|
import org.mozilla.gecko.GeckoAppShell;
|
||||||
import org.mozilla.gecko.annotation.WrapForJNI;
|
import org.mozilla.gecko.annotation.WrapForJNI;
|
||||||
import org.mozilla.gecko.util.GeckoBundle;
|
import org.mozilla.gecko.util.GeckoBundle;
|
||||||
|
import org.mozilla.gecko.util.WebAuthnUtils;
|
||||||
|
|
||||||
/* package */ class WebAuthnTokenManager {
|
/* package */ class WebAuthnTokenManager {
|
||||||
private static final String LOGTAG = "WebAuthnTokenManager";
|
private static final String LOGTAG = "WebAuthnTokenManager";
|
||||||
|
|
||||||
// from dom/webauthn/WebAuthnTransportIdentifiers.h
|
|
||||||
private static final byte AUTHENTICATOR_TRANSPORT_USB = 1;
|
|
||||||
private static final byte AUTHENTICATOR_TRANSPORT_NFC = 2;
|
|
||||||
private static final byte AUTHENTICATOR_TRANSPORT_BLE = 4;
|
|
||||||
private static final byte AUTHENTICATOR_TRANSPORT_INTERNAL = 8;
|
|
||||||
|
|
||||||
private static final Algorithm[] SUPPORTED_ALGORITHMS = {
|
private static final Algorithm[] SUPPORTED_ALGORITHMS = {
|
||||||
EC2Algorithm.ES256,
|
EC2Algorithm.ES256,
|
||||||
EC2Algorithm.ES384,
|
EC2Algorithm.ES384,
|
||||||
@@ -67,95 +60,17 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
RSAAlgorithm.RS512
|
RSAAlgorithm.RS512
|
||||||
};
|
};
|
||||||
|
|
||||||
private static List<Transport> getTransportsForByte(final byte transports) {
|
|
||||||
final ArrayList<Transport> result = new ArrayList<Transport>();
|
|
||||||
if ((transports & AUTHENTICATOR_TRANSPORT_USB) == AUTHENTICATOR_TRANSPORT_USB) {
|
|
||||||
result.add(Transport.USB);
|
|
||||||
}
|
|
||||||
if ((transports & AUTHENTICATOR_TRANSPORT_NFC) == AUTHENTICATOR_TRANSPORT_NFC) {
|
|
||||||
result.add(Transport.NFC);
|
|
||||||
}
|
|
||||||
if ((transports & AUTHENTICATOR_TRANSPORT_BLE) == AUTHENTICATOR_TRANSPORT_BLE) {
|
|
||||||
result.add(Transport.BLUETOOTH_LOW_ENERGY);
|
|
||||||
}
|
|
||||||
if ((transports & AUTHENTICATOR_TRANSPORT_INTERNAL) == AUTHENTICATOR_TRANSPORT_INTERNAL) {
|
|
||||||
result.add(Transport.INTERNAL);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class WebAuthnPublicCredential {
|
|
||||||
public final byte[] id;
|
|
||||||
public final byte transports;
|
|
||||||
|
|
||||||
public WebAuthnPublicCredential(final byte[] aId, final byte aTransports) {
|
|
||||||
this.id = aId;
|
|
||||||
this.transports = aTransports;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ArrayList<WebAuthnPublicCredential> CombineBuffers(
|
|
||||||
final Object[] idObjectList, final ByteBuffer transportList) {
|
|
||||||
if (idObjectList.length != transportList.remaining()) {
|
|
||||||
throw new RuntimeException("Couldn't extract allowed list!");
|
|
||||||
}
|
|
||||||
|
|
||||||
final ArrayList<WebAuthnPublicCredential> credList =
|
|
||||||
new ArrayList<WebAuthnPublicCredential>();
|
|
||||||
|
|
||||||
final byte[] transportBytes = new byte[transportList.remaining()];
|
|
||||||
transportList.get(transportBytes);
|
|
||||||
|
|
||||||
for (int i = 0; i < idObjectList.length; i++) {
|
|
||||||
final ByteBuffer id = (ByteBuffer) idObjectList[i];
|
|
||||||
final byte[] idBytes = new byte[id.remaining()];
|
|
||||||
id.get(idBytes);
|
|
||||||
|
|
||||||
credList.add(new WebAuthnPublicCredential(idBytes, transportBytes[i]));
|
|
||||||
}
|
|
||||||
return credList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// From WebAuthentication.webidl
|
|
||||||
public enum AttestationPreference {
|
|
||||||
NONE,
|
|
||||||
INDIRECT,
|
|
||||||
DIRECT,
|
|
||||||
}
|
|
||||||
|
|
||||||
@WrapForJNI
|
|
||||||
public static class MakeCredentialResponse {
|
|
||||||
public final byte[] clientDataJson;
|
|
||||||
public final byte[] keyHandle;
|
|
||||||
public final byte[] attestationObject;
|
|
||||||
public final String[] transports;
|
|
||||||
public final String authenticatorAttachment;
|
|
||||||
|
|
||||||
public MakeCredentialResponse(
|
|
||||||
final byte[] clientDataJson,
|
|
||||||
final byte[] keyHandle,
|
|
||||||
final byte[] attestationObject,
|
|
||||||
final String[] transports,
|
|
||||||
final String authenticatorAttachment) {
|
|
||||||
this.clientDataJson = clientDataJson;
|
|
||||||
this.keyHandle = keyHandle;
|
|
||||||
this.attestationObject = attestationObject;
|
|
||||||
this.transports = transports;
|
|
||||||
this.authenticatorAttachment = authenticatorAttachment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Exception extends RuntimeException {
|
public static class Exception extends RuntimeException {
|
||||||
public Exception(final String error) {
|
public Exception(final String error) {
|
||||||
super(error);
|
super(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GeckoResult<MakeCredentialResponse> makeCredential(
|
public static GeckoResult<WebAuthnUtils.MakeCredentialResponse> makeCredential(
|
||||||
final GeckoBundle credentialBundle,
|
final GeckoBundle credentialBundle,
|
||||||
final byte[] userId,
|
final byte[] userId,
|
||||||
final byte[] challenge,
|
final byte[] challenge,
|
||||||
final WebAuthnTokenManager.WebAuthnPublicCredential[] excludeList,
|
final WebAuthnUtils.WebAuthnPublicCredential[] excludeList,
|
||||||
final GeckoBundle authenticatorSelection,
|
final GeckoBundle authenticatorSelection,
|
||||||
final GeckoBundle extensions) {
|
final GeckoBundle extensions) {
|
||||||
if (!credentialBundle.containsKey("isWebAuthn")) {
|
if (!credentialBundle.containsKey("isWebAuthn")) {
|
||||||
@@ -227,12 +142,12 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
|
|
||||||
final List<PublicKeyCredentialDescriptor> excludedList =
|
final List<PublicKeyCredentialDescriptor> excludedList =
|
||||||
new ArrayList<PublicKeyCredentialDescriptor>();
|
new ArrayList<PublicKeyCredentialDescriptor>();
|
||||||
for (final WebAuthnTokenManager.WebAuthnPublicCredential cred : excludeList) {
|
for (final WebAuthnUtils.WebAuthnPublicCredential cred : excludeList) {
|
||||||
excludedList.add(
|
excludedList.add(
|
||||||
new PublicKeyCredentialDescriptor(
|
new PublicKeyCredentialDescriptor(
|
||||||
PublicKeyCredentialType.PUBLIC_KEY.toString(),
|
PublicKeyCredentialType.PUBLIC_KEY.toString(),
|
||||||
cred.id,
|
cred.id,
|
||||||
getTransportsForByte(cred.transports)));
|
WebAuthnUtils.getTransportsForByte(cred.transports)));
|
||||||
}
|
}
|
||||||
|
|
||||||
final PublicKeyCredentialRpEntity rp =
|
final PublicKeyCredentialRpEntity rp =
|
||||||
@@ -286,7 +201,7 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
intentTask = fidoClient.getRegisterPendingIntent(requestOptions);
|
intentTask = fidoClient.getRegisterPendingIntent(requestOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
final GeckoResult<MakeCredentialResponse> result = new GeckoResult<>();
|
final GeckoResult<WebAuthnUtils.MakeCredentialResponse> result = new GeckoResult<>();
|
||||||
|
|
||||||
intentTask.addOnSuccessListener(
|
intentTask.addOnSuccessListener(
|
||||||
pendingIntent -> {
|
pendingIntent -> {
|
||||||
@@ -321,36 +236,18 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
|
|
||||||
final AuthenticatorAttestationResponse responseData =
|
final AuthenticatorAttestationResponse responseData =
|
||||||
(AuthenticatorAttestationResponse) response;
|
(AuthenticatorAttestationResponse) response;
|
||||||
|
final WebAuthnUtils.MakeCredentialResponse credentialResponse =
|
||||||
|
new WebAuthnUtils.MakeCredentialResponse.Builder()
|
||||||
|
.setKeyHandle(publicKeyCredentialData.getRawId())
|
||||||
|
.setAuthenticatorAttachment(
|
||||||
|
publicKeyCredentialData.getAuthenticatorAttachment())
|
||||||
|
.setClientDataJson(responseData.getClientDataJSON())
|
||||||
|
.setAttestationObject(responseData.getAttestationObject())
|
||||||
|
.setTransports(responseData.getTransports())
|
||||||
|
.build();
|
||||||
|
|
||||||
Log.d(
|
Log.d(LOGTAG, "MakeCredentialResponse: " + credentialResponse.toString());
|
||||||
LOGTAG,
|
result.complete(credentialResponse);
|
||||||
"key handle: "
|
|
||||||
+ Base64.encodeToString(
|
|
||||||
publicKeyCredentialData.getRawId(), Base64.DEFAULT));
|
|
||||||
Log.d(
|
|
||||||
LOGTAG,
|
|
||||||
"clientDataJSON: "
|
|
||||||
+ Base64.encodeToString(
|
|
||||||
responseData.getClientDataJSON(), Base64.DEFAULT));
|
|
||||||
Log.d(
|
|
||||||
LOGTAG,
|
|
||||||
"attestation Object: "
|
|
||||||
+ Base64.encodeToString(
|
|
||||||
responseData.getAttestationObject(), Base64.DEFAULT));
|
|
||||||
|
|
||||||
Log.d(LOGTAG, "transports: " + String.join(", ", responseData.getTransports()));
|
|
||||||
Log.d(
|
|
||||||
LOGTAG,
|
|
||||||
"authenticatorAttachment :"
|
|
||||||
+ publicKeyCredentialData.getAuthenticatorAttachment());
|
|
||||||
|
|
||||||
result.complete(
|
|
||||||
new WebAuthnTokenManager.MakeCredentialResponse(
|
|
||||||
responseData.getClientDataJSON(),
|
|
||||||
publicKeyCredentialData.getRawId(),
|
|
||||||
responseData.getAttestationObject(),
|
|
||||||
responseData.getTransports(),
|
|
||||||
publicKeyCredentialData.getAuthenticatorAttachment()));
|
|
||||||
},
|
},
|
||||||
e -> {
|
e -> {
|
||||||
Log.w(LOGTAG, "Failed to launch activity: ", e);
|
Log.w(LOGTAG, "Failed to launch activity: ", e);
|
||||||
@@ -368,7 +265,7 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@WrapForJNI(calledFrom = "gecko")
|
@WrapForJNI(calledFrom = "gecko")
|
||||||
private static GeckoResult<MakeCredentialResponse> webAuthnMakeCredential(
|
private static GeckoResult<WebAuthnUtils.MakeCredentialResponse> webAuthnMakeCredential(
|
||||||
final GeckoBundle credentialBundle,
|
final GeckoBundle credentialBundle,
|
||||||
final ByteBuffer userId,
|
final ByteBuffer userId,
|
||||||
final ByteBuffer challenge,
|
final ByteBuffer challenge,
|
||||||
@@ -376,7 +273,7 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
final ByteBuffer transportList,
|
final ByteBuffer transportList,
|
||||||
final GeckoBundle authenticatorSelection,
|
final GeckoBundle authenticatorSelection,
|
||||||
final GeckoBundle extensions) {
|
final GeckoBundle extensions) {
|
||||||
final ArrayList<WebAuthnPublicCredential> excludeList;
|
final ArrayList<WebAuthnUtils.WebAuthnPublicCredential> excludeList;
|
||||||
|
|
||||||
final byte[] challBytes = new byte[challenge.remaining()];
|
final byte[] challBytes = new byte[challenge.remaining()];
|
||||||
final byte[] userBytes = new byte[userId.remaining()];
|
final byte[] userBytes = new byte[userId.remaining()];
|
||||||
@@ -384,7 +281,7 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
challenge.get(challBytes);
|
challenge.get(challBytes);
|
||||||
userId.get(userBytes);
|
userId.get(userBytes);
|
||||||
|
|
||||||
excludeList = WebAuthnPublicCredential.CombineBuffers(idList, transportList);
|
excludeList = WebAuthnUtils.WebAuthnPublicCredential.CombineBuffers(idList, transportList);
|
||||||
} catch (final RuntimeException e) {
|
} catch (final RuntimeException e) {
|
||||||
Log.w(LOGTAG, "Couldn't extract nio byte arrays!", e);
|
Log.w(LOGTAG, "Couldn't extract nio byte arrays!", e);
|
||||||
return GeckoResult.fromException(new WebAuthnTokenManager.Exception("UNKNOWN_ERR"));
|
return GeckoResult.fromException(new WebAuthnTokenManager.Exception("UNKNOWN_ERR"));
|
||||||
@@ -395,7 +292,7 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
credentialBundle,
|
credentialBundle,
|
||||||
userBytes,
|
userBytes,
|
||||||
challBytes,
|
challBytes,
|
||||||
excludeList.toArray(new WebAuthnPublicCredential[0]),
|
excludeList.toArray(new WebAuthnUtils.WebAuthnPublicCredential[0]),
|
||||||
authenticatorSelection,
|
authenticatorSelection,
|
||||||
extensions);
|
extensions);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
@@ -408,31 +305,6 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@WrapForJNI
|
|
||||||
public static class GetAssertionResponse {
|
|
||||||
public final byte[] clientDataJson;
|
|
||||||
public final byte[] keyHandle;
|
|
||||||
public final byte[] authData;
|
|
||||||
public final byte[] signature;
|
|
||||||
public final byte[] userHandle;
|
|
||||||
public final String authenticatorAttachment;
|
|
||||||
|
|
||||||
public GetAssertionResponse(
|
|
||||||
final byte[] clientDataJson,
|
|
||||||
final byte[] keyHandle,
|
|
||||||
final byte[] authData,
|
|
||||||
final byte[] signature,
|
|
||||||
final byte[] userHandle,
|
|
||||||
final String authenticatorAttachment) {
|
|
||||||
this.clientDataJson = clientDataJson;
|
|
||||||
this.keyHandle = keyHandle;
|
|
||||||
this.authData = authData;
|
|
||||||
this.signature = signature;
|
|
||||||
this.userHandle = userHandle;
|
|
||||||
this.authenticatorAttachment = authenticatorAttachment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static WebAuthnTokenManager.Exception parseErrorResponse(
|
private static WebAuthnTokenManager.Exception parseErrorResponse(
|
||||||
final AuthenticatorResponse response) {
|
final AuthenticatorResponse response) {
|
||||||
if (!(response instanceof AuthenticatorErrorResponse)) {
|
if (!(response instanceof AuthenticatorErrorResponse)) {
|
||||||
@@ -447,9 +319,9 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
return new WebAuthnTokenManager.Exception(responseData.getErrorCode().name());
|
return new WebAuthnTokenManager.Exception(responseData.getErrorCode().name());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GeckoResult<GetAssertionResponse> getAssertion(
|
private static GeckoResult<WebAuthnUtils.GetAssertionResponse> getAssertion(
|
||||||
final byte[] challenge,
|
final byte[] challenge,
|
||||||
final WebAuthnTokenManager.WebAuthnPublicCredential[] allowList,
|
final WebAuthnUtils.WebAuthnPublicCredential[] allowList,
|
||||||
final GeckoBundle assertionBundle,
|
final GeckoBundle assertionBundle,
|
||||||
final GeckoBundle extensions) {
|
final GeckoBundle extensions) {
|
||||||
|
|
||||||
@@ -460,12 +332,12 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
|
|
||||||
final List<PublicKeyCredentialDescriptor> allowedList =
|
final List<PublicKeyCredentialDescriptor> allowedList =
|
||||||
new ArrayList<PublicKeyCredentialDescriptor>();
|
new ArrayList<PublicKeyCredentialDescriptor>();
|
||||||
for (final WebAuthnTokenManager.WebAuthnPublicCredential cred : allowList) {
|
for (final WebAuthnUtils.WebAuthnPublicCredential cred : allowList) {
|
||||||
allowedList.add(
|
allowedList.add(
|
||||||
new PublicKeyCredentialDescriptor(
|
new PublicKeyCredentialDescriptor(
|
||||||
PublicKeyCredentialType.PUBLIC_KEY.toString(),
|
PublicKeyCredentialType.PUBLIC_KEY.toString(),
|
||||||
cred.id,
|
cred.id,
|
||||||
getTransportsForByte(cred.transports)));
|
WebAuthnUtils.getTransportsForByte(cred.transports)));
|
||||||
}
|
}
|
||||||
|
|
||||||
final AuthenticationExtensions.Builder extBuilder = new AuthenticationExtensions.Builder();
|
final AuthenticationExtensions.Builder extBuilder = new AuthenticationExtensions.Builder();
|
||||||
@@ -505,7 +377,7 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
intentTask = fidoClient.getSignPendingIntent(requestOptions);
|
intentTask = fidoClient.getSignPendingIntent(requestOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
final GeckoResult<GetAssertionResponse> result = new GeckoResult<>();
|
final GeckoResult<WebAuthnUtils.GetAssertionResponse> result = new GeckoResult<>();
|
||||||
intentTask.addOnSuccessListener(
|
intentTask.addOnSuccessListener(
|
||||||
pendingIntent -> {
|
pendingIntent -> {
|
||||||
GeckoRuntime.getInstance()
|
GeckoRuntime.getInstance()
|
||||||
@@ -539,46 +411,19 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
}
|
}
|
||||||
|
|
||||||
final AuthenticatorAssertionResponse responseData =
|
final AuthenticatorAssertionResponse responseData =
|
||||||
(AuthenticatorAssertionResponse) publicKeyCredentialData.getResponse();
|
(AuthenticatorAssertionResponse) response;
|
||||||
|
final WebAuthnUtils.GetAssertionResponse assertionResponse =
|
||||||
Log.d(
|
new WebAuthnUtils.GetAssertionResponse.Builder()
|
||||||
LOGTAG,
|
.setClientDataJson(responseData.getClientDataJSON())
|
||||||
"key handle: "
|
.setKeyHandle(publicKeyCredentialData.getRawId())
|
||||||
+ Base64.encodeToString(
|
.setAuthData(responseData.getAuthenticatorData())
|
||||||
publicKeyCredentialData.getRawId(), Base64.DEFAULT));
|
.setSignature(responseData.getSignature())
|
||||||
Log.d(
|
.setUserHandle(responseData.getUserHandle())
|
||||||
LOGTAG,
|
.setAuthenticatorAttachment(
|
||||||
"clientDataJSON: "
|
publicKeyCredentialData.getAuthenticatorAttachment())
|
||||||
+ Base64.encodeToString(
|
.build();
|
||||||
responseData.getClientDataJSON(), Base64.DEFAULT));
|
Log.d(LOGTAG, "GetAssertionResponse: " + assertionResponse.toString());
|
||||||
Log.d(
|
result.complete(assertionResponse);
|
||||||
LOGTAG,
|
|
||||||
"auth data: "
|
|
||||||
+ Base64.encodeToString(
|
|
||||||
responseData.getAuthenticatorData(), Base64.DEFAULT));
|
|
||||||
Log.d(
|
|
||||||
LOGTAG,
|
|
||||||
"signature: "
|
|
||||||
+ Base64.encodeToString(responseData.getSignature(), Base64.DEFAULT));
|
|
||||||
Log.d(
|
|
||||||
LOGTAG,
|
|
||||||
"authenticatorAttachment :"
|
|
||||||
+ publicKeyCredentialData.getAuthenticatorAttachment());
|
|
||||||
|
|
||||||
// Nullable field
|
|
||||||
byte[] userHandle = responseData.getUserHandle();
|
|
||||||
if (userHandle == null) {
|
|
||||||
userHandle = new byte[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
result.complete(
|
|
||||||
new WebAuthnTokenManager.GetAssertionResponse(
|
|
||||||
responseData.getClientDataJSON(),
|
|
||||||
publicKeyCredentialData.getRawId(),
|
|
||||||
responseData.getAuthenticatorData(),
|
|
||||||
responseData.getSignature(),
|
|
||||||
userHandle,
|
|
||||||
publicKeyCredentialData.getAuthenticatorAttachment()));
|
|
||||||
},
|
},
|
||||||
e -> {
|
e -> {
|
||||||
Log.w(LOGTAG, "Failed to get FIDO intent", e);
|
Log.w(LOGTAG, "Failed to get FIDO intent", e);
|
||||||
@@ -590,18 +435,18 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@WrapForJNI(calledFrom = "gecko")
|
@WrapForJNI(calledFrom = "gecko")
|
||||||
private static GeckoResult<GetAssertionResponse> webAuthnGetAssertion(
|
private static GeckoResult<WebAuthnUtils.GetAssertionResponse> webAuthnGetAssertion(
|
||||||
final ByteBuffer challenge,
|
final ByteBuffer challenge,
|
||||||
final Object[] idList,
|
final Object[] idList,
|
||||||
final ByteBuffer transportList,
|
final ByteBuffer transportList,
|
||||||
final GeckoBundle assertionBundle,
|
final GeckoBundle assertionBundle,
|
||||||
final GeckoBundle extensions) {
|
final GeckoBundle extensions) {
|
||||||
final ArrayList<WebAuthnPublicCredential> allowList;
|
final ArrayList<WebAuthnUtils.WebAuthnPublicCredential> allowList;
|
||||||
|
|
||||||
final byte[] challBytes = new byte[challenge.remaining()];
|
final byte[] challBytes = new byte[challenge.remaining()];
|
||||||
try {
|
try {
|
||||||
challenge.get(challBytes);
|
challenge.get(challBytes);
|
||||||
allowList = WebAuthnPublicCredential.CombineBuffers(idList, transportList);
|
allowList = WebAuthnUtils.WebAuthnPublicCredential.CombineBuffers(idList, transportList);
|
||||||
} catch (final RuntimeException e) {
|
} catch (final RuntimeException e) {
|
||||||
Log.w(LOGTAG, "Couldn't extract nio byte arrays!", e);
|
Log.w(LOGTAG, "Couldn't extract nio byte arrays!", e);
|
||||||
return GeckoResult.fromException(new WebAuthnTokenManager.Exception("UNKNOWN_ERR"));
|
return GeckoResult.fromException(new WebAuthnTokenManager.Exception("UNKNOWN_ERR"));
|
||||||
@@ -610,7 +455,7 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||||||
try {
|
try {
|
||||||
return getAssertion(
|
return getAssertion(
|
||||||
challBytes,
|
challBytes,
|
||||||
allowList.toArray(new WebAuthnPublicCredential[0]),
|
allowList.toArray(new WebAuthnUtils.WebAuthnPublicCredential[0]),
|
||||||
assertionBundle,
|
assertionBundle,
|
||||||
extensions);
|
extensions);
|
||||||
} catch (final java.lang.Exception e) {
|
} catch (final java.lang.Exception e) {
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ classes_with_WrapForJNI = [
|
|||||||
"SurfaceTextureListener",
|
"SurfaceTextureListener",
|
||||||
"TelemetryUtils",
|
"TelemetryUtils",
|
||||||
"WebAuthnTokenManager",
|
"WebAuthnTokenManager",
|
||||||
|
"WebAuthnUtils",
|
||||||
"WebMessage",
|
"WebMessage",
|
||||||
"WebNotification",
|
"WebNotification",
|
||||||
"WebNotificationDelegate",
|
"WebNotificationDelegate",
|
||||||
|
|||||||
Reference in New Issue
Block a user