Skip to content

Commit

Permalink
Merge pull request #6 from cryptocoinjs/nodeaes
Browse files Browse the repository at this point in the history
Use Node crypto AES
  • Loading branch information
jprichardson committed Dec 17, 2014
2 parents 85d89bd + 2acdf9f commit 5986f7e
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 71 deletions.
113 changes: 42 additions & 71 deletions lib/bip38.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
var AES = require('aes')
var assert = require('assert')
var crypto = require('crypto')
var cs = require('coinstring')
Expand All @@ -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()

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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)

Expand All @@ -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
2 changes: 2 additions & 0 deletions test/bip38.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ var Bip38 = require('../')
var fixtures = require('./fixtures')

describe('bip38', function() {
this.timeout(70000)

var bip38
beforeEach(function() {
bip38 = new Bip38()
Expand Down

0 comments on commit 5986f7e

Please sign in to comment.