authCipher.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. var aes = require('./aes')
  2. var Buffer = require('safe-buffer').Buffer
  3. var Transform = require('cipher-base')
  4. var inherits = require('inherits')
  5. var GHASH = require('./ghash')
  6. var xor = require('buffer-xor')
  7. var incr32 = require('./incr32')
  8. function xorTest (a, b) {
  9. var out = 0
  10. if (a.length !== b.length) out++
  11. var len = Math.min(a.length, b.length)
  12. for (var i = 0; i < len; ++i) {
  13. out += (a[i] ^ b[i])
  14. }
  15. return out
  16. }
  17. function calcIv (self, iv, ck) {
  18. if (iv.length === 12) {
  19. self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])])
  20. return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])])
  21. }
  22. var ghash = new GHASH(ck)
  23. var len = iv.length
  24. var toPad = len % 16
  25. ghash.update(iv)
  26. if (toPad) {
  27. toPad = 16 - toPad
  28. ghash.update(Buffer.alloc(toPad, 0))
  29. }
  30. ghash.update(Buffer.alloc(8, 0))
  31. var ivBits = len * 8
  32. var tail = Buffer.alloc(8)
  33. tail.writeUIntBE(ivBits, 0, 8)
  34. ghash.update(tail)
  35. self._finID = ghash.state
  36. var out = Buffer.from(self._finID)
  37. incr32(out)
  38. return out
  39. }
  40. function StreamCipher (mode, key, iv, decrypt) {
  41. Transform.call(this)
  42. var h = Buffer.alloc(4, 0)
  43. this._cipher = new aes.AES(key)
  44. var ck = this._cipher.encryptBlock(h)
  45. this._ghash = new GHASH(ck)
  46. iv = calcIv(this, iv, ck)
  47. this._prev = Buffer.from(iv)
  48. this._cache = Buffer.allocUnsafe(0)
  49. this._secCache = Buffer.allocUnsafe(0)
  50. this._decrypt = decrypt
  51. this._alen = 0
  52. this._len = 0
  53. this._mode = mode
  54. this._authTag = null
  55. this._called = false
  56. }
  57. inherits(StreamCipher, Transform)
  58. StreamCipher.prototype._update = function (chunk) {
  59. if (!this._called && this._alen) {
  60. var rump = 16 - (this._alen % 16)
  61. if (rump < 16) {
  62. rump = Buffer.alloc(rump, 0)
  63. this._ghash.update(rump)
  64. }
  65. }
  66. this._called = true
  67. var out = this._mode.encrypt(this, chunk)
  68. if (this._decrypt) {
  69. this._ghash.update(chunk)
  70. } else {
  71. this._ghash.update(out)
  72. }
  73. this._len += chunk.length
  74. return out
  75. }
  76. StreamCipher.prototype._final = function () {
  77. if (this._decrypt && !this._authTag) throw new Error('Unsupported state or unable to authenticate data')
  78. var tag = xor(this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID))
  79. if (this._decrypt && xorTest(tag, this._authTag)) throw new Error('Unsupported state or unable to authenticate data')
  80. this._authTag = tag
  81. this._cipher.scrub()
  82. }
  83. StreamCipher.prototype.getAuthTag = function getAuthTag () {
  84. if (this._decrypt || !Buffer.isBuffer(this._authTag)) throw new Error('Attempting to get auth tag in unsupported state')
  85. return this._authTag
  86. }
  87. StreamCipher.prototype.setAuthTag = function setAuthTag (tag) {
  88. if (!this._decrypt) throw new Error('Attempting to set auth tag in unsupported state')
  89. this._authTag = tag
  90. }
  91. StreamCipher.prototype.setAAD = function setAAD (buf) {
  92. if (this._called) throw new Error('Attempting to set AAD in unsupported state')
  93. this._ghash.update(buf)
  94. this._alen += buf.length
  95. }
  96. module.exports = StreamCipher