UNPKG

blocktrail-sdk

Version:

BlockTrail's Developer Friendly API binding for NodeJS

873 lines (755 loc) 26.2 kB
/** * @file {@link http://asmjs.org Asm.js} implementation of the {@link https://en.wikipedia.org/wiki/Advanced_Encryption_Standard Advanced Encryption Standard}. * @author Artem S Vybornov <vybornov@gmail.com> * @license MIT */ var AES_asm = function () { "use strict"; /** * Galois Field stuff init flag */ var ginit_done = false; /** * Galois Field exponentiation and logarithm tables for 3 (the generator) */ var gexp3, glog3; /** * Init Galois Field tables */ function ginit () { gexp3 = [], glog3 = []; var a = 1, c, d; for ( c = 0; c < 255; c++ ) { gexp3[c] = a; // Multiply by three d = a & 0x80, a <<= 1, a &= 255; if ( d === 0x80 ) a ^= 0x1b; a ^= gexp3[c]; // Set the log table value glog3[gexp3[c]] = c; } gexp3[255] = gexp3[0]; glog3[0] = 0; ginit_done = true; } /** * Galois Field multiplication * @param {int} a * @param {int} b * @return {int} */ function gmul ( a, b ) { var c = gexp3[ ( glog3[a] + glog3[b] ) % 255 ]; if ( a === 0 || b === 0 ) c = 0; return c; } /** * Galois Field reciprocal * @param {int} a * @return {int} */ function ginv ( a ) { var i = gexp3[ 255 - glog3[a] ]; if ( a === 0 ) i = 0; return i; } /** * AES stuff init flag */ var aes_init_done = false; /** * Encryption, Decryption, S-Box and KeyTransform tables */ var aes_sbox, aes_sinv, aes_enc, aes_dec; /** * Init AES tables */ function aes_init () { if ( !ginit_done ) ginit(); // Calculates AES S-Box value function _s ( a ) { var c, s, x; s = x = ginv(a); for ( c = 0; c < 4; c++ ) { s = ( (s << 1) | (s >>> 7) ) & 255; x ^= s; } x ^= 99; return x; } // Tables aes_sbox = [], aes_sinv = [], aes_enc = [ [], [], [], [] ], aes_dec = [ [], [], [], [] ]; for ( var i = 0; i < 256; i++ ) { var s = _s(i); // S-Box and its inverse aes_sbox[i] = s; aes_sinv[s] = i; // Ecryption and Decryption tables aes_enc[0][i] = ( gmul( 2, s ) << 24 ) | ( s << 16 ) | ( s << 8 ) | gmul( 3, s ); aes_dec[0][s] = ( gmul( 14, i ) << 24 ) | ( gmul( 9, i ) << 16 ) | ( gmul( 13, i ) << 8 ) | gmul( 11, i ); // Rotate tables for ( var t = 1; t < 4; t++ ) { aes_enc[t][i] = ( aes_enc[t-1][i] >>> 8 ) | ( aes_enc[t-1][i] << 24 ); aes_dec[t][s] = ( aes_dec[t-1][s] >>> 8 ) | ( aes_dec[t-1][s] << 24 ); } } } /** * Asm.js module constructor. * * <p> * Heap buffer layout by offset: * <pre> * 0x0000 encryption key schedule * 0x0400 decryption key schedule * 0x0800 sbox * 0x0c00 inv sbox * 0x1000 encryption tables * 0x2000 decryption tables * 0x3000 reserved (future GCM multiplication lookup table) * 0x4000 data * </pre> * Don't touch anything before <code>0x400</code>. * </p> * * @alias AES_asm * @class * @param {GlobalScope} stdlib - global scope object (e.g. <code>window</code>) * @param {Object} foreign - <i>ignored</i> * @param {ArrayBuffer} buffer - heap buffer to link with */ var wrapper = function ( stdlib, foreign, buffer ) { // Init AES stuff for the first time if ( !aes_init_done ) aes_init(); // Fill up AES tables var heap = new Uint32Array(buffer); heap.set( aes_sbox, 0x0800>>2 ); heap.set( aes_sinv, 0x0c00>>2 ); for ( var i = 0; i < 4; i++ ) { heap.set( aes_enc[i], ( 0x1000 + 0x400 * i )>>2 ); heap.set( aes_dec[i], ( 0x2000 + 0x400 * i )>>2 ); } /** * Calculate AES key schedules. * @instance * @memberof AES_asm * @param {int} ks - key size, 4/6/8 (for 128/192/256-bit key correspondingly) * @param {int} k0..k7 - key vector components */ function set_key ( ks, k0, k1, k2, k3, k4, k5, k6, k7 ) { var ekeys = heap.subarray( 0x000, 60 ), dkeys = heap.subarray( 0x100, 0x100+60 ); // Encryption key schedule ekeys.set( [ k0, k1, k2, k3, k4, k5, k6, k7 ] ); for ( var i = ks, rcon = 1; i < 4*ks+28; i++ ) { var k = ekeys[i-1]; if ( ( i % ks === 0 ) || ( ks === 8 && i % ks === 4 ) ) { k = aes_sbox[k>>>24]<<24 ^ aes_sbox[k>>>16&255]<<16 ^ aes_sbox[k>>>8&255]<<8 ^ aes_sbox[k&255]; } if ( i % ks === 0 ) { k = (k << 8) ^ (k >>> 24) ^ (rcon << 24); rcon = (rcon << 1) ^ ( (rcon & 0x80) ? 0x1b : 0 ); } ekeys[i] = ekeys[i-ks] ^ k; } // Decryption key schedule for ( var j = 0; j < i; j += 4 ) { for ( var jj = 0; jj < 4; jj++ ) { var k = ekeys[i-(4+j)+(4-jj)%4]; if ( j < 4 || j >= i-4 ) { dkeys[j+jj] = k; } else { dkeys[j+jj] = aes_dec[0][aes_sbox[k>>>24]] ^ aes_dec[1][aes_sbox[k>>>16&255]] ^ aes_dec[2][aes_sbox[k>>>8&255]] ^ aes_dec[3][aes_sbox[k&255]]; } } } // Set rounds number asm.set_rounds( ks + 5 ); } var asm = function ( stdlib, foreign, buffer ) { "use asm"; var S0 = 0, S1 = 0, S2 = 0, S3 = 0, I0 = 0, I1 = 0, I2 = 0, I3 = 0, N0 = 0, N1 = 0, N2 = 0, N3 = 0, M0 = 0, M1 = 0, M2 = 0, M3 = 0, H0 = 0, H1 = 0, H2 = 0, H3 = 0, R = 0; var HEAP = new stdlib.Uint32Array(buffer), DATA = new stdlib.Uint8Array(buffer); /** * AES core * @param {int} k - precomputed key schedule offset * @param {int} s - precomputed sbox table offset * @param {int} t - precomputed round table offset * @param {int} r - number of inner rounds to perform * @param {int} x0..x3 - 128-bit input block vector */ function _core ( k, s, t, r, x0, x1, x2, x3 ) { k = k|0; s = s|0; t = t|0; r = r|0; x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; var t1 = 0, t2 = 0, t3 = 0, y0 = 0, y1 = 0, y2 = 0, y3 = 0, i = 0; t1 = t|0x400, t2 = t|0x800, t3 = t|0xc00; // round 0 x0 = x0 ^ HEAP[(k|0)>>2], x1 = x1 ^ HEAP[(k|4)>>2], x2 = x2 ^ HEAP[(k|8)>>2], x3 = x3 ^ HEAP[(k|12)>>2]; // round 1..r for ( i = 16; (i|0) <= (r<<4); i = (i+16)|0 ) { y0 = HEAP[(t|x0>>22&1020)>>2] ^ HEAP[(t1|x1>>14&1020)>>2] ^ HEAP[(t2|x2>>6&1020)>>2] ^ HEAP[(t3|x3<<2&1020)>>2] ^ HEAP[(k|i|0)>>2], y1 = HEAP[(t|x1>>22&1020)>>2] ^ HEAP[(t1|x2>>14&1020)>>2] ^ HEAP[(t2|x3>>6&1020)>>2] ^ HEAP[(t3|x0<<2&1020)>>2] ^ HEAP[(k|i|4)>>2], y2 = HEAP[(t|x2>>22&1020)>>2] ^ HEAP[(t1|x3>>14&1020)>>2] ^ HEAP[(t2|x0>>6&1020)>>2] ^ HEAP[(t3|x1<<2&1020)>>2] ^ HEAP[(k|i|8)>>2], y3 = HEAP[(t|x3>>22&1020)>>2] ^ HEAP[(t1|x0>>14&1020)>>2] ^ HEAP[(t2|x1>>6&1020)>>2] ^ HEAP[(t3|x2<<2&1020)>>2] ^ HEAP[(k|i|12)>>2]; x0 = y0, x1 = y1, x2 = y2, x3 = y3; } // final round S0 = HEAP[(s|x0>>22&1020)>>2]<<24 ^ HEAP[(s|x1>>14&1020)>>2]<<16 ^ HEAP[(s|x2>>6&1020)>>2]<<8 ^ HEAP[(s|x3<<2&1020)>>2] ^ HEAP[(k|i|0)>>2], S1 = HEAP[(s|x1>>22&1020)>>2]<<24 ^ HEAP[(s|x2>>14&1020)>>2]<<16 ^ HEAP[(s|x3>>6&1020)>>2]<<8 ^ HEAP[(s|x0<<2&1020)>>2] ^ HEAP[(k|i|4)>>2], S2 = HEAP[(s|x2>>22&1020)>>2]<<24 ^ HEAP[(s|x3>>14&1020)>>2]<<16 ^ HEAP[(s|x0>>6&1020)>>2]<<8 ^ HEAP[(s|x1<<2&1020)>>2] ^ HEAP[(k|i|8)>>2], S3 = HEAP[(s|x3>>22&1020)>>2]<<24 ^ HEAP[(s|x0>>14&1020)>>2]<<16 ^ HEAP[(s|x1>>6&1020)>>2]<<8 ^ HEAP[(s|x2<<2&1020)>>2] ^ HEAP[(k|i|12)>>2]; } /** * ECB mode encryption * @param {int} x0..x3 - 128-bit input block vector */ function _ecb_enc ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; _core( 0x0000, 0x0800, 0x1000, R, x0, x1, x2, x3 ); } /** * ECB mode decryption * @param {int} x0..x3 - 128-bit input block vector */ function _ecb_dec ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; var t = 0; _core( 0x0400, 0x0c00, 0x2000, R, x0, x3, x2, x1 ); t = S1, S1 = S3, S3 = t; } /** * CBC mode encryption * @param {int} x0..x3 - 128-bit input block vector */ function _cbc_enc ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; _core( 0x0000, 0x0800, 0x1000, R, I0 ^ x0, I1 ^ x1, I2 ^ x2, I3 ^ x3 ); I0 = S0, I1 = S1, I2 = S2, I3 = S3; } /** * CBC mode decryption * @param {int} x0..x3 - 128-bit input block vector */ function _cbc_dec ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; var t = 0; _core( 0x0400, 0x0c00, 0x2000, R, x0, x3, x2, x1 ); t = S1, S1 = S3, S3 = t; S0 = S0 ^ I0, S1 = S1 ^ I1, S2 = S2 ^ I2, S3 = S3 ^ I3; I0 = x0, I1 = x1, I2 = x2, I3 = x3; } /** * CFB mode encryption * @param {int} x0..x3 - 128-bit input block vector */ function _cfb_enc ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; _core( 0x0000, 0x0800, 0x1000, R, I0, I1, I2, I3 ); I0 = S0 = S0 ^ x0, I1 = S1 = S1 ^ x1, I2 = S2 = S2 ^ x2, I3 = S3 = S3 ^ x3; } /** * CFB mode decryption * @param {int} x0..x3 - 128-bit input block vector */ function _cfb_dec ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; _core( 0x0000, 0x0800, 0x1000, R, I0, I1, I2, I3 ); S0 = S0 ^ x0, S1 = S1 ^ x1, S2 = S2 ^ x2, S3 = S3 ^ x3; I0 = x0, I1 = x1, I2 = x2, I3 = x3; } /** * OFB mode encryption / decryption * @param {int} x0..x3 - 128-bit input block vector */ function _ofb ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; _core( 0x0000, 0x0800, 0x1000, R, I0, I1, I2, I3 ); I0 = S0, I1 = S1, I2 = S2, I3 = S3; S0 = S0 ^ x0, S1 = S1 ^ x1, S2 = S2 ^ x2, S3 = S3 ^ x3; } /** * CTR mode encryption / decryption * @param {int} x0..x3 - 128-bit input block vector */ function _ctr ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; _core( 0x0000, 0x0800, 0x1000, R, N0, N1, N2, N3 ); N3 = ( ~M3 & N3 ) | M3 & ( N3 + 1 ), N2 = ( ~M2 & N2 ) | M2 & ( N2 + ( (N3|0) == 0 ) ), N1 = ( ~M1 & N1 ) | M1 & ( N1 + ( (N2|0) == 0 ) ), N0 = ( ~M0 & N0 ) | M0 & ( N0 + ( (N1|0) == 0 ) ); S0 = S0 ^ x0, S1 = S1 ^ x1, S2 = S2 ^ x2, S3 = S3 ^ x3; } /** * GCM mode MAC calculation * @param {int} x0..x3 - 128-bit input block vector */ function _gcm_mac ( x0, x1, x2, x3 ) { x0 = x0|0; x1 = x1|0; x2 = x2|0; x3 = x3|0; var y0 = 0, y1 = 0, y2 = 0, y3 = 0, z0 = 0, z1 = 0, z2 = 0, z3 = 0, i = 0, c = 0; x0 = x0 ^ I0, x1 = x1 ^ I1, x2 = x2 ^ I2, x3 = x3 ^ I3; y0 = H0|0, y1 = H1|0, y2 = H2|0, y3 = H3|0; for ( ; (i|0) < 128; i = (i + 1)|0 ) { if ( y0 >>> 31 ) { z0 = z0 ^ x0, z1 = z1 ^ x1, z2 = z2 ^ x2, z3 = z3 ^ x3; } y0 = (y0 << 1) | (y1 >>> 31), y1 = (y1 << 1) | (y2 >>> 31), y2 = (y2 << 1) | (y3 >>> 31), y3 = (y3 << 1); c = x3 & 1; x3 = (x3 >>> 1) | (x2 << 31), x2 = (x2 >>> 1) | (x1 << 31), x1 = (x1 >>> 1) | (x0 << 31), x0 = (x0 >>> 1); if ( c ) x0 = x0 ^ 0xe1000000; } I0 = z0, I1 = z1, I2 = z2, I3 = z3; } /** * Set the internal rounds number. * @instance * @memberof AES_asm * @param {int} r - number if inner AES rounds */ function set_rounds ( r ) { r = r|0; R = r; } /** * Populate the internal state of the module. * @instance * @memberof AES_asm * @param {int} s0...s3 - state vector */ function set_state ( s0, s1, s2, s3 ) { s0 = s0|0; s1 = s1|0; s2 = s2|0; s3 = s3|0; S0 = s0, S1 = s1, S2 = s2, S3 = s3; } /** * Populate the internal iv of the module. * @instance * @memberof AES_asm * @param {int} i0...i3 - iv vector */ function set_iv ( i0, i1, i2, i3 ) { i0 = i0|0; i1 = i1|0; i2 = i2|0; i3 = i3|0; I0 = i0, I1 = i1, I2 = i2, I3 = i3; } /** * Set nonce for CTR-family modes. * @instance * @memberof AES_asm * @param {int} n0..n3 - nonce vector */ function set_nonce ( n0, n1, n2, n3 ) { n0 = n0|0; n1 = n1|0; n2 = n2|0; n3 = n3|0; N0 = n0, N1 = n1, N2 = n2, N3 = n3; } /** * Set counter mask for CTR-family modes. * @instance * @memberof AES_asm * @param {int} m0...m3 - counter mask vector */ function set_mask ( m0, m1, m2, m3 ) { m0 = m0|0; m1 = m1|0; m2 = m2|0; m3 = m3|0; M0 = m0, M1 = m1, M2 = m2, M3 = m3; } /** * Set counter for CTR-family modes. * @instance * @memberof AES_asm * @param {int} c0...c3 - counter vector */ function set_counter ( c0, c1, c2, c3 ) { c0 = c0|0; c1 = c1|0; c2 = c2|0; c3 = c3|0; N3 = ( ~M3 & N3 ) | M3 & c3, N2 = ( ~M2 & N2 ) | M2 & c2, N1 = ( ~M1 & N1 ) | M1 & c1, N0 = ( ~M0 & N0 ) | M0 & c0; } /** * Store the internal state vector into the heap. * @instance * @memberof AES_asm * @param {int} pos - offset where to put the data * @return {int} The number of bytes have been written into the heap, always 16. */ function get_state ( pos ) { pos = pos|0; if ( pos & 15 ) return -1; DATA[pos|0] = S0>>>24, DATA[pos|1] = S0>>>16&255, DATA[pos|2] = S0>>>8&255, DATA[pos|3] = S0&255, DATA[pos|4] = S1>>>24, DATA[pos|5] = S1>>>16&255, DATA[pos|6] = S1>>>8&255, DATA[pos|7] = S1&255, DATA[pos|8] = S2>>>24, DATA[pos|9] = S2>>>16&255, DATA[pos|10] = S2>>>8&255, DATA[pos|11] = S2&255, DATA[pos|12] = S3>>>24, DATA[pos|13] = S3>>>16&255, DATA[pos|14] = S3>>>8&255, DATA[pos|15] = S3&255; return 16; } /** * Store the internal iv vector into the heap. * @instance * @memberof AES_asm * @param {int} pos - offset where to put the data * @return {int} The number of bytes have been written into the heap, always 16. */ function get_iv ( pos ) { pos = pos|0; if ( pos & 15 ) return -1; DATA[pos|0] = I0>>>24, DATA[pos|1] = I0>>>16&255, DATA[pos|2] = I0>>>8&255, DATA[pos|3] = I0&255, DATA[pos|4] = I1>>>24, DATA[pos|5] = I1>>>16&255, DATA[pos|6] = I1>>>8&255, DATA[pos|7] = I1&255, DATA[pos|8] = I2>>>24, DATA[pos|9] = I2>>>16&255, DATA[pos|10] = I2>>>8&255, DATA[pos|11] = I2&255, DATA[pos|12] = I3>>>24, DATA[pos|13] = I3>>>16&255, DATA[pos|14] = I3>>>8&255, DATA[pos|15] = I3&255; return 16; } /** * GCM initialization. * @instance * @memberof AES_asm */ function gcm_init ( ) { _ecb_enc( 0, 0, 0, 0 ); H0 = S0, H1 = S1, H2 = S2, H3 = S3; } /** * Perform ciphering operation on the supplied data. * @instance * @memberof AES_asm * @param {int} mode - block cipher mode (see {@link AES_asm} mode constants) * @param {int} pos - offset of the data being processed * @param {int} len - length of the data being processed * @return {int} Actual amount of data have been processed. */ function cipher ( mode, pos, len ) { mode = mode|0; pos = pos|0; len = len|0; var ret = 0; if ( pos & 15 ) return -1; while ( (len|0) >= 16 ) { _cipher_modes[mode&7]( DATA[pos|0]<<24 | DATA[pos|1]<<16 | DATA[pos|2]<<8 | DATA[pos|3], DATA[pos|4]<<24 | DATA[pos|5]<<16 | DATA[pos|6]<<8 | DATA[pos|7], DATA[pos|8]<<24 | DATA[pos|9]<<16 | DATA[pos|10]<<8 | DATA[pos|11], DATA[pos|12]<<24 | DATA[pos|13]<<16 | DATA[pos|14]<<8 | DATA[pos|15] ); DATA[pos|0] = S0>>>24, DATA[pos|1] = S0>>>16&255, DATA[pos|2] = S0>>>8&255, DATA[pos|3] = S0&255, DATA[pos|4] = S1>>>24, DATA[pos|5] = S1>>>16&255, DATA[pos|6] = S1>>>8&255, DATA[pos|7] = S1&255, DATA[pos|8] = S2>>>24, DATA[pos|9] = S2>>>16&255, DATA[pos|10] = S2>>>8&255, DATA[pos|11] = S2&255, DATA[pos|12] = S3>>>24, DATA[pos|13] = S3>>>16&255, DATA[pos|14] = S3>>>8&255, DATA[pos|15] = S3&255; ret = (ret + 16)|0, pos = (pos + 16)|0, len = (len - 16)|0; } return ret|0; } /** * Calculates MAC of the supplied data. * @instance * @memberof AES_asm * @param {int} mode - block cipher mode (see {@link AES_asm} mode constants) * @param {int} pos - offset of the data being processed * @param {int} len - length of the data being processed * @return {int} Actual amount of data have been processed. */ function mac ( mode, pos, len ) { mode = mode|0; pos = pos|0; len = len|0; var ret = 0; if ( pos & 15 ) return -1; while ( (len|0) >= 16 ) { _mac_modes[mode&1]( DATA[pos|0]<<24 | DATA[pos|1]<<16 | DATA[pos|2]<<8 | DATA[pos|3], DATA[pos|4]<<24 | DATA[pos|5]<<16 | DATA[pos|6]<<8 | DATA[pos|7], DATA[pos|8]<<24 | DATA[pos|9]<<16 | DATA[pos|10]<<8 | DATA[pos|11], DATA[pos|12]<<24 | DATA[pos|13]<<16 | DATA[pos|14]<<8 | DATA[pos|15] ); ret = (ret + 16)|0, pos = (pos + 16)|0, len = (len - 16)|0; } return ret|0; } /** * AES cipher modes table (virual methods) */ var _cipher_modes = [ _ecb_enc, _ecb_dec, _cbc_enc, _cbc_dec, _cfb_enc, _cfb_dec, _ofb, _ctr ]; /** * AES MAC modes table (virual methods) */ var _mac_modes = [ _cbc_enc, _gcm_mac ]; /** * Asm.js module exports */ return { set_rounds: set_rounds, set_state: set_state, set_iv: set_iv, set_nonce: set_nonce, set_mask: set_mask, set_counter:set_counter, get_state: get_state, get_iv: get_iv, gcm_init: gcm_init, cipher: cipher, mac: mac }; }( stdlib, foreign, buffer ); asm.set_key = set_key; return asm; }; /** * AES enciphering mode constants * @enum {int} * @const */ wrapper.ENC = { ECB: 0, CBC: 2, CFB: 4, OFB: 6, CTR: 7 }, /** * AES deciphering mode constants * @enum {int} * @const */ wrapper.DEC = { ECB: 1, CBC: 3, CFB: 5, OFB: 6, CTR: 7 }, /** * AES MAC mode constants * @enum {int} * @const */ wrapper.MAC = { CBC: 0, GCM: 1 }; /** * Heap data offset * @type {int} * @const */ wrapper.HEAP_DATA = 0x4000; return wrapper; }();