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 6 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", "@veramo/key-manager": "^5.6.0",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
"@zxing/text-encoding": "^0.9.0", "@zxing/text-encoding": "^0.9.0",
"asn1-ber": "^1.2.2",
"axios": "^1.6.8", "axios": "^1.6.8",
"cbor-x": "^1.5.9", "cbor-x": "^1.5.9",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
@ -10347,6 +10348,11 @@
"license": "MIT", "license": "MIT",
"optional": true "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": { "node_modules/asn1.js": {
"version": "5.4.1", "version": "5.4.1",
"license": "MIT", "license": "MIT",

1
package.json

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

128
src/libs/didPeer.ts

@ -1,11 +1,10 @@
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, verifyJWT } from "did-jwt"; 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 { bytesToMultibase } from "@veramo/utils";
import { generateRandomBytes } from "@/libs/crypto";
export interface JWK { export interface JWK {
kty: string; kty: string;
crv: string; crv: string;
@ -102,7 +101,7 @@ export function createPeerDid(publicKeyCose: Uint8Array) {
const methodSpecificId = bytesToMultibase( const methodSpecificId = bytesToMultibase(
publicKeyCose, publicKeyCose,
"base58btc", "base58btc",
"ed25519-pub", "secp256k1-pub",
); );
return "did:peer:0" + methodSpecificId; return "did:peer:0" + methodSpecificId;
} }
@ -128,18 +127,53 @@ export async function createJwt(
async function webAuthnES256KSigner(credentialID: ArrayBuffer) { async function webAuthnES256KSigner(credentialID: ArrayBuffer) {
return async (data: string | Uint8Array) => { return async (data: string | Uint8Array) => {
// also has clientDataJSON // also has clientDataJSON
const { signature, authenticatorData } = await generateWebAuthnSignature( const { signature } = await generateWebAuthnSignature(data, credentialID);
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 // Ensure R and S are 32 bytes each
const combinedSignature = Buffer.concat([ const rBuffer = Buffer.from(r);
Buffer.from(authenticatorData), const sBuffer = Buffer.from(s);
Buffer.from(signature), 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;
// 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,
);
return combinedSignature.toString("base64"); 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)) { if (!(dataToSign instanceof Uint8Array)) {
dataToSign = new TextEncoder().encode(dataToSign as string); dataToSign = new TextEncoder().encode(dataToSign as string);
} }
const challenge = generateRandomBytes(32);
const options = { const options = {
challenge: challenge, challenge: dataToSign,
allowCredentials: [{ id: credentialId, type: "public-key" }], allowCredentials: [{ id: credentialId, type: "public-key" }],
userVerification: "preferred", userVerification: "preferred",
}; };
const assertion = await navigator.credentials.get({ publicKey: options }); const assertion = await navigator.credentials.get({ publicKey: options });
console.log("assertion", assertion);
const authenticatorAssertionResponse = assertion?.response; const authenticatorAssertionResponse = assertion?.response;
console.log(
"clientDataJSON decoded",
JSON.parse(
new TextDecoder("utf-8").decode(
authenticatorAssertionResponse.clientDataJSON,
),
),
);
return { return {
signature: authenticatorAssertionResponse.signature, signature: authenticatorAssertionResponse.signature,
clientDataJSON: authenticatorAssertionResponse.clientDataJSON, clientDataJSON: authenticatorAssertionResponse.clientDataJSON,
@ -170,15 +212,55 @@ async function generateWebAuthnSignature(
export async function verifyJwt( export async function verifyJwt(
jwt: string, jwt: string,
issuerDid: string, issuerDid: string, // eslint-disable-line @typescript-eslint/no-unused-vars
publicKey: JWK, publicKey: JWK, // eslint-disable-line @typescript-eslint/no-unused-vars
) { ) {
const decoded = verifyJWT(jwt, { const decoded = verifyJWT(jwt, {
didAuthenticator: { // didAuthenticator: {
authenticators: [{ publicKeyJwk: publicKey }], // authenticators: [{ publicKeyJwk: publicKey }],
issuer: issuerDid, // issuer: issuerDid,
}, // },
resolver: getResolver(), //resolver: new Resolver({ ...getResolver() }),
resolver: new Resolver({ peer: peerDidToDidDocument }),
}); });
return decoded; 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