You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
4.4 KiB
157 lines
4.4 KiB
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;
|
|
};
|
|
|