UNPKG

@dojima-wallet/connection

Version:

Initialise and connection for layer 1&2 blockchain

194 lines (175 loc) 4.58 kB
import * as bip39 from "bip39"; import crypto from "crypto"; import { blake256 } from "foundry-primitives"; import { v4 as uuidv4 } from "uuid"; import { pbkdf2Async } from "./utils"; // Constants const cipher = "aes-128-ctr"; const kdf = "pbkdf2"; const prf = "hmac-sha256"; const dklen = 32; const c = 262144; const hashFunction = "sha256"; const meta = "d11k-keystore"; /** * The Keystore interface */ export type Keystore = { crypto: { cipher: string; ciphertext: string; cipherparams: { iv: string; }; kdf: string; kdfparams: { prf: string; dklen: number; salt: string; c: number; }; mac: string; }; id: string; version: number; meta: string; }; /** * Determines if the current environment is node * * @returns {boolean} True if current environment is node */ const _isNode = (): boolean => { return typeof window === "undefined"; }; /** * Generate a new phrase. * * @param {string} size The new phrase size. * @returns {string} The generated phrase based on the size. */ export const generatePhrase = (size = 12): string => { if (_isNode()) { const bytes = crypto.randomBytes((size == 12 ? 128 : 256) / 8); const phrase = bip39.entropyToMnemonic(bytes); return phrase; } else { const entropy = size == 12 ? 128 : 256; const phrase = bip39.generateMnemonic(entropy); return phrase; } }; /** * Validate the given phrase. * * @param {string} phrase * @returns {boolean} `true` or `false` */ export const validatePhrase = (phrase: string): boolean => { return bip39.validateMnemonic(phrase); }; /** * Get the seed from the given phrase. * * @param {string} phrase * @returns {Buffer} The seed from the given phrase. * * @throws {"Invalid BIP39 phrase"} Thrown if phrase is an invalid one. */ export const getSeed = (phrase: string): Buffer => { if (!validatePhrase(phrase)) { throw new Error("Invalid BIP39 phrase"); } return bip39.mnemonicToSeedSync(phrase); }; /** * Get the Keystore interface from the given phrase and password. * * @param {string} phrase * @param {string} password * @returns {Keystore} The keystore interface generated from the given phrase and password. * * @throws {"Invalid BIP39 phrase"} Thrown if phrase is an invalid one. */ export const encryptToKeyStore = async ( phrase: string, password: string ): Promise<Keystore> => { if (!validatePhrase(phrase)) { throw new Error("Invalid BIP39 phrase"); } const ID = _isNode() ? require("uuid").v4() : uuidv4(); const salt = crypto.randomBytes(32); const iv = crypto.randomBytes(16); const kdfParams = { prf: prf, dklen: dklen, salt: salt.toString("hex"), c: c, }; const cipherParams = { iv: iv.toString("hex"), }; const derivedKey = await pbkdf2Async( Buffer.from(password), salt, kdfParams.c, kdfParams.dklen, hashFunction ); const cipherIV = crypto.createCipheriv(cipher, derivedKey.slice(0, 16), iv); const cipherText = Buffer.concat([ cipherIV.update(Buffer.from(phrase, "utf8")), cipherIV.final(), ]); const mac = blake256( Buffer.concat([derivedKey.slice(16, 32), Buffer.from(cipherText)]) ); const cryptoStruct = { cipher: cipher, ciphertext: cipherText.toString("hex"), cipherparams: cipherParams, kdf: kdf, kdfparams: kdfParams, mac: mac, }; const keystore = { crypto: cryptoStruct, id: ID, version: 1, meta: meta, }; return keystore; }; /** * Get the phrase from the keystore * * @param {Keystore} keystore * @param {string} password * @returns {Keystore} The phrase from the keystore. * * @throws {"Invalid password"} Thrown if password is an incorrect one. */ export const decryptFromKeystore = async ( keystore: Keystore, password: string ): Promise<string> => { const kdfparams = keystore.crypto.kdfparams; const derivedKey = await pbkdf2Async( Buffer.from(password), Buffer.from(kdfparams.salt, "hex"), kdfparams.c, kdfparams.dklen, hashFunction ); const ciphertext = Buffer.from(keystore.crypto.ciphertext, "hex"); const mac = blake256(Buffer.concat([derivedKey.slice(16, 32), ciphertext])); if (mac !== keystore.crypto.mac) throw new Error("Invalid password"); const decipher = crypto.createDecipheriv( keystore.crypto.cipher, derivedKey.slice(0, 16), Buffer.from(keystore.crypto.cipherparams.iv, "hex") ); const phrase = Buffer.concat([decipher.update(ciphertext), decipher.final()]); return phrase.toString("utf8"); };