Compare commits

...

2 Commits

Author SHA1 Message Date
occheung 7229a56eef cert rdn: fix struct hierarchy 2020-11-11 17:13:45 +08:00
occheung 65220dbb46 cert: verify alg 2020-11-11 16:16:45 +08:00
7 changed files with 822 additions and 34 deletions

View File

@ -71,6 +71,11 @@ version = "0.4.19"
default-features = false
features = []
[dependencies.intrusive-collections]
version = "0.9.0"
default-features = false
features = []
[dependencies.simple_logger]
version = "1.11.0"
optional = true

View File

@ -24,7 +24,10 @@ use p256::ecdsa::signature::{Verifier, DigestVerifier};
use alloc::vec::Vec;
use heapless::{ Vec as HeaplessVec, consts::* };
use byteorder::{ByteOrder, NetworkEndian};
use core::convert::TryFrom;
use core::convert::TryInto;
#[derive(Debug, Clone)]
pub struct Certificate<'a> {
@ -64,6 +67,15 @@ pub struct Validity {
pub not_after: DateTime<FixedOffset>,
}
impl Validity {
pub fn is_valid(&self, current_time: &DateTime<FixedOffset>) -> Result<(), TlsError> {
match (current_time >= &self.not_before) && (current_time <= &self.not_after) {
true => Ok(()),
false => Err(TlsError::TimeValidityError),
}
}
}
#[derive(Debug, Clone)]
pub enum Time<'a> {
UTCTime(&'a [u8]),
@ -156,7 +168,9 @@ pub enum ExtensionValue<'a> {
Unrecognized,
}
#[derive(Debug, Clone)]
// Embedded value might be empty (&[])
// This means a reject-all/accept-none condition
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum GeneralName<'a> {
OtherName {
type_id: &'a [u8],
@ -175,6 +189,131 @@ pub enum GeneralName<'a> {
RegisteredID(&'a [u8]),
}
// Set operation for General Name (X is a subset of Y, where X, Y are the same variant)
// Will not handle `OtherName`, `X400Address`, `EDIPartyName`, `RegisteredID`,
// as these restrictions of these variants are not suggested
impl<'a> GeneralName<'a> {
pub fn is_subset_of(&self, other: &Self) -> bool {
match (self, other) {
// Special case: empty set
// Empty set is a subset of everything
(Self::URI(self_uri), Self::URI(other_uri)) => {
if self_uri.len() == 0 || other_uri.len() == 0 {
self_uri.len() == 0
} else {
self_uri.ends_with(other_uri)
}
},
(Self::RFC822Name(self_mail), Self::RFC822Name(other_mail)) => {
if self_mail.len() == 0 || other_mail.len() == 0 {
self_mail.len() == 0
} else {
self_mail.ends_with(other_mail)
}
},
(Self::DNSName(self_dns), Self::DNSName(other_dns)) => {
if self_dns.len() == 0 || other_dns.len() == 0 {
self_dns.len() == 0
} else {
self_dns.ends_with(other_dns)
}
},
(Self::IPAddress(self_ip), Self::IPAddress(other_ip)) => {
match (self_ip.len(), other_ip.len()) {
// `self` is a NULL network block
// It is always a subset of any network block
(0, _) => true,
// IPv4 Addresses
(8, 8) => {
let mut self_ip_prefix_len = 0;
for index in 4..8 {
self_ip_prefix_len += self_ip[index].count_ones();
}
let self_ipv4_cidr = smoltcp::wire::IpCidr::new(
smoltcp::wire::IpAddress::v4(
self_ip[0], self_ip[1], self_ip[2], self_ip[3]
),
self_ip_prefix_len.try_into().unwrap()
);
let mut other_ip_prefix_len = 0;
for index in 4..8 {
other_ip_prefix_len += other_ip[index].count_ones();
}
let other_ipv4_cidr = smoltcp::wire::IpCidr::new(
smoltcp::wire::IpAddress::v4(
other_ip[0], other_ip[1], other_ip[2], other_ip[3]
),
other_ip_prefix_len.try_into().unwrap()
);
other_ipv4_cidr.contains_subnet(&self_ipv4_cidr)
},
// Ipv6 Addresses
(32, 32) => {
let mut self_ip_prefix_len = 0;
for index in 16..32 {
self_ip_prefix_len += self_ip[index].count_ones();
}
let self_ipv4_cidr = smoltcp::wire::IpCidr::new(
smoltcp::wire::IpAddress::v6(
NetworkEndian::read_u16(&self_ip[0..2]),
NetworkEndian::read_u16(&self_ip[2..4]),
NetworkEndian::read_u16(&self_ip[4..6]),
NetworkEndian::read_u16(&self_ip[6..8]),
NetworkEndian::read_u16(&self_ip[8..10]),
NetworkEndian::read_u16(&self_ip[10..12]),
NetworkEndian::read_u16(&self_ip[12..14]),
NetworkEndian::read_u16(&self_ip[14..16]),
),
self_ip_prefix_len.try_into().unwrap()
);
let mut other_ip_prefix_len = 0;
for index in 16..32 {
other_ip_prefix_len += other_ip[index].count_ones();
}
let other_ipv4_cidr = smoltcp::wire::IpCidr::new(
smoltcp::wire::IpAddress::v6(
NetworkEndian::read_u16(&other_ip[0..2]),
NetworkEndian::read_u16(&other_ip[2..4]),
NetworkEndian::read_u16(&other_ip[4..6]),
NetworkEndian::read_u16(&other_ip[6..8]),
NetworkEndian::read_u16(&other_ip[8..10]),
NetworkEndian::read_u16(&other_ip[10..12]),
NetworkEndian::read_u16(&other_ip[12..14]),
NetworkEndian::read_u16(&other_ip[14..16]),
),
other_ip_prefix_len.try_into().unwrap()
);
other_ipv4_cidr.contains_subnet(&self_ipv4_cidr)
},
(_, _) => false // Heterogeneity, in terms of IP address type
// Self IP address is not NULL
}
},
_ => false // Heterogeneity, in terms of GeneralName variant
}
}
pub fn is_same_variant(&self, other: &Self) -> bool {
match (self, other) {
(Self::URI(..), Self::URI(..))
| (Self::RFC822Name(..), Self::RFC822Name(..))
| (Self::DNSName(..), Self::DNSName(..))
| (Self::IPAddress(..), Self::IPAddress(..)) => {
true
},
_ => false
}
}
}
#[derive(Debug, Clone)]
pub struct PolicyInformation<'a> {
pub id: &'a [u8],
@ -189,10 +328,28 @@ pub struct AlgorithmIdentifier<'a> {
#[derive(Debug, Clone)]
pub struct Name<'a> {
pub relative_distinguished_name: Vec<AttributeTypeAndValue<'a>>
pub relative_distinguished_name: Vec<RelativeDistinguishedName<'a>>
}
#[derive(Debug, Clone)]
impl<'a> PartialEq for Name<'a> {
fn eq(&self, other: &Self) -> bool {
for self_name in self.relative_distinguished_name.iter() {
if other.relative_distinguished_name.iter().find(
|&att_type_val| att_type_val == self_name
).is_none() {
return false;
}
}
true
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct RelativeDistinguishedName<'a> {
pub type_and_attributes: Vec<AttributeTypeAndValue<'a>>
}
#[derive(Debug, Clone, PartialEq)]
pub struct AttributeTypeAndValue<'a> {
pub attribute_type: &'a [u8], // OID
pub attribute_value: &'a str,
@ -438,3 +595,606 @@ impl<'a> Certificate<'a> {
}
}
}
pub struct ValidPolicyNode<'a> {
valid_policy: &'a [u8],
qualifier_set: &'a [u8],
expected_policy_set: Vec<&'a [u8]>,
}
// Method to verify a prospective certificate chain
// Section 6.1, RFC 5280
pub fn verify_certificate_chain(
certificates: Vec<Certificate>,
current_time: DateTime<FixedOffset>,
// `user_initial_policy_set`, it is any_policy
trusted_issuer_name: Name,
// trusted_signature_algorithm: crate::tls_packet::SignatureScheme,
trusted_public_key: CertificatePublicKey,
initial_policy_mapping_inhibit: bool,
initial_explicit_policy: bool,
initial_any_policy_inhibit: bool,
initial_permitted_subtrees: Vec<GeneralName>,
initial_excluded_subtrees: Vec<GeneralName>
) -> Result<(), TlsError> {
// Note that if the `user_initial_policy_set` variable is set to anyPolicy,
// Requirement: The existance of `valid_policy_tree`
// `valid_policy_tree` is not NULL iff leaves exist at depth k when k certificates are processed
// This leave us with a mapping of operation in the processing steps:
// Adding nodes below leaves -> Swap the leaves with a new set of nodes as leaves
// Pruning a branch due to the lack of new leaf -> no-op (old leaves are deallocated)
let mut valid_policy_tree: Vec<ValidPolicyNode> = Vec::new();
let mut initial_policy = ValidPolicyNode {
valid_policy: crate::oid::ANY_POLICY,
qualifier_set: &[],
expected_policy_set: Vec::new(),
};
initial_policy.expected_policy_set.push(crate::oid::ANY_POLICY);
valid_policy_tree.push(initial_policy);
let mut permitted_subtrees = initial_permitted_subtrees;
let mut excluded_subtrees = initial_excluded_subtrees;
let mut explicit_policy = if initial_explicit_policy {
0
} else {
certificates.len() + 1
};
let mut inhibit_any_policy = if initial_any_policy_inhibit {
0
} else {
certificates.len() + 1
};
let mut policy_mapping = if initial_policy_mapping_inhibit {
0
} else {
certificates.len() + 1
};
let mut working_public_key = trusted_public_key;
// working_public_key_parameters, except it is compeltely unnecessary
let mut working_issuer_name = trusted_issuer_name;
let mut max_path_length = certificates.len();
for cert_index in 0..certificates.len() {
let current_certificate = &certificates[cert_index];
current_certificate
.validate_signature_with_trusted(&working_public_key)
.map_err(|_| TlsError::SignatureValidationError)?;
current_certificate.tbs_certificate.validity
.is_valid(&current_time)?;
// Certificate Revocation List is not implemented
// This is a certificate-in-certificate scenario
if current_certificate.tbs_certificate.issuer != working_issuer_name {
return Err(TlsError::CertificateIssuerMismatch);
}
if current_certificate.tbs_certificate.issuer != current_certificate.tbs_certificate.subject
&& (cert_index + 1) != certificates.len() {
if permitted_subtrees.len() != 0 {
// Find the SAN extension
for extension in current_certificate.tbs_certificate
.extensions.extensions.iter() {
if let ExtensionValue::SubjectAlternativeName {
general_names
} = &extension.extension_value {
// For each alt. names in SAN, it is within one of the
// permitted_subtrees for that name type
// TODO: Do more than find exact match
for general_name in general_names.iter() {
permitted_subtrees.iter().find(
|&permitted_name| permitted_name == general_name
).ok_or(
TlsError::CertificateSubjectNotPermitted
)?;
}
}
}
}
if excluded_subtrees.len() != 0 {
// Find the SAN extension
for extension in current_certificate.tbs_certificate
.extensions.extensions.iter() {
if let ExtensionValue::SubjectAlternativeName {
general_names
} = &extension.extension_value {
// For each alt. names in SAN, it is NOT within one of the
// excluded_subtrees for that name type
// TODO: Do more than find exact match
for general_name in general_names.iter() {
excluded_subtrees.iter().find(
|&excluded_name| excluded_name == general_name
).map_or(
Ok(()),
|_| Err(TlsError::CertificateSubjectExcluded)
)?;
}
}
}
}
}
// Certificate policy, find a new set of leaves if exist
let mut new_valid_policy_leaves: Vec<ValidPolicyNode> = Vec::new();
let mut policy_info = None;
for extension in current_certificate.tbs_certificate.extensions.extensions.iter() {
if let ExtensionValue::CertificatePolicies { info } = &extension.extension_value {
policy_info.replace(info);
break;
}
}
if policy_info.is_some() {
let mut possible_any_policy = None;
// For each policy P that is not anyPolicy
for policy in policy_info.unwrap().iter() {
if policy.id == crate::oid::ANY_POLICY {
possible_any_policy.replace(policy);
continue;
}
let mut policy_not_matched = true;
let mut any_policy_found = false;
// For each node S at depth i-1, if S expects P,
// create a child with (P-OID, P-Q, {P-OID})
for policy_parent in valid_policy_tree.iter() {
if policy_parent.expected_policy_set
.iter()
.find(|&&expected_policy| expected_policy == policy.id)
.is_some() {
let mut new_node = ValidPolicyNode {
valid_policy: policy.id,
qualifier_set: policy.qualifier,
expected_policy_set: Vec::new()
};
new_node.expected_policy_set.push(policy.id);
new_valid_policy_leaves.push(new_node);
policy_not_matched = false;
}
if policy_parent.valid_policy == crate::oid::ANY_POLICY {
any_policy_found = true;
}
}
// If a match is not found for this policy,
// while an `anyPolicy' parent exists,
// Add policy P with (P-OID, P-Q, {P-OID})
// There is no need to add more than once
// Only `horizontal` leaf search will be performed,
// will only duplicate branch
if !policy_not_matched && any_policy_found {
let mut new_node = ValidPolicyNode {
valid_policy: policy.id,
qualifier_set: policy.qualifier,
expected_policy_set: Vec::new()
};
new_node.expected_policy_set.push(policy.id);
new_valid_policy_leaves.push(new_node);
}
}
// If cert has anyPolicy, and either (inhibit_anyPolicy > 0 OR i < n)
// AND certificate is self-issued, then forward all yet-to-be-copied
// policies in depth i-1 to leaves with qualifier as AP-Q
if possible_any_policy.is_some()
&& inhibit_any_policy > 0
&& cert_index + 1 < certificates.len()
{
for policy_parent in valid_policy_tree.iter() {
for expected_policy in policy_parent.expected_policy_set.iter() {
// If any expected policy cannot be found among the new leaves
// it needs to be added into the valid policies
if new_valid_policy_leaves.iter().find(
|&leaf_policy| &leaf_policy.valid_policy == expected_policy
).is_none() {
let mut new_node = ValidPolicyNode {
valid_policy: expected_policy,
qualifier_set: possible_any_policy.unwrap().qualifier,
expected_policy_set: Vec::new()
};
new_node.expected_policy_set.push(expected_policy);
new_valid_policy_leaves.push(new_node);
}
}
}
}
}
// Otherwise, do nothing.
// Empty vector can represent NULL.
// Replace old `valid_policy_tree` with new leaves
// This automatically does the following things:
// (d) prune childless branches, and
// (e) set the entire tree to NULL, if there are no cert policies.
valid_policy_tree = new_valid_policy_leaves;
// (f) Verify that either:
// -`explicit_policy` is greater than 0, OR
// -`valid_policy_tree` is not NULL
if explicit_policy == 0 && valid_policy_tree.len() == 0 {
return Err(TlsError::CertificatePolicyError);
}
// Prepare for the next certificate (towards end cert)
// Policy mapping is not handled
if cert_index + 1 == certificates.len() {
return wrap_up_verification(
current_certificate,
explicit_policy,
&valid_policy_tree
);
}
// (c, d, e, f) Re-assign `working_issuer_name` and `working_public_key`
// working_public_key already includes the algorithm of the key
working_issuer_name = current_certificate.tbs_certificate.subject.clone();
working_public_key = current_certificate.get_cert_public_key()
.map_err(|_| TlsError::SignatureValidationError)?;
// Only default pre-set signature algorithms are used.
// Parameter will never be relavent
// Counter updates, (l) verification for non-self-issued certs
// (h) If certificate is not self-issued, decrement all counters if non-zero
if current_certificate.tbs_certificate.issuer != current_certificate.tbs_certificate.subject {
explicit_policy -= 1;
policy_mapping -= 1;
inhibit_any_policy -= 1;
if max_path_length == 0 {
return Err(TlsError::CertificateVersionError);
} else {
max_path_length -= 1;
}
}
// Ensure that the certificate is v3
if current_certificate.tbs_certificate.version != Version::v3 {
return Err(TlsError::CertificateVersionError);
}
// (g) Permitted/Excluded subtrees operations
for extension in current_certificate.tbs_certificate.extensions.extensions.iter() {
if let ExtensionValue::NameConstraints {
permitted_subtrees: certificate_permitted_subtrees,
excluded_subtrees: certificate_excluded_subtrees
} = &extension.extension_value {
if certificate_permitted_subtrees.len() != 0 {
get_subtree_intersection(
&mut permitted_subtrees,
certificate_permitted_subtrees
);
}
if certificate_excluded_subtrees.len() != 0 {
get_subtree_union(
&mut excluded_subtrees,
certificate_excluded_subtrees
);
}
}
// (i) If policyConstraint extension is found, modify
// - explicit_policy, and/or
// - policy_mapping
if let ExtensionValue::PolicyConstraints {
require_explicit_policy,
inhibit_policy_mapping,
} = &extension.extension_value {
if require_explicit_policy.is_some() {
if usize::from(require_explicit_policy.unwrap()) < explicit_policy {
explicit_policy = require_explicit_policy.unwrap().into();
}
}
if inhibit_policy_mapping.is_some() {
if usize::from(inhibit_policy_mapping.unwrap()) < policy_mapping {
policy_mapping = inhibit_policy_mapping.unwrap().into();
}
}
}
// (j) Reduce inhibit_anyPolicy to that stated in the certificate
if let ExtensionValue::InhibitAnyPolicy {
skip_certs
} = &extension.extension_value {
if usize::from(*skip_certs) < inhibit_any_policy {
inhibit_any_policy = (*skip_certs).into();
}
}
// (m) Verify that there is a BasicConstraint extension,
// with cA set to true
if let ExtensionValue::BasicConstraints {
is_ca,
path_len_constraint
} = &extension.extension_value {
if !is_ca {
return Err(TlsError::CertificateVersionError);
}
if path_len_constraint.is_some() {
if path_len_constraint.unwrap() < max_path_length as u8 {
max_path_length = path_len_constraint.unwrap().into();
}
}
}
// (n) If key usage extension is found, keyCertSignbit must be set
if let ExtensionValue::KeyUsage {
usage
} = &extension.extension_value {
if usage & 0x0020 == 0 {
return Err(TlsError::CertificateVersionError);
}
}
}
}
Ok(())
}
fn wrap_up_verification(
end_cert: &Certificate,
mut explicit_policy: usize,
valid_policy_tree: &Vec<ValidPolicyNode>
) -> Result<(), TlsError> {
// (a) Decrement explicit_policy
if explicit_policy != 0 {
explicit_policy -= 1;
}
for extension in end_cert.tbs_certificate.extensions.extensions.iter() {
// (b) If there is policy constraint extension, and
// require_explicit_policy is 0, set explicit_policy_state to be 0
if let ExtensionValue::PolicyConstraints {
require_explicit_policy,
inhibit_policy_mapping
} = &extension.extension_value {
if require_explicit_policy.is_some() {
if require_explicit_policy.unwrap() == 0 {
explicit_policy = 0;
}
}
}
}
// (c) Instantiate cert key again, but only for returning to other procedure
// Getting it directly from certificate when needs to
// (d, e, f) Will not work with customized algorithm
// Only TLS signature algorithm will be supported
// (e) `initial_policy_set` is hardwired to any-policy
// The intersection is the entire valid_policy_tree (case II, section 6.1.4)
if explicit_policy > 0 || valid_policy_tree.len() != 0 {
Ok(())
} else {
Err(TlsError::CertificatePolicyError)
}
}
// Mutate state_subtree to get the intersection
fn get_subtree_intersection<'a>(
state_subtree: &mut Vec<GeneralName<'a>>,
cert_subtree: &Vec<GeneralName<'a>>
) {
// 1. Determine the variants that need to be preserved (i.e. body-count)
let mut has_self_uri_tree = false;
let mut has_other_uri_tree = false;
let mut has_self_rfc_822_name_tree = false;
let mut has_other_rfc_822_name_tree = false;
let mut has_self_dns_name_tree = false;
let mut has_other_dns_name_tree = false;
let mut has_self_ip_address_tree = false;
let mut has_other_ip_address_tree = false;
for general_name in state_subtree.iter() {
match general_name {
GeneralName::URI(..) => has_self_uri_tree = true,
GeneralName::RFC822Name(..) => has_self_rfc_822_name_tree = true,
GeneralName::DNSName(..) => has_self_dns_name_tree = true,
GeneralName::IPAddress(..) => has_self_ip_address_tree = true,
// Other general_name variants should not appear in this subtree
_ => {},
}
}
for general_name in cert_subtree.iter() {
match general_name {
GeneralName::URI(..) => has_other_uri_tree = true,
GeneralName::RFC822Name(..) => has_other_rfc_822_name_tree = true,
GeneralName::DNSName(..) => has_other_dns_name_tree = true,
GeneralName::IPAddress(..) => has_other_ip_address_tree = true,
// Other general_name variants should not appear in this subtree
_ => {},
}
}
// 2. Preserve subtrees that fit into the variants
let mut preserved_subtrees: Vec<GeneralName> = Vec::new();
for general_name in state_subtree.iter() {
match general_name {
GeneralName::URI(..) => {
if !has_other_uri_tree {
preserved_subtrees.push(*general_name);
}
},
GeneralName::RFC822Name(..) => {
if !has_other_rfc_822_name_tree {
preserved_subtrees.push(*general_name);
}
}
GeneralName::DNSName(..) => {
if !has_other_dns_name_tree {
preserved_subtrees.push(*general_name);
}
}
GeneralName::IPAddress(..) => {
if !has_other_ip_address_tree {
preserved_subtrees.push(*general_name);
}
}
// Other general_name variants should not appear in this subtree
_ => {},
}
}
for general_name in cert_subtree.iter() {
match general_name {
GeneralName::URI(..) => {
if !has_self_uri_tree {
preserved_subtrees.push(*general_name);
}
},
GeneralName::RFC822Name(..) => {
if !has_self_rfc_822_name_tree {
preserved_subtrees.push(*general_name);
}
}
GeneralName::DNSName(..) => {
if !has_self_dns_name_tree {
preserved_subtrees.push(*general_name);
}
}
GeneralName::IPAddress(..) => {
if !has_self_ip_address_tree {
preserved_subtrees.push(*general_name);
}
}
// Other general_name variants should not appear in this subtree
_ => {},
}
}
// 3. Perform intersection operation among 2 sets indirectly
//
// First, if the certificate does not specify any URI restrictions,
// leave the stored URI be.
//
// Assume all elements smong self_xxxx_tree and other_xxxx_tree are homogeneous.
//
// For each element in self_xxxx_tree, find intersection with other_xxxx_tree
// Take union operation at the result of each operations at the end
// i.e. (S1 U S2 U ... U Sk) n O = (S1 n O) U (S2 n O) U ... U (Sk n O)
// as stated in distributive law
//
// The with the same argument, but reversing the self_tree and other_tree,
// For each element in other_xxx_tree, find intersection with that elemenet from self
// Take union operation of the result of each operations at the end
// i.e. Sx n (O1 U O2 U ... Oj) = (Sx n O1) U (Sx n O2) U ... U (Sx n Oj)
// as stated in distributive law
//
// To further simplify, recognize that the brackets of (Sx n O) in the first statement
// encapsulates a series of union operators, while the bracks perform union operations
// among themselves as well.
// Therefore, the order of operations does not matter, stated in the associative law.
// i.e. S n O = U_(x, y) {Sx n Oy}
//
// Now consider all the variants, the processed subtree shall be a union of all variants
// where the variants are computed as a union of homogeneous intersections.
// By Identity law, if all heterogeneous intersections returns NULL,
// union of homogeneous intersections are equivalent to that of heterogeneous intersections.
//
// However, an empty set is not what always the correct solution. Here are exceptions:
//
// 1. If other_tree does not contain a variant that exists in self_tree,
// that variant in self_tree shall be left untouched.
//
// 2. Reverse of (1), self_tree does not have definitions on some variant,
// while other_tree has.
// Consider this method is for permitted_subtree operation, no definition means no restriction
// other_tree would like to impose tighter restriction, so every items of such variant
// from `other_tree` should be preserved.
//
// Both can be fixed by saving all `guaranteed` permitted subtrees before this process
for self_name in state_subtree.iter() {
for other_name in cert_subtree.iter() {
// Make use of subset method, note that all general names are hierarchical
if self_name.is_subset_of(other_name) {
preserved_subtrees.push(*self_name)
} else if other_name.is_subset_of(self_name) {
preserved_subtrees.push(*other_name)
}
// If neither are subset of the other, the intersection shall be none
// Should both names be homogeneous, it should imply an all-blocking name
match (self_name, other_name) {
(GeneralName::URI(self_uri), GeneralName::URI(other_uri)) => {
preserved_subtrees.push(
GeneralName::URI(&[])
)
},
(GeneralName::RFC822Name(self_mail), GeneralName::RFC822Name(other_mail)) => {
preserved_subtrees.push(
GeneralName::RFC822Name(&[])
)
},
(GeneralName::DNSName(self_dns), GeneralName::DNSName(other_dns)) => {
preserved_subtrees.push(
GeneralName::DNSName(&[])
)
},
(GeneralName::IPAddress(self_ip), GeneralName::IPAddress(other_ip)) => {
preserved_subtrees.push(
GeneralName::IPAddress(&[])
)
},
// Heterogeneous general name variants
_ => {}
}
}
}
// 4. Perform union operation
// Again recall that general names are hierarchical
// If two general names are not disjoint, one must be other's subset
// Therefore, pruning subsets is sufficient to determine the union.
// Put the result into state_subtree, as this shall be the output
//
// Note: Technically union operation can be a simple no-op
// But this is performed for the sake of memory space
state_subtree.clear();
prune_subset(state_subtree, &mut preserved_subtrees);
}
fn prune_subset<'a>(subtree_out: &mut Vec<GeneralName<'a>>, subtree_in: &mut Vec<GeneralName<'a>>) {
'outer: for i in 0..subtree_in.len() {
for j in 0..subtree_in.len() {
// A few cases to consider:
// If subtree_i is a strict_subset of subtree_j,
// then obviously i needs to be ejected
// However, if Si and Sj are equivalent, then only 1 needs to be ejected
// the following implementation will eject the one with lower index
if i != j {
if subtree_in[i] == subtree_in[j] {
if i < j {
continue 'outer;
}
} else if subtree_in[i].is_subset_of(&subtree_in[j]) {
continue 'outer;
}
}
}
subtree_out.push(subtree_in[i])
}
}
// Union operation among 2 subtrees sets, output through state_subtree
pub fn get_subtree_union<'a>(
state_subtree: &mut Vec<GeneralName<'a>>,
other_subtree: &Vec<GeneralName<'a>>
) {
// Join the 2 lists together, and then prune all subsets
let mut merged_subtrees: Vec<GeneralName> = Vec::new();
merged_subtrees.extend_from_slice(state_subtree);
merged_subtrees.extend_from_slice(other_subtree);
state_subtree.clear();
prune_subset(state_subtree, &mut merged_subtrees);
}

