UNPKG

noise-handshake

Version:
91 lines (72 loc) 2.21 kB
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) }