noise-handshake
Version:
Noise protocol handshake
91 lines (72 loc) • 2.21 kB
JavaScript
const sodium = require('sodium-universal')
const assert = require('nanoassert')
const b4a = require('b4a')
const CipherState = require('./cipher')
const curve = require('./dh')
const { HASHLEN, hkdf } = require('./hkdf')
module.exports = class SymmetricState extends CipherState {
constructor (opts = {}) {
super()
this.curve = opts.curve || curve
this.digest = b4a.alloc(HASHLEN)
this.chainingKey = null
this.offset = 0
this.DH_ALG = this.curve.ALG
}
mixHash (data) {
accumulateDigest(this.digest, data)
}
mixKeyAndHash (key) {
const [ck, tempH, tempK] = hkdf(this.chainingKey, key, '', 3 * HASHLEN)
this.chainingKey = ck
this.mixHash(tempH)
this.initialiseKey(tempK.subarray(0, 32))
}
mixKeyNormal (key) {
const [ck, tempK] = hkdf(this.chainingKey, key)
this.chainingKey = ck
this.initialiseKey(tempK.subarray(0, 32))
}
mixKey (remoteKey, localKey) {
const dh = this.curve.dh(remoteKey, localKey)
const hkdfResult = hkdf(this.chainingKey, dh)
this.chainingKey = hkdfResult[0]
this.initialiseKey(hkdfResult[1].subarray(0, 32))
}
encryptAndHash (plaintext) {
const ciphertext = this.encrypt(plaintext, this.digest)
accumulateDigest(this.digest, ciphertext)
return ciphertext
}
decryptAndHash (ciphertext) {
const plaintext = this.decrypt(ciphertext, this.digest)
accumulateDigest(this.digest, ciphertext)
return plaintext
}
getHandshakeHash (out) {
if (!out) return this.getHandshakeHash(b4a.alloc(HASHLEN))
assert(out.byteLength === HASHLEN, `output must be ${HASHLEN} bytes`)
out.set(this.digest)
return out
}
split () {
const res = hkdf(this.chainingKey, b4a.alloc(0))
return res.map(k => k.subarray(0, 32))
}
_clear () {
super._clear()
sodium.sodium_memzero(this.digest)
sodium.sodium_memzero(this.chainingKey)
this.digest = null
this.chainingKey = null
this.offset = null
this.curve = null
}
static get alg () {
return CipherState.alg + '_BLAKE2b'
}
}
function accumulateDigest (digest, input) {
const toHash = b4a.concat([digest, input])
sodium.crypto_generichash(digest, toHash)
}