Work on rekeying some more
This commit is contained in:
parent
bbc5d54158
commit
93ff823827
2 changed files with 45 additions and 14 deletions
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import crypto from 'crypto';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import { EventEmitter, once } from 'events';
|
import { EventEmitter, once } from 'events';
|
||||||
|
|
||||||
|
@ -21,7 +22,10 @@ class Connection extends EventEmitter {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
|
|
||||||
this.rekey_last = null;
|
this.rekey_last = null;
|
||||||
this.rekey_interval = 30 * 60 * 1000; // 30 minutes
|
this.rekey_interval_base = 30 * 60 * 1000; // 30 minutes
|
||||||
|
this.rekey_interval = this.rekey_interval_base + crypto.randomInt(0, 15 * 60 * 1000);
|
||||||
|
this.rekey_in_progress = false;
|
||||||
|
|
||||||
this.session_key = Buffer.from(secret_join, "base64");
|
this.session_key = Buffer.from(secret_join, "base64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,8 +58,20 @@ class Connection extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async rekey() {
|
async rekey() {
|
||||||
this.rekey_last = new Date();
|
try {
|
||||||
this.session_key = await rekey(this.socket, this.session_key);
|
this.rekey_in_progress = true;
|
||||||
|
this.session_key = await rekey(this, this.session_key);
|
||||||
|
this.rekey_interval = this.rekey_interval_base + crypto.randomInt(0, 15 * 60 * 1000);
|
||||||
|
this.rekey_last = new Date();
|
||||||
|
this.emit("rekey");
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
l.warn(`Error when rekeying connection ${this.address}:${this.port}: ${settings.cli.verbose ? error : error.message}, killing connection`);
|
||||||
|
await this.destroy();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.rekey_in_progress = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async destroy() {
|
async destroy() {
|
||||||
|
@ -79,15 +95,25 @@ class Connection extends EventEmitter {
|
||||||
|
|
||||||
async handle_message(msg_text) {
|
async handle_message(msg_text) {
|
||||||
const msg = JSON.parse(msg_text);
|
const msg = JSON.parse(msg_text);
|
||||||
this.emit("message", msg);
|
|
||||||
|
if(msg.event == "rekey") {
|
||||||
|
// Set and forget here
|
||||||
|
if(!this.rekey_in_progress) this.rekey();
|
||||||
|
}
|
||||||
|
this.emit("message", msg.event, msg.message);
|
||||||
|
this.emit(`message-${msg.event}`, msg.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(message) {
|
async send(event, message) {
|
||||||
if(new Date() - this.rekey_last > this.rekey_interval)
|
if(typeof event !== "string") throw new Error(`Error: Expected string for event name, but got value of type ${typeof event}.`);
|
||||||
|
|
||||||
|
// Rekey at semi-regular intervals, but only if we're not already in the process of doing so
|
||||||
|
if(new Date() - this.rekey_last > this.rekey_interval && !this.rekey_in_progress)
|
||||||
await this.rekey();
|
await this.rekey();
|
||||||
|
|
||||||
|
// TODO: Consider anonymous TLS, with jpake for mututal authentication
|
||||||
// TODO: Consider https://devdocs.io/node/crypto#crypto.createCipheriv() - which lets us use any openssl ciphers we like - e.g. ChaCha20-Poly1305
|
// TODO: Consider https://devdocs.io/node/crypto#crypto.createCipheriv() - which lets us use any openssl ciphers we like - e.g. ChaCha20-Poly1305
|
||||||
let payload = JSON.stringify(message);
|
let payload = JSON.stringify({ event, message });
|
||||||
payload = encrypt_bytes(this.session_key, payload);
|
payload = encrypt_bytes(this.session_key, payload);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,20 +10,25 @@ export default async function rekey(connection, secret_join) {
|
||||||
let jpake = new JPake(secret_join);
|
let jpake = new JPake(secret_join);
|
||||||
|
|
||||||
// 1: Round 1
|
// 1: Round 1
|
||||||
connection.send("jpake-rekey-r1", jpake.GetRound1Message());
|
connection.send("rekey", { round: 1, content: jpake.GetRound1Message() });
|
||||||
|
|
||||||
// 2: Round 2
|
// 2: Round 2
|
||||||
const their_round1 = (await once(connection, "message"))[0];
|
|
||||||
if(typeof their_round1 !== "string") return null;
|
|
||||||
|
|
||||||
const our_round2 = jpake.GetRound2Message(their_round1);
|
let [ event, their_round1 ] = (await once(connection, "message-rekey"));
|
||||||
if(typeof our_round2 !== "string") return null;
|
|
||||||
|
|
||||||
connection.send("jpake-rekey-r2", our_round2);
|
if(typeof their_round1 !== "object"
|
||||||
|
|| event !== "rekey"
|
||||||
|
|| their_round1.round !== 1
|
||||||
|
|| typeof their_round1.content !== "string")
|
||||||
|
throw new Error(`Error: Received invalid round 1 from peer`);
|
||||||
|
|
||||||
|
const our_round2 = jpake.GetRound2Message(their_round1.content);
|
||||||
|
if(typeof our_round2 !== "string") throw new Error(`Error: Failed to compute rekey round 2`);
|
||||||
|
|
||||||
|
connection.send("rekey", { round: 2, content: our_round2 });
|
||||||
|
|
||||||
// 3: Compute new shared key
|
// 3: Compute new shared key
|
||||||
const their_round2 = (await once(connection, "message"))[0];
|
const their_round2 = (await once(connection, "message-rekey"))[0];
|
||||||
if(typeof their_round2 !== "string") return null;
|
if(typeof their_round2 !== "string") return null;
|
||||||
const new_shared_key = jpake.ComputeSharedKey(their_round2);
|
const new_shared_key = jpake.ComputeSharedKey(their_round2);
|
||||||
if(typeof new_shared_key !== "string") return null;
|
if(typeof new_shared_key !== "string") return null;
|
||||||
|
|
Loading…
Reference in a new issue