UNPKG

blockstack

Version:

The Blockstack Javascript library for authentication, identity, and storage.

152 lines 5.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const bip39_1 = require("bip39"); const cryptoRandom_1 = require("./cryptoRandom"); const sha2Hash_1 = require("./sha2Hash"); const hmacSha256_1 = require("./hmacSha256"); const aesCipher_1 = require("./aesCipher"); const pbkdf2_1 = require("./pbkdf2"); /** * Encrypt a raw mnemonic phrase to be password protected * @param {string} phrase - Raw mnemonic phrase * @param {string} password - Password to encrypt mnemonic with * @return {Promise<Buffer>} The encrypted phrase * @private * @ignore * */ function encryptMnemonic(phrase, password, opts) { return tslib_1.__awaiter(this, void 0, void 0, function* () { // hex encoded mnemonic string let mnemonicEntropy; try { // must be bip39 mnemonic mnemonicEntropy = bip39_1.mnemonicToEntropy(phrase); } catch (error) { console.error('Invalid mnemonic phrase provided'); console.error(error); throw new Error('Not a valid bip39 mnemonic'); } // normalize plaintext to fixed length byte string const plaintextNormalized = Buffer.from(mnemonicEntropy, 'hex'); // AES-128-CBC with SHA256 HMAC const pbkdf2 = yield pbkdf2_1.createPbkdf2(); let salt; if (opts && opts.getRandomBytes) { salt = opts.getRandomBytes(16); } else { salt = cryptoRandom_1.randomBytes(16); } const keysAndIV = yield pbkdf2.derive(password, salt, 100000, 48, 'sha512'); const encKey = keysAndIV.slice(0, 16); const macKey = keysAndIV.slice(16, 32); const iv = keysAndIV.slice(32, 48); const cipher = yield aesCipher_1.createCipher(); const cipherText = yield cipher.encrypt('aes-128-cbc', encKey, iv, plaintextNormalized); const hmacPayload = Buffer.concat([salt, cipherText]); const hmacSha256 = yield hmacSha256_1.createHmacSha256(); const hmacDigest = yield hmacSha256.digest(macKey, hmacPayload); const payload = Buffer.concat([salt, hmacDigest, cipherText]); return payload; }); } exports.encryptMnemonic = encryptMnemonic; // Used to distinguish bad password during decrypt vs invalid format class PasswordError extends Error { } /** * @ignore */ function decryptMnemonicBuffer(dataBuffer, password) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const salt = dataBuffer.slice(0, 16); const hmacSig = dataBuffer.slice(16, 48); // 32 bytes const cipherText = dataBuffer.slice(48); const hmacPayload = Buffer.concat([salt, cipherText]); const pbkdf2 = yield pbkdf2_1.createPbkdf2(); const keysAndIV = yield pbkdf2.derive(password, salt, 100000, 48, 'sha512'); const encKey = keysAndIV.slice(0, 16); const macKey = keysAndIV.slice(16, 32); const iv = keysAndIV.slice(32, 48); const decipher = yield aesCipher_1.createCipher(); const decryptedResult = yield decipher.decrypt('aes-128-cbc', encKey, iv, cipherText); const hmacSha256 = yield hmacSha256_1.createHmacSha256(); const hmacDigest = yield hmacSha256.digest(macKey, hmacPayload); // hash both hmacSig and hmacDigest so string comparison time // is uncorrelated to the ciphertext const sha2Hash = yield sha2Hash_1.createSha2Hash(); const hmacSigHash = yield sha2Hash.digest(hmacSig); const hmacDigestHash = yield sha2Hash.digest(hmacDigest); if (!hmacSigHash.equals(hmacDigestHash)) { // not authentic throw new PasswordError('Wrong password (HMAC mismatch)'); } let mnemonic; try { mnemonic = bip39_1.entropyToMnemonic(decryptedResult); } catch (error) { console.error('Error thrown by `entropyToMnemonic`'); console.error(error); throw new PasswordError('Wrong password (invalid plaintext)'); } if (!bip39_1.validateMnemonic(mnemonic)) { throw new PasswordError('Wrong password (invalid plaintext)'); } return mnemonic; }); } /** * Decrypt legacy triplesec keys * @param {Buffer} dataBuffer - The encrypted key * @param {String} password - Password for data * @return {Promise<Buffer>} Decrypted seed * @private * @ignore */ function decryptLegacy(dataBuffer, password, triplesecDecrypt) { return new Promise((resolve, reject) => { if (!triplesecDecrypt) { reject(new Error('The `triplesec.decrypt` function must be provided')); } triplesecDecrypt({ key: Buffer.from(password), data: dataBuffer }, (err, plaintextBuffer) => { if (!err) { resolve(plaintextBuffer); } else { reject(err); } }); }); } /** * Decrypt an encrypted mnemonic phrase with a password. * Legacy triplesec encrypted payloads are also supported. * @param data - Buffer or hex-encoded string of the encrypted mnemonic * @param password - Password for data * @return the raw mnemonic phrase * @private * @ignore */ function decryptMnemonic(data, password, triplesecDecrypt) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data, 'hex'); try { return yield decryptMnemonicBuffer(dataBuffer, password); } catch (err) { if (err instanceof PasswordError) { throw err; } const data = yield decryptLegacy(dataBuffer, password, triplesecDecrypt); return data.toString(); } }); } exports.decryptMnemonic = decryptMnemonic; //# sourceMappingURL=wallet.js.map