hypercore-xsalsa20-onwrite-hook
Advanced tools
Comparing version 0.2.0 to 1.0.0
@@ -1,6 +0,4 @@ | ||
const { keyPair } = require('hypercore-crypto') | ||
const replicate = require('hypercore-replicate') | ||
const hypercore = require('hypercore') | ||
const xsalsa20 = require('xsalsa20-encoding') | ||
const crypto = require('crypto') | ||
const crypto = require('hypercore-crypto') | ||
const hook = require('./') | ||
@@ -10,30 +8,22 @@ const ram = require('random-access-memory') | ||
const key = crypto.randomBytes(32) | ||
const nonce = crypto.randomBytes(32) | ||
const onwrite = hook({ nonce, key }) | ||
const { publicKey, secretKey } = keyPair() | ||
const valueEncoding = xsalsa20(nonce, key) | ||
const feed = hypercore(ram, publicKey, { secretKey, valueEncoding }) | ||
const { publicKey, secretKey } = crypto.keyPair() | ||
const nonces = ram() | ||
feed.ready(() => { | ||
const copy = hypercore(ram, publicKey, { onwrite }) | ||
const other = hypercore(ram, publicKey, { valueEncoding }) | ||
const feed = hypercore(ram, publicKey, { | ||
secretKey, | ||
onwrite: hook(nonces, key) | ||
}) | ||
feed.append('hello') | ||
const copy = hypercore(ram, publicKey, { | ||
onwrite: hook(nonces, key) | ||
}) | ||
replicate(feed, replicate(copy, { live: true }), replicate(other, { live: true }), { | ||
live: true | ||
}) | ||
feed.append(Buffer.from('hello'), (err) => { | ||
feed.head(console.log) // ciphertext | ||
}) | ||
copy.update(() => { | ||
copy.head((err, buf) => { | ||
console.log('%s', buf) // 'hello' | ||
}) | ||
}) | ||
other.update(() => { | ||
other.head((err, buf) => { | ||
console.log('%s', buf) // 'hello' | ||
}) | ||
}) | ||
replicate(feed, copy, (err) => { | ||
if (err) throw err | ||
copy.head(console.log) // plaintext | ||
}) |
91
index.js
@@ -1,26 +0,46 @@ | ||
const xsalsa20 = require('xsalsa20') | ||
const xsalsa20 = require('xsalsa20-encoding') | ||
const assert = require('assert') | ||
/** | ||
* The size in bytes of the nonce attached to an | ||
* enciphered buffer returned from the xsalsa20 | ||
* encoding. | ||
* @public | ||
* @const | ||
*/ | ||
const NONCE_BYTES = 24 | ||
const KEY_BYTES = 32 | ||
function createHook(opts) { | ||
if (opts && 'object' !== typeof opts) { | ||
throw new TypeError('Expecting options to be an object.') | ||
/** | ||
* Creates a `onwrite()` hook for a Hypercore feed that uses the | ||
* xsalsa20 cipher to encipher or decipher blocks in a Hypercore feed. Blocks | ||
* that are written to a Hypercore feed are encrypted detached from the | ||
* nonce used for encryption. Nonces are written to a user supplied | ||
* "nonce storage" which can be reused for deciphering blocks appended | ||
* to a Hypercore feed. | ||
* | ||
* This function preserves block sizes but requires an external storage | ||
* for nonces. Users should provide a `random-access-storage` compliant | ||
* instance or a factory function that returns one. | ||
* @param {Function<Object,Buffer>} createStorage | ||
* @param {String|Buffer} key | ||
* @param {?(Object)} opts | ||
* @return {Function} | ||
*/ | ||
function createHook(createStorage, key, opts) { | ||
if ('string' === typeof key) { | ||
key = Buffer.from(key, 'hex') | ||
} | ||
// make copy of opts because we'll modify the | ||
// nonce and key in place if they are strings | ||
opts = Object.assign({}, opts) | ||
opts = Object.assign({}, opts) | ||
if ('string' === typeof opts.nonce) { | ||
opts.nonce = Buffer.from(opts.nonce, 'hex') | ||
} | ||
assert(Buffer.isBuffer(key), '`key` is not a buffer.') | ||
if ('string' === typeof opts.key) { | ||
opts.key = Buffer.from(opts.key, 'hex') | ||
if (createStorage && 'object' !== typeof createStorage) { | ||
assert('function' === typeof createStorage, | ||
'`createStorage` is not a function.') | ||
} | ||
if (opts.nonce && !Buffer.isBuffer(opts.nonce)) { | ||
throw new TypeError('Expecting given nonce to be a buffer.') | ||
} | ||
const storage = 'function' === typeof createStorage | ||
? createStorage(opts, key) | ||
: createStorage | ||
@@ -30,28 +50,27 @@ return onwrite | ||
function onwrite(index, data, peer, done) { | ||
const feed = this | ||
const offset = index * NONCE_BYTES | ||
// The caller could call `feed.append()` before the feed is ready | ||
// which calls this hook synchronously | ||
feed.ready(() => { | ||
// This hook should only handle 'readable' feeds that are | ||
// replicating with a peer. | ||
// The hook can only handle buffers as the 'xsalsa20' will | ||
// update the buffer in place | ||
if (!opts.key || feed.writable || !peer || !Buffer.isBuffer(data)) { | ||
if (!peer) { | ||
const encrypted = xsalsa20(key).encode(data) | ||
const nonce = encrypted.slice(0, NONCE_BYTES) | ||
encrypted.slice(NONCE_BYTES).copy(data) | ||
storage.write(offset, nonce, done) | ||
} else { | ||
storage.read(offset, NONCE_BYTES, (err, nonce) => { | ||
// istanbul ignore next | ||
if (err) { return done(err) } | ||
const attached = Buffer.concat([nonce, data]) | ||
const decrypted = xsalsa20(key).decode(attached) | ||
decrypted.copy(data) | ||
done(null) | ||
} else { | ||
// We use the feed's public key as a nonce if one is not given | ||
const nonce = Buffer.from(opts.nonce || feed.key).slice(0, NONCE_BYTES) | ||
const key = Buffer.from(opts.key).slice(0, KEY_BYTES) | ||
const xor = opts.xor || xsalsa20(nonce, key) | ||
xor.update(data, data) | ||
done(null) | ||
} | ||
}) | ||
}) | ||
} | ||
} | ||
} | ||
/** | ||
* Module exports. | ||
*/ | ||
module.exports = Object.assign(createHook, { | ||
NONCE_BYTES, | ||
KEY_BYTES, | ||
NONCE_BYTES | ||
}) |
{ | ||
"name": "hypercore-xsalsa20-onwrite-hook", | ||
"version": "0.2.0", | ||
"version": "1.0.0", | ||
"description": "A write hook to decrypt data using a XSalsa20 cipher into a hypercore storage when replicating from peers", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": ":" | ||
"test": "nyc tape test.js" | ||
}, | ||
@@ -19,8 +19,11 @@ "keywords": [ | ||
"hypercore": "^8.0.0", | ||
"hypercore-crypto": "^1.0.0", | ||
"hypercore-ready": "^0.2.0", | ||
"hypercore-replicate": "^0.2.0", | ||
"nyc": "^15.0.0", | ||
"random-access-memory": "^3.1.1", | ||
"xsalsa20-encoding": "^0.1.0" | ||
"tape": "^4.12.1" | ||
}, | ||
"dependencies": { | ||
"xsalsa20": "^1.0.2" | ||
"xsalsa20-encoding": "^0.2.0" | ||
}, | ||
@@ -27,0 +30,0 @@ "repository": { |
hypercore-xsalsa20-onwrite-hook | ||
=============================== | ||
A write hook to decrypt data using a XSalsa20 cipher into a hypercore | ||
storage when replicating from peers. | ||
> A write hook to decrypt data using a xsalsa20 cipher into a Hypercore | ||
> storage when replicating from peers. | ||
## Installation | ||
@@ -17,9 +16,4 @@ | ||
```js | ||
const hook = require('hypercore-xsalsa20-onwrite-hook') | ||
const feed = hypercore(storage, key, { | ||
onwrite: hook({ | ||
nonce: storageNonce, | ||
key: sharedStorageKey | ||
}) | ||
}) | ||
const nonces = ram() // or any `random-access-storage` compliant object | ||
const onwrite = hook(nonces, sharedSecret) | ||
``` | ||
@@ -30,7 +24,5 @@ | ||
```js | ||
const { keyPair } = require('hypercore-crypto') | ||
const replicate = require('hypercore-replicate') | ||
const hypercore = require('hypercore') | ||
const xsalsa20 = require('xsalsa20-encoding') | ||
const crypto = require('crypto') | ||
const crypto = require('hypercore-crypto') | ||
const hook = require('hypercore-xsalsa20-onwrite-hook') | ||
@@ -40,31 +32,21 @@ const ram = require('random-access-memory') | ||
const key = crypto.randomBytes(32) | ||
const nonce = crypto.randomBytes(32) | ||
const onwrite = hook({ nonce, key }) | ||
const { publicKey, secretKey } = keyPair() | ||
const valueEncoding = xsalsa20(nonce, key) | ||
const feed = hypercore(ram, publicKey, { secretKey, valueEncoding }) | ||
const { publicKey, secretKey } = crypto.keyPair() | ||
const nonces = ram() | ||
feed.ready(() => { | ||
const copy = hypercore(ram, publicKey, { onwrite }) | ||
const other = hypercore(ram, publicKey, { valueEncoding }) | ||
const feed = hypercore(ram, publicKey, { | ||
secretKey, | ||
onwrite: hook(nonces, key) | ||
}) | ||
feed.append('hello') | ||
const copy = hypercore(ram, publicKey, { | ||
onwrite: hook(nonces, key) | ||
}) | ||
replicate(feed, copy.replicate({ live: true }), other.replicate({ live: true }), { | ||
userData: Buffer.from([0xfa, 0xce]), | ||
live: true | ||
}) | ||
feed.append(Buffer.from('hello'), (err) => { | ||
feed.head(console.log) // ciphertext | ||
}) | ||
copy.update(() => { | ||
copy.head((err, buf) => { | ||
console.log('%s', buf) // 'hello' | ||
}) | ||
}) | ||
other.update(() => { | ||
other.head((err, buf) => { | ||
console.log('%s', buf) // 'hello' | ||
}) | ||
}) | ||
replicate(feed, copy, (err) => { | ||
copy.head(console.log) // plaintext | ||
}) | ||
@@ -75,12 +57,14 @@ ``` | ||
### `hook = require('hypercore-xsalsa20-onwrite-hook')(opts)` | ||
### `const onwrite = hook(nonceStorage, sharedKey)` | ||
where `opts` can be: | ||
Creates a `onwrite()` hook for a Hypercore feed that uses the | ||
xsalsa20 cipher to encipher or decipher blocks in a Hypercore feed. | ||
Blocks that are written to a Hypercore feed are encrypted detached | ||
from the nonce used for encryption. Nonces are written to a user | ||
supplied "nonce storage" which can be reused for deciphering blocks | ||
appended to a Hypercore feed. | ||
```js | ||
{ | ||
nonce: [Buffer], // An optional 24 byte nonce. If not given, the hypercore's public key is used | ||
key: Buffer, // A required shared 32 byte shared secret key | ||
} | ||
``` | ||
This function preserves block sizes but requires an external storage | ||
for nonces (`nonceStorage`). Users should provide a `random-access-storage` | ||
compliant instance or a factory function that returns one. | ||
@@ -87,0 +71,0 @@ ## License |
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
8419
6
138
0
7
70
+ Addedxsalsa20-encoding@^0.2.0
+ Addedb4a@1.6.7(transitive)
+ Addedblake2b@2.1.4(transitive)
+ Addedblake2b-wasm@2.4.0(transitive)
+ Addednanoassert@2.0.0(transitive)
+ Addedxsalsa20-encoding@0.2.0(transitive)
- Removedxsalsa20@^1.0.2