aion-gql-webcomponents
Version:
AION Webcomponents using AION GraphQL
146 lines (145 loc) • 6.24 kB
JavaScript
import { Buffer } from "buffer";
import rlp from "aion-rlp";
import { CryptoUtil } from "../util/CryptoUtil";
import nacl from "tweetnacl";
import scrypt from "scrypt-js";
import aesjs from "aes-js";
import { TransactionUtil } from "../util/TransactionUtil";
export default class KeystoreWalletProvider {
constructor(keystore, password) {
this.keystore = keystore;
this.password = password;
}
init(keystore, password) {
this.keystore = keystore;
this.password = password;
this.privateKey = null;
this.publicKey = null;
this.address = null;
}
async unlock(progressCallback) {
try {
if (!this.password) {
throw new Error('No password given.');
}
let ksv3 = this.fromRlp(this.keystore);
console.log(ksv3);
return this._decrypt(ksv3, this.password, progressCallback);
}
catch (error) {
console.log(error);
throw error;
}
}
async sign(transaction) {
let mainThis = this;
if (!mainThis.privateKey) {
throw new Error("Can not sign a transaction with null privatekey");
}
let encodedSignedTxn = TransactionUtil.signTransaction(transaction, mainThis.privateKey);
return encodedSignedTxn;
}
fromRlp(keystore) {
let hexContent = Buffer.from(keystore, 'hex');
let Ksv3 = rlp.decode(hexContent);
const Crypto = rlp.decode(Ksv3[3]);
const Cipherparams = rlp.decode(Crypto[4]);
const Kdfparams = rlp.decode(Crypto[5]);
let ksv3Json = {
id: Ksv3[0].toString('utf8'),
version: parseInt(Ksv3[1].toString('hex'), 16),
address: Ksv3[2].toString('utf8'),
crypto: {
cipher: Crypto[0].toString('utf8'),
ciphertext: Crypto[1].toString('utf8'),
kdf: Crypto[2].toString('utf8'),
mac: Crypto[3].toString('utf8'),
cipherparams: {
iv: Cipherparams[0].toString('utf8')
},
kdfparams: {
dklen: parseInt(Kdfparams[1].toString('hex'), 16),
n: parseInt(Kdfparams[2].toString('hex'), 16),
p: parseInt(Kdfparams[3].toString('hex'), 16),
r: parseInt(Kdfparams[4].toString('hex'), 16),
salt: Kdfparams[5].toString('utf8')
}
}
};
return ksv3Json;
}
async _decrypt(v3Keystore, password, progressCallback) {
if (!password) {
throw new Error('No password given.');
}
let json = v3Keystore;
if (json.version !== 3) {
throw new Error('Not a valid V3 wallet');
}
let mainThis = this;
return new Promise(function (resolve, reject) {
let kdfparams;
if (json.crypto.kdf === 'scrypt') {
kdfparams = json.crypto.kdfparams;
scrypt(new Buffer(password), new Buffer(kdfparams.salt, 'hex'), kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen, function (error, _progress, key) {
if (error) {
console.log("Error: " + error);
reject(error);
}
else if (key) {
console.log("Start unlocking.....");
try {
const ciphertext = new Buffer(json.crypto.ciphertext, 'hex');
let buffKey = new Buffer(key);
let mac = CryptoUtil.uia2hex(CryptoUtil.blake2b256(Buffer.concat([buffKey.slice(16, 32), ciphertext])));
if (!json.crypto.mac.startsWith("0x"))
mac = mac.substring(2);
if (mac !== json.crypto.mac) {
throw new Error('Key derivation failed - possibly wrong password');
}
if (json.crypto.cipher !== 'aes-128-ctr')
throw new Error("Cipher not supported yet : " + json.crypto.cipher);
const aesCbc = new aesjs.ModeOfOperation.ctr(buffKey.slice(0, 16), new Buffer(json.crypto.cipherparams.iv, 'hex'));
const seed = aesCbc.decrypt(ciphertext);
let keyPair = mainThis._createKeyPair(seed);
mainThis.address = CryptoUtil.createA0Address(keyPair._publicKey);
mainThis.privateKey = CryptoUtil.uia2hex(keyPair._privateKey, true);
mainThis.publicKey = CryptoUtil.uia2hex(keyPair._publicKey);
resolve([mainThis.address, mainThis.publicKey]);
}
catch (error) {
console.error(error);
reject(error);
}
}
else {
if (progressCallback)
progressCallback(_progress * 100);
}
});
}
else if (json.crypto.kdf === 'pbkdf2') {
let error = new Error('pbkdf2 is unsupported by AION keystore format');
console.error(error);
reject(error);
}
else {
let error = new Error('Unsupported key derivation scheme');
console.error(error);
reject(error);
}
});
}
_createKeyPair(privateKey) {
let kp;
let keyPair;
if (privateKey !== undefined) {
kp = nacl.sign.keyPair.fromSecretKey(privateKey);
keyPair = {
_privateKey: Buffer.from(kp.secretKey),
_publicKey: Buffer.from(kp.publicKey)
};
return keyPair;
}
}
}