Protocol (sender_keys.rs): - SenderKey: symmetric key with chain ratchet (forward secrecy per chain) - generate(), rotate(), encrypt(), decrypt() - SenderKeyDistribution: share key via 1:1 encrypted channel - SenderKeyMessage: encrypted group message (O(1) instead of O(N)) - Chain key ratchets forward on each message (HKDF) - Generation counter for key rotation tracking - 4 tests: basic, multi-message, rotation, old-key rejection WireMessage: - GroupSenderKey variant: encrypted group message - SenderKeyDistribution variant: key sharing Server: dedup handles new variants. CLI TUI + recv: stub handlers for new message types. 23/23 protocol tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
105 lines
3.0 KiB
Rust
105 lines
3.0 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::ratchet::RatchetHeader;
|
|
use crate::types::{Fingerprint, MessageId, SessionId};
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub enum MessageType {
|
|
Text,
|
|
File,
|
|
KeyExchange,
|
|
Receipt,
|
|
}
|
|
|
|
/// An encrypted message on the wire.
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub struct WarzoneMessage {
|
|
pub version: u8,
|
|
pub id: MessageId,
|
|
pub from: Fingerprint,
|
|
pub to: Fingerprint,
|
|
pub timestamp: i64,
|
|
pub msg_type: MessageType,
|
|
pub session_id: SessionId,
|
|
pub ratchet_header: RatchetHeader,
|
|
pub ciphertext: Vec<u8>,
|
|
pub signature: Vec<u8>,
|
|
}
|
|
|
|
/// Plaintext message content (inside the encrypted envelope).
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub enum MessageContent {
|
|
Text { body: String },
|
|
File { filename: String, data: Vec<u8> },
|
|
Receipt { message_id: MessageId },
|
|
}
|
|
|
|
/// Receipt type: delivered (received + decrypted) or read (user viewed).
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
pub enum ReceiptType {
|
|
Delivered,
|
|
Read,
|
|
}
|
|
|
|
/// Wire message format for transport between clients.
|
|
/// Used by both CLI and WASM — MUST be identical for interop.
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub enum WireMessage {
|
|
/// First message to a peer: X3DH key exchange + first ratchet message.
|
|
KeyExchange {
|
|
id: String,
|
|
sender_fingerprint: String,
|
|
sender_identity_encryption_key: [u8; 32],
|
|
ephemeral_public: [u8; 32],
|
|
used_one_time_pre_key_id: Option<u32>,
|
|
ratchet_message: crate::ratchet::RatchetMessage,
|
|
},
|
|
/// Subsequent messages: ratchet-encrypted.
|
|
Message {
|
|
id: String,
|
|
sender_fingerprint: String,
|
|
ratchet_message: crate::ratchet::RatchetMessage,
|
|
},
|
|
/// Delivery / read receipt (plaintext, not encrypted).
|
|
Receipt {
|
|
sender_fingerprint: String,
|
|
message_id: String,
|
|
receipt_type: ReceiptType,
|
|
},
|
|
/// File transfer header: announces an incoming chunked file.
|
|
FileHeader {
|
|
id: String,
|
|
sender_fingerprint: String,
|
|
filename: String,
|
|
file_size: u64,
|
|
total_chunks: u32,
|
|
sha256: String,
|
|
},
|
|
/// A single chunk of a file transfer (data is ratchet-encrypted).
|
|
FileChunk {
|
|
id: String,
|
|
sender_fingerprint: String,
|
|
filename: String,
|
|
chunk_index: u32,
|
|
total_chunks: u32,
|
|
data: Vec<u8>,
|
|
},
|
|
/// Group message encrypted with sender key (O(1) instead of O(N)).
|
|
GroupSenderKey {
|
|
id: String,
|
|
sender_fingerprint: String,
|
|
group_name: String,
|
|
generation: u32,
|
|
counter: u32,
|
|
ciphertext: Vec<u8>,
|
|
},
|
|
/// Sender key distribution: share your sender key with a group member.
|
|
/// This is sent via 1:1 encrypted channel (wrapped in KeyExchange/Message).
|
|
SenderKeyDistribution {
|
|
sender_fingerprint: String,
|
|
group_name: String,
|
|
chain_key: [u8; 32],
|
|
generation: u32,
|
|
},
|
|
}
|