View File

@ -22,4 +22,10 @@ pub enum Error {
DecryptionError,
CapacityError,
SignatureValidationError,
TimeValidityError,
CertificateIssuerMismatch,
CertificateSubjectNotPermitted,
CertificateSubjectExcluded,
CertificatePolicyError,
CertificateVersionError,
}

View File

@ -120,7 +120,7 @@ const CA_SIGNED_CERT: [u8; 0x0356] =
"308203523082023a02146048517ee55aabd1e8f2bd7db1d91e679708e644300d06092a864886f70d01010b05003067310b30090603550406130255533113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c74643120301e06035504030c176578616d706c65732e63612e756c666865696d2e6e6574301e170d3230313130363034323035305a170d3230313230363034323035305a3064310b30090603550406130255533113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c7464311d301b06035504030c146578616d706c65732e756c666865696d2e6e657430820122300d06092a864886f70d01010105000382010f003082010a0282010100b2940671bfe7ace7416ba9d34018c229588e9d4eed8bd6623e44ab1239e8f1f0de9050b2f485a98e63f5b483330fb0b5abaeb33d11889033b0b684bf34696d28206bb361782c4b106a8d47874cbbdf971b5ab887bca508bccf250a1a811cee078464638e441941347d4c8885ac9b59d9fc9636276912b04d9e3ab29bd8ad319572ae54f0b6145c4d675a78607dcc4793a4d432f1c2a41ea29dd4f7262b6fe472dfaea51aca992b4624e73fa9901fa364fc5b721052ef3187e659d58d2706770d365380a7ebab6caac5b23271c01531fdf95368ee48af5383035f249be7c18f50ce9e52877558efe4b2e29f61328396e2a3b5e71309ad13d93d6ba3d5c3eb2b650203010001300d06092a864886f70d01010b0500038201010063c9ab0f5d2e164513e8e74b656ae4f48dd004c3ead9f1026b7741cbf02bb0efcf19e0fbf8a788dae059a2393167f016bafc0e3efd5c5b4c43079b6506eb67f17f44f9591503c7d1fdb77bf631894817393ea82610ad5106d23ec6bf1a6d96d749f05c0136cd71256617a51fe862529aee4a37d5f456dc7da8b220ff10ede4e87bc63e4589b3f81133a7f82ab900419e8a2d802d59e99cfbbd268702efd17616168b45b5211da0e644c29dcb92dbbf32b43586bbab05deb0261771605c52836363bd28ff9853d44436349f5ba11f2640bc9c42688e0d5eb6cac9f3f5e5f98652fa4f4ba52604371ec45f09d678e31d463285a4b3734f587f35a339920544f476"
);
const SELF_SIGNED_WITH_SAN: [u8; 0x046C] =
const SELF_SIGNED_WITH_SAN: [u8; 0x048A] =
hex_literal::hex!(
"3082046830820350a00302010202145525dc68e0e749158bf5bee8fe12c71675127510300d06092a864886f70d01010b05003073310b3009060355040613025553310b300906035504080c0256413114301206035504070c0b416e6f746865724369747931123010060355040a0c094d79436f6d70616e7931133011060355040b0c0a4d794469766973696f6e3118301606035504030c0f7777772e636f6d70616e792e636f6d301e170d3230313130393035353034385a170d3231313130393035353034385a3073310b3009060355040613025553310b300906035504080c0256413114301206035504070c0b416e6f746865724369747931123010060355040a0c094d79436f6d70616e7931133011060355040b0c0a4d794469766973696f6e3118301606035504030c0f7777772e636f6d70616e792e636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100a03fec3ee07c6dce516520ab9166b7b5d95c13196328cb443e8419d748889ebc99a22b8032240a816b585e2affaea7383e4b655aacc06ee67814693fc440f6a27939e793a85280334a4662da460802123d191dccfe029997cc7f94f7ff9aef7a2d662f01fb9cd153e14c6b21ab28b209f09082d635b705bca4553efaeaddc21d7fc73366130a54270175c608cff2f2336fbf13896ebfc44016259032c81f2499f19ce14da840f25b0339fae72926b231ec6f14b7ff66716be0d3471f6485b6657270834fc4058e93625d4df95eb3a95c2eec48c3f060cdd740e373fc240e3c76d5c3c3f11cb53dab471d8af6b46e0294b3c7a75c82edcbab24af0448237310b90203010001a381f33081f0300b0603551d0f04040302043030130603551d25040c300a06082b0601050507030130760603551d11046f306d820f7777772e636f6d70616e792e6e6574820b636f6d70616e792e636f6d820b636f6d70616e792e6e6574810b626f6240636f6d70616e79810d616c69636540636f6d70616e79a01e06032a0304a0170c15736f6d65206f74686572206964656e7469666965728704c0a801c830430603551d1e043c303aa01c300e820c2e6578616d706c652e636f6d300a8708c0a80001ffffff00a11a300a8708c0aa0001ffffff00300c820a2e6c6f63616c686f7374300f0603551d2404083006800103810105300d06092a864886f70d01010b0500038201010079721d5b3b0f17edc425ac5f6faa5d085760eba5ebd0bc2a5010a0893e46c68656fecc0b66c7216fb093a07aaad88819011b9daa0407aef06778c7c04dbc2f5dd20d3156cccd3a36298b26d8d548d2ae4564b55f1e00fe49d6a7c53f05eecd0b63d7716d51a59c71141f9b223cbf79c57d46a7a240d2bc851f38c7da45c9ec7ad99381605247e46309541ea89fe9e175f8fd4c296b7ea667dafb8d4310e577c79be50a1d3b48c4d012a01aaf605816f37f5a00f1cb86dee776b31f8a444a79f48313502b23d001d1f3a641f9ec05960180cfa0440b8002afc3b49c3c1779adfd7f9ecef38fb14c2b3902323314990928f764c70a579e02453fec03bc78f98914"
"308204863082036ea003020102021447e58ecab88d894c58ec746381e6d039c04e3a93300d06092a864886f70d01010b05003073310b3009060355040613025553310b300906035504080c0256413114301206035504070c0b416e6f746865724369747931123010060355040a0c094d79436f6d70616e7931133011060355040b0c0a4d794469766973696f6e3118301606035504030c0f7777772e636f6d70616e792e636f6d301e170d3230313131303032313132385a170d3231313131303032313132385a3073310b3009060355040613025553310b300906035504080c0256413114301206035504070c0b416e6f746865724369747931123010060355040a0c094d79436f6d70616e7931133011060355040b0c0a4d794469766973696f6e3118301606035504030c0f7777772e636f6d70616e792e636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100bacdd00cf364e116a6b7cd23df3ccc31f6d6d2e6e82da833d50f7bd850b1e482acd1e9ecc9e0ea2c36925576fff44bdefbbb73f38c8d7db67ac7210801e5580329504ebc6982e5c95a758a36a1d0ff891478ab2f7392c1c6b4657d08645ff97ef23b0870de511d3532599eb250eac82bae22d6de23f2a5a48733d3a045fe0c5f942b5f79dbb08117bf1a814f00489e4118f30f8e37e66c61c5c4398cdc9f77ec39de78bf0b4c677cb9e485b868ce03c32c41f78849b82949df8231290172618234de85ec02990206fec3ed74b6b18e9d0acd6b4bed1c4f52749574f7cd9b8ad0db14be78a99eb8a894adecdbd0922ab953e3e447346022a4485ffd121efdfe1d0203010001a38201103082010c300b0603551d0f04040302043030130603551d25040c300a06082b0601050507030130760603551d11046f306d820f7777772e636f6d70616e792e6e6574820b636f6d70616e792e636f6d820b636f6d70616e792e6e6574810b626f6240636f6d70616e79810d616c69636540636f6d70616e79a01e06032a0304a0170c15736f6d65206f74686572206964656e7469666965728704c0a801c830430603551d1e043c303aa01c300e820c2e6578616d706c652e636f6d300a8708c0a80001ffffff00a11a300a8708c0aa0001ffffff00300c820a2e6c6f63616c686f7374300f0603551d2404083006800103810105301a0603551d200413301130060604551d2000300706052b05071314300d06092a864886f70d01010b050003820101008e6891f7bf506cb9bbc2d8f679d2d510b0b3121ee4662cbe90ddd1b1fa4f8a5b9cca49877fc64b9c08a2dbbb4563e3e92be62ce79088b2a1b382724f1b479efaba1749696461893335f56e9ad7b89359997af85a20425250be0559b2daf1179b61b65284f39da4386377a035038af179b93508925c227d4f205538c1dedfc768a98cd243196a9476ac79bb91c16e827ff84376520e89b09a236037be4f21b0262b151d156638ccfafac7cf383c5f20213cdd29d0f95329a4a2783328986fa2e70b501289c263e0bdc42cb439412b8be2601b6f1fe8c3e15f11230760d36ff008ccb42d8b10c5c92db35ae2a6a8dfcf461233cefbc30fc2d709608452744f1ce7"
);

