code cleanup

This commit is contained in:
core 2023-05-02 20:48:04 -04:00
parent 130aabad38
commit 4f9563755a
Signed by: core
GPG Key ID: FDBF740DADDCEECF
4 changed files with 360 additions and 99 deletions

View File

@ -1,8 +1,8 @@
use std::collections::HashMap;
use crate::pki::{EPFCertificate, EpfPublicKey}; use crate::pki::{EPFCertificate, EpfPublicKey};
use std::collections::HashMap;
pub struct EpfCaPool { pub struct EpfCaPool {
pub ca_lookup_table: HashMap<EpfPublicKey, EPFCertificate> pub ca_lookup_table: HashMap<EpfPublicKey, EPFCertificate>,
} }
pub trait EpfCaPoolOps { pub trait EpfCaPoolOps {
@ -14,7 +14,7 @@ pub trait EpfCaPoolOps {
impl EpfCaPoolOps for EpfCaPool { impl EpfCaPoolOps for EpfCaPool {
fn new() -> Self { fn new() -> Self {
Self { Self {
ca_lookup_table: HashMap::new() ca_lookup_table: HashMap::new(),
} }
} }
@ -23,6 +23,7 @@ impl EpfCaPoolOps for EpfCaPool {
} }
fn insert(&mut self, cert: &EPFCertificate) { fn insert(&mut self, cert: &EPFCertificate) {
self.ca_lookup_table.insert(cert.details.public_key, cert.clone()); self.ca_lookup_table
.insert(cert.details.public_key, cert.clone());
} }
} }

View File

@ -1,3 +1,3 @@
pub mod ca_pool;
pub mod pki; pub mod pki;
pub mod util; pub mod util;
pub mod ca_pool;

View File

@ -1,18 +1,19 @@
use std::collections::HashMap; use crate::ca_pool::{EpfCaPool, EpfCaPoolOps};
use std::error::Error; use crate::util::{pretty_print_date, u64_to_st};
use std::fmt::{Display, Formatter};
use std::time::{SystemTime};
use ed25519_dalek::{Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey}; use ed25519_dalek::{Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey};
use pem::Pem; use pem::Pem;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use crate::ca_pool::{EpfCaPool, EpfCaPoolOps}; use std::collections::HashMap;
use crate::util::{pretty_print_date, u64_to_st}; use std::error::Error;
use std::fmt::{Display, Formatter};
use std::time::SystemTime;
pub const EPFPKI_PUBLIC_KEY_LENGTH: usize = 32; pub const EPFPKI_PUBLIC_KEY_LENGTH: usize = 32;
pub const EPFPKI_SIGNATURE_LENGTH: usize = 64; pub const EPFPKI_SIGNATURE_LENGTH: usize = 64;
pub const EPFPKI_SELF_SIGNED_CERTIFICATE: &str = "0000000000000000000000000000000000000000000000000000000000000000"; pub const EPFPKI_SELF_SIGNED_CERTIFICATE: &str =
"0000000000000000000000000000000000000000000000000000000000000000";
pub type EpfPublicKey = [u8; 32]; pub type EpfPublicKey = [u8; 32];
pub type EpfPrivateKey = [u8; 64]; pub type EpfPrivateKey = [u8; 64];
@ -22,7 +23,7 @@ pub struct EPFCertificate {
pub details: EPFCertificateDetails, pub details: EPFCertificateDetails,
pub fingerprint: String, pub fingerprint: String,
#[serde(with = "serde_arrays")] #[serde(with = "serde_arrays")]
pub signature: [u8; EPFPKI_SIGNATURE_LENGTH] pub signature: [u8; EPFPKI_SIGNATURE_LENGTH],
} }
#[derive(Serialize, Deserialize, PartialEq, Debug, Eq, Clone)] #[derive(Serialize, Deserialize, PartialEq, Debug, Eq, Clone)]
@ -36,17 +37,21 @@ pub struct EPFCertificateDetails {
pub issuer_public_key: [u8; EPFPKI_PUBLIC_KEY_LENGTH], pub issuer_public_key: [u8; EPFPKI_PUBLIC_KEY_LENGTH],
pub claims: HashMap<String, String> pub claims: HashMap<String, String>,
} }
pub trait EpfPkiSerializable { pub trait EpfPkiSerializable {
const PEM_BANNER: &'static str; const PEM_BANNER: &'static str;
fn as_bytes(&self) -> Result<Vec<u8>, rmp_serde::encode::Error>; fn as_bytes(&self) -> Result<Vec<u8>, rmp_serde::encode::Error>;
fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> where Self: Sized; fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error>
where
Self: Sized;
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>>; fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>>;
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>> where Self: Sized; fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
where
Self: Sized;
} }
pub fn fingerprint(cert: &EPFCertificateDetails) -> Result<String, rmp_serde::encode::Error> { pub fn fingerprint(cert: &EPFCertificateDetails) -> Result<String, rmp_serde::encode::Error> {
@ -69,20 +74,41 @@ pub enum EpfPkiCertificateValidationError {
FingerprintDoesNotMatch { expected: String, got: String }, FingerprintDoesNotMatch { expected: String, got: String },
InvalidSignature { e: SignatureError }, InvalidSignature { e: SignatureError },
ExpiresAfterSigner, ExpiresAfterSigner,
ValidAfterSigner ValidAfterSigner,
} }
impl Display for EpfPkiCertificateValidationError { impl Display for EpfPkiCertificateValidationError {
#[cfg_attr(tarpaulin, ignore)] #[cfg_attr(tarpaulin, ignore)]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
EpfPkiCertificateValidationError::NoLongerValid { expired_at } => write!(f, "Certificate no longer valid (expired at {})", pretty_print_date(expired_at)), EpfPkiCertificateValidationError::NoLongerValid { expired_at } => write!(
EpfPkiCertificateValidationError::NotValidYet { valid_at } => write!(f, "Certificate not valid yet (valid at {})", pretty_print_date(valid_at)), f,
EpfPkiCertificateValidationError::InvalidCertificateData { e } => write!(f, "Unable to serialize cert data: {}", e), "Certificate no longer valid (expired at {})",
EpfPkiCertificateValidationError::FingerprintDoesNotMatch { expected, got } => write!(f, "Fingerprint mismatch (expected {}, got {})", expected, got), pretty_print_date(expired_at)
EpfPkiCertificateValidationError::InvalidSignature { e } => write!(f, "Certificate validation error: {}", e), ),
EpfPkiCertificateValidationError::ExpiresAfterSigner => write!(f, "Certificate expires after it's signing certificate"), EpfPkiCertificateValidationError::NotValidYet { valid_at } => write!(
EpfPkiCertificateValidationError::ValidAfterSigner => write!(f, "Certificate is valid longer than it's signing certificate") f,
"Certificate not valid yet (valid at {})",
pretty_print_date(valid_at)
),
EpfPkiCertificateValidationError::InvalidCertificateData { e } => {
write!(f, "Unable to serialize cert data: {}", e)
}
EpfPkiCertificateValidationError::FingerprintDoesNotMatch { expected, got } => write!(
f,
"Fingerprint mismatch (expected {}, got {})",
expected, got
),
EpfPkiCertificateValidationError::InvalidSignature { e } => {
write!(f, "Certificate validation error: {}", e)
}
EpfPkiCertificateValidationError::ExpiresAfterSigner => {
write!(f, "Certificate expires after it's signing certificate")
}
EpfPkiCertificateValidationError::ValidAfterSigner => write!(
f,
"Certificate is valid longer than it's signing certificate"
),
} }
} }
} }
@ -90,7 +116,11 @@ impl Display for EpfPkiCertificateValidationError {
pub trait EpfPkiCertificateOps { pub trait EpfPkiCertificateOps {
fn recalculate_fingerprint(&mut self) -> Result<(), rmp_serde::encode::Error>; fn recalculate_fingerprint(&mut self) -> Result<(), rmp_serde::encode::Error>;
fn sign(&mut self, private_key: &EpfPrivateKey) -> Result<(), Box<dyn Error>>; fn sign(&mut self, private_key: &EpfPrivateKey) -> Result<(), Box<dyn Error>>;
fn verify_with_time(&self, time: SystemTime, ca_pool: &EpfCaPool) -> Result<bool, EpfPkiCertificateValidationError>; fn verify_with_time(
&self,
time: SystemTime,
ca_pool: &EpfCaPool,
) -> Result<bool, EpfPkiCertificateValidationError>;
fn verify(&self, ca_pool: &EpfCaPool) -> Result<bool, EpfPkiCertificateValidationError>; fn verify(&self, ca_pool: &EpfCaPool) -> Result<bool, EpfPkiCertificateValidationError>;
} }
impl EpfPkiCertificateOps for EPFCertificate { impl EpfPkiCertificateOps for EPFCertificate {
@ -116,43 +146,64 @@ impl EpfPkiCertificateOps for EPFCertificate {
Ok(()) Ok(())
} }
fn verify_with_time(&self, time: SystemTime, ca_pool: &EpfCaPool) -> Result<bool, EpfPkiCertificateValidationError> { fn verify_with_time(
&self,
time: SystemTime,
ca_pool: &EpfCaPool,
) -> Result<bool, EpfPkiCertificateValidationError> {
// Is it expired // Is it expired
if u64_to_st(self.details.not_after) < time { if u64_to_st(self.details.not_after) < time {
return Err(EpfPkiCertificateValidationError::NoLongerValid {expired_at: u64_to_st(self.details.not_after)}) return Err(EpfPkiCertificateValidationError::NoLongerValid {
expired_at: u64_to_st(self.details.not_after),
});
} }
// Is it valid yet // Is it valid yet
if u64_to_st(self.details.not_before) > time { if u64_to_st(self.details.not_before) > time {
return Err(EpfPkiCertificateValidationError::NotValidYet {valid_at: u64_to_st(self.details.not_before)}) return Err(EpfPkiCertificateValidationError::NotValidYet {
valid_at: u64_to_st(self.details.not_before),
});
} }
let fingerprint_on_cert = fingerprint(&self.details).map_err(|e| EpfPkiCertificateValidationError::InvalidCertificateData { e })?; let fingerprint_on_cert = fingerprint(&self.details)
.map_err(|e| EpfPkiCertificateValidationError::InvalidCertificateData { e })?;
// Does the fingerprint match // Does the fingerprint match
if fingerprint_on_cert != self.fingerprint { if fingerprint_on_cert != self.fingerprint {
return Err(EpfPkiCertificateValidationError::FingerprintDoesNotMatch { expected: self.fingerprint.clone(), got: fingerprint_on_cert}) return Err(EpfPkiCertificateValidationError::FingerprintDoesNotMatch {
expected: self.fingerprint.clone(),
got: fingerprint_on_cert,
});
} }
// Does the signature match // Does the signature match
let signature = Signature::from_slice(&self.signature).map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?; let signature = Signature::from_slice(&self.signature)
.map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?;
let is_self_signed; let is_self_signed;
println!("{}: {:?} {:?}", self.details.name, self.details.issuer_public_key, self.details.public_key); println!(
"{}: {:?} {:?}",
self.details.name, self.details.issuer_public_key, self.details.public_key
);
let verifying_key = if self.details.issuer_public_key == self.details.public_key { let verifying_key = if self.details.issuer_public_key == self.details.public_key {
// self-signed certificate // self-signed certificate
is_self_signed = true; is_self_signed = true;
VerifyingKey::from_bytes(&self.details.public_key).map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })? VerifyingKey::from_bytes(&self.details.public_key)
.map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?
} else { } else {
is_self_signed = false; is_self_signed = false;
VerifyingKey::from_bytes(&self.details.issuer_public_key).map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })? VerifyingKey::from_bytes(&self.details.issuer_public_key)
.map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?
}; };
let cert_data_bytes = rmp_serde::to_vec(&self.details).map_err(|e| EpfPkiCertificateValidationError::InvalidCertificateData { e })?; let cert_data_bytes = rmp_serde::to_vec(&self.details)
.map_err(|e| EpfPkiCertificateValidationError::InvalidCertificateData { e })?;
verifying_key.verify(&cert_data_bytes, &signature).map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?; verifying_key
.verify(&cert_data_bytes, &signature)
.map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?;
// Signature OK // Signature OK
@ -164,7 +215,7 @@ impl EpfPkiCertificateOps for EPFCertificate {
return Ok(false); return Ok(false);
} }
} else if let Some(cert) = ca_pool.get_ca(&self.details.issuer_public_key) { } else if let Some(cert) = ca_pool.get_ca(&self.details.issuer_public_key) {
cert cert
} else { } else {
return Ok(false); return Ok(false);
}; };
@ -175,7 +226,7 @@ impl EpfPkiCertificateOps for EPFCertificate {
} }
// Make sure this cert isnt valid after the root // Make sure this cert isnt valid after the root
if ca_cert.details.not_before > self.details.not_before { if ca_cert.details.not_before > self.details.not_before {
return Err(EpfPkiCertificateValidationError::ValidAfterSigner) return Err(EpfPkiCertificateValidationError::ValidAfterSigner);
} }
// Cert OK // Cert OK
@ -194,10 +245,26 @@ impl Display for EPFCertificate {
writeln!(f, "EPFCertificate {{")?; writeln!(f, "EPFCertificate {{")?;
writeln!(f, "\tDetails: {{")?; writeln!(f, "\tDetails: {{")?;
writeln!(f, "\t\tName: {}", self.details.name)?; writeln!(f, "\t\tName: {}", self.details.name)?;
writeln!(f, "\t\tNot Before: {}", pretty_print_date(&u64_to_st(self.details.not_before)))?; writeln!(
writeln!(f, "\t\tNot After: {}", pretty_print_date(&u64_to_st(self.details.not_after)))?; f,
writeln!(f, "\t\tPublic Key: {}", hex::encode(self.details.public_key))?; "\t\tNot Before: {}",
writeln!(f, "\t\tIssuer Fingerprint: {}", hex::encode(self.details.issuer_public_key))?; pretty_print_date(&u64_to_st(self.details.not_before))
)?;
writeln!(
f,
"\t\tNot After: {}",
pretty_print_date(&u64_to_st(self.details.not_after))
)?;
writeln!(
f,
"\t\tPublic Key: {}",
hex::encode(self.details.public_key)
)?;
writeln!(
f,
"\t\tIssuer Fingerprint: {}",
hex::encode(self.details.issuer_public_key)
)?;
writeln!(f, "\t}}")?; writeln!(f, "\t}}")?;
writeln!(f, "\tFingerprint: {}", self.fingerprint)?; writeln!(f, "\tFingerprint: {}", self.fingerprint)?;
writeln!(f, "\tSignature: {}", hex::encode(self.signature))?; writeln!(f, "\tSignature: {}", hex::encode(self.signature))?;
@ -217,13 +284,18 @@ impl EpfPkiSerializable for EPFCertificate {
} }
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> { fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?)).as_bytes().to_vec()) Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?))
.as_bytes()
.to_vec())
} }
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>> where Self: Sized { fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
where
Self: Sized,
{
let pem = pem::parse(bytes)?; let pem = pem::parse(bytes)?;
if pem.tag() != Self::PEM_BANNER { if pem.tag() != Self::PEM_BANNER {
return Err("Not a certificate".into()) return Err("Not a certificate".into());
} }
Ok(Self::from_bytes(pem.contents())?) Ok(Self::from_bytes(pem.contents())?)
} }
@ -237,17 +309,24 @@ impl EpfPkiSerializable for EpfPublicKey {
} }
fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> { fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
bytes.try_into().map_err(|_| rmp_serde::decode::Error::LengthMismatch(bytes.len() as u32)) bytes
.try_into()
.map_err(|_| rmp_serde::decode::Error::LengthMismatch(bytes.len() as u32))
} }
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> { fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?)).as_bytes().to_vec()) Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?))
.as_bytes()
.to_vec())
} }
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>> where Self: Sized { fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
where
Self: Sized,
{
let pem = pem::parse(bytes)?; let pem = pem::parse(bytes)?;
if pem.tag() != Self::PEM_BANNER { if pem.tag() != Self::PEM_BANNER {
return Err("Not a public key".into()) return Err("Not a public key".into());
} }
Ok(Self::from_bytes(pem.contents())?) Ok(Self::from_bytes(pem.contents())?)
} }
@ -261,17 +340,24 @@ impl EpfPkiSerializable for EpfPrivateKey {
} }
fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> { fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
bytes.try_into().map_err(|_| rmp_serde::decode::Error::LengthMismatch(bytes.len() as u32)) bytes
.try_into()
.map_err(|_| rmp_serde::decode::Error::LengthMismatch(bytes.len() as u32))
} }
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> { fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?)).as_bytes().to_vec()) Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?))
.as_bytes()
.to_vec())
} }
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>> where Self: Sized { fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
where
Self: Sized,
{
let pem = pem::parse(bytes)?; let pem = pem::parse(bytes)?;
if pem.tag() != Self::PEM_BANNER { if pem.tag() != Self::PEM_BANNER {
return Err("Incorrect PEM tag".into()) return Err("Incorrect PEM tag".into());
} }
Ok(Self::from_bytes(pem.contents())?) Ok(Self::from_bytes(pem.contents())?)
} }
@ -279,12 +365,16 @@ impl EpfPkiSerializable for EpfPrivateKey {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashMap; use crate::ca_pool::{EpfCaPool, EpfCaPoolOps};
use std::time::{SystemTime, UNIX_EPOCH}; use crate::pki::{
EPFCertificate, EPFCertificateDetails, EpfPkiCertificateOps,
EpfPkiCertificateValidationError, EpfPkiSerializable, EpfPrivateKey, EpfPublicKey,
EPFPKI_PUBLIC_KEY_LENGTH, EPFPKI_SIGNATURE_LENGTH,
};
use ed25519_dalek::{SignatureError, SigningKey}; use ed25519_dalek::{SignatureError, SigningKey};
use rand::rngs::OsRng; use rand::rngs::OsRng;
use crate::ca_pool::{EpfCaPool, EpfCaPoolOps}; use std::collections::HashMap;
use crate::pki::{EPFCertificate, EPFCertificateDetails, EPFPKI_PUBLIC_KEY_LENGTH, EPFPKI_SIGNATURE_LENGTH, EpfPkiCertificateOps, EpfPkiCertificateValidationError, EpfPkiSerializable, EpfPrivateKey, EpfPublicKey}; use std::time::{SystemTime, UNIX_EPOCH};
#[test] #[test]
pub fn certificate_serialization() { pub fn certificate_serialization() {
@ -329,7 +419,10 @@ mod tests {
#[test] #[test]
pub fn pubkey_deserialization_pem() { pub fn pubkey_deserialization_pem() {
assert_eq!(EpfPublicKey::from_pem(&null_public_key_pem()).unwrap(), [0u8; 32]) assert_eq!(
EpfPublicKey::from_pem(&null_public_key_pem()).unwrap(),
[0u8; 32]
)
} }
#[test] #[test]
@ -355,13 +448,19 @@ mod tests {
#[test] #[test]
pub fn privkey_deserialization_pem() { pub fn privkey_deserialization_pem() {
assert_eq!(EpfPrivateKey::from_pem(&null_private_key_pem()).unwrap(), [0u8; 64]) assert_eq!(
EpfPrivateKey::from_pem(&null_private_key_pem()).unwrap(),
[0u8; 64]
)
} }
#[test] #[test]
#[should_panic] #[should_panic]
pub fn privkey_deserialization_pem_wrong_tag() { pub fn privkey_deserialization_pem_wrong_tag() {
assert_eq!(EpfPrivateKey::from_pem(&null_public_key_pem()).unwrap(), [0u8; 64]) assert_eq!(
EpfPrivateKey::from_pem(&null_public_key_pem()).unwrap(),
[0u8; 64]
)
} }
#[test] #[test]
@ -373,7 +472,10 @@ mod tests {
pub fn cert_fingerprinting() { pub fn cert_fingerprinting() {
let mut cert = cert(); let mut cert = cert();
cert.recalculate_fingerprint().unwrap(); cert.recalculate_fingerprint().unwrap();
assert_eq!(cert.fingerprint, "922c5cb83633b214d19d9aebf387314fcde67210ff92bd9691fbd059141d6adf"); assert_eq!(
cert.fingerprint,
"922c5cb83633b214d19d9aebf387314fcde67210ff92bd9691fbd059141d6adf"
);
} }
#[test] #[test]
@ -389,8 +491,16 @@ mod tests {
let mut ca_cert = EPFCertificate { let mut ca_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing CA".to_string(), name: "Testing CA".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 10, not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 60, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 10,
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 60,
public_key: *public_key.as_bytes(), public_key: *public_key.as_bytes(),
issuer_public_key: [0u8; 32], issuer_public_key: [0u8; 32],
claims: Default::default(), claims: Default::default(),
@ -416,8 +526,16 @@ mod tests {
let mut not_ca_cert = EPFCertificate { let mut not_ca_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing Certificate".to_string(), name: "Testing Certificate".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 10, not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 60, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 10,
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 60,
public_key: *public_key2.as_bytes(), public_key: *public_key2.as_bytes(),
issuer_public_key: [0u8; 32], issuer_public_key: [0u8; 32],
claims: Default::default(), claims: Default::default(),
@ -436,8 +554,15 @@ mod tests {
let expired_cert = EPFCertificate { let expired_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing Certificate - Expired".to_string(), name: "Testing Certificate - Expired".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(), not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() -20, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 20,
public_key: [0u8; 32], public_key: [0u8; 32],
issuer_public_key: [0u8; 32], issuer_public_key: [0u8; 32],
claims: Default::default(), claims: Default::default(),
@ -448,7 +573,10 @@ mod tests {
let ca_pool = EpfCaPool::new(); let ca_pool = EpfCaPool::new();
assert!(matches!(expired_cert.verify(&ca_pool).unwrap_err(), EpfPkiCertificateValidationError::NoLongerValid { .. })) assert!(matches!(
expired_cert.verify(&ca_pool).unwrap_err(),
EpfPkiCertificateValidationError::NoLongerValid { .. }
))
} }
#[test] #[test]
@ -456,8 +584,16 @@ mod tests {
let not_yet_valid_cert = EPFCertificate { let not_yet_valid_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing Certificate - Not Yet Valid".to_string(), name: "Testing Certificate - Not Yet Valid".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 20, not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 30, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 20,
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 30,
public_key: [0u8; 32], public_key: [0u8; 32],
issuer_public_key: [0u8; 32], issuer_public_key: [0u8; 32],
claims: Default::default(), claims: Default::default(),
@ -468,7 +604,10 @@ mod tests {
let ca_pool = EpfCaPool::new(); let ca_pool = EpfCaPool::new();
assert!(matches!(not_yet_valid_cert.verify(&ca_pool).unwrap_err(), EpfPkiCertificateValidationError::NotValidYet { .. })) assert!(matches!(
not_yet_valid_cert.verify(&ca_pool).unwrap_err(),
EpfPkiCertificateValidationError::NotValidYet { .. }
))
} }
#[test] #[test]
@ -479,8 +618,16 @@ mod tests {
let mut not_trusted_cert = EPFCertificate { let mut not_trusted_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing Certificate - Not Trusted".to_string(), name: "Testing Certificate - Not Trusted".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 20, not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 30, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 20,
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 30,
public_key: [0u8; 32], public_key: [0u8; 32],
issuer_public_key: *public_key.as_bytes(), issuer_public_key: *public_key.as_bytes(),
claims: Default::default(), claims: Default::default(),
@ -488,7 +635,9 @@ mod tests {
fingerprint: "".to_string(), fingerprint: "".to_string(),
signature: [0u8; EPFPKI_SIGNATURE_LENGTH], signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
}; };
not_trusted_cert.sign(&private_key.to_keypair_bytes()).unwrap(); not_trusted_cert
.sign(&private_key.to_keypair_bytes())
.unwrap();
let ca_pool = EpfCaPool::new(); let ca_pool = EpfCaPool::new();
@ -508,8 +657,16 @@ mod tests {
let mut ca_cert = EPFCertificate { let mut ca_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing CA".to_string(), name: "Testing CA".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 10, not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 60, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 10,
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 60,
public_key: *public_key.as_bytes(), public_key: *public_key.as_bytes(),
issuer_public_key: [0u8; 32], issuer_public_key: [0u8; 32],
claims: Default::default(), claims: Default::default(),
@ -524,8 +681,16 @@ mod tests {
let mut not_ca_cert = EPFCertificate { let mut not_ca_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing Certificate - Valid After Signer".to_string(), name: "Testing Certificate - Valid After Signer".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 10, not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 120, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 10,
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 120,
public_key: *public_key2.as_bytes(), public_key: *public_key2.as_bytes(),
issuer_public_key: [0u8; 32], issuer_public_key: [0u8; 32],
claims: Default::default(), claims: Default::default(),
@ -536,7 +701,10 @@ mod tests {
assert!(not_ca_cert.verify(&ca_pool).is_err()); assert!(not_ca_cert.verify(&ca_pool).is_err());
not_ca_cert.sign(&private_key.to_keypair_bytes()).unwrap(); not_ca_cert.sign(&private_key.to_keypair_bytes()).unwrap();
assert!(matches!(not_ca_cert.verify(&ca_pool).unwrap_err(), EpfPkiCertificateValidationError::ExpiresAfterSigner)); assert!(matches!(
not_ca_cert.verify(&ca_pool).unwrap_err(),
EpfPkiCertificateValidationError::ExpiresAfterSigner
));
} }
#[test] #[test]
@ -552,8 +720,16 @@ mod tests {
let mut ca_cert = EPFCertificate { let mut ca_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing CA".to_string(), name: "Testing CA".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 10, not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 60, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 10,
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 60,
public_key: *public_key.as_bytes(), public_key: *public_key.as_bytes(),
issuer_public_key: [0u8; 32], issuer_public_key: [0u8; 32],
claims: Default::default(), claims: Default::default(),
@ -568,8 +744,16 @@ mod tests {
let mut not_ca_cert = EPFCertificate { let mut not_ca_cert = EPFCertificate {
details: EPFCertificateDetails { details: EPFCertificateDetails {
name: "Testing Certificate - Valid After Signer".to_string(), name: "Testing Certificate - Valid After Signer".to_string(),
not_before: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 200, not_before: SystemTime::now()
not_after: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 10, .duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 200,
not_after: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ 10,
public_key: *public_key2.as_bytes(), public_key: *public_key2.as_bytes(),
issuer_public_key: [0u8; 32], issuer_public_key: [0u8; 32],
claims: Default::default(), claims: Default::default(),
@ -580,16 +764,45 @@ mod tests {
assert!(not_ca_cert.verify(&ca_pool).is_err()); assert!(not_ca_cert.verify(&ca_pool).is_err());
not_ca_cert.sign(&private_key.to_keypair_bytes()).unwrap(); not_ca_cert.sign(&private_key.to_keypair_bytes()).unwrap();
assert!(matches!(not_ca_cert.verify(&ca_pool).unwrap_err(), EpfPkiCertificateValidationError::ValidAfterSigner)); assert!(matches!(
not_ca_cert.verify(&ca_pool).unwrap_err(),
EpfPkiCertificateValidationError::ValidAfterSigner
));
} }
#[test] #[test]
pub fn verifying_error_display() { pub fn verifying_error_display() {
println!("{}", EpfPkiCertificateValidationError::NoLongerValid { expired_at: SystemTime::now() }); println!(
println!("{}", EpfPkiCertificateValidationError::NotValidYet { valid_at: SystemTime::now() }); "{}",
println!("{}", EpfPkiCertificateValidationError::InvalidCertificateData { e: rmp_serde::encode::Error::UnknownLength}); EpfPkiCertificateValidationError::NoLongerValid {
println!("{}", EpfPkiCertificateValidationError::FingerprintDoesNotMatch { expected: "".to_string(), got: "".to_string() }); expired_at: SystemTime::now()
println!("{}", EpfPkiCertificateValidationError::InvalidSignature { e: SignatureError::new() }); }
);
println!(
"{}",
EpfPkiCertificateValidationError::NotValidYet {
valid_at: SystemTime::now()
}
);
println!(
"{}",
EpfPkiCertificateValidationError::InvalidCertificateData {
e: rmp_serde::encode::Error::UnknownLength
}
);
println!(
"{}",
EpfPkiCertificateValidationError::FingerprintDoesNotMatch {
expected: "".to_string(),
got: "".to_string()
}
);
println!(
"{}",
EpfPkiCertificateValidationError::InvalidSignature {
e: SignatureError::new()
}
);
println!("{}", EpfPkiCertificateValidationError::ExpiresAfterSigner); println!("{}", EpfPkiCertificateValidationError::ExpiresAfterSigner);
println!("{}", EpfPkiCertificateValidationError::ValidAfterSigner); println!("{}", EpfPkiCertificateValidationError::ValidAfterSigner);
} }
@ -602,22 +815,69 @@ mod tests {
not_after: 0, not_after: 0,
public_key: [0u8; EPFPKI_PUBLIC_KEY_LENGTH], public_key: [0u8; EPFPKI_PUBLIC_KEY_LENGTH],
issuer_public_key: [0u8; EPFPKI_PUBLIC_KEY_LENGTH], issuer_public_key: [0u8; EPFPKI_PUBLIC_KEY_LENGTH],
claims: HashMap::new() claims: HashMap::new(),
}, },
fingerprint: "0000000000000000000000000000000000000000000000000000000000000000".to_string(), fingerprint: "0000000000000000000000000000000000000000000000000000000000000000"
.to_string(),
signature: [0u8; EPFPKI_SIGNATURE_LENGTH], signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
} }
} }
fn cert_bytes() -> Vec<u8> { fn cert_bytes() -> Vec<u8> {
vec![147, 150, 187, 73, 110, 118, 97, 108, 105, 100, 32, 84, 101, 115, 116, 105, 110, 103, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 0, 0, 220, 0, 32, 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, 220, 0, 32, 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, 128, 217, 64, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 220, 0, 64, 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] vec![
147, 150, 187, 73, 110, 118, 97, 108, 105, 100, 32, 84, 101, 115, 116, 105, 110, 103,
32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 0, 0, 220, 0, 32, 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,
220, 0, 32, 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, 128, 217, 64, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 220, 0, 64, 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,
]
} }
fn cert_pem() -> Vec<u8> { fn cert_pem() -> Vec<u8> {
vec![45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 69, 80, 70, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 13, 10, 107, 53, 97, 55, 83, 87, 53, 50, 89, 87, 120, 112, 90, 67, 66, 85, 90, 88, 78, 48, 97, 87, 53, 110, 73, 69, 78, 108, 99, 110, 82, 112, 90, 109, 108, 106, 89, 88, 82, 108, 65, 65, 68, 99, 65, 67, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 78, 119, 65, 73, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 103, 78, 108, 65, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 13, 10, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 78, 119, 65, 81, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 61, 13, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 69, 80, 70, 32, 67, 69, 82, 84, 73, 70, 73, 67, 65, 84, 69, 45, 45, 45, 45, 45, 13, 10] vec![
45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 69, 80, 70, 32, 67, 69, 82, 84, 73, 70, 73,
67, 65, 84, 69, 45, 45, 45, 45, 45, 13, 10, 107, 53, 97, 55, 83, 87, 53, 50, 89, 87,
120, 112, 90, 67, 66, 85, 90, 88, 78, 48, 97, 87, 53, 110, 73, 69, 78, 108, 99, 110,
82, 112, 90, 109, 108, 106, 89, 88, 82, 108, 65, 65, 68, 99, 65, 67, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 13, 10, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 78, 119,
65, 73, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 13, 10, 65, 65, 65, 65, 65,
65, 65, 65, 103, 78, 108, 65, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77,
68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77,
68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 13,
10, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65,
119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 68, 65, 119, 77, 78, 119, 65, 81, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 61, 13,
10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 69, 80, 70, 32, 67, 69, 82, 84, 73, 70, 73, 67,
65, 84, 69, 45, 45, 45, 45, 45, 13, 10,
]
} }
fn null_public_key_pem() -> Vec<u8> { fn null_public_key_pem() -> Vec<u8> {
vec![45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 69, 80, 70, 32, 80, 85, 66, 76, 73, 67, 32, 75, 69, 89, 45, 45, 45, 45, 45, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 61, 13, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 69, 80, 70, 32, 80, 85, 66, 76, 73, 67, 32, 75, 69, 89, 45, 45, 45, 45, 45, 13, 10] vec![
45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 69, 80, 70, 32, 80, 85, 66, 76, 73, 67, 32,
75, 69, 89, 45, 45, 45, 45, 45, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 61, 13, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 69,
80, 70, 32, 80, 85, 66, 76, 73, 67, 32, 75, 69, 89, 45, 45, 45, 45, 45, 13, 10,
]
} }
fn null_private_key_pem() -> Vec<u8> { fn null_private_key_pem() -> Vec<u8> {
vec![45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 69, 80, 70, 32, 80, 82, 73, 86, 65, 84, 69, 32, 75, 69, 89, 45, 45, 45, 45, 45, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 61, 61, 13, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 69, 80, 70, 32, 80, 82, 73, 86, 65, 84, 69, 32, 75, 69, 89, 45, 45, 45, 45, 45, 13, 10] vec![
45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 69, 80, 70, 32, 80, 82, 73, 86, 65, 84, 69,
32, 75, 69, 89, 45, 45, 45, 45, 45, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 61, 61, 13, 10, 45, 45, 45, 45, 45, 69, 78,
68, 32, 69, 80, 70, 32, 80, 82, 73, 86, 65, 84, 69, 32, 75, 69, 89, 45, 45, 45, 45, 45,
13, 10,
]
} }
} }

View File

@ -1,6 +1,6 @@
use chrono::{DateTime, Utc};
use std::ops::Add; use std::ops::Add;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use chrono::{DateTime, Utc};
pub fn pretty_print_date(date: &SystemTime) -> String { pub fn pretty_print_date(date: &SystemTime) -> String {
let datetime: DateTime<Utc> = (*date).into(); let datetime: DateTime<Utc> = (*date).into();