-
-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
163 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,190 @@ | ||
'use strict' | ||
var Transform = require('stream').Transform | ||
var inherits = require('inherits') | ||
var StringDecoder = require('string_decoder').StringDecoder | ||
module.exports = CipherBase | ||
inherits(CipherBase, Transform) | ||
function CipherBase (hashMode) { | ||
|
||
exports.CipherBase = CipherBase | ||
exports.CipherivBase = CipherivBase | ||
exports.DecipherBase = DecipherBase | ||
exports.DecipherivBase = DecipherivBase | ||
|
||
var K_CIPHER = 0 | ||
var K_DECIPHER = 1 | ||
|
||
function throwIfNotStringOrBuffer (val, prefix) { | ||
if (!Buffer.isBuffer(val) && typeof val !== 'string') throw new TypeError(prefx + ' must be a string or a buffer') | ||
} | ||
|
||
function throwIfNotBuffer (val, prefix) { | ||
if (!Buffer.isBuffer(val)) throw new TypeError(prefix + ' must be a buffer') | ||
} | ||
|
||
function getDecoder (decoder, encoding) { | ||
if (encoding === 'utf-8') encoding = 'utf8' | ||
decoder = decoder || new StringDecoder(encoding) | ||
if (decoder.encoding !== encoding) throw new Error('Cannot change encoding') | ||
return decoder | ||
} | ||
|
||
function toBuf (str, prefix) { | ||
throwIfNotStringOrBuffer(str, prefix) | ||
if (typeof str === 'string') return new Buffer(str, 'utf8') | ||
else return str | ||
} | ||
|
||
function CipherBase (cipher, password) { | ||
Transform.call(this) | ||
this.hashMode = typeof hashMode === 'string' | ||
if (this.hashMode) { | ||
this[hashMode] = this._finalOrDigest | ||
} else { | ||
this.final = this._finalOrDigest | ||
} | ||
|
||
this._kind = K_CIPHER | ||
this._authTag = null | ||
this._decoder = null | ||
this._encoding = null | ||
} | ||
CipherBase.prototype.update = function (data, inputEnc, outputEnc) { | ||
if (typeof data === 'string') { | ||
data = new Buffer(data, inputEnc) | ||
} | ||
var outData = this._update(data) | ||
if (this.hashMode) { | ||
return this | ||
} | ||
if (outputEnc) { | ||
outData = this._toString(outData, outputEnc) | ||
} | ||
return outData | ||
this._finalized = false | ||
|
||
this._init(toBuf(cipher, 'Cipher type'), toBuf(password, 'Password')) | ||
} | ||
|
||
CipherBase.prototype.setAutoPadding = function () {} | ||
inherits(CipherBase, Transform) | ||
|
||
CipherBase.prototype.getAuthTag = function () { | ||
throw new Error('trying to get auth tag in unsupported state') | ||
CipherBase.prototype._init = function () { | ||
throw new Error('_init is not implemented') | ||
} | ||
|
||
CipherBase.prototype.setAuthTag = function () { | ||
throw new Error('trying to set auth tag in unsupported state') | ||
CipherBase.prototype._initiv = function () { | ||
throw new Error('_initiv is not implemented') | ||
} | ||
|
||
CipherBase.prototype.setAAD = function () { | ||
throw new Error('trying to set aad in unsupported state') | ||
CipherBase.prototype._isAuthenticatedMode = function () { | ||
throw new Error('_isAuthenticatedMode is not implemented') | ||
} | ||
|
||
CipherBase.prototype._transform = function (data, _, next) { | ||
var err | ||
CipherBase.prototype._transform = function (chunk, encoding, callback) { | ||
var error = null | ||
try { | ||
if (this.hashMode) { | ||
this._update(data) | ||
} else { | ||
this.push(this._update(data)) | ||
} | ||
} catch (e) { | ||
err = e | ||
} finally { | ||
next(err) | ||
if (encoding !== 'buffer') chunk = new Buffer(chunk, encoding) | ||
this.push(this.update(chunk)) | ||
} catch (err) { | ||
error = err | ||
} | ||
|
||
callback(error) | ||
} | ||
CipherBase.prototype._flush = function (done) { | ||
var err | ||
|
||
CipherBase.prototype._flush = function (callback) { | ||
var error = null | ||
try { | ||
this.push(this._final()) | ||
} catch (e) { | ||
err = e | ||
} finally { | ||
done(err) | ||
} catch (err) { | ||
error = err | ||
} | ||
|
||
callback(error) | ||
} | ||
CipherBase.prototype._finalOrDigest = function (outputEnc) { | ||
var outData = this._final() || new Buffer('') | ||
if (outputEnc) { | ||
outData = this._toString(outData, outputEnc, true) | ||
|
||
CipherBase.prototype.update = function (data, inputEncoding, outputEncoding) { | ||
throwIfNotStringOrBuffer(data, 'Cipher data') | ||
if (this._finalized) throw new Error('Trying to add data in unsupported state') | ||
|
||
if (!Buffer.isBuffer(data)) data = new Buffer(data, inputEncoding || 'binary') | ||
|
||
data = this._update(data) | ||
if (outputEncoding && outputEncoding !== 'buffer') { | ||
this._decoder = getDecoder(this._decoder, outputEncoding) | ||
data = this._decoder.write(data) | ||
} | ||
return outData | ||
return data | ||
} | ||
|
||
CipherBase.prototype._toString = function (value, enc, final) { | ||
if (!this._decoder) { | ||
this._decoder = new StringDecoder(enc) | ||
this._encoding = enc | ||
} | ||
if (this._encoding !== enc) { | ||
throw new Error('can\'t switch encodings') | ||
CipherBase.prototype._update = function (data) { | ||
throw new Error('_update is not implemented') | ||
} | ||
|
||
CipherBase.prototype.final = function (encoding) { | ||
if (this._finalized) { | ||
var msg = this._isAuthenticatedMode() | ||
? 'Unsupported state or unable to authenticate data' | ||
: 'Unsupported state' | ||
throw new Error(msg) | ||
} | ||
var out = this._decoder.write(value) | ||
if (final) { | ||
out += this._decoder.end() | ||
this._finalized = true | ||
|
||
var data = this._final() | ||
if (encoding && encoding !== 'buffer') { | ||
this._decoder = getDecoder(this._decoder, encoding) | ||
data = this._decoder.end(data) | ||
} | ||
return out | ||
return data | ||
} | ||
|
||
CipherBase.prototype._final = function (data) { | ||
throw new Error('_final is not implemented') | ||
} | ||
|
||
CipherBase.prototype.setAAD = function (aadbuf) { | ||
throwIfNotBuffer(aadbuf, 'AAD') | ||
if (!this._isAuthenticatedMode() || this._finalized) throw new Error('Attempting to set AAD in unsupported state') | ||
this._setAAD(aadbuf) | ||
} | ||
|
||
CipherBase.prototype._setAAD = function (aadbuf) { | ||
throw new Error('_setAAD is not implemented') | ||
} | ||
|
||
CipherBase.prototype.getAuthTag = function () { | ||
// only after final and if encrypting | ||
if (this._kind !== K_CIPHER || this._authTag === null) throw new Error('Attempting to get auth tag in unsupported state') | ||
return new Buffer(this._authTag) | ||
} | ||
|
||
CipherBase.prototype.setAuthTag = function (tagbuf) { | ||
if (!Buffer.isBuffer(tagbuf)) throw new TypeError('Not a buffer') | ||
if (!this._isAuthenticatedMode() || this._kind !== K_DECIPHER || this._finalized) throw new Error('Attempting to set auth tag in unsupported state') | ||
this._authTag = new Buffer(tagbuf) | ||
} | ||
|
||
CipherBase.prototype.setAutoPadding = function (ap) { | ||
if (this._finalized) throw new Error('Attempting to set auto padding in unsupported state') | ||
this._setAutoPadding(!!ap) | ||
} | ||
|
||
CipherBase.prototype._setAutoPadding = function (ap) { | ||
throw new Error('_setAutoPadding is not implemented') | ||
} | ||
|
||
function CipherivBase (cipher, password, iv) { | ||
Transform.call(this) | ||
|
||
this._kind = K_CIPHER | ||
this._authTag = null | ||
this._decoder = null | ||
this._finalized = false | ||
|
||
this._initiv(toBuf(cipher, 'Cipher type'), toBuf(password, 'Password'), toBuf(iv, 'IV')) | ||
} | ||
|
||
inherits(CipherivBase, Transform) | ||
|
||
function DecipherBase (cipher, password, iv) { | ||
Transform.call(this) | ||
|
||
this._kind = K_DECIPHER | ||
this._authTag = null | ||
this._decoder = null | ||
this._finalized = false | ||
|
||
this._init(toBuf(cipher, 'Cipher type'), toBuf(password, 'Password')) | ||
} | ||
|
||
inherits(DecipherBase, Transform) | ||
|
||
function DecipherivBase (cipher, password, iv) { | ||
Transform.call(this) | ||
|
||
this._kind = K_DECIPHER | ||
this._authTag = null | ||
this._decoder = null | ||
this._finalized = false | ||
|
||
this._initiv(toBuf(cipher, 'Cipher type'), toBuf(password, 'Password'), toBuf(iv, 'IV')) | ||
} | ||
|
||
inherits(DecipherivBase, Transform) |