use time::OffsetDateTime;
use crate::{
abstractions::{Serializable, SerializationError, SerializationInfo, KEY_SIZE, SIGNATURE_SIZE},
codec::common::{assert_len, plabble_date},
};
#[derive(Debug)]
pub struct Certificate {
pub public_key: [u8; KEY_SIZE],
pub valid_from: OffsetDateTime,
pub valid_to: OffsetDateTime,
pub signature: [u8; SIGNATURE_SIZE],
pub domain_or_ip: String,
}
impl Serializable for Certificate {
fn size(&self) -> usize {
KEY_SIZE + 4 + 4 + SIGNATURE_SIZE + self.domain_or_ip.len()
}
fn get_bytes(&self) -> Vec<u8> {
let mut buff = Vec::new();
buff.extend_from_slice(&self.public_key);
buff.extend_from_slice(&plabble_date::to_timestamp(&self.valid_from).to_be_bytes());
buff.extend_from_slice(&plabble_date::to_timestamp(&self.valid_to).to_be_bytes());
buff.extend_from_slice(&self.signature);
buff.extend_from_slice(self.domain_or_ip.as_bytes());
buff
}
fn from_bytes(data: &[u8], _: Option<SerializationInfo>) -> Result<Self, SerializationError>
where
Self: Sized,
{
assert_len(data, KEY_SIZE + SIGNATURE_SIZE + 8)?;
let mut cert = Self {
public_key: [0u8; KEY_SIZE],
valid_from: plabble_date::from_timestamp(u32::from_be_bytes(
data[KEY_SIZE..(KEY_SIZE + 4)].try_into().unwrap(),
)),
valid_to: plabble_date::from_timestamp(u32::from_be_bytes(
data[(KEY_SIZE + 4)..(KEY_SIZE + 8)].try_into().unwrap(),
)),
signature: [0u8; SIGNATURE_SIZE],
domain_or_ip: String::new(),
};
cert.public_key.copy_from_slice(&data[..KEY_SIZE]);
cert.signature
.copy_from_slice(&data[(KEY_SIZE + 8)..(KEY_SIZE + 8 + SIGNATURE_SIZE)]);
cert.domain_or_ip =
String::from_utf8_lossy(&data[(KEY_SIZE + 8 + SIGNATURE_SIZE)..]).to_string();
Ok(cert)
}
}
#[cfg(test)]
mod test {
use time_macros::datetime;
use crate::abstractions::EPOCH;
use super::*;
#[test]
fn can_parse_cert() {
let cert = &[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
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,
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,
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,
0x61, 0x62, 0x62, 0x6c, 0x65, 0x2e, 0x6f, 0x72, 0x67,
];
let cert = Certificate::from_bytes(cert, None).unwrap();
assert_eq!(1, cert.public_key[0]);
assert_eq!(32, cert.public_key[31]);
assert_eq!(64, cert.signature.len());
assert_eq!(
OffsetDateTime::from_unix_timestamp(EPOCH + 12).unwrap(),
cert.valid_from
);
assert_eq!(
OffsetDateTime::from_unix_timestamp(EPOCH + 14).unwrap(),
cert.valid_to
);
assert_eq!("plabble.org", &cert.domain_or_ip);
}
#[test]
fn can_serialize_cert() {
let cert1 = Certificate {
public_key: [2u8; 32],
signature: [3u8; 64],
valid_from: datetime!(2020-01-01 00:00:00 UTC),
valid_to: datetime!(2020-01-15 00:00:00 UTC),
domain_or_ip: String::from("cert.plabble.org"),
};
let cert2 = Certificate::from_bytes(&cert1.get_bytes(), None).unwrap();
assert_eq!(cert1.domain_or_ip, cert2.domain_or_ip);
assert_eq!(cert1.public_key, cert2.public_key);
assert_eq!(cert1.signature, cert2.signature);
assert_eq!(cert1.valid_from, cert2.valid_from);
assert_eq!(cert1.valid_to, cert2.valid_to);
}
}