noise-protocol
Advanced tools
Comparing version 1.0.0 to 2.0.0
@@ -8,6 +8,6 @@ var sodium = require('sodium-native') | ||
assert(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES === KEYLEN) | ||
assert(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES === KEYLEN) | ||
// 16 bytes are cut off in the following functions | ||
assert(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES === 16 + NONCELEN) | ||
assert(sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES === MACLEN) | ||
assert(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES === 4 + NONCELEN) | ||
assert(sodium.crypto_aead_chacha20poly1305_ietf_ABYTES === MACLEN) | ||
@@ -23,4 +23,3 @@ module.exports = { | ||
var ElongatedNonce = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) | ||
sodium.sodium_memzero(ElongatedNonce) | ||
var ElongatedNonce = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) | ||
function encrypt (out, k, n, ad, plaintext) { | ||
@@ -33,5 +32,5 @@ assert(out.byteLength >= plaintext.byteLength + MACLEN, 'output buffer must be at least plaintext plus MACLEN bytes long') | ||
ElongatedNonce.set(n, 16) | ||
ElongatedNonce.set(n, 4) | ||
encrypt.bytesWritten = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(out, plaintext, ad, null, ElongatedNonce, k) | ||
encrypt.bytesWritten = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(out.subarray(0, plaintext.byteLength + MACLEN), plaintext, ad, null, ElongatedNonce, k) | ||
encrypt.bytesRead = encrypt.bytesWritten - MACLEN | ||
@@ -51,5 +50,5 @@ | ||
ElongatedNonce.set(n, 16) | ||
ElongatedNonce.set(n, 4) | ||
decrypt.bytesWritten = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(out, null, ciphertext, ad, ElongatedNonce, k) | ||
decrypt.bytesWritten = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(out.subarray(0, ciphertext.byteLength - MACLEN), null, ciphertext, ad, ElongatedNonce, k) | ||
decrypt.bytesRead = decrypt.bytesWritten + MACLEN | ||
@@ -56,0 +55,0 @@ |
33
dh.js
var sodium = require('sodium-native') | ||
var assert = require('nanoassert') | ||
var DHLEN = 2 * sodium.crypto_kx_SESSIONKEYBYTES | ||
var PKLEN = sodium.crypto_kx_PUBLICKEYBYTES | ||
var SKLEN = sodium.crypto_kx_SECRETKEYBYTES | ||
var DHLEN = sodium.crypto_scalarmult_BYTES | ||
var PKLEN = sodium.crypto_scalarmult_BYTES | ||
var SKLEN = sodium.crypto_scalarmult_SCALARBYTES | ||
var SEEDLEN = sodium.crypto_kx_SEEDBYTES | ||
@@ -16,4 +16,3 @@ | ||
generateSeedKeypair, | ||
initiator, | ||
responder | ||
dh | ||
} | ||
@@ -35,12 +34,9 @@ | ||
function initiator (output, lpk, lsk, pk) { | ||
function dh (output, lsk, pk) { | ||
assert(output.byteLength === DHLEN) | ||
assert(lpk.byteLength === PKLEN) | ||
assert(lsk.byteLength === SKLEN) | ||
assert(pk.byteLength === PKLEN) | ||
sodium.crypto_kx_client_session_keys( | ||
output.subarray(DHLEN * 1 / 2, DHLEN * 2 / 2), | ||
output.subarray(DHLEN * 0 / 2, DHLEN * 1 / 2), | ||
lpk, | ||
sodium.crypto_scalarmult( | ||
output, | ||
lsk, | ||
@@ -50,16 +46,1 @@ pk | ||
} | ||
function responder (output, lpk, lsk, pk) { | ||
assert(output.byteLength === DHLEN) | ||
assert(lpk.byteLength === PKLEN) | ||
assert(lsk.byteLength === SKLEN) | ||
assert(pk.byteLength === PKLEN) | ||
sodium.crypto_kx_server_session_keys( | ||
output.subarray(DHLEN * 0 / 2, DHLEN * 1 / 2), | ||
output.subarray(DHLEN * 1 / 2, DHLEN * 2 / 2), | ||
lpk, | ||
lsk, | ||
pk | ||
) | ||
} |
@@ -186,18 +186,15 @@ var sodium = require('sodium-native') | ||
function initialize (handshakePattern, initiator, prologue, s, e, rs, re) { | ||
assert(Object.keys(PATTERNS).includes(handshakePattern)) | ||
assert(typeof initiator === 'boolean') | ||
assert(prologue.byteLength != null) | ||
assert(Object.keys(PATTERNS).includes(handshakePattern), 'Unsupported handshake pattern') | ||
assert(typeof initiator === 'boolean', 'Initiator must be a boolean') | ||
assert(prologue.byteLength != null, 'prolouge must be a Buffer') | ||
assert(s == null ? true : s.publicKey.byteLength === dh.PKLEN) | ||
assert(s == null ? true : s.secretKey.byteLength === dh.SKLEN) | ||
assert(e == null ? true : e.publicKey.byteLength === dh.PKLEN, `e.publicKey must be ${dh.PKLEN} bytes`) | ||
assert(e == null ? true : e.secretKey.byteLength === dh.SKLEN, `e.secretKey must be ${dh.SKLEN} bytes`) | ||
assert(e == null ? true : e.publicKey.byteLength === dh.PKLEN) | ||
assert(e == null ? true : e.secretKey.byteLength === dh.SKLEN) | ||
assert(rs == null ? true : rs.byteLength === dh.PKLEN, `rs must be ${dh.PKLEN} bytes`) | ||
assert(re == null ? true : re.byteLength === dh.PKLEN, `re must be ${dh.PKLEN} bytes`) | ||
assert(rs == null ? true : rs.byteLength === dh.PKLEN) | ||
assert(re == null ? true : re.byteLength === dh.PKLEN) | ||
var state = new HandshakeState() | ||
var protocolName = Buffer.from(`Noise_${handshakePattern}_25519_XChaChaPoly_BLAKE2b`) | ||
var protocolName = Buffer.from(`Noise_${handshakePattern}_25519_ChaChaPoly_BLAKE2b`) | ||
@@ -210,4 +207,4 @@ symmetricState.initializeSymmetric(state.symmetricState, protocolName) | ||
if (s != null) { | ||
assert(s.publicKey.byteLength === dh.PKLEN) | ||
assert(s.secretKey.byteLength === dh.SKLEN) | ||
assert(s.publicKey.byteLength === dh.PKLEN, `s.publicKey must be ${dh.PKLEN} bytes`) | ||
assert(s.secretKey.byteLength === dh.SKLEN, `s.secretKey must be ${dh.SKLEN} bytes`) | ||
@@ -307,3 +304,3 @@ state.spk = sodiumBufferCopy(s.publicKey) | ||
case TOK_EE: | ||
dh[state.role === INITIATOR ? 'initiator' : 'responder'](DhResult, state.epk, state.esk, state.re) | ||
dh.dh(DhResult, state.esk, state.re) | ||
symmetricState.mixKey(state.symmetricState, DhResult) | ||
@@ -313,4 +310,4 @@ sodium.sodium_memzero(DhResult) | ||
case TOK_ES: | ||
if (state.role === INITIATOR) dh.initiator(DhResult, state.epk, state.esk, state.rs) | ||
else dh.responder(DhResult, state.spk, state.ssk, state.re) | ||
if (state.role === INITIATOR) dh.dh(DhResult, state.esk, state.rs) | ||
else dh.dh(DhResult, state.ssk, state.re) | ||
@@ -321,4 +318,4 @@ symmetricState.mixKey(state.symmetricState, DhResult) | ||
case TOK_SE: | ||
if (state.role === INITIATOR) dh.initiator(DhResult, state.spk, state.ssk, state.re) | ||
else dh.responder(DhResult, state.epk, state.esk, state.rs) | ||
if (state.role === INITIATOR) dh.dh(DhResult, state.ssk, state.re) | ||
else dh.dh(DhResult, state.esk, state.rs) | ||
@@ -329,3 +326,3 @@ symmetricState.mixKey(state.symmetricState, DhResult) | ||
case TOK_SS: | ||
dh[state.role === INITIATOR ? 'initiator' : 'responder'](DhResult, state.spk, state.ssk, state.rs) | ||
dh.dh(DhResult, state.ssk, state.rs) | ||
@@ -351,3 +348,3 @@ symmetricState.mixKey(state.symmetricState, DhResult) | ||
return {tx, rx} | ||
return { tx, rx } | ||
} | ||
@@ -406,3 +403,3 @@ } | ||
case TOK_EE: | ||
dh[state.role === INITIATOR ? 'initiator' : 'responder'](DhResult, state.epk, state.esk, state.re) | ||
dh.dh(DhResult, state.esk, state.re) | ||
symmetricState.mixKey(state.symmetricState, DhResult) | ||
@@ -412,4 +409,4 @@ sodium.sodium_memzero(DhResult) | ||
case TOK_ES: | ||
if (state.role === INITIATOR) dh.initiator(DhResult, state.epk, state.esk, state.rs) | ||
else dh.responder(DhResult, state.spk, state.ssk, state.re) | ||
if (state.role === INITIATOR) dh.dh(DhResult, state.esk, state.rs) | ||
else dh.dh(DhResult, state.ssk, state.re) | ||
@@ -420,4 +417,4 @@ symmetricState.mixKey(state.symmetricState, DhResult) | ||
case TOK_SE: | ||
if (state.role === INITIATOR) dh.initiator(DhResult, state.spk, state.ssk, state.re) | ||
else dh.responder(DhResult, state.epk, state.esk, state.rs) | ||
if (state.role === INITIATOR) dh.dh(DhResult, state.ssk, state.re) | ||
else dh.dh(DhResult, state.esk, state.rs) | ||
@@ -428,3 +425,3 @@ symmetricState.mixKey(state.symmetricState, DhResult) | ||
case TOK_SS: | ||
dh[state.role === INITIATOR ? 'initiator' : 'responder'](DhResult, state.spk, state.ssk, state.rs) | ||
dh.dh(DhResult, state.ssk, state.rs) | ||
@@ -450,3 +447,3 @@ symmetricState.mixKey(state.symmetricState, DhResult) | ||
return {tx, rx} | ||
return { tx, rx } | ||
} | ||
@@ -458,3 +455,3 @@ } | ||
if (state.symmetricState != null) { | ||
sodium.sodium_memzero(state.symmetricState) | ||
sodium.sodium_free(state.symmetricState) | ||
state.symmetricState = null | ||
@@ -466,3 +463,3 @@ } | ||
if (state.spk != null) { | ||
sodium.sodium_memzero(state.spk) | ||
sodium.sodium_free(state.spk) | ||
state.spk = null | ||
@@ -472,3 +469,3 @@ } | ||
if (state.ssk != null) { | ||
sodium.sodium_memzero(state.ssk) | ||
sodium.sodium_free(state.ssk) | ||
state.ssk = null | ||
@@ -478,3 +475,3 @@ } | ||
if (state.epk != null) { | ||
sodium.sodium_memzero(state.epk) | ||
sodium.sodium_free(state.epk) | ||
state.epk = null | ||
@@ -484,3 +481,3 @@ } | ||
if (state.esk != null) { | ||
sodium.sodium_memzero(state.esk) | ||
sodium.sodium_free(state.esk) | ||
state.esk = null | ||
@@ -490,3 +487,3 @@ } | ||
if (state.rs != null) { | ||
sodium.sodium_memzero(state.rs) | ||
sodium.sodium_free(state.rs) | ||
state.rs = null | ||
@@ -496,3 +493,3 @@ } | ||
if (state.re != null) { | ||
sodium.sodium_memzero(state.re) | ||
sodium.sodium_free(state.re) | ||
state.re = null | ||
@@ -506,3 +503,3 @@ } | ||
if (!obj) { | ||
obj = {publicKey: sodium.sodium_malloc(PKLEN), secretKey: sodium.sodium_malloc(SKLEN)} | ||
obj = { publicKey: sodium.sodium_malloc(PKLEN), secretKey: sodium.sodium_malloc(SKLEN) } | ||
return keygen(obj) | ||
@@ -520,5 +517,5 @@ } | ||
function seedKeygen (seed) { | ||
var obj = {publicKey: sodium.sodium_malloc(PKLEN), secretKey: sodium.sodium_malloc(SKLEN)} | ||
var obj = { publicKey: sodium.sodium_malloc(PKLEN), secretKey: sodium.sodium_malloc(SKLEN) } | ||
dh.generateSeedKeypair(obj.publicKey, obj.secretKey, seed) | ||
return obj | ||
} |
{ | ||
"name": "noise-protocol", | ||
"version": "1.0.0", | ||
"version": "2.0.0", | ||
"description": "Javascript implementation of the Noise Protocol Framework based on libsodium", | ||
@@ -8,13 +8,14 @@ "main": "index.js", | ||
"clone": "^2.1.2", | ||
"hmac-blake2b": "^0.2.0", | ||
"nanoassert": "^1.1.0", | ||
"sodium-native": "^2.2.1" | ||
"hmac-blake2b": "^1.0.0", | ||
"nanoassert": "^2.0.0", | ||
"sodium-native": "^3.1.1" | ||
}, | ||
"devDependencies": { | ||
"c8": "^3.2.0", | ||
"standard": "^11.0.1", | ||
"tape": "^4.9.0" | ||
"c8": "^7.1.2", | ||
"standard": "^14.3.4", | ||
"tape": "^5.0.0" | ||
}, | ||
"scripts": { | ||
"test": "tape test/**/*", | ||
"pretest": "standard", | ||
"test": "tape 'test/**/*.js'", | ||
"coverage": "c8 npm test" | ||
@@ -21,0 +22,0 @@ }, |
@@ -14,2 +14,6 @@ # `noise-protocol` | ||
This module only implements the `Noise_*_25519_ChaChaPoly_BLAKE2b` handshake, | ||
meaning `Curve25519` for DH, `ChaCha20Poly1305` for AEAD and `BLAKE2b` for | ||
hashing. | ||
## Usage | ||
@@ -23,3 +27,3 @@ | ||
// Initialize a Noise_KK_25519_XChaChaPoly_BLAKE2b handshake | ||
// Initialize a Noise_KK_25519_ChaChaPoly_BLAKE2b handshake | ||
var client = noise.initialize('KK', true, Buffer.alloc(0), sClient, null, sServer.publicKey) | ||
@@ -182,3 +186,3 @@ var server = noise.initialize('KK', false, Buffer.alloc(0), sServer, null, sClient.publicKey) | ||
functions or extract values and use with another transport encryption, as long | ||
as you are aware of the security implication of either choise. For initiator and | ||
as you are aware of the security implication of either choice. For initiator and | ||
responder, `tx` and `rx` are opposite so a responders `rx` is equal to an | ||
@@ -195,6 +199,2 @@ initiators `tx`. | ||
* Uses `libsodium`s `crypto_kx_*` API which hashes the shared secret with the | ||
client and server public key; `BLAKE2b-512(shared || client_pk || server_pk)` | ||
* Uses `crypto_aead_xchacha20poly1305_ietf_*` for symmetric cryptography with | ||
nonces `128-bit zero || 64-bit counter`, meaning the protocol name is `Noise_*_25519_XChaChaPoly_BLAKE2b`, with `*` being the handshake pattern | ||
* Functions follow the `fn(state, output, args...)` convention | ||
@@ -201,0 +201,0 @@ * Names the 16 bytes for an authentication tag as `MACLEN` |
@@ -10,5 +10,5 @@ var dh = require('../dh') | ||
test('generateKeypair', function (assert) { | ||
var kp1 = {sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN)} | ||
var kp2 = {sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN)} | ||
var kp3 = {sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN)} | ||
var kp1 = { sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN) } | ||
var kp2 = { sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN) } | ||
var kp3 = { sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN) } | ||
@@ -33,4 +33,4 @@ dh.generateKeypair(kp2.pk, kp2.sk) | ||
test('initiator / responder', function (assert) { | ||
var server = {sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN)} | ||
var client = {sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN)} | ||
var server = { sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN) } | ||
var client = { sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN) } | ||
@@ -43,4 +43,4 @@ dh.generateKeypair(server.pk, server.sk) | ||
dh.initiator(dhc, client.pk, client.sk, server.pk) | ||
dh.responder(dhs, server.pk, server.sk, client.pk) | ||
dh.dh(dhc, client.sk, server.pk) | ||
dh.dh(dhs, server.sk, client.pk) | ||
@@ -51,1 +51,36 @@ assert.ok(dhc.equals(dhs)) | ||
}) | ||
const badKeys = [ | ||
// Infinity | ||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
// Multiplicative Identity | ||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
// Order 8 | ||
[224, 235, 122, 124, 59, 65, 184, 174, 22, 86, 227, 250, 241, 159, 196, 106, 218, 9, 141, 235, 156, 50, 177, 253, 134, 98, 5, 22, 95, 73, 184, 0], | ||
// Order 8 | ||
[95, 156, 149, 188, 163, 80, 140, 36, 177, 208, 177, 85, 156, 131, 239, 91, 4, 68, 92, 196, 88, 28, 142, 134, 216, 34, 78, 221, 208, 159, 17, 87], | ||
// p - 1 (order 8) | ||
[236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], | ||
// p (same as Infinity) | ||
[237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], | ||
// 1 (Multiplicative identity) | ||
[238, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], | ||
// Order 8 | ||
[205, 235, 122, 124, 59, 65, 184, 174, 22, 86, 227, 250, 241, 159, 196, 106, 218, 9, 141, 235, 156, 50, 177, 253, 134, 98, 5, 22, 95, 73, 184, 128], | ||
[76, 156, 149, 188, 163, 80, 140, 36, 177, 208, 177, 85, 156, 131, 239, 91, 4, 68, 92, 196, 88, 28, 142, 134, 216, 34, 78, 221, 208, 159, 17, 215], | ||
[217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], | ||
[218, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], | ||
[219, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 25] | ||
] | ||
test('bad keys', function (assert) { | ||
var keypair = { sk: Buffer.alloc(dh.SKLEN), pk: Buffer.alloc(dh.PKLEN) } | ||
dh.generateKeypair(keypair.pk, keypair.sk) | ||
var dho = Buffer.alloc(dh.DHLEN) | ||
for (var i = 0; i < badKeys.length; i++) { | ||
assert.throws(() => dh.dh(dho, keypair.sk, badKeys[i])) | ||
} | ||
assert.end() | ||
}) |
@@ -1,2 +0,2 @@ | ||
var noise = require('../..') | ||
var noise = require('..') | ||
var test = require('tape') | ||
@@ -3,0 +3,0 @@ |
@@ -10,3 +10,3 @@ var hash = require('../hash') | ||
test('hash') | ||
test('hkdf') | ||
test.skip('hash') | ||
test.skip('hkdf') |
@@ -23,4 +23,6 @@ var noise = require('../..') | ||
assert.same(splitClient.rx, splitServer.tx) | ||
assert.notSame(splitServer.rx, splitServer.tx) | ||
assert.notSame(splitClient.rx, splitClient.tx) | ||
assert.end() | ||
}) |
48704
1034
+ Addedhmac-blake2b@1.0.0(transitive)
+ Addedsodium-native@3.4.1(transitive)
- Removedb4a@1.6.7(transitive)
- Removedblake2b@2.1.4(transitive)
- Removedblake2b-wasm@2.4.0(transitive)
- Removedhmac-blake2b@0.2.0(transitive)
- Removedini@1.3.8(transitive)
- Removednan@2.22.1(transitive)
- Removedsiphash24@1.3.1(transitive)
- Removedsodium-javascript@0.5.6(transitive)
- Removedsodium-native@2.4.9(transitive)
- Removedsodium-universal@2.0.0(transitive)
- Removedxsalsa20@1.2.0(transitive)
Updatedhmac-blake2b@^1.0.0
Updatednanoassert@^2.0.0
Updatedsodium-native@^3.1.1