From 5f6aa10cbc6f18f8b50b14d01631838982258fb9 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 18 May 2016 22:15:00 -0700 Subject: [PATCH] refactor aes. --- lib/bcoin/aes.js | 492 ++++++++++++++++++++++++++++----------------- lib/bcoin/utils.js | 34 +++- 2 files changed, 331 insertions(+), 195 deletions(-) diff --git a/lib/bcoin/aes.js b/lib/bcoin/aes.js index 5c74030b..6c94b94d 100644 --- a/lib/bcoin/aes.js +++ b/lib/bcoin/aes.js @@ -8,51 +8,105 @@ * Entered into the public domain by Vincent Rijmen. */ -var aes = exports; +var assert = require('assert'); + +/** + * An AES object for encrypting and decrypting blocks. + * @exports AES + * @constructor + * @param {Buffer} key + * @param {Number} bits + * @param {Buffer?} iv + */ + +function AES(key, bits, iv) { + if (!(this instanceof AES)) + return new AES(key, bits, iv); + + this.userKey = key; + this.bits = bits; + this.iv = iv; + + if (this.bits === 128) + this.rounds = 10; + else if (this.bits === 192) + this.rounds = 12; + else if (this.bits === 256) + this.rounds = 14; + else + throw new Error('Bad key size.'); + + this.decryptKey = null; + this.encryptKey = null; +} + +/** + * Destroy the object and zero the keys. + */ + +AES.prototype.destroy = function destroy() { + var i; + + assert(this.userKey, 'Already destroyed.'); + + this.userKey.fill(0); + this.userKey = null; + + if (this.iv) { + this.iv.fill(0); + this.iv = null; + } + + if (this.decryptKey) { + for (i = 0; i < this.decryptKey.length; i++) + this.decryptKey[i] = 0; + this.decryptKey = null; + } + + if (this.encryptKey) { + for (i = 0; i < this.encryptKey.length; i++) + this.encryptKey[i] = 0; + this.encryptKey = null; + } +}; /** * Convert the user key into an encryption key. - * @param {Buffer} userKey - * @param {Number} bits * @returns {Object} key */ -aes.getEncryptKey = function getEncryptKey(userKey, bits) { - var key = { data: [], rounds: 0 }; - var i, kp, rk, tmp; +AES.prototype.getEncryptKey = function getEncryptKey() { + var i = 0; + var key, kp, tmp; - if (bits !== 128 && bits !== 192 && bits !== 256) - throw new Error('Bad key size.'); + assert(this.userKey, 'Cannot use AES once it is destroyed.'); - i = 0; + if (this.encryptKey) + return this.encryptKey; + + key = []; kp = 0; - rk = key.data; - if (bits === 128) - key.rounds = 10; - else if (bits === 192) - key.rounds = 12; - else - key.rounds = 14; + key[kp + 0] = readU32(this.userKey, 0); + key[kp + 1] = readU32(this.userKey, 4); + key[kp + 2] = readU32(this.userKey, 8); + key[kp + 3] = readU32(this.userKey, 12); - rk[kp + 0] = readU32(userKey, 0); - rk[kp + 1] = readU32(userKey, 4); - rk[kp + 2] = readU32(userKey, 8); - rk[kp + 3] = readU32(userKey, 12); + this.encryptKey = key; - if (bits === 128) { + if (this.bits === 128) { for (;;) { - tmp = rk[kp + 3]; + tmp = key[kp + 3]; - rk[kp + 4] = rk[kp + 0] + key[kp + 4] = key[kp + 0] ^ (TE2[(tmp >>> 16) & 0xff] & 0xff000000) ^ (TE3[(tmp >>> 8) & 0xff] & 0x00ff0000) ^ (TE0[(tmp >>> 0) & 0xff] & 0x0000ff00) ^ (TE1[(tmp >>> 24) & 0xff] & 0x000000ff) ^ RCON[i]; - rk[kp + 5] = rk[kp + 1] ^ rk[kp + 4]; - rk[kp + 6] = rk[kp + 2] ^ rk[kp + 5]; - rk[kp + 7] = rk[kp + 3] ^ rk[kp + 6]; + key[kp + 5] = key[kp + 1] ^ key[kp + 4]; + key[kp + 6] = key[kp + 2] ^ key[kp + 5]; + key[kp + 7] = key[kp + 3] ^ key[kp + 6]; if (++i === 10) return key; @@ -61,62 +115,62 @@ aes.getEncryptKey = function getEncryptKey(userKey, bits) { } } - rk[kp + 4] = readU32(userKey, 16); - rk[kp + 5] = readU32(userKey, 20); + key[kp + 4] = readU32(this.userKey, 16); + key[kp + 5] = readU32(this.userKey, 20); - if (bits === 192) { + if (this.bits === 192) { for (;;) { - tmp = rk[kp + 5]; + tmp = key[kp + 5]; - rk[kp + 6] = rk[kp + 0] + key[kp + 6] = key[kp + 0] ^ (TE2[(tmp >>> 16) & 0xff] & 0xff000000) ^ (TE3[(tmp >>> 8) & 0xff] & 0x00ff0000) ^ (TE0[(tmp >>> 0) & 0xff] & 0x0000ff00) ^ (TE1[(tmp >>> 24) & 0xff] & 0x000000ff) ^ RCON[i]; - rk[kp + 7] = rk[kp + 1] ^ rk[kp + 6]; - rk[kp + 8] = rk[kp + 2] ^ rk[kp + 7]; - rk[kp + 9] = rk[kp + 3] ^ rk[kp + 8]; + key[kp + 7] = key[kp + 1] ^ key[kp + 6]; + key[kp + 8] = key[kp + 2] ^ key[kp + 7]; + key[kp + 9] = key[kp + 3] ^ key[kp + 8]; if (++i === 8) return key; - rk[kp + 10] = rk[kp + 4] ^ rk[kp + 9]; - rk[kp + 11] = rk[kp + 5] ^ rk[kp + 10]; + key[kp + 10] = key[kp + 4] ^ key[kp + 9]; + key[kp + 11] = key[kp + 5] ^ key[kp + 10]; kp += 6; } } - rk[kp + 6] = readU32(userKey, 24); - rk[kp + 7] = readU32(userKey, 28); + key[kp + 6] = readU32(this.userKey, 24); + key[kp + 7] = readU32(this.userKey, 28); - if (bits === 256) { + if (this.bits === 256) { for (;;) { - tmp = rk[kp + 7]; + tmp = key[kp + 7]; - rk[kp + 8] = rk[kp + 0] + key[kp + 8] = key[kp + 0] ^ (TE2[(tmp >>> 16) & 0xff] & 0xff000000) ^ (TE3[(tmp >>> 8) & 0xff] & 0x00ff0000) ^ (TE0[(tmp >>> 0) & 0xff] & 0x0000ff00) ^ (TE1[(tmp >>> 24) & 0xff] & 0x000000ff) ^ RCON[i]; - rk[kp + 9] = rk[kp + 1] ^ rk[kp + 8]; - rk[kp + 10] = rk[kp + 2] ^ rk[kp + 9]; - rk[kp + 11] = rk[kp + 3] ^ rk[kp + 10]; + key[kp + 9] = key[kp + 1] ^ key[kp + 8]; + key[kp + 10] = key[kp + 2] ^ key[kp + 9]; + key[kp + 11] = key[kp + 3] ^ key[kp + 10]; if (++i === 7) return key; - tmp = rk[kp + 11]; + tmp = key[kp + 11]; - rk[kp + 12] = rk[kp + 4] + key[kp + 12] = key[kp + 4] ^ (TE2[(tmp >>> 24) & 0xff] & 0xff000000) ^ (TE3[(tmp >>> 16) & 0xff] & 0x00ff0000) ^ (TE0[(tmp >>> 8) & 0xff] & 0x0000ff00) ^ (TE1[(tmp >>> 0) & 0xff] & 0x000000ff); - rk[kp + 13] = rk[kp + 5] ^ rk[kp + 12]; - rk[kp + 14] = rk[kp + 6] ^ rk[kp + 13]; - rk[kp + 15] = rk[kp + 7] ^ rk[kp + 14]; + key[kp + 13] = key[kp + 5] ^ key[kp + 12]; + key[kp + 14] = key[kp + 6] ^ key[kp + 13]; + key[kp + 15] = key[kp + 7] ^ key[kp + 14]; kp += 8; } @@ -127,48 +181,62 @@ aes.getEncryptKey = function getEncryptKey(userKey, bits) { /** * Convert the user key into a decryption key. - * @param {Buffer} userKey - * @param {Number} bits * @returns {Object} key */ -aes.getDecryptKey = function getDecryptKey(userKey, bits) { - var i, j, kp, rk, key, tmp; +AES.prototype.getDecryptKey = function getDecryptKey() { + var i, j, kp, key, tmp; + + assert(this.userKey, 'Cannot use AES once it is destroyed.'); + + if (this.decryptKey) + return this.decryptKey; // First, start with an encryption schedule. - key = aes.getEncryptKey(userKey, bits); - + key = this.getEncryptKey().slice(); kp = 0; - rk = key.data; + + this.decryptKey = key; // Invert the order of the round keys. - for (i = 0, j = 4 * key.rounds; i < j; i += 4, j -= 4) { - tmp = rk[kp + i + 0]; rk[kp + i + 0] = rk[kp + j + 0]; rk[kp + j + 0] = tmp; - tmp = rk[kp + i + 1]; rk[kp + i + 1] = rk[kp + j + 1]; rk[kp + j + 1] = tmp; - tmp = rk[kp + i + 2]; rk[kp + i + 2] = rk[kp + j + 2]; rk[kp + j + 2] = tmp; - tmp = rk[kp + i + 3]; rk[kp + i + 3] = rk[kp + j + 3]; rk[kp + j + 3] = tmp; + for (i = 0, j = 4 * this.rounds; i < j; i += 4, j -= 4) { + tmp = key[kp + i + 0]; + key[kp + i + 0] = key[kp + j + 0]; + key[kp + j + 0] = tmp; + + tmp = key[kp + i + 1]; + key[kp + i + 1] = key[kp + j + 1]; + key[kp + j + 1] = tmp; + + tmp = key[kp + i + 2]; + key[kp + i + 2] = key[kp + j + 2]; + key[kp + j + 2] = tmp; + + tmp = key[kp + i + 3]; + key[kp + i + 3] = key[kp + j + 3]; + key[kp + j + 3] = tmp; } // Apply the inverse MixColumn transform to // all round keys but the first and the last. - for (i = 1; i < key.rounds; i++) { + for (i = 1; i < this.rounds; i++) { kp += 4; - rk[kp + 0] = TD0[TE1[(rk[kp + 0] >>> 24) & 0xff] & 0xff] - ^ TD1[TE1[(rk[kp + 0] >>> 16) & 0xff] & 0xff] - ^ TD2[TE1[(rk[kp + 0] >>> 8) & 0xff] & 0xff] - ^ TD3[TE1[(rk[kp + 0] >>> 0) & 0xff] & 0xff]; - rk[kp + 1] = TD0[TE1[(rk[kp + 1] >>> 24) & 0xff] & 0xff] - ^ TD1[TE1[(rk[kp + 1] >>> 16) & 0xff] & 0xff] - ^ TD2[TE1[(rk[kp + 1] >>> 8) & 0xff] & 0xff] - ^ TD3[TE1[(rk[kp + 1] >>> 0) & 0xff] & 0xff]; - rk[kp + 2] = TD0[TE1[(rk[kp + 2] >>> 24) & 0xff] & 0xff] - ^ TD1[TE1[(rk[kp + 2] >>> 16) & 0xff] & 0xff] - ^ TD2[TE1[(rk[kp + 2] >>> 8) & 0xff] & 0xff] - ^ TD3[TE1[(rk[kp + 2] >>> 0) & 0xff] & 0xff]; - rk[kp + 3] = TD0[TE1[(rk[kp + 3] >>> 24) & 0xff] & 0xff] - ^ TD1[TE1[(rk[kp + 3] >>> 16) & 0xff] & 0xff] - ^ TD2[TE1[(rk[kp + 3] >>> 8) & 0xff] & 0xff] - ^ TD3[TE1[(rk[kp + 3] >>> 0) & 0xff] & 0xff]; + key[kp + 0] = TD0[TE1[(key[kp + 0] >>> 24) & 0xff] & 0xff] + ^ TD1[TE1[(key[kp + 0] >>> 16) & 0xff] & 0xff] + ^ TD2[TE1[(key[kp + 0] >>> 8) & 0xff] & 0xff] + ^ TD3[TE1[(key[kp + 0] >>> 0) & 0xff] & 0xff]; + key[kp + 1] = TD0[TE1[(key[kp + 1] >>> 24) & 0xff] & 0xff] + ^ TD1[TE1[(key[kp + 1] >>> 16) & 0xff] & 0xff] + ^ TD2[TE1[(key[kp + 1] >>> 8) & 0xff] & 0xff] + ^ TD3[TE1[(key[kp + 1] >>> 0) & 0xff] & 0xff]; + key[kp + 2] = TD0[TE1[(key[kp + 2] >>> 24) & 0xff] & 0xff] + ^ TD1[TE1[(key[kp + 2] >>> 16) & 0xff] & 0xff] + ^ TD2[TE1[(key[kp + 2] >>> 8) & 0xff] & 0xff] + ^ TD3[TE1[(key[kp + 2] >>> 0) & 0xff] & 0xff]; + key[kp + 3] = TD0[TE1[(key[kp + 3] >>> 24) & 0xff] & 0xff] + ^ TD1[TE1[(key[kp + 3] >>> 16) & 0xff] & 0xff] + ^ TD2[TE1[(key[kp + 3] >>> 8) & 0xff] & 0xff] + ^ TD3[TE1[(key[kp + 3] >>> 0) & 0xff] & 0xff]; } return key; @@ -177,48 +245,48 @@ aes.getDecryptKey = function getDecryptKey(userKey, bits) { /** * Encrypt a 16 byte block of data. * @param {Buffer} input - * @param {Object} key * @returns {Buffer} */ -aes.encryptBlock = function encryptBlock(input, key) { - var output = new Buffer(16); - var kp, rk, r, s0, s1, s2, s3, t0, t1, t2, t3; +AES.prototype.encryptBlock = function encryptBlock(input) { + var output, kp, key, r, s0, s1, s2, s3, t0, t1, t2, t3; - rk = key.data; + assert(this.userKey, 'Cannot use AES once it is destroyed.'); + + key = this.getEncryptKey(); kp = 0; // Map byte array block to cipher // state and add initial round key. - s0 = readU32(input, 0) ^ rk[0]; - s1 = readU32(input, 4) ^ rk[1]; - s2 = readU32(input, 8) ^ rk[2]; - s3 = readU32(input, 12) ^ rk[3]; + s0 = readU32(input, 0) ^ key[0]; + s1 = readU32(input, 4) ^ key[1]; + s2 = readU32(input, 8) ^ key[2]; + s3 = readU32(input, 12) ^ key[3]; // Nr - 1 full rounds - r = key.rounds >>> 1; + r = this.rounds >>> 1; for (;;) { t0 = TE0[(s0 >>> 24) & 0xff] ^ TE1[(s1 >>> 16) & 0xff] ^ TE2[(s2 >>> 8) & 0xff] ^ TE3[(s3 >>> 0) & 0xff] - ^ rk[kp + 4]; + ^ key[kp + 4]; t1 = TE0[(s1 >>> 24) & 0xff] ^ TE1[(s2 >>> 16) & 0xff] ^ TE2[(s3 >>> 8) & 0xff] ^ TE3[(s0 >>> 0) & 0xff] - ^ rk[kp + 5]; + ^ key[kp + 5]; t2 = TE0[(s2 >>> 24) & 0xff] ^ TE1[(s3 >>> 16) & 0xff] ^ TE2[(s0 >>> 8) & 0xff] ^ TE3[(s1 >>> 0) & 0xff] - ^ rk[kp + 6]; + ^ key[kp + 6]; t3 = TE0[(s3 >>> 24) & 0xff] ^ TE1[(s0 >>> 16) & 0xff] ^ TE2[(s1 >>> 8) & 0xff] ^ TE3[(s2 >>> 0) & 0xff] - ^ rk[kp + 7]; + ^ key[kp + 7]; kp += 8; @@ -229,22 +297,22 @@ aes.encryptBlock = function encryptBlock(input, key) { ^ TE1[(t1 >>> 16) & 0xff] ^ TE2[(t2 >>> 8) & 0xff] ^ TE3[(t3 >>> 0) & 0xff] - ^ rk[kp + 0]; + ^ key[kp + 0]; s1 = TE0[(t1 >>> 24) & 0xff] ^ TE1[(t2 >>> 16) & 0xff] ^ TE2[(t3 >>> 8) & 0xff] ^ TE3[(t0 >>> 0) & 0xff] - ^ rk[kp + 1]; + ^ key[kp + 1]; s2 = TE0[(t2 >>> 24) & 0xff] ^ TE1[(t3 >>> 16) & 0xff] ^ TE2[(t0 >>> 8) & 0xff] ^ TE3[(t1 >>> 0) & 0xff] - ^ rk[kp + 2]; + ^ key[kp + 2]; s3 = TE0[(t3 >>> 24) & 0xff] ^ TE1[(t0 >>> 16) & 0xff] ^ TE2[(t1 >>> 8) & 0xff] ^ TE3[(t2 >>> 0) & 0xff] - ^ rk[kp + 3]; + ^ key[kp + 3]; } // Apply last round and map cipher @@ -253,23 +321,24 @@ aes.encryptBlock = function encryptBlock(input, key) { ^ (TE3[(t1 >>> 16) & 0xff] & 0x00ff0000) ^ (TE0[(t2 >>> 8) & 0xff] & 0x0000ff00) ^ (TE1[(t3 >>> 0) & 0xff] & 0x000000ff) - ^ rk[kp + 0]; + ^ key[kp + 0]; s1 = (TE2[(t1 >>> 24) & 0xff] & 0xff000000) ^ (TE3[(t2 >>> 16) & 0xff] & 0x00ff0000) ^ (TE0[(t3 >>> 8) & 0xff] & 0x0000ff00) ^ (TE1[(t0 >>> 0) & 0xff] & 0x000000ff) - ^ rk[kp + 1]; + ^ key[kp + 1]; s2 = (TE2[(t2 >>> 24) & 0xff] & 0xff000000) ^ (TE3[(t3 >>> 16) & 0xff] & 0x00ff0000) ^ (TE0[(t0 >>> 8) & 0xff] & 0x0000ff00) ^ (TE1[(t1 >>> 0) & 0xff] & 0x000000ff) - ^ rk[kp + 2]; + ^ key[kp + 2]; s3 = (TE2[(t3 >>> 24) & 0xff] & 0xff000000) ^ (TE3[(t0 >>> 16) & 0xff] & 0x00ff0000) ^ (TE0[(t1 >>> 8) & 0xff] & 0x0000ff00) ^ (TE1[(t2 >>> 0) & 0xff] & 0x000000ff) - ^ rk[kp + 3]; + ^ key[kp + 3]; + output = new Buffer(16); writeU32(output, s0, 0); writeU32(output, s1, 4); writeU32(output, s2, 8); @@ -281,48 +350,48 @@ aes.encryptBlock = function encryptBlock(input, key) { /** * Decrypt a 16 byte block of data. * @param {Buffer} input - * @param {Object} key * @returns {Buffer} */ -aes.decryptBlock = function decryptBlock(input, key) { - var output = new Buffer(16); - var kp, rk, r, s0, s1, s2, s3, t0, t1, t2, t3; +AES.prototype.decryptBlock = function decryptBlock(input) { + var output, kp, key, r, s0, s1, s2, s3, t0, t1, t2, t3; + assert(this.userKey, 'Cannot use AES once it is destroyed.'); + + key = this.getDecryptKey(); kp = 0; - rk = key.data; // Map byte array block to cipher // state and add initial round key. - s0 = readU32(input, 0) ^ rk[kp + 0]; - s1 = readU32(input, 4) ^ rk[kp + 1]; - s2 = readU32(input, 8) ^ rk[kp + 2]; - s3 = readU32(input, 12) ^ rk[kp + 3]; + s0 = readU32(input, 0) ^ key[kp + 0]; + s1 = readU32(input, 4) ^ key[kp + 1]; + s2 = readU32(input, 8) ^ key[kp + 2]; + s3 = readU32(input, 12) ^ key[kp + 3]; // Nr - 1 full rounds - r = key.rounds >>> 1; + r = this.rounds >>> 1; for (;;) { t0 = TD0[(s0 >>> 24) & 0xff] ^ TD1[(s3 >>> 16) & 0xff] ^ TD2[(s2 >>> 8) & 0xff] ^ TD3[(s1 >>> 0) & 0xff] - ^ rk[kp + 4]; + ^ key[kp + 4]; t1 = TD0[(s1 >>> 24) & 0xff] ^ TD1[(s0 >>> 16) & 0xff] ^ TD2[(s3 >>> 8) & 0xff] ^ TD3[(s2 >>> 0) & 0xff] - ^ rk[kp + 5]; + ^ key[kp + 5]; t2 = TD0[(s2 >>> 24) & 0xff] ^ TD1[(s1 >>> 16) & 0xff] ^ TD2[(s0 >>> 8) & 0xff] ^ TD3[(s3 >>> 0) & 0xff] - ^ rk[kp + 6]; + ^ key[kp + 6]; t3 = TD0[(s3 >>> 24) & 0xff] ^ TD1[(s2 >>> 16) & 0xff] ^ TD2[(s1 >>> 8) & 0xff] ^ TD3[(s0 >>> 0) & 0xff] - ^ rk[kp + 7]; + ^ key[kp + 7]; kp += 8; @@ -333,22 +402,22 @@ aes.decryptBlock = function decryptBlock(input, key) { ^ TD1[(t3 >>> 16) & 0xff] ^ TD2[(t2 >>> 8) & 0xff] ^ TD3[(t1 >>> 0) & 0xff] - ^ rk[kp + 0]; + ^ key[kp + 0]; s1 = TD0[(t1 >>> 24) & 0xff] ^ TD1[(t0 >>> 16) & 0xff] ^ TD2[(t3 >>> 8) & 0xff] ^ TD3[(t2 >>> 0) & 0xff] - ^ rk[kp + 1]; + ^ key[kp + 1]; s2 = TD0[(t2 >>> 24) & 0xff] ^ TD1[(t1 >>> 16) & 0xff] ^ TD2[(t0 >>> 8) & 0xff] ^ TD3[(t3 >>> 0) & 0xff] - ^ rk[kp + 2]; + ^ key[kp + 2]; s3 = TD0[(t3 >>> 24) & 0xff] ^ TD1[(t2 >>> 16) & 0xff] ^ TD2[(t1 >>> 8) & 0xff] ^ TD3[(t0 >>> 0) & 0xff] - ^ rk[kp + 3]; + ^ key[kp + 3]; } // Apply last round and map cipher @@ -357,23 +426,24 @@ aes.decryptBlock = function decryptBlock(input, key) { ^ (TD4[(t3 >>> 16) & 0xff] << 16) ^ (TD4[(t2 >>> 8) & 0xff] << 8) ^ (TD4[(t1 >>> 0) & 0xff] << 0) - ^ rk[kp + 0]; + ^ key[kp + 0]; s1 = (TD4[(t1 >>> 24) & 0xff] << 24) ^ (TD4[(t0 >>> 16) & 0xff] << 16) ^ (TD4[(t3 >>> 8) & 0xff] << 8) ^ (TD4[(t2 >>> 0) & 0xff] << 0) - ^ rk[kp + 1]; + ^ key[kp + 1]; s2 = (TD4[(t2 >>> 24) & 0xff] << 24) ^ (TD4[(t1 >>> 16) & 0xff] << 16) ^ (TD4[(t0 >>> 8) & 0xff] << 8) ^ (TD4[(t3 >>> 0) & 0xff] << 0) - ^ rk[kp + 2]; + ^ key[kp + 2]; s3 = (TD4[(t3 >>> 24) & 0xff] << 24) ^ (TD4[(t2 >>> 16) & 0xff] << 16) ^ (TD4[(t1 >>> 8) & 0xff] << 8) ^ (TD4[(t0 >>> 0) & 0xff] << 0) - ^ rk[kp + 3]; + ^ key[kp + 3]; + output = new Buffer(16); writeU32(output, s0, 0); writeU32(output, s1, 4); writeU32(output, s2, 8); @@ -383,103 +453,146 @@ aes.decryptBlock = function decryptBlock(input, key) { }; /** - * Encrypt data with aes 256 (no chaining). + * Encrypt data with aes 256. * @param {Buffer} data * @param {Buffer} key * @param {Buffer} iv + * @param {Boolean?} chain - XOR chaining (cbc). * @returns {Buffer} */ -aes.encrypt = function encrypt(data, key, iv, chain) { - var i, out, key, prev, block, left, pad; +AES.encrypt = function encrypt(data, key, iv, chain) { + var aes = new AES(key, 256, iv); + var trailing = data.length % 16; + var len = data.length - trailing; + var blocks = []; + var i, prev, block, left, pad; - key = aes.getEncryptKey(key, 256); - prev = iv; - out = []; + // Setup initialization vector. + prev = aes.iv; - for (i = 0; i < data.length; i += 16) { + // Encrypt all blocks except for the last. + for (i = 0; i < len; i += 16) { block = data.slice(i, i + 16); - - if (i + 16 >= data.length) { - left = 16 - block.length; - if (left === 0) { - pad = new Buffer(16); - pad.fill(16); - if (chain) - block = xor(block, prev); - prev = aes.encryptBlock(block, key); - out.push(prev); - block = pad; - } else { - pad = new Buffer(left); - pad.fill(left); - block = Buffer.concat([block, pad]); - } - } - if (chain) block = xor(block, prev); - - prev = aes.encryptBlock(block, key); - out.push(prev); + prev = aes.encryptBlock(block); + blocks.push(prev); } - return Buffer.concat(out); + // Handle padding on the last block. + if (trailing === 0) { + block = new Buffer(16); + block.fill(16); + } else { + block = data.slice(len, len + trailing); + left = 16 - trailing; + pad = new Buffer(left); + pad.fill(left); + block = Buffer.concat([block, pad]); + } + + // Encrypt the last block, + // as well as the padding. + if (chain) + block = xor(block, prev); + + block = aes.encryptBlock(block); + blocks.push(block); + + aes.destroy(); + + return Buffer.concat(blocks); }; /** - * Decrypt data with aes 256 (no chaining). - * Derives key from passphrase automatically. + * Decrypt data with aes 256. * @param {Buffer} data * @param {Buffer} key * @param {Buffer} iv + * @param {Boolean?} chain - XOR chaining (cbc). * @returns {Buffer} */ -aes.decrypt = function decrypt(data, key, iv, chain) { - var i, j, b, n, out, key, prev, chunk, block; +AES.decrypt = function decrypt(data, key, iv, chain) { + var aes = new AES(key, 256, iv); + var blocks = []; + var i, b, n, prev, chunk, block; - key = aes.getDecryptKey(key, 256); - prev = iv; - out = []; + assert(data.length > 0); + assert(data.length % 16 === 0); + // Setup initialization vector. + prev = aes.iv; + + // Decrypt all blocks. for (i = 0; i < data.length; i += 16) { chunk = prev; prev = data.slice(i, i + 16); - block = aes.decryptBlock(prev, key); - + block = aes.decryptBlock(prev); if (chain) block = xor(block, chunk); - - if (i + 16 >= data.length) { - b = 16; - n = block[b - 1]; - - if (n === 0 || n > b) - throw new Error('Bad decrypt'); - - for (j = 0; j < n; j++) { - if (block[--b] !== n) - throw new Error('Bad decrypt'); - } - - if (n === 16) - break; - - block = block.slice(0, -n); - } - - out.push(block); + blocks.push(block); } - return Buffer.concat(out); + // Check padding on the last block. + block = blocks.pop(); + b = 16; + n = block[b - 1]; + + if (n === 0 || n > b) + throw new Error('Bad decrypt'); + + for (i = 0; i < n; i++) { + if (block[--b] !== n) + throw new Error('Bad decrypt'); + } + + // Slice off the padding unless + // the entire block was padding. + if (n < 16) { + block = block.slice(0, -n); + blocks.push(block); + } + + aes.destroy(); + + return Buffer.concat(blocks); +}; + +/** + * Electronic codebook mode. + */ + +AES.ecb = {}; + +/** + * Encrypt data with aes 256 ecb. + * @param {Buffer} data + * @param {Buffer} key + * @returns {Buffer} + */ + +AES.ecb.encrypt = function encrypt(data, key) { + return AES.encrypt(data, key, null, false); +}; + +/** + * Decrypt data with aes 256 ecb. + * @param {Buffer} data + * @param {Buffer} key + * @returns {Buffer} + */ + +AES.ecb.decrypt = function decrypt(data, key) { + return AES.decrypt(data, key, null, false); }; /** * Cipher block chaining mode. */ -aes.cbc = {}; +AES.cbc = {}; /** * Encrypt data with aes 256 cbc. @@ -489,21 +602,20 @@ aes.cbc = {}; * @returns {Buffer} */ -aes.cbc.encrypt = function encrypt(data, key, iv) { - return aes.encrypt(data, key, iv, true); +AES.cbc.encrypt = function encrypt(data, key, iv) { + return AES.encrypt(data, key, iv, true); }; /** * Decrypt data with aes 256 cbc. - * Derives key from passphrase automatically. * @param {Buffer} data * @param {Buffer} key * @param {Buffer} iv * @returns {Buffer} */ -aes.cbc.decrypt = function decrypt(data, key, iv) { - return aes.decrypt(data, key, iv, true); +AES.cbc.decrypt = function decrypt(data, key, iv) { + return AES.decrypt(data, key, iv, true); }; /* @@ -1111,3 +1223,9 @@ var RCON = [ 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 ]; + +/* + * Expose + */ + +module.exports = AES; diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 1491d329..7fd0ba72 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -398,7 +398,7 @@ utils.pbkdf2 = function pbkdf2(key, salt, iterations, dkLen, alg) { */ utils.encrypt = function encrypt(data, passphrase) { - var key, cipher; + var key, cipher, out; assert(crypto, 'No crypto module available.'); assert(passphrase, 'No passphrase.'); @@ -411,15 +411,24 @@ utils.encrypt = function encrypt(data, passphrase) { key = utils.pbkdf2key(passphrase, null, 2048, 32, 16); - if (!crypto) - return aes.cbc.encrypt(data, key.key, key.iv); + if (!crypto) { + out = aes.cbc.encrypt(data, key.key, key.iv); + key.key.fill(0); + key.iv.fill(0); + return out; + } cipher = crypto.createCipheriv('aes-256-cbc', key.key, key.iv); - return Buffer.concat([ + out = Buffer.concat([ cipher.update(data), cipher.final() ]); + + key.key.fill(0); + key.iv.fill(0); + + return out; }; /** @@ -430,7 +439,7 @@ utils.encrypt = function encrypt(data, passphrase) { */ utils.decrypt = function decrypt(data, passphrase) { - var key, decipher; + var key, decipher, out; assert(crypto, 'No crypto module available.'); assert(passphrase, 'No passphrase.'); @@ -443,15 +452,24 @@ utils.decrypt = function decrypt(data, passphrase) { key = utils.pbkdf2key(passphrase, null, 2048, 32, 16); - if (!crypto) - return aes.cbc.decrypt(data, key.key, key.iv); + if (!crypto) { + out = aes.cbc.decrypt(data, key.key, key.iv); + key.key.fill(0); + key.iv.fill(0); + return out; + } decipher = crypto.createDecipheriv('aes-256-cbc', key.key, key.iv); - return Buffer.concat([ + out = Buffer.concat([ decipher.update(data), decipher.final() ]); + + key.key.fill(0); + key.iv.fill(0); + + return out; }; /**