extern crate chrono; extern crate num; #[cfg(test)] #[macro_use] extern crate quickcheck; #[macro_use] extern crate simple_asn1; extern crate simple_rsa; 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_rsa::{RSAPublicKey,RSAError,SigningHash, SIGNING_HASH_SHA1, SIGNING_HASH_SHA224, SIGNING_HASH_SHA256, SIGNING_HASH_SHA384, SIGNING_HASH_SHA512}; #[derive(Clone,Debug,PartialEq)] enum HashAlgorithm { None, MD2, MD5, SHA1, SHA224, SHA256, SHA384, SHA512 } #[derive(Clone,Debug,PartialEq)] enum PubKeyAlgorithm { RSA, RSAPSS, DSA, EC, DH, Unknown(OID) } #[derive(Debug)] enum X509ParseError { ASN1DecodeError(ASN1DecodeErr), RSAError(RSAError), NotEnoughData, ItemNotFound, IllegalFormat, NoSerialNumber, NoSignatureAlgorithm, NoNameInformation, IllFormedNameInformation, NoValueForName, UnknownAttrTypeValue, IllegalStringValue, NoValidityInfo, ImproperValidityInfo, NoSubjectPublicKeyInfo, ImproperSubjectPublicKeyInfo, BadPublicKeyAlgorithm, UnsupportedPublicKey, InvalidRSAKey, UnsupportedExtension, UnexpectedNegativeNumber, MissingNumber, NoSignatureFound, UnsupportedSignature, SignatureFailed } #[derive(Clone,Debug,PartialEq)] enum X509PublicKey { RSA(RSAPublicKey) } 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 FromASN1 for PubKeyAlgorithm { type Error = X509ParseError; fn from_asn1(bs: &[ASN1Block]) -> Result<(PubKeyAlgorithm,&[ASN1Block]),X509ParseError> { match bs.split_first() { None => Err(X509ParseError::NotEnoughData), Some((x, rest)) => { match x { &ASN1Block::ObjectIdentifier(_, _, ref oid) => { if oid == oid!(1,2,840,113549,1,1,1) { return Ok((PubKeyAlgorithm::RSA, rest)) } if oid == oid!(1,2,840,113549,1,1,10) { return Ok((PubKeyAlgorithm::RSAPSS, rest)) } if oid == oid!(1,2,840,10040,4,1) { return Ok((PubKeyAlgorithm::DSA, rest)) } if oid == oid!(1,2,840,10045,2,1) { return Ok((PubKeyAlgorithm::EC, rest)) } if oid == oid!(1,2,840,10046,2,1) { return Ok((PubKeyAlgorithm::DH, rest)) } Ok((PubKeyAlgorithm::Unknown(oid.clone()), rest)) } _ => Err(X509ParseError::ItemNotFound) } } } } } impl ToASN1 for PubKeyAlgorithm { type Error = ASN1EncodeErr; fn to_asn1_class(&self, c: ASN1Class) -> Result,Self::Error> { let res = match self { &PubKeyAlgorithm::RSA => ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,113549,1,1,1)), &PubKeyAlgorithm::RSAPSS => ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,113549,1,1,10)), &PubKeyAlgorithm::DSA => ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10040,4,1)), &PubKeyAlgorithm::EC => ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10045,2,1)), &PubKeyAlgorithm::DH => ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10046,2,1)), &PubKeyAlgorithm::Unknown(ref oid) => ASN1Block::ObjectIdentifier(c, 0, oid.clone()) }; Ok(vec![res]) } } #[derive(Clone,Debug,PartialEq)] struct SignatureAlgorithm { hash_alg: HashAlgorithm, key_alg: PubKeyAlgorithm } impl FromASN1 for SignatureAlgorithm { type Error = X509ParseError; fn from_asn1(bs: &[ASN1Block]) -> Result<(SignatureAlgorithm,&[ASN1Block]),X509ParseError> { match bs.split_first() { None => Err(X509ParseError::NotEnoughData), 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) } } } } } #[derive(Clone,Debug,PartialEq)] pub enum SigAlgEncodeErr { ASN1Problem(ASN1EncodeErr), UnknownSignatureAlgorithm } impl From for SigAlgEncodeErr { fn from(v: ASN1EncodeErr) -> SigAlgEncodeErr { SigAlgEncodeErr::ASN1Problem(v) } } impl ToASN1 for SignatureAlgorithm { type Error = SigAlgEncodeErr; fn to_asn1_class(&self,c: ASN1Class) -> Result,SigAlgEncodeErr> { let badval = SigAlgEncodeErr::UnknownSignatureAlgorithm; let oid = match self.key_alg { PubKeyAlgorithm::RSA => match self.hash_alg { HashAlgorithm::None => oid!(1,2,840,113549,1,1,1), HashAlgorithm::MD2 => oid!(1,2,840,113549,1,1,2), HashAlgorithm::MD5 => oid!(1,2,840,113549,1,1,4), HashAlgorithm::SHA1 => oid!(1,2,840,113549,1,1,5), HashAlgorithm::SHA224 => oid!(1,2,840,113549,1,1,14), HashAlgorithm::SHA256 => oid!(1,2,840,113549,1,1,11), HashAlgorithm::SHA384 => oid!(1,2,840,113549,1,1,12), HashAlgorithm::SHA512 => oid!(1,2,840,113549,1,1,13), }, PubKeyAlgorithm::RSAPSS => match self.hash_alg { HashAlgorithm::None => oid!(1,2,840,113549,1,1,10), HashAlgorithm::MD2 => return Err(badval), HashAlgorithm::MD5 => return Err(badval), HashAlgorithm::SHA1 => return Err(badval), HashAlgorithm::SHA224 => oid!(2,16,840,1,101,3,4,2,4), HashAlgorithm::SHA256 => oid!(2,16,840,1,101,3,4,2,1), HashAlgorithm::SHA384 => oid!(2,16,840,1,101,3,4,2,2), HashAlgorithm::SHA512 => oid!(2,16,840,1,101,3,4,2,3), }, PubKeyAlgorithm::DSA => match self.hash_alg { HashAlgorithm::None => oid!(1,2,840,10040,4,1), HashAlgorithm::MD2 => return Err(badval), HashAlgorithm::MD5 => return Err(badval), HashAlgorithm::SHA1 => oid!(1,2,840,10040,4,3), HashAlgorithm::SHA224 => oid!(2,16,840,1,101,3,4,3,1), HashAlgorithm::SHA256 => oid!(2,16,840,1,101,3,4,3,2), HashAlgorithm::SHA384 => return Err(badval), HashAlgorithm::SHA512 => return Err(badval), }, PubKeyAlgorithm::EC => match self.hash_alg { HashAlgorithm::None => oid!(1,2,840,10045,2,1), HashAlgorithm::MD2 => return Err(badval), HashAlgorithm::MD5 => return Err(badval), HashAlgorithm::SHA1 => oid!(1,2,840,10045,4,1), HashAlgorithm::SHA224 => oid!(1,2,840,10045,4,3,1), HashAlgorithm::SHA256 => oid!(1,2,840,10045,4,3,2), HashAlgorithm::SHA384 => oid!(1,2,840,10045,4,3,3), HashAlgorithm::SHA512 => oid!(1,2,840,10045,4,3,4), }, PubKeyAlgorithm::DH => match self.hash_alg { HashAlgorithm::None => oid!(1,2,840,10046,2,1), _ => return Err(badval) } PubKeyAlgorithm::Unknown(ref oid) => match self.hash_alg { HashAlgorithm::None => oid.clone(), _ => return Err(badval) } }; Ok(vec![ASN1Block::ObjectIdentifier(c, 0, oid)]) } } #[derive(Clone,Debug,PartialEq)] struct Certificate { version: u32, serial: BigUint, signature_alg: SignatureAlgorithm, issuer: InfoBlock, subject: InfoBlock, validity: Validity, subject_key: X509PublicKey, extensions: Vec<()> } impl FromASN1WithBody for Certificate { type Error = X509ParseError; fn from_asn1_with_body<'a>(bs: &'a[ASN1Block], raw_input: &[u8]) -> Result<(Certificate,&'a[ASN1Block]),X509ParseError> { match bs.split_first() { None => Err(X509ParseError::NotEnoughData), Some((&ASN1Block::Sequence(_,_,ref v), rest)) if v.len() == 3 => { // Certificate ::= SEQUENCE { // tbsCertificate TBSCertificate, // signatureAlgorithm AlgorithmIdentifier, // signatureValue BIT STRING } let certblock = get_tbs_certificate(&v[0])?; let algblock = get_signature_alg(&v[1])?; let hashend = v[1].offset(); match v[2] { ASN1Block::BitString(_, _, size, ref sig) if size % 8 == 0 => { let signed_block: &[u8] = &raw_input[0..hashend]; check_signature(algblock, &certblock.subject_key, signed_block, sig.to_vec()); Ok((certblock, rest)) } _ => Err(X509ParseError::NoSignatureFound) } } Some(_) => Err(X509ParseError::ItemNotFound) } } } fn check_signature(alg: SignatureAlgorithm, key: &X509PublicKey, block: &[u8], sig: Vec) -> Result<(),X509ParseError> { match (alg.key_alg, key) { (PubKeyAlgorithm::RSA, &X509PublicKey::RSA(ref key)) => { let shash = signing_hash(alg.hash_alg)?; if !key.verify(shash, block, sig) { return Err(X509ParseError::SignatureFailed); } Ok(()) } _ => { Err(X509ParseError::UnsupportedSignature) } } } fn signing_hash(a: HashAlgorithm) -> Result<&'static SigningHash,X509ParseError> { match a { HashAlgorithm::SHA1 => Ok(&SIGNING_HASH_SHA1), HashAlgorithm::SHA224 => Ok(&SIGNING_HASH_SHA224), HashAlgorithm::SHA256 => Ok(&SIGNING_HASH_SHA256), HashAlgorithm::SHA384 => Ok(&SIGNING_HASH_SHA384), HashAlgorithm::SHA512 => Ok(&SIGNING_HASH_SHA512), _ => Err(X509ParseError::UnsupportedSignature) } } 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 } match x { &ASN1Block::Sequence(_, _, ref v) if v.len() == 2 => { let (alg, _) = SignatureAlgorithm::from_asn1(v)?; Ok(alg) } _ => Err(X509ParseError::IllegalFormat) } } fn get_version(bs: &[ASN1Block]) -> Result<(u32, &[ASN1Block]),X509ParseError> { match bs.split_first() { Some((&ASN1Block::Integer(_, _, ref v), rest)) => { match v.to_u8() { Some(0) => Ok((1, rest)), Some(1) => Ok((2, rest)), Some(2) => Ok((3, rest)), _ => Ok((1, &bs)) } } _ => Err(X509ParseError::NoSerialNumber) } } fn get_serial(bs: &[ASN1Block]) -> Result<(BigUint,&[ASN1Block]),X509ParseError> { match bs.split_first() { Some((first, rest)) => { let res = decode_biguint(first)?; Ok((res, rest)) } None => Err(X509ParseError::NoSerialNumber) } } fn decode_biguint(b: &ASN1Block) -> Result { match b { &ASN1Block::Integer(_, _, ref v) => { match v.to_biguint() { Some(sn) => Ok(sn), _ => Err(X509ParseError::UnexpectedNegativeNumber) } } _ => Err(X509ParseError::MissingNumber) } } fn get_signature_info(bs: &[ASN1Block]) -> Result<(SignatureAlgorithm, &[ASN1Block]),X509ParseError> { match bs.split_first() { Some((x, rest)) => { let alg = get_signature_alg(&x)?; Ok((alg, rest)) } _ => Err(X509ParseError::NoSignatureAlgorithm) } } #[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> { match b.split_first() { // SubjectPublicKeyInfo ::= SEQUENCE { // algorithm AlgorithmIdentifier, // subjectPublicKey BIT STRING } Some((&ASN1Block::Sequence(_, _, ref info), rest)) => { if info.len() != 2 { return Err(X509ParseError::ImproperSubjectPublicKeyInfo) } let alginfo = get_signature_alg(&info[0])?; // this better not really be a signature with a hash if alginfo.hash_alg != HashAlgorithm::None { return Err(X509ParseError::BadPublicKeyAlgorithm) } // the actual key format depends on the algorithm match alginfo.key_alg { PubKeyAlgorithm::RSA => { let key = get_rsa_public_key(&info[1])?; Ok((X509PublicKey::RSA(key), rest)) } _ => { Err(X509ParseError::UnsupportedPublicKey) } } } _ => Err(X509ParseError::NoSubjectPublicKeyInfo) } } fn get_rsa_public_key(b: &ASN1Block) -> Result { match b { &ASN1Block::BitString(_, _, size, ref vec) if size % 8 == 0 => { der_decode(vec).map_err(|x| X509ParseError::RSAError(x)) } _ => 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; use super::*; impl Arbitrary for PubKeyAlgorithm { fn arbitrary(g: &mut G) -> PubKeyAlgorithm { match g.gen::() % 6 { 0 => PubKeyAlgorithm::RSA, 1 => PubKeyAlgorithm::RSAPSS, 2 => PubKeyAlgorithm::DSA, 3 => PubKeyAlgorithm::EC, 4 => PubKeyAlgorithm::DH, 5 => { let v1 = g.gen::(); let v2 = g.gen::(); let oid = oid!(1,2,840,10049,v1,v2); PubKeyAlgorithm::Unknown(oid) } _ => panic!("A broken, cruel world.") } } } fn inversion_works(v: T) -> bool { match der_encode(&v) { Ok(der) => match der_decode(&der) { Ok(v2) => v == v2, Err(_) => false }, Err(_) => false } } quickcheck! { fn pubkey_alg_inverts(pka: PubKeyAlgorithm) -> bool { inversion_works(pka) } } impl Arbitrary for SignatureAlgorithm { fn arbitrary(g: &mut G) -> SignatureAlgorithm { let possibles = vec![ SignatureAlgorithm { hash_alg: HashAlgorithm::None, key_alg: PubKeyAlgorithm::RSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA1, key_alg: PubKeyAlgorithm::RSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::MD5, key_alg: PubKeyAlgorithm::RSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::MD2, key_alg: PubKeyAlgorithm::RSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA256, key_alg: PubKeyAlgorithm::RSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA384, key_alg: PubKeyAlgorithm::RSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA512, key_alg: PubKeyAlgorithm::RSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA224, key_alg: PubKeyAlgorithm::RSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::None, key_alg: PubKeyAlgorithm::DSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA1, key_alg: PubKeyAlgorithm::DSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::None, key_alg: PubKeyAlgorithm::EC }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA1, key_alg: PubKeyAlgorithm::EC }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA224, key_alg: PubKeyAlgorithm::EC }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA256, key_alg: PubKeyAlgorithm::EC }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA384, key_alg: PubKeyAlgorithm::EC }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA512, key_alg: PubKeyAlgorithm::EC }, SignatureAlgorithm { hash_alg: HashAlgorithm::None, key_alg: PubKeyAlgorithm::RSAPSS }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA256, key_alg: PubKeyAlgorithm::RSAPSS }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA384, key_alg: PubKeyAlgorithm::RSAPSS }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA512, key_alg: PubKeyAlgorithm::RSAPSS }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA224, key_alg: PubKeyAlgorithm::RSAPSS }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA224, key_alg: PubKeyAlgorithm::DSA }, SignatureAlgorithm { hash_alg: HashAlgorithm::SHA256, key_alg: PubKeyAlgorithm::DSA }]; match g.choose(&possibles[..]) { Some(v) => v.clone(), None => panic!("Couldn't generate arb value.") } } } quickcheck! { fn sigalg_inverts(sa: SignatureAlgorithm) -> bool { inversion_works(sa) } } 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); der_decode(&buffer[..]) } #[test] fn x509_tests() { assert!(can_parse("test/server.bin").is_ok()); } }