View File

@ -82,4 +82,7 @@ pub const ID_KP_CLIENT_AUTH: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3,
pub const ID_KP_CODE_SIGNING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 3]; // 1.3.6.1.5.5.7.3.3
pub const ID_KP_EMAIL_PROTECTION: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 4]; // 1.3.6.1.5.5.7.3.4
pub const ID_KP_TIME_STAMPING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 8]; // 1.3.6.1.5.5.7.3.8
pub const ID_KP_OCSP_SIGNING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 9];
pub const ID_KP_OCSP_SIGNING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 9];
// anyPolicy OID
pub const ANY_POLICY: &'static [u8] = &[85, 29, 32, 0];

View File

@ -18,20 +18,21 @@ use byteorder::{ByteOrder, NetworkEndian};
use crate::tls_packet::*;
use crate::certificate::{
Certificate as Asn1DerCertificate,
Version as Asn1DerVersion,
AlgorithmIdentifier as Asn1DerAlgId,
Time as Asn1DerTime,
Validity as Asn1DerValidity,
SubjectPublicKeyInfo as Asn1DerSubjectPublicKeyInfo,
Extensions as Asn1DerExtensions,
Extension as Asn1DerExtension,
ExtensionValue as Asn1DerExtensionValue,
PolicyInformation as Asn1DerPolicyInformation,
TBSCertificate as Asn1DerTBSCertificate,
Name as Asn1DerName,
AttributeTypeAndValue as Asn1DerAttribute,
GeneralName as Asn1DerGeneralName,
Certificate as Asn1DerCertificate,
Version as Asn1DerVersion,
AlgorithmIdentifier as Asn1DerAlgId,
Time as Asn1DerTime,
Validity as Asn1DerValidity,
SubjectPublicKeyInfo as Asn1DerSubjectPublicKeyInfo,
Extensions as Asn1DerExtensions,
Extension as Asn1DerExtension,
ExtensionValue as Asn1DerExtensionValue,
PolicyInformation as Asn1DerPolicyInformation,
TBSCertificate as Asn1DerTBSCertificate,
Name as Asn1DerName,
AttributeTypeAndValue as Asn1DerAttribute,
GeneralName as Asn1DerGeneralName,
RelativeDistinguishedName as Asn1DerRDN,
};
use crate::oid;
@ -812,10 +813,10 @@ pub fn parse_asn1_der_oid(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
// Parser for Name, applicable to issuer and subject field of TBS cert.
pub fn parse_asn1_der_name(bytes: &[u8]) -> IResult<&[u8], Asn1DerName> {
let (rest, mut rdn_sequence) = parse_asn1_der_sequence(bytes)?;
let mut attributes_vec: Vec<Asn1DerAttribute> = Vec::new();
let mut attributes_vec: Vec<Asn1DerRDN> = Vec::new();
while rdn_sequence.len() != 0 {
let (rem, attribute) = parse_asn1_der_attribute_type_and_value(
let (rem, attribute) = parse_asn1_der_relative_distinguished_name(
rdn_sequence
)?;
rdn_sequence = rem;
@ -830,19 +831,37 @@ pub fn parse_asn1_der_name(bytes: &[u8]) -> IResult<&[u8], Asn1DerName> {
))
}
// Parser for Relative Distinguished Name (RDN)
pub fn parse_asn1_der_relative_distinguished_name(bytes: &[u8]) -> IResult<&[u8], Asn1DerRDN> {
let (rest, mut attribute_set) = parse_asn1_der_set(bytes)?;
let mut attributes_vec: Vec<Asn1DerAttribute> = Vec::new();
while attribute_set.len() != 0 {
let (rem, attribute) = parse_asn1_der_attribute_type_and_value(
attribute_set
)?;
attribute_set = rem;
attributes_vec.push(attribute);
}
Ok((
rest,
Asn1DerRDN {
type_and_attributes: attributes_vec
}
))
}
// Parser for AttributeTypeAndValue struct, typically wrapped inside Name struct
pub fn parse_asn1_der_attribute_type_and_value(bytes: &[u8]) -> IResult<&[u8], Asn1DerAttribute> {
let (rest, set) = parse_asn1_der_set(bytes)?;
let (_, attribute) = complete(
parse_asn1_der_sequence
)(set)?;
let (rest, set) = parse_asn1_der_sequence(bytes)?;
let (_, (oid, (tag_val, _, value))) = complete(
tuple((
parse_asn1_der_oid,
parse_asn1_der_object
))
)(attribute)?;
)(set)?;
// Verify that tag_val is either "PrintableString or UTF8String"
if tag_val != 0x13 && tag_val != 0x0C {
@ -1419,12 +1438,7 @@ pub fn parse_asn1_der_policy_information(bytes: &[u8]) -> IResult<&[u8], Asn1Der
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
}
let (_, (oid, (_, _, qualifier))) = complete(
tuple((
parse_asn1_der_oid,
parse_asn1_der_object
))
)(value)?;
let (qualifier, oid) = parse_asn1_der_oid(value)?;
Ok((
rest,

View File

@ -595,7 +595,7 @@ pub(crate) struct Cookie {
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
#[repr(u16)]
pub(crate) enum SignatureScheme {
pub enum SignatureScheme {
/* RSASSA-PKCS1-v1_5 algorithms */
rsa_pkcs1_sha256 = 0x0401,
rsa_pkcs1_sha384 = 0x0501,