noise-protocol
Advanced tools
Comparing version 0.1.1 to 0.2.0
var sodium = require('sodium-native') | ||
var assert = require('nanoassert') | ||
var clone = require('clone') | ||
var symmetricState = require('./symmetric-state') | ||
@@ -37,11 +38,21 @@ var cipherState = require('./cipher-state') | ||
// initiator, ->, true | ||
// responder, <-, false | ||
const INITIATOR = Symbol('initiator') | ||
const RESPONDER = Symbol('responder') | ||
const TOK_S = Symbol('s') | ||
const TOK_E = Symbol('e') | ||
const TOK_ES = Symbol('es') | ||
const TOK_SE = Symbol('se') | ||
const TOK_EE = Symbol('ee') | ||
const TOK_SS = Symbol('es') | ||
// initiator, -> | ||
// responder, <- | ||
var PATTERNS = Object.freeze({ | ||
N: { | ||
premessages: [ | ||
[false, 's'] | ||
[RESPONDER, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e', 'es'] | ||
[INITIATOR, TOK_E, TOK_ES] | ||
] | ||
@@ -51,7 +62,7 @@ }, | ||
premessages: [ | ||
[true, 's'], | ||
[false, 's'] | ||
[INITIATOR, TOK_S], | ||
[RESPONDER, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e', 'es', 'ss'] | ||
[INITIATOR, TOK_E, TOK_ES, TOK_SS] | ||
] | ||
@@ -61,6 +72,6 @@ }, | ||
premessages: [ | ||
[false, 's'] | ||
[RESPONDER, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e', 'es', 's', 'ss'] | ||
[INITIATOR, TOK_E, TOK_ES, TOK_S, TOK_SS] | ||
] | ||
@@ -71,4 +82,4 @@ }, | ||
messagePatterns: [ | ||
[true, 'e'], | ||
[false, 'e', 'ee'] | ||
[INITIATOR, TOK_E], | ||
[RESPONDER, TOK_E, TOK_EE] | ||
] | ||
@@ -78,7 +89,7 @@ }, | ||
premessages: [ | ||
[true, 's'] | ||
[INITIATOR, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e'], | ||
[false, 'e', 'ee', 'se'] | ||
[INITIATOR, TOK_E], | ||
[RESPONDER, TOK_E, TOK_EE, TOK_SE] | ||
] | ||
@@ -88,7 +99,7 @@ }, | ||
premessages: [ | ||
[false, 's'] | ||
[RESPONDER, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e', 'es'], | ||
[false, 'e', 'ee'] | ||
[INITIATOR, TOK_E, TOK_ES], | ||
[RESPONDER, TOK_E, TOK_EE] | ||
] | ||
@@ -98,8 +109,8 @@ }, | ||
premessages: [ | ||
[true, 's'], | ||
[false, 's'] | ||
[INITIATOR, TOK_S], | ||
[RESPONDER, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e', 'es', 'ss'], | ||
[false, 'e', 'ee', 'se'] | ||
[INITIATOR, TOK_E, TOK_ES, TOK_SS], | ||
[RESPONDER, TOK_E, TOK_EE, TOK_SE] | ||
] | ||
@@ -110,4 +121,4 @@ }, | ||
messagePatterns: [ | ||
[true, 'e'], | ||
[false, 'e', 'ee', 's', 'es'] | ||
[INITIATOR, TOK_E], | ||
[RESPONDER, TOK_E, TOK_EE, TOK_S, TOK_ES] | ||
] | ||
@@ -117,7 +128,7 @@ }, | ||
premessages: [ | ||
[true, 's'] | ||
[INITIATOR, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e'], | ||
[false, 'e', 'ee', 'se', 's', 'es'] | ||
[INITIATOR, TOK_E], | ||
[RESPONDER, TOK_E, TOK_EE, TOK_SE, TOK_S, TOK_ES] | ||
] | ||
@@ -128,5 +139,5 @@ }, | ||
messagePatterns: [ | ||
[true, 'e'], | ||
[false, 'e', 'ee'], | ||
[true, 's', 'se'] | ||
[INITIATOR, TOK_E], | ||
[RESPONDER, TOK_E, TOK_EE], | ||
[INITIATOR, TOK_S, TOK_SE] | ||
] | ||
@@ -137,4 +148,4 @@ }, | ||
messagePatterns: [ | ||
[true, 'e', 's'], | ||
[false, 'e', 'ee', 'se'] | ||
[INITIATOR, TOK_E, TOK_S], | ||
[RESPONDER, TOK_E, TOK_EE, TOK_SE] | ||
] | ||
@@ -144,8 +155,8 @@ }, | ||
premessages: [ | ||
[false, 's'] | ||
[RESPONDER, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e', 'es'], | ||
[false, 'e', 'ee'], | ||
[true, 's', 'se'] | ||
[INITIATOR, TOK_E, TOK_ES], | ||
[RESPONDER, TOK_E, TOK_EE], | ||
[INITIATOR, TOK_S, TOK_SE] | ||
] | ||
@@ -155,7 +166,7 @@ }, | ||
premessages: [ | ||
[false, 's'] | ||
[RESPONDER, TOK_S] | ||
], | ||
messagePatterns: [ | ||
[true, 'e', 'es', 's', 'ss'], | ||
[false, 'e', 'ee', 'se'] | ||
[INITIATOR, TOK_E, TOK_ES, TOK_S, TOK_SS], | ||
[RESPONDER, TOK_E, TOK_EE, TOK_SE] | ||
] | ||
@@ -166,5 +177,5 @@ }, | ||
messagePatterns: [ | ||
[true, 'e'], | ||
[false, 'e', 'ee', 's', 'es'], | ||
[true, 's', 'se'] | ||
[INITIATOR, TOK_E], | ||
[RESPONDER, TOK_E, TOK_EE, TOK_S, TOK_ES], | ||
[INITIATOR, TOK_S, TOK_SE] | ||
] | ||
@@ -175,4 +186,4 @@ }, | ||
messagePatterns: [ | ||
[true, 'e', 's'], | ||
[false, 'e', 'ee', 'se', 's', 'es'] | ||
[INITIATOR, TOK_E, TOK_S], | ||
[RESPONDER, TOK_E, TOK_EE, TOK_SE, TOK_S, TOK_ES] | ||
] | ||
@@ -182,2 +193,8 @@ } | ||
function sodiumBufferCopy (src) { | ||
var buf = sodium.sodium_malloc(src.byteLength) | ||
buf.set(src) | ||
return buf | ||
} | ||
function initialize (handshakePattern, initiator, prologue, s, e, rs, re) { | ||
@@ -204,16 +221,28 @@ assert(Object.keys(PATTERNS).includes(handshakePattern)) | ||
state.initiator = initiator | ||
state.role = initiator === true ? INITIATOR : RESPONDER | ||
if (s != null) { | ||
state.spk = s.publicKey | ||
state.ssk = s.secretKey | ||
assert(s.publicKey.byteLength === dh.PKLEN) | ||
assert(s.secretKey.byteLength === dh.SKLEN) | ||
state.spk = sodiumBufferCopy(s.publicKey) | ||
state.ssk = sodiumBufferCopy(s.secretKey) | ||
} | ||
if (e != null) { | ||
state.epk = e.publicKey | ||
state.esk = e.secretKey | ||
assert(e.publicKey.byteLength === dh.PKLEN) | ||
assert(e.secretKey.byteLength === dh.SKLEN) | ||
state.epk = sodiumBufferCopy(e.publicKey) | ||
state.esk = sodiumBufferCopy(e.secretKey) | ||
} | ||
if (rs != null) state.rs = rs | ||
if (re != null) state.re = re | ||
if (rs != null) { | ||
assert(rs.byteLength === dh.PKLEN) | ||
state.rs = sodiumBufferCopy(rs) | ||
} | ||
if (re != null) { | ||
assert(re.byteLength === dh.PKLEN) | ||
state.re = sodiumBufferCopy(re) | ||
} | ||
@@ -224,13 +253,13 @@ // hashing | ||
for (var pattern of clone(pat.premessages)) { | ||
var initiatorPattern = pattern.shift() | ||
var patternRole = pattern.shift() | ||
for (var token of pattern) { | ||
switch (token) { | ||
case 'e': | ||
assert(state.initiator === initiatorPattern ? state.epk.byteLength != null : state.re.byteLength != null) | ||
symmetricState.mixHash(state.symmetricState, state.initiator === initiatorPattern ? state.epk : state.re) | ||
case TOK_E: | ||
assert(state.role === patternRole ? state.epk.byteLength != null : state.re.byteLength != null) | ||
symmetricState.mixHash(state.symmetricState, state.role === patternRole ? state.epk : state.re) | ||
break | ||
case 's': | ||
assert(state.initiator === initiatorPattern ? state.spk.byteLength != null : state.rs.byteLength != null) | ||
symmetricState.mixHash(state.symmetricState, state.initiator === initiatorPattern ? state.spk : state.rs) | ||
case TOK_S: | ||
assert(state.role === patternRole ? state.spk.byteLength != null : state.rs.byteLength != null) | ||
symmetricState.mixHash(state.symmetricState, state.role === patternRole ? state.spk : state.rs) | ||
break | ||
@@ -245,2 +274,7 @@ default: | ||
assert(state.messagePatterns.filter(p => p[0] === INITIATOR).some(p => p.includes(TOK_S)) | ||
? (state.spk !== null && state.ssk !== null) | ||
: true, // Default if none is found | ||
'This handshake pattern requires a static keypair') | ||
return state | ||
@@ -259,7 +293,8 @@ } | ||
assert(mpat != null) | ||
assert(mpat.shift() === state.initiator) | ||
assert(state.role === mpat.shift()) | ||
for (var token of mpat) { | ||
switch (token) { | ||
case 'e': | ||
case TOK_E: | ||
assert(state.epk == null) | ||
@@ -280,3 +315,3 @@ assert(state.esk == null) | ||
case 's': | ||
case TOK_S: | ||
assert(state.spk.byteLength === dh.PKLEN) | ||
@@ -289,9 +324,9 @@ | ||
case 'ee': | ||
dh[state.initiator ? 'initiator' : 'responder'](DhResult, state.epk, state.esk, state.re) | ||
case TOK_EE: | ||
dh[state.role === INITIATOR ? 'initiator' : 'responder'](DhResult, state.epk, state.esk, state.re) | ||
symmetricState.mixKey(state.symmetricState, DhResult) | ||
sodium.sodium_memzero(DhResult) | ||
break | ||
case 'es': | ||
if (state.initiator) dh.initiator(DhResult, state.epk, state.esk, state.rs) | ||
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) | ||
@@ -302,4 +337,4 @@ | ||
break | ||
case 'se': | ||
if (state.initiator) dh.initiator(DhResult, state.spk, state.ssk, state.re) | ||
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) | ||
@@ -310,4 +345,4 @@ | ||
break | ||
case 'ss': | ||
dh[state.initiator ? 'initiator' : 'responder'](DhResult, state.spk, state.ssk, state.rs) | ||
case TOK_SS: | ||
dh[state.role === INITIATOR ? 'initiator' : 'responder'](DhResult, state.spk, state.ssk, state.rs) | ||
@@ -347,7 +382,7 @@ symmetricState.mixKey(state.symmetricState, DhResult) | ||
assert(mpat != null) | ||
assert(mpat.shift() === !state.initiator) | ||
assert(mpat.shift() !== state.role) | ||
for (var token of mpat) { | ||
switch (token) { | ||
case 'e': | ||
case TOK_E: | ||
assert(state.re == null) | ||
@@ -365,3 +400,3 @@ assert(message.byteLength - moffset >= dh.PKLEN) | ||
case 's': | ||
case TOK_S: | ||
assert(state.rs == null) | ||
@@ -388,9 +423,9 @@ state.rs = sodium.sodium_malloc(dh.PKLEN) | ||
break | ||
case 'ee': | ||
dh[state.initiator ? 'initiator' : 'responder'](DhResult, state.epk, state.esk, state.re) | ||
case TOK_EE: | ||
dh[state.role === INITIATOR ? 'initiator' : 'responder'](DhResult, state.epk, state.esk, state.re) | ||
symmetricState.mixKey(state.symmetricState, DhResult) | ||
sodium.sodium_memzero(DhResult) | ||
break | ||
case 'es': | ||
if (state.initiator) dh.initiator(DhResult, state.epk, state.esk, state.rs) | ||
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) | ||
@@ -401,4 +436,4 @@ | ||
break | ||
case 'se': | ||
if (state.initiator) dh.initiator(DhResult, state.spk, state.ssk, state.re) | ||
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) | ||
@@ -409,4 +444,4 @@ | ||
break | ||
case 'ss': | ||
dh[state.initiator ? 'initiator' : 'responder'](DhResult, state.spk, state.ssk, state.rs) | ||
case TOK_SS: | ||
dh[state.role === INITIATOR ? 'initiator' : 'responder'](DhResult, state.spk, state.ssk, state.rs) | ||
@@ -443,3 +478,3 @@ symmetricState.mixKey(state.symmetricState, DhResult) | ||
state.initiator = null | ||
state.role = null | ||
@@ -492,5 +527,1 @@ if (state.spk != null) { | ||
} | ||
function clone (o) { // Good enough for now | ||
return JSON.parse(JSON.stringify(o)) | ||
} |
{ | ||
"name": "noise-protocol", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "Javascript implementation of the Noise Protocol Framework based on libsodium", | ||
"main": "index.js", | ||
"dependencies": { | ||
"clone": "^2.1.2", | ||
"hmac-blake2b": "^0.2.0", | ||
@@ -12,2 +13,3 @@ "nanoassert": "^1.1.0", | ||
"devDependencies": { | ||
"c8": "^3.2.0", | ||
"standard": "^11.0.1", | ||
@@ -17,3 +19,4 @@ "tape": "^4.9.0" | ||
"scripts": { | ||
"test": "tape test/**/*" | ||
"test": "tape test/**/*", | ||
"coverage": "c8 npm test" | ||
}, | ||
@@ -20,0 +23,0 @@ "repository": { |
@@ -91,2 +91,6 @@ # `noise-protocol` | ||
:alert: Key material passed in is copied into libsodium Secure Buffers, which | ||
can be cleared with `noise.destroy(state)`. Be aware that you manually have to | ||
destroy this state object, unless you want to rely on GC clearing it for you. | ||
Returns a `HandshakeState` object, which should be treated as an opaque object. | ||
@@ -93,0 +97,0 @@ This state is passed as the first argument to subsequent `noise` functions. |
@@ -40,6 +40,3 @@ var sodium = require('sodium-native') | ||
ciphertext[i] ^= i + 1 | ||
try { | ||
cipher.decrypt(decrypted, key, nonce, null, ciphertext) | ||
assert.fail() | ||
} catch (_) {} | ||
assert.throws(_ => cipher.decrypt(decrypted, key, nonce, null, ciphertext)) | ||
ciphertext[i] ^= i + 1 | ||
@@ -79,6 +76,3 @@ } | ||
ciphertext[i] ^= 255 | ||
try { | ||
cipher.decrypt(decrypted, key, nonce, ad, ciphertext) | ||
assert.fail() | ||
} catch (_) {} | ||
assert.throws(_ => cipher.decrypt(decrypted, key, nonce, ad, ciphertext)) | ||
ciphertext[i] ^= 255 | ||
@@ -85,0 +79,0 @@ } |
Sorry, the diff of this file is not supported yet
723100
24
1006
207
4
3
+ Addedclone@^2.1.2
+ Addedclone@2.1.2(transitive)