blocktrail-sdk
Version:
BlockTrail's Developer Friendly API binding for NodeJS
310 lines (248 loc) • 7.68 kB
JavaScript
function AES ( options ) {
options = options || {};
this.heap = _heap_init( Uint8Array, options ).subarray( AES_asm.HEAP_DATA );
this.asm = options.asm || AES_asm( global, null, this.heap.buffer );
this.mode = null;
this.key = null;
this.reset( options );
}
function AES_set_key ( key ) {
if ( key !== undefined ) {
if ( is_buffer(key) || is_bytes(key) ) {
key = new Uint8Array(key);
}
else if ( is_string(key) ) {
key = string_to_bytes(key);
}
else {
throw new TypeError("unexpected key type");
}
var keylen = key.length;
if ( keylen !== 16 && keylen !== 24 && keylen !== 32 )
throw new IllegalArgumentError("illegal key size");
var keyview = new DataView( key.buffer, key.byteOffset, key.byteLength );
this.asm.set_key(
keylen >> 2,
keyview.getUint32(0),
keyview.getUint32(4),
keyview.getUint32(8),
keyview.getUint32(12),
keylen > 16 ? keyview.getUint32(16) : 0,
keylen > 16 ? keyview.getUint32(20) : 0,
keylen > 24 ? keyview.getUint32(24) : 0,
keylen > 24 ? keyview.getUint32(28) : 0
);
this.key = key;
}
else if ( !this.key ) {
throw new Error("key is required");
}
}
function AES_set_iv ( iv ) {
if ( iv !== undefined ) {
if ( is_buffer(iv) || is_bytes(iv) ) {
iv = new Uint8Array(iv);
}
else if ( is_string(iv) ) {
iv = string_to_bytes(iv);
}
else {
throw new TypeError("unexpected iv type");
}
if ( iv.length !== 16 )
throw new IllegalArgumentError("illegal iv size");
var ivview = new DataView( iv.buffer, iv.byteOffset, iv.byteLength );
this.iv = iv;
this.asm.set_iv( ivview.getUint32(0), ivview.getUint32(4), ivview.getUint32(8), ivview.getUint32(12) );
}
else {
this.iv = null;
this.asm.set_iv( 0, 0, 0, 0 );
}
}
function AES_set_padding ( padding ) {
if ( padding !== undefined ) {
this.padding = !!padding;
}
else {
this.padding = true;
}
}
function AES_reset ( options ) {
options = options || {};
this.result = null;
this.pos = 0;
this.len = 0;
AES_set_key.call( this, options.key );
if ( this.hasOwnProperty('iv') ) AES_set_iv.call( this, options.iv );
if ( this.hasOwnProperty('padding') ) AES_set_padding.call( this, options.padding );
return this;
}
function AES_Encrypt_process ( data ) {
if ( is_string(data) )
data = string_to_bytes(data);
if ( is_buffer(data) )
data = new Uint8Array(data);
if ( !is_bytes(data) )
throw new TypeError("data isn't of expected type");
var asm = this.asm,
heap = this.heap,
amode = AES_asm.ENC[this.mode],
hpos = AES_asm.HEAP_DATA,
pos = this.pos,
len = this.len,
dpos = 0,
dlen = data.length || 0,
rpos = 0,
rlen = (len + dlen) & -16,
wlen = 0;
var result = new Uint8Array(rlen);
while ( dlen > 0 ) {
wlen = _heap_write( heap, pos+len, data, dpos, dlen );
len += wlen;
dpos += wlen;
dlen -= wlen;
wlen = asm.cipher( amode, hpos + pos, len );
if ( wlen ) result.set( heap.subarray( pos, pos + wlen ), rpos );
rpos += wlen;
if ( wlen < len ) {
pos += wlen;
len -= wlen;
} else {
pos = 0;
len = 0;
}
}
this.result = result;
this.pos = pos;
this.len = len;
return this;
}
function AES_Encrypt_finish ( data ) {
var presult = null,
prlen = 0;
if ( data !== undefined ) {
presult = AES_Encrypt_process.call( this, data ).result;
prlen = presult.length;
}
var asm = this.asm,
heap = this.heap,
amode = AES_asm.ENC[this.mode],
hpos = AES_asm.HEAP_DATA,
pos = this.pos,
len = this.len,
plen = 16 - len % 16,
rlen = len;
if ( this.hasOwnProperty('padding') ) {
if ( this.padding ) {
for ( var p = 0; p < plen; ++p ) heap[ pos + len + p ] = plen;
len += plen;
rlen = len;
}
else if ( len % 16 ) {
throw new IllegalArgumentError("data length must be a multiple of the block size");
}
}
else {
len += plen;
}
var result = new Uint8Array( prlen + rlen );
if ( prlen ) result.set( presult );
if ( len ) asm.cipher( amode, hpos + pos, len );
if ( rlen ) result.set( heap.subarray( pos, pos + rlen ), prlen );
this.result = result;
this.pos = 0;
this.len = 0;
return this;
}
function AES_Decrypt_process ( data ) {
if ( is_string(data) )
data = string_to_bytes(data);
if ( is_buffer(data) )
data = new Uint8Array(data);
if ( !is_bytes(data) )
throw new TypeError("data isn't of expected type");
var asm = this.asm,
heap = this.heap,
amode = AES_asm.DEC[this.mode],
hpos = AES_asm.HEAP_DATA,
pos = this.pos,
len = this.len,
dpos = 0,
dlen = data.length || 0,
rpos = 0,
rlen = (len + dlen) & -16,
plen = 0,
wlen = 0;
if ( this.hasOwnProperty('padding') && this.padding ) {
plen = len + dlen - rlen || 16;
rlen -= plen;
}
var result = new Uint8Array(rlen);
while ( dlen > 0 ) {
wlen = _heap_write( heap, pos+len, data, dpos, dlen );
len += wlen;
dpos += wlen;
dlen -= wlen;
wlen = asm.cipher( amode, hpos + pos, len - ( !dlen ? plen : 0 ) );
if ( wlen ) result.set( heap.subarray( pos, pos + wlen ), rpos );
rpos += wlen;
if ( wlen < len ) {
pos += wlen;
len -= wlen;
} else {
pos = 0;
len = 0;
}
}
this.result = result;
this.pos = pos;
this.len = len;
return this;
}
function AES_Decrypt_finish ( data ) {
var presult = null,
prlen = 0;
if ( data !== undefined ) {
presult = AES_Decrypt_process.call( this, data ).result;
prlen = presult.length;
}
var asm = this.asm,
heap = this.heap,
amode = AES_asm.DEC[this.mode],
hpos = AES_asm.HEAP_DATA,
pos = this.pos,
len = this.len,
rlen = len;
if ( len > 0 ) {
if ( len % 16 ) {
if ( this.hasOwnProperty('padding') ) {
throw new IllegalArgumentError("data length must be a multiple of the block size");
} else {
len += 16 - len % 16;
}
}
asm.cipher( amode, hpos + pos, len );
if ( this.hasOwnProperty('padding') && this.padding ) {
var pad = heap[ pos + rlen - 1 ];
if ( pad < 1 || pad > 16 || pad > rlen )
throw new SecurityError("bad padding");
var pcheck = 0;
for ( var i = pad; i > 1; i-- ) pcheck |= pad ^ heap[ pos + rlen - i ];
if ( pcheck )
throw new SecurityError("bad padding");
rlen -= pad;
}
}
var result = new Uint8Array( prlen + rlen );
if ( prlen > 0 ) {
result.set( presult );
}
if ( rlen > 0 ) {
result.set( heap.subarray( pos, pos + rlen ), prlen );
}
this.result = result;
this.pos = 0;
this.len = 0;
return this;
}