Wire E2E messaging: send, recv, session persistence, auto-registration

CLI client (warzone):
- `warzone init` now generates pre-key bundle (1 SPK + 10 OTPKs),
  stores secrets in local sled DB, saves bundle for server registration
- `warzone register -s <url>` registers bundle with server
- `warzone send <fp> <msg> -s <url>` full E2E flow:
  - Auto-registers bundle on first use
  - Fetches recipient's pre-key bundle
  - Performs X3DH key exchange (first message) or uses existing session
  - Encrypts with Double Ratchet
  - Sends WireMessage envelope to server
- `warzone recv -s <url>` polls and decrypts:
  - Handles KeyExchange messages (X3DH respond + ratchet init as Bob)
  - Handles Message (decrypt with existing ratchet session)
  - Saves session state after each decrypt

Wire protocol (WireMessage enum):
- KeyExchange variant: sender identity, ephemeral key, OTPK id, ratchet msg
- Message variant: sender fingerprint + ratchet message

Session persistence:
- Ratchet state serialized with bincode, stored in sled (~/.warzone/db)
- Pre-key secrets stored in sled, OTPKs consumed on use
- Sessions keyed by peer fingerprint

Networking (net.rs):
- register_bundle, fetch_bundle, send_message, poll_messages
- JSON API over HTTP, bundles serialized with bincode + base64

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-03-26 21:40:21 +04:00
parent e364f437a2
commit 82f5061aa1
9 changed files with 527 additions and 9 deletions

View File

@@ -15,7 +15,7 @@ struct Cli {
#[derive(Subcommand)]
enum Commands {
/// Generate a new identity (seed + keypair)
/// Generate a new identity (seed + keypair + pre-keys)
Init,
/// Recover identity from BIP39 mnemonic
Recover {
@@ -25,6 +25,12 @@ enum Commands {
},
/// Show your fingerprint and public key
Info,
/// Register your key bundle with a server
Register {
/// Server URL
#[arg(short, long, default_value = "http://localhost:7700")]
server: String,
},
/// Send an encrypted message
Send {
/// Recipient fingerprint (e.g. a3f8:c912:44be:7d01)
@@ -49,22 +55,30 @@ enum Commands {
},
}
fn main() -> anyhow::Result<()> {
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Init => cli::init::run()?,
Commands::Recover { words } => cli::recover::run(&words.join(" "))?,
Commands::Info => cli::info::run()?,
Commands::Register { server } => {
cli::init::register_with_server(&server).await?;
}
Commands::Send {
recipient,
message,
server,
} => {
println!("TODO: send '{}' to {} via {}", message, recipient, server);
// Auto-register bundle on first send
if let Err(_) = cli::init::register_with_server(&server).await {
eprintln!("Warning: failed to register bundle with server");
}
cli::send::run(&recipient, &message, &server).await?;
}
Commands::Recv { server } => {
println!("TODO: poll messages from {}", server);
cli::recv::run(&server).await?;
}
Commands::Chat { server } => {
println!("TODO: launch TUI connected to {}", server);