chore: rename quicproquo → quicprochat in Rust workspace

Rename all crate directories, package names, binary names, proto
package/module paths, ALPN strings, env var prefixes, config filenames,
mDNS service names, and plugin ABI symbols from quicproquo/qpq to
quicprochat/qpc.
This commit is contained in:
2026-03-07 18:24:52 +01:00
parent d8c1392587
commit a710037dde
212 changed files with 609 additions and 609 deletions

View File

@@ -0,0 +1,255 @@
//! OPAQUE auth handlers — registration and login.
use std::sync::Arc;
use bytes::Bytes;
use prost::Message;
use quicprochat_proto::qpc::v1;
use quicprochat_rpc::error::RpcStatus;
use quicprochat_rpc::method::{HandlerResult, RequestContext};
use crate::auth::{PendingLogin, SessionInfo, SESSION_TTL_SECS};
use crate::domain::auth::AuthService;
use crate::domain::types::{RegisterFinishReq, RegisterStartReq};
use super::ServerState;
pub async fn handle_opaque_register_start(
state: Arc<ServerState>,
ctx: RequestContext,
) -> HandlerResult {
let req = match v1::OpaqueRegisterStartRequest::decode(ctx.payload) {
Ok(r) => r,
Err(e) => return HandlerResult::err(RpcStatus::BadRequest, &format!("decode: {e}")),
};
if req.username.is_empty() {
return HandlerResult::err(RpcStatus::BadRequest, "username must not be empty");
}
let svc = AuthService {
store: Arc::clone(&state.store),
opaque_setup: Arc::clone(&state.opaque_setup),
pending_logins: Arc::clone(&state.pending_logins),
sessions: Arc::clone(&state.sessions),
auth_cfg: Arc::clone(&state.auth_cfg),
};
let domain_req = RegisterStartReq {
username: req.username,
request_bytes: req.request,
};
match svc.register_start(domain_req) {
Ok(resp) => {
let proto = v1::OpaqueRegisterStartResponse {
response: resp.response_bytes,
};
HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}
Err(e) => HandlerResult::err(RpcStatus::Internal, &format!("register_start: {e}")),
}
}
pub async fn handle_opaque_register_finish(
state: Arc<ServerState>,
ctx: RequestContext,
) -> HandlerResult {
let req = match v1::OpaqueRegisterFinishRequest::decode(ctx.payload) {
Ok(r) => r,
Err(e) => return HandlerResult::err(RpcStatus::BadRequest, &format!("decode: {e}")),
};
if req.username.is_empty() {
return HandlerResult::err(RpcStatus::BadRequest, "username must not be empty");
}
let svc = AuthService {
store: Arc::clone(&state.store),
opaque_setup: Arc::clone(&state.opaque_setup),
pending_logins: Arc::clone(&state.pending_logins),
sessions: Arc::clone(&state.sessions),
auth_cfg: Arc::clone(&state.auth_cfg),
};
let domain_req = RegisterFinishReq {
username: req.username.clone(),
upload_bytes: req.upload,
identity_key: req.identity_key.clone(),
};
match svc.register_finish(domain_req) {
Ok(resp) => {
state
.hooks
.on_user_registered(&req.username, &req.identity_key);
let proto = v1::OpaqueRegisterFinishResponse {
success: resp.success,
};
HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}
Err(e) => HandlerResult::err(RpcStatus::Internal, &format!("register_finish: {e}")),
}
}
pub async fn handle_opaque_login_start(
state: Arc<ServerState>,
ctx: RequestContext,
) -> HandlerResult {
let req = match v1::OpaqueLoginStartRequest::decode(ctx.payload) {
Ok(r) => r,
Err(e) => return HandlerResult::err(RpcStatus::BadRequest, &format!("decode: {e}")),
};
if req.username.is_empty() {
return HandlerResult::err(RpcStatus::BadRequest, "username must not be empty");
}
// Look up user record.
let user_record = match state.store.get_user_record(&req.username) {
Ok(Some(r)) => r,
Ok(None) => {
return HandlerResult::err(RpcStatus::NotFound, "user not found");
}
Err(e) => return HandlerResult::err(RpcStatus::Internal, &format!("store: {e}")),
};
// Deserialise stored registration.
let registration =
match opaque_ke::ServerRegistration::<quicprochat_core::opaque_auth::OpaqueSuite>::deserialize(&user_record) {
Ok(r) => r,
Err(e) => {
return HandlerResult::err(
RpcStatus::Internal,
&format!("corrupt user record: {e}"),
)
}
};
// Start login.
let credential_request =
match opaque_ke::CredentialRequest::<quicprochat_core::opaque_auth::OpaqueSuite>::deserialize(&req.request)
{
Ok(r) => r,
Err(e) => {
return HandlerResult::err(RpcStatus::BadRequest, &format!("bad login request: {e}"))
}
};
let login_start = match opaque_ke::ServerLogin::<
quicprochat_core::opaque_auth::OpaqueSuite,
>::start(
&mut rand::rngs::OsRng,
&state.opaque_setup,
Some(registration),
credential_request,
req.username.as_bytes(),
Default::default(),
) {
Ok(r) => r,
Err(e) => {
return HandlerResult::err(RpcStatus::Internal, &format!("login start: {e}"));
}
};
let response_bytes = login_start.message.serialize().to_vec();
// Store pending login state.
let now = crate::auth::current_timestamp();
state.pending_logins.insert(
req.username.clone(),
PendingLogin {
state_bytes: login_start.state.serialize().to_vec(),
created_at: now,
},
);
let proto = v1::OpaqueLoginStartResponse {
response: response_bytes,
};
HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}
pub async fn handle_opaque_login_finish(
state: Arc<ServerState>,
ctx: RequestContext,
) -> HandlerResult {
let req = match v1::OpaqueLoginFinishRequest::decode(ctx.payload) {
Ok(r) => r,
Err(e) => return HandlerResult::err(RpcStatus::BadRequest, &format!("decode: {e}")),
};
if req.username.is_empty() {
return HandlerResult::err(RpcStatus::BadRequest, "username must not be empty");
}
// Retrieve pending login state.
let pending = match state.pending_logins.remove(&req.username) {
Some((_, p)) => p,
None => {
return HandlerResult::err(
RpcStatus::BadRequest,
"no pending login for this username",
);
}
};
let login_state = match opaque_ke::ServerLogin::<
quicprochat_core::opaque_auth::OpaqueSuite,
>::deserialize(&pending.state_bytes)
{
Ok(s) => s,
Err(e) => {
return HandlerResult::err(
RpcStatus::Internal,
&format!("corrupt pending login: {e}"),
)
}
};
let finalization = match opaque_ke::CredentialFinalization::<
quicprochat_core::opaque_auth::OpaqueSuite,
>::deserialize(&req.finalization)
{
Ok(f) => f,
Err(e) => {
return HandlerResult::err(RpcStatus::BadRequest, &format!("bad finalization: {e}"));
}
};
if let Err(e) = login_state.finish(finalization, Default::default()) {
state.hooks.on_auth(&crate::hooks::AuthEvent {
username: req.username.clone(),
success: false,
failure_reason: format!("{e}"),
});
return HandlerResult::err(RpcStatus::Unauthorized, &format!("login failed: {e}"));
}
// Generate session token.
let mut token = vec![0u8; 32];
rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut token);
let now = crate::auth::current_timestamp();
state.sessions.insert(
token.clone(),
SessionInfo {
username: req.username.clone(),
identity_key: req.identity_key.clone(),
created_at: now,
expires_at: now + SESSION_TTL_SECS,
},
);
state.hooks.on_auth(&crate::hooks::AuthEvent {
username: req.username,
success: true,
failure_reason: String::new(),
});
let proto = v1::OpaqueLoginFinishResponse {
session_token: token,
};
HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}