Skip to main content

vcl_protocol/
packet.rs

1//! # VCL Packet
2//!
3//! Defines [`VCLPacket`] — the core unit of data transmission in VCL Protocol —
4//! and [`PacketType`] which determines how the connection layer routes each packet.
5//!
6//! Every packet is:
7//! - **Chained** — contains the SHA-256 hash of the previous packet in the same direction
8//! - **Signed** — Ed25519 signature over the packet hash
9//! - **Encrypted** — payload encrypted with XChaCha20-Poly1305
10
11use sha2::{Sha256, Digest};
12use ed25519_dalek::{SigningKey, VerifyingKey, Signature, Signer, Verifier};
13use serde::{Serialize, Deserialize};
14use crate::error::VCLError;
15
16/// Determines how a [`VCLPacket`] is routed by the connection layer.
17///
18/// Users only interact with `Data` packets directly.
19/// `Ping`, `Pong`, and `KeyRotation` are handled transparently inside `recv()`.
20#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
21pub enum PacketType {
22    /// A regular data packet — returned by [`VCLConnection::recv()`](crate::connection::VCLConnection::recv).
23    Data,
24    /// Liveness check sent by [`VCLConnection::ping()`](crate::connection::VCLConnection::ping).
25    Ping,
26    /// Automatic reply to a [`Ping`](PacketType::Ping). Handled inside `recv()`.
27    Pong,
28    /// Mid-session key rotation initiated by [`VCLConnection::rotate_keys()`](crate::connection::VCLConnection::rotate_keys).
29    KeyRotation,
30}
31
32/// A single unit of data transmission in VCL Protocol.
33///
34/// Packets are created, signed, and serialized internally by [`VCLConnection`].
35/// After `recv()` returns, `payload` contains the **decrypted** data.
36///
37/// [`VCLConnection`]: crate::connection::VCLConnection
38#[derive(Serialize, Deserialize, Clone, Debug)]
39pub struct VCLPacket {
40    /// Protocol version. Currently `2`.
41    pub version: u8,
42    /// Routing type — see [`PacketType`].
43    pub packet_type: PacketType,
44    /// Monotonically increasing sequence number (per direction).
45    pub sequence: u64,
46    /// SHA-256 hash of the previous packet in the same direction.
47    /// All-zeros for the first packet.
48    pub prev_hash: Vec<u8>,
49    /// 24-byte XChaCha20 nonce used to encrypt this packet's payload.
50    pub nonce: [u8; 24],
51    /// After `recv()`: decrypted payload. On the wire: XChaCha20-Poly1305 ciphertext.
52    pub payload: Vec<u8>,
53    /// 64-byte Ed25519 signature over the packet hash.
54    pub signature: Vec<u8>,
55}
56
57impl VCLPacket {
58    /// Create a new [`PacketType::Data`] packet.
59    pub fn new(sequence: u64, prev_hash: Vec<u8>, payload: Vec<u8>, nonce: [u8; 24]) -> Self {
60        Self::new_typed(sequence, prev_hash, payload, nonce, PacketType::Data)
61    }
62
63    /// Create a packet with a specific [`PacketType`].
64    /// Used internally for Ping, Pong, and KeyRotation packets.
65    pub fn new_typed(
66        sequence: u64,
67        prev_hash: Vec<u8>,
68        payload: Vec<u8>,
69        nonce: [u8; 24],
70        packet_type: PacketType,
71    ) -> Self {
72        VCLPacket {
73            version: 2,
74            packet_type,
75            sequence,
76            prev_hash,
77            nonce,
78            payload,
79            signature: Vec::new(),
80        }
81    }
82
83    /// Compute the SHA-256 hash of this packet (version + sequence + prev_hash + nonce + payload).
84    /// Used for chain linking and signature generation.
85    pub fn compute_hash(&self) -> Vec<u8> {
86        let mut hasher = Sha256::new();
87        hasher.update(self.version.to_be_bytes());
88        hasher.update(self.sequence.to_be_bytes());
89        hasher.update(&self.prev_hash);
90        hasher.update(&self.nonce);
91        hasher.update(&self.payload);
92        hasher.finalize().to_vec()
93    }
94
95    /// Sign this packet with an Ed25519 `private_key` (32 bytes).
96    /// Sets `self.signature` to the 64-byte signature.
97    ///
98    /// # Errors
99    /// Returns [`VCLError::InvalidKey`] if `private_key` is not 32 bytes.
100    pub fn sign(&mut self, private_key: &[u8]) -> Result<(), VCLError> {
101        let key_bytes: &[u8; 32] = private_key
102            .try_into()
103            .map_err(|_| VCLError::InvalidKey("Private key must be 32 bytes".to_string()))?;
104        let signing_key = SigningKey::from_bytes(key_bytes);
105        let hash = self.compute_hash();
106        let signature: Signature = signing_key.sign(&hash);
107        self.signature = signature.to_bytes().to_vec();
108        Ok(())
109    }
110
111    /// Verify the Ed25519 signature against `public_key` (32 bytes).
112    ///
113    /// Returns `Ok(true)` if valid, `Ok(false)` if the signature does not match.
114    ///
115    /// # Errors
116    /// Returns [`VCLError::InvalidKey`] if `public_key` is malformed.
117    pub fn verify(&self, public_key: &[u8]) -> Result<bool, VCLError> {
118        if self.signature.len() != 64 {
119            return Ok(false);
120        }
121        let key_bytes: &[u8; 32] = public_key
122            .try_into()
123            .map_err(|_| VCLError::InvalidKey("Public key must be 32 bytes".to_string()))?;
124        let verifying_key = VerifyingKey::from_bytes(key_bytes)
125            .map_err(|e| VCLError::InvalidKey(format!("Invalid public key: {}", e)))?;
126        let sig_bytes: &[u8; 64] = self.signature
127            .as_slice()
128            .try_into()
129            .map_err(|_| VCLError::InvalidKey("Signature must be 64 bytes".to_string()))?;
130        let signature = Signature::from_bytes(sig_bytes);
131        let hash = self.compute_hash();
132        Ok(verifying_key.verify(&hash, &signature).is_ok())
133    }
134
135    /// Returns `true` if `self.prev_hash` matches `expected_prev_hash`.
136    /// Called on every incoming packet to verify chain continuity.
137    pub fn validate_chain(&self, expected_prev_hash: &[u8]) -> bool {
138        self.prev_hash == expected_prev_hash
139    }
140
141    /// Serialize this packet to bytes using bincode.
142    pub fn serialize(&self) -> Vec<u8> {
143        bincode::serialize(self).unwrap()
144    }
145
146    /// Deserialize a packet from bytes.
147    ///
148    /// # Errors
149    /// Returns [`VCLError::SerializationError`] if the bytes are malformed.
150    pub fn deserialize(data: &[u8]) -> Result<Self, VCLError> {
151        bincode::deserialize(data).map_err(|e| VCLError::SerializationError(e.to_string()))
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use crate::crypto::KeyPair;
158    use super::*;
159
160    fn test_keypair() -> KeyPair {
161        KeyPair::generate()
162    }
163
164    #[test]
165    fn test_packet_new() {
166        let packet = VCLPacket::new(1, vec![0; 32], b"test".to_vec(), [0; 24]);
167        assert_eq!(packet.version, 2);
168        assert_eq!(packet.sequence, 1);
169        assert_eq!(packet.payload, b"test");
170        assert_eq!(packet.packet_type, PacketType::Data);
171    }
172
173    #[test]
174    fn test_compute_hash() {
175        let p1 = VCLPacket::new(1, vec![0; 32], b"A".to_vec(), [0; 24]);
176        let p2 = VCLPacket::new(1, vec![0; 32], b"B".to_vec(), [0; 24]);
177        assert_ne!(p1.compute_hash(), p2.compute_hash());
178    }
179
180    #[test]
181    fn test_sign_verify() {
182        let kp = test_keypair();
183        let mut packet = VCLPacket::new(1, vec![0; 32], b"test".to_vec(), [0; 24]);
184        packet.sign(&kp.private_key).unwrap();
185        assert!(packet.verify(&kp.public_key).unwrap());
186    }
187
188    #[test]
189    fn test_verify_wrong_key_fails() {
190        let kp1 = test_keypair();
191        let kp2 = test_keypair();
192        let mut packet = VCLPacket::new(1, vec![0; 32], b"test".to_vec(), [0; 24]);
193        packet.sign(&kp1.private_key).unwrap();
194        assert!(!packet.verify(&kp2.public_key).unwrap());
195    }
196
197    #[test]
198    fn test_validate_chain() {
199        let prev = vec![1, 2, 3];
200        let packet = VCLPacket::new(1, prev.clone(), b"test".to_vec(), [0; 24]);
201        assert!(packet.validate_chain(&prev));
202        assert!(!packet.validate_chain(&[4, 5, 6]));
203    }
204
205    #[test]
206    fn test_serialize_deserialize() {
207        let original = VCLPacket::new(42, vec![9; 32], b"payload".to_vec(), [7; 24]);
208        let bytes = original.serialize();
209        let restored = VCLPacket::deserialize(&bytes).unwrap();
210        assert_eq!(original.sequence, restored.sequence);
211        assert_eq!(original.payload, restored.payload);
212        assert_eq!(original.nonce, restored.nonce);
213        assert_eq!(restored.packet_type, PacketType::Data);
214    }
215
216    #[test]
217    fn test_packet_types() {
218        let ping = VCLPacket::new_typed(0, vec![0; 32], vec![], [0; 24], PacketType::Ping);
219        let pong = VCLPacket::new_typed(0, vec![0; 32], vec![], [0; 24], PacketType::Pong);
220        let rot  = VCLPacket::new_typed(0, vec![0; 32], vec![], [0; 24], PacketType::KeyRotation);
221        assert_eq!(ping.packet_type, PacketType::Ping);
222        assert_eq!(pong.packet_type, PacketType::Pong);
223        assert_eq!(rot.packet_type,  PacketType::KeyRotation);
224    }
225}