import { IIdentifier } from "@veramo/core"; import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup"; import { getRandomBytesSync } from "ethereum-cryptography/random"; import { entropyToMnemonic } from "ethereum-cryptography/bip39"; import { wordlist } from "ethereum-cryptography/bip39/wordlists/english"; import { HDNode } from "@ethersproject/hdnode"; import * as didJwt from "did-jwt"; import * as u8a from "uint8arrays"; /** * * * @param {string} address * @param {string} publicHex * @param {string} privateHex * @param {string} derivationPath * @return {*} {Omit} */ export const newIdentifier = ( address: string, publicHex: string, privateHex: string, derivationPath: string ): Omit => { return { did: DEFAULT_DID_PROVIDER_NAME + ":" + address, keys: [ { kid: publicHex, kms: "local", meta: { derivationPath: derivationPath }, privateKeyHex: privateHex, publicKeyHex: publicHex, type: "Secp256k1", }, ], provider: DEFAULT_DID_PROVIDER_NAME, services: [], }; }; /** * * * @param {string} mnemonic * @return {*} {[string, string, string, string]} */ export const deriveAddress = ( mnemonic: string ): [string, string, string, string] => { const UPORT_ROOT_DERIVATION_PATH = "m/7696500'/0'/0'/0'"; mnemonic = mnemonic.trim().toLowerCase(); const hdnode: HDNode = HDNode.fromMnemonic(mnemonic); const rootNode: HDNode = hdnode.derivePath(UPORT_ROOT_DERIVATION_PATH); const privateHex = rootNode.privateKey.substring(2); // original starts with '0x' const publicHex = rootNode.publicKey.substring(2); // original starts with '0x' const address = rootNode.address; return [address, privateHex, publicHex, UPORT_ROOT_DERIVATION_PATH]; }; /** * * * @return {*} {string} */ export const generateSeed = (): string => { const entropy: Uint8Array = getRandomBytesSync(32); const mnemonic = entropyToMnemonic(entropy, wordlist); return mnemonic; }; /** * Retreive an access token * * @param {IIdentifier} identifier * @return {*} */ export const accessToken = async (identifier: IIdentifier) => { const did: string = identifier.did; const privateKeyHex: string = identifier.keys[0].privateKeyHex as string; const signer = SimpleSigner(privateKeyHex); const nowEpoch = Math.floor(Date.now() / 1000); const endEpoch = nowEpoch + 60; // add one minute const tokenPayload = { exp: endEpoch, iat: nowEpoch, iss: did }; const alg = undefined; // defaults to 'ES256K', more standardized but harder to verify vs ES256K-R const jwt: string = await didJwt.createJWT(tokenPayload, { alg, issuer: did, signer, }); return jwt; }; export const sign = async (privateKeyHex: string) => { const signer = SimpleSigner(privateKeyHex); return signer; }; /** * 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(process.env.PRIVATE_KEY) * signer(data, (err, signature) => { * ... * }) * * @param {String} hexPrivateKey a hex encoded private key * @return {Function} a configured signer function */ export 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 export 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 export function bytesToHex(b: Uint8Array): string { return u8a.toString(b, "base16"); }