gets past code errors and now gotta verify signature (note that had to add Buffer in asn1 lib)
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Combine the WebAuthn components into a single signature format as required by did-jwt
|
const combSig64 = combinedSignature.toString("base64");
|
||||||
const combinedSignature = Buffer.concat([
|
console.log("combSig64", combSig64);
|
||||||
Buffer.from(authenticatorData),
|
const combSig64Url = combSig64
|
||||||
Buffer.from(signature),
|
.replace(/\+/g, "-")
|
||||||
]);
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=+$/, "");
|
||||||
return combinedSignature.toString("base64");
|
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" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user