plabble_codec/codec/objects/
certificate.rs

1use time::OffsetDateTime;
2
3use crate::{
4    abstractions::{Serializable, SerializationError, SerializationInfo, KEY_SIZE, SIGNATURE_SIZE},
5    codec::common::{assert_len, plabble_date},
6};
7
8/// A certificate
9///
10/// # Fields
11///
12/// * `public_key` - the public key of the certificate (32 bytes)
13/// * `valid_from` - the date the certificate is valid from (4 bytes, Plabble timestamp)
14/// * `valid_to` - the date the certificate is valid to (4 bytes, Plabble timestamp)
15/// * `signature` - the signature of the certificate (64 bytes)
16/// * `domain_or_ip` - the domain or IP address the certificate is from
17#[derive(Debug)]
18pub struct Certificate {
19    pub public_key: [u8; KEY_SIZE],
20    pub valid_from: OffsetDateTime,
21    pub valid_to: OffsetDateTime,
22    pub signature: [u8; SIGNATURE_SIZE],
23    pub domain_or_ip: String,
24}
25
26impl Serializable for Certificate {
27    fn size(&self) -> usize {
28        KEY_SIZE + 4 + 4 + SIGNATURE_SIZE + self.domain_or_ip.len()
29    }
30
31    fn get_bytes(&self) -> Vec<u8> {
32        let mut buff = Vec::new();
33        buff.extend_from_slice(&self.public_key);
34        buff.extend_from_slice(&plabble_date::to_timestamp(&self.valid_from).to_be_bytes());
35        buff.extend_from_slice(&plabble_date::to_timestamp(&self.valid_to).to_be_bytes());
36        buff.extend_from_slice(&self.signature);
37        buff.extend_from_slice(self.domain_or_ip.as_bytes());
38        buff
39    }
40
41    fn from_bytes(data: &[u8], _: Option<SerializationInfo>) -> Result<Self, SerializationError>
42    where
43        Self: Sized,
44    {
45        assert_len(data, KEY_SIZE + SIGNATURE_SIZE + 8)?;
46        let mut cert = Self {
47            public_key: [0u8; KEY_SIZE],
48            valid_from: plabble_date::from_timestamp(u32::from_be_bytes(
49                data[KEY_SIZE..(KEY_SIZE + 4)].try_into().unwrap(),
50            )),
51            valid_to: plabble_date::from_timestamp(u32::from_be_bytes(
52                data[(KEY_SIZE + 4)..(KEY_SIZE + 8)].try_into().unwrap(),
53            )),
54            signature: [0u8; SIGNATURE_SIZE],
55            domain_or_ip: String::new(),
56        };
57        cert.public_key.copy_from_slice(&data[..KEY_SIZE]);
58        cert.signature
59            .copy_from_slice(&data[(KEY_SIZE + 8)..(KEY_SIZE + 8 + SIGNATURE_SIZE)]);
60        cert.domain_or_ip =
61            String::from_utf8_lossy(&data[(KEY_SIZE + 8 + SIGNATURE_SIZE)..]).to_string();
62        Ok(cert)
63    }
64}
65
66#[cfg(test)]
67mod test {
68    use time_macros::datetime;
69
70    use crate::abstractions::EPOCH;
71
72    use super::*;
73
74    #[test]
75    fn can_parse_cert() {
76        let cert = &[
77            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
78            25, 26, 27, 28, 29, 30, 31, 32, 0, 0, 0, 12, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
79            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
80            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x70, 0x6c,
81            0x61, 0x62, 0x62, 0x6c, 0x65, 0x2e, 0x6f, 0x72, 0x67,
82        ];
83
84        let cert = Certificate::from_bytes(cert, None).unwrap();
85        assert_eq!(1, cert.public_key[0]);
86        assert_eq!(32, cert.public_key[31]);
87        assert_eq!(64, cert.signature.len());
88        assert_eq!(
89            OffsetDateTime::from_unix_timestamp(EPOCH + 12).unwrap(),
90            cert.valid_from
91        );
92        assert_eq!(
93            OffsetDateTime::from_unix_timestamp(EPOCH + 14).unwrap(),
94            cert.valid_to
95        );
96        assert_eq!("plabble.org", &cert.domain_or_ip);
97    }
98
99    #[test]
100    fn can_serialize_cert() {
101        let cert1 = Certificate {
102            public_key: [2u8; 32],
103            signature: [3u8; 64],
104            valid_from: datetime!(2020-01-01 00:00:00 UTC),
105            valid_to: datetime!(2020-01-15 00:00:00 UTC),
106            domain_or_ip: String::from("cert.plabble.org"),
107        };
108        let cert2 = Certificate::from_bytes(&cert1.get_bytes(), None).unwrap();
109        assert_eq!(cert1.domain_or_ip, cert2.domain_or_ip);
110        assert_eq!(cert1.public_key, cert2.public_key);
111        assert_eq!(cert1.signature, cert2.signature);
112        assert_eq!(cert1.valid_from, cert2.valid_from);
113        assert_eq!(cert1.valid_to, cert2.valid_to);
114    }
115}