UNPKG

blocktrail-sdk

Version:

BlockTrail's Developer Friendly API binding for NodeJS

428 lines (340 loc) 11.7 kB
/** * Galois/Counter mode */ var _AES_GCM_data_maxLength = 68719476704; // 2^36 - 2^5 function _gcm_mac_process ( data ) { var heap = this.heap, asm = this.asm, dpos = 0, dlen = data.length || 0, wlen = 0; while ( dlen > 0 ) { wlen = _heap_write( heap, 0, data, dpos, dlen ); dpos += wlen; dlen -= wlen; while ( wlen & 15 ) heap[ wlen++ ] = 0; asm.mac( AES_asm.MAC.GCM, AES_asm.HEAP_DATA, wlen ); } } function AES_GCM ( options ) { this.nonce = null; this.adata = null; this.iv = null; this.counter = 1; this.tagSize = 16; AES.call( this, options ); this.mode = 'GCM'; } function AES_GCM_Encrypt ( options ) { AES_GCM.call( this, options ); } function AES_GCM_Decrypt ( options ) { AES_GCM.call( this, options ); } function AES_GCM_reset ( options ) { options = options || {}; AES_reset.call( this, options ); var asm = this.asm, heap = this.heap; asm.gcm_init(); var tagSize = options.tagSize; if ( tagSize !== undefined ) { if ( !is_number(tagSize) ) throw new TypeError("tagSize must be a number"); if ( tagSize < 4 || tagSize > 16 ) throw new IllegalArgumentError("illegal tagSize value"); this.tagSize = tagSize; } else { this.tagSize = 16; } var nonce = options.nonce; if ( nonce !== undefined ) { if ( is_bytes(nonce) || is_buffer(nonce) ) { nonce = new Uint8Array(nonce); } else if ( is_string(nonce) ) { nonce = string_to_bytes(nonce); } else { throw new TypeError("unexpected nonce type"); } this.nonce = nonce; var noncelen = nonce.length || 0, noncebuf = new Uint8Array(16); if ( noncelen !== 12 ) { _gcm_mac_process.call( this, nonce ); heap[0] = heap[1] = heap[2] = heap[3] = heap[4] = heap[5] = heap[6] = heap[7] = heap[8] = heap[9] = heap[10] = 0, heap[11] = noncelen>>>29, heap[12] = noncelen>>>21&255, heap[13] = noncelen>>>13&255, heap[14] = noncelen>>>5&255, heap[15] = noncelen<<3&255; asm.mac( AES_asm.MAC.GCM, AES_asm.HEAP_DATA, 16 ); asm.get_iv( AES_asm.HEAP_DATA ); asm.set_iv(); noncebuf.set( heap.subarray( 0, 16 ) ); } else { noncebuf.set(nonce); noncebuf[15] = 1; } var nonceview = new DataView( noncebuf.buffer ); this.gamma0 = nonceview.getUint32(12); asm.set_nonce( nonceview.getUint32(0), nonceview.getUint32(4), nonceview.getUint32(8), 0 ); asm.set_mask( 0, 0, 0, 0xffffffff ); } else { throw new Error("nonce is required"); } var adata = options.adata; if ( adata !== undefined && adata !== null ) { if ( is_bytes(adata) || is_buffer(adata) ) { adata = new Uint8Array(adata); } else if ( is_string(adata) ) { adata = string_to_bytes(adata); } else { throw new TypeError("unexpected adata type"); } if ( adata.length > _AES_GCM_data_maxLength ) throw new IllegalArgumentError("illegal adata length"); if ( adata.length ) { this.adata = adata; _gcm_mac_process.call( this, adata ); } else { this.adata = null; } } else { this.adata = null; } var counter = options.counter; if ( counter !== undefined ) { if ( !is_number(counter) ) throw new TypeError("counter must be a number"); if ( counter < 1 || counter > 0xffffffff ) throw new RangeError("counter must be a positive 32-bit integer"); this.counter = counter; asm.set_counter( 0, 0, 0, this.gamma0+counter|0 ); } else { this.counter = 1; asm.set_counter( 0, 0, 0, this.gamma0+1|0 ); } var iv = options.iv; if ( iv !== undefined ) { if ( !is_number(counter) ) throw new TypeError("counter must be a number"); this.iv = iv; AES_set_iv.call( this, iv ); } return this; } function AES_GCM_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 dpos = 0, dlen = data.length || 0, asm = this.asm, heap = this.heap, counter = this.counter, pos = this.pos, len = this.len, rpos = 0, rlen = ( len + dlen ) & -16, wlen = 0; if ( ((counter-1)<<4) + len + dlen > _AES_GCM_data_maxLength ) throw new RangeError("counter overflow"); 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( AES_asm.ENC.CTR, AES_asm.HEAP_DATA + pos, len ); wlen = asm.mac( AES_asm.MAC.GCM, AES_asm.HEAP_DATA + pos, wlen ); if ( wlen ) result.set( heap.subarray( pos, pos + wlen ), rpos ); counter += (wlen>>>4); rpos += wlen; if ( wlen < len ) { pos += wlen; len -= wlen; } else { pos = 0; len = 0; } } this.result = result; this.counter = counter; this.pos = pos; this.len = len; return this; } function AES_GCM_Encrypt_finish () { var asm = this.asm, heap = this.heap, counter = this.counter, tagSize = this.tagSize, adata = this.adata, pos = this.pos, len = this.len; var result = new Uint8Array( len + tagSize ); asm.cipher( AES_asm.ENC.CTR, AES_asm.HEAP_DATA + pos, (len + 15) & -16 ); if ( len ) result.set( heap.subarray( pos, pos + len ) ); for ( var i = len; i & 15; i++ ) heap[ pos + i ] = 0; asm.mac( AES_asm.MAC.GCM, AES_asm.HEAP_DATA + pos, i ); var alen = ( adata !== null ) ? adata.length : 0, clen = ( (counter-1) << 4) + len; heap[0] = heap[1] = heap[2] = 0, heap[3] = alen>>>29, heap[4] = alen>>>21, heap[5] = alen>>>13&255, heap[6] = alen>>>5&255, heap[7] = alen<<3&255, heap[8] = heap[9] = heap[10] = 0, heap[11] = clen>>>29, heap[12] = clen>>>21&255, heap[13] = clen>>>13&255, heap[14] = clen>>>5&255, heap[15] = clen<<3&255; asm.mac( AES_asm.MAC.GCM, AES_asm.HEAP_DATA, 16 ); asm.get_iv( AES_asm.HEAP_DATA ); asm.set_counter( 0, 0, 0, this.gamma0 ); asm.cipher( AES_asm.ENC.CTR, AES_asm.HEAP_DATA, 16 ); result.set( heap.subarray( 0, tagSize ), len ); this.result = result; this.counter = 1; this.pos = 0; this.len = 0; return this; } function AES_GCM_encrypt ( data ) { var result1 = AES_GCM_Encrypt_process.call( this, data ).result, result2 = AES_GCM_Encrypt_finish.call(this).result; var result = new Uint8Array( result1.length + result2.length ); if ( result1.length ) result.set( result1 ); if ( result2.length ) result.set( result2, result1.length ); this.result = result; return this; } function AES_GCM_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 dpos = 0, dlen = data.length || 0, asm = this.asm, heap = this.heap, counter = this.counter, tagSize = this.tagSize, pos = this.pos, len = this.len, rpos = 0, rlen = len + dlen > tagSize ? ( len + dlen - tagSize ) & -16 : 0, tlen = len + dlen - rlen, wlen = 0; if ( ((counter-1)<<4) + len + dlen > _AES_GCM_data_maxLength ) throw new RangeError("counter overflow"); var result = new Uint8Array(rlen); while ( dlen > tlen ) { wlen = _heap_write( heap, pos+len, data, dpos, dlen-tlen ); len += wlen; dpos += wlen; dlen -= wlen; wlen = asm.mac( AES_asm.MAC.GCM, AES_asm.HEAP_DATA + pos, wlen ); wlen = asm.cipher( AES_asm.DEC.CTR, AES_asm.HEAP_DATA + pos, wlen ); if ( wlen ) result.set( heap.subarray( pos, pos+wlen ), rpos ); counter += (wlen>>>4); rpos += wlen; pos = 0; len = 0; } if ( dlen > 0 ) { len += _heap_write( heap, 0, data, dpos, dlen ); } this.result = result; this.counter = counter; this.pos = pos; this.len = len; return this; } function AES_GCM_Decrypt_finish () { var asm = this.asm, heap = this.heap, tagSize = this.tagSize, adata = this.adata, counter = this.counter, pos = this.pos, len = this.len, rlen = len - tagSize, wlen = 0; if ( len < tagSize ) throw new IllegalStateError("authentication tag not found"); var result = new Uint8Array(rlen), atag = new Uint8Array( heap.subarray( pos+rlen, pos+len ) ); for ( var i = rlen; i & 15; i++ ) heap[ pos + i ] = 0; wlen = asm.mac( AES_asm.MAC.GCM, AES_asm.HEAP_DATA + pos, i ); wlen = asm.cipher( AES_asm.DEC.CTR, AES_asm.HEAP_DATA + pos, i ); if ( rlen ) result.set( heap.subarray( pos, pos+rlen ) ); var alen = ( adata !== null ) ? adata.length : 0, clen = ( (counter-1) << 4) + len - tagSize; heap[0] = heap[1] = heap[2] = 0, heap[3] = alen>>>29, heap[4] = alen>>>21, heap[5] = alen>>>13&255, heap[6] = alen>>>5&255, heap[7] = alen<<3&255, heap[8] = heap[9] = heap[10] = 0, heap[11] = clen>>>29, heap[12] = clen>>>21&255, heap[13] = clen>>>13&255, heap[14] = clen>>>5&255, heap[15] = clen<<3&255; asm.mac( AES_asm.MAC.GCM, AES_asm.HEAP_DATA, 16 ); asm.get_iv( AES_asm.HEAP_DATA ); asm.set_counter( 0, 0, 0, this.gamma0 ); asm.cipher( AES_asm.ENC.CTR, AES_asm.HEAP_DATA, 16 ); var acheck = 0; for ( var i = 0; i < tagSize; ++i ) acheck |= atag[i] ^ heap[i]; if ( acheck ) throw new SecurityError("data integrity check failed"); this.result = result; this.counter = 1; this.pos = 0; this.len = 0; return this; } function AES_GCM_decrypt ( data ) { var result1 = AES_GCM_Decrypt_process.call( this, data ).result, result2 = AES_GCM_Decrypt_finish.call( this ).result; var result = new Uint8Array( result1.length + result2.length ); if ( result1.length ) result.set( result1 ); if ( result2.length ) result.set( result2, result1.length ); this.result = result; return this; } var AES_GCM_prototype = AES_GCM.prototype; AES_GCM_prototype.BLOCK_SIZE = 16; AES_GCM_prototype.reset = AES_GCM_reset; AES_GCM_prototype.encrypt = AES_GCM_encrypt; AES_GCM_prototype.decrypt = AES_GCM_decrypt; var AES_GCM_Encrypt_prototype = AES_GCM_Encrypt.prototype; AES_GCM_Encrypt_prototype.BLOCK_SIZE = 16; AES_GCM_Encrypt_prototype.reset = AES_GCM_reset; AES_GCM_Encrypt_prototype.process = AES_GCM_Encrypt_process; AES_GCM_Encrypt_prototype.finish = AES_GCM_Encrypt_finish; var AES_GCM_Decrypt_prototype = AES_GCM_Decrypt.prototype; AES_GCM_Decrypt_prototype.BLOCK_SIZE = 16; AES_GCM_Decrypt_prototype.reset = AES_GCM_reset; AES_GCM_Decrypt_prototype.process = AES_GCM_Decrypt_process; AES_GCM_Decrypt_prototype.finish = AES_GCM_Decrypt_finish;