diff --git a/src/cert.rs b/src/cert.rs index ac65e525..d2107cca 100644 --- a/src/cert.rs +++ b/src/cert.rs @@ -27,7 +27,7 @@ pub struct Cert<'a> { pub issuer: untrusted::Input<'a>, pub validity: untrusted::Input<'a>, pub subject: untrusted::Input<'a>, - pub spki: untrusted::Input<'a>, + pub spki: SubjectPublicKeyInfoRef<'a>, pub basic_constraints: Option>, pub eku: Option>, @@ -68,7 +68,9 @@ pub fn parse_cert_internal<'a>( let issuer = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; let validity = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; let subject = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; - let spki = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?; + let spki = SubjectPublicKeyInfoRef( + der::expect_tag_and_get_value(tbs, der::Tag::Sequence)? + ); // In theory there could be fields [1] issuerUniqueID and [2] // subjectUniqueID, but in practice there never are, and to keep the @@ -202,3 +204,46 @@ fn remember_extension<'a>(cert: &mut Cert<'a>, extn_id: untrusted::Input, Ok(Understood::Yes) } + +pub struct SubjectPublicKeyInfo<'a> { + pub algorithm_id_value: untrusted::Input<'a>, + pub key_value: SubjectPublicKeyRef<'a>, +} + +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct SubjectPublicKeyInfoRef<'a>(pub untrusted::Input<'a>); + +impl<'a> SubjectPublicKeyInfoRef<'a> { + // Parse the public key into an algorithm OID, an optional curve OID, and the + // key value. The caller needs to check whether these match the + // `PublicKeyAlgorithm` for the `SignatureAlgorithm` that is matched when + // parsing the signature. + pub fn parse(self) -> Result, Error> { + let input: untrusted::Input = From::from(self); + input.read_all(Error::BadDER, |input| { + let algorithm_id_value = + der::expect_tag_and_get_value(input, der::Tag::Sequence)?; + let key_value = der::bit_string_with_no_unused_bits(input)?; + Ok(SubjectPublicKeyInfo { + algorithm_id_value: algorithm_id_value, + key_value: SubjectPublicKeyRef(key_value), + }) + }) + } +} + +impl<'a> From> for untrusted::Input<'a> { + fn from(spkir: SubjectPublicKeyInfoRef<'a>) -> Self { + spkir.0 + } +} + +#[derive(PartialEq, Eq)] +pub struct SubjectPublicKeyRef<'a>(pub untrusted::Input<'a>); + +impl<'a> From> for untrusted::Input<'a> { + fn from(spkr: SubjectPublicKeyRef<'a>) -> Self { + spkr.0 + } +} + diff --git a/src/signed_data.rs b/src/signed_data.rs index 9116fa0b..b0b51869 100644 --- a/src/signed_data.rs +++ b/src/signed_data.rs @@ -15,6 +15,7 @@ use {der, Error}; use ring::signature; use untrusted; +use cert::SubjectPublicKeyInfoRef; /// X.509 certificates and related items that are signed are almost always /// encoded in the format "tbs||signatureAlgorithm||signature". This structure @@ -90,7 +91,7 @@ pub fn parse_signed_data<'a>(der: &mut untrusted::Reader<'a>) /// but generally more common algorithms should go first, as it is scanned /// linearly for matches. pub fn verify_signed_data(supported_algorithms: &[&SignatureAlgorithm], - spki_value: untrusted::Input, + spki_value: SubjectPublicKeyInfoRef, signed_data: &SignedData) -> Result<(), Error> { // We need to verify the signature in `signed_data` using the public key // in `public_key`. In order to know which *ring* signature verification @@ -133,42 +134,19 @@ pub fn verify_signed_data(supported_algorithms: &[&SignatureAlgorithm], } pub fn verify_signature(signature_alg: &SignatureAlgorithm, - spki_value: untrusted::Input, msg: untrusted::Input, + spki_value: SubjectPublicKeyInfoRef, + msg: untrusted::Input, signature: untrusted::Input) -> Result<(), Error> { - let spki = parse_spki_value(spki_value)?; + let spki = spki_value.parse()?; if !signature_alg.public_key_alg_id .matches_algorithm_id_value(spki.algorithm_id_value) { return Err(Error::UnsupportedSignatureAlgorithmForPublicKey); } - signature::verify(signature_alg.verification_alg, spki.key_value, msg, - signature) + signature::verify(signature_alg.verification_alg, spki.key_value.into(), + msg, signature) .map_err(|_| Error::InvalidSignatureForPublicKey) } - -struct SubjectPublicKeyInfo<'a> { - algorithm_id_value: untrusted::Input<'a>, - key_value: untrusted::Input<'a>, -} - -// Parse the public key into an algorithm OID, an optional curve OID, and the -// key value. The caller needs to check whether these match the -// `PublicKeyAlgorithm` for the `SignatureAlgorithm` that is matched when -// parsing the signature. -fn parse_spki_value(input: untrusted::Input) - -> Result { - input.read_all(Error::BadDER, |input| { - let algorithm_id_value = - der::expect_tag_and_get_value(input, der::Tag::Sequence)?; - let key_value = der::bit_string_with_no_unused_bits(input)?; - Ok(SubjectPublicKeyInfo { - algorithm_id_value: algorithm_id_value, - key_value: key_value, - }) - }) -} - - /// A signature algorithm. pub struct SignatureAlgorithm { public_key_alg_id: AlgorithmIdentifier, @@ -345,6 +323,7 @@ mod tests { use std; use std::io::BufRead; use {der, Error, signed_data}; + use cert::SubjectPublicKeyInfoRef; use untrusted; // TODO: The expected results need to be modified for SHA-1 deprecation. @@ -365,6 +344,7 @@ mod tests { let spki_value = spki_value.read_all(Error::BadDER, |input| { der::expect_tag_and_get_value(input, der::Tag::Sequence) }).unwrap(); + let spki_value = SubjectPublicKeyInfoRef(spki_value); // we can't use `parse_signed_data` because it requires `data` // to be an ASN.1 SEQUENCE, and that isn't the case with diff --git a/src/trust_anchor_util.rs b/src/trust_anchor_util.rs index f5aee387..a82cbb37 100644 --- a/src/trust_anchor_util.rs +++ b/src/trust_anchor_util.rs @@ -72,9 +72,10 @@ pub fn generate_code_for_trust_anchors(name: &str, } fn trust_anchor_from_cert<'a>(cert: Cert<'a>) -> TrustAnchor<'a> { + let spki: untrusted::Input = From::from(cert.spki); TrustAnchor { subject: cert.subject.as_slice_less_safe(), - spki: cert.spki.as_slice_less_safe(), + spki: spki.as_slice_less_safe(), name_constraints: cert.name_constraints .map(|nc| nc.as_slice_less_safe()) } diff --git a/src/verify_cert.rs b/src/verify_cert.rs index 38654653..e4924ffc 100644 --- a/src/verify_cert.rs +++ b/src/verify_cert.rs @@ -15,7 +15,7 @@ use untrusted; use {cert, der, Error, name, signed_data, SignatureAlgorithm, time, TrustAnchor}; -use cert::{Cert, EndEntityOrCA}; +use cert::{Cert, EndEntityOrCA, SubjectPublicKeyInfoRef}; pub fn build_chain<'a>(required_eku_if_present: KeyPurposeId, supported_sig_algs: &[&SignatureAlgorithm], @@ -59,7 +59,8 @@ pub fn build_chain<'a>(required_eku_if_present: KeyPurposeId, name_constraints, Error::BadDER, |value| name::check_name_constraints(value, &cert))?; - let trust_anchor_spki = untrusted::Input::from(trust_anchor.spki); + let trust_anchor_spki = + SubjectPublicKeyInfoRef(untrusted::Input::from(trust_anchor.spki)); // TODO: check_distrust(trust_anchor_subject, trust_anchor_spki)?; @@ -112,7 +113,8 @@ pub fn build_chain<'a>(required_eku_if_present: KeyPurposeId, } fn check_signatures(supported_sig_algs: &[&SignatureAlgorithm], - cert_chain: &Cert, trust_anchor_key: untrusted::Input) + cert_chain: &Cert, + trust_anchor_key: SubjectPublicKeyInfoRef) -> Result<(), Error> { let mut spki_value = trust_anchor_key; let mut cert = cert_chain; diff --git a/src/webpki.rs b/src/webpki.rs index f9f1dcc9..1ffa95e4 100644 --- a/src/webpki.rs +++ b/src/webpki.rs @@ -248,6 +248,12 @@ impl <'a> EndEntityCert<'a> { signed_data::verify_signature(signature_alg, self.inner.spki, msg, signature) } + + /// Returns the certificate's public key. + pub fn public_key_bytes(&self) -> Result { + let spki = self.inner.spki.parse()?; + Ok(spki.key_value.into()) + } } /// A trust anchor (a.k.a. root CA). diff --git a/tests/integration.rs b/tests/integration.rs index 8dc919b3..79b72247 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -103,6 +103,12 @@ pub fn ed25519() let _ = cert.verify_is_valid_tls_server_cert(ALL_SIGALGS, &anchors, &[], time) .unwrap(); + assert_eq!(cert.public_key_bytes(), + Ok(untrusted::Input::from( + &[0xfe, 0x5a, 0x1e, 0x36, 0x6c, 0x17, 0x27, 0x5b, 0xf1, + 0x58, 0x1e, 0x3a, 0x0e, 0xe6, 0x56, 0x29, 0x8d, 0x9e, + 0x1b, 0x3f, 0xd3, 0x3f, 0x96, 0x46, 0xef, 0xbf, 0x04, + 0x6b, 0xc7, 0x3d, 0x47, 0x5c]))); } #[cfg(feature = "trust_anchor_util")]