-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- port tests from `pull-box-stream` - fix anything that fails tests - handle stream chunks > max content length - max content length is (2^16 - 1), not (2^16) - content length is encoded as uint, not int
- Loading branch information
1 parent
24d7318
commit 38b34a3
Showing
8 changed files
with
304 additions
and
39 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
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 |
---|---|---|
|
@@ -51,6 +51,7 @@ | |
"secret-channel": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"pull-bitflipper": "^0.1.1", | ||
"pull-stream": "^3.7.0" | ||
} | ||
} |
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
This file was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -0,0 +1,265 @@ | ||
const test = require('node:test') | ||
const assert = require('node:assert') | ||
const pull = require('pull-stream') | ||
const pullBitflipper = require('pull-bitflipper') | ||
const { randomBytes } = require('crypto') | ||
const { pullEncrypter, pullDecrypter, KEY_SIZE, NONCE_SIZE } = require('../') | ||
const { randomInt } = require('node:crypto') | ||
|
||
test('encrypt and decrypt: simple', async (t) => { | ||
// generate a random secret, `KEYBYTES` bytes long. | ||
const key = randomBytes(KEY_SIZE) | ||
// generate a random nonce, `NONCE_SIZE` bytes long. | ||
const nonce = randomBytes(NONCE_SIZE) | ||
|
||
const plaintext1 = Buffer.from('hello world') | ||
|
||
await new Promise((resolve, _reject) => { | ||
pull( | ||
pull.values([plaintext1]), | ||
pullEncrypter(key, nonce), | ||
pullDecrypter(key, nonce), | ||
pull.concat((err, plaintext2) => { | ||
assert.ifError(err) | ||
assert.equal(plaintext2.toString('utf8'), plaintext1.toString('utf8')) | ||
resolve() | ||
}), | ||
) | ||
}) | ||
}) | ||
|
||
test('encrypt and decrypt: random buffers', async (t) => { | ||
const key = randomBytes(KEY_SIZE) | ||
const nonce = randomBytes(NONCE_SIZE) | ||
const inputBuffers = randomBuffers(randomInt(1e2, 1e3), () => randomInt(1, 1e5)) | ||
|
||
await new Promise((resolve, _reject) => { | ||
pull( | ||
pull.values(inputBuffers), | ||
pullEncrypter(key, nonce), | ||
pullDecrypter(key, nonce), | ||
pull.collect((err, outputBuffers) => { | ||
assert.ifError(err) | ||
|
||
const input = Buffer.concat(inputBuffers) | ||
const output = Buffer.concat(outputBuffers) | ||
assert.equal(output.length, input.length) | ||
assert.equal(output.toString('utf8'), input.toString('utf8')) | ||
|
||
resolve() | ||
}), | ||
) | ||
}) | ||
}) | ||
|
||
test('detect flipped bits', async (t) => { | ||
const key = randomBytes(KEY_SIZE) | ||
const nonce = randomBytes(NONCE_SIZE) | ||
const inputBuffers = randomBuffers(100, () => 1024) | ||
|
||
await new Promise((resolve, _reject) => { | ||
pull( | ||
pull.values(inputBuffers), | ||
pullEncrypter(key, nonce), | ||
pullBitflipper(0.2), | ||
pullDecrypter(key, nonce), | ||
pull.collect((err, outputBuffers) => { | ||
assert.ok(err) | ||
assert.equal(err.message, 'could not verify data') | ||
assert.notEqual(outputBuffers.length, inputBuffers.length) | ||
resolve() | ||
}), | ||
) | ||
}) | ||
}) | ||
|
||
test('protect against reordering', async (t) => { | ||
const key = randomBytes(KEY_SIZE) | ||
const nonce = randomBytes(NONCE_SIZE) | ||
const inputBuffers = randomBuffers(100, () => 1024) | ||
|
||
await new Promise((resolve, _reject) => { | ||
pull( | ||
pull.values(inputBuffers), | ||
pullEncrypter(key, nonce), | ||
pull.collect((err, valid) => { | ||
assert.ifError(err) | ||
|
||
// randomly switch two blocks | ||
const invalid = valid.slice() | ||
// since every even packet is a header, | ||
// moving those will produce valid messages | ||
// but the counters will be wrong. | ||
const i = randomInt(valid.length) | ||
let j | ||
do j = randomInt(valid.length) | ||
while (j === i) | ||
invalid[i] = valid[j] | ||
invalid[i + 1] = valid[j + 1] | ||
invalid[j] = valid[i] | ||
invalid[j + 1] = valid[i + 1] | ||
|
||
pull( | ||
pull.values(invalid), | ||
pullDecrypter(key, nonce), | ||
pull.collect((err, outputBuffers) => { | ||
assert.ok(err) | ||
assert.equal(err.message, 'could not verify data') | ||
assert.notEqual(outputBuffers.length, inputBuffers.length) | ||
resolve() | ||
}), | ||
) | ||
}), | ||
) | ||
}) | ||
}) | ||
|
||
test('detect unexpected hangup', async (t) => { | ||
const key = randomBytes(KEY_SIZE) | ||
const nonce = randomBytes(NONCE_SIZE) | ||
|
||
const inputBuffers = [ | ||
Buffer.from('I <3 TLS\n'), | ||
Buffer.from('...\n'), | ||
Buffer.from('NOT!!!!!!!!!!!!!!!\n'), | ||
] | ||
|
||
await new Promise((resolve, _reject) => { | ||
pull( | ||
pull.values(inputBuffers), | ||
pullEncrypter(key, nonce), | ||
pull.take(4), // header content header content. | ||
pullDecrypter(key, nonce), | ||
pull.collect((err, outputBuffers) => { | ||
assert.ok(err) // expects an error | ||
assert.equal( | ||
err.message, | ||
'pull-secret-channel/decrypter: stream ended before end-of-stream message', | ||
) | ||
assert.equal(outputBuffers.length, 2) | ||
assert.equal(Buffer.concat(outputBuffers).toString('utf8'), 'I <3 TLS\n...\n') | ||
resolve() | ||
}), | ||
) | ||
}) | ||
}) | ||
|
||
test('immediately hangup', async (t) => { | ||
const key = randomBytes(KEY_SIZE) | ||
const nonce = randomBytes(NONCE_SIZE) | ||
|
||
await new Promise((resolve, _reject) => { | ||
pull( | ||
pull.values([]), | ||
pullEncrypter(key, nonce), | ||
pullDecrypter(key, nonce), | ||
pull.collect((err, outputBuffers) => { | ||
assert.ifError(err) | ||
assert.deepEqual(outputBuffers, []) | ||
resolve() | ||
}), | ||
) | ||
}) | ||
}) | ||
|
||
test('skip empty buffers', async (t) => { | ||
const key = randomBytes(KEY_SIZE) | ||
const nonce = randomBytes(NONCE_SIZE) | ||
|
||
const inputBuffers = [ | ||
Buffer.alloc(0), | ||
Buffer.from('hello'), | ||
Buffer.alloc(0), | ||
Buffer.from('world'), | ||
] | ||
let chunks = 0 | ||
|
||
await new Promise((resolve, _reject) => { | ||
pull( | ||
pull.values(inputBuffers), | ||
pullEncrypter(key, nonce), | ||
pull.through(() => { | ||
chunks++ | ||
}), | ||
pullDecrypter(key, nonce), | ||
pull.collect((err, outputBuffers) => { | ||
assert.ifError(err) | ||
|
||
const input = Buffer.concat(inputBuffers) | ||
const output = Buffer.concat(outputBuffers) | ||
assert.equal(output.length, input.length) | ||
assert.equal(output.toString('utf8'), input.toString('utf8')) | ||
|
||
assert.equal(chunks, 5) // header, content, header, content, end-of-stream | ||
|
||
resolve() | ||
}), | ||
) | ||
}) | ||
}) | ||
|
||
function randomBuffers(bufferCount, getBufferLength) { | ||
const buffers = [] | ||
for (let i = 0; i < bufferCount; i++) { | ||
buffers.push(randomBytes(getBufferLength())) | ||
} | ||
return buffers | ||
} | ||
|
||
test('stalled abort: encrypter', async (t) => { | ||
const key = randomBytes(KEY_SIZE) | ||
const nonce = randomBytes(NONCE_SIZE) | ||
|
||
const stallErr = new Error('intentional stall') | ||
const read = pull(stall(), pullEncrypter(key, nonce)) | ||
|
||
let i = 0 | ||
|
||
await new Promise((resolve, _reject) => { | ||
read(null, function (err, _data) { | ||
assert.equal(err, stallErr) | ||
assert.equal(++i, 1) | ||
}) | ||
|
||
read(stallErr, function () { | ||
assert.ok(true) | ||
assert.equal(++i, 2) | ||
resolve() | ||
}) | ||
}) | ||
}) | ||
|
||
test('stalled abort: decrypter', async (t) => { | ||
const key = randomBytes(KEY_SIZE) | ||
const nonce = randomBytes(NONCE_SIZE) | ||
|
||
const stallErr = new Error('intentional stall') | ||
const read = pull(stall(), pullDecrypter(key, nonce)) | ||
|
||
let i = 0 | ||
|
||
await new Promise((resolve, _reject) => { | ||
read(null, function (err, _data) { | ||
assert.equal(err, stallErr) | ||
assert.equal(++i, 1) | ||
}) | ||
|
||
read(stallErr, function () { | ||
assert.ok(true) | ||
assert.equal(++i, 2) | ||
resolve() | ||
}) | ||
}) | ||
}) | ||
|
||
function stall() { | ||
var _cb | ||
return function (abort, cb) { | ||
if (abort) { | ||
_cb && _cb(abort) | ||
cb && cb(abort) | ||
} else { | ||
_cb = cb | ||
} | ||
} | ||
} |
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 |
---|---|---|
|
@@ -48,5 +48,6 @@ | |
"b4a": "^1.6.4", | ||
"debug": "^4.3.4", | ||
"sodium-native": "^4.0.4" | ||
} | ||
}, | ||
"devDependencies": {} | ||
} |
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
Oops, something went wrong.