Browse Source

gets past code errors and now gotta verify signature (note that had to add Buffer in asn1 lib)

pull/116/head
Trent Larson 4 months ago
parent
commit
3f002cb4f2
  1. 6
      package-lock.json
  2. 1
      package.json
  3. 128
      src/libs/didPeer.ts

6
package-lock.json

@ -28,6 +28,7 @@
"@veramo/key-manager": "^5.6.0",
"@vueuse/core": "^10.9.0",
"@zxing/text-encoding": "^0.9.0",
"asn1-ber": "^1.2.2",
"axios": "^1.6.8",
"cbor-x": "^1.5.9",
"class-transformer": "^0.5.1",
@ -10347,6 +10348,11 @@
"license": "MIT",
"optional": true
},
"node_modules/asn1-ber": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/asn1-ber/-/asn1-ber-1.2.2.tgz",
"integrity": "sha512-CbNem/7hxrjSiOAOOTX4iZxu+0m3jiLqlsERQwwPM1IDR/22M8IPpA1VVndCLw5KtjRYyRODbvAEIfuTogNDng=="
},
"node_modules/asn1.js": {
"version": "5.4.1",
"license": "MIT",

1
package.json

@ -30,6 +30,7 @@
"@veramo/key-manager": "^5.6.0",
"@vueuse/core": "^10.9.0",
"@zxing/text-encoding": "^0.9.0",
"asn1-ber": "^1.2.2",
"axios": "^1.6.8",
"cbor-x": "^1.5.9",
"class-transformer": "^0.5.1",

128
src/libs/didPeer.ts

@ -1,11 +1,10 @@
import asn1 from "asn1-ber";
import { Buffer } from "buffer/";
import { decode as cborDecode } from "cbor-x";
import { createJWS, JWTPayload, verifyJWT } from "did-jwt";
import { getResolver } from "@veramo/did-provider-peer";
import { DIDResolutionResult, Resolver } from "did-resolver";
import { bytesToMultibase } from "@veramo/utils";
import { generateRandomBytes } from "@/libs/crypto";
export interface JWK {
kty: string;
crv: string;
@ -102,7 +101,7 @@ export function createPeerDid(publicKeyCose: Uint8Array) {
const methodSpecificId = bytesToMultibase(
publicKeyCose,
"base58btc",
"ed25519-pub",
"secp256k1-pub",
);
return "did:peer:0" + methodSpecificId;
}
@ -128,18 +127,53 @@ export async function createJwt(
async function webAuthnES256KSigner(credentialID: ArrayBuffer) {
return async (data: string | Uint8Array) => {
// also has clientDataJSON
const { signature, authenticatorData } = await generateWebAuthnSignature(
data,
credentialID,
);
const { signature } = await 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("signature inside signer", signature);
console.log("buffer signature inside signer", signatureBuffer);
// Decode the DER-encoded signature to extract R and S values
const reader = new asn1.BerReader(signatureBuffer);
console.log("after reader");
reader.readSequence();
console.log("after read sequence");
const r = reader.readString(asn1.Ber.Integer, true);
console.log("after r");
const s = reader.readString(asn1.Ber.Integer, true);
console.log("after r & s");
// Combine the WebAuthn components into a single signature format as required by did-jwt
const combinedSignature = Buffer.concat([
Buffer.from(authenticatorData),
Buffer.from(signature),
]);
// Ensure R and S are 32 bytes each
const rBuffer = Buffer.from(r);
const sBuffer = Buffer.from(s);
console.log("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;
return combinedSignature.toString("base64");
// Concatenate R and S to form the 64-byte array (ECDSA signature format expected by JWT)
const combinedSignature = Buffer.concat([rPadded, sPadded]);
console.log(
"combinedSignature",
combinedSignature.length,
combinedSignature,
);
const combSig64 = combinedSignature.toString("base64");
console.log("combSig64", combSig64);
const combSig64Url = combSig64
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
console.log("combSig64Url", combSig64Url);
return combSig64Url;
};
}
@ -150,17 +184,25 @@ async function generateWebAuthnSignature(
if (!(dataToSign instanceof Uint8Array)) {
dataToSign = new TextEncoder().encode(dataToSign as string);
}
const challenge = generateRandomBytes(32);
const options = {
challenge: challenge,
challenge: dataToSign,
allowCredentials: [{ id: credentialId, type: "public-key" }],
userVerification: "preferred",
};
const assertion = await navigator.credentials.get({ publicKey: options });
console.log("assertion", assertion);
const authenticatorAssertionResponse = assertion?.response;
console.log(
"clientDataJSON decoded",
JSON.parse(
new TextDecoder("utf-8").decode(
authenticatorAssertionResponse.clientDataJSON,
),
),
);
return {
signature: authenticatorAssertionResponse.signature,
clientDataJSON: authenticatorAssertionResponse.clientDataJSON,
@ -170,15 +212,55 @@ async function generateWebAuthnSignature(
export async function verifyJwt(
jwt: string,
issuerDid: string,
publicKey: JWK,
issuerDid: string, // eslint-disable-line @typescript-eslint/no-unused-vars
publicKey: JWK, // eslint-disable-line @typescript-eslint/no-unused-vars
) {
const decoded = verifyJWT(jwt, {
didAuthenticator: {
authenticators: [{ publicKeyJwk: publicKey }],
issuer: issuerDid,
},
resolver: getResolver(),
// didAuthenticator: {
// authenticators: [{ publicKeyJwk: publicKey }],
// issuer: issuerDid,
// },
//resolver: new Resolver({ ...getResolver() }),
resolver: new Resolver({ peer: peerDidToDidDocument }),
});
return decoded;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function peerDidToDidDocument(did: string): Promise<DIDResolutionResult> {
if (!did.startsWith("did:peer:0z")) {
throw new Error(
"This only verifies a peer DID method 0 base58btc encoded.",
);
}
// this is basically hard-coded based on the results from the @aviarytech/did-peer resolver
const id = did.split(":")[2];
const multibase = id.slice(1);
const encnumbasis = multibase.slice(1);
const didDocument = {
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1",
],
assertionMethod: ["did:peer:" + id + "#" + encnumbasis],
authentication: ["did:peer:" + id + "#" + encnumbasis],
capabilityDelegation: ["did:peer:" + id + "#" + encnumbasis],
capabilityInvocation: ["did:peer:" + id + "#" + encnumbasis],
id: did,
keyAgreement: undefined,
service: undefined,
verificationMethod: [
{
id: "did:peer:" + id + "#" + encnumbasis,
type: "EcdsaSecp256k1VerificationKey2019",
controller: did,
publicKeyMultibase: multibase,
},
],
};
return {
didDocument,
didDocumentMetadata: {},
didResolutionMetadata: { contentType: "application/did+ld+json" },
};
}

Loading…
Cancel
Save