pub struct SignedRoomMessage {
pub fingerprint: String,
pub ed25519_pubkey_b64: String,
pub payload_b64: String,
pub signature_b64: String,
pub signed_at_ms: i64,
pub mldsa_pubkey_b64: Option<String>,
pub mldsa_signature_b64: Option<String>,
}Expand description
Application-level signed envelope around a RoomMessage. Used for
any message whose authenticity matters beyond gossipsub’s transport-
level signing.
The following variants MUST be sent inside a Signed envelope, and
receivers MUST drop them when they arrive unsigned:
MemberLeave(signer must equal the claimedsender_fingerprint; huddle 0.7.11 — closes the unsigned-leave spoof bug)MemberAnnounce(signer must equal the claimedsender_fingerprint; huddle 0.7.11 — closes the TOFU-pubkey hijack bug)FileOffer(signer must equal the claimedsender_fingerprint; huddle 0.7.11 — prevents attribution spoofing)RotateRoomKey(signer must equal the claimedrotator_fingerprint)OwnerGrant,BanMember(signer must be a current room owner)SasInit,SasResponse,SasConfirm(SAS handshake — signature binds the ephemeral X25519 pubkey to the sender’s identity)CodeJoinRequest,CodeJoinResponse(signer is the joiner / owner)JoinRefused(signer is a room owner; tells the rejected joiner it really came from the room)ProfileUpdate(signer must equal the claimedsender_fingerprint; prevents anyone from spoofing another peer’s username)ContactRequest(signer must equal the claimedrequester_fingerprint; huddle 1.0 — the signature proves who’s asking to connect)Reaction,Edit,Delete(huddle 2.0 / F10 — signer must equal the claimedsender_fingerprint; edits & deletes are additionally applied only when the signer is the original sender OR a current room owner)RoomSetting(huddle 2.0 / F9 — disappearing-messages TTL; the signer must be the room creator or a current owner)
Verification happens via crate::crypto::verify_signed: it re-derives
the fingerprint from ed25519_pubkey_b64, asserts equality with
fingerprint, runs Ed25519::verify_strict over the decoded
payload_b64, and rejects envelopes whose signed_at_ms falls
outside a ±5 min window from now (replay protection).
Format choice: payload is base64’d serialized RoomMessage JSON
(not the JSON bytes directly) so the envelope itself is plain JSON
without escaping nightmares.
Fields§
§fingerprint: String§ed25519_pubkey_b64: String§payload_b64: String§signature_b64: String§signed_at_ms: i64huddle 0.7.11: epoch-ms timestamp the sender bound into the
signature, used by receivers as replay protection. #[serde(default)]
for forward-compat parsing — but the verifier rejects 0 and
values outside the configured window, so legacy pre-0.7.11 senders
no longer satisfy verify_signed.
mldsa_pubkey_b64: Option<String>huddle 2.0.6 (WS2-a): optional composite ML-DSA-65 post-quantum
signature. When the hybrid path is used, the same signed_bytes
(payload || domain || timestamp) are signed with the sender’s ML-DSA-65
key too; mldsa_pubkey_b64 carries that key. A verifier that has
pinned the sender’s ML-DSA key (learned from a prior signed announce,
like the ML-KEM pin) checks this signature via
crate::crypto::verify_signed_mldsa, so a quantum adversary that forges
the Ed25519 signature still cannot forge the envelope. Both fields are
absent on classical envelopes — byte-identical to pre-2.0.6 — and ignored
by older peers.
mldsa_signature_b64: Option<String>Trait Implementations§
Source§impl Clone for SignedRoomMessage
impl Clone for SignedRoomMessage
Source§fn clone(&self) -> SignedRoomMessage
fn clone(&self) -> SignedRoomMessage
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for SignedRoomMessage
impl Debug for SignedRoomMessage
Source§impl<'de> Deserialize<'de> for SignedRoomMessage
impl<'de> Deserialize<'de> for SignedRoomMessage
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for SignedRoomMessage
Source§impl PartialEq for SignedRoomMessage
impl PartialEq for SignedRoomMessage
Source§fn eq(&self, other: &SignedRoomMessage) -> bool
fn eq(&self, other: &SignedRoomMessage) -> bool
self and other values to be equal, and is used by ==.