pub struct MessageFull {
id: MessageId,
signature: Signature,
message: Box<Message>,
}Expand description
Complete signed message ready for transmission and storage.
§Message Authentication and Integrity
MessageFull represents a complete, authenticated message that includes:
- Content: The original
Message(e.g.,MessageV0) - Signature: Ed25519 digital signature over the serialized message
- Identity: Blake3 hash-based unique identifier
This structure ensures non-repudiation and integrity - recipients can cryptographically verify that the message came from the claimed sender and hasn’t been tampered with.
§Cryptographic Construction
The message construction follows a specific protocol:
- Serialize: Convert
Messageto bytes using postcard - Sign: Create Ed25519 signature over the serialized bytes
- Hash: Compute Blake3 hash of
serialized_message || signaturefor the ID
This ensures that:
- The signature covers the entire message content
- The ID uniquely identifies this specific signed message
- Replay attacks are prevented through unique IDs
§Wire Format and Storage
MessageFull is the canonical storage format used throughout the system:
- Network transmission: Serialized with postcard for efficiency
- Database storage: Stored as binary blobs in key-value stores
- Message indexing: ID used as primary key, sender/timestamp extracted for indexes
The postcard serialization format is:
[id: 32 bytes][message: variable][signature: 64 bytes]§Identity and Deduplication
The Blake3-based ID serves multiple purposes:
- Deduplication: Identical signed messages have identical IDs
- Content addressing: Messages can be retrieved by their cryptographic hash
- Integrity verification: ID changes if any part of the message is modified
- Ordering: IDs provide deterministic message ordering (with timestamp ties)
§Security Properties
MessageFull provides the following security guarantees:
- Authentication: Ed25519 signature proves sender identity
- Integrity: Any modification invalidates the signature
- Non-repudiation: Sender cannot deny creating a valid signature
- Uniqueness: Blake3 ID prevents message duplication
Note: Confidentiality is provided by the Content encryption, not at this layer.
§Example Usage
use zoe_wire_protocol::{MessageFull, Message, MessageV0, Content, Kind};
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
// Create a signed message
let signing_key = SigningKey::generate(&mut OsRng);
let message = Message::MessageV0(MessageV0 {
sender: signing_key.verifying_key(),
when: 1640995200,
kind: Kind::Regular,
tags: vec![],
content: Content::raw("Hello!".as_bytes().to_stdvec()),
});
let full_message = MessageFull::new(message, &signing_key)?;
// Verify the signature and ID
assert!(full_message.verify()?);
// The ID is deterministic for the same signed content
let id = full_message.id;§Storage Integration
This structure is optimized for the key-value storage architecture:
- Primary key:
idfield for O(1) message retrieval - Indexed fields:
senderandwhenextracted from embedded message for queries - Tag tables: Separate tables for efficient tag-based filtering
- Blob storage: Entire
MessageFullserialized as atomic unit
Fields§
§id: MessageIdBlake3 hash serving as the unique message identifier.
Computed as: Blake3(messsage.as_bytes())
Notice that the ID does not include the signature as
that may contain random ness (ML-DSA does) and thus would
create different hash IDs for the same content message.
That means that if there are two messages with the same contentn but valid signatures (verified against the sender) they are considered the same in terms of storage and retrieval and there is no guarantee you received the one with the same signature - just with a valid signature.
This ID is:
- Unique: Cryptographically improbable to collide
- Deterministic: Same content always produces same ID
- Tamper-evident: Changes to messagd change the ID
- Content-addressed: Can be used to retrieve the message
signature: SignatureCryptographic signature over the serialized message.
Created by signing postcard::serialize(message) with the sender’s private key.
Recipients verify this signature using the public key in message.sender.
Security note: The signature covers the entire serialized message, including all metadata, tags, and content. This prevents partial modification attacks.
message: Box<Message>The original message content and metadata.
Boxed to minimize stack usage since messages can be large.
Contains version-specific message data (e.g., MessageV0).
Implementations§
Source§impl MessageFull
impl MessageFull
Sourcepub fn new(message: Message, signer: &KeyPair) -> Result<Self, MessageFullError>
pub fn new(message: Message, signer: &KeyPair) -> Result<Self, MessageFullError>
Create a new MessageFull with proper signature and ID
fn with_signature( signature: Signature, message: Box<Message>, message_bytes: &[u8], ) -> Result<Self, MessageFullError>
pub fn id(&self) -> &MessageId
pub fn message(&self) -> &Message
pub fn signature(&self) -> &Signature
pub fn ref_tag(&self) -> Tag
Sourcepub fn storage_value(&self) -> Result<Vec<u8>, MessageFullError>
pub fn storage_value(&self) -> Result<Vec<u8>, MessageFullError>
The value this message is stored under in the storage
Sourcepub fn storage_timeout(&self) -> Option<u64>
pub fn storage_timeout(&self) -> Option<u64>
The timeout for this message in the storage
pub fn store_key(&self) -> Option<StoreKey>
Sourcepub fn from_storage_value(value: &[u8]) -> Result<Self, MessageFullError>
pub fn from_storage_value(value: &[u8]) -> Result<Self, MessageFullError>
Deserialize a message from its storage value
pub fn when(&self) -> &u64
pub fn kind(&self) -> &Kind
pub fn content(&self) -> &Content
Sourcepub fn raw_content(&self) -> Option<&Vec<u8>>
pub fn raw_content(&self) -> Option<&Vec<u8>>
Get raw content bytes if this message contains raw content
Sourcepub fn encrypted_content(&self) -> Option<&ChaCha20Poly1305Content>
pub fn encrypted_content(&self) -> Option<&ChaCha20Poly1305Content>
Get encrypted content if this message contains encrypted content
Sourcepub fn is_expired(&self, current_time: u64) -> bool
pub fn is_expired(&self, current_time: u64) -> bool
Check if this message is expired based on its timeout and current time
Source§impl MessageFull
impl MessageFull
Sourcepub fn try_deserialize_content<C>(&self) -> Result<C, MessageFullError>where
C: for<'a> Deserialize<'a>,
pub fn try_deserialize_content<C>(&self) -> Result<C, MessageFullError>where
C: for<'a> Deserialize<'a>,
Try to deserialize content if it’s raw (unencrypted)
Trait Implementations§
Source§impl Clone for MessageFull
impl Clone for MessageFull
Source§fn clone(&self) -> MessageFull
fn clone(&self) -> MessageFull
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for MessageFull
impl Debug for MessageFull
Source§impl<'de> Deserialize<'de> for MessageFull
impl<'de> Deserialize<'de> for MessageFull
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>,
Source§impl From<&MessageFull> for Tag
impl From<&MessageFull> for Tag
Source§fn from(message: &MessageFull) -> Self
fn from(message: &MessageFull) -> Self
Source§impl From<MessageFull> for MessageFullWire
impl From<MessageFull> for MessageFullWire
Source§fn from(val: MessageFull) -> Self
fn from(val: MessageFull) -> Self
Source§impl PartialEq for MessageFull
Manual PartialEq implementation for MessageFull
impl PartialEq for MessageFull
Manual PartialEq implementation for MessageFull
Source§impl Serialize for MessageFull
impl Serialize for MessageFull
Source§impl TryFrom<MessageFullWire> for MessageFull
impl TryFrom<MessageFullWire> for MessageFull
Source§type Error = MessageFullError
type Error = MessageFullError
impl Eq for MessageFull
Manual Eq implementation for MessageFull
Auto Trait Implementations§
impl Freeze for MessageFull
impl RefUnwindSafe for MessageFull
impl Send for MessageFull
impl Sync for MessageFull
impl Unpin for MessageFull
impl UnwindSafe for MessageFull
Blanket Implementations§
§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Classify for T
impl<T> Classify for T
type Classified = T
fn classify(self) -> T
§impl<T> Classify for T
impl<T> Classify for T
type Classified = T
fn classify(self) -> T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Declassify for T
impl<T> Declassify for T
type Declassified = T
fn declassify(self) -> T
§impl<T> Declassify for T
impl<T> Declassify for T
type Declassified = T
fn declassify(self) -> T
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.