Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API to export public key #87

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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