Browse Source

refactor, and make separate testing buttons for different approaches

pull/116/head
Trent Larson 3 months ago
parent
commit
141a7fd563
  1. 496
      src/libs/didPeer.ts
  2. 149
      src/views/TestView.vue

496
src/libs/didPeer.ts

@ -1,20 +1,19 @@
import asn1 from "asn1-ber";
import { Buffer } from "buffer/";
import { decode as cborDecode } from "cbor-x";
import { createJWS, JWTPayload } from "did-jwt";
import { JWTPayload } from "did-jwt";
import { DIDResolutionResult } from "did-resolver";
import { sha256 } from "ethereum-cryptography/sha256.js";
import { bytesToMultibase } from "@veramo/utils";
import {
startAuthentication,
startRegistration, WebAuthnAbortService,
startRegistration,
} from "@simplewebauthn/browser";
import {
generateAuthenticationOptions,
generateRegistrationOptions,
verifyAuthenticationResponse,
verifyRegistrationResponse,
VerifyRegistrationResponseOpts,
} from "@simplewebauthn/server";
import { VerifyAuthenticationResponseOpts } from "@simplewebauthn/server/esm/authentication/verifyAuthenticationResponse";
import {
@ -23,7 +22,6 @@ import {
PublicKeyCredentialRequestOptionsJSON,
} from "@simplewebauthn/types";
import { generateRandomBytes } from "@/libs/crypto";
import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers";
export interface JWK {
@ -54,13 +52,6 @@ export async function registerCredential(userId: Uint8Array) {
// Don't prompt users for additional information about the authenticator
// (Recommended for smoother UX)
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: {
// Defaults
residentKey: "preferred",
@ -69,24 +60,21 @@ export async function registerCredential(userId: Uint8Array) {
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);
console.log("attResp", attResp);
const verification = await verifyRegistrationResponse({
response: attResp,
expectedChallenge: options.challenge,
expectedOrigin: window.location.origin,
expectedRPID: window.location.hostname,
});
console.log("verification", verification);
const jwkObj = cborDecode(
verification.registrationInfo?.credentialPublicKey as Uint8Array,
);
console.log("jwkObj from verification", jwkObj);
console.log(
"[1]==2 => kty EC",
"[3]==-7 => alg ES256",
"[-1]==1 => crv P-256",
);
// references for parsing auth data and getting the public key
// https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/helpers/parseAuthenticatorData.ts#L11
// https://chatgpt.com/share/78a5c91d-099d-46dc-aa6d-fc0c916509fa
// https://chatgpt.com/share/3c13f061-6031-45bc-a2d7-3347c1e7a2d7
const { publicKeyJwk } = cborToKeys(
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) {
// 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 methodSpecificId = bytesToMultibase(
publicKeyBytes,
"base58btc",
"secp256k1-pub",
"p256-pub",
);
return "did:peer:0" + methodSpecificId;
}
@ -205,11 +107,11 @@ export class PeerSetup {
public clientDataJsonDecoded?: object;
public clientDataJsonBase64Url?: 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)));
// const payloadHash: Uint8Array = sha256(this.challenge);
const options: PublicKeyCredentialRequestOptionsJSON =
@ -233,83 +135,65 @@ export class PeerSetup {
"utf-8",
),
);
//console.log("simple authenticatorData for signing", this.authenticatorData);
// console.log("simple authenticatorData for signing", this.authenticatorData);
this.signature = clientAuth.response.signature;
const headerBase64 = Buffer.from(JSON.stringify(header)).toString("base64");
const payloadBase64 = clientAuth.response.clientDataJSON;
// Our custom type of JWANT means the signature is based on a concatenation of the two Webauthn properties
const header: JWTPayload = { typ: "JWANT", alg: "ES256" };
const headerBase64 = Buffer.from(JSON.stringify(header))
.toString("base64")
.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 signature = clientAuth.response.signature;
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");
public async createJwtNavigator(fullPayload: object, credentialId: string) {
const dataToSignString = JSON.stringify(fullPayload);
const dataToSignBuffer = Buffer.from(dataToSignString);
//console.log("lower credentialId", credentialId);
// 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;
// console.log("nav credential get", credential);
this.authenticatorDataBase64Url = bufferToBase64URLString(
credential?.response.authenticatorData,
);
this.authenticatorData = Buffer.from(
this.authenticatorDataBase64Url as Base64URLString,
"base64",
).buffer;
// console.log("lower authenticator data", this.authenticatorData);
this.clientDataJsonBase64Url =
authenticatorAssertionResponse.clientDataJSON;
this.clientDataJsonBase64Url = bufferToBase64URLString(
credential?.response.clientDataJSON,
);
this.clientDataJsonDecoded = JSON.parse(
new TextDecoder("utf-8").decode(
authenticatorAssertionResponse.clientDataJSON,
),
new TextDecoder("utf-8").decode(credential?.response.clientDataJSON),
);
// console.log("lower clientDataJSON decoded", this.clientDataJsonDecoded);
const origSignature = Buffer.from(
authenticatorAssertionResponse.signature,
).toString("base64");
this.signature = origSignature
// Our custom type of JWANT means the signature is based on a concatenation of the two Webauthn properties
const header: JWTPayload = { typ: "JWANT", alg: "ES256" };
const headerBase64 = Buffer.from(JSON.stringify(header))
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
@ -319,122 +203,159 @@ export class PeerSetup {
ClientDataJSON: this.clientDataJsonBase64Url,
};
const dataInJwtString = JSON.stringify(dataInJwt);
const payloadBase64 = Buffer.from(dataInJwtString).toString("base64");
const jwt = headerBase64 + "." + payloadBase64 + "." + this.signature;
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) {
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.
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 payloadBase64 = Buffer.from(dataInJwtString)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
const combSig64 = combinedSignature.toString("base64");
console.log("lower combSig64", combSig64);
const combSig64Url = combSig64
const origSignature = Buffer.from(credential?.response.signature)
.toString("base64")
this.signature = origSignature
.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));
const jwt = headerBase64 + "." + payloadBase64 + "." + this.signature;
return jwt;
}
console.log("lower credentialId", credentialId);
this.challenge = dataToSign;
const options = {
publicKey: {
challenge: this.challenge.buffer,
rpID: window.location.hostname,
//allowCredentials: [{ id: credentialId, type: "public-key" }],
userVerification: "preferred",
//extensions: fullPayload,
// 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 verifyJwtSimplewebauthn(
jwt: string,
credId: Base64URLString,
rawId: Uint8Array,
authenticatorData: ArrayBuffer,
authenticatorDataBase64Url: Base64URLString,
challenge: Uint8Array,
clientDataJSON: object,
clientDataJsonBase64Url: Base64URLString,
publicKeyBytes: Uint8Array,
publicKeyJwk: JWK,
signature: Base64URLString,
) {
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",
},
};
const verification = await verifyAuthenticationResponse(authOpts);
return verification.verified;
}
// console.log("lower authentication options", options);
const assertion = await navigator.credentials.get(options);
// console.log("lower credential get", assertion);
// 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 clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
const sigBuffer = Buffer.from(signature, "base64");
const finalSigBuffer = unwrapEC2Signature(sigBuffer);
const authenticatorAssertionResponse = assertion?.response;
// Hash the client data
const hash = sha256(clientDataFromBase);
this.authenticatorDataBase64Url =
authenticatorAssertionResponse.authenticatorData;
this.authenticatorData = Buffer.from(
this.authenticatorDataBase64Url as Base64URLString,
"base64",
).buffer;
// console.log("lower authenticator data", this.authenticatorData);
// Construct the preimage
const preimage = Buffer.concat([authDataFromBase, hash]);
this.clientDataJsonBase64Url =
authenticatorAssertionResponse.clientDataJSON;
this.clientDataJsonDecoded = JSON.parse(
new TextDecoder("utf-8").decode(
authenticatorAssertionResponse.clientDataJSON,
),
const isValid = p256.verify(
finalSigBuffer,
new Uint8Array(preimage),
publicKeyBytes,
);
// console.log("lower clientDataJSON decoded", this.clientDataJsonDecoded);
this.signature = Buffer.from(
authenticatorAssertionResponse.signature,
).toString("base64");
return this.signature;
}
return isValid;
}
export async function verifyJwt(
export async function verifyJwtWebCrypto(
jwt: string,
credId: Base64URLString,
rawId: Uint8Array,
@ -447,35 +368,6 @@ export async function verifyJwt(
publicKeyJwk: JWK,
signature: Base64URLString,
) {
// 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);
const authDataFromBase = Buffer.from(authenticatorDataBase64Url, "base64");
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
const sigBuffer = Buffer.from(signature, "base64");
@ -487,18 +379,6 @@ export async function verifyJwt(
// Construct the preimage
const preimage = Buffer.concat([authDataFromBase, hash]);
// console.log("finalSigBuffer", finalSigBuffer);
// console.log("preimage", preimage);
// console.log("publicKeyBytes", publicKeyBytes);
// This uses p256 from @noble/curves/p256, which I would prefer but it's returning false.
// const isValid = p256.verify(
// finalSigBuffer,
// new Uint8Array(preimage),
// publicKeyBytes,
// );
// console.log("isValid", isValid);
const WebCrypto = await getWebCrypto();
const verifyAlgorithm = {
name: "ECDSA",
@ -515,17 +395,12 @@ export async function verifyJwt(
false,
["verify"],
);
// console.log("verifyAlgorithm", verifyAlgorithm);
// console.log("publicKeyCryptoKey", publicKeyCryptoKey);
// console.log("finalSigBuffer", finalSigBuffer);
// console.log("preimage", preimage);
const verified = await WebCrypto.subtle.verify(
verifyAlgorithm,
publicKeyCryptoKey,
finalSigBuffer,
preimage,
);
// console.log("verified", verified);
return verified;
}
@ -628,7 +503,6 @@ function base64URLStringToBuffer(base64URLString) {
function cborToKeys(publicKeyBytes: Uint8Array) {
const jwkObj = cborDecode(publicKeyBytes);
console.log("jwkObj from verification", jwkObj);
if (
jwkObj[1] != 2 || // kty "EC"
jwkObj[3] != -7 || // alg "ES256"
@ -663,7 +537,7 @@ async function pemToCryptoKey(pem: string) {
for (let i = 0; i < binaryDerString.length; i++) {
binaryDer[i] = binaryDerString.charCodeAt(i);
}
console.log("binaryDer", binaryDer.buffer);
// console.log("binaryDer", binaryDer.buffer);
return await window.crypto.subtle.importKey(
"spki",
binaryDer.buffer,

149
src/views/TestView.vue

@ -172,25 +172,52 @@
<div class="mt-8">
<h2 class="text-xl font-bold mb-4">Passkeys</h2>
<div>
Register
<button
@click="register()"
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
>
Register
Simplewebauthn
</button>
</div>
<div>
Create
<button
@click="create()"
@click="createJwtSimplewebauthn()"
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
>
Create
Simplewebauthn
</button>
<button
@click="verify()"
@click="createJwtNavigator()"
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
>
Navigator
</button>
</div>
<div>
Verify
<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>
</section>
</template>
@ -206,11 +233,11 @@ import {
createPeerDid,
JWK,
PeerSetup,
registerCredential,
verifyJwt,
registerCredential, verifyJwtP256,
verifyJwtSimplewebauthn,
verifyJwtWebCrypto,
} from "@/libs/didPeer";
import { Buffer } from "buffer";
import {JWTPayload} from "did-jwt";
import { JWTPayload } from "did-jwt";
const inputFileNameRef = ref<Blob>();
@ -220,21 +247,12 @@ export default class Help extends Vue {
fileName?: string;
// for passkeys
authenticatorData?: ArrayBuffer;
credId?: Base64URLString;
jwt?: string;
jwt2?: string;
payload = {
"@context": "https://schema.org",
type: "GiveAction",
description: "pizza",
};
peerSetup?: PeerSetup;
peerSetup2?: PeerSetup;
publicKeyJwk?: JWK;
publicKeyBytes?: Uint8Array;
rawId?: Uint8Array;
savedCredentialId = "";
userId?: ArrayBuffer;
async uploadFile(event: Event) {
@ -269,8 +287,6 @@ export default class Help extends Vue {
public async register() {
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);
console.log("public key", cred);
@ -278,55 +294,76 @@ export default class Help extends Vue {
this.publicKeyBytes = cred.publicKeyBytes;
this.credId = cred.credId as string;
this.rawId = cred.rawId as Uint8Array;
this.savedCredentialId = this.credId;
}
public async create() {
console.log("Starting a create");
public async createJwtSimplewebauthn() {
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
const timestamps: Partial<JWTPayload> = {
iat: Math.floor(Date.now() / 1000),
exp: undefined,
};
const fullPayload = { ...timestamps, ...this.payload, did };
const fullPayload = { ...timestamps, ...payload, did };
this.peerSetup = new PeerSetup();
const rawJwt = await this.peerSetup.createJwt(
this.jwt = await this.peerSetup.createJwtSimplewebauthn(
fullPayload,
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 peerSetup", this.peerSetup);
}
public async createJwtNavigator() {
const did = createPeerDid(this.publicKeyBytes as Uint8Array);
console.log("generated peer did", did);
this.peerSetup2 = new PeerSetup();
const rawJwt2 = await this.peerSetup2.createJwt2(
const payload = {
"@context": "https://schema.org",
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 raw jwt2", rawJwt2);
this.jwt2 = rawJwt2
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
console.log("lower jwt4url2", this.jwt2);
// console.log("lower peerSetup2", this.peerSetup2);
console.log("lower jwt4url", this.jwt);
}
public async verify() {
if (!this.jwt || !this.peerSetup) {
alert("Create a JWT first.");
return;
public async verifyP256() {
const decoded = await verifyJwtP256(
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);
}
const decoded = await verifyJwt(
this.jwt,
public async verifySimplewebauthn() {
const decoded = await verifyJwtSimplewebauthn(
this.jwt as string,
this.credId as Base64URLString,
this.rawId as Uint8Array,
this.peerSetup.authenticatorData as ArrayBuffer,
@ -339,21 +376,23 @@ export default class Help extends Vue {
this.peerSetup.signature as Base64URLString,
);
console.log("decoded", decoded);
}
const decoded2 = await verifyJwt(
this.jwt2 as string,
public async verifyWebCrypto() {
const decoded = await verifyJwtWebCrypto(
this.jwt as string,
this.credId as Base64URLString,
this.rawId as Uint8Array,
this.peerSetup2.authenticatorData as ArrayBuffer,
this.peerSetup2.authenticatorDataBase64Url as Base64URLString,
this.peerSetup2.challenge as Uint8Array,
this.peerSetup2.clientDataJsonDecoded,
this.peerSetup2.clientDataJsonBase64Url as Base64URLString,
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.peerSetup2.signature as Base64URLString,
this.peerSetup.signature as Base64URLString,
);
console.log("decoded2", decoded2);
console.log("decoded", decoded);
}
}
</script>

Loading…
Cancel
Save