From f4b8c7e945e909cb288b56e09a846c376689631b Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Tue, 16 Jan 2018 17:48:58 -0800 Subject: [PATCH] Refactor, still broken, but moving towards not broken. --- .gitignore | 1 + .travis.yml | 8 + Cargo.toml | 1 + src/algident.rs | 354 +++++++++++++++++++++ src/atv.rs | 366 ++++++++++++++++++++++ src/error.rs | 36 +++ src/lib.rs | 746 ++++++++++++++------------------------------- src/misc.rs | 181 +++++++++++ src/name.rs | 137 +++++++++ src/publickey.rs | 245 +++++++++++++++ src/validity.rs | 117 +++++++ test/dsa2048-1.der | Bin 0 -> 1228 bytes test/dsa2048-2.der | Bin 0 -> 1232 bytes test/dsa3072-1.der | Bin 0 -> 1613 bytes test/dsa3072-2.der | Bin 0 -> 1613 bytes test/ec384-1.der | Bin 0 -> 544 bytes test/ec384-2.der | Bin 0 -> 544 bytes test/ec384-3.der | Bin 0 -> 515 bytes 18 files changed, 1670 insertions(+), 522 deletions(-) create mode 100644 .travis.yml create mode 100644 src/algident.rs create mode 100644 src/atv.rs create mode 100644 src/error.rs create mode 100644 src/misc.rs create mode 100644 src/name.rs create mode 100644 src/publickey.rs create mode 100644 src/validity.rs create mode 100644 test/dsa2048-1.der create mode 100644 test/dsa2048-2.der create mode 100644 test/dsa3072-1.der create mode 100644 test/dsa3072-2.der create mode 100644 test/ec384-1.der create mode 100644 test/ec384-2.der create mode 100644 test/ec384-3.der diff --git a/.gitignore b/.gitignore index 143b1ca..c510add 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /target/ **/*.rs.bk Cargo.lock +*.pem diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8c91a74 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: rust +rust: + - stable + - beta + - nightly +matrix: + allow_failures: + - rust: nightly diff --git a/Cargo.toml b/Cargo.toml index 71c054b..b2a9823 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ num = "^0.1.40" sha-1 = "^0.7.0" sha2 = "^0.7.0" simple_asn1 = "^0.1.0" +simple_dsa = { path = "../simple_dsa" } simple_rsa = { path = "../simple_rsa" } [dev-dependencies] diff --git a/src/algident.rs b/src/algident.rs new file mode 100644 index 0000000..00e8679 --- /dev/null +++ b/src/algident.rs @@ -0,0 +1,354 @@ +use error::X509ParseError; +use num::BigUint; +use num::bigint::ToBigInt; +use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1}; + +#[derive(Clone,Debug,PartialEq)] +enum HashAlgorithm { SHA1, SHA224, SHA256, SHA384, SHA512 } + +#[derive(Clone,Debug,PartialEq)] +enum PublicKeyInfo { RSA, DSA, EC } + +#[derive(Clone,Debug,PartialEq)] +pub struct AlgorithmIdentifier { + hash: HashAlgorithm, + algo: PublicKeyInfo +} + +impl FromASN1 for AlgorithmIdentifier { + type Error = X509ParseError; + + fn from_asn1(v: &[ASN1Block]) + -> Result<(AlgorithmIdentifier,&[ASN1Block]),X509ParseError> + { + match v.split_first() { + None => + Err(X509ParseError::NotEnoughData), + Some((x, rest)) => { + let v = decode_algorithm_ident(&x)?; + Ok((v, rest)) + } + } + } +} + +fn decode_algorithm_ident(x: &ASN1Block) + -> Result +{ + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + match x { + &ASN1Block::Sequence(_, _, ref v) if v.len() >= 1 => { + match v[0] { + ASN1Block::ObjectIdentifier(_, _, ref oid) => { + if oid == oid!(1,2,840,113549,1,1,5) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA1, + algo: PublicKeyInfo::RSA + }); + } + if oid == oid!(1,2,840,113549,1,1,11) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA256, + algo: PublicKeyInfo::RSA + }); + } + if oid == oid!(1,2,840,113549,1,1,12) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA384, + algo: PublicKeyInfo::RSA + }); + } + if oid == oid!(1,2,840,113549,1,1,13) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA512, + algo: PublicKeyInfo::RSA + }); + } + if oid == oid!(1,2,840,113549,1,1,14) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA224, + algo: PublicKeyInfo::RSA + }); + } + if oid == oid!(1,2,840,10040,4,3) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA1, + algo: PublicKeyInfo::DSA + }); + } + if oid == oid!(1,2,840,10045,4,1) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA1, + algo: PublicKeyInfo::EC + }); + } + if oid == oid!(1,2,840,10045,4,3,1) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA224, + algo: PublicKeyInfo::EC + }); + } + if oid == oid!(1,2,840,10045,4,3,2) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA256, + algo: PublicKeyInfo::EC + }); + } + if oid == oid!(1,2,840,10045,4,3,3) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA384, + algo: PublicKeyInfo::EC + }); + } + if oid == oid!(1,2,840,10045,4,3,4) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA512, + algo: PublicKeyInfo::EC + }); + } +// if oid == oid!(2,16,840,1,101,3,4,2,1) { +// return Ok(AlgorithmIdentifier { +// hash: HashAlgorithm::SHA256, +// algo: PublicKeyInfo::RSAPSS +// }); +// } +// if oid == oid!(2,16,840,1,101,3,4,2,2) { +// return Ok(AlgorithmIdentifier { +// hash: HashAlgorithm::SHA384, +// algo: PublicKeyInfo::RSAPSS +// }); +// } +// if oid == oid!(2,16,840,1,101,3,4,2,3) { +// return Ok(AlgorithmIdentifier { +// hash: HashAlgorithm::SHA512, +// algo: PublicKeyInfo::RSAPSS +// }); +// } +// if oid == oid!(2,16,840,1,101,3,4,2,4) { +// return Ok(AlgorithmIdentifier { +// hash: HashAlgorithm::SHA224, +// algo: PublicKeyInfo::RSAPSS +// }); +// } + if oid == oid!(2,16,840,1,101,3,4,3,1) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA224, + algo: PublicKeyInfo::DSA + }); + } + if oid == oid!(2,16,840,1,101,3,4,3,2) { + return Ok(AlgorithmIdentifier { + hash: HashAlgorithm::SHA256, + algo: PublicKeyInfo::DSA + }); + } + Err(X509ParseError::UnknownAlgorithm) + } + _ => + Err(X509ParseError::UnknownAlgorithm) + } + } + _ => + Err(X509ParseError::IllFormedAlgoInfo) + } +} + + +pub enum SigAlgEncodeError { + ASN1Error(ASN1EncodeErr), + InvalidDSAValue, InvalidHash +} + +impl From for SigAlgEncodeError { + fn from(e: ASN1EncodeErr) -> SigAlgEncodeError { + SigAlgEncodeError::ASN1Error(e) + } +} + + +impl ToASN1 for AlgorithmIdentifier { + type Error = SigAlgEncodeError; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,SigAlgEncodeError> + { + let block = encode_algorithm_ident(c, self)?; + Ok(vec![block]) + } +} + +fn encode_algorithm_ident(c: ASN1Class, x: &AlgorithmIdentifier) + -> Result +{ + match x.algo { + PublicKeyInfo::RSA => { + match x.hash { + HashAlgorithm::SHA1 => { + let o = oid!(1,2,840,113549,1,1,5); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA224 => { + let o = oid!(1,2,840,113549,1,1,14); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA256 => { + let o = oid!(1,2,840,113549,1,1,11); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA384 => { + let o = oid!(1,2,840,113549,1,1,12); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA512 => { + let o = oid!(1,2,840,113549,1,1,13); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + } + } + PublicKeyInfo::DSA => { + match x.hash { + HashAlgorithm::SHA1 => { + let o = oid!(1,2,840,10040,4,3); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA224 => { + let o = oid!(2,16,840,1,101,3,4,3,1); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA256 => { + let o = oid!(2,16,840,1,101,3,4,3,2); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + _ => + Err(SigAlgEncodeError::InvalidHash), + } + } + PublicKeyInfo::EC => { + match x.hash { + HashAlgorithm::SHA1 => { + let o = oid!(1,2,840,10045,4,1); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA224 => { + let o = oid!(1,2,840,10045,4,3,1); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA256 => { + let o = oid!(1,2,840,10045,4,3,2); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA384 => { + let o = oid!(1,2,840,10045,4,3,3); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + HashAlgorithm::SHA512 => { + let o = oid!(1,2,840,10045,4,3,4); + let obj = ASN1Block::ObjectIdentifier(c, 0, o); + Ok(ASN1Block::Sequence(c, 0, vec![obj])) + } + } + } + } +} + +#[cfg(test)] +mod test { + use quickcheck::{Arbitrary,Gen}; + use super::*; + + fn rsa1(g: &mut G) -> AlgorithmIdentifier { + AlgorithmIdentifier{ + hash: HashAlgorithm::SHA1, + algo: PublicKeyInfo::RSA + } + } + + fn rsa224(g: &mut G) -> AlgorithmIdentifier { + AlgorithmIdentifier{ + hash: HashAlgorithm::SHA224, + algo: PublicKeyInfo::RSA + } + } + + fn rsa256(g: &mut G) -> AlgorithmIdentifier { + AlgorithmIdentifier{ + hash: HashAlgorithm::SHA256, + algo: PublicKeyInfo::RSA + } + } + + fn rsa384(g: &mut G) -> AlgorithmIdentifier { + AlgorithmIdentifier{ + hash: HashAlgorithm::SHA384, + algo: PublicKeyInfo::RSA + } + } + + fn rsa512(g: &mut G) -> AlgorithmIdentifier { + AlgorithmIdentifier{ + hash: HashAlgorithm::SHA512, + algo: PublicKeyInfo::RSA + } + } + + fn dsa1(g: &mut G) -> AlgorithmIdentifier { + AlgorithmIdentifier{ + hash: HashAlgorithm::SHA1, + algo: PublicKeyInfo::DSA + } + } + + fn dsa224(g: &mut G) -> AlgorithmIdentifier { + AlgorithmIdentifier{ + hash: HashAlgorithm::SHA224, + algo: PublicKeyInfo::DSA + } + } + + fn dsa256(g: &mut G) -> AlgorithmIdentifier { + AlgorithmIdentifier{ + hash: HashAlgorithm::SHA256, + algo: PublicKeyInfo::DSA + } + } + + impl Arbitrary for AlgorithmIdentifier { + fn arbitrary(g: &mut G) -> AlgorithmIdentifier { + let opts = [rsa1,rsa224,rsa256,rsa384,rsa512,dsa1,dsa224,dsa256]; + let f = g.choose(&opts).unwrap(); + f(g) + } + } + + quickcheck!{ + fn algident_roundtrips(v: AlgorithmIdentifier) -> bool { + match encode_algorithm_ident(ASN1Class::Universal, &v) { + Err(_) => + false, + Ok(block) => { + match decode_algorithm_ident(&block) { + Err(_) => + false, + Ok(v2) => + v == v2 + } + } + } + } + } +} + diff --git a/src/atv.rs b/src/atv.rs new file mode 100644 index 0000000..ee42351 --- /dev/null +++ b/src/atv.rs @@ -0,0 +1,366 @@ +use error::X509ParseError; +use name::X520Name; +use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1}; +use std::ops::Index; + +#[derive(Clone,Debug)] +pub struct InfoBlock { + fields: Vec +} + +const EMPTY_STRING: &'static str = ""; + +impl Index for InfoBlock { + type Output = str; + + fn index(&self, name: X520Name) -> &str { + for atv in self.fields.iter() { + if name == atv.attrtype { + return &atv.value; + } + } + &EMPTY_STRING + } +} + +impl PartialEq for InfoBlock { + fn eq(&self, other: &InfoBlock) -> bool { + for x in self.fields.iter() { + if !other.fields.contains(x) { + return false; + } + } + for x in other.fields.iter() { + if !self.fields.contains(x) { + return false; + } + } + true + } +} + +fn decode_info_block(x: &ASN1Block) + -> Result +{ + // Name ::= CHOICE { -- only one possibility for now -- + // rdnSequence RDNSequence } + // + // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + // + // RelativeDistinguishedName ::= + // SET SIZE (1..MAX) OF AttributeTypeAndValue + match x { + &ASN1Block::Sequence(_, _, ref items) => { + let mut atvs = Vec::new(); + + for set in items.iter() { + match set { + &ASN1Block::Set(_, _, ref setitems) => { + for atv in setitems.iter() { + let v = decode_attribute_type_value(atv)?; + atvs.push(v); + } + } + _ => + return Err(X509ParseError::IllFormedInfoBlock) + } + } + + Ok(InfoBlock{ fields: atvs }) + } + _ => + Err(X509ParseError::IllFormedInfoBlock) + } +} + +impl FromASN1 for InfoBlock { + type Error = X509ParseError; + + fn from_asn1(v: &[ASN1Block]) + -> Result<(InfoBlock,&[ASN1Block]),X509ParseError> + { + match v.split_first() { + None => + Err(X509ParseError::NotEnoughData), + Some((x, rest)) => { + let v = decode_info_block(&x)?; + Ok((v, rest)) + } + } + } +} + +fn encode_info_block(c: ASN1Class, b: &InfoBlock) + -> Result +{ + let mut encoded_fields = Vec::with_capacity(b.fields.len()); + + for fld in b.fields.iter() { + let val = encode_attribute_type_value(c, fld)?; + encoded_fields.push(val); + } + + let set = ASN1Block::Set(c, 0, encoded_fields); + + Ok(ASN1Block::Sequence(c, 0, vec![set])) +} + +impl ToASN1 for InfoBlock { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,ASN1EncodeErr> + { + let block = encode_info_block(c, self)?; + Ok(vec![block]) + } +} + + +#[derive(Clone,Debug,PartialEq)] +struct AttributeTypeValue { + attrtype: X520Name, + value: String +} + +fn decode_attribute_type_value(x: &ASN1Block) + -> Result +{ + // AttributeTypeAndValue ::= SEQUENCE { + // type AttributeType, + // value AttributeValue } + match x { + &ASN1Block::Sequence(_, _, ref xs) => { + let (name, rest) = X520Name::from_asn1(xs)?; + match rest.first() { + None => Err(X509ParseError::NotEnoughData), + Some(ref x) => { + let atvstr = get_atv_string(name, x)?; + Ok(AttributeTypeValue{ + attrtype: name, + value: atvstr + }) + } + } + } + _ => + Err(X509ParseError::IllFormedAttrTypeValue) + } +} + +impl FromASN1 for AttributeTypeValue { + type Error = X509ParseError; + + fn from_asn1(v: &[ASN1Block]) + -> Result<(AttributeTypeValue,&[ASN1Block]),X509ParseError> + { + match v.split_first() { + None => + Err(X509ParseError::NotEnoughData), + Some((x, rest)) => { + let v = decode_attribute_type_value(&x)?; + Ok((v, rest)) + } + } + } +} + +fn encode_attribute_type_value(c: ASN1Class, x: &AttributeTypeValue) + -> Result +{ + let mut resvec = x.attrtype.to_asn1_class(c)?; + let value = match x.attrtype { + X520Name::CountryName => + ASN1Block::PrintableString(c,0,x.value.clone()), + X520Name::SerialNumber => + ASN1Block::PrintableString(c,0,x.value.clone()), + X520Name::DomainComponent => + ASN1Block::IA5String(c,0,x.value.clone()), + X520Name::EmailAddress => + ASN1Block::IA5String(c,0,x.value.clone()), + _ => + ASN1Block::UTF8String(c,0,x.value.clone()) + }; + resvec.push(value); + Ok(ASN1Block::Sequence(c, 0, resvec)) +} + +impl ToASN1 for AttributeTypeValue { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,ASN1EncodeErr> + { + let block = encode_attribute_type_value(c, self)?; + Ok(vec![block]) + } +} + +fn get_atv_string(n: X520Name, x: &ASN1Block) + -> Result +{ + match n { + X520Name::CountryName => { + let res = get_printable_val(x)?; + if res.len() != 2 { + return Err(X509ParseError::IllegalStringValue); + } + Ok(res) + } + X520Name::SerialNumber => get_printable_val(x), + X520Name::DomainComponent => get_ia5_val(x), + X520Name::EmailAddress => get_ia5_val(x), + _ => get_string_val(x), + } +} + +fn get_string_val(a: &ASN1Block) -> Result +{ + match a { + &ASN1Block::TeletexString(_,_,ref v) => Ok(v.clone()), + &ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()), + &ASN1Block::UniversalString(_,_,ref v) => Ok(v.clone()), + &ASN1Block::UTF8String(_,_,ref v) => Ok(v.clone()), + &ASN1Block::BMPString(_,_,ref v) => Ok(v.clone()), + _ => + Err(X509ParseError::IllegalStringValue) + } +} + +fn get_printable_val(a: &ASN1Block) -> Result +{ + match a { + &ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()), + _ => + Err(X509ParseError::IllegalStringValue) + } +} + +fn get_ia5_val(a: &ASN1Block) -> Result +{ + match a { + &ASN1Block::IA5String(_,_,ref v) => Ok(v.clone()), + _ => + Err(X509ParseError::IllegalStringValue) + } +} + +#[cfg(test)] +mod test { + use quickcheck::{Arbitrary,Gen}; + use std::iter::FromIterator; + use super::*; + + impl Arbitrary for X520Name { + fn arbitrary(g: &mut G) -> X520Name { + let names = vec![X520Name::Name, + X520Name::Surname, + X520Name::GivenName, + X520Name::Initials, + X520Name::GenerationQualifier, + X520Name::CommonName, + X520Name::LocalityName, + X520Name::StateOrProvinceName, + X520Name::OrganizationName, + X520Name::OrganizationalUnit, + X520Name::Title, + X520Name::DNQualifier, + X520Name::CountryName, + X520Name::SerialNumber, + X520Name::Pseudonym, + X520Name::DomainComponent, + X520Name::EmailAddress]; + g.choose(&names).unwrap().clone() + } + } + + impl Arbitrary for AttributeTypeValue { + fn arbitrary(g: &mut G) -> AttributeTypeValue { + let name = X520Name::arbitrary(g); + let val = match name { + X520Name::CountryName => { + let mut base = gen_printable(g); + base.push('U'); + base.push('S'); + base.truncate(2); + base + } + X520Name::SerialNumber => gen_printable(g), + X520Name::DomainComponent => gen_ia5(g), + X520Name::EmailAddress => gen_ia5(g), + _ => gen_utf8(g) + }; + AttributeTypeValue{ attrtype: name, value: val } + } + } + + const PRINTABLE_CHARS: &'static str = + "ABCDEFGHIJKLMOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+,-./:=? "; + + fn gen_printable(g: &mut G) -> String { + let count = g.gen_range::(0, 384); + let mut items = Vec::with_capacity(count); + + for _ in 0..count { + let v = g.choose(PRINTABLE_CHARS.as_bytes()).unwrap(); + items.push(*v as char); + } + String::from_iter(items.iter()) + } + + fn gen_ia5(g: &mut G) -> String { + let count = g.gen_range::(0, 384); + let mut items = Vec::with_capacity(count); + + for _ in 0..count { + items.push(g.gen::() as char); + } + String::from_iter(items.iter()) + } + + fn gen_utf8(g: &mut G) -> String { + String::arbitrary(g) + } + + impl Arbitrary for InfoBlock { + fn arbitrary(g: &mut G) -> InfoBlock { + let count = g.gen_range::(0,12); + let mut items = Vec::with_capacity(count); + let mut names = Vec::with_capacity(count); + + while items.len() < count { + let atv = AttributeTypeValue::arbitrary(g); + if !names.contains(&atv.attrtype) { + names.push(atv.attrtype); + items.push(atv); + } + } + + InfoBlock{ fields: items } + } + } + + quickcheck! { + fn attrtypeval_roundtrips(v: AttributeTypeValue) -> bool { + match encode_attribute_type_value(ASN1Class::Universal, &v) { + Err(_) => false, + Ok(bstr) => + match decode_attribute_type_value(&bstr) { + Err(_) => false, + Ok(v2) => v == v2 + } + } + } + + fn infoblock_roundtrips(v: InfoBlock) -> bool { + match encode_info_block(ASN1Class::Universal, &v) { + Err(_) => false, + Ok(bstr) => + match decode_info_block(&bstr) { + Err(_) => false, + Ok(v2) => v == v2 + } + } + } + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..00e8e08 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,36 @@ +use simple_asn1::ASN1DecodeErr; +use simple_dsa::DSAError; +use simple_rsa::RSAError; + +/// The error type for parsing and validating an X.509 certificate. +#[derive(Debug)] +pub enum X509ParseError { + ASN1DecodeError(ASN1DecodeErr), + RSAError(RSAError), DSAError(DSAError), + NotEnoughData, + IllFormedName, IllFormedAttrTypeValue, IllFormedInfoBlock, + IllFormedValidity, IllFormedCertificateInfo, IllFormedSerialNumber, + IllFormedAlgoInfo, IllFormedKey, IllFormedEverything, + IllegalStringValue, NoSerialNumber, InvalidDSAInfo, ItemNotFound, + UnknownAlgorithm, InvalidRSAKey, InvalidDSAKey, KeyNotFound +} + +impl From for X509ParseError { + fn from(e: ASN1DecodeErr) -> X509ParseError { + X509ParseError::ASN1DecodeError(e) + } +} + +impl From for X509ParseError { + fn from(e: RSAError) -> X509ParseError { + X509ParseError::RSAError(e) + } +} + +impl From for X509ParseError { + fn from(e: DSAError) -> X509ParseError { + X509ParseError::DSAError(e) + } +} + + diff --git a/src/lib.rs b/src/lib.rs index 09f778c..c4ee6b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,12 +5,173 @@ extern crate num; extern crate quickcheck; #[macro_use] extern crate simple_asn1; +extern crate simple_dsa; extern crate simple_rsa; +mod algident; +mod atv; +mod error; +mod misc; +mod name; +mod publickey; +mod validity; + +use algident::AlgorithmIdentifier; +use atv::InfoBlock; +use error::X509ParseError; +use misc::{X509Serial,X509Version}; +use publickey::X509PublicKey; +use simple_asn1::{ASN1Block,FromASN1}; +use validity::Validity; + +/******************************************************************************* + * + * The actual certificate data type and methods + * + ******************************************************************************/ + +/// The type of an X.509 certificate. +#[derive(Debug)] +pub struct Certificate { + pub version: X509Version, + pub serial: X509Serial, + pub signature_alg: AlgorithmIdentifier, + pub issuer: InfoBlock, + pub subject: InfoBlock, + pub validity: Validity, + pub subject_key: X509PublicKey, + pub extensions: Vec<()> +} + +fn decode_certificate(x: &ASN1Block) + -> Result +{ + // + // TBSCertificate ::= SEQUENCE { + // version [0] Version DEFAULT v1, + // serialNumber CertificateSerialNumber, + // signature AlgorithmIdentifier, + // issuer Name, + // validity Validity, + // subject Name, + // subjectPublicKeyInfo SubjectPublicKeyInfo, + // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + // -- If present, version MUST be v2 or v3 + // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + // -- If present, version MUST be v2 or v3 + // extensions [3] Extensions OPTIONAL + // -- If present, version MUST be v3 -- } + // + println!("x: {:?}", x); + match x { + &ASN1Block::Sequence(_, _, ref b0) => { + let (version, b1) = X509Version::from_asn1(b0)?; + println!("version: {:?}", version); + let (serial, b2) = X509Serial::from_asn1(b1)?; + println!("serial: {:?}", serial); + let (ident, b3) = AlgorithmIdentifier::from_asn1(b2)?; + println!("ident: {:?}", ident); + let (issuer, b4) = InfoBlock::from_asn1(b3)?; + println!("issuer: {:?}", issuer); + let (validity, b5) = Validity::from_asn1(b4)?; + println!("validity: {:?}", validity); + let (subject, b6) = InfoBlock::from_asn1(b5)?; + println!("subject: {:?}", subject); + let (subkey, b7) = X509PublicKey::from_asn1(b6)?; + println!("subkey: {:?}", subkey); + println!("REMAINDER: {:?}", b7); + Ok(Certificate { + version: version, + serial: serial, + signature_alg: ident, + issuer: issuer, + subject: subject, + validity: validity, + subject_key: subkey, + extensions: vec![] + }) + } + _ => + Err(X509ParseError::IllFormedCertificateInfo) + } +} + +/******************************************************************************* + * + * X.509 parsing routines + * + ******************************************************************************/ + +fn parse_x509(blocks: &[ASN1Block], buffer: &[u8]) + -> Result +{ + match blocks.first() { + None => + Err(X509ParseError::NotEnoughData), + Some(&ASN1Block::Sequence(_, _, ref x)) => { + let cert = decode_certificate(&x[0])?; + println!("cert: {:?}", cert); + Ok(cert) + } + Some(_) => + Err(X509ParseError::IllFormedEverything) + } +} + + +/******************************************************************************* + * + * Testing is for winners! + * + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use simple_asn1::from_der; + use std::fs::File; + use std::io::Read; + use super::*; + + fn can_parse(f: &str) -> Result { + let mut fd = File::open(f).unwrap(); + let mut buffer = Vec::new(); + let _amt = fd.read_to_end(&mut buffer); + println!("_amt: {:?}", _amt); + let asn1: Vec = from_der(&buffer[..])?; + parse_x509(&asn1, &buffer) + } + + #[test] + fn rsa_tests() { + assert!(can_parse("test/rsa2048-1.der").is_ok()); + assert!(can_parse("test/rsa2048-2.der").is_ok()); + assert!(can_parse("test/rsa4096-1.der").is_ok()); + assert!(can_parse("test/rsa4096-2.der").is_ok()); + assert!(can_parse("test/rsa4096-3.der").is_ok()); + } + + #[test] + fn dsa_tests() { + assert!(can_parse("test/dsa2048-1.der").is_ok()); + assert!(can_parse("test/dsa2048-2.der").is_ok()); + assert!(can_parse("test/dsa3072-1.der").is_ok()); + assert!(can_parse("test/dsa3072-2.der").is_ok()); + } + +// #[test] + fn ecc_tests() { + assert!(can_parse("test/ec384-1.der").is_ok()); + assert!(can_parse("test/ec384-2.der").is_ok()); + assert!(can_parse("test/ec384-3.der").is_ok()); + } +} + +/* use chrono::{DateTime,Utc}; use num::{BigUint,ToPrimitive}; use simple_asn1::{ASN1Block,ASN1Class,FromASN1,FromASN1WithBody,OID,ToASN1}; use simple_asn1::{ASN1DecodeErr,ASN1EncodeErr,der_decode}; +use simple_dsa::{DSAPublicKey}; use simple_rsa::{RSAPublicKey,RSAError,SigningHash, SIGNING_HASH_SHA1, SIGNING_HASH_SHA224, SIGNING_HASH_SHA256, SIGNING_HASH_SHA384, SIGNING_HASH_SHA512}; @@ -19,7 +180,14 @@ use simple_rsa::{RSAPublicKey,RSAError,SigningHash, enum HashAlgorithm { None, MD2, MD5, SHA1, SHA224, SHA256, SHA384, SHA512 } #[derive(Clone,Debug,PartialEq)] -enum PubKeyAlgorithm { RSA, RSAPSS, DSA, EC, DH, Unknown(OID) } +enum PubKeyAlgorithm { + RSA, + RSAPSS, + DSA(BigUint,BigUint,BigUint), + EC, + DH, + Unknown(OID) +} #[derive(Debug)] enum X509ParseError { @@ -28,14 +196,15 @@ enum X509ParseError { NoSignatureAlgorithm, NoNameInformation, IllFormedNameInformation, NoValueForName, UnknownAttrTypeValue, IllegalStringValue, NoValidityInfo, ImproperValidityInfo, NoSubjectPublicKeyInfo, ImproperSubjectPublicKeyInfo, - BadPublicKeyAlgorithm, UnsupportedPublicKey, InvalidRSAKey, + BadPublicKeyAlgorithm, UnsupportedPublicKey, InvalidRSAKey, InvalidDSAInfo, UnsupportedExtension, UnexpectedNegativeNumber, MissingNumber, NoSignatureFound, UnsupportedSignature, SignatureFailed } #[derive(Clone,Debug,PartialEq)] enum X509PublicKey { - RSA(RSAPublicKey) + DSA(DSAPublicKey), + RSA(RSAPublicKey), } impl From for X509ParseError { @@ -126,152 +295,7 @@ impl FromASN1 for SignatureAlgorithm { Some((x, rest)) => { match x { &ASN1Block::ObjectIdentifier(_, _, ref oid) => { - if oid == oid!(1,2,840,113549,1,1,1) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::None, - key_alg: PubKeyAlgorithm::RSA - }, rest)); - } - if oid == oid!(1,2,840,113549,1,1,5) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA1, - key_alg: PubKeyAlgorithm::RSA - }, rest)); - } - if oid == oid!(1,2,840,113549,1,1,4) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::MD5, - key_alg: PubKeyAlgorithm::RSA - }, rest)); - } - if oid == oid!(1,2,840,113549,1,1,2) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::MD2, - key_alg: PubKeyAlgorithm::RSA - }, rest)); - } - if oid == oid!(1,2,840,113549,1,1,11) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA256, - key_alg: PubKeyAlgorithm::RSA - }, rest)); - } - if oid == oid!(1,2,840,113549,1,1,12) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA384, - key_alg: PubKeyAlgorithm::RSA - }, rest)); - } - if oid == oid!(1,2,840,113549,1,1,13) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA512, - key_alg: PubKeyAlgorithm::RSA - }, rest)); - } - if oid == oid!(1,2,840,113549,1,1,14) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA224, - key_alg: PubKeyAlgorithm::RSA - }, rest)); - } - if oid == oid!(1,2,840,10040,4,1) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::None, - key_alg: PubKeyAlgorithm::DSA - }, rest)); - } - if oid == oid!(1,2,840,10040,4,3) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA1, - key_alg: PubKeyAlgorithm::DSA - }, rest)); - } - if oid == oid!(1,2,840,10045,2,1) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::None, - key_alg: PubKeyAlgorithm::EC - }, rest)); - } - if oid == oid!(1,2,840,10045,4,1) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA1, - key_alg: PubKeyAlgorithm::EC - }, rest)); - } - if oid == oid!(1,2,840,10045,4,3,1) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA224, - key_alg: PubKeyAlgorithm::EC - }, rest)); - } - if oid == oid!(1,2,840,10045,4,3,2) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA256, - key_alg: PubKeyAlgorithm::EC - }, rest)); - } - if oid == oid!(1,2,840,10045,4,3,3) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA384, - key_alg: PubKeyAlgorithm::EC - }, rest)); - } - if oid == oid!(1,2,840,10045,4,3,4) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA512, - key_alg: PubKeyAlgorithm::EC - }, rest)); - } - if oid == oid!(1,2,840,113549,1,1,10) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::None, - key_alg: PubKeyAlgorithm::RSAPSS - }, rest)); - } - if oid == oid!(2,16,840,1,101,3,4,2,1) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA256, - key_alg: PubKeyAlgorithm::RSAPSS - }, rest)); - } - if oid == oid!(2,16,840,1,101,3,4,2,2) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA384, - key_alg: PubKeyAlgorithm::RSAPSS - }, rest)); - } - if oid == oid!(2,16,840,1,101,3,4,2,3) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA512, - key_alg: PubKeyAlgorithm::RSAPSS - }, rest)); - } - if oid == oid!(2,16,840,1,101,3,4,2,4) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA224, - key_alg: PubKeyAlgorithm::RSAPSS - }, rest)); - } - if oid == oid!(2,16,840,1,101,3,4,3,1) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA224, - key_alg: PubKeyAlgorithm::DSA - }, rest)); - } - if oid == oid!(2,16,840,1,101,3,4,3,2) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::SHA256, - key_alg: PubKeyAlgorithm::DSA - }, rest)); - } - if oid == oid!(1,2,840,10046,2,1) { - return Ok((SignatureAlgorithm { - hash_alg: HashAlgorithm::None, - key_alg: PubKeyAlgorithm::DH - }, rest)); - } - Err(X509ParseError::ItemNotFound) - } + } _ => Err(X509ParseError::ItemNotFound) } @@ -280,6 +304,21 @@ impl FromASN1 for SignatureAlgorithm { } } +fn decode_dsa_info(vs: &[ASN1Block]) + -> Result<(BigUint, BigUint, BigUint), X509ParseError> +{ + match vs.split_first() { + Some((&ASN1Block::Sequence(_, _, ref info), rest)) => { + let p = decode_biguint(&info[0])?; + let q = decode_biguint(&info[1])?; + let g = decode_biguint(&info[2])?; + Ok((p, q, g)) + } + _ => + Err(X509ParseError::InvalidDSAInfo) + } +} + #[derive(Clone,Debug,PartialEq)] pub enum SigAlgEncodeErr { ASN1Problem(ASN1EncodeErr), @@ -441,72 +480,25 @@ fn signing_hash(a: HashAlgorithm) } } -fn get_tbs_certificate(x: &ASN1Block) - -> Result -{ - match x { - &ASN1Block::Sequence(_, _, ref v0) => { - // TBSCertificate ::= SEQUENCE { - // version [0] Version DEFAULT v1, - let (version, v1) = get_version(v0)?; - // serialNumber CertificateSerialNumber, - let (serial, v2) = get_serial(v1)?; - // signature AlgorithmIdentifier, - let (algo, v3) = get_signature_info(v2)?; - // issuer Name, - let (issuer, v4) = get_name_data(v3)?; - // validity Validity, - let (validity, v5) = get_validity_data(v4)?; - // subject Name, - let (subject, v6) = get_name_data(v5)?; - // subjectPublicKeyInfo SubjectPublicKeyInfo, - let (subpki, v7) = get_subject_pki(v6)?; - - if (version < 3) && !v7.is_empty() { - return Err(X509ParseError::UnsupportedExtension) - } - - // FIXME: Support v3 extensions - if !v7.is_empty() { - return Err(X509ParseError::UnsupportedExtension) - // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, - // -- If present, version MUST be v2 or v3 - // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, - // -- If present, version MUST be v2 or v3 - // extensions [3] Extensions OPTIONAL - // -- If present, version MUST be v3 -- } - // - } - - Ok(Certificate{ - version: version, - serial: serial, - signature_alg: algo, - issuer: issuer, - subject: subject, - validity: validity, - subject_key: subpki, - extensions: vec![] - }) - } - _ => - Err(X509ParseError::IllegalFormat) - } -} - fn get_signature_alg(x: &ASN1Block) -> Result { // AlgorithmIdentifier ::= SEQUENCE { // algorithm OBJECT IDENTIFIER, // parameters ANY DEFINED BY algorithm OPTIONAL } + println!("get_signature_alg {:?}", x); match x { - &ASN1Block::Sequence(_, _, ref v) if v.len() == 2 => { + &ASN1Block::Sequence(_, _, ref v) => { + // initially there was a length check on v as a side condition + // for this case, but it caused unexpected problems and I took + // it out. let (alg, _) = SignatureAlgorithm::from_asn1(v)?; Ok(alg) } - _ => + _ => { + println!("Pattern match failed?!"); Err(X509ParseError::IllegalFormat) + } } } @@ -558,6 +550,7 @@ fn get_signature_info(bs: &[ASN1Block]) { match bs.split_first() { Some((x, rest)) => { + println!("x: {:?}", x); let alg = get_signature_alg(&x)?; Ok((alg, rest)) } @@ -566,322 +559,6 @@ fn get_signature_info(bs: &[ASN1Block]) } } -#[derive(Clone,Debug,PartialEq)] -struct InfoBlock { - name: String, - surname: String, - given_name: String, - initials: String, - generation_qualifier: String, - common_name: String, - locality: String, - state_province: String, - organization: String, - unit: String, - title: String, - dn_qualifier: String, - country: String, - serial_number: String, - pseudonym: String, - domain_component: String, - email: String -} - -fn empty_block() -> InfoBlock { - InfoBlock { - name: "".to_string(), - surname: "".to_string(), - given_name: "".to_string(), - initials: "".to_string(), - generation_qualifier: "".to_string(), - common_name: "".to_string(), - locality: "".to_string(), - state_province: "".to_string(), - organization: "".to_string(), - unit: "".to_string(), - title: "".to_string(), - dn_qualifier: "".to_string(), - country: "".to_string(), - serial_number: "".to_string(), - pseudonym: "".to_string(), - domain_component: "".to_string(), - email: "".to_string() - } -} - -fn get_name_data(bs: &[ASN1Block]) - -> Result<(InfoBlock,&[ASN1Block]),X509ParseError> -{ - match bs.split_first() { - Some((x,rest)) => { - match x { - // Name ::= CHOICE { -- only one possibility for now -- - // rdnSequence RDNSequence } - // - // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName - &ASN1Block::Sequence(_, _, ref items) => { - // RelativeDistinguishedName ::= - // SET SIZE (1..MAX) OF AttributeTypeAndValue - let mut iblock = empty_block(); - - for item in items.iter() { - match item { - &ASN1Block::Set(_, _, ref info) => { - for atv in info.iter() { - parse_attr_type_val(&atv, &mut iblock)?; - } - } - _ => - return Err(X509ParseError::IllFormedNameInformation) - } - } - Ok((iblock, rest)) - } - _ => - Err(X509ParseError::NoNameInformation) - } - } - _ => - Err(X509ParseError::NoNameInformation) - } -} - -fn parse_attr_type_val(val: &ASN1Block, iblock: &mut InfoBlock) - -> Result<(),X509ParseError> -{ - match val { - // AttributeTypeAndValue ::= SEQUENCE { - // type AttributeType, - // value AttributeValue } - &ASN1Block::Sequence(_, _, ref oidval) => { - match oidval.split_first() { - // AttributeType ::= OBJECT IDENTIFIER - Some((&ASN1Block::ObjectIdentifier(_, _, ref oid), rest)) => { - match rest.first() { - // AttributeValue ::= ANY -- DEFINED BY AttributeType - Some(val) => - process_atv(oid, val, iblock), - None => - Err(X509ParseError::NoValueForName) - } - } - _ => - Err(X509ParseError::IllFormedNameInformation) - } - } - _ => - Err(X509ParseError::IllFormedNameInformation) - } -} - -fn process_atv(oid: &OID, val: &ASN1Block, iblock: &mut InfoBlock) - -> Result<(),X509ParseError> -{ - //-- Arc for standard naming attributes - // - //id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 } - // - //-- Naming attributes of type X520name - // - //id-at-name AttributeType ::= { id-at 41 } - if oid == oid!(2,5,4,41) { - iblock.name = get_string_value(val)?; - return Ok(()); - } - //id-at-surname AttributeType ::= { id-at 4 } - if oid == oid!(2,5,4,4) { - iblock.surname = get_string_value(val)?; - return Ok(()); - } - //id-at-givenName AttributeType ::= { id-at 42 } - if oid == oid!(2,5,4,42) { - iblock.given_name = get_string_value(val)?; - return Ok(()); - } - //id-at-initials AttributeType ::= { id-at 43 } - if oid == oid!(2,5,4,43) { - iblock.initials = get_string_value(val)?; - return Ok(()); - } - //id-at-generationQualifier AttributeType ::= { id-at 44 } - if oid == oid!(2,5,4,44) { - iblock.generation_qualifier = get_string_value(val)?; - return Ok(()); - } - // - //-- Naming attributes of type X520CommonName - // - //id-at-commonName AttributeType ::= { id-at 3 } - if oid == oid!(2,5,4,3) { - iblock.common_name = get_string_value(val)?; - return Ok(()); - } - //-- Naming attributes of type X520LocalityName - // - //id-at-localityName AttributeType ::= { id-at 7 } - if oid == oid!(2,5,4,7) { - iblock.locality = get_string_value(val)?; - return Ok(()); - } - //-- Naming attributes of type X520StateOrProvinceName - // - //id-at-stateOrProvinceName AttributeType ::= { id-at 8 } - if oid == oid!(2,5,4,8) { - iblock.state_province = get_string_value(val)?; - return Ok(()); - } - //-- Naming attributes of type X520OrganizationName - // - //id-at-organizationName AttributeType ::= { id-at 10 } - if oid == oid!(2,5,4,10) { - iblock.organization = get_string_value(val)?; - return Ok(()); - } - //-- Naming attributes of type X520OrganizationalUnitName - // - //id-at-organizationalUnitName AttributeType ::= { id-at 11 } - if oid == oid!(2,5,4,11) { - iblock.unit = get_string_value(val)?; - return Ok(()); - } - //-- Naming attributes of type X520Title - // - //id-at-title AttributeType ::= { id-at 12 } - if oid == oid!(2,5,4,12) { - iblock.title = get_string_value(val)?; - return Ok(()); - } - //-- Naming attributes of type X520dnQualifier - // - //id-at-dnQualifier AttributeType ::= { id-at 46 } - // - //X520dnQualifier ::= PrintableString - if oid == oid!(2,5,4,46) { - iblock.dn_qualifier = get_printable_string_value(val)?; - return Ok(()); - } - // - //-- Naming attributes of type X520countryName (digraph from IS 3166) - // - //id-at-countryName AttributeType ::= { id-at 6 } - // - //X520countryName ::= PrintableString (SIZE (2)) - if oid == oid!(2,5,4,6) { - iblock.country = get_printable_string_value(val)?; - if iblock.country.len() != 2 { - return Err(X509ParseError::IllegalStringValue); - } - return Ok(()); - } - // - //-- Naming attributes of type X520SerialNumber - // - //id-at-serialNumber AttributeType ::= { id-at 5 } - // - //X520SerialNumber ::= PrintableString (SIZE (1..ub-serial-number)) - if oid == oid!(2,5,4,5) { - iblock.serial_number = get_printable_string_value(val)?; - return Ok(()); - } - // - //-- Naming attributes of type X520Pseudonym - // - //id-at-pseudonym AttributeType ::= { id-at 65 } - if oid == oid!(2,5,4,65) { - iblock.pseudonym = get_string_value(val)?; - return Ok(()); - } - //-- Naming attributes of type DomainComponent (from RFC 4519) - // - //id-domainComponent AttributeType ::= { 0 9 2342 19200300 100 1 25 } - // - //DomainComponent ::= IA5String - if oid == oid!(0,9,2342,19200300,100,1,25) { - iblock.domain_component = get_ia5_string_value(val)?; - return Ok(()); - } - //-- Legacy attributes - // - //pkcs-9 OBJECT IDENTIFIER ::= - // { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } - // - //id-emailAddress AttributeType ::= { pkcs-9 1 } - // - //EmailAddress ::= IA5String (SIZE (1..ub-emailaddress-length)) - if oid == oid!(1,2,840,113549,1,9,1) { - iblock.email = get_ia5_string_value(val)?; - return Ok(()); - } - - Err(X509ParseError::UnknownAttrTypeValue) -} - -fn get_string_value(a: &ASN1Block) -> Result -{ - match a { - &ASN1Block::TeletexString(_,_,ref v) => Ok(v.clone()), - &ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()), - &ASN1Block::UniversalString(_,_,ref v) => Ok(v.clone()), - &ASN1Block::UTF8String(_,_,ref v) => Ok(v.clone()), - &ASN1Block::BMPString(_,_,ref v) => Ok(v.clone()), - _ => - Err(X509ParseError::IllegalStringValue) - } -} - -fn get_printable_string_value(a: &ASN1Block) -> Result -{ - match a { - &ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()), - _ => - Err(X509ParseError::IllegalStringValue) - } -} - -fn get_ia5_string_value(a: &ASN1Block) -> Result -{ - match a { - &ASN1Block::IA5String(_,_,ref v) => Ok(v.clone()), - _ => - Err(X509ParseError::IllegalStringValue) - } -} - -#[derive(Clone,Debug,PartialEq)] -struct Validity { - not_before: DateTime, - not_after: DateTime -} - -fn get_validity_data(bs: &[ASN1Block]) - -> Result<(Validity,&[ASN1Block]),X509ParseError> -{ - match bs.split_first() { - // Validity ::= SEQUENCE { - // notBefore Time, - // notAfter Time } - Some((&ASN1Block::Sequence(_, _, ref valxs), rest)) => { - if valxs.len() != 2 { - return Err(X509ParseError::ImproperValidityInfo); - } - let nb = get_time(&valxs[0])?; - let na = get_time(&valxs[1])?; - Ok((Validity{ not_before: nb, not_after: na }, rest)) - } - _ => - Err(X509ParseError::NoValidityInfo) - } -} - -fn get_time(b: &ASN1Block) -> Result, X509ParseError> { - match b { - &ASN1Block::UTCTime(_, _, v) => Ok(v.clone()), - &ASN1Block::GeneralizedTime(_, _, v) => Ok(v.clone()), - _ => - Err(X509ParseError::ImproperValidityInfo) - } -} - fn get_subject_pki(b: &[ASN1Block]) -> Result<(X509PublicKey, &[ASN1Block]), X509ParseError> { @@ -890,6 +567,7 @@ fn get_subject_pki(b: &[ASN1Block]) // algorithm AlgorithmIdentifier, // subjectPublicKey BIT STRING } Some((&ASN1Block::Sequence(_, _, ref info), rest)) => { + println!("get_subject_pki {:?}", info); if info.len() != 2 { return Err(X509ParseError::ImproperSubjectPublicKeyInfo) } @@ -907,6 +585,9 @@ fn get_subject_pki(b: &[ASN1Block]) Ok((X509PublicKey::RSA(key), rest)) } _ => { + let key = get_dsa_public_key(&info[1])?; + println!("key alg: {:?}", alginfo.key_alg); + println!("info: {:?}", info); Err(X509ParseError::UnsupportedPublicKey) } } @@ -928,9 +609,20 @@ fn get_rsa_public_key(b: &ASN1Block) } } +fn get_dsa_public_key(b: &ASN1Block) + -> Result +{ + match b { + &ASN1Block::BitString(_, _, size, ref vec) if size % 8 == 0 => { + unimplemented!(); + } + _ => + Err(X509ParseError::InvalidRSAKey) + } +} + #[cfg(test)] mod tests { - use quickcheck::{Arbitrary,Gen}; use simple_asn1::{der_decode,der_encode}; use std::fs::File; use std::io::Read; @@ -941,7 +633,9 @@ mod tests { match g.gen::() % 6 { 0 => PubKeyAlgorithm::RSA, 1 => PubKeyAlgorithm::RSAPSS, - 2 => PubKeyAlgorithm::DSA, + 2 => { + PubKeyAlgorithm::DSA, + } 3 => PubKeyAlgorithm::EC, 4 => PubKeyAlgorithm::DH, 5 => { @@ -1099,5 +793,13 @@ mod tests { assert!(can_parse("test/rsa4096-1.der").is_ok()); assert!(can_parse("test/rsa4096-2.der").is_ok()); assert!(can_parse("test/rsa4096-3.der").is_ok()); + assert!(can_parse("test/dsa2048-1.der").is_ok()); + assert!(can_parse("test/dsa2048-2.der").is_ok()); + assert!(can_parse("test/dsa3072-1.der").is_ok()); + assert!(can_parse("test/dsa3072-2.der").is_ok()); + assert!(can_parse("test/ec384-1.der").is_ok()); + assert!(can_parse("test/ec384-2.der").is_ok()); + assert!(can_parse("test/ec384-3.der").is_ok()); } } +*/ diff --git a/src/misc.rs b/src/misc.rs new file mode 100644 index 0000000..152f7d6 --- /dev/null +++ b/src/misc.rs @@ -0,0 +1,181 @@ +use error::X509ParseError; +use num::{BigInt,BigUint,One,ToPrimitive,Zero}; +use num::bigint::ToBigInt; +use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1}; + +#[derive(Clone,Copy,Debug,PartialEq)] +pub enum X509Version { V1, V2, V3 } + +fn decode_version(bs: &[ASN1Block]) + -> Result<(X509Version,&[ASN1Block]),X509ParseError> +{ + match bs.split_first() { + Some((&ASN1Block::Integer(_, _, ref v), rest)) => { + match v.to_u8() { + Some(0) => Ok((X509Version::V1, rest)), + Some(1) => Ok((X509Version::V2, rest)), + Some(2) => Ok((X509Version::V3, rest)), + _ => Ok((X509Version::V1, &bs)) + } + } + _ => + Err(X509ParseError::NotEnoughData) + } +} + +impl FromASN1 for X509Version { + type Error = X509ParseError; + + fn from_asn1(v: &[ASN1Block]) + -> Result<(X509Version,&[ASN1Block]),X509ParseError> + { + decode_version(v) + } +} + +fn encode_version(c: ASN1Class, v: X509Version) -> Vec { + match v { + X509Version::V1 => { + let zero: BigInt = Zero::zero(); + let block = ASN1Block::Integer(c, 0, zero); + vec![block] + } + X509Version::V2 => { + let one: BigInt = One::one(); + let block = ASN1Block::Integer(c, 0, one); + vec![block] + } + X509Version::V3 => { + let two: BigInt = BigInt::from(2 as u64); + let block = ASN1Block::Integer(c, 0, two); + vec![block] + } + } +} + +impl ToASN1 for X509Version { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,ASN1EncodeErr> + { + Ok(encode_version(c, *self)) + } +} + +/******************************************************************************/ + +#[derive(Clone,Debug,PartialEq)] +pub struct X509Serial { + num: BigUint +} + +fn decode_serial(x: &ASN1Block) + -> Result +{ + match x { + &ASN1Block::Integer(_, _, ref v) => { + match v.to_biguint() { + None => + Err(X509ParseError::IllFormedSerialNumber), + Some(n) => + Ok(X509Serial{ num: n }) + } + } + _ => + Err(X509ParseError::NoSerialNumber) + } +} + +impl FromASN1 for X509Serial { + type Error = X509ParseError; + + fn from_asn1(v: &[ASN1Block]) + -> Result<(X509Serial,&[ASN1Block]),X509ParseError> + { + match v.split_first() { + None => + Err(X509ParseError::NoSerialNumber), + Some((x, rest)) => { + let v = decode_serial(x)?; + Ok((v, rest)) + } + } + } +} + +pub enum SerialEncodeErr { ASN1Error(ASN1EncodeErr), InvalidSerialNumber } + +impl From for SerialEncodeErr { + fn from(e: ASN1EncodeErr) -> SerialEncodeErr { + SerialEncodeErr::ASN1Error(e) + } +} + +fn encode_serial(c: ASN1Class, serial: &X509Serial) + -> Result +{ + match serial.num.to_bigint() { + None => + Err(SerialEncodeErr::InvalidSerialNumber), + Some(n) => + Ok(ASN1Block::Integer(c, 0, n)) + } +} + +impl ToASN1 for X509Serial { + type Error = SerialEncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,SerialEncodeErr> + { + let v = encode_serial(c, self)?; + Ok(vec![v]) + } +} + + +#[cfg(test)] +mod test { + use quickcheck::{Arbitrary,Gen}; + use super::*; + + fn check_version_roundtrip(v: X509Version) { + let blocks = encode_version(ASN1Class::Universal, v); + match decode_version(&blocks) { + Err(_) => + assert!(false), + Ok((v2,_)) => + assert_eq!(v, v2) + } + } + + #[test] + fn versions_roundtrip() { + check_version_roundtrip(X509Version::V1); + check_version_roundtrip(X509Version::V2); + check_version_roundtrip(X509Version::V3); + } + + impl Arbitrary for X509Serial { + fn arbitrary(g: &mut G) -> X509Serial { + let count = g.gen_range::(0,16); + let bits = g.gen_iter::().take(count).collect(); + let val = BigUint::new(bits); + X509Serial{ num: val } + } + } + + quickcheck! { + fn serial_roundtrips(s: X509Serial) -> bool { + match encode_serial(ASN1Class::Universal, &s) { + Err(_) => false, + Ok(block) => + match decode_serial(&block) { + Err(_) => false, + Ok(s2) => s == s2 + } + } + } + } +} diff --git a/src/name.rs b/src/name.rs new file mode 100644 index 0000000..10aa2e8 --- /dev/null +++ b/src/name.rs @@ -0,0 +1,137 @@ +use num::BigUint; +use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1}; + +use error::X509ParseError; + +#[derive(Copy,Clone,Debug,Eq,Hash,PartialEq)] +pub enum X520Name { + Name, Surname, GivenName, Initials, GenerationQualifier, CommonName, + LocalityName, StateOrProvinceName, OrganizationName, OrganizationalUnit, + Title, DNQualifier, CountryName, SerialNumber, Pseudonym, DomainComponent, + EmailAddress +} + +impl FromASN1 for X520Name { + type Error = X509ParseError; + + fn from_asn1(v: &[ASN1Block]) + -> Result<(X520Name,&[ASN1Block]),X509ParseError> + { + match v.split_first() { + None => + Err(X509ParseError::NotEnoughData), + Some((x,rest)) => { + let name = decode_name(&x)?; + Ok((name,rest)) + } + } + } +} + +fn decode_name(val: &ASN1Block) + -> Result +{ + match val { + &ASN1Block::ObjectIdentifier(_, _, ref oid) => { + if oid == oid!(2,5,4,41) {return Ok(X520Name::Name) } + if oid == oid!(2,5,4,4) {return Ok(X520Name::Surname) } + if oid == oid!(2,5,4,42) {return Ok(X520Name::GivenName) } + if oid == oid!(2,5,4,43) {return Ok(X520Name::Initials) } + if oid == oid!(2,5,4,44) {return Ok(X520Name::GenerationQualifier)} + if oid == oid!(2,5,4,3) {return Ok(X520Name::CommonName) } + if oid == oid!(2,5,4,7) {return Ok(X520Name::LocalityName) } + if oid == oid!(2,5,4,8) {return Ok(X520Name::StateOrProvinceName)} + if oid == oid!(2,5,4,10) {return Ok(X520Name::OrganizationName) } + if oid == oid!(2,5,4,11) {return Ok(X520Name::OrganizationalUnit) } + if oid == oid!(2,5,4,12) {return Ok(X520Name::Title) } + if oid == oid!(2,5,4,46) {return Ok(X520Name::DNQualifier) } + if oid == oid!(2,5,4,6) {return Ok(X520Name::CountryName) } + if oid == oid!(2,5,4,5) {return Ok(X520Name::SerialNumber) } + if oid == oid!(2,5,4,65) {return Ok(X520Name::Pseudonym) } + if oid == oid!(0,9,2342,19200300,100,1,25) { + return Ok(X520Name::DomainComponent); + } + if oid == oid!(1,2,840,113549,1,9,1) { + return Ok(X520Name::EmailAddress); + } + Err(X509ParseError::IllFormedName) + } + _ => + Err(X509ParseError::IllFormedName) + } +} + +impl ToASN1 for X520Name { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,ASN1EncodeErr> + { + let block = encode_name(c, *self); + Ok(vec![block]) + } +} + +fn encode_name(class: ASN1Class, name: X520Name) + -> ASN1Block +{ + let oid = match name { + X520Name::Name => oid!(2,5,4,41), + X520Name::Surname => oid!(2,5,4,4), + X520Name::GivenName => oid!(2,5,4,42), + X520Name::Initials => oid!(2,5,4,43), + X520Name::GenerationQualifier => oid!(2,5,4,44), + X520Name::CommonName => oid!(2,5,4,3), + X520Name::LocalityName => oid!(2,5,4,7), + X520Name::StateOrProvinceName => oid!(2,5,4,8), + X520Name::OrganizationName => oid!(2,5,4,10), + X520Name::OrganizationalUnit => oid!(2,5,4,11), + X520Name::Title => oid!(2,5,4,12), + X520Name::DNQualifier => oid!(2,5,4,46), + X520Name::CountryName => oid!(2,5,4,6), + X520Name::SerialNumber => oid!(2,5,4,5), + X520Name::Pseudonym => oid!(2,5,4,65), + X520Name::DomainComponent => oid!(0,9,2342,19200300,100,1,25), + X520Name::EmailAddress => oid!(1,2,840,113549,1,9,1) + }; + + ASN1Block::ObjectIdentifier(class, 0, oid) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn encdec_test(n: X520Name) { + let block = encode_name(ASN1Class::Universal, n); + let vec = vec![block]; + match X520Name::from_asn1(&vec) { + Err(_) => + assert!(false), + Ok((m, _)) => + assert_eq!(n,m) + } + } + + #[test] + fn name_encoding_roundtrips() { + encdec_test(X520Name::Name); + encdec_test(X520Name::Surname); + encdec_test(X520Name::GivenName); + encdec_test(X520Name::Initials); + encdec_test(X520Name::GenerationQualifier); + encdec_test(X520Name::CommonName); + encdec_test(X520Name::LocalityName); + encdec_test(X520Name::StateOrProvinceName); + encdec_test(X520Name::OrganizationName); + encdec_test(X520Name::OrganizationalUnit); + encdec_test(X520Name::Title); + encdec_test(X520Name::DNQualifier); + encdec_test(X520Name::CountryName); + encdec_test(X520Name::SerialNumber); + encdec_test(X520Name::Pseudonym); + encdec_test(X520Name::DomainComponent); + encdec_test(X520Name::EmailAddress); + } +} + diff --git a/src/publickey.rs b/src/publickey.rs new file mode 100644 index 0000000..ba6075e --- /dev/null +++ b/src/publickey.rs @@ -0,0 +1,245 @@ +use algident::SigAlgEncodeError; +use error::X509ParseError; +use num::BigUint; +use num::bigint::ToBigInt; +use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1, + der_decode,der_encode,from_der,to_der}; +use simple_dsa::{DSAParameterSize,DSAParameters,DSAPublicKey}; +use simple_rsa::RSAPublicKey; + +#[derive(Clone,Debug,PartialEq)] +pub enum X509PublicKey { + DSA(DSAPublicKey), + RSA(RSAPublicKey), +} + +impl FromASN1 for X509PublicKey { + type Error = X509ParseError; + + fn from_asn1(bs: &[ASN1Block]) + -> Result<(X509PublicKey, &[ASN1Block]),X509ParseError> + { + match bs.split_first() { + None => + Err(X509ParseError::KeyNotFound), + Some((x, rest)) => { + println!("key info: {:?}", x); + let v = decode_public_key(&x)?; + Ok((v, rest)) + } + } + } +} + +impl ToASN1 for X509PublicKey { + type Error = ASN1EncodeErr; + + fn to_asn1_class(&self, c: ASN1Class) + -> Result,ASN1EncodeErr> + { + let block = encode_public_key(c, self)?; + Ok(vec![block]) + } +} + +fn decode_public_key(block: &ASN1Block) + -> Result +{ + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + match block { + &ASN1Block::Sequence(_, _, ref info) => { + let (id, alginfo) = strip_algident(&info[0])?; + + if id == oid!(1,2,840,113549,1,1,1) { + let key = decode_rsa_key(&info[1])?; + return Ok(X509PublicKey::RSA(key)); + } + if id == oid!(1,2,840,10040,4,1) { + let params = decode_dsa_info(&alginfo)?; + let key = decode_dsa_key(&info[1], ¶ms)?; + return Ok(X509PublicKey::DSA(key)); + } + Err(X509ParseError::IllFormedKey) + } + _ => + Err(X509ParseError::IllFormedKey) + } +} + +fn strip_algident(block: &ASN1Block) + -> Result<(OID,ASN1Block),X509ParseError> +{ + match block { + &ASN1Block::Sequence(_, _, ref v) if v.len() == 2 => { + match v[0] { + ASN1Block::ObjectIdentifier(_, _, ref oid) => { + Ok((oid.clone(), v[1].clone())) + } + _ => Err(X509ParseError::IllFormedAlgoInfo) + } + } + _ => + Err(X509ParseError::IllFormedAlgoInfo) + } +} + +fn encode_public_key(c: ASN1Class, key: &X509PublicKey) + -> Result +{ + match key { + &X509PublicKey::RSA(ref rsa) => encode_rsa_pubkey(c, rsa), + &X509PublicKey::DSA(ref dsa) => encode_dsa_pubkey(c, dsa) + } +} + +fn encode_rsa_pubkey(c: ASN1Class, key: &RSAPublicKey) + -> Result +{ + let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,113549,1,1,1)); + let objkey = encode_rsa_key(c, key)?; + Ok(ASN1Block::Sequence(c, 0, vec![objoid, objkey])) +} + +fn encode_dsa_pubkey(c: ASN1Class, key: &DSAPublicKey) + -> Result +{ + let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10040,4,1)); + let objkey = encode_dsa_key(c, key)?; + Ok(ASN1Block::Sequence(c, 0, vec![objoid, objkey])) +} + +fn encode_rsa_key(c: ASN1Class, k: &RSAPublicKey) + -> Result +{ + let bstr = der_encode(k)?; + Ok(ASN1Block::BitString(c, 0, bstr.len() * 8, bstr)) +} + +fn decode_rsa_key(b: &ASN1Block) -> Result { + match b { + &ASN1Block::BitString(_, _, size, ref vec) if size % 8 == 0 => { + der_decode(vec).map_err(|x| X509ParseError::from(x)) + } + _ => + Err(X509ParseError::InvalidRSAKey) + } +} + +fn encode_dsa_key(c: ASN1Class, k: &DSAPublicKey) + -> Result +{ + unimplemented!() +} + +fn decode_dsa_key(b: &ASN1Block, params: &DSAParameters) + -> Result +{ + match b { + &ASN1Block::BitString(_, _, size, ref vec) if size % 8 == 0 => { + let vals = from_der(&vec)?; + match vals.first() { + Some(&ASN1Block::Integer(_, _, ref val)) => { + match val.to_biguint() { + Some(y) => { + Ok(DSAPublicKey::new(params, y)) + } + None => + Err(X509ParseError::InvalidDSAKey) + } + } + _ => + Err(X509ParseError::InvalidDSAKey) + } + } + _ => + Err(X509ParseError::InvalidRSAKey) + } +} + +fn decode_dsa_info(v: &ASN1Block) + -> Result +{ + match v { + &ASN1Block::Sequence(_, _, ref info) => { + let p = decode_biguint(&info[0])?; + let q = decode_biguint(&info[1])?; + let g = decode_biguint(&info[2])?; + DSAParameters::new(p, g, q).map_err(|x| X509ParseError::from(x)) + } + _ => + Err(X509ParseError::InvalidDSAInfo) + } +} + +fn encode_dsa_info(c: ASN1Class, params: &DSAParameters) + -> Result +{ + match (params.p.to_bigint(), params.q.to_bigint(), params.g.to_bigint()) { + (Some(pbs), Some(qbs), Some(gbs)) => { + let pb = ASN1Block::Integer(c, 0, pbs); + let qb = ASN1Block::Integer(c, 0, qbs); + let gb = ASN1Block::Integer(c, 0, gbs); + let vs = vec![pb, qb, gb]; + Ok(ASN1Block::Sequence(c, 0, vs)) + } + _ => + Err(SigAlgEncodeError::InvalidDSAValue) + } +} + +fn decode_biguint(b: &ASN1Block) -> Result { + match b { + &ASN1Block::Integer(_, _, ref v) => { + match v.to_biguint() { + Some(sn) => Ok(sn), + _ => Err(X509ParseError::InvalidDSAInfo) + } + } + _ => + Err(X509ParseError::InvalidDSAInfo) + } +} + + + +#[cfg(test)] +mod test { + use simple_dsa::DSAKeyPair; + use simple_rsa::RSAKeyPair; + use super::*; + + const NUM_TESTS: usize = 1; + + #[test] + fn rsa_public_key_tests() { + for _ in 0..NUM_TESTS { + let pair = RSAKeyPair::generate(2048).unwrap(); + let public = pair.public; + let block = encode_rsa_key(ASN1Class::Universal, &public).unwrap(); + let public2 = decode_rsa_key(&block).unwrap(); + assert_eq!(public, public2); + let x509public = X509PublicKey::RSA(public); + let block2 = encode_public_key(ASN1Class::Universal, &x509public).unwrap(); + let x509public2 = decode_public_key(&block2).unwrap(); + assert_eq!(x509public, x509public2); + } + } + + #[test] + fn dsa_public_key_tests() { + for _ in 0..NUM_TESTS { + let params = DSAParameters::generate(DSAParameterSize::L1024N160).unwrap(); + let pair = DSAKeyPair::generate_w_params(¶ms).unwrap(); + let public = pair.public; + let block = encode_dsa_key(ASN1Class::Universal, &public).unwrap(); + let public2 = decode_dsa_key(&block, ¶ms).unwrap(); + assert_eq!(public, public2); + let x509public = X509PublicKey::DSA(public); + let block2 = encode_public_key(ASN1Class::Universal, &x509public).unwrap(); + let x509public2 = decode_public_key(&block2).unwrap(); + assert_eq!(x509public, x509public2); + } + } +} diff --git a/src/validity.rs b/src/validity.rs new file mode 100644 index 0000000..ca4d8b2 --- /dev/null +++ b/src/validity.rs @@ -0,0 +1,117 @@ +use chrono::{DateTime,Utc}; +use error::X509ParseError; +use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1}; + +#[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 super::*; + + fn arbitrary_date(g: &mut G) -> DateTime { + loop { + let y = g.gen_range::(1900,3000); + let m = g.gen_range::(0,12); + let d = g.gen_range::(0,31); + let h = g.gen_range::(0,24); + let m = g.gen_range::(0,60); + let s = g.gen_range::(0,60); + match Utc.ymd_opt(y,m,d).and_hms_opt(h,m,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 + } + } + } +} diff --git a/test/dsa2048-1.der b/test/dsa2048-1.der new file mode 100644 index 0000000000000000000000000000000000000000..ac8ae314033b5c582019eb773775af599ce364a2 GIT binary patch literal 1228 zcmXqLVmV>Z#FEFv$?&<~(fr|!zZC}DY@7*g9*n8XEX+&>C5GGvoNUaYENsF|p}{Z? z2M?2fkfERfKS+k1ha(`rs3a#bFU3&QKo}&%#lsU}YGA40lbKYMSX61qYrqW>=H_7u zNi8lhlsAxNb41ysV4hZnXOx8@JFdnzc=R9%$EG# zX*H!yE-$-hd=qkYTFDu3Al7Yp$nrBz{IOfw_9x?@@QlZ!~sAQ&OCXo4?wc>j%&E11HXx-Da@v+G4pPp=#l*m6Nu7Y+ZF_(6fw;13XWTO19j* v^#0!r>80IF3hH+^wk|pn4PGVk)p{Ri{NQjGvC&JXgQo$!PsVK3i(va7H z8zjul!xEBOTw*A1Aj`(7)#lOmotKf5QA{AQJTp1lp){kov?x_CIX~AxPMp`!+|bC# z!oa}L#MCTGoY&C8z|hDX${j$LH8HyxG%;Di0*TR}iAjg4iII`vWcSRJ-+@eN;WoFQ z&wi)&{(X_y0{{8W^E8ybt&g7;cy0e-ne@#S|0{Or-V*+0{wU4fICkm$faL9O7ad<7 zsUiJI_H)|otvoS#A6MkJ@4b5R$hqjQG`_u8R<})vOf}iR%52>#gFi|=`#5!8-%oh` zS^R8GZtP5+HFbi=xcpdx{geE;X4F32xs*w3nKEB@lUNt^4j3vvE0`M{%>be zWSC^Bd-#j^zwh0X^7r3gb6Dw+Y1cl!cbvWl4==Soa$1#$)LzbU$;*Z_ZCci4Br|aE`3$42ke2=}rumVWW&G!JN{(|ZnkdR(z^`2P zdDY8=pOWHC-2BznTt9fOA2@No>^6gS*A~ka2~`Vct(>&wW7BKboj1jo<}9iB{nzM^ zhs4L++JmJ{?;Y4boeeaXVY#sRln{u{pJ;UAfPIklR=p%K?(;Pai zbfovZsL=3aJ9vI^MVFEBva{Qc-Mh8_KDU3^qQloO&br-xbKU&lM-z^7@1Ay?a|3e| zBP#@nl{x%UWfZvE~1_lLyyMj88CHW%2%wm}lzY zg1Oztnyf!m&Hm}UQsYIO%ad?J?c~6|2i2?UjSuqIUv5$6$mZvh`#!J3sW!p+_LBt{ z9CDPJSQdUdp4+UxCj9e^w!df88bdlB^RLQCc$_u+-THMYX;n!@h8Bh_IgD!e7Dm4; zVP&7q@O(*tB+k1U;Uv1yT!F#VYwj2OF3;s_4 literal 0 HcmV?d00001 diff --git a/test/dsa3072-1.der b/test/dsa3072-1.der new file mode 100644 index 0000000000000000000000000000000000000000..dde41348de34b06a1c462228fb8c004e850ce1bd GIT binary patch literal 1613 zcmXqLV)Ha;VtvQN$*}$O_Lf&OtK1E^**Fv0JQ!1%S(up&N({LTIN6v(S=fY`LW5x( z4jv}|AVWa|evk}14@W?LQAti>UW%cpfiOsji-#w|)WA}~Co`!iv8d9J*MJ)&%+13R zl3HA1C~qLk#;Mij(e|B}k&{tOAhA3%IoqK$qqwvvRWCU|*Fa93*U;S1$jHLLz|hpl zJW8C`(89pb$Q;TYAXhc995ZNQwq#@1YV$Z}!NO?J#H_>A#MsEt6xMxqed(W7owKLg zu2mLRsT1vB-Y=)m(uMA@5APMzbudIt&{v{ z(ZZC|P%Xpx!^+ygu3S$3a9=r=-o1HRCpYxk>`mFVv)-HQ+VZ$foCf(TJ#Z%h; zU#jWy-^qRZXuERvhnd&Hk8*bXx^b_{D1rUxvdBw}3qEf?ao@{og=R)0(~i|Yg`XS0 zoae&1bB&Fn%k_hy2^S=pL=rE(XX$9Y^e}&)yu_@1cK`RBn0Ze6`R#e!Om_|H_@^_@ zI&gF$BhOl+%VE1Xb{j;ma%3vqJS{r#6MMU!=j4YvjKRE3_J0ab`z(4F#?@j~>+0ll z=``C5m6C*&S69dsUwt=K*sS8^n(%o%4`ma0nG_k$Gi@l?w<7-Alc;p}u6l#SQw#Q7 zmcR0({MOn%7VFnBs53P&HmDW&kt@&wwNBMnx?>Y}HQ*G%zd3JJ{ zX2aX5tXDW5M#M39cKOx4;^z7_Q?{Dt*0HsYQ}vdu?zm*6Hq|dBpW- z!AngpFbLiITr!inW#`$@1xq;|WGHXjve^8Mzxd+S%jW%)Y5I~cCC|Vk@of3;t4xgB zxc0{cecM``xXkFxrTt$$b4_iEC8irN-!NJvsV^^IeOokczPjyvPsba&!5Y;T|5pX5 zFXR1FFW1($&+SV|e>88yuQNQ)R6hRk$_&`JZ5GXHIabUw?k+>Phx~_ZCKr_9uo`rOr}2aqMW-d#@0i>*9%X?`Zh%cNJ$@ z_B8(Zg}c1RIw$Vg{`FRUXRwU#PQ#8Zyz_pS`s`H5LmhHuASFJ5*{c~gn!$ql~)PxG8_liO&rS|j{7-=~WAKQ**XbxR&t zyLFYWi79+lxoEP)Q_tD^etO)tKXBySl%VMWzH0v#uG?a{jsJ>M_%Ed^I_ErI2hUp{ zbhPWq8+#@&s`!9ZSVO^#% zb48Nzn^oUf`tqZGD%GDUWjP*xbhTiyq_j-!i{Lp?`<1@hDg?zW6zZ)0Ah=ms@zUPw z|7PdEQLm7+{H0anAU##4Mn^S_!L-^vENJ4j-{%c%*qxLW6PfI)Onp8+KB{qbK1Z#` zs|y=7Up}(G{HR<8e0 literal 0 HcmV?d00001 diff --git a/test/dsa3072-2.der b/test/dsa3072-2.der new file mode 100644 index 0000000000000000000000000000000000000000..fe1d75118b727b602acca0071e3643f154ca35e4 GIT binary patch literal 1613 zcmXqLV)Ha;VtvQN$F)G88o62g$JWa0KKRmEYOoLX%jZQpqrIT^(S63a7_vmHt^ic5=9^^)^*4dldm4b2UWj4TWc49!g~ zqQrR(Ees5e%%R)?a#a({F@q*%OEz|`Hji@_EQ|(C%sNa>jExLUVclofm;PDRIeWV8 zT4iCCI?)d1{c`#|O@fRmJDyvtzbP;WB>eqJxz1TD`(hvpe6kP>!CA| z`-22lm6{(|`>fB0N9EP+mJ3I^s%KPd|JiMD>h!q;pV*syt7bd3N4E90miD)b&8&Xo zeEG1c_o*P&TcT;%ZpjbN6kjn9+fnnv`Ok`jQ95C*UrThQMD=d+K5V}C%ksG1I?0a~ zElfEL)iRtvtgH>}%H`x=?!G&r= z14kD!@~kzw9JY&Nw?XtON2b!v)1m`EvA64aPJXDv7|h#b|EKV@&!Tr>TrF0$u1-Fe zPP4sGDM?s)b%jjv)pt{c%_?5537^OFP&R>=Ns-|^(}se5E8@RBiAs0xsy9eHwP4R> z`72M#Z>`;9v3?zcI#Uy4gIe)l#@@$FY8ts5_w`O2Oo=vBO)v^L@Z+Xt@g%RAlI6#j zZ8l%^|EWFK;@g$Sui4kS8&8q#+COPx!nVRo@4W8UnxE!(l;6kquJh0`)t26qXD63w zHoTq6dWGX*L>zNxmtWm0ZmwT5Wvh8^9b4--Rd3nqj!Q;rQ~fg5-c1i|(=bYqe(cE< zywv0ZgV4RtB{P{@cAgDgu$1FLhVr&8i_PEoi!WZiY~DYarZ4$Y@(er@&zAqb%EY*h zYky48x2?sA%Z$!k+W*xv*VLw1V!8qI4WmVp`ttJCw?*UTtJ}`^biAP(tWj<8e^r3` zGTuM+a&3M4+`g3bNAouPI>Yl!<>Mc(%z%wcJ}|~xd!6Y@&uMXSr$BtIL_X@GOE}l5|j)wn!S8IJMVX?&wj>deUn}1ndGeN`dOK>_xu06e_^^e zA8dTlz}&>x$^eQ8H-|4`u`2&(^3L!Kxn_`Ammw%I+uc0+-S(35w}&qNIu&$x>nc|b z{$SoIA#Tr|t{?1e_;&2|;$_#AHQ$yQSx8#Af zTUY6tn8H_;izZ7v^_;!$r^jvk14qtH37Q_@tM+f?q0Z_Lf}52UFYUel zZ+8A0^$JPLUs^Q|(obX3C_Osn0)f+k-3ecr%^-AP$7k;$&g)aT>lqZ(J|bJTjg zy0B66UuR}rR`jXtc=444m!pA; zXLT)k>pMHe@sPRVBDQsJHeK`8UKU;~z4y~({q@gY&Y1Eqs{5$q39J9lre3gi-jvQB zatNh}XZBz)aAi`co}hE9u`cSOx7*8Ae)`ewcBYX}%)GzH{x$ilGyAE#Ig=v8*)IZp iPnzp8Ge7-IE14eNbC6k?Gwy5JB^A3r2E_-qe*pk)L$|m9 literal 0 HcmV?d00001 diff --git a/test/ec384-1.der b/test/ec384-1.der new file mode 100644 index 0000000000000000000000000000000000000000..fdc7c92642a33b05ce22f19426529b60b401a98a GIT binary patch literal 544 zcmXqLVv;dvVqDC`$?zyEeCM6xg3k@O*f_M>JkHs&Ff$oc7;+nMvN4CUun9AT1{?Al z@ParTJZ%0&spQgNQKccT z0XImPn};PNwYbDk-awX(Q>)FR?K>|cC!?4^VtHnAwnJ$~acNPiUUGh}ft)z6p}C=v zk%ghTfuV^>lsK=Ug@K`wIg~qqE-Nz-U}Fb|5ECOCt9By`gA#KR155IyZ*8s%tdBqS z``j608SA26VUjHI+W1qE|LdvyA0J)iqW5Tj2fqu)E7v$Cac+fR-i?Y$`UdmYiO6kG z?#u|>oupP*_V(84u%|Mo8#k=^a3zU%`9}MygI~N_{wqB^qL7aqFU;u-1}RJiWs@(~ zWmz5fE$uBhWwb?JSPVNrf8eq_8{nZcWu&`ukb@WGN;CU-P3+ y#jL8f|6_Q#lS?jnI*Wn9t2*Z|QNkI4+S~aZjP+Vh^0tV~o4$o5xK!qm$3Xzg0i}lk literal 0 HcmV?d00001 diff --git a/test/ec384-2.der b/test/ec384-2.der new file mode 100644 index 0000000000000000000000000000000000000000..bea37b56388795cba6b1fef9a0d22c63a0d8ea70 GIT binary patch literal 544 zcmXqLVv;dvVqDC`$?$aY%x5CJzc>xJ*f_M>JkHs&Ff$ue7;+nMvN4CUun9AT1{?Al z@ParTJZ%0&spQgNQKccT z0XImPn};PNwYbDk-awX(Q>)FR?K>|cC!?4^VtHnAwnJ$~acNPiUUGh}ft)z6p}C=v zk%ghTfsv_slsK=Ug@K`wIg~qqE-Nz-U}Fb|5ECOCt9By`gA#KR155IyZ*8s%tdBqS z``j608SA26VUjHI+W1qE|LdvyA0J)iqW5Tj2fqu)E7v$Cac+fR-i?Y$`UdmYiO6kG z?#u|>oupP*_V(84u%|Mo8#k=^a3zU%`9}MygI~N_{wqB^qL7aqFU;u-1}RJi^^N@6 zx97?@^FBVXAWWmqb$)t%*r{~kyAID6o#{=V`>?1_%+s&y;LfO?Gw)d^iq|q3czFnY z+;xBJXX{OQnjulbbphKO?z-;Q6L@*_aGC$DJ(3oVD(6Z(Ty-OTftY#dr`9_MUXm{|-m4Y>_C*_cCF*o2uvgAMr& zctIQv9yb4?)b#v3LqP+6kN`UmM?ij2Nls#3ilL~1Fi41thbO|+z*4~{GpQ)CsM1j0 zK$eYDtIebBJ1-+AqnJQqd1i99Lup2FX;G?Pa(=FXoH(zcxuKDfg`v5Dv5`@fIIp3F zfuWH(luJ*ilo<%Hv4g{aiII&}yOD)Ki8+aZCHc~~HrEB#$DjIr?u@aFby2S{NtSqR z{He(Q_0;{3kFIjjd$hlU--Y9qYaEj}w?Z)QM#Ut3gZb-39^8`gZdlEk}wqkYxEFJ3MGl^z~Z$VU!&<_rddR3<})Gv$77i*IG?&8+ZDF7>@? zU}W=HE;=B7_l(HscuNkQ`?H&bW;jkVG*#)=UvWrv=5HngM~|AGw^2$Zsa?kx#g%7$ mzmz*uYjU_