Skip to content

Commit

Permalink
fix: prettier formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranjamie committed Jun 23, 2020
1 parent 3aaf773 commit 0069bd7
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 83 deletions.
1 change: 0 additions & 1 deletion .prettierignore
@@ -1,5 +1,4 @@
src/wallet.ts
src/encryption/wallet.ts
src/encryption/index.ts
src/encryption/ec.ts
src/errors.ts
Expand Down
181 changes: 99 additions & 82 deletions src/encryption/wallet.ts
@@ -1,126 +1,143 @@
import { validateMnemonic, mnemonicToEntropy, entropyToMnemonic } from 'bip39'
import { randomBytes, GetRandomBytes } from './cryptoRandom'
import { createSha2Hash } from './sha2Hash'
import { createHmacSha256 } from './hmacSha256'
import { createCipher } from './aesCipher'
import { createPbkdf2 } from './pbkdf2'
import { TriplesecDecryptSignature } from './cryptoUtils'
import { validateMnemonic, mnemonicToEntropy, entropyToMnemonic } from 'bip39';
import { randomBytes, GetRandomBytes } from './cryptoRandom';
import { createSha2Hash } from './sha2Hash';
import { createHmacSha256 } from './hmacSha256';
import { createCipher } from './aesCipher';
import { createPbkdf2 } from './pbkdf2';
import { TriplesecDecryptSignature } from './cryptoUtils';

/**
* 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
* @ignore
* */
export async function encryptMnemonic(phrase: string, password: string, opts?: {
getRandomBytes?: GetRandomBytes
}): Promise<Buffer> {
export async function encryptMnemonic(
phrase: string,
password: string,
opts?: {
getRandomBytes?: GetRandomBytes;
}
): Promise<Buffer> {
// hex encoded mnemonic string
let mnemonicEntropy: string
let mnemonicEntropy: string;
try {
// must be bip39 mnemonic
mnemonicEntropy = mnemonicToEntropy(phrase)
mnemonicEntropy = mnemonicToEntropy(phrase);
} catch (error) {
console.error('Invalid mnemonic phrase provided')
console.error(error)
throw new Error('Not a valid bip39 mnemonic')
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')
const plaintextNormalized = Buffer.from(mnemonicEntropy, 'hex');

// AES-128-CBC with SHA256 HMAC
const pbkdf2 = await createPbkdf2()
let salt: Buffer
const pbkdf2 = await createPbkdf2();
let salt: Buffer;
if (opts && opts.getRandomBytes) {
salt = opts.getRandomBytes(16)
salt = opts.getRandomBytes(16);
} else {
salt = randomBytes(16)
salt = randomBytes(16);
}
const keysAndIV = await 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 = await createCipher()
const cipherText = await cipher.encrypt('aes-128-cbc', encKey, iv, plaintextNormalized)

const hmacPayload = Buffer.concat([salt, cipherText])
const hmacSha256 = await createHmacSha256()
const hmacDigest = await hmacSha256.digest(macKey, hmacPayload)

const payload = Buffer.concat([salt, hmacDigest, cipherText])
return payload
const keysAndIV = await 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 = await createCipher();
const cipherText = await cipher.encrypt(
'aes-128-cbc',
encKey,
iv,
plaintextNormalized
);

const hmacPayload = Buffer.concat([salt, cipherText]);
const hmacSha256 = await createHmacSha256();
const hmacDigest = await hmacSha256.digest(macKey, hmacPayload);

const payload = Buffer.concat([salt, hmacDigest, cipherText]);
return payload;
}

// Used to distinguish bad password during decrypt vs invalid format
class PasswordError extends Error { }
class PasswordError extends Error {}

/**
* @ignore
*/
async function decryptMnemonicBuffer(dataBuffer: Buffer, password: string): Promise<string> {
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 = await createPbkdf2()
const keysAndIV = await 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 = await createCipher()
const decryptedResult = await decipher.decrypt('aes-128-cbc', encKey, iv, cipherText);

const hmacSha256 = await createHmacSha256()
const hmacDigest = await hmacSha256.digest(macKey, hmacPayload)
* @ignore
*/
async function decryptMnemonicBuffer(
dataBuffer: Buffer,
password: string
): Promise<string> {
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 = await createPbkdf2();
const keysAndIV = await 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 = await createCipher();
const decryptedResult = await decipher.decrypt(
'aes-128-cbc',
encKey,
iv,
cipherText
);

const hmacSha256 = await createHmacSha256();
const hmacDigest = await hmacSha256.digest(macKey, hmacPayload);

// hash both hmacSig and hmacDigest so string comparison time
// is uncorrelated to the ciphertext
const sha2Hash = await createSha2Hash()
const hmacSigHash = await sha2Hash.digest(hmacSig)
const hmacDigestHash = await sha2Hash.digest(hmacDigest)
const sha2Hash = await createSha2Hash();
const hmacSigHash = await sha2Hash.digest(hmacSig);
const hmacDigestHash = await sha2Hash.digest(hmacDigest);

if (!hmacSigHash.equals(hmacDigestHash)) {
// not authentic
throw new PasswordError('Wrong password (HMAC mismatch)')
throw new PasswordError('Wrong password (HMAC mismatch)');
}

let mnemonic: string
let mnemonic: string;
try {
mnemonic = entropyToMnemonic(decryptedResult)
mnemonic = entropyToMnemonic(decryptedResult);
} catch (error) {
console.error('Error thrown by `entropyToMnemonic`')
console.error(error)
throw new PasswordError('Wrong password (invalid plaintext)')
console.error('Error thrown by `entropyToMnemonic`');
console.error(error);
throw new PasswordError('Wrong password (invalid plaintext)');
}
if (!validateMnemonic(mnemonic)) {
throw new PasswordError('Wrong password (invalid plaintext)')
throw new PasswordError('Wrong password (invalid plaintext)');
}

return mnemonic
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
* @ignore
*/
function decryptLegacy(dataBuffer: Buffer,
password: string,
triplesecDecrypt: TriplesecDecryptSignature
function decryptLegacy(
dataBuffer: Buffer,
password: string,
triplesecDecrypt: TriplesecDecryptSignature
): Promise<Buffer> {
return new Promise<Buffer>((resolve, reject) => {
if (!triplesecDecrypt) {
reject(new Error('The `triplesec.decrypt` function must be provided'))
reject(new Error('The `triplesec.decrypt` function must be provided'));
}
triplesecDecrypt(
{
Expand All @@ -129,34 +146,34 @@ function decryptLegacy(dataBuffer: Buffer,
},
(err, plaintextBuffer) => {
if (!err) {
resolve(plaintextBuffer)
resolve(plaintextBuffer);
} else {
reject(err)
reject(err);
}
}
)
})
);
});
}

/**
* Decrypt an encrypted mnemonic phrase with a password.
* Legacy triplesec encrypted payloads are also supported.
* 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
* @ignore
*/
export async function decryptMnemonic(
data: string | Buffer,
password: string,
password: string,
triplesecDecrypt?: TriplesecDecryptSignature
) {
const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data, 'hex');
try {
return await decryptMnemonicBuffer(dataBuffer, password);
} catch (err) {
if (err instanceof PasswordError) {
if (err instanceof PasswordError) {
throw err;
}
const data = await decryptLegacy(dataBuffer, password, triplesecDecrypt);
Expand Down

0 comments on commit 0069bd7

Please sign in to comment.