|
@ -7,7 +7,7 @@ import { sha256 } from "ethereum-cryptography/sha256.js"; |
|
|
import { bytesToMultibase } from "@veramo/utils"; |
|
|
import { bytesToMultibase } from "@veramo/utils"; |
|
|
import { |
|
|
import { |
|
|
startAuthentication, |
|
|
startAuthentication, |
|
|
startRegistration, |
|
|
startRegistration, WebAuthnAbortService, |
|
|
} from "@simplewebauthn/browser"; |
|
|
} from "@simplewebauthn/browser"; |
|
|
import { |
|
|
import { |
|
|
generateAuthenticationOptions, |
|
|
generateAuthenticationOptions, |
|
@ -207,55 +207,33 @@ export class PeerSetup { |
|
|
public signature?: Base64URLString; |
|
|
public signature?: Base64URLString; |
|
|
public publicKeyJwk?: JWK; |
|
|
public publicKeyJwk?: JWK; |
|
|
|
|
|
|
|
|
public async createJwt( |
|
|
public async createJwt(fullPayload: object, credentialId: string) { |
|
|
payload: object, |
|
|
|
|
|
issuerDid: string, |
|
|
|
|
|
credentialId: string, |
|
|
|
|
|
) { |
|
|
|
|
|
const header: JWTPayload = { typ: "JWT", alg: "ES256" }; |
|
|
const header: JWTPayload = { typ: "JWT", alg: "ES256" }; |
|
|
|
|
|
|
|
|
// from createJWT in did-jwt/src/JWT.ts
|
|
|
this.challenge = new Uint8Array(Buffer.from(JSON.stringify(fullPayload))); |
|
|
const timestamps: Partial<JWTPayload> = { |
|
|
// const payloadHash: Uint8Array = sha256(this.challenge);
|
|
|
iat: Math.floor(Date.now() / 1000), |
|
|
|
|
|
exp: undefined, |
|
|
|
|
|
}; |
|
|
|
|
|
const fullPayload = { ...timestamps, ...payload, iss: issuerDid }; |
|
|
|
|
|
|
|
|
|
|
|
const payloadHash: Uint8Array = sha256( |
|
|
|
|
|
Buffer.from(JSON.stringify(fullPayload)), |
|
|
|
|
|
); |
|
|
|
|
|
const options: PublicKeyCredentialRequestOptionsJSON = |
|
|
const options: PublicKeyCredentialRequestOptionsJSON = |
|
|
await generateAuthenticationOptions({ |
|
|
await generateAuthenticationOptions({ |
|
|
challenge: payloadHash, |
|
|
challenge: this.challenge, |
|
|
rpID: window.location.hostname, |
|
|
rpID: window.location.hostname, |
|
|
// Require users to use a previously-registered authenticator
|
|
|
|
|
|
// allowCredentials: userPasskeys.map(passkey => ({
|
|
|
|
|
|
// id: passkey.id,
|
|
|
|
|
|
// transports: passkey.transports,
|
|
|
|
|
|
// })),
|
|
|
|
|
|
}); |
|
|
}); |
|
|
console.log("custom authentication options", options); |
|
|
// console.log("simple authentication options", options);
|
|
|
|
|
|
|
|
|
const clientAuth = await startAuthentication(options); |
|
|
const clientAuth = await startAuthentication(options); |
|
|
console.log("custom clientAuth", clientAuth); |
|
|
// console.log("simple credential get", clientAuth);
|
|
|
|
|
|
|
|
|
this.authenticatorDataBase64Url = clientAuth.response.authenticatorData; |
|
|
this.authenticatorDataBase64Url = clientAuth.response.authenticatorData; |
|
|
this.authenticatorData = Buffer.from( |
|
|
this.authenticatorData = Buffer.from( |
|
|
clientAuth.response.authenticatorData, |
|
|
clientAuth.response.authenticatorData, |
|
|
"base64", |
|
|
"base64", |
|
|
).buffer; |
|
|
).buffer; |
|
|
this.challenge = payloadHash; |
|
|
|
|
|
this.clientDataJsonBase64Url = clientAuth.response.clientDataJSON; |
|
|
this.clientDataJsonBase64Url = clientAuth.response.clientDataJSON; |
|
|
this.clientDataJsonDecoded = JSON.parse( |
|
|
this.clientDataJsonDecoded = JSON.parse( |
|
|
Buffer.from(clientAuth.response.clientDataJSON, "base64").toString( |
|
|
Buffer.from(clientAuth.response.clientDataJSON, "base64").toString( |
|
|
"utf-8", |
|
|
"utf-8", |
|
|
), |
|
|
), |
|
|
); |
|
|
); |
|
|
console.log("authenticatorData for signing", this.authenticatorData); |
|
|
//console.log("simple authenticatorData for signing", this.authenticatorData);
|
|
|
console.log( |
|
|
|
|
|
"clientDataJSON for signing", |
|
|
|
|
|
Buffer.from(clientAuth.response.clientDataJSON, "base64"), |
|
|
|
|
|
); |
|
|
|
|
|
this.signature = clientAuth.response.signature; |
|
|
this.signature = clientAuth.response.signature; |
|
|
|
|
|
|
|
|
const headerBase64 = Buffer.from(JSON.stringify(header)).toString("base64"); |
|
|
const headerBase64 = Buffer.from(JSON.stringify(header)).toString("base64"); |
|
@ -265,25 +243,90 @@ export class PeerSetup { |
|
|
return headerBase64 + "." + payloadBase64 + "." + signature; |
|
|
return headerBase64 + "." + payloadBase64 + "." + signature; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public async createJwt2( |
|
|
public async createJwt2(fullPayload: object, credentialId: string) { |
|
|
payload: object, |
|
|
const header: JWTPayload = { typ: "JWT", alg: "ES256" }; |
|
|
issuerDid: string, |
|
|
const headerBase64 = Buffer.from(JSON.stringify(header)).toString("base64"); |
|
|
credentialId: string, |
|
|
|
|
|
) { |
|
|
const dataToSignString = JSON.stringify(fullPayload); |
|
|
const signer = await this.webAuthnES256KSigner(credentialId); |
|
|
const dataToSignBuffer = Buffer.from(dataToSignString); |
|
|
|
|
|
|
|
|
// from createJWT in did-jwt/src/JWT.ts
|
|
|
//console.log("lower credentialId", credentialId);
|
|
|
const header: JWTPayload = { typ: "JWT", alg: "ES256K" }; |
|
|
this.challenge = new Uint8Array(dataToSignBuffer); |
|
|
const timestamps: Partial<JWTPayload> = { |
|
|
const options = { |
|
|
iat: Math.floor(Date.now() / 1000), |
|
|
publicKey: { |
|
|
exp: undefined, |
|
|
challenge: this.challenge.buffer, |
|
|
|
|
|
rpID: window.location.hostname, |
|
|
|
|
|
//allowCredentials: [{ id: credentialId, type: "public-key" }],
|
|
|
|
|
|
userVerification: "preferred", |
|
|
|
|
|
//extensions: fullPayload,
|
|
|
|
|
|
}, |
|
|
}; |
|
|
}; |
|
|
const fullPayload = { ...timestamps, ...payload, iss: issuerDid }; |
|
|
|
|
|
|
|
|
|
|
|
const jwt = createJWS(fullPayload, signer, header); |
|
|
// 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(/=+$/, ""); |
|
|
|
|
|
|
|
|
|
|
|
const dataInJwt = { |
|
|
|
|
|
AuthenticationData: this.authenticatorDataBase64Url, |
|
|
|
|
|
ClientDataJSON: this.clientDataJsonBase64Url, |
|
|
|
|
|
}; |
|
|
|
|
|
const dataInJwtString = JSON.stringify(dataInJwt); |
|
|
|
|
|
const payloadBase64 = Buffer.from(dataInJwtString).toString("base64"); |
|
|
|
|
|
const jwt = headerBase64 + "." + payloadBase64 + "." + this.signature; |
|
|
return jwt; |
|
|
return jwt; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Attempted with JWS, but it will not match because it signs different content (header + payload)
|
|
|
|
|
|
//const signer = await this.webAuthnES256KSigner(credentialId);
|
|
|
|
|
|
//const jwt = createJWS(fullPayload, signer, { typ: "JWT", alg: "ES256" });
|
|
|
async webAuthnES256KSigner(credentialID: string) { |
|
|
async webAuthnES256KSigner(credentialID: string) { |
|
|
return async (data: string | Uint8Array) => { |
|
|
return async (data: string | Uint8Array) => { |
|
|
const signature = await this.generateWebAuthnSignature( |
|
|
const signature = await this.generateWebAuthnSignature( |
|
@ -293,22 +336,23 @@ export class PeerSetup { |
|
|
|
|
|
|
|
|
// This converts from the browser ArrayBuffer to a Node.js Buffer, which is a requirement for the asn1 library.
|
|
|
// This converts from the browser ArrayBuffer to a Node.js Buffer, which is a requirement for the asn1 library.
|
|
|
const signatureBuffer = Buffer.from(signature); |
|
|
const signatureBuffer = Buffer.from(signature); |
|
|
console.log("signature inside signer", signature); |
|
|
console.log("lower signature inside signer", signature); |
|
|
console.log("buffer signature inside signer", signatureBuffer); |
|
|
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
|
|
|
// Decode the DER-encoded signature to extract R and S values
|
|
|
const reader = new asn1.BerReader(signatureBuffer); |
|
|
const reader = new asn1.BerReader(signatureBuffer); |
|
|
console.log("after reader"); |
|
|
console.log("lower after reader"); |
|
|
reader.readSequence(); |
|
|
reader.readSequence(); |
|
|
console.log("after read sequence"); |
|
|
console.log("lower after read sequence"); |
|
|
const r = reader.readString(asn1.Ber.Integer, true); |
|
|
const r = reader.readString(asn1.Ber.Integer, true); |
|
|
console.log("after r"); |
|
|
console.log("lower after r"); |
|
|
const s = reader.readString(asn1.Ber.Integer, true); |
|
|
const s = reader.readString(asn1.Ber.Integer, true); |
|
|
console.log("after r & s"); |
|
|
console.log("lower after r & s"); |
|
|
|
|
|
|
|
|
// Ensure R and S are 32 bytes each
|
|
|
// Ensure R and S are 32 bytes each
|
|
|
const rBuffer = Buffer.from(r); |
|
|
const rBuffer = Buffer.from(r); |
|
|
const sBuffer = Buffer.from(s); |
|
|
const sBuffer = Buffer.from(s); |
|
|
console.log("after rBuffer & sBuffer", rBuffer, sBuffer); |
|
|
console.log("lower after rBuffer & sBuffer", rBuffer, sBuffer); |
|
|
const rWithoutPrefix = rBuffer.length > 32 ? rBuffer.slice(1) : rBuffer; |
|
|
const rWithoutPrefix = rBuffer.length > 32 ? rBuffer.slice(1) : rBuffer; |
|
|
const sWithoutPrefix = sBuffer.length > 32 ? sBuffer.slice(1) : sBuffer; |
|
|
const sWithoutPrefix = sBuffer.length > 32 ? sBuffer.slice(1) : sBuffer; |
|
|
const rPadded = |
|
|
const rPadded = |
|
@ -323,18 +367,18 @@ export class PeerSetup { |
|
|
// Concatenate R and S to form the 64-byte array (ECDSA signature format expected by JWT)
|
|
|
// Concatenate R and S to form the 64-byte array (ECDSA signature format expected by JWT)
|
|
|
const combinedSignature = Buffer.concat([rPadded, sPadded]); |
|
|
const combinedSignature = Buffer.concat([rPadded, sPadded]); |
|
|
console.log( |
|
|
console.log( |
|
|
"combinedSignature", |
|
|
"lower combinedSignature", |
|
|
combinedSignature.length, |
|
|
combinedSignature.length, |
|
|
combinedSignature, |
|
|
combinedSignature, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
const combSig64 = combinedSignature.toString("base64"); |
|
|
const combSig64 = combinedSignature.toString("base64"); |
|
|
console.log("combSig64", combSig64); |
|
|
console.log("lower combSig64", combSig64); |
|
|
const combSig64Url = combSig64 |
|
|
const combSig64Url = combSig64 |
|
|
.replace(/\+/g, "-") |
|
|
.replace(/\+/g, "-") |
|
|
.replace(/\//g, "_") |
|
|
.replace(/\//g, "_") |
|
|
.replace(/=+$/, ""); |
|
|
.replace(/=+$/, ""); |
|
|
console.log("combSig64Url", combSig64Url); |
|
|
console.log("lower combSig64Url", combSig64Url); |
|
|
return combSig64Url; |
|
|
return combSig64Url; |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
@ -343,30 +387,50 @@ export class PeerSetup { |
|
|
dataToSign: string | Uint8Array, // from Signer interface
|
|
|
dataToSign: string | Uint8Array, // from Signer interface
|
|
|
credentialId: string, |
|
|
credentialId: string, |
|
|
) { |
|
|
) { |
|
|
if (dataToSign instanceof Uint8Array) { |
|
|
if (!(dataToSign instanceof Uint8Array)) { |
|
|
dataToSign = new TextDecoder("utf-8").decode(dataToSign as Uint8Array); |
|
|
console.log("lower dataToSign & typeof ", typeof dataToSign, dataToSign); |
|
|
|
|
|
dataToSign = new Uint8Array(base64URLStringToBuffer(dataToSign)); |
|
|
} |
|
|
} |
|
|
|
|
|
console.log("lower credentialId", credentialId); |
|
|
console.log("credentialId", credentialId); |
|
|
this.challenge = dataToSign; |
|
|
const options = { |
|
|
const options = { |
|
|
challenge: new TextEncoder().encode(dataToSign).buffer, |
|
|
publicKey: { |
|
|
|
|
|
challenge: this.challenge.buffer, |
|
|
|
|
|
rpID: window.location.hostname, |
|
|
//allowCredentials: [{ id: credentialId, type: "public-key" }],
|
|
|
//allowCredentials: [{ id: credentialId, type: "public-key" }],
|
|
|
userVerification: "preferred", |
|
|
userVerification: "preferred", |
|
|
|
|
|
//extensions: fullPayload,
|
|
|
|
|
|
}, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
const assertion = await navigator.credentials.get({ publicKey: options }); |
|
|
// console.log("lower authentication options", options);
|
|
|
console.log("assertion", assertion); |
|
|
const assertion = await navigator.credentials.get(options); |
|
|
|
|
|
// console.log("lower credential get", assertion);
|
|
|
|
|
|
|
|
|
const authenticatorAssertionResponse = assertion?.response; |
|
|
const authenticatorAssertionResponse = assertion?.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( |
|
|
this.clientDataJsonDecoded = JSON.parse( |
|
|
new TextDecoder("utf-8").decode( |
|
|
new TextDecoder("utf-8").decode( |
|
|
authenticatorAssertionResponse.clientDataJSON, |
|
|
authenticatorAssertionResponse.clientDataJSON, |
|
|
), |
|
|
), |
|
|
); |
|
|
); |
|
|
console.log("clientDataJSON decoded", this.clientDataJsonDecoded); |
|
|
// console.log("lower clientDataJSON decoded", this.clientDataJsonDecoded);
|
|
|
this.authenticatorData = authenticatorAssertionResponse.authenticatorData; |
|
|
|
|
|
console.log("authenticator data", this.authenticatorData); |
|
|
this.signature = Buffer.from( |
|
|
return authenticatorAssertionResponse.signature; |
|
|
authenticatorAssertionResponse.signature, |
|
|
|
|
|
).toString("base64"); |
|
|
|
|
|
|
|
|
|
|
|
return this.signature; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -383,109 +447,34 @@ export async function verifyJwt( |
|
|
publicKeyJwk: JWK, |
|
|
publicKeyJwk: JWK, |
|
|
signature: Base64URLString, |
|
|
signature: Base64URLString, |
|
|
) { |
|
|
) { |
|
|
// Here's a combined auth & verify process, based on some of the inputs.
|
|
|
// const authData = arrayToBase64Url(Buffer.from(authenticatorData));
|
|
|
//
|
|
|
// const authOpts: VerifyAuthenticationResponseOpts = {
|
|
|
// const options: PublicKeyCredentialRequestOptionsJSON =
|
|
|
|
|
|
// await generateAuthenticationOptions({
|
|
|
|
|
|
// rpID: window.location.hostname,
|
|
|
|
|
|
// // Require users to use a previously-registered authenticator
|
|
|
|
|
|
// // allowCredentials: userPasskeys.map(passkey => ({
|
|
|
|
|
|
// // id: passkey.id,
|
|
|
|
|
|
// // transports: passkey.transports,
|
|
|
|
|
|
// // })),
|
|
|
|
|
|
// });
|
|
|
|
|
|
// console.log("authentication options", options);
|
|
|
|
|
|
//
|
|
|
|
|
|
// const clientAuth = await startAuthentication(options);
|
|
|
|
|
|
// console.log("clientAuth", clientAuth);
|
|
|
|
|
|
//
|
|
|
|
|
|
// const verfOpts: VerifyAuthenticationResponseOpts = {
|
|
|
|
|
|
// authenticator: {
|
|
|
// authenticator: {
|
|
|
// credentialID: credId,
|
|
|
// credentialID: credId,
|
|
|
// credentialPublicKey: publicKeyBytes,
|
|
|
// credentialPublicKey: publicKeyBytes,
|
|
|
// counter: 0,
|
|
|
// counter: 0,
|
|
|
// },
|
|
|
// },
|
|
|
// expectedChallenge: options.challenge,
|
|
|
// expectedChallenge: arrayToBase64Url(challenge),
|
|
|
// expectedOrigin: window.location.origin,
|
|
|
// expectedOrigin: window.location.origin,
|
|
|
// expectedRPID: window.location.hostname,
|
|
|
// expectedRPID: window.location.hostname,
|
|
|
// response: clientAuth,
|
|
|
// response: {
|
|
|
|
|
|
// authenticatorAttachment: "platform",
|
|
|
|
|
|
// clientExtensionResults: {},
|
|
|
|
|
|
// id: credId,
|
|
|
|
|
|
// rawId: arrayToBase64Url(rawId),
|
|
|
|
|
|
// response: {
|
|
|
|
|
|
// authenticatorData: authData,
|
|
|
|
|
|
// clientDataJSON: arrayToBase64Url(
|
|
|
|
|
|
// Buffer.from(JSON.stringify(clientDataJSON)),
|
|
|
|
|
|
// ),
|
|
|
|
|
|
// signature: signature,
|
|
|
|
|
|
// },
|
|
|
|
|
|
// type: "public-key",
|
|
|
|
|
|
// },
|
|
|
// };
|
|
|
// };
|
|
|
// console.log("verfOpts", verfOpts);
|
|
|
// console.log("auth opts", authOpts);
|
|
|
// const verificationFromClient = await verifyAuthenticationResponse(verfOpts);
|
|
|
// const verification = await verifyAuthenticationResponse(authOpts);
|
|
|
// console.log("client auth verification", verificationFromClient);
|
|
|
// console.log("auth verification", verification);
|
|
|
|
|
|
|
|
|
const authData = arrayToBase64Url(Buffer.from(authenticatorData)); |
|
|
|
|
|
const authOpts: VerifyAuthenticationResponseOpts = { |
|
|
|
|
|
authenticator: { |
|
|
|
|
|
credentialID: credId, |
|
|
|
|
|
credentialPublicKey: publicKeyBytes, |
|
|
|
|
|
counter: 0, |
|
|
|
|
|
}, |
|
|
|
|
|
expectedChallenge: arrayToBase64Url(challenge), |
|
|
|
|
|
expectedOrigin: window.location.origin, |
|
|
|
|
|
expectedRPID: window.location.hostname, |
|
|
|
|
|
response: { |
|
|
|
|
|
authenticatorAttachment: "platform", |
|
|
|
|
|
clientExtensionResults: {}, |
|
|
|
|
|
id: credId, |
|
|
|
|
|
rawId: arrayToBase64Url(rawId), |
|
|
|
|
|
response: { |
|
|
|
|
|
authenticatorData: authData, |
|
|
|
|
|
clientDataJSON: arrayToBase64Url( |
|
|
|
|
|
Buffer.from(JSON.stringify(clientDataJSON)), |
|
|
|
|
|
), |
|
|
|
|
|
signature: signature, |
|
|
|
|
|
}, |
|
|
|
|
|
type: "public-key", |
|
|
|
|
|
}, |
|
|
|
|
|
}; |
|
|
|
|
|
console.log("auth opts", authOpts); |
|
|
|
|
|
const verification = await verifyAuthenticationResponse(authOpts); |
|
|
|
|
|
console.log("auth verification", verification); |
|
|
|
|
|
|
|
|
|
|
|
// It doesn't work to use the did-jwt verifyJWT with did-resolver Resolver
|
|
|
|
|
|
// const decoded = verifyJWT(jwt, {
|
|
|
|
|
|
// resolver: new Resolver({ peer: peerDidToDidDocument }),
|
|
|
|
|
|
// });
|
|
|
|
|
|
// return decoded;
|
|
|
|
|
|
|
|
|
|
|
|
// const [headerB64, concatenatedPayloadB64, signatureB64] = jwt.split(".");
|
|
|
|
|
|
//
|
|
|
|
|
|
// const header = JSON.parse(atob(headerB64));
|
|
|
|
|
|
// const jsonPayload = JSON.parse(atob(concatenatedPayloadB64));
|
|
|
|
|
|
// const [jsonPayloadB64, otherDataHashB64] = concatenatedPayloadB64.split(".");
|
|
|
|
|
|
//
|
|
|
|
|
|
// const otherDataHash = base64urlDecode(otherDataHashB64);
|
|
|
|
|
|
// const signature = base64urlDecode(signatureB64);
|
|
|
|
|
|
//
|
|
|
|
|
|
// const dataToVerify = new TextEncoder().encode(`${headerB64}.${concatenatedPayloadB64}`);
|
|
|
|
|
|
// const dataHash = await sha256(dataToVerify);
|
|
|
|
|
|
//
|
|
|
|
|
|
// const authenticatorData = base64urlDecode(jsonPayload.authenticatorData);
|
|
|
|
|
|
// const clientDataJSON = jsonPayload.clientDataJSON;
|
|
|
|
|
|
|
|
|
|
|
|
/////////
|
|
|
|
|
|
|
|
|
|
|
|
// const clientBuffer = clientDataJsonBase64Url
|
|
|
|
|
|
// .replace(/-/g, "+")
|
|
|
|
|
|
// .replace(/_/g, "/");
|
|
|
|
|
|
// const clientData = new Uint8Array(Buffer.from(clientBuffer, "base64"));
|
|
|
|
|
|
// const clientDataHash = sha256(clientData);
|
|
|
|
|
|
//
|
|
|
|
|
|
// const verifyData = new Uint8Array([
|
|
|
|
|
|
// ...new Uint8Array(authenticatorData),
|
|
|
|
|
|
// ...new Uint8Array(clientDataHash),
|
|
|
|
|
|
// ]);
|
|
|
|
|
|
// console.log("verifyData", verifyData);
|
|
|
|
|
|
// const sigBase64Raw = signature.replace(/-/g, "+").replace(/_/g, "/");
|
|
|
|
|
|
//
|
|
|
|
|
|
// const sigHex = new Uint8Array(Buffer.from(sigBase64Raw, "base64")); //Buffer.from(sigBase64Raw, "base64").toString("hex");
|
|
|
|
|
|
// const msgHex = verifyData; //new Buffer(verifyData).toString("hex");
|
|
|
|
|
|
// const pubHex = publicKeyBytes; //new Buffer(publicKeyBytes).toString("hex");
|
|
|
|
|
|
// console.log("sig msg pub", sigHex, msgHex, pubHex);
|
|
|
|
|
|
// const isValid = p256.verify(sigHex, msgHex, pubHex);
|
|
|
|
|
|
|
|
|
|
|
|
/////////
|
|
|
|
|
|
|
|
|
|
|
|
const authDataFromBase = Buffer.from(authenticatorDataBase64Url, "base64"); |
|
|
const authDataFromBase = Buffer.from(authenticatorDataBase64Url, "base64"); |
|
|
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64"); |
|
|
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64"); |
|
@ -498,9 +487,9 @@ 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); |
|
|
// console.log("finalSigBuffer", finalSigBuffer);
|
|
|
console.log("preimage", preimage); |
|
|
// console.log("preimage", preimage);
|
|
|
console.log("publicKeyBytes", publicKeyBytes); |
|
|
// console.log("publicKeyBytes", publicKeyBytes);
|
|
|
|
|
|
|
|
|
// This uses p256 from @noble/curves/p256, which I would prefer but it's returning false.
|
|
|
// This uses p256 from @noble/curves/p256, which I would prefer but it's returning false.
|
|
|
// const isValid = p256.verify(
|
|
|
// const isValid = p256.verify(
|
|
@ -510,8 +499,6 @@ export async function verifyJwt( |
|
|
// );
|
|
|
// );
|
|
|
// console.log("isValid", isValid);
|
|
|
// console.log("isValid", isValid);
|
|
|
|
|
|
|
|
|
/////////
|
|
|
|
|
|
|
|
|
|
|
|
const WebCrypto = await getWebCrypto(); |
|
|
const WebCrypto = await getWebCrypto(); |
|
|
const verifyAlgorithm = { |
|
|
const verifyAlgorithm = { |
|
|
name: "ECDSA", |
|
|
name: "ECDSA", |
|
@ -521,10 +508,6 @@ export async function verifyJwt( |
|
|
name: "ECDSA", |
|
|
name: "ECDSA", |
|
|
namedCurve: publicKeyJwk.crv, |
|
|
namedCurve: publicKeyJwk.crv, |
|
|
}; |
|
|
}; |
|
|
// const publicKeyCryptoKey = await importKey({
|
|
|
|
|
|
// publicKeyJwk,
|
|
|
|
|
|
// algorithm: keyAlgorithm,
|
|
|
|
|
|
// });
|
|
|
|
|
|
const publicKeyCryptoKey = await WebCrypto.subtle.importKey( |
|
|
const publicKeyCryptoKey = await WebCrypto.subtle.importKey( |
|
|
"jwk", |
|
|
"jwk", |
|
|
publicKeyJwk, |
|
|
publicKeyJwk, |
|
@ -532,17 +515,17 @@ export async function verifyJwt( |
|
|
false, |
|
|
false, |
|
|
["verify"], |
|
|
["verify"], |
|
|
); |
|
|
); |
|
|
console.log("verifyAlgorithm", verifyAlgorithm); |
|
|
// console.log("verifyAlgorithm", verifyAlgorithm);
|
|
|
console.log("publicKeyCryptoKey", publicKeyCryptoKey); |
|
|
// console.log("publicKeyCryptoKey", publicKeyCryptoKey);
|
|
|
console.log("finalSigBuffer", finalSigBuffer); |
|
|
// console.log("finalSigBuffer", finalSigBuffer);
|
|
|
console.log("preimage", preimage); |
|
|
// 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); |
|
|
// console.log("verified", verified);
|
|
|
return verified; |
|
|
return verified; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -552,7 +535,8 @@ async function peerDidToDidDocument(did: string): Promise<DIDResolutionResult> { |
|
|
"This only verifies a peer DID, method 0, encoded base58btc.", |
|
|
"This only verifies a peer DID, method 0, encoded base58btc.", |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
// this is basically hard-coded based on the results from the @aviarytech/did-peer resolver
|
|
|
// this is basically hard-coded from https://www.w3.org/TR/did-core/#example-various-verification-method-types
|
|
|
|
|
|
// (another reference is the @aviarytech/did-peer resolver)
|
|
|
const id = did.split(":")[2]; |
|
|
const id = did.split(":")[2]; |
|
|
const multibase = id.slice(1); |
|
|
const multibase = id.slice(1); |
|
|
const encnumbasis = multibase.slice(1); |
|
|
const encnumbasis = multibase.slice(1); |
|
@ -617,6 +601,31 @@ function base64urlEncode(buffer: ArrayBuffer) { |
|
|
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); |
|
|
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// from @simplewebauthn/browser
|
|
|
|
|
|
function bufferToBase64URLString(buffer) { |
|
|
|
|
|
const bytes = new Uint8Array(buffer); |
|
|
|
|
|
let str = ''; |
|
|
|
|
|
for (const charCode of bytes) { |
|
|
|
|
|
str += String.fromCharCode(charCode); |
|
|
|
|
|
} |
|
|
|
|
|
const base64String = btoa(str); |
|
|
|
|
|
return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// from @simplewebauthn/browser
|
|
|
|
|
|
function base64URLStringToBuffer(base64URLString) { |
|
|
|
|
|
const base64 = base64URLString.replace(/-/g, '+').replace(/_/g, '/'); |
|
|
|
|
|
const padLength = (4 - (base64.length % 4)) % 4; |
|
|
|
|
|
const padded = base64.padEnd(base64.length + padLength, '='); |
|
|
|
|
|
const binary = atob(padded); |
|
|
|
|
|
const buffer = new ArrayBuffer(binary.length); |
|
|
|
|
|
const bytes = new Uint8Array(buffer); |
|
|
|
|
|
for (let i = 0; i < binary.length; i++) { |
|
|
|
|
|
bytes[i] = binary.charCodeAt(i); |
|
|
|
|
|
} |
|
|
|
|
|
return buffer; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
function cborToKeys(publicKeyBytes: Uint8Array) { |
|
|
function cborToKeys(publicKeyBytes: Uint8Array) { |
|
|
const jwkObj = cborDecode(publicKeyBytes); |
|
|
const jwkObj = cborDecode(publicKeyBytes); |
|
|
console.log("jwkObj from verification", jwkObj); |
|
|
console.log("jwkObj from verification", jwkObj); |
|
|