refactor, and make separate testing buttons for different approaches
This commit is contained in:
@@ -1,20 +1,19 @@
|
|||||||
import asn1 from "asn1-ber";
|
import asn1 from "asn1-ber";
|
||||||
import { Buffer } from "buffer/";
|
import { Buffer } from "buffer/";
|
||||||
import { decode as cborDecode } from "cbor-x";
|
import { decode as cborDecode } from "cbor-x";
|
||||||
import { createJWS, JWTPayload } from "did-jwt";
|
import { JWTPayload } from "did-jwt";
|
||||||
import { DIDResolutionResult } from "did-resolver";
|
import { DIDResolutionResult } from "did-resolver";
|
||||||
import { sha256 } from "ethereum-cryptography/sha256.js";
|
import { sha256 } from "ethereum-cryptography/sha256.js";
|
||||||
import { bytesToMultibase } from "@veramo/utils";
|
import { bytesToMultibase } from "@veramo/utils";
|
||||||
import {
|
import {
|
||||||
startAuthentication,
|
startAuthentication,
|
||||||
startRegistration, WebAuthnAbortService,
|
startRegistration,
|
||||||
} from "@simplewebauthn/browser";
|
} from "@simplewebauthn/browser";
|
||||||
import {
|
import {
|
||||||
generateAuthenticationOptions,
|
generateAuthenticationOptions,
|
||||||
generateRegistrationOptions,
|
generateRegistrationOptions,
|
||||||
verifyAuthenticationResponse,
|
verifyAuthenticationResponse,
|
||||||
verifyRegistrationResponse,
|
verifyRegistrationResponse,
|
||||||
VerifyRegistrationResponseOpts,
|
|
||||||
} from "@simplewebauthn/server";
|
} from "@simplewebauthn/server";
|
||||||
import { VerifyAuthenticationResponseOpts } from "@simplewebauthn/server/esm/authentication/verifyAuthenticationResponse";
|
import { VerifyAuthenticationResponseOpts } from "@simplewebauthn/server/esm/authentication/verifyAuthenticationResponse";
|
||||||
import {
|
import {
|
||||||
@@ -23,7 +22,6 @@ import {
|
|||||||
PublicKeyCredentialRequestOptionsJSON,
|
PublicKeyCredentialRequestOptionsJSON,
|
||||||
} from "@simplewebauthn/types";
|
} from "@simplewebauthn/types";
|
||||||
|
|
||||||
import { generateRandomBytes } from "@/libs/crypto";
|
|
||||||
import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers";
|
import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers";
|
||||||
|
|
||||||
export interface JWK {
|
export interface JWK {
|
||||||
@@ -54,13 +52,6 @@ export async function registerCredential(userId: Uint8Array) {
|
|||||||
// Don't prompt users for additional information about the authenticator
|
// Don't prompt users for additional information about the authenticator
|
||||||
// (Recommended for smoother UX)
|
// (Recommended for smoother UX)
|
||||||
attestationType: "none",
|
attestationType: "none",
|
||||||
// Prevent users from re-registering existing authenticators
|
|
||||||
// excludeCredentials: userPasskeys.map(passkey => ({
|
|
||||||
// id: passkey.id,
|
|
||||||
// // Optional
|
|
||||||
// transports: passkey.transports,
|
|
||||||
// })),
|
|
||||||
// // See "Guiding use of authenticators via authenticatorSelection" below
|
|
||||||
authenticatorSelection: {
|
authenticatorSelection: {
|
||||||
// Defaults
|
// Defaults
|
||||||
residentKey: "preferred",
|
residentKey: "preferred",
|
||||||
@@ -69,24 +60,21 @@ export async function registerCredential(userId: Uint8Array) {
|
|||||||
authenticatorAttachment: "platform",
|
authenticatorAttachment: "platform",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
// someday, instead of simplwebauthn, we'll go direct: navigator.credentials.create with PublicKeyCredentialCreationOptions
|
||||||
|
// with pubKeyCredParams: { type: "public-key", alg: -7 }
|
||||||
const attResp = await startRegistration(options);
|
const attResp = await startRegistration(options);
|
||||||
console.log("attResp", attResp);
|
|
||||||
const verification = await verifyRegistrationResponse({
|
const verification = await verifyRegistrationResponse({
|
||||||
response: attResp,
|
response: attResp,
|
||||||
expectedChallenge: options.challenge,
|
expectedChallenge: options.challenge,
|
||||||
expectedOrigin: window.location.origin,
|
expectedOrigin: window.location.origin,
|
||||||
expectedRPID: window.location.hostname,
|
expectedRPID: window.location.hostname,
|
||||||
});
|
});
|
||||||
console.log("verification", verification);
|
|
||||||
const jwkObj = cborDecode(
|
// references for parsing auth data and getting the public key
|
||||||
verification.registrationInfo?.credentialPublicKey as Uint8Array,
|
// https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/helpers/parseAuthenticatorData.ts#L11
|
||||||
);
|
// https://chatgpt.com/share/78a5c91d-099d-46dc-aa6d-fc0c916509fa
|
||||||
console.log("jwkObj from verification", jwkObj);
|
// https://chatgpt.com/share/3c13f061-6031-45bc-a2d7-3347c1e7a2d7
|
||||||
console.log(
|
|
||||||
"[1]==2 => kty EC",
|
|
||||||
"[3]==-7 => alg ES256",
|
|
||||||
"[-1]==1 => crv P-256",
|
|
||||||
);
|
|
||||||
const { publicKeyJwk } = cborToKeys(
|
const { publicKeyJwk } = cborToKeys(
|
||||||
verification.registrationInfo?.credentialPublicKey as Uint8Array,
|
verification.registrationInfo?.credentialPublicKey as Uint8Array,
|
||||||
);
|
);
|
||||||
@@ -101,99 +89,13 @@ export async function registerCredential(userId: Uint8Array) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function registerCredential2(userId: Uint8Array) {
|
|
||||||
const challenge = generateRandomBytes(32);
|
|
||||||
const publicKeyOptions: PublicKeyCredentialCreationOptions = {
|
|
||||||
challenge: challenge,
|
|
||||||
rp: {
|
|
||||||
name: "Time Safari",
|
|
||||||
id: window.location.hostname,
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
id: userId,
|
|
||||||
name: "Current-User",
|
|
||||||
displayName: "Current User",
|
|
||||||
},
|
|
||||||
pubKeyCredParams: [
|
|
||||||
{
|
|
||||||
type: "public-key",
|
|
||||||
alg: -7, // ES256 algorithm
|
|
||||||
},
|
|
||||||
],
|
|
||||||
authenticatorSelection: {
|
|
||||||
authenticatorAttachment: "platform",
|
|
||||||
userVerification: "preferred",
|
|
||||||
},
|
|
||||||
timeout: 60000,
|
|
||||||
attestation: "direct",
|
|
||||||
};
|
|
||||||
|
|
||||||
const credential = await navigator.credentials.create({
|
|
||||||
publicKey: publicKeyOptions,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("credential", credential);
|
|
||||||
console.log(credential?.id, " is the new ID base64-url-encoded");
|
|
||||||
console.log(arrayToBase64Url(credential?.rawId), " is the base64 rawId");
|
|
||||||
const attestationResponse = credential?.response;
|
|
||||||
const verfInput: VerifyRegistrationResponseOpts = {
|
|
||||||
response: {
|
|
||||||
id: credential?.id as string,
|
|
||||||
rawId: credential?.id as string, //Buffer.from(credential?.rawId).toString("base64"),
|
|
||||||
response: {
|
|
||||||
attestationObject: arrayToBase64Url(attestationResponse?.attestationObject),
|
|
||||||
clientDataJSON: arrayToBase64Url(attestationResponse?.clientDataJSON),
|
|
||||||
},
|
|
||||||
clientExtensionResults: {},
|
|
||||||
type: "public-key",
|
|
||||||
},
|
|
||||||
expectedChallenge: arrayToBase64Url(challenge),
|
|
||||||
expectedOrigin: window.location.origin,
|
|
||||||
};
|
|
||||||
console.log("verfInput", verfInput);
|
|
||||||
const verification = await verifyRegistrationResponse(verfInput);
|
|
||||||
console.log("verification", verification);
|
|
||||||
|
|
||||||
// Parse the attestation response to get the public key
|
|
||||||
const clientDataJSON = attestationResponse.clientDataJSON;
|
|
||||||
console.log("clientDataJSON raw", clientDataJSON);
|
|
||||||
console.log(
|
|
||||||
"clientDataJSON dec",
|
|
||||||
new TextDecoder("utf-8").decode(clientDataJSON),
|
|
||||||
);
|
|
||||||
const attestationObject = cborDecode(
|
|
||||||
new Uint8Array(attestationResponse.attestationObject),
|
|
||||||
);
|
|
||||||
console.log("attestationObject", attestationObject);
|
|
||||||
|
|
||||||
const { publicKeyJwk, publicKeyBuffer } = cborToKeys(
|
|
||||||
verification.registrationInfo?.credentialPublicKey as Uint8Array,
|
|
||||||
);
|
|
||||||
|
|
||||||
//const publicKeyBytes = extractPublicKeyCose(attestationObject.authData);
|
|
||||||
//const publicKeyJwk = extractPublicKeyJwk(attestationObject.authData);
|
|
||||||
|
|
||||||
return {
|
|
||||||
authData: attestationObject.authData,
|
|
||||||
credId: credential?.id,
|
|
||||||
rawId: credential?.rawId,
|
|
||||||
publicKeyJwk,
|
|
||||||
publicKeyBytes: publicKeyBuffer,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse authData
|
|
||||||
// here's one: https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/helpers/parseAuthenticatorData.ts#L11
|
|
||||||
// from https://chatgpt.com/c/0ce72fda-bc5d-42ff-a748-6022f6e39fa0
|
|
||||||
// from https://chatgpt.com/share/78a5c91d-099d-46dc-aa6d-fc0c916509fa
|
|
||||||
|
|
||||||
export function createPeerDid(publicKeyBytes: Uint8Array) {
|
export function createPeerDid(publicKeyBytes: Uint8Array) {
|
||||||
// https://github.com/decentralized-identity/veramo/blob/next/packages/did-provider-peer/src/peer-did-provider.ts#L67
|
// https://github.com/decentralized-identity/veramo/blob/next/packages/did-provider-peer/src/peer-did-provider.ts#L67
|
||||||
//const provider = new PeerDIDProvider({ defaultKms: LOCAL_KMS_NAME });
|
//const provider = new PeerDIDProvider({ defaultKms: LOCAL_KMS_NAME });
|
||||||
const methodSpecificId = bytesToMultibase(
|
const methodSpecificId = bytesToMultibase(
|
||||||
publicKeyBytes,
|
publicKeyBytes,
|
||||||
"base58btc",
|
"base58btc",
|
||||||
"secp256k1-pub",
|
"p256-pub",
|
||||||
);
|
);
|
||||||
return "did:peer:0" + methodSpecificId;
|
return "did:peer:0" + methodSpecificId;
|
||||||
}
|
}
|
||||||
@@ -205,11 +107,11 @@ export class PeerSetup {
|
|||||||
public clientDataJsonDecoded?: object;
|
public clientDataJsonDecoded?: object;
|
||||||
public clientDataJsonBase64Url?: Base64URLString;
|
public clientDataJsonBase64Url?: Base64URLString;
|
||||||
public signature?: Base64URLString;
|
public signature?: Base64URLString;
|
||||||
public publicKeyJwk?: JWK;
|
|
||||||
|
|
||||||
public async createJwt(fullPayload: object, credentialId: string) {
|
|
||||||
const header: JWTPayload = { typ: "JWT", alg: "ES256" };
|
|
||||||
|
|
||||||
|
public async createJwtSimplewebauthn(
|
||||||
|
fullPayload: object,
|
||||||
|
credentialId: string,
|
||||||
|
) {
|
||||||
this.challenge = new Uint8Array(Buffer.from(JSON.stringify(fullPayload)));
|
this.challenge = new Uint8Array(Buffer.from(JSON.stringify(fullPayload)));
|
||||||
// const payloadHash: Uint8Array = sha256(this.challenge);
|
// const payloadHash: Uint8Array = sha256(this.challenge);
|
||||||
const options: PublicKeyCredentialRequestOptionsJSON =
|
const options: PublicKeyCredentialRequestOptionsJSON =
|
||||||
@@ -233,83 +135,13 @@ export class PeerSetup {
|
|||||||
"utf-8",
|
"utf-8",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
//console.log("simple authenticatorData for signing", this.authenticatorData);
|
// console.log("simple authenticatorData for signing", this.authenticatorData);
|
||||||
this.signature = clientAuth.response.signature;
|
this.signature = clientAuth.response.signature;
|
||||||
|
|
||||||
const headerBase64 = Buffer.from(JSON.stringify(header)).toString("base64");
|
// Our custom type of JWANT means the signature is based on a concatenation of the two Webauthn properties
|
||||||
const payloadBase64 = clientAuth.response.clientDataJSON;
|
const header: JWTPayload = { typ: "JWANT", alg: "ES256" };
|
||||||
const signature = clientAuth.response.signature;
|
const headerBase64 = Buffer.from(JSON.stringify(header))
|
||||||
|
.toString("base64")
|
||||||
return headerBase64 + "." + payloadBase64 + "." + signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async createJwt2(fullPayload: object, credentialId: string) {
|
|
||||||
const header: JWTPayload = { typ: "JWT", alg: "ES256" };
|
|
||||||
const headerBase64 = Buffer.from(JSON.stringify(header)).toString("base64");
|
|
||||||
|
|
||||||
const dataToSignString = JSON.stringify(fullPayload);
|
|
||||||
const dataToSignBuffer = Buffer.from(dataToSignString);
|
|
||||||
|
|
||||||
//console.log("lower credentialId", credentialId);
|
|
||||||
this.challenge = new Uint8Array(dataToSignBuffer);
|
|
||||||
const options = {
|
|
||||||
publicKey: {
|
|
||||||
challenge: this.challenge.buffer,
|
|
||||||
rpID: window.location.hostname,
|
|
||||||
//allowCredentials: [{ id: credentialId, type: "public-key" }],
|
|
||||||
userVerification: "preferred",
|
|
||||||
//extensions: fullPayload,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// console.log("lower authentication options", options);
|
|
||||||
// console.log("lower options in base64", {
|
|
||||||
// publicKey: {
|
|
||||||
// challenge: bufferToBase64URLString(options.publicKey.challenge),
|
|
||||||
// rpID: window.location.hostname,
|
|
||||||
// userVerification: "preferred",
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
const credential = await navigator.credentials.get(options);
|
|
||||||
// console.log("lower credential get", credential);
|
|
||||||
// console.log("lower credential get in base64", {
|
|
||||||
// id: credential?.id,
|
|
||||||
// rawId: bufferToBase64URLString(credential?.rawId),
|
|
||||||
// response: {
|
|
||||||
// authenticatorData: bufferToBase64URLString(
|
|
||||||
// credential?.response.authenticatorData,
|
|
||||||
// ),
|
|
||||||
// clientDataJSON: bufferToBase64URLString(
|
|
||||||
// credential?.response.clientDataJSON,
|
|
||||||
// ),
|
|
||||||
// signature: bufferToBase64URLString(credential?.response.signature),
|
|
||||||
// },
|
|
||||||
// type: credential?.type,
|
|
||||||
// });
|
|
||||||
|
|
||||||
const authenticatorAssertionResponse = credential?.response;
|
|
||||||
|
|
||||||
this.authenticatorDataBase64Url =
|
|
||||||
authenticatorAssertionResponse.authenticatorData;
|
|
||||||
this.authenticatorData = Buffer.from(
|
|
||||||
this.authenticatorDataBase64Url as Base64URLString,
|
|
||||||
"base64",
|
|
||||||
).buffer;
|
|
||||||
// console.log("lower authenticator data", this.authenticatorData);
|
|
||||||
|
|
||||||
this.clientDataJsonBase64Url =
|
|
||||||
authenticatorAssertionResponse.clientDataJSON;
|
|
||||||
this.clientDataJsonDecoded = JSON.parse(
|
|
||||||
new TextDecoder("utf-8").decode(
|
|
||||||
authenticatorAssertionResponse.clientDataJSON,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// console.log("lower clientDataJSON decoded", this.clientDataJsonDecoded);
|
|
||||||
|
|
||||||
const origSignature = Buffer.from(
|
|
||||||
authenticatorAssertionResponse.signature,
|
|
||||||
).toString("base64");
|
|
||||||
this.signature = origSignature
|
|
||||||
.replace(/\+/g, "-")
|
.replace(/\+/g, "-")
|
||||||
.replace(/\//g, "_")
|
.replace(/\//g, "_")
|
||||||
.replace(/=+$/, "");
|
.replace(/=+$/, "");
|
||||||
@@ -320,121 +152,131 @@ export class PeerSetup {
|
|||||||
};
|
};
|
||||||
const dataInJwtString = JSON.stringify(dataInJwt);
|
const dataInJwtString = JSON.stringify(dataInJwt);
|
||||||
const payloadBase64 = Buffer.from(dataInJwtString).toString("base64");
|
const payloadBase64 = Buffer.from(dataInJwtString).toString("base64");
|
||||||
const jwt = headerBase64 + "." + payloadBase64 + "." + this.signature;
|
|
||||||
return jwt;
|
const signature = clientAuth.response.signature;
|
||||||
|
|
||||||
|
return headerBase64 + "." + payloadBase64 + "." + signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempted with JWS, but it will not match because it signs different content (header + payload)
|
public async createJwtNavigator(fullPayload: object, credentialId: string) {
|
||||||
//const signer = await this.webAuthnES256KSigner(credentialId);
|
const dataToSignString = JSON.stringify(fullPayload);
|
||||||
//const jwt = createJWS(fullPayload, signer, { typ: "JWT", alg: "ES256" });
|
const dataToSignBuffer = Buffer.from(dataToSignString);
|
||||||
async webAuthnES256KSigner(credentialID: string) {
|
|
||||||
return async (data: string | Uint8Array) => {
|
|
||||||
const signature = await this.generateWebAuthnSignature(
|
|
||||||
data,
|
|
||||||
credentialID,
|
|
||||||
);
|
|
||||||
|
|
||||||
// This converts from the browser ArrayBuffer to a Node.js Buffer, which is a requirement for the asn1 library.
|
// console.log("lower credentialId", credentialId);
|
||||||
const signatureBuffer = Buffer.from(signature);
|
this.challenge = new Uint8Array(dataToSignBuffer);
|
||||||
console.log("lower signature inside signer", signature);
|
|
||||||
console.log("lower buffer signature inside signer", signatureBuffer);
|
|
||||||
console.log("lower base64 buffer signature inside signer", signatureBuffer.toString("base64"));
|
|
||||||
// Decode the DER-encoded signature to extract R and S values
|
|
||||||
const reader = new asn1.BerReader(signatureBuffer);
|
|
||||||
console.log("lower after reader");
|
|
||||||
reader.readSequence();
|
|
||||||
console.log("lower after read sequence");
|
|
||||||
const r = reader.readString(asn1.Ber.Integer, true);
|
|
||||||
console.log("lower after r");
|
|
||||||
const s = reader.readString(asn1.Ber.Integer, true);
|
|
||||||
console.log("lower after r & s");
|
|
||||||
|
|
||||||
// Ensure R and S are 32 bytes each
|
|
||||||
const rBuffer = Buffer.from(r);
|
|
||||||
const sBuffer = Buffer.from(s);
|
|
||||||
console.log("lower after rBuffer & sBuffer", rBuffer, sBuffer);
|
|
||||||
const rWithoutPrefix = rBuffer.length > 32 ? rBuffer.slice(1) : rBuffer;
|
|
||||||
const sWithoutPrefix = sBuffer.length > 32 ? sBuffer.slice(1) : sBuffer;
|
|
||||||
const rPadded =
|
|
||||||
rWithoutPrefix.length < 32
|
|
||||||
? Buffer.concat([Buffer.alloc(32 - rWithoutPrefix.length), rBuffer])
|
|
||||||
: rWithoutPrefix;
|
|
||||||
const sPadded =
|
|
||||||
rWithoutPrefix.length < 32
|
|
||||||
? Buffer.concat([Buffer.alloc(32 - sWithoutPrefix.length), sBuffer])
|
|
||||||
: sWithoutPrefix;
|
|
||||||
|
|
||||||
// Concatenate R and S to form the 64-byte array (ECDSA signature format expected by JWT)
|
|
||||||
const combinedSignature = Buffer.concat([rPadded, sPadded]);
|
|
||||||
console.log(
|
|
||||||
"lower combinedSignature",
|
|
||||||
combinedSignature.length,
|
|
||||||
combinedSignature,
|
|
||||||
);
|
|
||||||
|
|
||||||
const combSig64 = combinedSignature.toString("base64");
|
|
||||||
console.log("lower combSig64", combSig64);
|
|
||||||
const combSig64Url = combSig64
|
|
||||||
.replace(/\+/g, "-")
|
|
||||||
.replace(/\//g, "_")
|
|
||||||
.replace(/=+$/, "");
|
|
||||||
console.log("lower combSig64Url", combSig64Url);
|
|
||||||
return combSig64Url;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async generateWebAuthnSignature(
|
|
||||||
dataToSign: string | Uint8Array, // from Signer interface
|
|
||||||
credentialId: string,
|
|
||||||
) {
|
|
||||||
if (!(dataToSign instanceof Uint8Array)) {
|
|
||||||
console.log("lower dataToSign & typeof ", typeof dataToSign, dataToSign);
|
|
||||||
dataToSign = new Uint8Array(base64URLStringToBuffer(dataToSign));
|
|
||||||
}
|
|
||||||
console.log("lower credentialId", credentialId);
|
|
||||||
this.challenge = dataToSign;
|
|
||||||
const options = {
|
const options = {
|
||||||
publicKey: {
|
publicKey: {
|
||||||
challenge: this.challenge.buffer,
|
challenge: this.challenge.buffer,
|
||||||
rpID: window.location.hostname,
|
rpID: window.location.hostname,
|
||||||
//allowCredentials: [{ id: credentialId, type: "public-key" }],
|
|
||||||
userVerification: "preferred",
|
userVerification: "preferred",
|
||||||
//extensions: fullPayload,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log("lower authentication options", options);
|
const credential = await navigator.credentials.get(options);
|
||||||
const assertion = await navigator.credentials.get(options);
|
// console.log("nav credential get", credential);
|
||||||
// console.log("lower credential get", assertion);
|
|
||||||
|
|
||||||
const authenticatorAssertionResponse = assertion?.response;
|
this.authenticatorDataBase64Url = bufferToBase64URLString(
|
||||||
|
credential?.response.authenticatorData,
|
||||||
this.authenticatorDataBase64Url =
|
);
|
||||||
authenticatorAssertionResponse.authenticatorData;
|
|
||||||
this.authenticatorData = Buffer.from(
|
this.authenticatorData = Buffer.from(
|
||||||
this.authenticatorDataBase64Url as Base64URLString,
|
this.authenticatorDataBase64Url as Base64URLString,
|
||||||
"base64",
|
"base64",
|
||||||
).buffer;
|
).buffer;
|
||||||
// console.log("lower authenticator data", this.authenticatorData);
|
|
||||||
|
|
||||||
this.clientDataJsonBase64Url =
|
this.clientDataJsonBase64Url = bufferToBase64URLString(
|
||||||
authenticatorAssertionResponse.clientDataJSON;
|
credential?.response.clientDataJSON,
|
||||||
this.clientDataJsonDecoded = JSON.parse(
|
);
|
||||||
new TextDecoder("utf-8").decode(
|
this.clientDataJsonDecoded = JSON.parse(
|
||||||
authenticatorAssertionResponse.clientDataJSON,
|
new TextDecoder("utf-8").decode(credential?.response.clientDataJSON),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
// console.log("lower clientDataJSON decoded", this.clientDataJsonDecoded);
|
|
||||||
|
|
||||||
this.signature = Buffer.from(
|
// Our custom type of JWANT means the signature is based on a concatenation of the two Webauthn properties
|
||||||
authenticatorAssertionResponse.signature,
|
const header: JWTPayload = { typ: "JWANT", alg: "ES256" };
|
||||||
).toString("base64");
|
const headerBase64 = Buffer.from(JSON.stringify(header))
|
||||||
|
.toString("base64")
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=+$/, "");
|
||||||
|
|
||||||
return this.signature;
|
const dataInJwt = {
|
||||||
|
AuthenticationData: this.authenticatorDataBase64Url,
|
||||||
|
ClientDataJSON: this.clientDataJsonBase64Url,
|
||||||
|
};
|
||||||
|
const dataInJwtString = JSON.stringify(dataInJwt);
|
||||||
|
const payloadBase64 = Buffer.from(dataInJwtString)
|
||||||
|
.toString("base64")
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=+$/, "");
|
||||||
|
|
||||||
|
const origSignature = Buffer.from(credential?.response.signature)
|
||||||
|
.toString("base64")
|
||||||
|
this.signature = origSignature
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=+$/, "");
|
||||||
|
|
||||||
|
const jwt = headerBase64 + "." + payloadBase64 + "." + this.signature;
|
||||||
|
return jwt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return a low-level signing function, similar to createJWS approach
|
||||||
|
// async webAuthnES256KSigner(credentialID: string) {
|
||||||
|
// return async (data: string | Uint8Array) => {
|
||||||
|
// // get signature from WebAuthn
|
||||||
|
// const signature = await this.generateWebAuthnSignature(data);
|
||||||
|
//
|
||||||
|
// // This converts from the browser ArrayBuffer to a Node.js Buffer, which is a requirement for the asn1 library.
|
||||||
|
// const signatureBuffer = Buffer.from(signature);
|
||||||
|
// console.log("lower signature inside signer", signature);
|
||||||
|
// console.log("lower buffer signature inside signer", signatureBuffer);
|
||||||
|
// console.log("lower base64 buffer signature inside signer", signatureBuffer.toString("base64"));
|
||||||
|
// // Decode the DER-encoded signature to extract R and S values
|
||||||
|
// const reader = new asn1.BerReader(signatureBuffer);
|
||||||
|
// console.log("lower after reader");
|
||||||
|
// reader.readSequence();
|
||||||
|
// console.log("lower after read sequence");
|
||||||
|
// const r = reader.readString(asn1.Ber.Integer, true);
|
||||||
|
// console.log("lower after r");
|
||||||
|
// const s = reader.readString(asn1.Ber.Integer, true);
|
||||||
|
// console.log("lower after r & s");
|
||||||
|
//
|
||||||
|
// // Ensure R and S are 32 bytes each
|
||||||
|
// const rBuffer = Buffer.from(r);
|
||||||
|
// const sBuffer = Buffer.from(s);
|
||||||
|
// console.log("lower after rBuffer & sBuffer", rBuffer, sBuffer);
|
||||||
|
// const rWithoutPrefix = rBuffer.length > 32 ? rBuffer.slice(1) : rBuffer;
|
||||||
|
// const sWithoutPrefix = sBuffer.length > 32 ? sBuffer.slice(1) : sBuffer;
|
||||||
|
// const rPadded =
|
||||||
|
// rWithoutPrefix.length < 32
|
||||||
|
// ? Buffer.concat([Buffer.alloc(32 - rWithoutPrefix.length), rBuffer])
|
||||||
|
// : rWithoutPrefix;
|
||||||
|
// const sPadded =
|
||||||
|
// rWithoutPrefix.length < 32
|
||||||
|
// ? Buffer.concat([Buffer.alloc(32 - sWithoutPrefix.length), sBuffer])
|
||||||
|
// : sWithoutPrefix;
|
||||||
|
//
|
||||||
|
// // Concatenate R and S to form the 64-byte array (ECDSA signature format expected by JWT)
|
||||||
|
// const combinedSignature = Buffer.concat([rPadded, sPadded]);
|
||||||
|
// console.log(
|
||||||
|
// "lower combinedSignature",
|
||||||
|
// combinedSignature.length,
|
||||||
|
// combinedSignature,
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// const combSig64 = combinedSignature.toString("base64");
|
||||||
|
// console.log("lower combSig64", combSig64);
|
||||||
|
// const combSig64Url = combSig64
|
||||||
|
// .replace(/\+/g, "-")
|
||||||
|
// .replace(/\//g, "_")
|
||||||
|
// .replace(/=+$/, "");
|
||||||
|
// console.log("lower combSig64Url", combSig64Url);
|
||||||
|
// return combSig64Url;
|
||||||
|
// };
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyJwt(
|
export async function verifyJwtSimplewebauthn(
|
||||||
jwt: string,
|
jwt: string,
|
||||||
credId: Base64URLString,
|
credId: Base64URLString,
|
||||||
rawId: Uint8Array,
|
rawId: Uint8Array,
|
||||||
@@ -447,35 +289,53 @@ export async function verifyJwt(
|
|||||||
publicKeyJwk: JWK,
|
publicKeyJwk: JWK,
|
||||||
signature: Base64URLString,
|
signature: Base64URLString,
|
||||||
) {
|
) {
|
||||||
// const authData = arrayToBase64Url(Buffer.from(authenticatorData));
|
const authData = arrayToBase64Url(Buffer.from(authenticatorData));
|
||||||
// const authOpts: VerifyAuthenticationResponseOpts = {
|
const authOpts: VerifyAuthenticationResponseOpts = {
|
||||||
// authenticator: {
|
authenticator: {
|
||||||
// credentialID: credId,
|
credentialID: credId,
|
||||||
// credentialPublicKey: publicKeyBytes,
|
credentialPublicKey: publicKeyBytes,
|
||||||
// counter: 0,
|
counter: 0,
|
||||||
// },
|
},
|
||||||
// expectedChallenge: arrayToBase64Url(challenge),
|
expectedChallenge: arrayToBase64Url(challenge),
|
||||||
// expectedOrigin: window.location.origin,
|
expectedOrigin: window.location.origin,
|
||||||
// expectedRPID: window.location.hostname,
|
expectedRPID: window.location.hostname,
|
||||||
// response: {
|
response: {
|
||||||
// authenticatorAttachment: "platform",
|
authenticatorAttachment: "platform",
|
||||||
// clientExtensionResults: {},
|
clientExtensionResults: {},
|
||||||
// id: credId,
|
id: credId,
|
||||||
// rawId: arrayToBase64Url(rawId),
|
rawId: arrayToBase64Url(rawId),
|
||||||
// response: {
|
response: {
|
||||||
// authenticatorData: authData,
|
authenticatorData: authData,
|
||||||
// clientDataJSON: arrayToBase64Url(
|
clientDataJSON: arrayToBase64Url(
|
||||||
// Buffer.from(JSON.stringify(clientDataJSON)),
|
Buffer.from(JSON.stringify(clientDataJSON)),
|
||||||
// ),
|
),
|
||||||
// signature: signature,
|
signature: signature,
|
||||||
// },
|
},
|
||||||
// type: "public-key",
|
type: "public-key",
|
||||||
// },
|
},
|
||||||
// };
|
};
|
||||||
// console.log("auth opts", authOpts);
|
const verification = await verifyAuthenticationResponse(authOpts);
|
||||||
// const verification = await verifyAuthenticationResponse(authOpts);
|
return verification.verified;
|
||||||
// console.log("auth verification", verification);
|
}
|
||||||
|
|
||||||
|
// I'd love to use this but it doesn't verify.
|
||||||
|
// Pequires:
|
||||||
|
// npm install @noble/curves
|
||||||
|
// ... and this import:
|
||||||
|
// import { p256 } from "@noble/curves/p256";
|
||||||
|
export async function verifyJwtP256(
|
||||||
|
jwt: string,
|
||||||
|
credId: Base64URLString,
|
||||||
|
rawId: Uint8Array,
|
||||||
|
authenticatorData: ArrayBuffer,
|
||||||
|
authenticatorDataBase64Url: Base64URLString,
|
||||||
|
challenge: Uint8Array,
|
||||||
|
clientDataJSON: object,
|
||||||
|
clientDataJsonBase64Url: Base64URLString,
|
||||||
|
publicKeyBytes: Uint8Array,
|
||||||
|
publicKeyJwk: JWK,
|
||||||
|
signature: Base64URLString,
|
||||||
|
) {
|
||||||
const authDataFromBase = Buffer.from(authenticatorDataBase64Url, "base64");
|
const authDataFromBase = Buffer.from(authenticatorDataBase64Url, "base64");
|
||||||
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
|
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
|
||||||
const sigBuffer = Buffer.from(signature, "base64");
|
const sigBuffer = Buffer.from(signature, "base64");
|
||||||
@@ -487,17 +347,37 @@ export async function verifyJwt(
|
|||||||
// Construct the preimage
|
// Construct the preimage
|
||||||
const preimage = Buffer.concat([authDataFromBase, hash]);
|
const preimage = Buffer.concat([authDataFromBase, hash]);
|
||||||
|
|
||||||
// console.log("finalSigBuffer", finalSigBuffer);
|
const isValid = p256.verify(
|
||||||
// console.log("preimage", preimage);
|
finalSigBuffer,
|
||||||
// console.log("publicKeyBytes", publicKeyBytes);
|
new Uint8Array(preimage),
|
||||||
|
publicKeyBytes,
|
||||||
|
);
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
// This uses p256 from @noble/curves/p256, which I would prefer but it's returning false.
|
export async function verifyJwtWebCrypto(
|
||||||
// const isValid = p256.verify(
|
jwt: string,
|
||||||
// finalSigBuffer,
|
credId: Base64URLString,
|
||||||
// new Uint8Array(preimage),
|
rawId: Uint8Array,
|
||||||
// publicKeyBytes,
|
authenticatorData: ArrayBuffer,
|
||||||
// );
|
authenticatorDataBase64Url: Base64URLString,
|
||||||
// console.log("isValid", isValid);
|
challenge: Uint8Array,
|
||||||
|
clientDataJSON: object,
|
||||||
|
clientDataJsonBase64Url: Base64URLString,
|
||||||
|
publicKeyBytes: Uint8Array,
|
||||||
|
publicKeyJwk: JWK,
|
||||||
|
signature: Base64URLString,
|
||||||
|
) {
|
||||||
|
const authDataFromBase = Buffer.from(authenticatorDataBase64Url, "base64");
|
||||||
|
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
|
||||||
|
const sigBuffer = Buffer.from(signature, "base64");
|
||||||
|
const finalSigBuffer = unwrapEC2Signature(sigBuffer);
|
||||||
|
|
||||||
|
// Hash the client data
|
||||||
|
const hash = sha256(clientDataFromBase);
|
||||||
|
|
||||||
|
// Construct the preimage
|
||||||
|
const preimage = Buffer.concat([authDataFromBase, hash]);
|
||||||
|
|
||||||
const WebCrypto = await getWebCrypto();
|
const WebCrypto = await getWebCrypto();
|
||||||
const verifyAlgorithm = {
|
const verifyAlgorithm = {
|
||||||
@@ -515,17 +395,12 @@ export async function verifyJwt(
|
|||||||
false,
|
false,
|
||||||
["verify"],
|
["verify"],
|
||||||
);
|
);
|
||||||
// console.log("verifyAlgorithm", verifyAlgorithm);
|
|
||||||
// console.log("publicKeyCryptoKey", publicKeyCryptoKey);
|
|
||||||
// console.log("finalSigBuffer", finalSigBuffer);
|
|
||||||
// console.log("preimage", preimage);
|
|
||||||
const verified = await WebCrypto.subtle.verify(
|
const verified = await WebCrypto.subtle.verify(
|
||||||
verifyAlgorithm,
|
verifyAlgorithm,
|
||||||
publicKeyCryptoKey,
|
publicKeyCryptoKey,
|
||||||
finalSigBuffer,
|
finalSigBuffer,
|
||||||
preimage,
|
preimage,
|
||||||
);
|
);
|
||||||
// console.log("verified", verified);
|
|
||||||
return verified;
|
return verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +503,6 @@ function base64URLStringToBuffer(base64URLString) {
|
|||||||
|
|
||||||
function cborToKeys(publicKeyBytes: Uint8Array) {
|
function cborToKeys(publicKeyBytes: Uint8Array) {
|
||||||
const jwkObj = cborDecode(publicKeyBytes);
|
const jwkObj = cborDecode(publicKeyBytes);
|
||||||
console.log("jwkObj from verification", jwkObj);
|
|
||||||
if (
|
if (
|
||||||
jwkObj[1] != 2 || // kty "EC"
|
jwkObj[1] != 2 || // kty "EC"
|
||||||
jwkObj[3] != -7 || // alg "ES256"
|
jwkObj[3] != -7 || // alg "ES256"
|
||||||
@@ -663,7 +537,7 @@ async function pemToCryptoKey(pem: string) {
|
|||||||
for (let i = 0; i < binaryDerString.length; i++) {
|
for (let i = 0; i < binaryDerString.length; i++) {
|
||||||
binaryDer[i] = binaryDerString.charCodeAt(i);
|
binaryDer[i] = binaryDerString.charCodeAt(i);
|
||||||
}
|
}
|
||||||
console.log("binaryDer", binaryDer.buffer);
|
// console.log("binaryDer", binaryDer.buffer);
|
||||||
return await window.crypto.subtle.importKey(
|
return await window.crypto.subtle.importKey(
|
||||||
"spki",
|
"spki",
|
||||||
binaryDer.buffer,
|
binaryDer.buffer,
|
||||||
|
|||||||
@@ -172,24 +172,51 @@
|
|||||||
|
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<h2 class="text-xl font-bold mb-4">Passkeys</h2>
|
<h2 class="text-xl font-bold mb-4">Passkeys</h2>
|
||||||
<button
|
<div>
|
||||||
@click="register()"
|
|
||||||
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
|
||||||
>
|
|
||||||
Register
|
Register
|
||||||
</button>
|
<button
|
||||||
<button
|
@click="register()"
|
||||||
@click="create()"
|
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
||||||
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
>
|
||||||
>
|
Simplewebauthn
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
Create
|
Create
|
||||||
</button>
|
<button
|
||||||
<button
|
@click="createJwtSimplewebauthn()"
|
||||||
@click="verify()"
|
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
||||||
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
>
|
||||||
>
|
Simplewebauthn
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="createJwtNavigator()"
|
||||||
|
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
||||||
|
>
|
||||||
|
Navigator
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
Verify
|
Verify
|
||||||
</button>
|
<button
|
||||||
|
@click="verifySimplewebauthn()"
|
||||||
|
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
||||||
|
>
|
||||||
|
Simplewebauthn
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="verifyWebCrypto()"
|
||||||
|
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
||||||
|
>
|
||||||
|
WebCrypto
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="verifyP256()"
|
||||||
|
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
|
||||||
|
>
|
||||||
|
p256 - broken
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -206,11 +233,11 @@ import {
|
|||||||
createPeerDid,
|
createPeerDid,
|
||||||
JWK,
|
JWK,
|
||||||
PeerSetup,
|
PeerSetup,
|
||||||
registerCredential,
|
registerCredential, verifyJwtP256,
|
||||||
verifyJwt,
|
verifyJwtSimplewebauthn,
|
||||||
|
verifyJwtWebCrypto,
|
||||||
} from "@/libs/didPeer";
|
} from "@/libs/didPeer";
|
||||||
import { Buffer } from "buffer";
|
import { JWTPayload } from "did-jwt";
|
||||||
import {JWTPayload} from "did-jwt";
|
|
||||||
|
|
||||||
const inputFileNameRef = ref<Blob>();
|
const inputFileNameRef = ref<Blob>();
|
||||||
|
|
||||||
@@ -220,21 +247,12 @@ export default class Help extends Vue {
|
|||||||
fileName?: string;
|
fileName?: string;
|
||||||
|
|
||||||
// for passkeys
|
// for passkeys
|
||||||
authenticatorData?: ArrayBuffer;
|
|
||||||
credId?: Base64URLString;
|
credId?: Base64URLString;
|
||||||
jwt?: string;
|
jwt?: string;
|
||||||
jwt2?: string;
|
|
||||||
payload = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
type: "GiveAction",
|
|
||||||
description: "pizza",
|
|
||||||
};
|
|
||||||
peerSetup?: PeerSetup;
|
peerSetup?: PeerSetup;
|
||||||
peerSetup2?: PeerSetup;
|
|
||||||
publicKeyJwk?: JWK;
|
publicKeyJwk?: JWK;
|
||||||
publicKeyBytes?: Uint8Array;
|
publicKeyBytes?: Uint8Array;
|
||||||
rawId?: Uint8Array;
|
rawId?: Uint8Array;
|
||||||
savedCredentialId = "";
|
|
||||||
userId?: ArrayBuffer;
|
userId?: ArrayBuffer;
|
||||||
|
|
||||||
async uploadFile(event: Event) {
|
async uploadFile(event: Event) {
|
||||||
@@ -269,8 +287,6 @@ export default class Help extends Vue {
|
|||||||
|
|
||||||
public async register() {
|
public async register() {
|
||||||
this.userId = generateRandomBytes(16).buffer;
|
this.userId = generateRandomBytes(16).buffer;
|
||||||
const encodedUserId = Buffer.from(this.userId).toString("base64");
|
|
||||||
console.log("encodedUserId", encodedUserId);
|
|
||||||
|
|
||||||
const cred = await registerCredential(this.userId as Uint8Array);
|
const cred = await registerCredential(this.userId as Uint8Array);
|
||||||
console.log("public key", cred);
|
console.log("public key", cred);
|
||||||
@@ -278,55 +294,59 @@ export default class Help extends Vue {
|
|||||||
this.publicKeyBytes = cred.publicKeyBytes;
|
this.publicKeyBytes = cred.publicKeyBytes;
|
||||||
this.credId = cred.credId as string;
|
this.credId = cred.credId as string;
|
||||||
this.rawId = cred.rawId as Uint8Array;
|
this.rawId = cred.rawId as Uint8Array;
|
||||||
this.savedCredentialId = this.credId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async create() {
|
public async createJwtSimplewebauthn() {
|
||||||
console.log("Starting a create");
|
|
||||||
const did = createPeerDid(this.publicKeyBytes as Uint8Array);
|
const did = createPeerDid(this.publicKeyBytes as Uint8Array);
|
||||||
// console.log("did", did);
|
console.log("generated peer did", did);
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
type: "GiveAction",
|
||||||
|
description: "pizza",
|
||||||
|
};
|
||||||
// from createJWT in did-jwt/src/JWT.ts
|
// from createJWT in did-jwt/src/JWT.ts
|
||||||
const timestamps: Partial<JWTPayload> = {
|
const timestamps: Partial<JWTPayload> = {
|
||||||
iat: Math.floor(Date.now() / 1000),
|
iat: Math.floor(Date.now() / 1000),
|
||||||
exp: undefined,
|
exp: undefined,
|
||||||
};
|
};
|
||||||
const fullPayload = { ...timestamps, ...this.payload, did };
|
const fullPayload = { ...timestamps, ...payload, did };
|
||||||
|
|
||||||
this.peerSetup = new PeerSetup();
|
this.peerSetup = new PeerSetup();
|
||||||
const rawJwt = await this.peerSetup.createJwt(
|
this.jwt = await this.peerSetup.createJwtSimplewebauthn(
|
||||||
fullPayload,
|
fullPayload,
|
||||||
this.credId as string,
|
this.credId as string,
|
||||||
);
|
);
|
||||||
//console.log("simple raw jwt", rawJwt);
|
|
||||||
this.jwt = rawJwt
|
|
||||||
.replace(/\+/g, "-")
|
|
||||||
.replace(/\//g, "_")
|
|
||||||
.replace(/=+$/, "");
|
|
||||||
console.log("simple jwt4url", this.jwt);
|
console.log("simple jwt4url", this.jwt);
|
||||||
// console.log("simple peerSetup", this.peerSetup);
|
|
||||||
|
|
||||||
this.peerSetup2 = new PeerSetup();
|
|
||||||
const rawJwt2 = await this.peerSetup2.createJwt2(
|
|
||||||
fullPayload,
|
|
||||||
this.credId as string,
|
|
||||||
);
|
|
||||||
// console.log("lower raw jwt2", rawJwt2);
|
|
||||||
this.jwt2 = rawJwt2
|
|
||||||
.replace(/\+/g, "-")
|
|
||||||
.replace(/\//g, "_")
|
|
||||||
.replace(/=+$/, "");
|
|
||||||
console.log("lower jwt4url2", this.jwt2);
|
|
||||||
// console.log("lower peerSetup2", this.peerSetup2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async verify() {
|
public async createJwtNavigator() {
|
||||||
if (!this.jwt || !this.peerSetup) {
|
const did = createPeerDid(this.publicKeyBytes as Uint8Array);
|
||||||
alert("Create a JWT first.");
|
console.log("generated peer did", did);
|
||||||
return;
|
|
||||||
}
|
const payload = {
|
||||||
const decoded = await verifyJwt(
|
"@context": "https://schema.org",
|
||||||
this.jwt,
|
type: "GiveAction",
|
||||||
|
description: "pizza",
|
||||||
|
};
|
||||||
|
// from createJWT in did-jwt/src/JWT.ts
|
||||||
|
const timestamps: Partial<JWTPayload> = {
|
||||||
|
iat: Math.floor(Date.now() / 1000),
|
||||||
|
exp: undefined,
|
||||||
|
};
|
||||||
|
const fullPayload = { ...timestamps, ...payload, did };
|
||||||
|
|
||||||
|
this.peerSetup = new PeerSetup();
|
||||||
|
this.jwt = await this.peerSetup.createJwtNavigator(
|
||||||
|
fullPayload,
|
||||||
|
this.credId as string,
|
||||||
|
);
|
||||||
|
console.log("lower jwt4url", this.jwt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async verifyP256() {
|
||||||
|
const decoded = await verifyJwtP256(
|
||||||
|
this.jwt as string,
|
||||||
this.credId as Base64URLString,
|
this.credId as Base64URLString,
|
||||||
this.rawId as Uint8Array,
|
this.rawId as Uint8Array,
|
||||||
this.peerSetup.authenticatorData as ArrayBuffer,
|
this.peerSetup.authenticatorData as ArrayBuffer,
|
||||||
@@ -339,21 +359,40 @@ export default class Help extends Vue {
|
|||||||
this.peerSetup.signature as Base64URLString,
|
this.peerSetup.signature as Base64URLString,
|
||||||
);
|
);
|
||||||
console.log("decoded", decoded);
|
console.log("decoded", decoded);
|
||||||
|
}
|
||||||
|
|
||||||
const decoded2 = await verifyJwt(
|
public async verifySimplewebauthn() {
|
||||||
this.jwt2 as string,
|
const decoded = await verifyJwtSimplewebauthn(
|
||||||
|
this.jwt as string,
|
||||||
this.credId as Base64URLString,
|
this.credId as Base64URLString,
|
||||||
this.rawId as Uint8Array,
|
this.rawId as Uint8Array,
|
||||||
this.peerSetup2.authenticatorData as ArrayBuffer,
|
this.peerSetup.authenticatorData as ArrayBuffer,
|
||||||
this.peerSetup2.authenticatorDataBase64Url as Base64URLString,
|
this.peerSetup.authenticatorDataBase64Url as Base64URLString,
|
||||||
this.peerSetup2.challenge as Uint8Array,
|
this.peerSetup.challenge as Uint8Array,
|
||||||
this.peerSetup2.clientDataJsonDecoded,
|
this.peerSetup.clientDataJsonDecoded,
|
||||||
this.peerSetup2.clientDataJsonBase64Url as Base64URLString,
|
this.peerSetup.clientDataJsonBase64Url as Base64URLString,
|
||||||
this.publicKeyBytes as Uint8Array,
|
this.publicKeyBytes as Uint8Array,
|
||||||
this.publicKeyJwk as JWK,
|
this.publicKeyJwk as JWK,
|
||||||
this.peerSetup2.signature as Base64URLString,
|
this.peerSetup.signature as Base64URLString,
|
||||||
);
|
);
|
||||||
console.log("decoded2", decoded2);
|
console.log("decoded", decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async verifyWebCrypto() {
|
||||||
|
const decoded = await verifyJwtWebCrypto(
|
||||||
|
this.jwt as string,
|
||||||
|
this.credId as Base64URLString,
|
||||||
|
this.rawId as Uint8Array,
|
||||||
|
this.peerSetup.authenticatorData as ArrayBuffer,
|
||||||
|
this.peerSetup.authenticatorDataBase64Url as Base64URLString,
|
||||||
|
this.peerSetup.challenge as Uint8Array,
|
||||||
|
this.peerSetup.clientDataJsonDecoded,
|
||||||
|
this.peerSetup.clientDataJsonBase64Url as Base64URLString,
|
||||||
|
this.publicKeyBytes as Uint8Array,
|
||||||
|
this.publicKeyJwk as JWK,
|
||||||
|
this.peerSetup.signature as Base64URLString,
|
||||||
|
);
|
||||||
|
console.log("decoded", decoded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user