Trent Larson
4 months ago
11 changed files with 181 additions and 97 deletions
@ -0,0 +1,106 @@ |
|||
/** |
|||
* Verifiable Credential & DID functions, specifically for EndorserSearch.org tools |
|||
*/ |
|||
|
|||
import * as didJwt from "did-jwt"; |
|||
import { IIdentifier } from "@veramo/core"; |
|||
import * as u8a from "uint8arrays"; |
|||
|
|||
import { createDidPeerJwt } from "@/libs/crypto/vc/passkeyDidPeer"; |
|||
import {JWTDecoded} from "did-jwt/lib/JWT"; |
|||
|
|||
/** |
|||
* Meta info about a key |
|||
*/ |
|||
export interface KeyMeta { |
|||
/** |
|||
* Decentralized ID for the key |
|||
*/ |
|||
did: string; |
|||
/** |
|||
* Stringified IIDentifier object from Veramo |
|||
*/ |
|||
identity?: string; |
|||
/** |
|||
* The Webauthn credential ID in hex, if this is from a passkey |
|||
*/ |
|||
passkeyCredIdHex?: string; |
|||
} |
|||
|
|||
/** |
|||
* Tell whether a key is from a passkey |
|||
* @param keyMeta contains info about the key, whose passkeyCredIdHex determines if the key is from a passkey |
|||
*/ |
|||
export function isFromPasskey(keyMeta?: KeyMeta): boolean { |
|||
return !!keyMeta?.passkeyCredIdHex; |
|||
} |
|||
|
|||
export async function createEndorserJwtForKey( |
|||
account: KeyMeta, |
|||
payload: object, |
|||
) { |
|||
if (account?.identity) { |
|||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|||
const identity: IIdentifier = JSON.parse(account.identity!); |
|||
const privateKeyHex = identity.keys[0].privateKeyHex; |
|||
const signer = await SimpleSigner(privateKeyHex as string); |
|||
return didJwt.createJWT(payload, { |
|||
issuer: account.did, |
|||
signer: signer, |
|||
}); |
|||
} else if (account?.passkeyCredIdHex) { |
|||
return createDidPeerJwt(account.did, account.passkeyCredIdHex, payload); |
|||
} else { |
|||
throw new Error("No identity data found to sign for DID " + account.did); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Copied out of did-jwt since it's deprecated in that library. |
|||
* |
|||
* The SimpleSigner returns a configured function for signing data. |
|||
* |
|||
* @example |
|||
* const signer = SimpleSigner(import.meta.env.PRIVATE_KEY) |
|||
* signer(data, (err, signature) => { |
|||
* ... |
|||
* }) |
|||
* |
|||
* @param {String} hexPrivateKey a hex encoded private key |
|||
* @return {Function} a configured signer function |
|||
*/ |
|||
function SimpleSigner(hexPrivateKey: string): didJwt.Signer { |
|||
const signer = didJwt.ES256KSigner(didJwt.hexToBytes(hexPrivateKey), true); |
|||
return async (data) => { |
|||
const signature = (await signer(data)) as string; |
|||
return fromJose(signature); |
|||
}; |
|||
} |
|||
|
|||
// from did-jwt/util; see SimpleSigner above
|
|||
function fromJose(signature: string): { |
|||
r: string; |
|||
s: string; |
|||
recoveryParam?: number; |
|||
} { |
|||
const signatureBytes: Uint8Array = didJwt.base64ToBytes(signature); |
|||
if (signatureBytes.length < 64 || signatureBytes.length > 65) { |
|||
throw new TypeError( |
|||
`Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`, |
|||
); |
|||
} |
|||
const r = bytesToHex(signatureBytes.slice(0, 32)); |
|||
const s = bytesToHex(signatureBytes.slice(32, 64)); |
|||
const recoveryParam = |
|||
signatureBytes.length === 65 ? signatureBytes[64] : undefined; |
|||
return {r, s, recoveryParam}; |
|||
} |
|||
|
|||
// from did-jwt/util; see SimpleSigner above
|
|||
function bytesToHex(b: Uint8Array): string { |
|||
return u8a.toString(b, "base16"); |
|||
} |
|||
|
|||
export function decodeEndorserJwt(jwt: string): JWTDecoded { |
|||
return didJwt.decodeJWT(jwt); |
|||
} |
Loading…
Reference in new issue