Skip to content

Commit

Permalink
add nep2 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Ejhfast committed Aug 27, 2017
1 parent 305dab9 commit 120b367
Showing 1 changed file with 87 additions and 0 deletions.
87 changes: 87 additions & 0 deletions src/nep2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// this code draws heavily from functions written originally by snowypowers

import bs58check from 'bs58check';
import wif from 'wif';
import { SHA256, AES, enc } from 'crypto-js'
import C from 'crypto-js'
import scrypt from 'js-scrypt'
import { getAccountsFromWIFKey, getAccountsFromPrivateKey, generatePrivateKey, getWIFFromPrivateKey } from './wallet';
import { ab2hexstring, hexXor } from './utils';

const NEP_HEADER = "0142"
const NEP_FLAG = "e0"

// specified by nep2, same as bip38
const scrypt_options = {
cost: 16384,
blockSize: 8,
parallel: 8,
size: 64
};

// generate new encrypted wif given passphrase
// (returns a promise)
export const generateEncryptedWif = (passphrase) => {
const newPrivateKey = generatePrivateKey();
const newWif = getWIFFromPrivateKey(newPrivateKey);
return encrypt_wif(newWif, passphrase).then((encWif) => {
const loadAccount = getAccountsFromWIFKey(newWif);
return {
wif: newWif,
address: loadAccount[0].address,
encryptedWif: encWif,
passphrase: passphrase
};
});
};

// encrypt wif using keyphrase under nep2 standard
// returns encrypted wif
const encrypt = (wifKey, keyphrase, progressCallback) => {
const address = getAccountsFromWIFKey(wifKey)[0].address
const privateKey = getAccountsFromWIFKey(wifKey)[0].privatekey
// SHA Salt (use the first 4 bytes)
const addressHash = SHA256(SHA256(enc.Latin1.parse(address))).toString().slice(0, 8)
// Scrypt
const derived = scrypt.hashSync(Buffer.from(keyphrase, 'utf8'), Buffer.from(addressHash, 'hex'), scrypt_options, progressCallback).toString('hex')
const derived1 = derived.slice(0, 64)
const derived2 = derived.slice(64)
//AES Encrypt
const xor = hexXor(privateKey, derived1)
const encrypted = AES.encrypt(enc.Hex.parse(xor), enc.Hex.parse(derived2), { mode: C.mode.ECB, padding: C.pad.NoPadding })
//Construct
const assembled = NEP_HEADER + NEP_FLAG + addressHash + encrypted.ciphertext.toString()
return bs58check.encode(Buffer.from(assembled, 'hex'))
};

// decrypt encrypted wif using keyphrase under nep2 standard
// returns wif
const decrypt = (encryptedKey, keyphrase, progressCallback) => {
const assembled = ab2hexstring(bs58check.decode(encryptedKey))
const addressHash = assembled.substr(6, 8)
const encrypted = assembled.substr(-64)
const derived = scrypt.hashSync(Buffer.from(keyphrase, 'utf8'), Buffer.from(addressHash, 'hex'), scrypt_options, progressCallback).toString('hex')
const derived1 = derived.slice(0, 64)
const derived2 = derived.slice(64)
const ciphertext = { ciphertext: enc.Hex.parse(encrypted), salt: "" }
const decrypted = AES.decrypt(ciphertext, enc.Hex.parse(derived2), { mode: C.mode.ECB, padding: C.pad.NoPadding })
const privateKey = hexXor(decrypted.toString(), derived1)
const address = getAccountsFromPrivateKey(privateKey)[0].address
const newAddressHash = SHA256(SHA256(enc.Latin1.parse(address))).toString().slice(0, 8)
if (addressHash !== newAddressHash) throw new Error("Wrong Password!")
return getWIFFromPrivateKey(Buffer.from(privateKey, 'hex'));
};

// helpers to wrap synchronous functions in promises

export const encrypt_wif = (wif, passphrase) => {
return (new Promise((success, reject) => {
success(encrypt(wif, passphrase));
}));
};

export const decrypt_wif = (encrypted_wif, passphrase) => {
return (new Promise((success, reject) => {
success(decrypt(encrypted_wif, passphrase));
}));
};

0 comments on commit 120b367

Please sign in to comment.