authCipher.js 2.6 KB

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