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

Added an unrecognized_extensions hash to the Cert struct #259

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
60 changes: 44 additions & 16 deletions src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

use crate::{der, signed_data, Error};
#[cfg(feature = "std")]
use std::collections::HashMap;

pub enum EndEntityOrCa<'a> {
EndEntity,
Expand All @@ -32,6 +34,8 @@ pub struct Cert<'a> {
pub eku: Option<untrusted::Input<'a>>,
pub name_constraints: Option<untrusted::Input<'a>>,
pub subject_alt_name: Option<untrusted::Input<'a>>,
#[cfg(feature = "std")]
pub unrecognized_extensions: HashMap<&'a[u8], untrusted::Input<'a>>,
}

pub fn parse_cert<'a>(
Expand Down Expand Up @@ -93,6 +97,8 @@ pub(crate) fn parse_cert_internal<'a>(
eku: None,
name_constraints: None,
subject_alt_name: None,
#[cfg(feature = "std")]
unrecognized_extensions: HashMap::new(),
};

// mozilla::pkix allows the extensions to be omitted. However, since
Expand Down Expand Up @@ -166,42 +172,64 @@ enum Understood {

fn remember_extension<'a>(
cert: &mut Cert<'a>,
extn_id: untrusted::Input,
extn_id: untrusted::Input<'a>,
value: untrusted::Input<'a>,
) -> Result<Understood, Error> {
// We don't do anything with certificate policies so we can safely ignore
// all policy-related stuff. We assume that the policy-related extensions
// are not marked critical.

// id-ce 2.5.29
static ID_CE: [u8; 2] = oid![2, 5, 29];

if extn_id.len() != ID_CE.len() + 1 || !extn_id.as_slice_less_safe().starts_with(&ID_CE) {
return Ok(Understood::No);
}

let out = match *extn_id.as_slice_less_safe().last().unwrap() {
const ID_CE_KEY_USAGE: &[u8] = &oid![2, 5, 29, 15];
const ID_CE_SUBJECT_ALT_NAME: &[u8] = &oid![2, 5, 29, 17];
const ID_CE_BASIC_CONSTRAINTS: &[u8] = &oid![2, 5, 29, 19];
const ID_CE_NAME_CONSTRAINTS: &[u8] = &oid![2, 5, 29, 30];
const ID_CE_EXT_KEY_USAGE: &[u8] = &oid![2, 5, 29, 37];

let extension_id = &*extn_id.as_slice_less_safe();
// note: the "strange" arrays below could be generated by the `oid!` macro. However,
// I can't find a way to make Rust let me put the output of that macro in a match expression.
// It looks like it's in progress, however (https://github.com/rust-lang/rust/issues/74446).
// Until then, I'm afraid we're left with this jankiness (suggestions appreciated)
let out = match extension_id {
// id-ce-keyUsage 2.5.29.15. We ignore the KeyUsage extension. For CA
// certificates, BasicConstraints.cA makes KeyUsage redundant. Firefox
// and other common browsers do not check KeyUsage for end-entities,
// though it would be kind of nice to ensure that a KeyUsage without
// the keyEncipherment bit could not be used for RSA key exchange.
15 => {
ID_CE_KEY_USAGE => {
return Ok(Understood::Yes);
}

// id-ce-subjectAltName 2.5.29.17
17 => &mut cert.subject_alt_name,
ID_CE_SUBJECT_ALT_NAME => &mut cert.subject_alt_name,

// id-ce-basicConstraints 2.5.29.19
19 => &mut cert.basic_constraints,
ID_CE_BASIC_CONSTRAINTS => &mut cert.basic_constraints,

// id-ce-nameConstraints 2.5.29.30
30 => &mut cert.name_constraints,
ID_CE_NAME_CONSTRAINTS => &mut cert.name_constraints,

// id-ce-extKeyUsage 2.5.29.37
37 => &mut cert.eku,

ID_CE_EXT_KEY_USAGE => &mut cert.eku,

//This is not a recognized extension, add it to the unrecognized
// extension hash and return `Understood::No`
#[cfg(feature = "std")]
_ => {
let value = value.read_all(Error::BadDER, |value| {
Ok(value.read_bytes_to_end())
})?;
match cert.unrecognized_extensions.insert(extension_id, value) {
Some(_) => {
// There appears to be two unrecognized extensions with the same ID.
return Err(Error::ExtensionValueInvalid);
}
None => {
// Insertion was successful, and the extension is unique to the certificate
return Ok(Understood::No)
},
}
}
#[cfg(not(feature = "std"))]
_ => {
return Ok(Understood::No);
}
Expand Down