Skip to main content

radicle_protocol/
wire.rs

1pub mod frame;
2pub mod message;
3pub mod varint;
4
5pub use frame::StreamId;
6pub use message::{AddressType, MessageType};
7
8use std::collections::BTreeMap;
9use std::convert::TryFrom;
10use std::fmt::Debug;
11use std::mem;
12use std::ops::Deref;
13use std::str::FromStr;
14use std::string::FromUtf8Error;
15
16use bytes::{Buf, BufMut};
17
18use cypheraddr::tor;
19
20use radicle::crypto::{PublicKey, Signature, Unverified};
21use radicle::git;
22use radicle::git::fmt;
23use radicle::git::raw;
24use radicle::identity::RepoId;
25use radicle::node;
26use radicle::node::Alias;
27use radicle::node::NodeId;
28use radicle::node::Timestamp;
29use radicle::node::UserAgent;
30use radicle::storage::refs::Refs;
31use radicle::storage::refs::RefsAt;
32use radicle::storage::refs::SignedRefs;
33
34use crate::bounded::BoundedVec;
35use crate::service::filter;
36
37/// The default type we use to represent sizes on the wire.
38///
39/// Since wire messages are limited to 64KB by the transport layer,
40/// two bytes is enough to represent any message.
41///
42/// Note that in certain cases, we may use a smaller type.
43pub type Size = u16;
44
45#[derive(thiserror::Error, Debug)]
46pub enum Invalid {
47    #[error("invalid Git object identifier size: expected {expected}, got {actual}")]
48    Oid { expected: usize, actual: usize },
49    #[error(transparent)]
50    Bounded(#[from] crate::bounded::Error),
51    #[error("invalid filter size: {actual}")]
52    FilterSize { actual: usize },
53    #[error("UTF-8 error: {0}")]
54    FromUtf8(#[from] FromUtf8Error),
55    #[error(transparent)]
56    RefName(#[from] fmt::Error),
57    #[error(transparent)]
58    Alias(#[from] node::AliasError),
59    #[error("invalid user agent string: {err}")]
60    InvalidUserAgent { err: String },
61    #[error("invalid onion address: {0}")]
62    OnionAddr(#[from] tor::OnionAddrDecodeError),
63    #[error("invalid timestamp: {actual_millis} millis")]
64    Timestamp { actual_millis: u64 },
65
66    // Message types
67    #[error("invalid control message type: {actual:x}")]
68    ControlType { actual: u8 },
69    #[error("invalid stream type: {actual:x}")]
70    StreamType { actual: u8 },
71    #[error("invalid address type: {actual:x}")]
72    AddressType { actual: u8 },
73    #[error("invalid message type: {actual:x}")]
74    MessageType { actual: u16 },
75    #[error("invalid info message type: {actual:x}")]
76    InfoMessageType { actual: u16 },
77
78    // Protocol version handling
79    #[error("invalid protocol version string: {actual:x?}")]
80    ProtocolVersion { actual: [u8; 4] },
81    #[error("unsupported protocol version: {actual}")]
82    ProtocolVersionUnsupported { actual: u8 },
83}
84
85#[derive(thiserror::Error, Debug)]
86pub enum Error {
87    #[error(transparent)]
88    Invalid(#[from] Invalid),
89
90    #[error("unexpected end of buffer, requested {requested} more bytes but only {available} are available")]
91    UnexpectedEnd { available: usize, requested: usize },
92}
93
94impl From<bytes::TryGetError> for Error {
95    fn from(
96        bytes::TryGetError {
97            available,
98            requested,
99        }: bytes::TryGetError,
100    ) -> Self {
101        Self::UnexpectedEnd {
102            available,
103            requested,
104        }
105    }
106}
107
108/// Things that can be encoded as binary.
109pub trait Encode {
110    /// Encode self by writing it to the given buffer.
111    fn encode(&self, buffer: &mut impl BufMut);
112
113    /// A convenience wrapper around [`Encode::encode`]
114    /// that allocates a [`Vec`].
115    fn encode_to_vec(&self) -> Vec<u8> {
116        let mut buf = Vec::new();
117        self.encode(&mut buf);
118        buf
119    }
120}
121
122/// Things that can be decoded from binary.
123pub trait Decode: Sized {
124    fn decode(buffer: &mut impl Buf) -> Result<Self, Error>;
125
126    /// A convenience wrapper around [`Decode::decode`] to decode
127    /// from a slice exactly.
128    ///
129    /// # Panics
130    ///
131    ///  - If decoding failed because there were not enough bytes.
132    ///  - If there are any bytes left after decoding.
133    #[cfg(test)]
134    fn decode_exact(mut data: &[u8]) -> Result<Self, Invalid> {
135        match Self::decode(&mut data) {
136            Ok(value) => {
137                if !data.is_empty() {
138                    panic!("{} bytes left in buffer", data.len());
139                }
140                Ok(value)
141            }
142            Err(err @ Error::UnexpectedEnd { .. }) => {
143                panic!("{}", err);
144            }
145            Err(Error::Invalid(e)) => Err(e),
146        }
147    }
148}
149
150impl Encode for u8 {
151    fn encode(&self, buf: &mut impl BufMut) {
152        buf.put_u8(*self);
153    }
154}
155
156impl Encode for u16 {
157    fn encode(&self, buf: &mut impl BufMut) {
158        buf.put_u16(*self);
159    }
160}
161
162impl Encode for u32 {
163    fn encode(&self, buf: &mut impl BufMut) {
164        buf.put_u32(*self);
165    }
166}
167
168impl Encode for u64 {
169    fn encode(&self, buf: &mut impl BufMut) {
170        buf.put_u64(*self);
171    }
172}
173
174impl Encode for PublicKey {
175    fn encode(&self, buf: &mut impl BufMut) {
176        self.deref().encode(buf)
177    }
178}
179
180impl<const T: usize> Encode for &[u8; T] {
181    fn encode(&self, buf: &mut impl BufMut) {
182        buf.put_slice(&**self);
183    }
184}
185
186impl<const T: usize> Encode for [u8; T] {
187    fn encode(&self, buf: &mut impl BufMut) {
188        buf.put_slice(self);
189    }
190}
191
192impl<T> Encode for &[T]
193where
194    T: Encode,
195{
196    fn encode(&self, buf: &mut impl BufMut) {
197        (self.len() as Size).encode(buf);
198
199        for item in self.iter() {
200            item.encode(buf);
201        }
202    }
203}
204
205impl<T, const N: usize> Encode for BoundedVec<T, N>
206where
207    T: Encode,
208{
209    fn encode(&self, buf: &mut impl BufMut) {
210        self.as_slice().encode(buf)
211    }
212}
213
214impl Encode for &str {
215    fn encode(&self, buf: &mut impl BufMut) {
216        assert!(self.len() <= u8::MAX as usize);
217
218        (self.len() as u8).encode(buf);
219        let bytes = self.as_bytes();
220
221        // Nb. Don't use the [`Encode`] instance here for &[u8], because we are prefixing the
222        // length ourselves.
223        buf.put_slice(bytes);
224    }
225}
226
227impl Encode for String {
228    fn encode(&self, buf: &mut impl BufMut) {
229        self.as_str().encode(buf)
230    }
231}
232
233impl Encode for git::Url {
234    fn encode(&self, buf: &mut impl BufMut) {
235        self.to_string().encode(buf)
236    }
237}
238
239impl Encode for RepoId {
240    fn encode(&self, buf: &mut impl BufMut) {
241        self.deref().encode(buf)
242    }
243}
244
245impl Encode for Refs {
246    fn encode(&self, buf: &mut impl BufMut) {
247        let len: Size = self
248            .len()
249            .try_into()
250            .expect("`Refs::len()` must be less than or equal to `Size::MAX`");
251        len.encode(buf);
252
253        for (name, oid) in self.iter() {
254            name.as_str().encode(buf);
255            oid.encode(buf);
256        }
257    }
258}
259
260impl Encode for cypheraddr::tor::OnionAddrV3 {
261    fn encode(&self, buf: &mut impl BufMut) {
262        self.into_raw_bytes().encode(buf)
263    }
264}
265
266impl Encode for UserAgent {
267    fn encode(&self, buf: &mut impl BufMut) {
268        self.as_ref().encode(buf)
269    }
270}
271
272impl Encode for Alias {
273    fn encode(&self, buf: &mut impl BufMut) {
274        self.as_ref().encode(buf)
275    }
276}
277
278impl<A, B> Encode for (A, B)
279where
280    A: Encode,
281    B: Encode,
282{
283    fn encode(&self, buf: &mut impl BufMut) {
284        self.0.encode(buf);
285        self.1.encode(buf);
286    }
287}
288
289impl Encode for git::fmt::RefString {
290    fn encode(&self, buf: &mut impl BufMut) {
291        self.as_str().encode(buf)
292    }
293}
294
295impl Encode for Signature {
296    fn encode(&self, buf: &mut impl BufMut) {
297        self.deref().encode(buf)
298    }
299}
300
301impl Encode for git::Oid {
302    fn encode(&self, buf: &mut impl BufMut) {
303        // Nb. We use length-encoding here to support future SHA-2 object ids.
304        let bytes: &[u8] = self.as_ref();
305        bytes.encode(buf)
306    }
307}
308
309////////////////////////////////////////////////////////////////////////////////
310
311impl Decode for PublicKey {
312    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
313        let buf: [u8; 32] = Decode::decode(buf)?;
314
315        Ok(PublicKey::from(buf))
316    }
317}
318
319impl Decode for Refs {
320    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
321        let len = Size::decode(buf)?;
322        let mut refs = BTreeMap::new();
323
324        for _ in 0..len {
325            let name = String::decode(buf)?;
326            let name = git::fmt::RefString::try_from(name).map_err(Invalid::from)?;
327            let oid = git::Oid::decode(buf)?;
328
329            refs.insert(name, oid);
330        }
331        Ok(refs.into())
332    }
333}
334
335impl Decode for git::fmt::RefString {
336    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
337        let ref_str = String::decode(buf)?;
338        Ok(git::fmt::RefString::try_from(ref_str).map_err(Invalid::from)?)
339    }
340}
341
342impl Decode for UserAgent {
343    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
344        let user_agent = String::decode(buf)?;
345        Ok(UserAgent::from_str(&user_agent).map_err(|err| Invalid::InvalidUserAgent { err })?)
346    }
347}
348
349impl Decode for Alias {
350    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
351        let alias = String::decode(buf)?;
352        Ok(Alias::from_str(&alias).map_err(Invalid::from)?)
353    }
354}
355
356impl<A, B> Decode for (A, B)
357where
358    A: Decode,
359    B: Decode,
360{
361    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
362        let a = A::decode(buf)?;
363        let b = B::decode(buf)?;
364        Ok((a, b))
365    }
366}
367
368impl Decode for git::Oid {
369    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
370        const LEN_EXPECTED: usize = mem::size_of::<raw::Oid>();
371
372        let len = Size::decode(buf)? as usize;
373
374        if len != LEN_EXPECTED {
375            return Err(Invalid::Oid {
376                expected: LEN_EXPECTED,
377                actual: len,
378            }
379            .into());
380        }
381
382        let buf: [u8; LEN_EXPECTED] = Decode::decode(buf)?;
383        let oid = raw::Oid::from_bytes(&buf).expect("the buffer is exactly the right size");
384        let oid = git::Oid::from(oid);
385
386        Ok(oid)
387    }
388}
389
390impl Decode for Signature {
391    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
392        let bytes: [u8; 64] = Decode::decode(buf)?;
393
394        Ok(Signature::from(bytes))
395    }
396}
397
398impl Decode for u8 {
399    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
400        Ok(buf.try_get_u8()?)
401    }
402}
403
404impl Decode for u16 {
405    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
406        Ok(buf.try_get_u16()?)
407    }
408}
409
410impl Decode for u32 {
411    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
412        Ok(buf.try_get_u32()?)
413    }
414}
415
416impl Decode for u64 {
417    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
418        Ok(buf.try_get_u64()?)
419    }
420}
421
422impl<const N: usize> Decode for [u8; N] {
423    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
424        let mut ary = [0; N];
425        buf.try_copy_to_slice(&mut ary).map_err(Error::from)?;
426
427        Ok(ary)
428    }
429}
430
431impl<T, const N: usize> Decode for BoundedVec<T, N>
432where
433    T: Decode,
434{
435    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
436        let len: usize = Size::decode(buf)? as usize;
437        let mut items = Self::with_capacity(len).map_err(Invalid::from)?;
438
439        for _ in 0..items.capacity() {
440            let item = T::decode(buf)?;
441            items.push(item).ok();
442        }
443        Ok(items)
444    }
445}
446
447impl Decode for String {
448    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
449        let len = u8::decode(buf)?;
450        let mut bytes = vec![0; len as usize];
451
452        buf.try_copy_to_slice(&mut bytes)?;
453
454        let string = String::from_utf8(bytes).map_err(Invalid::from)?;
455
456        Ok(string)
457    }
458}
459
460impl Decode for RepoId {
461    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
462        let oid: git::Oid = Decode::decode(buf)?;
463
464        Ok(Self::from(oid))
465    }
466}
467
468impl Encode for filter::Filter {
469    fn encode(&self, buf: &mut impl BufMut) {
470        self.deref().as_bytes().encode(buf);
471    }
472}
473
474impl Decode for filter::Filter {
475    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
476        let size: usize = Size::decode(buf)? as usize;
477        if !filter::FILTER_SIZES.contains(&size) {
478            return Err(Invalid::FilterSize { actual: size }.into());
479        }
480
481        let mut bytes = vec![0; size];
482
483        buf.try_copy_to_slice(&mut bytes)?;
484
485        let f = filter::BloomFilter::from(bytes);
486        debug_assert_eq!(f.hashes(), filter::FILTER_HASHES);
487
488        Ok(Self::from(f))
489    }
490}
491
492impl<V> Encode for SignedRefs<V> {
493    fn encode(&self, buf: &mut impl BufMut) {
494        self.id.encode(buf);
495        self.refs.encode(buf);
496        self.signature.encode(buf);
497    }
498}
499
500impl Decode for SignedRefs<Unverified> {
501    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
502        let id = NodeId::decode(buf)?;
503        let refs = Refs::decode(buf)?;
504        let signature = Signature::decode(buf)?;
505
506        Ok(Self::new(refs, id, signature))
507    }
508}
509
510impl Encode for RefsAt {
511    fn encode(&self, buf: &mut impl BufMut) {
512        self.remote.encode(buf);
513        self.at.encode(buf);
514    }
515}
516
517impl Decode for RefsAt {
518    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
519        let remote = NodeId::decode(buf)?;
520        let at = git::Oid::decode(buf)?;
521        Ok(Self { remote, at })
522    }
523}
524
525impl Encode for node::Features {
526    fn encode(&self, buf: &mut impl BufMut) {
527        self.deref().encode(buf)
528    }
529}
530
531impl Decode for node::Features {
532    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
533        let features = u64::decode(buf)?;
534
535        Ok(Self::from(features))
536    }
537}
538
539impl Decode for tor::OnionAddrV3 {
540    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
541        let bytes: [u8; tor::ONION_V3_RAW_LEN] = Decode::decode(buf)?;
542        let addr = tor::OnionAddrV3::from_raw_bytes(bytes).map_err(Invalid::from)?;
543
544        Ok(addr)
545    }
546}
547
548impl Encode for Timestamp {
549    fn encode(&self, buf: &mut impl BufMut) {
550        self.deref().encode(buf)
551    }
552}
553
554impl Decode for Timestamp {
555    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
556        let millis = u64::decode(buf)?;
557        let ts = Timestamp::try_from(millis).map_err(|value| Invalid::Timestamp {
558            actual_millis: value,
559        })?;
560
561        Ok(ts)
562    }
563}
564
565#[cfg(test)]
566fn roundtrip<T>(value: T)
567where
568    T: Encode + Decode + PartialEq + Debug,
569{
570    let encoded = value.encode_to_vec();
571    assert_eq!(T::decode_exact(&encoded).expect("roundtrip"), value);
572}
573
574#[cfg(test)]
575#[macro_export]
576macro_rules! prop_roundtrip {
577    ($t:ty, $name:tt) => {
578        paste::paste! {
579            #[quickcheck]
580            fn [< prop_roundtrip_ $name:lower >](v: $t) {
581                $crate::wire::roundtrip(v);
582            }
583        }
584    };
585    ($t:ty) => {
586        paste::paste! {
587            prop_roundtrip!($t, [< $t >]);
588        }
589    };
590}
591
592#[cfg(test)]
593mod tests {
594    use super::*;
595
596    use qcheck;
597    use qcheck_macros::quickcheck;
598
599    use radicle::assert_matches;
600    use radicle::crypto::Unverified;
601    use radicle::storage::refs::SignedRefs;
602
603    prop_roundtrip!(u16);
604    prop_roundtrip!(u32);
605    prop_roundtrip!(u64);
606    prop_roundtrip!(BoundedVec<u8, 16>, vec);
607    prop_roundtrip!(PublicKey);
608    prop_roundtrip!(filter::Filter, filter);
609    prop_roundtrip!(RepoId);
610    prop_roundtrip!(Refs);
611    prop_roundtrip!((String, String), tuple);
612    prop_roundtrip!(SignedRefs<Unverified>, signed_refs);
613
614    #[quickcheck]
615    fn prop_string(input: String) -> qcheck::TestResult {
616        if input.len() > u8::MAX as usize {
617            return qcheck::TestResult::discard();
618        }
619
620        roundtrip(input);
621
622        qcheck::TestResult::passed()
623    }
624
625    #[quickcheck]
626    fn prop_signature(input: [u8; 64]) {
627        roundtrip(Signature::from(input));
628    }
629
630    #[quickcheck]
631    fn prop_oid(input: [u8; 20]) {
632        roundtrip(git::Oid::from_sha1(input));
633    }
634
635    #[test]
636    fn test_string() {
637        assert_eq!(
638            String::from("hello").encode_to_vec(),
639            vec![5, b'h', b'e', b'l', b'l', b'o']
640        );
641    }
642
643    #[test]
644    fn test_alias() {
645        assert_eq!(
646            Alias::from_str("hello").unwrap().encode_to_vec(),
647            vec![5, b'h', b'e', b'l', b'l', b'o']
648        );
649    }
650
651    #[test]
652    fn test_filter_invalid() {
653        let b = bloomy::BloomFilter::with_size(filter::FILTER_SIZE_M / 3);
654        let f = filter::Filter::from(b);
655        let bytes = f.encode_to_vec();
656
657        assert_matches!(
658            filter::Filter::decode_exact(&bytes).unwrap_err(),
659            Invalid::FilterSize { .. }
660        );
661    }
662
663    #[test]
664    fn test_bounded_vec_limit() {
665        let v: BoundedVec<u8, 2> = vec![1, 2].try_into().unwrap();
666        let buf = &v.encode_to_vec();
667
668        assert_matches!(
669            BoundedVec::<u8, 1>::decode_exact(buf),
670            Err(Invalid::Bounded(crate::bounded::Error::InvalidSize {
671                expected: 1,
672                actual: 2
673            })),
674            "fail when vector is too small for buffer",
675        );
676
677        assert!(
678            BoundedVec::<u8, 2>::decode_exact(buf).is_ok(),
679            "successfully decode vector of same size",
680        );
681    }
682}