From dc04c88c3302eb4b9a160bc0c052becba5611a5e Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Sun, 16 Jun 2024 19:42:55 -0600 Subject: [PATCH] modify test payload, add test to help with server verification --- src/libs/didPeer.ts | 60 ++++++++++++++++++++++-------- src/views/TestView.vue | 83 +++++++++++++++++++++++++----------------- 2 files changed, 93 insertions(+), 50 deletions(-) diff --git a/src/libs/didPeer.ts b/src/libs/didPeer.ts index a9ee6fc..3e259a6 100644 --- a/src/libs/didPeer.ts +++ b/src/libs/didPeer.ts @@ -23,7 +23,8 @@ import { import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers"; -const PEER_DID_PREFIX = "did:peer:0"; +const PEER_DID_PREFIX = "did:peer:"; +const PEER_DID_MULTIBASE_PREFIX = PEER_DID_PREFIX + "0"; export interface JWK { kty: string; crv: string; @@ -99,11 +100,11 @@ export function createPeerDid(publicKeyBytes: Uint8Array) { "base58btc", "p256-pub", ); - return PEER_DID_PREFIX + methodSpecificId; + return PEER_DID_MULTIBASE_PREFIX + methodSpecificId; } function peerDidToPublicKeyBytes(did: string) { - return multibaseToBytes(did.substring(PEER_DID_PREFIX.length)); + return multibaseToBytes(did.substring(PEER_DID_MULTIBASE_PREFIX.length)); } export class PeerSetup { @@ -112,10 +113,19 @@ export class PeerSetup { public clientDataJsonBase64Url?: Base64URLString; public signature?: Base64URLString; - public async createJwtSimplewebauthn(fullPayload: object, credIdHex: string) { + public async createJwtSimplewebauthn( + issuerDid: string, + payload: object, + credIdHex: string, + ) { const credentialId = arrayBufferToBase64URLString( Buffer.from(credIdHex, "hex").buffer, ); + const fullPayload = { + ...payload, + iat: Math.floor(Date.now() / 1000), + iss: issuerDid, + }; this.challenge = new Uint8Array(Buffer.from(JSON.stringify(fullPayload))); // const payloadHash: Uint8Array = sha256(this.challenge); const options: PublicKeyCredentialRequestOptionsJSON = @@ -147,18 +157,33 @@ export class PeerSetup { .replace(/=+$/, ""); const dataInJwt = { - AuthenticationData: authenticatorDataBase64Url, - ClientDataJSON: this.clientDataJsonBase64Url, + AuthenticationDataB64URL: authenticatorDataBase64Url, + ClientDataJSONB64URL: this.clientDataJsonBase64Url, + iat: Math.floor(Date.now() / 1000), + iss: issuerDid, }; const dataInJwtString = JSON.stringify(dataInJwt); - const payloadBase64 = Buffer.from(dataInJwtString).toString("base64"); + const payloadBase64 = Buffer.from(dataInJwtString) + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/, ""); const signature = clientAuth.response.signature; return headerBase64 + "." + payloadBase64 + "." + signature; } - public async createJwtNavigator(fullPayload: object, credIdHex: string) { + public async createJwtNavigator( + issuerDid: string, + payload: object, + credIdHex: string, + ) { + const fullPayload = { + ...payload, + iat: Math.floor(Date.now() / 1000), + iss: issuerDid, + }; const dataToSignString = JSON.stringify(fullPayload); const dataToSignBuffer = Buffer.from(dataToSignString); const credentialId = Buffer.from(credIdHex, "hex"); @@ -200,8 +225,10 @@ export class PeerSetup { .replace(/=+$/, ""); const dataInJwt = { - AuthenticationData: authenticatorDataBase64Url, - ClientDataJSON: this.clientDataJsonBase64Url, + AuthenticationDataB64URL: authenticatorDataBase64Url, + ClientDataJSONB64URL: this.clientDataJsonBase64Url, + iat: Math.floor(Date.now() / 1000), + iss: issuerDid, }; const dataInJwtString = JSON.stringify(dataInJwt); const payloadBase64 = Buffer.from(dataInJwtString) @@ -284,7 +311,7 @@ export class PeerSetup { // import { p256 } from "@noble/curves/p256"; export async function verifyJwtP256( credIdHex: string, - did: string, + issuerDid: string, authenticatorData: ArrayBuffer, challenge: Uint8Array, clientDataJsonBase64Url: Base64URLString, @@ -294,7 +321,7 @@ export async function verifyJwtP256( const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64"); const sigBuffer = Buffer.from(signature, "base64"); const finalSigBuffer = unwrapEC2Signature(sigBuffer); - const publicKeyBytes = peerDidToPublicKeyBytes(did); + const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid); // Hash the client data const hash = sha256(clientDataFromBase); @@ -312,14 +339,14 @@ export async function verifyJwtP256( export async function verifyJwtSimplewebauthn( credIdHex: string, - did: string, + issuerDid: string, authenticatorData: ArrayBuffer, challenge: Uint8Array, clientDataJsonBase64Url: Base64URLString, signature: Base64URLString, ) { const authData = arrayToBase64Url(Buffer.from(authenticatorData)); - const publicKeyBytes = peerDidToPublicKeyBytes(did); + const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid); const credId = arrayBufferToBase64URLString( Buffer.from(credIdHex, "hex").buffer, ); @@ -351,7 +378,7 @@ export async function verifyJwtSimplewebauthn( export async function verifyJwtWebCrypto( credId: Base64URLString, - did: string, + issuerDid: string, authenticatorData: ArrayBuffer, challenge: Uint8Array, clientDataJsonBase64Url: Base64URLString, @@ -368,12 +395,13 @@ export async function verifyJwtWebCrypto( // Construct the preimage const preimage = Buffer.concat([authDataFromBase, hash]); + const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid); + const WebCrypto = await getWebCrypto(); const verifyAlgorithm = { name: "ECDSA", hash: { name: "SHA-256" }, }; - const publicKeyBytes = peerDidToPublicKeyBytes(did); const publicKeyJwk = cborToKeys(publicKeyBytes).publicKeyJwk; const keyAlgorithm = { name: "ECDSA", diff --git a/src/views/TestView.vue b/src/views/TestView.vue index 3ced12e..dc6a40c 100644 --- a/src/views/TestView.vue +++ b/src/views/TestView.vue @@ -221,6 +221,12 @@ p256 - broken + @@ -241,11 +247,20 @@ import { verifyJwtSimplewebauthn, verifyJwtWebCrypto, } from "@/libs/didPeer"; -import { JWTPayload } from "did-jwt"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; const inputFileNameRef = ref(); +const TEST_PAYLOAD = { + vc: { + credentialSubject: { + "@context": "https://schema.org", + "@type": "GiveAction", + description: "pizza", + }, + }, +}; + @Component({ components: { QuickNav } }) export default class Help extends Vue { // for file import @@ -265,8 +280,10 @@ export default class Help extends Vue { this.userName = settings?.firstName as string; await accountsDB.open(); - const account: { identity?: string } | undefined = - await accountsDB.accounts.where("did").equals(this.activeDid).first(); + const account: { identity?: string } | undefined = await accountsDB.accounts + .where("did") + .equals(this.activeDid) + .first(); if (this.activeDid) { if (account) { this.credIdHex = account.passkeyCredIdHex as string; @@ -318,50 +335,24 @@ export default class Help extends Vue { did: this.activeDid, passkeyCredIdHex: this.credIdHex, publicKeyHex: Buffer.from(publicKeyBytes).toString("hex"), - }); - - // await db.settings.update(MASTER_SETTINGS_KEY, { - // activeDid: this.did, - // }); + });`` } public async createJwtSimplewebauthn() { - const payload = { - "@context": "https://schema.org", - type: "GiveAction", - description: "pizza", - }; - // from createJWT in did-jwt/src/JWT.ts - const timestamps: Partial = { - iat: Math.floor(Date.now() / 1000), - }; - const fullPayload = { ...timestamps, ...payload, iss: this.activeDid }; - this.peerSetup = new PeerSetup(); this.jwt = await this.peerSetup.createJwtSimplewebauthn( - fullPayload, + this.activeDid as string, + TEST_PAYLOAD, this.credIdHex as string, ); console.log("simple jwt4url", this.jwt); } public async createJwtNavigator() { - console.log("generated peer did", this.activeDid); - - const payload = { - "@context": "https://schema.org", - type: "GiveAction", - description: "pizza", - }; - // from createJWT in did-jwt/src/JWT.ts - const timestamps: Partial = { - iat: Math.floor(Date.now() / 1000), - }; - const fullPayload = { ...timestamps, ...payload, iss: this.activeDid }; - this.peerSetup = new PeerSetup(); this.jwt = await this.peerSetup.createJwtNavigator( - fullPayload, + this.activeDid as string, + TEST_PAYLOAD, this.credIdHex as string, ); console.log("lower jwt4url", this.jwt); @@ -402,5 +393,29 @@ export default class Help extends Vue { ); console.log("decoded", decoded); } + public async verifyMyJwt() { + const jwt = + "eyJ0eXAiOiJKV0FOVCIsImFsZyI6IkVTMjU2In0.eyJBdXRoZW50aWNhdGlvbkRhdGFCNjRVUkwiOiJTWllONVlnT2pHaDBOQmNQWkhaZ1c0X2tycm1paGpMSG1Wenp1b01kbDJNRkFBQUFBQSIsIkNsaWVudERhdGFKU09OQjY0VVJMIjoiZXlKMGVYQmxJam9pZDJWaVlYVjBhRzR1WjJWMElpd2lZMmhoYkd4bGJtZGxJam9pWlhsS01sbDVTVFpsZVVwcVkyMVdhMXBYTlRCaFYwWnpWVE5XYVdGdFZtcGtRMGsyWlhsS1FWa3lPWFZrUjFZMFpFTkpOa2x0YURCa1NFSjZUMms0ZG1NeVRtOWFWekZvVEcwNWVWcDVTWE5KYTBJd1pWaENiRWxxYjJsU01td3lXbFZHYW1SSGJIWmlhVWx6U1cxU2JHTXlUbmxoV0VJd1lWYzVkVWxxYjJsalIydzJaVzFGYVdaWU1ITkpiV3hvWkVOSk5rMVVZM2hQUkZVMFRtcHJOVTFEZDJsaFdFNTZTV3B2YVZwSGJHdFBia0pzV2xoSk5rMUljRXhVVlZweFpHeFdibGRZU2s1TlYyaFpaREJTYW1GV2JFbGhWVVUxVkZob1dXUkZjRkZYUnpWVFZFVndNbU5YT1U1VWEwWk1ZakJTVFZkRWJIZFRNREZZVkVkSmVsWnJVbnBhTTFab1RWaEJlV1ZzWTNobFJtaFRZekp3WVZVeFVrOWpNbG95VkZjMVQyVlZNVlJPTWxKRFRrZHpNMVJyUm05U2JtUk5UVE5DV1ZGdVNrTlhSMlExVjFWdk5XTnRhMmxtVVNJc0ltOXlhV2RwYmlJNkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk9EQTRNQ0lzSW1OeWIzTnpUM0pwWjJsdUlqcG1ZV3h6WlgwIiwiaWF0IjoxNzE4NTg2OTkyLCJpc3MiOiJkaWQ6cGVlcjowektNRmp2VWdZck0xaFh3RGNpWUhpQTlNeFh0SlBYblJMSnZxb01OQUtvRExYOXBLTVdMYjNWRHNndWExcDJ6VzF4WFJzalpTVE5zZnZNbk55TVM3ZEI0azdOQWhGd0wzcFhCckJYZ3lZSjlyaSJ9.MEUCIQDJyCTbMPIFnuBoW3FYnlgtDEIHZ2OrkCEvqVnHU7kJDQIgVxjBjfW1TwQfcSOYwK8Z7AdCWGJlyxtLEsrnPif7caE"; + const pieces = jwt.split("."); + console.log("pieces", typeof pieces[1], pieces); + const payload = JSON.parse(Buffer.from(pieces[1], "base64").toString()); + const authData = Buffer.from(payload["AuthenticationDataB64URL"], "base64"); + const clientJSON = Buffer.from( + payload["ClientDataJSONB64URL"], + "base64", + ).toString(); + const clientData = JSON.parse(clientJSON); + const challenge = clientData.challenge; + const signatureB64URL = pieces[2]; + const decoded = await verifyJwtWebCrypto( + this.credIdHex as Base64URLString, + this.activeDid as string, + authData, + challenge, + payload["ClientDataJSONB64URL"], + signatureB64URL, + ); + console.log("decoded", decoded); + } }