109 lines
3.1 KiB
JavaScript
109 lines
3.1 KiB
JavaScript
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||
|
// This file is part of Parity.
|
||
|
|
||
|
// Parity is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU General Public License as published by
|
||
|
// the Free Software Foundation, either version 3 of the License, or
|
||
|
// (at your option) any later version.
|
||
|
|
||
|
// Parity is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU General Public License for more details.
|
||
|
|
||
|
// You should have received a copy of the GNU General Public License
|
||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
import scrypt from 'scryptsy';
|
||
|
import Transaction from 'ethereumjs-tx';
|
||
|
import { pbkdf2Sync } from 'crypto';
|
||
|
import { createDecipheriv } from 'browserify-aes';
|
||
|
|
||
|
import { inHex } from '~/api/format/input';
|
||
|
import { sha3 } from '~/api/util/sha3';
|
||
|
|
||
|
// Adapted from https://github.com/kvhnuke/etherwallet/blob/mercury/app/scripts/myetherwallet.js
|
||
|
|
||
|
export class Signer {
|
||
|
static fromJson (json, password) {
|
||
|
return Signer
|
||
|
.getSeed(json, password)
|
||
|
.then((seed) => {
|
||
|
return new Signer(seed);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
static getSeed (json, password) {
|
||
|
try {
|
||
|
const seed = Signer.getSyncSeed(json, password);
|
||
|
|
||
|
return Promise.resolve(seed);
|
||
|
} catch (error) {
|
||
|
return Promise.reject(error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static getSyncSeed (json, password) {
|
||
|
if (json.version !== 3) {
|
||
|
throw new Error('Only V3 wallets are supported');
|
||
|
}
|
||
|
|
||
|
const { kdf } = json.crypto;
|
||
|
const kdfparams = json.crypto.kdfparams || {};
|
||
|
const pwd = Buffer.from(password);
|
||
|
const salt = Buffer.from(kdfparams.salt, 'hex');
|
||
|
let derivedKey;
|
||
|
|
||
|
if (kdf === 'scrypt') {
|
||
|
derivedKey = scrypt(pwd, salt, kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen);
|
||
|
} else if (kdf === 'pbkdf2') {
|
||
|
if (kdfparams.prf !== 'hmac-sha256') {
|
||
|
throw new Error('Unsupported parameters to PBKDF2');
|
||
|
}
|
||
|
|
||
|
derivedKey = pbkdf2Sync(pwd, salt, kdfparams.c, kdfparams.dklen, 'sha256');
|
||
|
} else {
|
||
|
throw new Error('Unsupported key derivation scheme');
|
||
|
}
|
||
|
|
||
|
const ciphertext = Buffer.from(json.crypto.ciphertext, 'hex');
|
||
|
const mac = sha3(Buffer.concat([derivedKey.slice(16, 32), ciphertext]));
|
||
|
|
||
|
if (mac !== inHex(json.crypto.mac)) {
|
||
|
throw new Error('Key derivation failed - possibly wrong password');
|
||
|
}
|
||
|
|
||
|
const decipher = createDecipheriv(
|
||
|
json.crypto.cipher,
|
||
|
derivedKey.slice(0, 16),
|
||
|
Buffer.from(json.crypto.cipherparams.iv, 'hex')
|
||
|
);
|
||
|
|
||
|
let seed = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
||
|
|
||
|
while (seed.length < 32) {
|
||
|
const nullBuff = Buffer.from([0x00]);
|
||
|
|
||
|
seed = Buffer.concat([nullBuff, seed]);
|
||
|
}
|
||
|
|
||
|
return seed;
|
||
|
}
|
||
|
|
||
|
constructor (seed) {
|
||
|
this.seed = seed;
|
||
|
}
|
||
|
|
||
|
signTransactionObject (tx) {
|
||
|
tx.sign(this.seed);
|
||
|
|
||
|
return tx;
|
||
|
}
|
||
|
|
||
|
signTransaction (transaction) {
|
||
|
const tx = new Transaction(transaction);
|
||
|
|
||
|
return inHex(this.signTransactionObject(tx).serialize().toString('hex'));
|
||
|
}
|
||
|
}
|