UNPKG

@trezor/connect

Version:

High-level javascript interface for Trezor hardware wallet.

112 lines 4.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.verifyEntropy = exports.generateEntropy = void 0; const crypto_1 = require("@noble/hashes/crypto"); const hmac_1 = require("@noble/hashes/hmac"); const sha256_1 = require("@noble/hashes/sha256"); const utils_1 = require("@noble/hashes/utils"); const bip39_1 = require("@scure/bip39"); const crypto_utils_1 = require("@trezor/crypto-utils"); const utxo_lib_1 = require("@trezor/utxo-lib"); const constants_1 = require("../../constants"); const generateEntropy = (len) => { try { return Buffer.from((0, utils_1.randomBytes)(len)); } catch { throw new Error('generateEntropy: Environment does not support crypto random'); } }; exports.generateEntropy = generateEntropy; const BASE_ITERATION_COUNT = 10000; const ROUND_COUNT = 4; const roundFunction = async (i, passphrase, e, salt, r) => { const data = Buffer.concat([Buffer.from([i]), passphrase]); const iterations = Math.floor((BASE_ITERATION_COUNT << e) / ROUND_COUNT); const { subtle } = crypto_1.crypto; const key = await subtle.importKey('raw', data, 'PBKDF2', false, ['deriveBits']); const bits = await subtle.deriveBits({ name: 'PBKDF2', hash: 'SHA-256', salt: Buffer.concat([salt, r]), iterations, }, key, r.length * 8); return Buffer.from(bits); }; const xor = (a, b) => { if (a.length !== b.length) { throw new Error('Buffers must be of equal length to XOR.'); } const result = Buffer.alloc(a.length); for (let i = 0; i < a.length; i++) { result[i] = a[i] ^ b[i]; } return result; }; const entropyToSeedSlip39 = async (encryptedSecret) => { const iterationExponent = 1; const passphrase = Buffer.from('', 'utf-8'); const salt = Buffer.alloc(0); const half = Math.floor(encryptedSecret.length / 2); let l = encryptedSecret.subarray(0, half); let r = encryptedSecret.subarray(half); for (let round = ROUND_COUNT - 1; round >= 0; round--) { const f = await roundFunction(round, passphrase, iterationExponent, salt, r); const rr = xor(l, f); l = r; r = rr; } return Buffer.concat([r, l]); }; const getEntropy = (trezorEntropy, hostEntropy, strength) => { const data = Buffer.concat([ Buffer.from(trezorEntropy, 'hex'), Buffer.from(hostEntropy, 'hex'), ]); const entropy = (0, sha256_1.sha256)(data); return Buffer.from(entropy.subarray(0, Math.floor(strength / 8))); }; const computeSeed = (type, secret) => { const BackupType = constants_1.PROTO.Enum_BackupType; if (type && [ BackupType.Slip39_Basic, BackupType.Slip39_Advanced, BackupType.Slip39_Single_Extendable, BackupType.Slip39_Basic_Extendable, BackupType.Slip39_Advanced_Extendable, ].includes(type)) { return entropyToSeedSlip39(secret); } return (0, bip39_1.mnemonicToSeed)((0, bip39_1.entropyToMnemonic)(secret, [...crypto_utils_1.bip39])).then(seed => Buffer.from(seed)); }; const verifyCommitment = (entropy, commitment) => { const hmacDigest = (0, hmac_1.hmac)(sha256_1.sha256, Buffer.from(entropy, 'hex'), Buffer.alloc(0)); if (!Buffer.from(hmacDigest).equals(Buffer.from(commitment, 'hex'))) { throw new Error('Invalid entropy commitment'); } }; const verifyEntropy = async ({ type, strength, trezorEntropy, hostEntropy, commitment, xpubs, }) => { try { if (!trezorEntropy || !commitment || !strength || Object.keys(xpubs).length < 1) { throw new Error('Missing verifyEntropy data'); } verifyCommitment(trezorEntropy, commitment); const secret = getEntropy(trezorEntropy, hostEntropy, strength); const seed = await computeSeed(type, secret); const node = utxo_lib_1.bip32.fromSeed(seed); Object.keys(xpubs).forEach(path => { const pubKey = node.derivePath(path); const xpub = pubKey.neutered().toBase58(); if (xpub !== xpubs[path]) { throw new Error('verifyEntropy xpub mismatch'); } }); return { success: true }; } catch (error) { return { success: false, error: error.message }; } }; exports.verifyEntropy = verifyEntropy; //# sourceMappingURL=verifyEntropy.js.map