import { IIdentifier } from "@veramo/core"; 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 { CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI, createEndorserJwtForDid, CONTACT_URL_PATH_ENDORSER_CH_OLD, CONTACT_IMPORT_ONE_URL_PATH_TIME_SAFARI, } from "@/libs/endorserServer"; import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup"; import { decodeEndorserJwt } from "@/libs/crypto/vc"; export const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'"; export const LOCAL_KMS_NAME = "local"; /** * * * @param {string} address * @param {string} publicHex * @param {string} privateHex * @param {string} derivationPath * @return {*} {Omit<IIdentifier, 'provider'>} */ export const newIdentifier = ( address: string, publicHex: string, privateHex: string, derivationPath: string, ): Omit<IIdentifier, keyof "provider"> => { return { did: DEFAULT_DID_PROVIDER_NAME + ":" + address, keys: [ { kid: publicHex, kms: LOCAL_KMS_NAME, 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, derivationPath: string = DEFAULT_ROOT_DERIVATION_PATH, ): [string, string, string, string] => { mnemonic = mnemonic.trim().toLowerCase(); const hdnode: HDNode = HDNode.fromMnemonic(mnemonic); const rootNode: HDNode = hdnode.derivePath(derivationPath); 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, derivationPath]; }; export const generateRandomBytes = (numBytes: number): Uint8Array => { return getRandomBytesSync(numBytes); }; /** * * * @return {*} {string} */ export const generateSeed = (): string => { const entropy: Uint8Array = getRandomBytesSync(32); const mnemonic = entropyToMnemonic(entropy, wordlist); return mnemonic; }; /** * Retrieve an access token, or "" if no DID is provided. * * @return {*} */ export const accessToken = async (did?: string) => { if (did) { const nowEpoch = Math.floor(Date.now() / 1000); const endEpoch = nowEpoch + 60; // add one minute const tokenPayload = { exp: endEpoch, iat: nowEpoch, iss: did }; return createEndorserJwtForDid(did, tokenPayload); } else { return ""; } }; /** @return payload of JWT pulled out of the URL and decoded: { iat: number, iss: string (DID), own: { name, publicEncKey (base64-encoded key) } } Result may be a single contact or it may be { contacts: [ contact, ... ] } */ export const getContactPayloadFromJwtUrl = (jwtUrlText: string) => { let jwtText = jwtUrlText; const appImportConfirmUrlLoc = jwtText.indexOf( CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI, ); if (appImportConfirmUrlLoc > -1) { jwtText = jwtText.substring( appImportConfirmUrlLoc + CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI.length, ); } const appImportOneUrlLoc = jwtText.indexOf( CONTACT_IMPORT_ONE_URL_PATH_TIME_SAFARI, ); if (appImportOneUrlLoc > -1) { jwtText = jwtText.substring( appImportOneUrlLoc + CONTACT_IMPORT_ONE_URL_PATH_TIME_SAFARI.length, ); } const endorserUrlPathLoc = jwtText.indexOf(CONTACT_URL_PATH_ENDORSER_CH_OLD); if (endorserUrlPathLoc > -1) { jwtText = jwtText.substring( endorserUrlPathLoc + CONTACT_URL_PATH_ENDORSER_CH_OLD.length, ); } // JWT format: { header, payload, signature, data } const jwt = decodeEndorserJwt(jwtText); return jwt.payload; }; export const nextDerivationPath = (origDerivPath: string) => { let lastStr = origDerivPath.split("/").slice(-1)[0]; if (lastStr.endsWith("'")) { lastStr = lastStr.slice(0, -1); } const lastNum = parseInt(lastStr, 10); const newLastNum = lastNum + 1; const newLastStr = newLastNum.toString() + (lastStr.endsWith("'") ? "'" : ""); const newDerivPath = origDerivPath .split("/") .slice(0, -1) .concat([newLastStr]) .join("/"); return newDerivPath; };