refine "vc" functions further, separating passkey-specific items from did-peer file
This commit is contained in:
96
src/libs/crypto/vc/didPeer.ts
Normal file
96
src/libs/crypto/vc/didPeer.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import {Buffer} from "buffer/";
|
||||||
|
import {decode as cborDecode} from "cbor-x";
|
||||||
|
import {bytesToMultibase, multibaseToBytes} from "did-jwt";
|
||||||
|
|
||||||
|
import {getWebCrypto} from "@/libs/crypto/vc/passkeyHelpers";
|
||||||
|
|
||||||
|
const PEER_DID_PREFIX = "did:peer:";
|
||||||
|
const PEER_DID_MULTIBASE_PREFIX = PEER_DID_PREFIX + "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* similar code is in crowd-funder-for-time-pwa libs/crypto/vc/passkeyDidPeer.ts verifyJwtWebCrypto
|
||||||
|
*
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
export async function verifyPeerSignature(
|
||||||
|
payloadBytes: Buffer,
|
||||||
|
issuerDid: string,
|
||||||
|
signatureBytes: Uint8Array,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid);
|
||||||
|
|
||||||
|
const WebCrypto = await getWebCrypto();
|
||||||
|
const verifyAlgorithm = {
|
||||||
|
name: "ECDSA",
|
||||||
|
hash: { name: "SHA-256" },
|
||||||
|
};
|
||||||
|
const publicKeyJwk = cborToKeys(publicKeyBytes).publicKeyJwk;
|
||||||
|
const keyAlgorithm = {
|
||||||
|
name: "ECDSA",
|
||||||
|
namedCurve: publicKeyJwk.crv,
|
||||||
|
};
|
||||||
|
const publicKeyCryptoKey = await WebCrypto.subtle.importKey(
|
||||||
|
"jwk",
|
||||||
|
publicKeyJwk,
|
||||||
|
keyAlgorithm,
|
||||||
|
false,
|
||||||
|
["verify"],
|
||||||
|
);
|
||||||
|
const verified = await WebCrypto.subtle.verify(
|
||||||
|
verifyAlgorithm,
|
||||||
|
publicKeyCryptoKey,
|
||||||
|
signatureBytes,
|
||||||
|
payloadBytes,
|
||||||
|
);
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cborToKeys(publicKeyBytes: Uint8Array) {
|
||||||
|
const jwkObj = cborDecode(publicKeyBytes);
|
||||||
|
if (
|
||||||
|
jwkObj[1] != 2 || // kty "EC"
|
||||||
|
jwkObj[3] != -7 || // alg "ES256"
|
||||||
|
jwkObj[-1] != 1 || // crv "P-256"
|
||||||
|
jwkObj[-2].length != 32 || // x
|
||||||
|
jwkObj[-3].length != 32 // y
|
||||||
|
) {
|
||||||
|
throw new Error("Unable to extract key.");
|
||||||
|
}
|
||||||
|
const publicKeyJwk = {
|
||||||
|
alg: "ES256",
|
||||||
|
crv: "P-256",
|
||||||
|
kty: "EC",
|
||||||
|
x: arrayToBase64Url(jwkObj[-2]),
|
||||||
|
y: arrayToBase64Url(jwkObj[-3]),
|
||||||
|
};
|
||||||
|
const publicKeyBuffer = Buffer.concat([
|
||||||
|
Buffer.from(jwkObj[-2]),
|
||||||
|
Buffer.from(jwkObj[-3]),
|
||||||
|
]);
|
||||||
|
return { publicKeyJwk, publicKeyBuffer };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toBase64Url(anythingB64: string) {
|
||||||
|
return anythingB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arrayToBase64Url(anything: Uint8Array) {
|
||||||
|
return toBase64Url(Buffer.from(anything).toString("base64"));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function peerDidToPublicKeyBytes(did: string) {
|
||||||
|
return multibaseToBytes(did.substring(PEER_DID_MULTIBASE_PREFIX.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
"p256-pub",
|
||||||
|
);
|
||||||
|
return PEER_DID_MULTIBASE_PREFIX + methodSpecificId;
|
||||||
|
}
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
* Verifiable Credential & DID functions, specifically for EndorserSearch.org tools
|
* Verifiable Credential & DID functions, specifically for EndorserSearch.org tools
|
||||||
|
*
|
||||||
|
* The goal is to make this folder similar across projects, then move it to a library.
|
||||||
|
* Other projects: endorser-ch, image-api
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as didJwt from "did-jwt";
|
import * as didJwt from "did-jwt";
|
||||||
|
import { JWTDecoded } from "did-jwt/lib/JWT";
|
||||||
import { IIdentifier } from "@veramo/core";
|
import { IIdentifier } from "@veramo/core";
|
||||||
import * as u8a from "uint8arrays";
|
import * as u8a from "uint8arrays";
|
||||||
|
|
||||||
import { createDidPeerJwt } from "@/libs/crypto/vc/passkeyDidPeer";
|
import { createDidPeerJwt } from "@/libs/crypto/vc/passkeyDidPeer";
|
||||||
import {JWTDecoded} from "did-jwt/lib/JWT";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Meta info about a key
|
* Meta info about a key
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Buffer } from "buffer/";
|
import { Buffer } from "buffer/";
|
||||||
import { decode as cborDecode } from "cbor-x";
|
import { JWTPayload } from "did-jwt";
|
||||||
import { bytesToMultibase, JWTPayload, multibaseToBytes } from "did-jwt";
|
|
||||||
import { DIDResolutionResult } from "did-resolver";
|
import { DIDResolutionResult } from "did-resolver";
|
||||||
import { sha256 } from "ethereum-cryptography/sha256.js";
|
import { sha256 } from "ethereum-cryptography/sha256.js";
|
||||||
import {
|
import {
|
||||||
@@ -21,13 +20,14 @@ import {
|
|||||||
} from "@simplewebauthn/types";
|
} from "@simplewebauthn/types";
|
||||||
|
|
||||||
import { AppString } from "@/constants/app";
|
import { AppString } from "@/constants/app";
|
||||||
|
import { unwrapEC2Signature } from "@/libs/crypto/vc/passkeyHelpers";
|
||||||
import {
|
import {
|
||||||
getWebCrypto,
|
arrayToBase64Url,
|
||||||
unwrapEC2Signature,
|
cborToKeys,
|
||||||
} from "@/libs/crypto/vc/passkeyHelpers";
|
peerDidToPublicKeyBytes,
|
||||||
|
verifyPeerSignature,
|
||||||
|
} from "@/libs/crypto/vc/didPeer";
|
||||||
|
|
||||||
const PEER_DID_PREFIX = "did:peer:";
|
|
||||||
const PEER_DID_MULTIBASE_PREFIX = PEER_DID_PREFIX + "0";
|
|
||||||
export interface JWK {
|
export interface JWK {
|
||||||
kty: string;
|
kty: string;
|
||||||
crv: string;
|
crv: string;
|
||||||
@@ -35,14 +35,6 @@ export interface JWK {
|
|||||||
y: string;
|
y: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toBase64Url(anythingB64: string) {
|
|
||||||
return anythingB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function arrayToBase64Url(anything: Uint8Array) {
|
|
||||||
return toBase64Url(Buffer.from(anything).toString("base64"));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function registerCredential(passkeyName?: string) {
|
export async function registerCredential(passkeyName?: string) {
|
||||||
const options: PublicKeyCredentialCreationOptionsJSON =
|
const options: PublicKeyCredentialCreationOptionsJSON =
|
||||||
await generateRegistrationOptions({
|
await generateRegistrationOptions({
|
||||||
@@ -95,21 +87,6 @@ export async function registerCredential(passkeyName?: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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",
|
|
||||||
"p256-pub",
|
|
||||||
);
|
|
||||||
return PEER_DID_MULTIBASE_PREFIX + methodSpecificId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function peerDidToPublicKeyBytes(did: string) {
|
|
||||||
return multibaseToBytes(did.substring(PEER_DID_MULTIBASE_PREFIX.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PeerSetup {
|
export class PeerSetup {
|
||||||
public authenticatorData?: ArrayBuffer;
|
public authenticatorData?: ArrayBuffer;
|
||||||
public challenge?: Uint8Array;
|
public challenge?: Uint8Array;
|
||||||
@@ -422,33 +399,7 @@ export async function verifyJwtWebCrypto(
|
|||||||
|
|
||||||
// Construct the preimage
|
// Construct the preimage
|
||||||
const preimage = Buffer.concat([authDataFromBase, hash]);
|
const preimage = Buffer.concat([authDataFromBase, hash]);
|
||||||
|
return verifyPeerSignature(preimage, issuerDid, finalSigBuffer);
|
||||||
const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid);
|
|
||||||
|
|
||||||
const WebCrypto = await getWebCrypto();
|
|
||||||
const verifyAlgorithm = {
|
|
||||||
name: "ECDSA",
|
|
||||||
hash: { name: "SHA-256" },
|
|
||||||
};
|
|
||||||
const publicKeyJwk = cborToKeys(publicKeyBytes).publicKeyJwk;
|
|
||||||
const keyAlgorithm = {
|
|
||||||
name: "ECDSA",
|
|
||||||
namedCurve: publicKeyJwk.crv,
|
|
||||||
};
|
|
||||||
const publicKeyCryptoKey = await WebCrypto.subtle.importKey(
|
|
||||||
"jwk",
|
|
||||||
publicKeyJwk,
|
|
||||||
keyAlgorithm,
|
|
||||||
false,
|
|
||||||
["verify"],
|
|
||||||
);
|
|
||||||
const verified = await WebCrypto.subtle.verify(
|
|
||||||
verifyAlgorithm,
|
|
||||||
publicKeyCryptoKey,
|
|
||||||
finalSigBuffer,
|
|
||||||
preimage,
|
|
||||||
);
|
|
||||||
return verified;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
@@ -554,31 +505,6 @@ function base64URLStringToArrayBuffer(base64URLString: string) {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cborToKeys(publicKeyBytes: Uint8Array) {
|
|
||||||
const jwkObj = cborDecode(publicKeyBytes);
|
|
||||||
if (
|
|
||||||
jwkObj[1] != 2 || // kty "EC"
|
|
||||||
jwkObj[3] != -7 || // alg "ES256"
|
|
||||||
jwkObj[-1] != 1 || // crv "P-256"
|
|
||||||
jwkObj[-2].length != 32 || // x
|
|
||||||
jwkObj[-3].length != 32 // y
|
|
||||||
) {
|
|
||||||
throw new Error("Unable to extract key.");
|
|
||||||
}
|
|
||||||
const publicKeyJwk = {
|
|
||||||
alg: "ES256",
|
|
||||||
crv: "P-256",
|
|
||||||
kty: "EC",
|
|
||||||
x: arrayToBase64Url(jwkObj[-2]),
|
|
||||||
y: arrayToBase64Url(jwkObj[-3]),
|
|
||||||
};
|
|
||||||
const publicKeyBuffer = Buffer.concat([
|
|
||||||
Buffer.from(jwkObj[-2]),
|
|
||||||
Buffer.from(jwkObj[-3]),
|
|
||||||
]);
|
|
||||||
return { publicKeyJwk, publicKeyBuffer };
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
async function pemToCryptoKey(pem: string) {
|
async function pemToCryptoKey(pem: string) {
|
||||||
const binaryDerString = atob(
|
const binaryDerString = atob(
|
||||||
|
|||||||
@@ -11,10 +11,11 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
|||||||
import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
|
import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
|
||||||
import { GenericCredWrapper, containsHiddenDid } from "@/libs/endorserServer";
|
import { GenericCredWrapper, containsHiddenDid } from "@/libs/endorserServer";
|
||||||
import * as serverUtil from "@/libs/endorserServer";
|
import * as serverUtil from "@/libs/endorserServer";
|
||||||
import { createPeerDid, registerCredential } from "@/libs/crypto/vc/passkeyDidPeer";
|
import { registerCredential } from "@/libs/crypto/vc/passkeyDidPeer";
|
||||||
|
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
import {KeyMeta} from "@/libs/crypto/vc";
|
import {KeyMeta} from "@/libs/crypto/vc";
|
||||||
|
import {createPeerDid} from "@/libs/crypto/vc/didPeer";
|
||||||
|
|
||||||
export const PRIVACY_MESSAGE =
|
export const PRIVACY_MESSAGE =
|
||||||
"The data you send will be visible to the world -- except: your IDs and the IDs of anyone you tag will stay private, only visible to them and others you explicitly allow.";
|
"The data you send will be visible to the world -- except: your IDs and the IDs of anyone you tag will stay private, only visible to them and others you explicitly allow.";
|
||||||
|
|||||||
Reference in New Issue
Block a user