Message Signing
How Ed25519 signing and canonical JSON ensure message integrity.
Overview
Starweft applies Ed25519 digital signatures to every message on the P2P network. Because there is no central server, message authenticity and integrity are guaranteed solely through cryptographic signatures.
The library used for signing is ed25519-dalek. Both the secret key and public key are 32 bytes in length, and signatures are 64 bytes.
Signing Flow
Messages are signed through the following steps.
The sending node creates an UnsignedEnvelope<T>. The envelope contains metadata such as the protocol version, message ID, source/destination actor IDs, and Lamport timestamp, along with a typed message body (body).
let envelope = UnsignedEnvelope::new(actor_id, Some(to_actor_id), body)
.with_vision_id(vision_id)
.with_project_id(project_id)
.with_lamport_ts(lamport_ts);The signable fields (SignableEnvelope) are serialized to Canonical JSON. The signature field is excluded from the signing target.
let signable_bytes = canonical_json(&SignableEnvelope {
protocol, msg_id, msg_type, from_actor_id, to_actor_id,
vision_id, project_id, task_id, lamport_ts, created_at,
expires_at, body,
})?;A signature is generated over the Canonical JSON byte sequence using the Ed25519 secret key. The resulting signature is Base64-encoded and stored in a MessageSignature struct.
let signature = keypair.sign_bytes(&signable_bytes)?;
// MessageSignature { alg: "ed25519", key_id: "...", sig: "<base64>" }The signed Envelope<T> is now complete and ready to be transmitted over the network.
let signed_envelope = unsigned_envelope.sign(&keypair)?;Signature Verification
The receiving side verifies the signature using Envelope::verify_with_key().
envelope.verify_with_key(&verifying_key)?;The verification process works as follows.
- Confirm that
signature.algis"ed25519" - Confirm that
msg_typematchesbody.msg_type() - Reconstruct a
SignableEnvelopefrom the signable fields and serialize it to Canonical JSON - Verify the Ed25519 signature using the sender's public key
Wire Envelope
During network transmission, the WireEnvelope format is used. It differs from Envelope<T> in that the body field is held as a serde_json::Value (untyped JSON) rather than a typed struct.
WireEnvelope also implements verify_with_key(), enabling signature verification even when body remains a Value type. This means relay nodes can verify signatures without deserializing the message body.
Canonical JSON
Serialization Rules
Canonical JSON deterministically produces a byte sequence according to the following rules.
- Object keys are sorted alphabetically (applied recursively to all nested levels)
- Array element order is preserved
- Scalar values (numbers, strings, booleans, null) use standard JSON representation
- No extraneous whitespace is included
// Input (key order undefined)
{"z": 1, "a": {"c": 3, "b": 2}, "m": [{"y": 4, "x": 5}]}
// Canonical JSON output (keys sorted)
{"a":{"b":2,"c":3},"m":[{"x":5,"y":4}],"z":1}Why Canonical JSON Is Needed
Ed25519 signs over byte sequences. Even if two JSON documents are logically identical, differences in key ordering or formatting will produce different byte sequences, causing signature verification to fail.
Canonical JSON addresses the following problems.
- Field insertion order differences -- Key ordering can vary due to
serde_json'spreserve_orderfeature or platform-specific behavior - Cross-platform determinism -- Produces identical byte sequences across nodes running on different operating systems and architectures
- Cross-language compatibility -- In the future, even if non-Rust clients participate, they can verify signatures as long as they follow the same Canonical JSON rules
mDNS and the Security Model
In Starweft v0.3.0, automatic peer discovery on the local network via mDNS is supported when using the libp2p transport. It can be enabled with discovery.mdns = true in the configuration file.
mDNS Is Not Authenticated
The mDNS protocol itself has no authentication mechanism. Any node on the same LAN may be discovered as a peer.
Ed25519 Signatures Protect the Message Layer
Even though mDNS is unauthenticated, Starweft's security is not compromised. All protocol messages are signed with Ed25519, maintaining the following guarantees.
- Tamper detection -- Signature verification fails if message content has been altered
- Impersonation prevention -- Nodes without the secret key cannot generate valid signatures
- Peer identification via public keys -- Peer identity can be verified through public keys registered via
peer addor exchanged throughCapabilityQuery/CapabilityAdvertisement
Related Pages
Ed25519 key generation, storage, and exchange methods
Signature verification and integrity checks for event logs