Skip to content

Commit

Permalink
Add function to export certificate's public key
Browse files Browse the repository at this point in the history
I agree to license my contributions to each file under the terms given
at the top of each file I changed.
  • Loading branch information
shahn committed Jan 19, 2019
1 parent d139e98 commit b487385
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 35 deletions.
49 changes: 47 additions & 2 deletions src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<untrusted::Input<'a>>,
pub eku: Option<untrusted::Input<'a>>,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<SubjectPublicKeyInfo<'a>, 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<SubjectPublicKeyInfoRef<'a>> 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<SubjectPublicKeyRef<'a>> for untrusted::Input<'a> {
fn from(spkr: SubjectPublicKeyRef<'a>) -> Self {
spkr.0
}
}

38 changes: 9 additions & 29 deletions src/signed_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<SubjectPublicKeyInfo, Error> {
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,
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/trust_anchor_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand Down
8 changes: 5 additions & 3 deletions src/verify_cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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)?;

Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions src/webpki.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<untrusted::Input, Error> {
let spki = self.inner.spki.parse()?;
Ok(spki.key_value.into())
}
}

/// A trust anchor (a.k.a. root CA).
Expand Down
6 changes: 6 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down

0 comments on commit b487385

Please sign in to comment.