Work on rekeying some more

This commit is contained in:
Starbeamrainbowlabs 2021-10-02 17:34:15 +01:00
parent bbc5d54158
commit 93ff823827
Signed by: sbrl
GPG Key ID: 1BE5172E637709C2
2 changed files with 45 additions and 14 deletions

View File

@ -1,5 +1,6 @@
"use strict";
import crypto from 'crypto';
import net from 'net';
import { EventEmitter, once } from 'events';
@ -21,7 +22,10 @@ class Connection extends EventEmitter {
this.socket = socket;
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");
}
@ -54,8 +58,20 @@ class Connection extends EventEmitter {
}
async rekey() {
this.rekey_last = new Date();
this.session_key = await rekey(this.socket, this.session_key);
try {
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() {
@ -79,15 +95,25 @@ class Connection extends EventEmitter {
async handle_message(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) {
if(new Date() - this.rekey_last > this.rekey_interval)
async send(event, message) {
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();
// 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
let payload = JSON.stringify(message);
let payload = JSON.stringify({ event, message });
payload = encrypt_bytes(this.session_key, payload);

View File

@ -10,20 +10,25 @@ export default async function rekey(connection, secret_join) {
let jpake = new JPake(secret_join);
// 1: Round 1
connection.send("jpake-rekey-r1", jpake.GetRound1Message());
connection.send("rekey", { round: 1, content: jpake.GetRound1Message() });
// 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);
if(typeof our_round2 !== "string") return null;
let [ event, their_round1 ] = (await once(connection, "message-rekey"));
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
const their_round2 = (await once(connection, "message"))[0];
const their_round2 = (await once(connection, "message-rekey"))[0];
if(typeof their_round2 !== "string") return null;
const new_shared_key = jpake.ComputeSharedKey(their_round2);
if(typeof new_shared_key !== "string") return null;