hypercore-xsalsa20-onwrite-hook
Advanced tools
Comparing version 1.0.2 to 2.0.0
const replicate = require('hypercore-replicate') | ||
const hypercore = require('hypercore') | ||
const crypto = require('hypercore-crypto') | ||
const File = require('hypercore-indexed-file') | ||
const pump = require('pump') | ||
const hook = require('./') | ||
@@ -10,20 +12,39 @@ const ram = require('random-access-memory') | ||
const { publicKey, secretKey } = crypto.keyPair() | ||
const nonces = ram() | ||
const feed = hypercore(ram, publicKey, { | ||
const source = File(__filename, { | ||
key: publicKey, | ||
secretKey, | ||
onwrite: hook(nonces, key) | ||
}) | ||
}, (err) => { | ||
const copy = hypercore(ram, publicKey, { | ||
onwrite: hook(nonces, key) | ||
}) | ||
const cipher = hypercore(ram, publicKey, { | ||
secretKey, | ||
onwrite: hook(key) | ||
}) | ||
feed.append(Buffer.from('hello'), (err) => { | ||
feed.head(console.log) // ciphertext | ||
}) | ||
const edge = hypercore(ram, publicKey) | ||
replicate(feed, copy, (err) => { | ||
if (err) throw err | ||
copy.head(console.log) // plaintext | ||
const reader = hypercore(ram, publicKey, { | ||
onwrite: hook(key) | ||
}) | ||
source.ready(() => { | ||
//source.head(console.log) // plaintext | ||
// load cipher hypercore | ||
pump(source.createReadStream(), cipher.createWriteStream(), (err) => { | ||
if (err) throw err | ||
cipher.head(console.log) // ciphertext | ||
replicate(cipher, edge, (err) => { | ||
if (err) throw err | ||
edge.head(console.log) // ciphertext | ||
replicate(edge, reader, (err) => { | ||
if (err) throw err | ||
reader.head(console.log) // plaintext | ||
reader.createReadStream().pipe(process.stdout) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
62
index.js
const xsalsa20 = require('xsalsa20-encoding') | ||
const varint = require('varint') | ||
const crypto = require('hypercore-crypto') | ||
const assert = require('assert') | ||
@@ -15,17 +17,9 @@ | ||
* 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 | ||
* xsalsa20 cipher to encipher or decipher blocks in a Hypercore feed. | ||
* Nonces are computed from the Hypercore feed's public key and block index. | ||
* @param {String|Buffer} key | ||
* @param {?(Object)} opts | ||
* @return {Function} | ||
*/ | ||
function createHook(createStorage, key, opts) { | ||
function createHook(key, opts) { | ||
// istanbul ignore next | ||
if ('string' === typeof key) { | ||
@@ -35,35 +29,27 @@ key = Buffer.from(key, 'hex') | ||
opts = Object.assign({}, opts) | ||
assert(Buffer.isBuffer(key), '`key` is not a buffer.') | ||
if (createStorage && 'object' !== typeof createStorage) { | ||
assert('function' === typeof createStorage, | ||
'`createStorage` is not a function.') | ||
} | ||
const storage = 'function' === typeof createStorage | ||
? createStorage(opts, key) | ||
: createStorage | ||
return onwrite | ||
function onwrite(index, data, peer, done) { | ||
const offset = index * NONCE_BYTES | ||
const block = Buffer.from(varint.encode(index)) | ||
const nonce = Buffer.allocUnsafe(24) | ||
// nonce = hash(key || block) | ||
crypto.data(Buffer.concat([this.key, block])).copy(nonce) | ||
// if not replicating from a peer, encipher plaintext, | ||
// otherwise decipher ciphertext with computed nonce | ||
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) | ||
xsalsa20(key, { nonce: () => nonce }) | ||
.encode(data) | ||
.slice(NONCE_BYTES) | ||
.copy(data) | ||
} 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) | ||
}) | ||
xsalsa20(key) | ||
.decode(Buffer.concat([nonce, data])) | ||
.copy(data) | ||
} | ||
done(null) | ||
} | ||
@@ -75,4 +61,2 @@ } | ||
*/ | ||
module.exports = Object.assign(createHook, { | ||
NONCE_BYTES | ||
}) | ||
module.exports = createHook |
{ | ||
"name": "hypercore-xsalsa20-onwrite-hook", | ||
"version": "1.0.2", | ||
"version": "2.0.0", | ||
"description": "A write hook to decrypt data using a XSalsa20 cipher into a hypercore storage when replicating from peers", | ||
@@ -19,3 +19,3 @@ "main": "index.js", | ||
"hypercore": "^8.0.0", | ||
"hypercore-crypto": "^1.0.0", | ||
"hypercore-indexed-file": "^0.1.3", | ||
"hypercore-ready": "^0.2.0", | ||
@@ -28,3 +28,5 @@ "hypercore-replicate": "^0.2.0", | ||
"dependencies": { | ||
"xsalsa20-encoding": "^1.0.1" | ||
"hypercore-crypto": "^1.0.0", | ||
"varint": "^5.0.0", | ||
"xsalsa20-encoding": "^1.1.0" | ||
}, | ||
@@ -31,0 +33,0 @@ "repository": { |
@@ -16,4 +16,3 @@ hypercore-xsalsa20-onwrite-hook | ||
```js | ||
const nonces = ram() // or any `random-access-storage` compliant object | ||
const onwrite = hook(nonces, sharedSecret) | ||
const onwrite = hook(sharedSecret) | ||
``` | ||
@@ -27,2 +26,3 @@ | ||
const crypto = require('hypercore-crypto') | ||
const pump = require('pump') | ||
const hook = require('hypercore-xsalsa20-onwrite-hook') | ||
@@ -34,19 +34,36 @@ const ram = require('random-access-memory') | ||
const { publicKey, secretKey } = crypto.keyPair() | ||
const nonces = ram() | ||
const feed = hypercore(ram, publicKey, { | ||
const source = hypercore(ram, publicKey, { | ||
secretKey, | ||
onwrite: hook(nonces, key) | ||
}) | ||
const copy = hypercore(ram, publicKey, { | ||
onwrite: hook(nonces, key) | ||
const cipher = hypercore(ram, publicKey, { | ||
secretKey, | ||
onwrite: hook(key) | ||
}) | ||
feed.append(Buffer.from('hello'), (err) => { | ||
feed.head(console.log) // ciphertext | ||
const edge = hypercore(ram, publicKey) | ||
const reader = hypercore(ram, publicKey, { | ||
onwrite: hook(key) | ||
}) | ||
replicate(feed, copy, (err) => { | ||
copy.head(console.log) // plaintext | ||
source.append(Buffer.from('hello'), (err) => { | ||
source.head(console.log) // plaintext | ||
// load cipher hypercore | ||
pump(source.createReadStream(), cipher.createWriteStream(), (err) => { | ||
if (err) throw err | ||
cipher.head(console.log) // ciphertext | ||
replicate(cipher, edge, (err) => { | ||
if (err) throw err | ||
edge.head(console.log) // ciphertext | ||
replicate(edge, reader, (err) => { | ||
if (err) throw err | ||
reader.head(console.log) // plaintext | ||
}) | ||
}) | ||
}) | ||
}) | ||
@@ -61,13 +78,7 @@ ``` | ||
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. | ||
Nonces are computed from the Hypercore feed's public key and block | ||
index. | ||
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. | ||
## License | ||
MIT |
60
test.js
@@ -10,23 +10,22 @@ const replicate = require('hypercore-replicate') | ||
function createHookTest(storage, key) { | ||
return (t) => { | ||
const message = Buffer.from('hello') | ||
const onwrite = hook(storage, key) | ||
const feed = hypercore(ram, { onwrite }) | ||
feed.ready(() => { | ||
// use `Buffer.from()` to make copy as `feed.append()` | ||
// will modify `buffer` in place | ||
feed.append(Buffer.from(message), (err) => { | ||
const onwrite = hook(storage, key) // shadow | ||
const authenticated = hypercore(ram, feed.key, { onwrite }) | ||
const unauthenticated = hypercore(ram, feed.key) | ||
ready(authenticated, unauthenticated, () => { | ||
replicate(feed, authenticated, unauthenticated, (err) => { | ||
authenticated.head((err, buf) => { | ||
t.ok(0 === Buffer.compare(buf, message), 'authenticated message') | ||
test('createHook(key)', (t) => { | ||
const key = crypto.randomBytes(32) | ||
const message = Buffer.from('hello') | ||
const onwrite = hook(key) | ||
const feed = hypercore(ram, { onwrite }) | ||
feed.ready(() => { | ||
// use `Buffer.from()` to make copy as `feed.append()` | ||
// will modify `buffer` in place | ||
feed.append(Buffer.from(message), (err) => { | ||
const onwrite = hook(key) // shadow | ||
const authenticated = hypercore(ram, feed.key, { onwrite }) | ||
const unauthenticated = hypercore(ram, feed.key) | ||
ready(authenticated, unauthenticated, () => { | ||
replicate(feed, authenticated, unauthenticated, (err) => { | ||
authenticated.head((err, buf) => { | ||
t.ok(0 === Buffer.compare(buf, message), 'authenticated message') | ||
unauthenticated.head((err, buf) => { | ||
t.ok(0 !== Buffer.compare(buf, message), 'unauthenticated message') | ||
t.end() | ||
}) | ||
unauthenticated.head((err, buf) => { | ||
t.ok(0 !== Buffer.compare(buf, message), 'unauthenticated message') | ||
t.end() | ||
}) | ||
@@ -37,22 +36,3 @@ }) | ||
}) | ||
} | ||
} | ||
test('createHook(createStorage, key)', (t) => { | ||
const storage = ram() | ||
const key = crypto.randomBytes(32) | ||
createHookTest(storage, key)(t) | ||
}) | ||
}) | ||
test('createHook(createStorage, key) - storage factory', (t) => { | ||
const storage = ram() | ||
const key = crypto.randomBytes(32).toString('hex') | ||
createHookTest(factory, key)(t) | ||
function factory(opts, k) { | ||
t.ok('object' === typeof opts) | ||
t.ok(0 === Buffer.compare( | ||
Buffer.from(k, 'hex'), | ||
Buffer.from(key, 'hex'))) | ||
return storage | ||
} | ||
}) |
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
81
7791
3
122
+ Addedhypercore-crypto@^1.0.0
+ Addedvarint@^5.0.0
+ Addedb4a@1.6.7(transitive)
+ Addedblake2b@2.1.4(transitive)
+ Addedblake2b-wasm@2.4.0(transitive)
+ Addedbuffer-alloc@1.2.0(transitive)
+ Addedbuffer-alloc-unsafe@1.1.0(transitive)
+ Addedbuffer-fill@1.0.0(transitive)
+ Addedbuffer-from@1.1.2(transitive)
+ Addedhypercore-crypto@1.0.0(transitive)
+ Addedini@1.3.8(transitive)
+ Addednan@2.22.0(transitive)
+ Addednanoassert@1.1.02.0.0(transitive)
+ Addednode-gyp-build@4.8.4(transitive)
+ Addedsiphash24@1.3.1(transitive)
+ Addedsodium-javascript@0.5.6(transitive)
+ Addedsodium-native@2.4.9(transitive)
+ Addedsodium-universal@2.0.0(transitive)
+ Addeduint64be@2.0.2(transitive)
+ Addedvarint@5.0.2(transitive)
Updatedxsalsa20-encoding@^1.1.0