deaddrop_core/protocol/
codec.rs1use crate::{DdError, MAX_FRAME_SIZE, Result};
2use serde::{Serialize, de::DeserializeOwned};
3use tokio::io::{AsyncReadExt, AsyncWriteExt};
4
5pub fn encode_cbor<T: Serialize>(value: &T) -> Result<Vec<u8>> {
6 let mut buf = Vec::new();
7 ciborium::into_writer(value, &mut buf)
8 .map_err(|e| DdError::invalid_frame(format!("cbor encode: {e}")))?;
9 if buf.len() as u32 > MAX_FRAME_SIZE {
10 return Err(DdError::protocol(
11 crate::ErrorCode::Ddp1006LimitExceeded,
12 "encoded object exceeds max frame",
13 ));
14 }
15 Ok(buf)
16}
17
18pub fn decode_cbor<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
19 if bytes.len() as u32 > MAX_FRAME_SIZE {
20 return Err(DdError::protocol(
21 crate::ErrorCode::Ddp1006LimitExceeded,
22 "frame too large",
23 ));
24 }
25 ciborium::from_reader(bytes).map_err(|e| DdError::invalid_frame(format!("cbor decode: {e}")))
26}
27
28pub async fn write_frame_async<W: AsyncWriteExt + Unpin>(w: &mut W, payload: &[u8]) -> Result<()> {
29 if payload.len() as u32 > MAX_FRAME_SIZE {
30 return Err(DdError::protocol(
31 crate::ErrorCode::Ddp1006LimitExceeded,
32 "frame too large",
33 ));
34 }
35 w.write_all(&(payload.len() as u32).to_be_bytes()).await?;
36 w.write_all(payload).await?;
37 w.flush().await?;
38 Ok(())
39}
40
41pub async fn read_frame_async<R: AsyncReadExt + Unpin>(r: &mut R) -> Result<Vec<u8>> {
42 let mut len_buf = [0u8; 4];
43 r.read_exact(&mut len_buf).await?;
44 let len = u32::from_be_bytes(len_buf);
45 if len > MAX_FRAME_SIZE {
46 return Err(DdError::protocol(
47 crate::ErrorCode::Ddp1006LimitExceeded,
48 "frame too large",
49 ));
50 }
51 let mut buf = vec![0u8; len as usize];
52 r.read_exact(&mut buf).await?;
53 Ok(buf)
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use crate::CapabilityDoc;
60
61 #[test]
62 fn malformed_cbor_is_error_not_panic() {
63 assert!(decode_cbor::<CapabilityDoc>(&[]).is_err());
64 assert!(decode_cbor::<CapabilityDoc>(&[0xff, 0x00, 0x01]).is_err());
65 }
66
67 #[test]
68 fn caps_roundtrip() {
69 let c = CapabilityDoc::local_v2();
70 let b = encode_cbor(&c).unwrap();
71 let d: CapabilityDoc = decode_cbor(&b).unwrap();
72 assert_eq!(c, d);
73 }
74
75 #[test]
76 fn random_bytes_do_not_panic_message_or_envelope() {
77 use crate::DropEnvelope;
78 use crate::protocol::Message;
79 let mut seed = 0x9e37_79b9_7f4a_7c15u64;
80 for _ in 0..256 {
81 seed = seed.wrapping_mul(0x5851_f42d_4c95_7f2d).wrapping_add(1);
82 let n = (seed % 64) as usize;
83 let bytes: Vec<u8> = (0..n)
84 .map(|i| (seed.wrapping_add(i as u64 * 17) >> ((i % 8) * 8)) as u8)
85 .collect();
86 let _ = decode_cbor::<Message>(&bytes);
87 let _ = decode_cbor::<DropEnvelope>(&bytes);
88 let _ = decode_cbor::<CapabilityDoc>(&bytes);
89 }
90 }
91}