diff --git a/lib/bip38.js b/lib/bip38.js index e240f4f..944b335 100644 --- a/lib/bip38.js +++ b/lib/bip38.js @@ -1,4 +1,3 @@ -var AES = require('aes') var assert = require('assert') var crypto = require('crypto') var cs = require('coinstring') @@ -9,6 +8,23 @@ var curve = ecurve.getCurveByName('secp256k1') var BigInteger = require('bigi') +function bufferXOR(buf1, buf2) { + assert.equal(buf1.length, buf2.length) + + var out = new Buffer(buf1.length) + for (var i = 0; i < buf1.length; i++) { + out[i] = buf1[i] ^ buf2[i] + } + + return out +} + +// SHA256(SHA256(buffer)) +function sha256x2(buffer) { + buffer = crypto.createHash('sha256').update(buffer).digest() + return crypto.createHash('sha256').update(buffer).digest() +} + function Bip38(versions) { if (!(this instanceof Bip38)) return new Bip38() @@ -40,21 +56,21 @@ Bip38.prototype.encryptRaw = function(buffer, compressed, passphrase, saltAddres var derivedHalf1 = scryptBuf.slice(0, 32) var derivedHalf2 = scryptBuf.slice(32, 64) - var aes = createAES(derivedHalf2) - var encryptFn = aes.encrypt.bind(aes) + var xorBuf = bufferXOR(buffer, derivedHalf1) + var cipher = crypto.createCipheriv('aes-256-ecb', derivedHalf2, new Buffer(0)) + cipher.setAutoPadding(false) + cipher.end(xorBuf) - var xorBuf = BufferXOR(buffer, derivedHalf1) - var encryptedHalf1 = callAES(xorBuf.slice(0, 16), encryptFn) - var encryptedHalf2 = callAES(xorBuf.slice(16, 32), encryptFn) + var cipherText = cipher.read() - // 0x01 + 0x42 + flagByte + salt + encryptedHalf1 + encryptedHalf2 + // 0x01 + 0x42 + flagByte + salt + cipherText var flagByte = compressed ? 0xe0 : 0xc0 var prefix = new Buffer(3) prefix.writeUInt8(0x01, 0) prefix.writeUInt8(0x42, 1) prefix.writeUInt8(flagByte, 2) - return Buffer.concat([prefix, salt, encryptedHalf1, encryptedHalf2]) + return Buffer.concat([prefix, salt, cipherText]) } Bip38.prototype.encrypt = function(wif, passphrase, saltAddress) { @@ -103,20 +119,16 @@ Bip38.prototype.decryptRaw = function(encData, passphrase) { var derivedHalf1 = scryptBuf.slice(0, 32) var derivedHalf2 = scryptBuf.slice(32, 64) - var aes = createAES(derivedHalf2) - var decryptFn = aes.decrypt.bind(aes) - var privKeyBuf = encData.slice(7, 7 + 32) - var decryptedHalf1 = callAES(privKeyBuf.slice(0, 16), decryptFn) - var decryptedHalf2 = callAES(privKeyBuf.slice(16, 32), decryptFn) - var dec = Buffer.concat([decryptedHalf1, decryptedHalf2]) + var decipher = crypto.createDecipheriv('aes-256-ecb', derivedHalf2, new Buffer(0)) + decipher.setAutoPadding(false) + decipher.end(privKeyBuf) - for (var x = 0; x < 32; x++) { - dec[x] ^= derivedHalf1[x] - } + var plainText = decipher.read() + var privateKey = bufferXOR(plainText, derivedHalf1) return { - privateKey: dec, + privateKey: privateKey, compressed: compressed } } @@ -185,15 +197,20 @@ Bip38.prototype.decryptECMult = function(encData, passphrase) { var derivedHalf1 = seedBPass.slice(0,32) var derivedHalf2 = seedBPass.slice(32,64) - var aes = createAES(derivedHalf2) - var decryptFn = aes.decrypt.bind(aes) - - var tmp = BufferXOR(callAES(encryptedPart2, decryptFn), derivedHalf1.slice(16,32)) - encryptedPart1 = Buffer.concat([encryptedPart1, tmp.slice(0, 8)], 16); // Append last 8 bytes + var decipher = crypto.createDecipheriv('aes-256-ecb', derivedHalf2, new Buffer(0)) + decipher.setAutoPadding(false) + decipher.end(encryptedPart2) + var decryptedPart2 = decipher.read() + var tmp = bufferXOR(decryptedPart2, derivedHalf1.slice(16, 32)) var seedBPart2 = tmp.slice(8, 16) - var tmp2 = callAES(encryptedPart1, decryptFn) - var seedBPart1 = BufferXOR(tmp2, derivedHalf1.slice(0,16)) + + var decipher2 = crypto.createDecipheriv('aes-256-ecb', derivedHalf2, new Buffer(0)) + decipher2.setAutoPadding(false) + decipher2.write(encryptedPart1) // first 8 bytes + decipher2.end(tmp.slice(0, 8)) // last 8 bytes + + var seedBPart1 = bufferXOR(decipher2.read(), derivedHalf1.slice(0, 16)) var seedB = Buffer.concat([seedBPart1, seedBPart2], 24) var factorB = sha256x2(seedB) @@ -206,50 +223,4 @@ Bip38.prototype.decryptECMult = function(encData, passphrase) { } } -function BufferXOR(buf1, buf2) { - assert.equal(buf1.length, buf2.length) - - var out = new Buffer(buf1.length) - for (var i = 0; i < buf1.length; i++) { - out[i] = buf1[i] ^ buf2[i] - } - - return out -} - -//convert 256 bit buffer to SJCL AES (requires big endian) -function createAES(keyBuffer) { - assert.equal(keyBuffer.length, 32, 'AES key must be 256 bits') - - var aesKey = [] - - for (var i = 0; i < 8; ++i) { - aesKey.push(keyBuffer.readUInt32BE(i*4)) - } - - return new AES(aesKey) -} - -function callAES(dataBuffer, fn) { - var part = [] - for (var i = 0; i < 4; ++i) { - part.push(dataBuffer.readUInt32BE(i * 4)) - } - - var encryptedData = fn(part) - var encryptedDataBuf = new Buffer(16) - - for (var i = 0; i < encryptedData.length; ++i) { - encryptedDataBuf.writeUInt32BE(encryptedData[i], i * 4) - } - - return encryptedDataBuf -} - -// SHA256(SHA256(buffer)) -function sha256x2(buffer) { - buffer = crypto.createHash('sha256').update(buffer).digest() - return crypto.createHash('sha256').update(buffer).digest() -} - module.exports = Bip38 diff --git a/test/bip38.test.js b/test/bip38.test.js index decf26c..2896a73 100644 --- a/test/bip38.test.js +++ b/test/bip38.test.js @@ -4,6 +4,8 @@ var Bip38 = require('../') var fixtures = require('./fixtures') describe('bip38', function() { + this.timeout(70000) + var bip38 beforeEach(function() { bip38 = new Bip38()