|
| 1 | +'use strict' |
1 | 2 | var Transform = require('stream').Transform |
2 | 3 | var inherits = require('inherits') |
3 | 4 | var StringDecoder = require('string_decoder').StringDecoder |
4 | | -module.exports = CipherBase |
5 | | -inherits(CipherBase, Transform) |
6 | | -function CipherBase (hashMode) { |
| 5 | + |
| 6 | +exports.CipherBase = CipherBase |
| 7 | +exports.CipherivBase = CipherivBase |
| 8 | +exports.DecipherBase = DecipherBase |
| 9 | +exports.DecipherivBase = DecipherivBase |
| 10 | + |
| 11 | +var K_CIPHER = 0 |
| 12 | +var K_DECIPHER = 1 |
| 13 | + |
| 14 | +function throwIfNotStringOrBuffer (val, prefix) { |
| 15 | + if (!Buffer.isBuffer(val) && typeof val !== 'string') throw new TypeError(prefx + ' must be a string or a buffer') |
| 16 | +} |
| 17 | + |
| 18 | +function throwIfNotBuffer (val, prefix) { |
| 19 | + if (!Buffer.isBuffer(val)) throw new TypeError(prefix + ' must be a buffer') |
| 20 | +} |
| 21 | + |
| 22 | +function getDecoder (decoder, encoding) { |
| 23 | + if (encoding === 'utf-8') encoding = 'utf8' |
| 24 | + decoder = decoder || new StringDecoder(encoding) |
| 25 | + if (decoder.encoding !== encoding) throw new Error('Cannot change encoding') |
| 26 | + return decoder |
| 27 | +} |
| 28 | + |
| 29 | +function toBuf (str, prefix) { |
| 30 | + throwIfNotStringOrBuffer(str, prefix) |
| 31 | + if (typeof str === 'string') return new Buffer(str, 'utf8') |
| 32 | + else return str |
| 33 | +} |
| 34 | + |
| 35 | +function CipherBase (cipher, password) { |
7 | 36 | Transform.call(this) |
8 | | - this.hashMode = typeof hashMode === 'string' |
9 | | - if (this.hashMode) { |
10 | | - this[hashMode] = this._finalOrDigest |
11 | | - } else { |
12 | | - this.final = this._finalOrDigest |
13 | | - } |
| 37 | + |
| 38 | + this._kind = K_CIPHER |
| 39 | + this._authTag = null |
14 | 40 | this._decoder = null |
15 | | - this._encoding = null |
16 | | -} |
17 | | -CipherBase.prototype.update = function (data, inputEnc, outputEnc) { |
18 | | - if (typeof data === 'string') { |
19 | | - data = new Buffer(data, inputEnc) |
20 | | - } |
21 | | - var outData = this._update(data) |
22 | | - if (this.hashMode) { |
23 | | - return this |
24 | | - } |
25 | | - if (outputEnc) { |
26 | | - outData = this._toString(outData, outputEnc) |
27 | | - } |
28 | | - return outData |
| 41 | + this._finalized = false |
| 42 | + |
| 43 | + this._init(toBuf(cipher, 'Cipher type'), toBuf(password, 'Password')) |
29 | 44 | } |
30 | 45 |
|
31 | | -CipherBase.prototype.setAutoPadding = function () {} |
| 46 | +inherits(CipherBase, Transform) |
32 | 47 |
|
33 | | -CipherBase.prototype.getAuthTag = function () { |
34 | | - throw new Error('trying to get auth tag in unsupported state') |
| 48 | +CipherBase.prototype._init = function () { |
| 49 | + throw new Error('_init is not implemented') |
35 | 50 | } |
36 | 51 |
|
37 | | -CipherBase.prototype.setAuthTag = function () { |
38 | | - throw new Error('trying to set auth tag in unsupported state') |
| 52 | +CipherBase.prototype._initiv = function () { |
| 53 | + throw new Error('_initiv is not implemented') |
39 | 54 | } |
40 | 55 |
|
41 | | -CipherBase.prototype.setAAD = function () { |
42 | | - throw new Error('trying to set aad in unsupported state') |
| 56 | +CipherBase.prototype._isAuthenticatedMode = function () { |
| 57 | + throw new Error('_isAuthenticatedMode is not implemented') |
43 | 58 | } |
44 | 59 |
|
45 | | -CipherBase.prototype._transform = function (data, _, next) { |
46 | | - var err |
| 60 | +CipherBase.prototype._transform = function (chunk, encoding, callback) { |
| 61 | + var error = null |
47 | 62 | try { |
48 | | - if (this.hashMode) { |
49 | | - this._update(data) |
50 | | - } else { |
51 | | - this.push(this._update(data)) |
52 | | - } |
53 | | - } catch (e) { |
54 | | - err = e |
55 | | - } finally { |
56 | | - next(err) |
| 63 | + if (encoding !== 'buffer') chunk = new Buffer(chunk, encoding) |
| 64 | + this.push(this.update(chunk)) |
| 65 | + } catch (err) { |
| 66 | + error = err |
57 | 67 | } |
| 68 | + |
| 69 | + callback(error) |
58 | 70 | } |
59 | | -CipherBase.prototype._flush = function (done) { |
60 | | - var err |
| 71 | + |
| 72 | +CipherBase.prototype._flush = function (callback) { |
| 73 | + var error = null |
61 | 74 | try { |
62 | 75 | this.push(this._final()) |
63 | | - } catch (e) { |
64 | | - err = e |
65 | | - } finally { |
66 | | - done(err) |
| 76 | + } catch (err) { |
| 77 | + error = err |
67 | 78 | } |
| 79 | + |
| 80 | + callback(error) |
68 | 81 | } |
69 | | -CipherBase.prototype._finalOrDigest = function (outputEnc) { |
70 | | - var outData = this._final() || new Buffer('') |
71 | | - if (outputEnc) { |
72 | | - outData = this._toString(outData, outputEnc, true) |
| 82 | + |
| 83 | +CipherBase.prototype.update = function (data, inputEncoding, outputEncoding) { |
| 84 | + throwIfNotStringOrBuffer(data, 'Cipher data') |
| 85 | + if (this._finalized) throw new Error('Trying to add data in unsupported state') |
| 86 | + |
| 87 | + if (!Buffer.isBuffer(data)) data = new Buffer(data, inputEncoding || 'binary') |
| 88 | + |
| 89 | + data = this._update(data) |
| 90 | + if (outputEncoding && outputEncoding !== 'buffer') { |
| 91 | + this._decoder = getDecoder(this._decoder, outputEncoding) |
| 92 | + data = this._decoder.write(data) |
73 | 93 | } |
74 | | - return outData |
| 94 | + return data |
75 | 95 | } |
76 | 96 |
|
77 | | -CipherBase.prototype._toString = function (value, enc, final) { |
78 | | - if (!this._decoder) { |
79 | | - this._decoder = new StringDecoder(enc) |
80 | | - this._encoding = enc |
81 | | - } |
82 | | - if (this._encoding !== enc) { |
83 | | - throw new Error('can\'t switch encodings') |
| 97 | +CipherBase.prototype._update = function (data) { |
| 98 | + throw new Error('_update is not implemented') |
| 99 | +} |
| 100 | + |
| 101 | +CipherBase.prototype.final = function (encoding) { |
| 102 | + if (this._finalized) { |
| 103 | + var msg = this._isAuthenticatedMode() |
| 104 | + ? 'Unsupported state or unable to authenticate data' |
| 105 | + : 'Unsupported state' |
| 106 | + throw new Error(msg) |
84 | 107 | } |
85 | | - var out = this._decoder.write(value) |
86 | | - if (final) { |
87 | | - out += this._decoder.end() |
| 108 | + this._finalized = true |
| 109 | + |
| 110 | + var data = this._final() |
| 111 | + if (encoding && encoding !== 'buffer') { |
| 112 | + this._decoder = getDecoder(this._decoder, encoding) |
| 113 | + data = this._decoder.end(data) |
88 | 114 | } |
89 | | - return out |
| 115 | + return data |
| 116 | +} |
| 117 | + |
| 118 | +CipherBase.prototype._final = function (data) { |
| 119 | + throw new Error('_final is not implemented') |
90 | 120 | } |
| 121 | + |
| 122 | +CipherBase.prototype.setAAD = function (aadbuf) { |
| 123 | + throwIfNotBuffer(aadbuf, 'AAD') |
| 124 | + if (!this._isAuthenticatedMode() || this._finalized) throw new Error('Attempting to set AAD in unsupported state') |
| 125 | + this._setAAD(aadbuf) |
| 126 | +} |
| 127 | + |
| 128 | +CipherBase.prototype._setAAD = function (aadbuf) { |
| 129 | + throw new Error('_setAAD is not implemented') |
| 130 | +} |
| 131 | + |
| 132 | +CipherBase.prototype.getAuthTag = function () { |
| 133 | + // only after final and if encrypting |
| 134 | + if (this._kind !== K_CIPHER || this._authTag === null) throw new Error('Attempting to get auth tag in unsupported state') |
| 135 | + return new Buffer(this._authTag) |
| 136 | +} |
| 137 | + |
| 138 | +CipherBase.prototype.setAuthTag = function (tagbuf) { |
| 139 | + if (!Buffer.isBuffer(tagbuf)) throw new TypeError('Not a buffer') |
| 140 | + if (!this._isAuthenticatedMode() || this._kind !== K_DECIPHER || this._finalized) throw new Error('Attempting to set auth tag in unsupported state') |
| 141 | + this._authTag = new Buffer(tagbuf) |
| 142 | +} |
| 143 | + |
| 144 | +CipherBase.prototype.setAutoPadding = function (ap) { |
| 145 | + if (this._finalized) throw new Error('Attempting to set auto padding in unsupported state') |
| 146 | + this._setAutoPadding(!!ap) |
| 147 | +} |
| 148 | + |
| 149 | +CipherBase.prototype._setAutoPadding = function (ap) { |
| 150 | + throw new Error('_setAutoPadding is not implemented') |
| 151 | +} |
| 152 | + |
| 153 | +function CipherivBase (cipher, password, iv) { |
| 154 | + Transform.call(this) |
| 155 | + |
| 156 | + this._kind = K_CIPHER |
| 157 | + this._authTag = null |
| 158 | + this._decoder = null |
| 159 | + this._finalized = false |
| 160 | + |
| 161 | + this._initiv(toBuf(cipher, 'Cipher type'), toBuf(password, 'Password'), toBuf(iv, 'IV')) |
| 162 | +} |
| 163 | + |
| 164 | +inherits(CipherivBase, Transform) |
| 165 | + |
| 166 | +function DecipherBase (cipher, password, iv) { |
| 167 | + Transform.call(this) |
| 168 | + |
| 169 | + this._kind = K_DECIPHER |
| 170 | + this._authTag = null |
| 171 | + this._decoder = null |
| 172 | + this._finalized = false |
| 173 | + |
| 174 | + this._init(toBuf(cipher, 'Cipher type'), toBuf(password, 'Password')) |
| 175 | +} |
| 176 | + |
| 177 | +inherits(DecipherBase, Transform) |
| 178 | + |
| 179 | +function DecipherivBase (cipher, password, iv) { |
| 180 | + Transform.call(this) |
| 181 | + |
| 182 | + this._kind = K_DECIPHER |
| 183 | + this._authTag = null |
| 184 | + this._decoder = null |
| 185 | + this._finalized = false |
| 186 | + |
| 187 | + this._initiv(toBuf(cipher, 'Cipher type'), toBuf(password, 'Password'), toBuf(iv, 'IV')) |
| 188 | +} |
| 189 | + |
| 190 | +inherits(DecipherivBase, Transform) |
0 commit comments