Derive registration credentials off-thread; rate-limit REGISTER; constant-time SCRAM compare

This commit is contained in:
Jean Chevronnet 2026-07-12 02:31:52 +00:00
parent 496c0f99a6
commit 9a918839fe
No known key found for this signature in database
10 changed files with 214 additions and 36 deletions

View file

@ -2,8 +2,9 @@ use anyhow::Result;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpStream;
use crate::engine::db::Db;
use crate::engine::Engine;
use crate::proto::Protocol;
use crate::proto::{NetAction, Protocol};
// One uplink session: connect, handshake + burst, then translate lines forever.
pub async fn run(mut proto: Box<dyn Protocol>, mut engine: Engine, addr: &str) -> Result<()> {
@ -24,8 +25,29 @@ pub async fn run(mut proto: Box<dyn Protocol>, mut engine: Engine, addr: &str) -
tracing::debug!(dir = "<<", %line);
for event in proto.parse(&line) {
for action in engine.handle(event) {
for out in proto.serialize(&action) {
send(&mut write, &out).await?;
let outs = match action {
// Registration: derive the password off the reactor so the
// ~1s of key stretching can't stall the link, after a cheap
// gate that rejects taken names and rate-limits floods.
NetAction::DeferRegister { account, password, email, reply } => {
match engine.pre_register_check(&account, &reply) {
Some(rejection) => rejection,
None => {
let iterations = engine.scram_iterations();
let creds = tokio::task::spawn_blocking(move || {
Db::derive_credentials(&password, iterations)
})
.await?;
engine.complete_register(&account, creds, email, reply)
}
}
}
action => vec![action],
};
for act in outs {
for out in proto.serialize(&act) {
send(&mut write, &out).await?;
}
}
}
}