UNPKG

@zlattice/lattice-js

Version:

Lattice blockchain TypeScript SDK with dual module support (CJS + ESM)

230 lines 9.34 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.KdfParams = exports.Kdf = exports.Aes = exports.Cipher = exports.FileKey = exports.SCRYPT_KEY_LEN = exports.SCRYPT_R = exports.SCRYPT_P = exports.SCRYPT_N = exports.KDF_SCRYPT = exports.AES_128_CTR = void 0; exports.generateCipher = generateCipher; exports.generateFileKey = generateFileKey; exports.decryptFileKey = decryptFileKey; const constants_1 = require("../common/constants.js"); const crypto_1 = require("../crypto/index.js"); const bytes_1 = require("@ethersproject/bytes"); const scrypt_1 = require("@noble/hashes/scrypt"); const utils_1 = require("@noble/hashes/utils"); const CryptoJS = __importStar(require("crypto-js")); const neverthrow_1 = require("neverthrow"); const uuid_1 = require("uuid"); const AES_128_CTR = "aes-128-ctr"; exports.AES_128_CTR = AES_128_CTR; const KDF_SCRYPT = "scrypt"; exports.KDF_SCRYPT = KDF_SCRYPT; const SCRYPT_N = 1 << 18; // 1<<18 = 262144,CPU/内存成本因子,控制计算和内存的使用量。 exports.SCRYPT_N = SCRYPT_N; const SCRYPT_P = 1; // 1,并行度因子,控制 scrypt 函数的并行度。 exports.SCRYPT_P = SCRYPT_P; const SCRYPT_R = 8; // 8,块大小因子,影响内部工作状态和内存占用。 exports.SCRYPT_R = SCRYPT_R; const SCRYPT_KEY_LEN = 32; // 32,生成的密钥长度,单位byte exports.SCRYPT_KEY_LEN = SCRYPT_KEY_LEN; const AES_BLOCK_SIZE = 16; // 16,AES 块大小,单位byte class FileKey { constructor(uuid, address, cipher, isGm) { this.uuid = uuid; this.address = address; this.cipher = cipher; this.isGm = isGm; } static fromJson(json) { try { const data = JSON.parse(json); return (0, neverthrow_1.ok)(new FileKey(data.uuid, data.address, new Cipher(new Aes(data.cipher.aes.cipher, data.cipher.aes.iv), new Kdf(data.cipher.kdf.kdf, new KdfParams(data.cipher.kdf.kdfParams.DkLen, data.cipher.kdf.kdfParams.n, data.cipher.kdf.kdfParams.p, data.cipher.kdf.kdfParams.r, data.cipher.kdf.kdfParams.salt)), data.cipher.cipherText, data.cipher.mac), data.isGM)); } catch (error) { return (0, neverthrow_1.err)(new Error("Invalid file key")); } } } exports.FileKey = FileKey; class Cipher { constructor(aes, kdf, cipherText, mac) { this.aes = aes; this.kdf = kdf; this.cipherText = cipherText; this.mac = mac; } } exports.Cipher = Cipher; class Aes { constructor(cipher, iv) { this.cipher = cipher; this.iv = iv; } } exports.Aes = Aes; class Kdf { constructor(kdf, // 密钥派生函数,scrypt, PBKDF2, bcrypt, HKDF kdfParams) { this.kdf = kdf; this.kdfParams = kdfParams; } } exports.Kdf = Kdf; class KdfParams { constructor(DkLen, // 生成的密钥长度,单位byte n, // CPU/内存成本因子,控制计算和内存的使用量。 p, // 并行度因子,控制 scrypt 函数的并行度。 r, // 块大小因子,影响内部工作状态和内存占用。 salt // 盐值,在密钥派生过程中加入随机性。 ) { this.DkLen = DkLen; this.n = n; this.p = p; this.r = r; this.salt = salt; } } exports.KdfParams = KdfParams; /** * Generate file key * @param privateKey - The private key * @param passphrase - The passphrase * @param curve - The curve * @returns The file key */ function generateFileKey(privateKey, passphrase, curve) { const crypto = (0, crypto_1.createCrypto)(curve); const address = crypto.publicKeyToAddress(crypto.getPublicKeyFromPrivateKey(privateKey)); const uuid = (0, uuid_1.v4)(); const generateCipherResult = generateCipher(privateKey, passphrase, curve); if (generateCipherResult.isErr()) { return (0, neverthrow_1.err)(generateCipherResult.error); } return (0, neverthrow_1.ok)(new FileKey(uuid, address, generateCipherResult.value, curve === constants_1.Curves.Sm2p256v1)); } /** * Decrypt file key * @param fileKey - The file key * @param passphrase - The passphrase * @returns The private key */ function decryptFileKey(fileKey, passphrase) { const result = typeof fileKey === "string" ? FileKey.fromJson(fileKey) : (0, neverthrow_1.ok)(fileKey); if (result.isErr()) { return (0, neverthrow_1.err)(result.error); } const fk = result.value; const salt = Buffer.from(fk.cipher.kdf.kdfParams.salt, "hex"); const key = scryptKey(passphrase, salt, SCRYPT_N); const aesKey = key.subarray(0, AES_BLOCK_SIZE); const hashKey = key.subarray(AES_BLOCK_SIZE, AES_BLOCK_SIZE * 2); // compact amc const cipher = Buffer.from(fk.cipher.cipherText, "hex"); const curve = fk.isGm ? constants_1.Curves.Sm2p256v1 : constants_1.Curves.Secp256k1; const actualMac = (0, crypto_1.createCrypto)(curve).hash(Buffer.concat([hashKey, cipher])); const expectedMac = Buffer.from(fk.cipher.mac, "hex"); if (actualMac.toString("hex") !== expectedMac.toString("hex")) { return (0, neverthrow_1.err)(new Error("根据密码无法解析出私钥,请检查密码")); } const iv = Buffer.from(fk.cipher.aes.iv, "hex"); const privateKey = aesCtrDecrypt(aesKey, iv, cipher); return (0, neverthrow_1.ok)((0, bytes_1.hexlify)(privateKey)); } /** * Generate cipher * @param privateKey - The private key * @param passphrase - The passphrase * @param curve - The curve * @returns The cipher */ function generateCipher(privateKey, passphrase, curve) { // generate salt const salt = Buffer.from((0, utils_1.randomBytes)(32)); const key = scryptKey(passphrase, salt, SCRYPT_N); const aesKey = key.subarray(0, AES_BLOCK_SIZE); const hashKey = key.subarray(AES_BLOCK_SIZE, AES_BLOCK_SIZE * 2); // compact amc const ivBytes = Buffer.from((0, utils_1.randomBytes)(AES_BLOCK_SIZE)); const cipher = aesCtrEncrypt(aesKey, ivBytes, Buffer.from(privateKey.startsWith(constants_1.HEX_PREFIX) ? privateKey.slice(constants_1.HEX_PREFIX.length) : privateKey, "hex")); const mac = (0, crypto_1.createCrypto)(curve).hash(Buffer.concat([hashKey, cipher])); return (0, neverthrow_1.ok)(new Cipher(new Aes(AES_128_CTR, ivBytes.toString("hex")), new Kdf(KDF_SCRYPT, new KdfParams(32, SCRYPT_N, SCRYPT_P, SCRYPT_R, salt.toString("hex"))), cipher.toString("hex"), mac.toString("hex"))); } /** * Scrypt key * @param password - The password * @param salt - The salt * @param n - The n * @returns The derived key */ function scryptKey(password, salt, n) { const derivedKey = (0, scrypt_1.scrypt)(password, salt, { N: n, r: SCRYPT_R, p: SCRYPT_P, dkLen: SCRYPT_KEY_LEN }); return Buffer.from(derivedKey); } /** * AES-CTR encrypt * @param key - The key * @param iv - The iv * @param message - The message * @returns The encrypted buffer */ function aesCtrEncrypt(key, iv, message) { const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Hex.parse(message.toString("hex")), CryptoJS.enc.Hex.parse(key.toString("hex")), { iv: CryptoJS.enc.Hex.parse(iv.toString("hex")), mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding // no padding }); return Buffer.from(encrypted.ciphertext.toString(CryptoJS.enc.Hex), "hex"); } /** * AES-CTR decrypt * @param key - The key * @param iv - The iv * @param cipher - The cipher * @returns The decrypted buffer */ function aesCtrDecrypt(key, iv, cipher) { const decrypted = CryptoJS.AES.decrypt(CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Hex.parse(cipher.toString("hex")) }), CryptoJS.enc.Hex.parse(key.toString("hex")), { iv: CryptoJS.enc.Hex.parse(iv.toString("hex")), mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding // no padding }); return Buffer.from(decrypted.toString(CryptoJS.enc.Hex), "hex"); } //# sourceMappingURL=file_key.js.map