Skip to main content

deaddrop_core/protocol/
codec.rs

1use 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}