UNPKG

turtlecoin-wallet-backend

Version:

[![Build Status](https://travis-ci.org/turtlecoin/turtlecoin-wallet-backend-js.svg?branch=master)](https://travis-ci.org/turtlecoin/turtlecoin-wallet-backend-js)

124 lines (123 loc) 5.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const crypto = require("crypto"); const pbkdf2 = require("pbkdf2"); const Constants_1 = require("./Constants"); const WalletError_1 = require("./WalletError"); class WalletEncryption { /** * Encrypt the wallet using the given password. Note that an empty password does not mean an * unencrypted wallet - simply a wallet encrypted with the empty string. * * This will take some time (Roughly a second on a modern PC) - it runs 500,000 iterations of pbkdf2. * * Example: * ```javascript * const dataJson = wallet.encryptWalletToString('hunter2'); * * ``` * * @param password The password to encrypt the wallet with * * @return Returns a string containing the encrypted fileData. */ static encryptWalletToString(walletJson, password) { const buffer = WalletEncryption.encryptWalletToBuffer(walletJson, password); return JSON.stringify(buffer); } /** * Encrypt the wallet using the given password. Note that an empty password does not mean an * unencrypted wallet - simply a wallet encrypted with the empty string. * * This will take some time (Roughly a second on a modern PC) - it runs 500,000 iterations of pbkdf2. * * Example: * ```javascript * const dataJson = wallet.encryptWalletToBuffer('hunter2'); * * ``` * * @param password The password to encrypt the wallet with * * @return Returns a Buffer containing the encrypted fileData. */ static encryptWalletToBuffer(walletJson, password) { /* Append the identifier so we can verify the password is correct */ const data = Buffer.concat([ Constants_1.IS_CORRECT_PASSWORD_IDENTIFIER, Buffer.from(walletJson), ]); /* Random salt */ const salt = crypto.randomBytes(16); /* PBKDF2 key for our encryption */ const key = pbkdf2.pbkdf2Sync(password, salt, Constants_1.PBKDF2_ITERATIONS, 16, 'sha256'); /* Encrypt with AES */ const cipher = crypto.createCipheriv('aes-128-cbc', key, salt); /* Perform the encryption */ const encryptedData = Buffer.concat([ cipher.update(data), cipher.final(), ]); /* Write the wallet identifier to the file so we know it's a wallet file. Write the salt so it can be decrypted again */ const fileData = Buffer.concat([ Constants_1.IS_A_WALLET_IDENTIFIER, salt, encryptedData, ]); return fileData; } /** * Decrypt the wallet from the given encrypted string with the given password and return * a JSON string. Uses pbkdf2 encryption, not the same as turtle-service * * Returns the JSON, and an error. If error is not undefined, the JSON will * be an empty string. */ static decryptWalletFromString(dataString, password) { const data = Buffer.from(JSON.parse(dataString).data); return WalletEncryption.decryptWalletFromBuffer(data, password); } /** * Decrypt the wallet from the given Buffer with the given password and return * a JSON string. Uses pbkdf2 encryption, not the same as turtle-service * * Returns the JSON, and an error. If error is not undefined, the JSON will * be an empty string. */ static decryptWalletFromBuffer(data, password) { /* Take a slice containing the wallet identifier magic bytes */ const magicBytes1 = data.slice(0, Constants_1.IS_A_WALLET_IDENTIFIER.length); if (magicBytes1.compare(Constants_1.IS_A_WALLET_IDENTIFIER) !== 0) { return ['', new WalletError_1.WalletError(WalletError_1.WalletErrorCode.NOT_A_WALLET_FILE)]; } /* Remove the magic bytes */ data = data.slice(Constants_1.IS_A_WALLET_IDENTIFIER.length, data.length); /* Grab the salt from the data */ const salt = data.slice(0, 16); /* Remove the salt from the data */ data = data.slice(salt.length, data.length); /* Derive our key with pbkdf2, 16 bytes long */ const key = pbkdf2.pbkdf2Sync(password, salt, Constants_1.PBKDF2_ITERATIONS, 16, 'sha256'); /* Setup the aes decryption */ const decipher = crypto.createDecipheriv('aes-128-cbc', key, salt); let decrypted; try { /* Perform the decryption */ decrypted = Buffer.concat([decipher.update(data), decipher.final()]); } catch (err) { return ['', new WalletError_1.WalletError(WalletError_1.WalletErrorCode.WRONG_PASSWORD)]; } /* Grab the second set of magic bytes */ const magicBytes2 = decrypted.slice(0, Constants_1.IS_CORRECT_PASSWORD_IDENTIFIER.length); /* Verify the magic bytes are present */ if (magicBytes2.compare(Constants_1.IS_CORRECT_PASSWORD_IDENTIFIER) !== 0) { return ['', new WalletError_1.WalletError(WalletError_1.WalletErrorCode.WRONG_PASSWORD)]; } /* Remove the magic bytes */ decrypted = decrypted.slice(Constants_1.IS_CORRECT_PASSWORD_IDENTIFIER.length, decrypted.length); return [decrypted.toString(), undefined]; } } exports.WalletEncryption = WalletEncryption;