use chrono::{DateTime,Utc}; use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1}; use x509::error::X509ParseError; #[derive(Clone,Debug,PartialEq)] pub struct Validity { not_before: DateTime, not_after: DateTime } fn decode_validity_data(bs: &ASN1Block) -> Result { // Validity ::= SEQUENCE { // notBefore Time, // notAfter Time } match bs { &ASN1Block::Sequence(_, _, ref valxs) => { if valxs.len() != 2 { return Err(X509ParseError::IllFormedValidity); } let nb = get_time(&valxs[0])?; let na = get_time(&valxs[1])?; Ok(Validity{ not_before: nb, not_after: na }) } _ => Err(X509ParseError::IllFormedValidity) } } impl FromASN1 for Validity { type Error = X509ParseError; fn from_asn1(v: &[ASN1Block]) -> Result<(Validity,&[ASN1Block]),X509ParseError> { match v.split_first() { None => Err(X509ParseError::NotEnoughData), Some((x, rest)) => { let v = decode_validity_data(&x)?; Ok((v, rest)) } } } } fn encode_validity_data(c: ASN1Class, v: &Validity) -> ASN1Block { let mut vs = Vec::with_capacity(2); vs.push(ASN1Block::GeneralizedTime(c, 0, v.not_before)); vs.push(ASN1Block::GeneralizedTime(c, 0, v.not_after)); ASN1Block::Sequence(c, 0, vs) } impl ToASN1 for Validity { type Error = ASN1EncodeErr; fn to_asn1_class(&self, c: ASN1Class) -> Result,ASN1EncodeErr> { let block = encode_validity_data(c, self); Ok(vec![block]) } } fn get_time(b: &ASN1Block) -> Result, X509ParseError> { match b { &ASN1Block::UTCTime(_, _, v) => Ok(v.clone()), &ASN1Block::GeneralizedTime(_, _, v) => Ok(v.clone()), _ => Err(X509ParseError::IllFormedValidity) } } #[cfg(test)] mod test { use chrono::TimeZone; use chrono::offset::LocalResult; use quickcheck::{Arbitrary,Gen}; use rand::Rng; use super::*; fn arbitrary_date(g: &mut G) -> DateTime { loop { let y = g.gen_range(1900,3000); let mo = g.gen_range(0,12); let d = g.gen_range(0,31); let h = g.gen_range(0,24); let mi = g.gen_range(0,60); let s = g.gen_range(0,60); match Utc.ymd_opt(y,mo,d).and_hms_opt(h,mi,s) { LocalResult::None => continue, LocalResult::Single(x) => return x, LocalResult::Ambiguous(x,_) => return x } } } impl Arbitrary for Validity { fn arbitrary(g: &mut G) -> Validity { Validity { not_before: arbitrary_date(g), not_after: arbitrary_date(g) } } } quickcheck! { fn validity_roundtrips(v: Validity) -> bool { let bstr = encode_validity_data(ASN1Class::Universal, &v); match decode_validity_data(&bstr) { Err(_) => false, Ok(v2) => v == v2 } } } }