Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] v2.0.0 (only ciphers, not hashes) #3

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .eslintrc

This file was deleted.

5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.nyc_output
coverage
node_modules

npm-debug.log
17 changes: 13 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
sudo: false
language: node_js
node_js:
- "0.11"
- "0.10"
- "0.12"
- "iojs"
- "4"
- "5"
- "6"
- "7"
env:
matrix:
- TEST_SUITE=unit
matrix:
include:
- node_js: "6"
env: TEST_SUITE=lint
script: npm run-script $TEST_SUITE
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# cipher-base

[![NPM Package](https://img.shields.io/npm/v/cipher-base.svg?style=flat-square)](https://www.npmjs.org/package/cipher-base)
[![Build Status](https://img.shields.io/travis/crypto-browserify/cipher-base.svg?branch=master&style=flat-square)](https://travis-ci.org/crypto-browserify/cipher-base)

[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)

Abstract base class to inherit from if you want to create streams implementing the same API as node crypto [Cipher][1] or [Decipher][2] (for [Hash][3] check [crypto-browserify/hash-base][4]).

## Example

```js
const CipherBase = require('cipher-base')
const inherits = require('inherits')

// our cipher will apply XOR 0x42 to every byte
function MyCipher () {
CipherBase.call(this, true) // for Deciper you need pass `false`
}

inherits(MyCipher, CipherBase)

MyCipher.prototype._isAuthenticatedMode = function () {
return false
}

MyCipher.prototype._setAutoPadding = function (ap) {}
MyCipher.prototype._setAAD = function (aadbuf) {}

MyCipher.prototype._update = function (data) {
const result = Buffer.allocUnsafe(data.length)
for (let i = 0; i < data.length; ++i) result[i] = data[i] ^ 0x42
return result
}

MyCipher.prototype._final = function () {
return Buffer.allocUnsafe(0)
}

const data = Buffer.from([ 0x00, 0x42 ])
const cipher = new MyCipher()
console.log(Buffer.concat([cipher.update(data), cipher.final()]))
// => <Buffer 42 00>
```

## LICENSE

MIT

[1]: https://nodejs.org/api/crypto.html#crypto_class_cipher
[2]: https://nodejs.org/api/crypto.html#crypto_class_decipher
[3]: https://nodejs.org/api/crypto.html#crypto_class_hash
[4]: https://github.com/crypto-browserify/hash-base
190 changes: 126 additions & 64 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,90 +1,152 @@
'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) {
Transform.call(this)
this.hashMode = typeof hashMode === 'string'
if (this.hashMode) {
this[hashMode] = this._finalOrDigest
} else {
this.final = this._finalOrDigest
}
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)

var K_CIPHER = 0
var K_DECIPHER = 1

function throwIfNotStringOrBuffer (val, prefix) {
if (!Buffer.isBuffer(val) && typeof val !== 'string') {
throw new TypeError(prefix + ' must be a string or a buffer')
}
return outData
}

CipherBase.prototype.setAutoPadding = function () {}
function throwIfNotBuffer (val, prefix) {
if (!Buffer.isBuffer(val)) throw new TypeError(prefix + ' must be a buffer')
}

CipherBase.prototype.getAuthTag = function () {
throw new Error('trying to get auth tag in unsupported state')
function getDecoder (decoder, encoding) {
decoder = decoder || new StringDecoder(encoding)
if (decoder.encoding !== encoding) throw new Error('Cannot change encoding')
return decoder
}

CipherBase.prototype.setAuthTag = function () {
throw new Error('trying to set auth tag in unsupported state')
function CipherBase (chipher) {
Transform.call(this)

this._kind = chipher ? K_CIPHER : K_DECIPHER
this._authTag = null
this._decoder = null
this._finalized = false
}

CipherBase.prototype.setAAD = function () {
throw new Error('trying to set aad in unsupported state')
inherits(CipherBase, Transform)

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)
this.update(chunk, encoding)
} 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)
this.push(this.final())
} 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 = Buffer.from(data, inputEncoding)

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._update = function () {
throw new Error('_update is not implemented')
}

CipherBase.prototype._toString = function (value, enc, fin) {
if (!this._decoder) {
this._decoder = new StringDecoder(enc)
this._encoding = enc
CipherBase.prototype.final = function (outputEncoding) {
if (this._finalized) {
var msg = this._isAuthenticatedMode()
? 'Unsupported state or unable to authenticate data'
: 'Unsupported state'
throw new Error(msg)
}
if (this._encoding !== enc) {
throw new Error('can\'t switch encodings')
this._finalized = true

var data = this._final()
if (outputEncoding && outputEncoding !== 'buffer') {
this._decoder = getDecoder(this._decoder, outputEncoding)
data = this._decoder.end(data)
}
var out = this._decoder.write(value)
if (fin) {
out += this._decoder.end()

if (this._kind === K_DECIPHER && this._isAuthenticatedMode() && this._authTag !== null) {
this._authTag.fill(0)
this._authTag = null
}
return out

return data
}

CipherBase.prototype._final = function (outputEncoding) {
throw new Error('_final is not implemented')
}

CipherBase.prototype.setAutoPadding = function (ap) {
if (this._finalized) {
throw new Error('Attempting to set auto padding in unsupported state')
}

this._setAutoPadding(ap)
return this
}

CipherBase.prototype._setAutoPadding = function (ap) {
throw new Error('_setAutoPadding is not implemented')
}

CipherBase.prototype.getAuthTag = function () {
if (this._kind !== K_CIPHER || this._authTag === null || !this._finalized) {
throw new Error('Attempting to get auth tag in unsupported state')
}

return Buffer.from(this._authTag)
}

CipherBase.prototype.setAuthTag = function (tagbuf) {
throwIfNotBuffer(tagbuf, 'Auth tag')
if (!this._isAuthenticatedMode() || this._kind !== K_DECIPHER || this._finalized) {
throw new Error('Attempting to set auth tag in unsupported state')
}

this._authTag = Buffer.from(tagbuf)
return this
}

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)
return this
}

CipherBase.prototype._setAAD = function (aadbuf) {
throw new Error('_setAAD is not implemented')
}

module.exports = CipherBase
36 changes: 23 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,39 @@
"name": "cipher-base",
"version": "1.0.2",
"description": "abstract base class for crypto-streams",
"main": "index.js",
"scripts": {
"test": "node test.js | tspec"
},
"repository": {
"type": "git",
"url": "git+https://github.com/crypto-browserify/cipher-base.git"
},
"keywords": [
"cipher",
"stream"
],
"author": "Calvin Metcalf <[email protected]>",
"license": "MIT",
"homepage": "https://github.com/crypto-browserify/cipher-base#readme",
"bugs": {
"url": "https://github.com/crypto-browserify/cipher-base/issues"
},
"homepage": "https://github.com/crypto-browserify/cipher-base#readme",
"license": "MIT",
"author": "Calvin Metcalf <[email protected]>",
"files": [
"index.js"
],
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/crypto-browserify/cipher-base.git"
},
"scripts": {
"coverage": "nyc node test/*.js",
"lint": "standard",
"test": "npm run lint && npm run unit",
"unit": "node test/*.js"
},
"dependencies": {
"inherits": "^2.0.1"
},
"devDependencies": {
"tap-spec": "^4.1.0",
"tape": "^4.2.0"
"nyc": "^8.3.2",
"standard": "*",
"tape": "^4.6.2"
},
"engines": {
"node": ">=4"
}
}
17 changes: 0 additions & 17 deletions readme.md

This file was deleted.

Loading