xsalsa20-encoding
Advanced tools
Comparing version 0.1.1 to 0.2.0
const crypto = require('crypto') | ||
const Codec = require('./') | ||
const nonce = crypto.randomBytes(24) | ||
const key = crypto.randomBytes(32) | ||
const codec = Codec(nonce, key) | ||
const codec = Codec(key) | ||
const hello = codec.encode('hello') | ||
@@ -21,5 +20,5 @@ const world = codec.encode('world') | ||
const codecx = Codec(nonce, key, { valueEncoding: Message }) | ||
const codecy = Codec(nonce, key, { valueEncoding: Message }) | ||
const codecx = Codec(key, { valueEncoding: Message }) | ||
const codecy = Codec(key, { valueEncoding: Message }) | ||
const buffer = codecx.encode({ data: Buffer.from('hello world') }) | ||
console.log(codecy.decode(buffer)) |
87
index.js
const xsalsa20 = require('xsalsa20') | ||
const blake2b = require('blake2b') | ||
const crypto = require('crypto') | ||
const assert = require('assert') | ||
@@ -9,16 +11,3 @@ class DefaultEncoding { | ||
function createCodec(nonce, key, opts) { | ||
if (Buffer.isBuffer(nonce) && Buffer.isBuffer(key)) { | ||
if (32 === nonce.length && 24 === key.length) { | ||
[ nonce, key ] = [ key, nonce ] | ||
} | ||
} | ||
if (32 === nonce.length && !Buffer.isBuffer(key)) { | ||
opts = key | ||
key = nonce | ||
nonce = Buffer.alloc(24) | ||
Buffer.from(blake2b(nonce.length).update(key).digest()).copy(nonce) | ||
} | ||
function createCodec(key, opts) { | ||
if (!opts || 'object' !== typeof opts) { | ||
@@ -28,21 +17,10 @@ opts = {} | ||
if (!Buffer.isBuffer(key)) { | ||
throw new TypeError('Expecting secret key to be a buffer') | ||
} | ||
assert(Buffer.isBuffer(key), 'key should be a buffer') | ||
assert(32 >= key.length, 'key should be 32 bytes') | ||
assert('object' === typeof opts, 'options should be an object') | ||
if (key.length < 32) { | ||
throw new RangeError('Expecting secret key to be at least 32 bytes') | ||
} | ||
const { | ||
valueEncoding = new DefaultEncoding() | ||
} = opts | ||
if (!Buffer.isBuffer(nonce)) { | ||
throw new TypeError('Expecting nonce to be a buffer') | ||
} | ||
if (nonce.length < 24) { | ||
throw new RangeError('Expecting nonce to be at least 24 bytes') | ||
} | ||
const { valueEncoding = new DefaultEncoding() } = opts | ||
nonce = nonce.slice(0, 24) | ||
key = key.slice(0, 32) | ||
@@ -58,19 +36,13 @@ | ||
decode, | ||
nonce, | ||
key, | ||
} | ||
function encode(value, buffer, offset) { | ||
function encode(value, buffer, offset) { | ||
const encodedValue = valueEncoding.encode(value) | ||
const plaintext = toBuffer(encodedValue) | ||
const length = encodingLength(encodedValue) | ||
const length = encodingLength(plaintext) | ||
if (!Buffer.isBuffer(plaintext)) { | ||
throw new TypeError('Cannot convert value to a buffer') | ||
} | ||
assert(Buffer.isBuffer(plaintext), 'cannot convert plaintext to a buffer') | ||
assert(plaintext.length, 'cannot encode empty plaintext buffer') | ||
if ('number' === typeof buffer) { | ||
offset = buffer | ||
} | ||
if (!Buffer.isBuffer(buffer)) { | ||
@@ -84,8 +56,10 @@ buffer = Buffer.alloc(length) | ||
const ciphertext = buffer.slice(offset) | ||
const ciphertext = buffer.slice(offset + 24) | ||
const nonce = buffer.slice(offset) | ||
if (ciphertext.length < length) { | ||
throw new RangeError('Cannot store ciphertext in buffer at offset.') | ||
} | ||
assert(ciphertext.length >= length - 24, | ||
'cannot store ciphertext in buffer at offset.') | ||
crypto.randomBytes(24).copy(nonce) | ||
const xor = xsalsa20(nonce, key) | ||
@@ -97,15 +71,17 @@ | ||
encode.bytes = length | ||
return ciphertext | ||
return buffer.slice(offset, offset + length) | ||
} | ||
function decode(buffer, offset) { | ||
if (!offset || 'number' !== typeof offset) { | ||
offset = 0 | ||
function decode(buffer, start, end) { | ||
if (!start || 'number' !== typeof start) { | ||
start = 0 | ||
} | ||
if (!Buffer.isBuffer(buffer)) { | ||
throw new TypeError('Expecting decode input to be a buffer.') | ||
if (!end || 'number' !== typeof end) { | ||
end = buffer.length | ||
} | ||
const ciphertext = buffer.slice(offset) | ||
assert(Buffer.isBuffer(buffer), 'cannot decode non-buffer') | ||
const ciphertext = buffer.slice(start + 24, end) | ||
const length = encodingLength(ciphertext) | ||
@@ -117,3 +93,4 @@ | ||
const plaintext = Buffer.allocUnsafe(length) | ||
const plaintext = Buffer.allocUnsafe(length - 24) | ||
const nonce = buffer.slice(start, start + 24) | ||
const xor = xsalsa20(nonce, key) | ||
@@ -134,3 +111,3 @@ | ||
const buffer = toBuffer(value) | ||
return buffer ? buffer.length : 0 | ||
return buffer && buffer.length ? 24 + buffer.length : 0 | ||
} | ||
@@ -151,3 +128,3 @@ | ||
if (value && 'object' === value && 'Buffer' === value.type) { | ||
if (value && 'object' === typeof value && 'Buffer' === value.type) { | ||
if (Array.isArray(value.data)) { | ||
@@ -154,0 +131,0 @@ return Buffer.from(value.data) |
{ | ||
"name": "xsalsa20-encoding", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "XSalsa20 codec that implements tha abstract-encoding interface.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
xsalsa20-encoding | ||
================= | ||
> XSalsa20 codec that implements tha abstract-encoding interface. | ||
> XSalsa20 codec that implements that [abstract-encoding][abstract-encoding] interface. | ||
> Nonces are generated randomly and prepended to the ciphertext. | ||
@@ -15,3 +16,3 @@ ## Installation | ||
```js | ||
const codec = require('xsalsa20-encoding')(nonce, secretKey) | ||
const codec = require('xsalsa20-encoding')(secretKey) | ||
@@ -31,6 +32,5 @@ // encode a value | ||
const nonce = crypto.randomBytes(24) | ||
const key = crypto.randomBytes(32) | ||
const codec = Codec(nonce, key) | ||
const codec = Codec(key) | ||
const hello = codec.encode('hello') | ||
@@ -52,3 +52,3 @@ const world = codec.encode('world') | ||
const codec = Codec(nonce, key, { valueEncoding: Message }) | ||
const codec = Codec(key, { valueEncoding: Message }) | ||
const encoded = codec.encode({ data: 'hello world' }) | ||
@@ -60,17 +60,8 @@ const message = codec.decode(encoded) // { data: 'hello world' } | ||
### `codec = require('xsalsa20-encoding')([nonce,] secretKey[, opts])` | ||
### `codec = require('xsalsa20-encoding')([secretKey[, opts])` | ||
Create a codec object from a 24 byte `nonce` and 32 byte `secretKey`. If | ||
only a 32 byte `nonce` is given, it is treated as a `secretKey`. | ||
Create a codec object from 32 byte `secretKey`. | ||
```js | ||
const nonce = crypto.randomBytes(24) | ||
const key = crypto.randomBytes(32) | ||
const codec = Codec(nonce, key) | ||
``` | ||
or | ||
```js | ||
const key = crypto.randomBytes(32) | ||
const codec = Codec(key) | ||
@@ -104,3 +95,3 @@ ``` | ||
```js | ||
const length = codec.encodingLength('hello world') // 11 | ||
const length = codec.encodingLength('hello world') // 35 | ||
``` | ||
@@ -111,1 +102,4 @@ | ||
MIT | ||
[abstract-encoding]: https://github.com/mafintosh/abstract-encoding |
101
test.js
@@ -7,8 +7,6 @@ const blake2b = require('blake2b') | ||
test('codec = Codec(nonce, key)', (t) => { | ||
const nonce = crypto.randomBytes(24) | ||
test('codec = Codec(key)', (t) => { | ||
const key = crypto.randomBytes(32) | ||
const codec = Codec(nonce, key) | ||
const codec = Codec(key) | ||
t.equal(0, Buffer.compare(nonce, codec.nonce), 'codec.nonce') | ||
t.equal(0, Buffer.compare(key, codec.key), 'codec.key') | ||
@@ -23,62 +21,6 @@ t.equal('function', typeof codec.encode, 'codec.encode') | ||
test('codec = Codec(key, nonce)', (t) => { | ||
const nonce = crypto.randomBytes(24) | ||
test('codec.encode() | codec.decode()', (t) => { | ||
const key = crypto.randomBytes(32) | ||
const codec = Codec(key, nonce) | ||
t.equal(0, Buffer.compare(nonce, codec.nonce), 'codec.nonce') | ||
t.equal(0, Buffer.compare(key, codec.key), 'codec.key') | ||
t.end() | ||
}) | ||
test('codec = Codec(key)', (t) => { | ||
const key = crypto.randomBytes(32) | ||
const nonce = Buffer.alloc(24) | ||
const codec = Codec(key) | ||
Buffer.from(blake2b(nonce.length).update(key).digest()).copy(nonce) | ||
t.equal(0, Buffer.compare(nonce, codec.nonce), 'codec.nonce') | ||
t.equal(0, Buffer.compare(key, codec.key), 'codec.key') | ||
t.end() | ||
}) | ||
test('buffer = codec.encode(value[, buffer[, offset]])', (t) => { | ||
const key = Buffer.from('0c05e0034d7c68aa08fed79f8642e10bd79a70c57402a7ecbe81a08f311e2265', 'hex') | ||
{ | ||
const codec = Codec(key) | ||
t.equal( | ||
0, | ||
Buffer.compare( | ||
Buffer.from('41a7084876', 'hex'), | ||
codec.encode('hello') | ||
), | ||
'encodes to expected value without nonce' | ||
) | ||
} | ||
{ | ||
const nonce = Buffer.from('c2acac53ced5a443e192140e65d7b07cd6130137b731676a', 'hex') | ||
const codec = Codec(nonce, key) | ||
t.true(Buffer.isBuffer(codec.encode('hello'))) | ||
t.equal( | ||
0, | ||
Buffer.compare( | ||
Buffer.from('289c3accba', 'hex'), | ||
codec.encode('hello') | ||
), | ||
'encodes to expected value with nonce' | ||
) | ||
} | ||
t.end() | ||
}) | ||
test('buffer = codec.decode(buffer[, offset])', (t) => { | ||
const nonce = crypto.randomBytes(24) | ||
const key = crypto.randomBytes(32) | ||
const codec = Codec(key, nonce) | ||
t.equal( | ||
@@ -93,15 +35,42 @@ 0, | ||
t.equal(24 + 5, codec.encode.bytes) | ||
t.equal(24 + 5, codec.decode.bytes) | ||
t.throws(() => codec.encode(null)) | ||
t.throws(() => codec.encode({})) | ||
t.throws(() => codec.encode(false)) | ||
t.throws(() => codec.encode(123)) | ||
t.throws(() => codec.encode('')) | ||
t.throws(() => codec.decode(null)) | ||
t.throws(() => codec.decode({})) | ||
t.throws(() => codec.decode(Buffer.alloc(0))) | ||
t.throws(() => codec.decode(Buffer.alloc(24), -1)) | ||
t.ok(codec.encode('hello')) | ||
t.ok(codec.encode(JSON.parse(JSON.stringify(Buffer.from('hello'))))) | ||
t.ok(codec.encode([ ...Buffer.from('hello') ])) | ||
t.ok(codec.encode(Buffer.from('hello'), Buffer.alloc(24 + 6), 1)) | ||
t.ok(0 === Buffer.compare(Buffer.from('world'), codec.decode( | ||
Buffer.concat([ codec.encode('hello'), codec.encode('world') ]), | ||
codec.encodingLength('hello') | ||
))) | ||
t.ok(0 === Buffer.compare(Buffer.from('hello'), codec.decode( | ||
Buffer.concat([ codec.encode('hello'), codec.encode('world') ]), | ||
0, codec.encodingLength('hello') | ||
))) | ||
t.end() | ||
}) | ||
test('codec = Codec(key, nonce, { valueEncoding })', (t) => { | ||
test('codec = Codec(key, { valueEncoding })', (t) => { | ||
const { Message } = pbs('message Message { string data = 1; }') | ||
const nonce = crypto.randomBytes(24) | ||
const key = crypto.randomBytes(32) | ||
const codec = Codec(key, nonce, { valueEncoding: Message }) | ||
const codec = Codec(key, { valueEncoding: Message }) | ||
t.deepEqual( | ||
{ data: 'hello world' }, | ||
codec.decode(codec.encode({ data: 'hello world' })) | ||
) | ||
codec.decode(codec.encode({ data: 'hello world' }))) | ||
t.end() | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
9348
173
100