Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #507 from blockchain/v3.43-release
Browse files Browse the repository at this point in the history
v3.43 release
  • Loading branch information
prwelber authored Apr 16, 2018
2 parents c30c65a + 594bb83 commit 6edace4
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 307 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ lib
# Generated coverage reports
coverage
coverage-lcov

# IDE
.idea
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v7.5.0
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "blockchain-wallet-client",
"version": "3.42.2",
"version": "3.43.8",
"description": "Blockchain.info JavaScript Wallet",
"homepage": "https://github.com/blockchain/my-wallet-v3",
"bugs": {
Expand Down Expand Up @@ -49,9 +49,9 @@
"bignumber.js": "^4.0.2",
"bip39": "2.1.*",
"bitcoin-coinify-client": "^0.7.6",
"bitcoin-sfox-client": "^0.2.6",
"bitcoin-sfox-client": "^0.3.0",
"bitcoin-unocoin-client": "^0.3.6",
"bitcoincashjs-lib": "https://github.com/bitcoinjs/bitcoinjs-lib#d0b716fe0660d7db0e53a616f02312dfaab2b761",
"bitcoincashjs-lib": "https://github.com/blockchain/bitcoinjs-lib#d0b716fe0660d7db0e53a616f02312dfaab2b761",
"bitcoinjs-lib": "2.1.*",
"bs58": "2.0.*",
"cashaddress": "^1.1.0",
Expand Down
76 changes: 49 additions & 27 deletions src/eth/eth-wallet.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
const crypto = require('crypto');
const WebSocket = require('ws');
const ethUtil = require('ethereumjs-util');
const WalletCrypto = require('../wallet-crypto');
const EthHd = require('ethereumjs-wallet/hdkey');
const { construct } = require('ramda');
const { isPositiveNumber, asyncOnce, dedup } = require('../helpers');
const { construct, has } = require('ramda');
const { isPositiveNumber, isHex, asyncOnce, dedup, unsortedEquals, isNumber } = require('../helpers');
const API = require('../api');
const EthTxBuilder = require('./eth-tx-builder');
const EthAccount = require('./eth-account');
const EthSocket = require('./eth-socket');
const EthWalletTx = require('./eth-wallet-tx');
ethUtil.scrypt = require('scryptsy');

const objHasKeys = (obj, keys) => keys.every(k => has(k, obj));

const METADATA_TYPE_ETH = 5;
const DERIVATION_PATH = "m/44'/60'/0'/0";
Expand Down Expand Up @@ -411,38 +413,58 @@ class EthWallet {
return Buffer.concat([decipher.update(data), decipher.final()]);
}

fromMew (input, password, nonStrict) {
var json = (typeof input === 'object') ? input : JSON.parse(nonStrict ? input.toLowerCase() : input);
if (json.version !== 3) {
throw new Error('Not a v3 wallet');
}
var derivedKey;
var kdfparams;
if (json.crypto.kdf === 'scrypt') {
kdfparams = json.crypto.kdfparams;
derivedKey = ethUtil.scrypt(new Buffer(password), new Buffer(kdfparams.salt, 'hex'), kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen);
} else if (json.crypto.kdf === 'pbkdf2') {
kdfparams = json.crypto.kdfparams;
if (kdfparams.prf !== 'hmac-sha256') {
throw new Error('Unsupported parameters to PBKDF2');
}
derivedKey = crypto.pbkdf2Sync(new Buffer(password), new Buffer(kdfparams.salt, 'hex'), kdfparams.c, kdfparams.dklen, 'sha256');
} else {
throw new Error('Unsupported key derivation scheme');
}

extractSeed (derivedKey, json) {
if (!Buffer.isBuffer(derivedKey)) { throw new Error('Expected key to be a Buffer'); }
if (typeof json.crypto !== 'object') { throw new Error('Expected crypto to be an object'); }
var ciphertext = new Buffer(json.crypto.ciphertext, 'hex');
var mac = ethUtil.sha3(Buffer.concat([derivedKey.slice(16, 32), ciphertext]));
if (mac.toString('hex') !== json.crypto.mac) {
throw new Error('Key derivation failed - possibly wrong passphrase');
}
if (mac.toString('hex') !== json.crypto.mac) { throw new Error('Key derivation failed - possibly wrong passphrase'); }

var decipher = crypto.createDecipheriv(json.crypto.cipher, derivedKey.slice(0, 16), new Buffer(json.crypto.cipherparams.iv, 'hex'));
var seed = this.decipherBuffer(decipher, ciphertext, 'hex');
while (seed.length < 32) {
var nullBuff = new Buffer([0x00]);
seed = Buffer.concat([nullBuff, seed]);
}
return EthAccount.fromMew(seed);
return seed;
}

fromMew (json, password) {
if (typeof json !== 'object') { throw new Error('Not a supported file type'); }
if (isNaN(json.version)) { throw new Error('Not a supported wallet. Please use a valid wallet version.'); }
if (!objHasKeys(json, ['crypto', 'id', 'version'])) { throw new Error('File is malformatted'); }
if (!objHasKeys(json.crypto, ['cipher', 'cipherparams', 'ciphertext', 'kdf', 'kdfparams', 'mac'])) { throw new Error('Crypto is not valid'); }
if (!isHex(json.crypto.cipherparams.iv)) { throw new Error('Not a supported param: cipherparams.iv'); }
if (!isHex(json.crypto.ciphertext)) { throw new Error('Not a supported param: ciphertext'); }

let kdfparams;
// TODO: breakout format validation into separate function
if (json.crypto.kdf === 'scrypt') {
kdfparams = json.crypto.kdfparams;
if (!unsortedEquals(Object.keys(kdfparams), ['dklen', 'n', 'p', 'r', 'salt'])) { throw new Error('File is malformatted'); }
if (!objHasKeys(kdfparams, ['dklen', 'n', 'p', 'r'])) { throw new Error('Not a supported param: kdfparams'); }
if (!isHex(kdfparams.salt)) { throw new Error('Not a supported param: kdfparams.salt'); }
if (!['dklen', 'n', 'p', 'r'].every(i => isNumber(kdfparams[i]))) { throw new Error('Not a supported param: dklen, n, p, r must be numbers'); }

let { salt, n, r, p, dklen } = kdfparams;
let derivedKey = WalletCrypto.scrypt(Buffer.from(password), Buffer.from(salt, 'hex'), n, r, p, dklen);
let seed = this.extractSeed(derivedKey, json);
return EthAccount.fromMew(seed);
} else if (json.crypto.kdf === 'pbkdf2') {
kdfparams = json.crypto.kdfparams;
if (!unsortedEquals(Object.keys(kdfparams), ['c', 'dklen', 'prf', 'salt'])) { throw new Error('File is malformatted'); }
if (!isHex(kdfparams.salt)) { throw new Error('Not a supported param: kdfparams.salt'); }
if (kdfparams.prf !== 'hmac-sha256') { throw new Error('Unsupported parameters to PBKDF2'); }
if (!objHasKeys(kdfparams, ['c', 'dklen'])) { throw new Error('Not a supported param: kdfparams'); }
if (!['c', 'dklen'].every(i => isNumber(kdfparams[i]))) { throw new Error('Not a supported param: c and dklen must be numbers'); }

let { salt, c, dklen } = kdfparams;
let derivedKey = WalletCrypto.pbkdf2(Buffer.from(password), Buffer.from(salt, 'hex'), c, dklen, 'sha256');
let seed = this.extractSeed(derivedKey, json);
return EthAccount.fromMew(seed);
} else {
throw new Error('Unsupported key derivation scheme');
}
}

/* end mew */
Expand Down
8 changes: 8 additions & 0 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,14 @@ Helpers.trace = (...args) => {
}
};

Helpers.unsortedEquals = (arrA, arrB) => {
var arrAsorted = arrA.sort();
var arrBsorted = arrB.sort();
if (arrAsorted.length !== arrBsorted.length) { return false; }
for (var i = 0; i < arrAsorted.length; i++) { if (arrAsorted[i] !== arrBsorted[i]) { return false; } }
return true;
};

Helpers.bitcoincash = {
messagePrefix: '\u0018Bitcoin Signed Message:\n',
bip32: {
Expand Down
65 changes: 31 additions & 34 deletions src/import-export.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,63 +85,60 @@ var ImportExport = new function () {
if (!isECMult) {
var addresshash = Buffer(hex.slice(3, 7));

WalletCrypto.scrypt(passphrase, addresshash, 16384, 8, 8, 64, function (derivedBytes) {
var k = derivedBytes.slice(32, 32 + 32);
var derivedBytes = WalletCrypto.scrypt(passphrase, addresshash, 16384, 8, 8, 64);
let k = derivedBytes.slice(32, 32 + 32);

var decryptedBytes = WalletCrypto.AES.decrypt(Buffer(hex.slice(7, 7 + 32)), k, null, AESopts);
for (var x = 0; x < 32; x++) { decryptedBytes[x] ^= derivedBytes[x]; }
var decryptedBytes = WalletCrypto.AES.decrypt(Buffer(hex.slice(7, 7 + 32)), k, null, AESopts);
for (var x = 0; x < 32; x++) { decryptedBytes[x] ^= derivedBytes[x]; }

decrypted = BigInteger.fromBuffer(decryptedBytes);
decrypted = BigInteger.fromBuffer(decryptedBytes);

verifyHashAndReturn();
});
verifyHashAndReturn();
} else {
var ownerentropy = hex.slice(7, 7 + 8);
var ownersalt = Buffer(!hasLotSeq ? ownerentropy : ownerentropy.slice(0, 4));

WalletCrypto.scrypt(passphrase, ownersalt, 16384, 8, 8, 32, function (prefactorA) {
var passfactor;
var prefactorA = WalletCrypto.scrypt(passphrase, ownersalt, 16384, 8, 8, 32);
var passfactor;

if (!hasLotSeq) {
passfactor = prefactorA;
} else {
var prefactorB = Buffer.concat([prefactorA, Buffer(ownerentropy)]);
passfactor = hash256(prefactorB);
}
if (!hasLotSeq) {
passfactor = prefactorA;
} else {
var prefactorB = Buffer.concat([prefactorA, Buffer(ownerentropy)]);
passfactor = hash256(prefactorB);
}

var kp = new Bitcoin.ECPair(BigInteger.fromBuffer(passfactor), null, {network: constants.getNetwork()});
var kp = new Bitcoin.ECPair(BigInteger.fromBuffer(passfactor), null, {network: constants.getNetwork()});

var passpoint = kp.getPublicKeyBuffer();
var passpoint = kp.getPublicKeyBuffer();

var encryptedpart2 = Buffer(hex.slice(23, 23 + 16));
var encryptedpart2 = Buffer(hex.slice(23, 23 + 16));

var addresshashplusownerentropy = Buffer(hex.slice(3, 3 + 12));
var addresshashplusownerentropy = Buffer(hex.slice(3, 3 + 12));

WalletCrypto.scrypt(passpoint, addresshashplusownerentropy, 1024, 1, 1, 64, function (derived) {
var k = derived.slice(32);
var derived = WalletCrypto.scrypt(passpoint, addresshashplusownerentropy, 1024, 1, 1, 64);
let k = derived.slice(32);

var unencryptedpart2Bytes = WalletCrypto.AES.decrypt(encryptedpart2, k, null, AESopts);
var unencryptedpart2Bytes = WalletCrypto.AES.decrypt(encryptedpart2, k, null, AESopts);

for (var i = 0; i < 16; i++) { unencryptedpart2Bytes[i] ^= derived[i + 16]; }
for (var i = 0; i < 16; i++) { unencryptedpart2Bytes[i] ^= derived[i + 16]; }

var encryptedpart1 = Buffer.concat([Buffer(hex.slice(15, 15 + 8)), Buffer(unencryptedpart2Bytes.slice(0, 0 + 8))]);
var encryptedpart1 = Buffer.concat([Buffer(hex.slice(15, 15 + 8)), Buffer(unencryptedpart2Bytes.slice(0, 0 + 8))]);

var unencryptedpart1Bytes = WalletCrypto.AES.decrypt(encryptedpart1, k, null, AESopts);
var unencryptedpart1Bytes = WalletCrypto.AES.decrypt(encryptedpart1, k, null, AESopts);

for (var ii = 0; ii < 16; ii++) { unencryptedpart1Bytes[ii] ^= derived[ii]; }
for (var ii = 0; ii < 16; ii++) { unencryptedpart1Bytes[ii] ^= derived[ii]; }

var seedb = Buffer.concat([Buffer(unencryptedpart1Bytes.slice(0, 0 + 16)), Buffer(unencryptedpart2Bytes.slice(8, 8 + 8))]);
var seedb = Buffer.concat([Buffer(unencryptedpart1Bytes.slice(0, 0 + 16)), Buffer(unencryptedpart2Bytes.slice(8, 8 + 8))]);

var factorb = hash256(seedb);
var factorb = hash256(seedb);

// secp256k1: N
var N = BigInteger.fromHex('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
// secp256k1: N
var N = BigInteger.fromHex('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');

decrypted = BigInteger.fromBuffer(passfactor).multiply(BigInteger.fromBuffer(factorb)).remainder(N);
decrypted = BigInteger.fromBuffer(passfactor).multiply(BigInteger.fromBuffer(factorb)).remainder(N);

verifyHashAndReturn();
});
});
verifyHashAndReturn();
}
};
};
Expand Down
12 changes: 10 additions & 2 deletions src/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,17 @@ Metadata.extractResponse = curry((encKey, res) => {
if (res === null) {
return res;
} else {
let parseOrLog = (str) => {
try {
return JSON.parse(str);
} catch (e) {
console.log('Unable to parse metadata contents: ' + str);
throw e;
}
};
return encKey
? compose(JSON.parse, M.decrypt(encKey), prop('payload'))(res)
: compose(JSON.parse, M.BufferToString, M.B64ToBuffer, prop('payload'))(res);
? compose(parseOrLog, M.decrypt(encKey), prop('payload'))(res)
: compose(parseOrLog, M.BufferToString, M.B64ToBuffer, prop('payload'))(res);
}
});

Expand Down
Loading

1 comment on commit 6edace4

@Artic2019
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Запустить

Please sign in to comment.