Added support for PBKD2 encrypted wallets (which Parity uses).
This commit is contained in:
parent
70f10cd23e
commit
91f9a47afa
@ -9,7 +9,7 @@ function prefixAddress(address) {
|
|||||||
if (address.substring(0, 2) !== '0x') {
|
if (address.substring(0, 2) !== '0x') {
|
||||||
address = '0x' + address;
|
address = '0x' + address;
|
||||||
}
|
}
|
||||||
return address;
|
return address.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -25,6 +25,15 @@ Output.push({
|
|||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var privateKeys = {
|
||||||
|
'0x0b88d4b324ec24c8c078551e6e5075547157e5b6': '0xd4375d2a931db84ea8825b69a3128913597744d9236cacec675cc18e1bda4446',
|
||||||
|
'0x2e326fa404fc3661de4f4361776ed9bbabdc26e3': '0xcf367fc32bf789b3339c6664af4a12263e9db0e0eb70f247da1d1165e150c487',
|
||||||
|
'0x00a329c0648769a73afac7f9381e08fb43dbea72': '0x4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7',
|
||||||
|
'0x4a9cf99357f5789251a8d7fad5b86d0f31eeb938': '0xa016182717223d01f776149ec0b4a217d0e9930cad263f205427c6d3cd5560e7',
|
||||||
|
'0x88a5c2d9919e46f883eb62f7b8dd9d0cc45bc290': '0xf03e581353c794928373fb0893bc731aefc4c4e234e643f3a46998b03cd4d7c5',
|
||||||
|
'0x17c5185167401ed00cf5f5b2fc97d9bbfdb7d025': '0x4242424242424242424242424242424242424242424242424242424242424242',
|
||||||
|
}
|
||||||
|
|
||||||
var walletPath = path.join(__dirname, 'test-wallets');
|
var walletPath = path.join(__dirname, 'test-wallets');
|
||||||
fs.readdirSync(walletPath).forEach(function(filename) {
|
fs.readdirSync(walletPath).forEach(function(filename) {
|
||||||
var data = require(path.join(walletPath, filename));
|
var data = require(path.join(walletPath, filename));
|
||||||
@ -33,7 +42,7 @@ fs.readdirSync(walletPath).forEach(function(filename) {
|
|||||||
var password = filename.substring(0, filename.length - 5).split('-');
|
var password = filename.substring(0, filename.length - 5).split('-');
|
||||||
password = password[password.length - 1];
|
password = password[password.length - 1];
|
||||||
|
|
||||||
if (password === 'life') { password = 'foobar42'; }
|
if (password === 'life') { password = 'foo'; }
|
||||||
|
|
||||||
if (data.ethaddr) {
|
if (data.ethaddr) {
|
||||||
Output.push({
|
Output.push({
|
||||||
@ -41,7 +50,7 @@ fs.readdirSync(walletPath).forEach(function(filename) {
|
|||||||
address: prefixAddress(data.ethaddr),
|
address: prefixAddress(data.ethaddr),
|
||||||
json: JSON.stringify(data),
|
json: JSON.stringify(data),
|
||||||
password: password,
|
password: password,
|
||||||
privateKey: '',
|
privateKey: privateKeys[prefixAddress(data.ethaddr)],
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -50,7 +59,7 @@ fs.readdirSync(walletPath).forEach(function(filename) {
|
|||||||
address: prefixAddress(data.address),
|
address: prefixAddress(data.address),
|
||||||
json: JSON.stringify(data),
|
json: JSON.stringify(data),
|
||||||
password: password,
|
password: password,
|
||||||
privateKey: '',
|
privateKey: privateKeys[prefixAddress(data.address)],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
1
tests/make-tests/test-wallets/wallet-parity-.json
Normal file
1
tests/make-tests/test-wallets/wallet-parity-.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"id":"a9b7570b-fd36-8a32-f4ea-26080a941143","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"1de99c07c6592eaac911ae9cf07db88d"},"ciphertext":"951ca34a268314c0cde0409566bcac4d4907dc87f34764fe8542786415cb5726","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"7d2ebe49878f15e031bcdf9f0be47e7d49542252145850b7862a08a0c209a0e9"},"mac":"dc595787a3d08d98e2d808a981b8e9566d8ed1087c0257481ffc1196e13d8c0a"},"address":"00a329c0648769a73afac7f9381e08fb43dbea72","name":"Blank","meta":"{\"passwordHint\":\"\",\"timestamp\":1495227675346}"}
|
@ -2,7 +2,7 @@
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
var utils = require('ethers-utils');
|
var utils = require('../../utils');
|
||||||
|
|
||||||
function randomBytes(seed, lower, upper) {
|
function randomBytes(seed, lower, upper) {
|
||||||
if (!upper) { upper = lower; }
|
if (!upper) { upper = lower; }
|
||||||
|
3
tests/tests/keep.txt
Normal file
3
tests/tests/keep.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This file just ensures this directory exists.
|
||||||
|
|
||||||
|
Run the files in make-tests to create the individual test cases.
|
@ -4,6 +4,8 @@ var aes = require('aes-js');
|
|||||||
var scrypt = require('scrypt-js');
|
var scrypt = require('scrypt-js');
|
||||||
var uuid = require('uuid');
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var hmac = require('ethers-utils/hmac');
|
||||||
|
var pbkdf2 = require('ethers-utils/pbkdf2');
|
||||||
var utils = require('ethers-utils');
|
var utils = require('ethers-utils');
|
||||||
|
|
||||||
var SigningKey = require('./signing-key.js');
|
var SigningKey = require('./signing-key.js');
|
||||||
@ -69,6 +71,7 @@ utils.defineProperty(secretStorage, 'isValidWallet', function(json) {
|
|||||||
if (!data.version || parseInt(data.version) !== data.version || parseInt(data.version) !== 3) {
|
if (!data.version || parseInt(data.version) !== data.version || parseInt(data.version) !== 3) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: Put more checks to make sure it has kdf, iv and all that good stuff
|
// @TODO: Put more checks to make sure it has kdf, iv and all that good stuff
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -89,7 +92,7 @@ utils.defineProperty(secretStorage, 'decryptCrowdsale', function(json, password)
|
|||||||
throw new Error('invalid encseed');
|
throw new Error('invalid encseed');
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = utils.pbkdf2(password, password, 2000, 32, utils.hmac.createSha256Hmac).slice(0, 16);
|
var key = pbkdf2(password, password, 2000, 32, hmac.createSha256Hmac).slice(0, 16);
|
||||||
|
|
||||||
var iv = encseed.slice(0, 16);
|
var iv = encseed.slice(0, 16);
|
||||||
var encryptedSeed = encseed.slice(16);
|
var encryptedSeed = encseed.slice(16);
|
||||||
@ -140,72 +143,112 @@ utils.defineProperty(secretStorage, 'decrypt', function(json, password, progress
|
|||||||
return utils.keccak256(utils.concat([derivedHalf, ciphertext]));
|
return utils.keccak256(utils.concat([derivedHalf, ciphertext]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
var getSigningKey = function(key) {
|
||||||
var kdf = searchPath(data, 'crypto/kdf');
|
var ciphertext = arrayify(searchPath(data, 'crypto/ciphertext'));
|
||||||
if (kdf && typeof(kdf) === 'string' && kdf.toLowerCase() === 'scrypt') {
|
|
||||||
var salt = arrayify(searchPath(data, 'crypto/kdfparams/salt'), 'crypto/kdfparams/salt');
|
|
||||||
var N = parseInt(searchPath(data, 'crypto/kdfparams/n'));
|
|
||||||
var r = parseInt(searchPath(data, 'crypto/kdfparams/r'));
|
|
||||||
var p = parseInt(searchPath(data, 'crypto/kdfparams/p'));
|
|
||||||
if (!N || !r || !p) {
|
|
||||||
reject(new Error('unsupported key-derivation function parameters'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure N is a power of 2
|
var computedMAC = utils.hexlify(computeMAC(key.slice(16, 32), ciphertext)).substring(2);
|
||||||
if ((N & (N - 1)) !== 0) {
|
if (computedMAC !== searchPath(data, 'crypto/mac').toLowerCase()) {
|
||||||
reject(new Error('unsupported key-derivation function parameter value for N'));
|
reject(new Error('invalid password'));
|
||||||
return;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
var dkLen = parseInt(searchPath(data, 'crypto/kdfparams/dklen'));
|
|
||||||
if (dkLen !== 32) {
|
|
||||||
reject( new Error('unsupported key-derivation derived-key length'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrypt(password, salt, N, r, p, dkLen, function(error, progress, key) {
|
|
||||||
if (error) {
|
|
||||||
error.progress = progress;
|
|
||||||
reject(error);
|
|
||||||
|
|
||||||
} else if (key) {
|
|
||||||
key = arrayify(key);
|
|
||||||
|
|
||||||
var ciphertext = arrayify(searchPath(data, 'crypto/ciphertext'));
|
|
||||||
|
|
||||||
var computedMAC = utils.hexlify(computeMAC(key.slice(16, 32), ciphertext)).substring(2);
|
|
||||||
if (computedMAC !== searchPath(data, 'crypto/mac').toLowerCase()) {
|
|
||||||
reject(new Error('invalid password'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var privateKey = decrypt(key.slice(0, 16), ciphertext);
|
|
||||||
|
|
||||||
if (!privateKey) {
|
|
||||||
reject(new Error('unsupported cipher'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var signingKey = new SigningKey(privateKey);
|
|
||||||
if (signingKey.address !== utils.getAddress(data.address)) {
|
|
||||||
reject(new Error('address mismatch'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progressCallback) { progressCallback(1); }
|
|
||||||
resolve(signingKey);
|
|
||||||
|
|
||||||
} else if (progressCallback) {
|
|
||||||
return progressCallback(progress);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// @TOOD: Support pbkdf2 kdf
|
|
||||||
reject(new Error('unsupported key-derivation function'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var privateKey = decrypt(key.slice(0, 16), ciphertext);
|
||||||
|
|
||||||
|
if (!privateKey) {
|
||||||
|
reject(new Error('unsupported cipher'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var signingKey = new SigningKey(privateKey);
|
||||||
|
if (signingKey.address !== utils.getAddress(data.address)) {
|
||||||
|
reject(new Error('address mismatch'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return signingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var kdf = searchPath(data, 'crypto/kdf');
|
||||||
|
if (kdf && typeof(kdf) === 'string') {
|
||||||
|
if (kdf.toLowerCase() === 'scrypt') {
|
||||||
|
var salt = arrayify(searchPath(data, 'crypto/kdfparams/salt'), 'crypto/kdfparams/salt');
|
||||||
|
var N = parseInt(searchPath(data, 'crypto/kdfparams/n'));
|
||||||
|
var r = parseInt(searchPath(data, 'crypto/kdfparams/r'));
|
||||||
|
var p = parseInt(searchPath(data, 'crypto/kdfparams/p'));
|
||||||
|
if (!N || !r || !p) {
|
||||||
|
reject(new Error('unsupported key-derivation function parameters'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure N is a power of 2
|
||||||
|
if ((N & (N - 1)) !== 0) {
|
||||||
|
reject(new Error('unsupported key-derivation function parameter value for N'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dkLen = parseInt(searchPath(data, 'crypto/kdfparams/dklen'));
|
||||||
|
if (dkLen !== 32) {
|
||||||
|
reject( new Error('unsupported key-derivation derived-key length'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrypt(password, salt, N, r, p, dkLen, function(error, progress, key) {
|
||||||
|
if (error) {
|
||||||
|
error.progress = progress;
|
||||||
|
reject(error);
|
||||||
|
|
||||||
|
} else if (key) {
|
||||||
|
key = arrayify(key);
|
||||||
|
|
||||||
|
var signingKey = getSigningKey(key);
|
||||||
|
if (!signingKey) { return; }
|
||||||
|
|
||||||
|
if (progressCallback) { progressCallback(1); }
|
||||||
|
resolve(signingKey);
|
||||||
|
|
||||||
|
} else if (progressCallback) {
|
||||||
|
return progressCallback(progress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (kdf.toLowerCase() === 'pbkdf2') {
|
||||||
|
var salt = arrayify(searchPath(data, 'crypto/kdfparams/salt'), 'crypto/kdfparams/salt');
|
||||||
|
|
||||||
|
var prfFunc = null;
|
||||||
|
var prf = searchPath(data, 'crypto/kdfparams/prf');
|
||||||
|
if (prf === 'hmac-sha256') {
|
||||||
|
prfFunc = hmac.createSha256Hmac;
|
||||||
|
} else if (prf === 'hmac-sha512') {
|
||||||
|
prfFunc = hmac.createSha512Hmac;
|
||||||
|
} else {
|
||||||
|
reject(new Error('unsupported prf'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var c = parseInt(searchPath(data, 'crypto/kdfparams/c'));
|
||||||
|
|
||||||
|
var dkLen = parseInt(searchPath(data, 'crypto/kdfparams/dklen'));
|
||||||
|
if (dkLen !== 32) {
|
||||||
|
reject( new Error('unsupported key-derivation derived-key length'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = pbkdf2(password, salt, c, dkLen, prfFunc);
|
||||||
|
|
||||||
|
var signingKey = getSigningKey(key);
|
||||||
|
if (!signingKey) { return; }
|
||||||
|
|
||||||
|
resolve(signingKey);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
reject(new Error('unsupported key-derivation function'));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
reject(new Error('unsupported key-derivation function'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user