You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					118 lines
				
				2.9 KiB
			
		
		
			
		
	
	
					118 lines
				
				2.9 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								var aes = require('./aes')
							 | 
						||
| 
								 | 
							
								var Buffer = require('safe-buffer').Buffer
							 | 
						||
| 
								 | 
							
								var Transform = require('cipher-base')
							 | 
						||
| 
								 | 
							
								var inherits = require('inherits')
							 | 
						||
| 
								 | 
							
								var GHASH = require('./ghash')
							 | 
						||
| 
								 | 
							
								var xor = require('buffer-xor')
							 | 
						||
| 
								 | 
							
								var incr32 = require('./incr32')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function xorTest (a, b) {
							 | 
						||
| 
								 | 
							
								  var out = 0
							 | 
						||
| 
								 | 
							
								  if (a.length !== b.length) out++
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var len = Math.min(a.length, b.length)
							 | 
						||
| 
								 | 
							
								  for (var i = 0; i < len; ++i) {
							 | 
						||
| 
								 | 
							
								    out += (a[i] ^ b[i])
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return out
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function calcIv (self, iv, ck) {
							 | 
						||
| 
								 | 
							
								  if (iv.length === 12) {
							 | 
						||
| 
								 | 
							
								    self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])])
							 | 
						||
| 
								 | 
							
								    return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])])
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  var ghash = new GHASH(ck)
							 | 
						||
| 
								 | 
							
								  var len = iv.length
							 | 
						||
| 
								 | 
							
								  var toPad = len % 16
							 | 
						||
| 
								 | 
							
								  ghash.update(iv)
							 | 
						||
| 
								 | 
							
								  if (toPad) {
							 | 
						||
| 
								 | 
							
								    toPad = 16 - toPad
							 | 
						||
| 
								 | 
							
								    ghash.update(Buffer.alloc(toPad, 0))
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  ghash.update(Buffer.alloc(8, 0))
							 | 
						||
| 
								 | 
							
								  var ivBits = len * 8
							 | 
						||
| 
								 | 
							
								  var tail = Buffer.alloc(8)
							 | 
						||
| 
								 | 
							
								  tail.writeUIntBE(ivBits, 0, 8)
							 | 
						||
| 
								 | 
							
								  ghash.update(tail)
							 | 
						||
| 
								 | 
							
								  self._finID = ghash.state
							 | 
						||
| 
								 | 
							
								  var out = Buffer.from(self._finID)
							 | 
						||
| 
								 | 
							
								  incr32(out)
							 | 
						||
| 
								 | 
							
								  return out
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function StreamCipher (mode, key, iv, decrypt) {
							 | 
						||
| 
								 | 
							
								  Transform.call(this)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var h = Buffer.alloc(4, 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._cipher = new aes.AES(key)
							 | 
						||
| 
								 | 
							
								  var ck = this._cipher.encryptBlock(h)
							 | 
						||
| 
								 | 
							
								  this._ghash = new GHASH(ck)
							 | 
						||
| 
								 | 
							
								  iv = calcIv(this, iv, ck)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._prev = Buffer.from(iv)
							 | 
						||
| 
								 | 
							
								  this._cache = Buffer.allocUnsafe(0)
							 | 
						||
| 
								 | 
							
								  this._secCache = Buffer.allocUnsafe(0)
							 | 
						||
| 
								 | 
							
								  this._decrypt = decrypt
							 | 
						||
| 
								 | 
							
								  this._alen = 0
							 | 
						||
| 
								 | 
							
								  this._len = 0
							 | 
						||
| 
								 | 
							
								  this._mode = mode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._authTag = null
							 | 
						||
| 
								 | 
							
								  this._called = false
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inherits(StreamCipher, Transform)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								StreamCipher.prototype._update = function (chunk) {
							 | 
						||
| 
								 | 
							
								  if (!this._called && this._alen) {
							 | 
						||
| 
								 | 
							
								    var rump = 16 - (this._alen % 16)
							 | 
						||
| 
								 | 
							
								    if (rump < 16) {
							 | 
						||
| 
								 | 
							
								      rump = Buffer.alloc(rump, 0)
							 | 
						||
| 
								 | 
							
								      this._ghash.update(rump)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._called = true
							 | 
						||
| 
								 | 
							
								  var out = this._mode.encrypt(this, chunk)
							 | 
						||
| 
								 | 
							
								  if (this._decrypt) {
							 | 
						||
| 
								 | 
							
								    this._ghash.update(chunk)
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    this._ghash.update(out)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  this._len += chunk.length
							 | 
						||
| 
								 | 
							
								  return out
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								StreamCipher.prototype._final = function () {
							 | 
						||
| 
								 | 
							
								  if (this._decrypt && !this._authTag) throw new Error('Unsupported state or unable to authenticate data')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var tag = xor(this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID))
							 | 
						||
| 
								 | 
							
								  if (this._decrypt && xorTest(tag, this._authTag)) throw new Error('Unsupported state or unable to authenticate data')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._authTag = tag
							 | 
						||
| 
								 | 
							
								  this._cipher.scrub()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								StreamCipher.prototype.getAuthTag = function getAuthTag () {
							 | 
						||
| 
								 | 
							
								  if (this._decrypt || !Buffer.isBuffer(this._authTag)) throw new Error('Attempting to get auth tag in unsupported state')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this._authTag
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								StreamCipher.prototype.setAuthTag = function setAuthTag (tag) {
							 | 
						||
| 
								 | 
							
								  if (!this._decrypt) throw new Error('Attempting to set auth tag in unsupported state')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._authTag = tag
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								StreamCipher.prototype.setAAD = function setAAD (buf) {
							 | 
						||
| 
								 | 
							
								  if (this._called) throw new Error('Attempting to set AAD in unsupported state')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._ghash.update(buf)
							 | 
						||
| 
								 | 
							
								  this._alen += buf.length
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = StreamCipher
							 |