Skip to content
Merged
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
1 change: 1 addition & 0 deletions x509/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,7 @@ mod general_name;
pub mod pkix_extensions;
pub mod pkix_oids;
mod time;
pub mod trust_anchor_format;
mod validity;

pub use crate::{
Expand Down
49 changes: 3 additions & 46 deletions x509/src/pkix_extensions.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -326,60 +326,17 @@ pub struct PolicyMapping {
/// ```
///
/// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct NameConstraints<'a> {
/// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
//#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub permitted_subtrees: Option<GeneralSubtrees<'a>>,

/// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub excluded_subtrees: Option<GeneralSubtrees<'a>>,
}

const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0);
const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1);

impl<'a> ::der::Decodable<'a> for NameConstraints<'a> {
fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
decoder.sequence(|decoder| {
let permitted_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)?
.map(|cs| cs.value);
let excluded_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
Ok(Self {
permitted_subtrees,
excluded_subtrees,
})
})
}
}

impl<'a> ::der::Sequence<'a> for NameConstraints<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
f(&[
&self
.permitted_subtrees
.as_ref()
.map(|elem| ContextSpecific {
tag_number: PERMITTED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
&self.excluded_subtrees.as_ref().map(|elem| ContextSpecific {
tag_number: EXCLUDED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
])
}
}

/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension.
///
/// ```text
Expand Down
268 changes: 268 additions & 0 deletions x509/src/trust_anchor_format.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
//! Trust anchor-related structures as defined in RFC 5914

use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints};
use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String};
use der::{
DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Sequence, Tag,
TagMode, TagNumber,
};
use spki::SubjectPublicKeyInfo;
use x501::name::Name;

/// TrustAnchorInfo ::= SEQUENCE {
/// version TrustAnchorInfoVersion DEFAULT v1,
/// pubKey SubjectPublicKeyInfo,
/// keyId KeyIdentifier,
/// taTitle TrustAnchorTitle OPTIONAL,
/// certPath CertPathControls OPTIONAL,
/// exts \[1\] EXPLICIT Extensions OPTIONAL,
/// taTitleLangTag \[2\] UTF8String OPTIONAL }
///
/// TrustAnchorInfoVersion ::= INTEGER { v1(1) }
///
/// TrustAnchorTitle ::= UTF8String (SIZE (1..64))
#[derive(Clone, Eq, PartialEq)]
pub struct TrustAnchorInfo<'a> {
/// version TrustAnchorInfoVersion DEFAULT v1,
pub version: Option<u8>,

/// pubKey SubjectPublicKeyInfo,
pub pub_key: SubjectPublicKeyInfo<'a>,

/// keyId KeyIdentifier,
pub key_id: OctetString<'a>,

/// taTitle TrustAnchorTitle OPTIONAL,
pub ta_title: Option<Utf8String<'a>>,

/// certPath CertPathControls OPTIONAL,
pub cert_path: Option<CertPathControls<'a>>,

/// exts \[1\] EXPLICIT Extensions OPTIONAL,
pub extensions: Option<Extensions<'a>>,

/// taTitleLangTag \[2\] UTF8String OPTIONAL }
pub ta_title_lang_tag: Option<Utf8String<'a>>,
}

// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> {
// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let version = match decoder.decode()? {
Some(v) => Some(v),
_ => Some(1),
};

let pub_key = decoder.decode()?;
let key_id = decoder.decode()?;
let ta_title = decoder.decode()?;
let cert_path = decoder.decode()?;
let extensions =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
let ta_title_lang_tag =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)?
.map(|cs| cs.value);
Ok(Self {
version,
pub_key,
key_id,
ta_title,
cert_path,
extensions,
ta_title_lang_tag,
})
}
}
Comment on lines +50 to +77

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this one use the proc macro now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you left a note here:

Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific

It should probably work now.


const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1);
const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0);
impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
#[allow(unused_imports)]
use core::convert::TryFrom;
f(&[
&::der::asn1::OptionalRef(if self.version == Some(1) {
None
} else {
Some(&self.version)
}),
&self.pub_key,
&self.key_id,
&self.ta_title,
&self.cert_path,
&self.extensions.as_ref().map(|exts| ContextSpecific {
tag_number: TAF_EXTENSIONS_TAG,
tag_mode: TagMode::Explicit,
value: exts.clone(),
}),
&self
.ta_title_lang_tag
.as_ref()
.map(|ta_title_lang_tag| ContextSpecific {
tag_number: TA_TITLE_LANG_TAG,
tag_mode: TagMode::Implicit,
value: *ta_title_lang_tag,
}),
])
}
}

impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?;
f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?;
f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?;
f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?;
f.write_fmt(format_args!(
"\tTA title language tag: {:?}\n",
self.ta_title_lang_tag
))?;
f.write_fmt(format_args!(
"\tCertificate path controls: {:?}\n",
self.cert_path
))?;
if let Some(exts) = self.extensions.as_ref() {
for (i, e) in exts.iter().enumerate() {
f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?;
}
} else {
f.write_fmt(format_args!("\tExtensions: None\n"))?;
}
Ok(())
}
}

/// CertPathControls ::= SEQUENCE {
/// taName Name,
/// certificate \[0\] Certificate OPTIONAL,
/// policySet \[1\] CertificatePolicies OPTIONAL,
/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
/// nameConstr \[3\] NameConstraints OPTIONAL,
/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct CertPathControls<'a> {
/// taName Name,
pub ta_name: Name<'a>,

/// certificate \[0\] Certificate OPTIONAL,
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub certificate: Option<Certificate<'a>>,

/// policySet \[1\] CertificatePolicies OPTIONAL,
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub policy_set: Option<CertificatePolicies<'a>>,

/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
pub policy_flags: Option<CertPolicyFlags<'a>>,

/// nameConstr \[3\] NameConstraints OPTIONAL,
#[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")]
pub name_constr: Option<NameConstraints<'a>>,

/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")]
pub path_len_constraint: Option<u32>,
}

/// CertPolicyFlags ::= BIT STRING {
/// inhibitPolicyMapping (0),
/// requireExplicitPolicy (1),
/// inhibitAnyPolicy (2) }
pub type CertPolicyFlags<'a> = BitString<'a>;

/// TrustAnchorChoice ::= CHOICE {
/// certificate Certificate,
/// tbsCert \[1\] EXPLICIT TBSCertificate,
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorChoice<'a> {
/// certificate Certificate,
Certificate(Certificate<'a>),
// Not supporting TBSCertificate option
// tbsCert \[1\] EXPLICIT TBSCertificate,
//TbsCertificate(TBSCertificate<'a>),
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
TaInfo(TrustAnchorInfo<'a>),
}

//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1);
const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2);

impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let t = decoder.peek_tag()?;
let o = t.octet();
// Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here
match o {
0x30 => {
let cert = decoder.decode()?;
Ok(TrustAnchorChoice::Certificate(cert))
}
// TODO - need DecodeValue on TBSCertificate to support this
// 0xA1 => {
// let on = decoder
// .context_specific::<TBSCertificate<'a>>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?;
// match on {
// Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)),
// _ => Err(ErrorKind::Failed.into()),
// }
// }
0xA2 => {
let on = decoder
.context_specific::<TrustAnchorInfo<'a>>(TAC_TA_INFO_TAG, TagMode::Explicit)?;
match on {
Some(on) => Ok(TrustAnchorChoice::TaInfo(on)),
_ => Err(ErrorKind::Failed.into()),
}
}
_ => Err(ErrorKind::TagUnknown { byte: o }.into()),
}
}
}

impl<'a> EncodeValue for TrustAnchorChoice<'a> {
fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> {
match self {
Self::Certificate(certificate) => certificate.encode(encoder),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encode(encoder),
Self::TaInfo(variant) => variant.encode(encoder),
}
}
fn value_len(&self) -> ::der::Result<::der::Length> {
match self {
Self::Certificate(certificate) => certificate.encoded_len(),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encoded_len(),
Self::TaInfo(variant) => variant.encoded_len(),
}
}
}

//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag
impl<'a> FixedTag for TrustAnchorChoice<'a> {
const TAG: Tag = ::der::Tag::ContextSpecific {
constructed: true,
number: TAC_TA_INFO_TAG,
};
}

// Not supporting these structures
// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice
//
// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1)
// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
// id-smime(16) id-ct(1) 34 }
Binary file addedx509/tests/examples/eca.der
Binary file not shown.
Binary file addedx509/tests/examples/eca_policies.ta
Binary file not shown.
Binary file addedx509/tests/examples/entrust.der
Binary file not shown.
Binary file addedx509/tests/examples/entrust_dnConstraint.ta
Binary file not shown.
Binary file addedx509/tests/examples/exostar.der
Binary file not shown.
Binary file addedx509/tests/examples/exostar_policyFlags.ta
Binary file not shown.
Binary file addedx509/tests/examples/raytheon.der
Binary file not shown.
Binary file not shown.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
add support for RFC5914 structs. by carl-wallace · Pull Request #356 · RustCrypto/formats · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions x509/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,7 @@ mod general_name;
pub mod pkix_extensions;
pub mod pkix_oids;
mod time;
pub mod trust_anchor_format;
mod validity;

pub use crate::{
Expand Down
49 changes: 3 additions & 46 deletions x509/src/pkix_extensions.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -326,60 +326,17 @@ pub struct PolicyMapping {
/// ```
///
/// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct NameConstraints<'a> {
/// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
//#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub permitted_subtrees: Option<GeneralSubtrees<'a>>,

/// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub excluded_subtrees: Option<GeneralSubtrees<'a>>,
}

const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0);
const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1);

impl<'a> ::der::Decodable<'a> for NameConstraints<'a> {
fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
decoder.sequence(|decoder| {
let permitted_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)?
.map(|cs| cs.value);
let excluded_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
Ok(Self {
permitted_subtrees,
excluded_subtrees,
})
})
}
}

impl<'a> ::der::Sequence<'a> for NameConstraints<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
f(&[
&self
.permitted_subtrees
.as_ref()
.map(|elem| ContextSpecific {
tag_number: PERMITTED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
&self.excluded_subtrees.as_ref().map(|elem| ContextSpecific {
tag_number: EXCLUDED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
])
}
}

/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension.
///
/// ```text
Expand Down
268 changes: 268 additions & 0 deletions x509/src/trust_anchor_format.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
//! Trust anchor-related structures as defined in RFC 5914

use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints};
use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String};
use der::{
DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Sequence, Tag,
TagMode, TagNumber,
};
use spki::SubjectPublicKeyInfo;
use x501::name::Name;

/// TrustAnchorInfo ::= SEQUENCE {
/// version TrustAnchorInfoVersion DEFAULT v1,
/// pubKey SubjectPublicKeyInfo,
/// keyId KeyIdentifier,
/// taTitle TrustAnchorTitle OPTIONAL,
/// certPath CertPathControls OPTIONAL,
/// exts \[1\] EXPLICIT Extensions OPTIONAL,
/// taTitleLangTag \[2\] UTF8String OPTIONAL }
///
/// TrustAnchorInfoVersion ::= INTEGER { v1(1) }
///
/// TrustAnchorTitle ::= UTF8String (SIZE (1..64))
#[derive(Clone, Eq, PartialEq)]
pub struct TrustAnchorInfo<'a> {
/// version TrustAnchorInfoVersion DEFAULT v1,
pub version: Option<u8>,

/// pubKey SubjectPublicKeyInfo,
pub pub_key: SubjectPublicKeyInfo<'a>,

/// keyId KeyIdentifier,
pub key_id: OctetString<'a>,

/// taTitle TrustAnchorTitle OPTIONAL,
pub ta_title: Option<Utf8String<'a>>,

/// certPath CertPathControls OPTIONAL,
pub cert_path: Option<CertPathControls<'a>>,

/// exts \[1\] EXPLICIT Extensions OPTIONAL,
pub extensions: Option<Extensions<'a>>,

/// taTitleLangTag \[2\] UTF8String OPTIONAL }
pub ta_title_lang_tag: Option<Utf8String<'a>>,
}

// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> {
// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let version = match decoder.decode()? {
Some(v) => Some(v),
_ => Some(1),
};

let pub_key = decoder.decode()?;
let key_id = decoder.decode()?;
let ta_title = decoder.decode()?;
let cert_path = decoder.decode()?;
let extensions =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
let ta_title_lang_tag =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)?
.map(|cs| cs.value);
Ok(Self {
version,
pub_key,
key_id,
ta_title,
cert_path,
extensions,
ta_title_lang_tag,
})
}
}
Comment on lines +50 to +77

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this one use the proc macro now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you left a note here:

Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific

It should probably work now.


const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1);
const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0);
impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
#[allow(unused_imports)]
use core::convert::TryFrom;
f(&[
&::der::asn1::OptionalRef(if self.version == Some(1) {
None
} else {
Some(&self.version)
}),
&self.pub_key,
&self.key_id,
&self.ta_title,
&self.cert_path,
&self.extensions.as_ref().map(|exts| ContextSpecific {
tag_number: TAF_EXTENSIONS_TAG,
tag_mode: TagMode::Explicit,
value: exts.clone(),
}),
&self
.ta_title_lang_tag
.as_ref()
.map(|ta_title_lang_tag| ContextSpecific {
tag_number: TA_TITLE_LANG_TAG,
tag_mode: TagMode::Implicit,
value: *ta_title_lang_tag,
}),
])
}
}

impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?;
f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?;
f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?;
f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?;
f.write_fmt(format_args!(
"\tTA title language tag: {:?}\n",
self.ta_title_lang_tag
))?;
f.write_fmt(format_args!(
"\tCertificate path controls: {:?}\n",
self.cert_path
))?;
if let Some(exts) = self.extensions.as_ref() {
for (i, e) in exts.iter().enumerate() {
f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?;
}
} else {
f.write_fmt(format_args!("\tExtensions: None\n"))?;
}
Ok(())
}
}

/// CertPathControls ::= SEQUENCE {
/// taName Name,
/// certificate \[0\] Certificate OPTIONAL,
/// policySet \[1\] CertificatePolicies OPTIONAL,
/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
/// nameConstr \[3\] NameConstraints OPTIONAL,
/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct CertPathControls<'a> {
/// taName Name,
pub ta_name: Name<'a>,

/// certificate \[0\] Certificate OPTIONAL,
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub certificate: Option<Certificate<'a>>,

/// policySet \[1\] CertificatePolicies OPTIONAL,
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub policy_set: Option<CertificatePolicies<'a>>,

/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
pub policy_flags: Option<CertPolicyFlags<'a>>,

/// nameConstr \[3\] NameConstraints OPTIONAL,
#[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")]
pub name_constr: Option<NameConstraints<'a>>,

/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")]
pub path_len_constraint: Option<u32>,
}

/// CertPolicyFlags ::= BIT STRING {
/// inhibitPolicyMapping (0),
/// requireExplicitPolicy (1),
/// inhibitAnyPolicy (2) }
pub type CertPolicyFlags<'a> = BitString<'a>;

/// TrustAnchorChoice ::= CHOICE {
/// certificate Certificate,
/// tbsCert \[1\] EXPLICIT TBSCertificate,
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorChoice<'a> {
/// certificate Certificate,
Certificate(Certificate<'a>),
// Not supporting TBSCertificate option
// tbsCert \[1\] EXPLICIT TBSCertificate,
//TbsCertificate(TBSCertificate<'a>),
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
TaInfo(TrustAnchorInfo<'a>),
}

//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1);
const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2);

impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let t = decoder.peek_tag()?;
let o = t.octet();
// Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here
match o {
0x30 => {
let cert = decoder.decode()?;
Ok(TrustAnchorChoice::Certificate(cert))
}
// TODO - need DecodeValue on TBSCertificate to support this
// 0xA1 => {
// let on = decoder
// .context_specific::<TBSCertificate<'a>>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?;
// match on {
// Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)),
// _ => Err(ErrorKind::Failed.into()),
// }
// }
0xA2 => {
let on = decoder
.context_specific::<TrustAnchorInfo<'a>>(TAC_TA_INFO_TAG, TagMode::Explicit)?;
match on {
Some(on) => Ok(TrustAnchorChoice::TaInfo(on)),
_ => Err(ErrorKind::Failed.into()),
}
}
_ => Err(ErrorKind::TagUnknown { byte: o }.into()),
}
}
}

impl<'a> EncodeValue for TrustAnchorChoice<'a> {
fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> {
match self {
Self::Certificate(certificate) => certificate.encode(encoder),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encode(encoder),
Self::TaInfo(variant) => variant.encode(encoder),
}
}
fn value_len(&self) -> ::der::Result<::der::Length> {
match self {
Self::Certificate(certificate) => certificate.encoded_len(),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encoded_len(),
Self::TaInfo(variant) => variant.encoded_len(),
}
}
}

//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag
impl<'a> FixedTag for TrustAnchorChoice<'a> {
const TAG: Tag = ::der::Tag::ContextSpecific {
constructed: true,
number: TAC_TA_INFO_TAG,
};
}

// Not supporting these structures
// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice
//
// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1)
// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
// id-smime(16) id-ct(1) 34 }
Binary file addedx509/tests/examples/eca.der
Binary file not shown.
Binary file addedx509/tests/examples/eca_policies.ta
Binary file not shown.
Binary file addedx509/tests/examples/entrust.der
Binary file not shown.
Binary file addedx509/tests/examples/entrust_dnConstraint.ta
Binary file not shown.
Binary file addedx509/tests/examples/exostar.der
Binary file not shown.
Binary file addedx509/tests/examples/exostar_policyFlags.ta
Binary file not shown.
Binary file addedx509/tests/examples/raytheon.der
Binary file not shown.
Binary file not shown.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' add support for RFC5914 structs. by carl-wallace · Pull Request #356 · RustCrypto/formats · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions x509/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,7 @@ mod general_name;
pub mod pkix_extensions;
pub mod pkix_oids;
mod time;
pub mod trust_anchor_format;
mod validity;

pub use crate::{
Expand Down
49 changes: 3 additions & 46 deletions x509/src/pkix_extensions.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -326,60 +326,17 @@ pub struct PolicyMapping {
/// ```
///
/// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct NameConstraints<'a> {
/// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
//#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub permitted_subtrees: Option<GeneralSubtrees<'a>>,

/// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub excluded_subtrees: Option<GeneralSubtrees<'a>>,
}

const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0);
const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1);

impl<'a> ::der::Decodable<'a> for NameConstraints<'a> {
fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
decoder.sequence(|decoder| {
let permitted_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)?
.map(|cs| cs.value);
let excluded_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
Ok(Self {
permitted_subtrees,
excluded_subtrees,
})
})
}
}

impl<'a> ::der::Sequence<'a> for NameConstraints<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
f(&[
&self
.permitted_subtrees
.as_ref()
.map(|elem| ContextSpecific {
tag_number: PERMITTED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
&self.excluded_subtrees.as_ref().map(|elem| ContextSpecific {
tag_number: EXCLUDED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
])
}
}

/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension.
///
/// ```text
Expand Down
268 changes: 268 additions & 0 deletions x509/src/trust_anchor_format.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
//! Trust anchor-related structures as defined in RFC 5914

use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints};
use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String};
use der::{
DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Sequence, Tag,
TagMode, TagNumber,
};
use spki::SubjectPublicKeyInfo;
use x501::name::Name;

/// TrustAnchorInfo ::= SEQUENCE {
/// version TrustAnchorInfoVersion DEFAULT v1,
/// pubKey SubjectPublicKeyInfo,
/// keyId KeyIdentifier,
/// taTitle TrustAnchorTitle OPTIONAL,
/// certPath CertPathControls OPTIONAL,
/// exts \[1\] EXPLICIT Extensions OPTIONAL,
/// taTitleLangTag \[2\] UTF8String OPTIONAL }
///
/// TrustAnchorInfoVersion ::= INTEGER { v1(1) }
///
/// TrustAnchorTitle ::= UTF8String (SIZE (1..64))
#[derive(Clone, Eq, PartialEq)]
pub struct TrustAnchorInfo<'a> {
/// version TrustAnchorInfoVersion DEFAULT v1,
pub version: Option<u8>,

/// pubKey SubjectPublicKeyInfo,
pub pub_key: SubjectPublicKeyInfo<'a>,

/// keyId KeyIdentifier,
pub key_id: OctetString<'a>,

/// taTitle TrustAnchorTitle OPTIONAL,
pub ta_title: Option<Utf8String<'a>>,

/// certPath CertPathControls OPTIONAL,
pub cert_path: Option<CertPathControls<'a>>,

/// exts \[1\] EXPLICIT Extensions OPTIONAL,
pub extensions: Option<Extensions<'a>>,

/// taTitleLangTag \[2\] UTF8String OPTIONAL }
pub ta_title_lang_tag: Option<Utf8String<'a>>,
}

// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> {
// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let version = match decoder.decode()? {
Some(v) => Some(v),
_ => Some(1),
};

let pub_key = decoder.decode()?;
let key_id = decoder.decode()?;
let ta_title = decoder.decode()?;
let cert_path = decoder.decode()?;
let extensions =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
let ta_title_lang_tag =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)?
.map(|cs| cs.value);
Ok(Self {
version,
pub_key,
key_id,
ta_title,
cert_path,
extensions,
ta_title_lang_tag,
})
}
}
Comment on lines +50 to +77

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this one use the proc macro now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you left a note here:

Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific

It should probably work now.


const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1);
const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0);
impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
#[allow(unused_imports)]
use core::convert::TryFrom;
f(&[
&::der::asn1::OptionalRef(if self.version == Some(1) {
None
} else {
Some(&self.version)
}),
&self.pub_key,
&self.key_id,
&self.ta_title,
&self.cert_path,
&self.extensions.as_ref().map(|exts| ContextSpecific {
tag_number: TAF_EXTENSIONS_TAG,
tag_mode: TagMode::Explicit,
value: exts.clone(),
}),
&self
.ta_title_lang_tag
.as_ref()
.map(|ta_title_lang_tag| ContextSpecific {
tag_number: TA_TITLE_LANG_TAG,
tag_mode: TagMode::Implicit,
value: *ta_title_lang_tag,
}),
])
}
}

impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?;
f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?;
f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?;
f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?;
f.write_fmt(format_args!(
"\tTA title language tag: {:?}\n",
self.ta_title_lang_tag
))?;
f.write_fmt(format_args!(
"\tCertificate path controls: {:?}\n",
self.cert_path
))?;
if let Some(exts) = self.extensions.as_ref() {
for (i, e) in exts.iter().enumerate() {
f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?;
}
} else {
f.write_fmt(format_args!("\tExtensions: None\n"))?;
}
Ok(())
}
}

/// CertPathControls ::= SEQUENCE {
/// taName Name,
/// certificate \[0\] Certificate OPTIONAL,
/// policySet \[1\] CertificatePolicies OPTIONAL,
/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
/// nameConstr \[3\] NameConstraints OPTIONAL,
/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct CertPathControls<'a> {
/// taName Name,
pub ta_name: Name<'a>,

/// certificate \[0\] Certificate OPTIONAL,
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub certificate: Option<Certificate<'a>>,

/// policySet \[1\] CertificatePolicies OPTIONAL,
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub policy_set: Option<CertificatePolicies<'a>>,

/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
pub policy_flags: Option<CertPolicyFlags<'a>>,

/// nameConstr \[3\] NameConstraints OPTIONAL,
#[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")]
pub name_constr: Option<NameConstraints<'a>>,

/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")]
pub path_len_constraint: Option<u32>,
}

/// CertPolicyFlags ::= BIT STRING {
/// inhibitPolicyMapping (0),
/// requireExplicitPolicy (1),
/// inhibitAnyPolicy (2) }
pub type CertPolicyFlags<'a> = BitString<'a>;

/// TrustAnchorChoice ::= CHOICE {
/// certificate Certificate,
/// tbsCert \[1\] EXPLICIT TBSCertificate,
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorChoice<'a> {
/// certificate Certificate,
Certificate(Certificate<'a>),
// Not supporting TBSCertificate option
// tbsCert \[1\] EXPLICIT TBSCertificate,
//TbsCertificate(TBSCertificate<'a>),
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
TaInfo(TrustAnchorInfo<'a>),
}

//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1);
const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2);

impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let t = decoder.peek_tag()?;
let o = t.octet();
// Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here
match o {
0x30 => {
let cert = decoder.decode()?;
Ok(TrustAnchorChoice::Certificate(cert))
}
// TODO - need DecodeValue on TBSCertificate to support this
// 0xA1 => {
// let on = decoder
// .context_specific::<TBSCertificate<'a>>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?;
// match on {
// Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)),
// _ => Err(ErrorKind::Failed.into()),
// }
// }
0xA2 => {
let on = decoder
.context_specific::<TrustAnchorInfo<'a>>(TAC_TA_INFO_TAG, TagMode::Explicit)?;
match on {
Some(on) => Ok(TrustAnchorChoice::TaInfo(on)),
_ => Err(ErrorKind::Failed.into()),
}
}
_ => Err(ErrorKind::TagUnknown { byte: o }.into()),
}
}
}

impl<'a> EncodeValue for TrustAnchorChoice<'a> {
fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> {
match self {
Self::Certificate(certificate) => certificate.encode(encoder),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encode(encoder),
Self::TaInfo(variant) => variant.encode(encoder),
}
}
fn value_len(&self) -> ::der::Result<::der::Length> {
match self {
Self::Certificate(certificate) => certificate.encoded_len(),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encoded_len(),
Self::TaInfo(variant) => variant.encoded_len(),
}
}
}

//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag
impl<'a> FixedTag for TrustAnchorChoice<'a> {
const TAG: Tag = ::der::Tag::ContextSpecific {
constructed: true,
number: TAC_TA_INFO_TAG,
};
}

// Not supporting these structures
// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice
//
// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1)
// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
// id-smime(16) id-ct(1) 34 }
Binary file addedx509/tests/examples/eca.der
Binary file not shown.
Binary file addedx509/tests/examples/eca_policies.ta
Binary file not shown.
Binary file addedx509/tests/examples/entrust.der
Binary file not shown.
Binary file addedx509/tests/examples/entrust_dnConstraint.ta
Binary file not shown.
Binary file addedx509/tests/examples/exostar.der
Binary file not shown.
Binary file addedx509/tests/examples/exostar_policyFlags.ta
Binary file not shown.
Binary file addedx509/tests/examples/raytheon.der
Binary file not shown.
Binary file not shown.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' add support for RFC5914 structs. by carl-wallace · Pull Request #356 · RustCrypto/formats · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions x509/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,7 @@ mod general_name;
pub mod pkix_extensions;
pub mod pkix_oids;
mod time;
pub mod trust_anchor_format;
mod validity;

pub use crate::{
Expand Down
49 changes: 3 additions & 46 deletions x509/src/pkix_extensions.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -326,60 +326,17 @@ pub struct PolicyMapping {
/// ```
///
/// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct NameConstraints<'a> {
/// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
//#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub permitted_subtrees: Option<GeneralSubtrees<'a>>,

/// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub excluded_subtrees: Option<GeneralSubtrees<'a>>,
}

const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0);
const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1);

impl<'a> ::der::Decodable<'a> for NameConstraints<'a> {
fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
decoder.sequence(|decoder| {
let permitted_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)?
.map(|cs| cs.value);
let excluded_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
Ok(Self {
permitted_subtrees,
excluded_subtrees,
})
})
}
}

impl<'a> ::der::Sequence<'a> for NameConstraints<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
f(&[
&self
.permitted_subtrees
.as_ref()
.map(|elem| ContextSpecific {
tag_number: PERMITTED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
&self.excluded_subtrees.as_ref().map(|elem| ContextSpecific {
tag_number: EXCLUDED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
])
}
}

/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension.
///
/// ```text
Expand Down
268 changes: 268 additions & 0 deletions x509/src/trust_anchor_format.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
//! Trust anchor-related structures as defined in RFC 5914

use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints};
use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String};
use der::{
DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Sequence, Tag,
TagMode, TagNumber,
};
use spki::SubjectPublicKeyInfo;
use x501::name::Name;

/// TrustAnchorInfo ::= SEQUENCE {
/// version TrustAnchorInfoVersion DEFAULT v1,
/// pubKey SubjectPublicKeyInfo,
/// keyId KeyIdentifier,
/// taTitle TrustAnchorTitle OPTIONAL,
/// certPath CertPathControls OPTIONAL,
/// exts \[1\] EXPLICIT Extensions OPTIONAL,
/// taTitleLangTag \[2\] UTF8String OPTIONAL }
///
/// TrustAnchorInfoVersion ::= INTEGER { v1(1) }
///
/// TrustAnchorTitle ::= UTF8String (SIZE (1..64))
#[derive(Clone, Eq, PartialEq)]
pub struct TrustAnchorInfo<'a> {
/// version TrustAnchorInfoVersion DEFAULT v1,
pub version: Option<u8>,

/// pubKey SubjectPublicKeyInfo,
pub pub_key: SubjectPublicKeyInfo<'a>,

/// keyId KeyIdentifier,
pub key_id: OctetString<'a>,

/// taTitle TrustAnchorTitle OPTIONAL,
pub ta_title: Option<Utf8String<'a>>,

/// certPath CertPathControls OPTIONAL,
pub cert_path: Option<CertPathControls<'a>>,

/// exts \[1\] EXPLICIT Extensions OPTIONAL,
pub extensions: Option<Extensions<'a>>,

/// taTitleLangTag \[2\] UTF8String OPTIONAL }
pub ta_title_lang_tag: Option<Utf8String<'a>>,
}

// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> {
// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let version = match decoder.decode()? {
Some(v) => Some(v),
_ => Some(1),
};

let pub_key = decoder.decode()?;
let key_id = decoder.decode()?;
let ta_title = decoder.decode()?;
let cert_path = decoder.decode()?;
let extensions =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
let ta_title_lang_tag =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)?
.map(|cs| cs.value);
Ok(Self {
version,
pub_key,
key_id,
ta_title,
cert_path,
extensions,
ta_title_lang_tag,
})
}
}
Comment on lines +50 to +77

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this one use the proc macro now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you left a note here:

Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific

It should probably work now.


const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1);
const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0);
impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
#[allow(unused_imports)]
use core::convert::TryFrom;
f(&[
&::der::asn1::OptionalRef(if self.version == Some(1) {
None
} else {
Some(&self.version)
}),
&self.pub_key,
&self.key_id,
&self.ta_title,
&self.cert_path,
&self.extensions.as_ref().map(|exts| ContextSpecific {
tag_number: TAF_EXTENSIONS_TAG,
tag_mode: TagMode::Explicit,
value: exts.clone(),
}),
&self
.ta_title_lang_tag
.as_ref()
.map(|ta_title_lang_tag| ContextSpecific {
tag_number: TA_TITLE_LANG_TAG,
tag_mode: TagMode::Implicit,
value: *ta_title_lang_tag,
}),
])
}
}

impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?;
f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?;
f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?;
f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?;
f.write_fmt(format_args!(
"\tTA title language tag: {:?}\n",
self.ta_title_lang_tag
))?;
f.write_fmt(format_args!(
"\tCertificate path controls: {:?}\n",
self.cert_path
))?;
if let Some(exts) = self.extensions.as_ref() {
for (i, e) in exts.iter().enumerate() {
f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?;
}
} else {
f.write_fmt(format_args!("\tExtensions: None\n"))?;
}
Ok(())
}
}

/// CertPathControls ::= SEQUENCE {
/// taName Name,
/// certificate \[0\] Certificate OPTIONAL,
/// policySet \[1\] CertificatePolicies OPTIONAL,
/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
/// nameConstr \[3\] NameConstraints OPTIONAL,
/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct CertPathControls<'a> {
/// taName Name,
pub ta_name: Name<'a>,

/// certificate \[0\] Certificate OPTIONAL,
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub certificate: Option<Certificate<'a>>,

/// policySet \[1\] CertificatePolicies OPTIONAL,
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub policy_set: Option<CertificatePolicies<'a>>,

/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
pub policy_flags: Option<CertPolicyFlags<'a>>,

/// nameConstr \[3\] NameConstraints OPTIONAL,
#[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")]
pub name_constr: Option<NameConstraints<'a>>,

/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")]
pub path_len_constraint: Option<u32>,
}

/// CertPolicyFlags ::= BIT STRING {
/// inhibitPolicyMapping (0),
/// requireExplicitPolicy (1),
/// inhibitAnyPolicy (2) }
pub type CertPolicyFlags<'a> = BitString<'a>;

/// TrustAnchorChoice ::= CHOICE {
/// certificate Certificate,
/// tbsCert \[1\] EXPLICIT TBSCertificate,
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorChoice<'a> {
/// certificate Certificate,
Certificate(Certificate<'a>),
// Not supporting TBSCertificate option
// tbsCert \[1\] EXPLICIT TBSCertificate,
//TbsCertificate(TBSCertificate<'a>),
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
TaInfo(TrustAnchorInfo<'a>),
}

//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1);
const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2);

impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let t = decoder.peek_tag()?;
let o = t.octet();
// Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here
match o {
0x30 => {
let cert = decoder.decode()?;
Ok(TrustAnchorChoice::Certificate(cert))
}
// TODO - need DecodeValue on TBSCertificate to support this
// 0xA1 => {
// let on = decoder
// .context_specific::<TBSCertificate<'a>>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?;
// match on {
// Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)),
// _ => Err(ErrorKind::Failed.into()),
// }
// }
0xA2 => {
let on = decoder
.context_specific::<TrustAnchorInfo<'a>>(TAC_TA_INFO_TAG, TagMode::Explicit)?;
match on {
Some(on) => Ok(TrustAnchorChoice::TaInfo(on)),
_ => Err(ErrorKind::Failed.into()),
}
}
_ => Err(ErrorKind::TagUnknown { byte: o }.into()),
}
}
}

impl<'a> EncodeValue for TrustAnchorChoice<'a> {
fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> {
match self {
Self::Certificate(certificate) => certificate.encode(encoder),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encode(encoder),
Self::TaInfo(variant) => variant.encode(encoder),
}
}
fn value_len(&self) -> ::der::Result<::der::Length> {
match self {
Self::Certificate(certificate) => certificate.encoded_len(),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encoded_len(),
Self::TaInfo(variant) => variant.encoded_len(),
}
}
}

//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag
impl<'a> FixedTag for TrustAnchorChoice<'a> {
const TAG: Tag = ::der::Tag::ContextSpecific {
constructed: true,
number: TAC_TA_INFO_TAG,
};
}

// Not supporting these structures
// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice
//
// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1)
// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
// id-smime(16) id-ct(1) 34 }
Binary file addedx509/tests/examples/eca.der
Binary file not shown.
Binary file addedx509/tests/examples/eca_policies.ta
Binary file not shown.
Binary file addedx509/tests/examples/entrust.der
Binary file not shown.
Binary file addedx509/tests/examples/entrust_dnConstraint.ta
Binary file not shown.
Binary file addedx509/tests/examples/exostar.der
Binary file not shown.
Binary file addedx509/tests/examples/exostar_policyFlags.ta
Binary file not shown.
Binary file addedx509/tests/examples/raytheon.der
Binary file not shown.
Binary file not shown.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' add support for RFC5914 structs. by carl-wallace · Pull Request #356 · RustCrypto/formats · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions x509/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,7 @@ mod general_name;
pub mod pkix_extensions;
pub mod pkix_oids;
mod time;
pub mod trust_anchor_format;
mod validity;

pub use crate::{
Expand Down
49 changes: 3 additions & 46 deletions x509/src/pkix_extensions.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -326,60 +326,17 @@ pub struct PolicyMapping {
/// ```
///
/// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct NameConstraints<'a> {
/// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
//#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub permitted_subtrees: Option<GeneralSubtrees<'a>>,

/// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub excluded_subtrees: Option<GeneralSubtrees<'a>>,
}

const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0);
const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1);

impl<'a> ::der::Decodable<'a> for NameConstraints<'a> {
fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
decoder.sequence(|decoder| {
let permitted_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)?
.map(|cs| cs.value);
let excluded_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
Ok(Self {
permitted_subtrees,
excluded_subtrees,
})
})
}
}

impl<'a> ::der::Sequence<'a> for NameConstraints<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
f(&[
&self
.permitted_subtrees
.as_ref()
.map(|elem| ContextSpecific {
tag_number: PERMITTED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
&self.excluded_subtrees.as_ref().map(|elem| ContextSpecific {
tag_number: EXCLUDED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
])
}
}

/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension.
///
/// ```text
Expand Down
268 changes: 268 additions & 0 deletions x509/src/trust_anchor_format.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
//! Trust anchor-related structures as defined in RFC 5914

use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints};
use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String};
use der::{
DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Sequence, Tag,
TagMode, TagNumber,
};
use spki::SubjectPublicKeyInfo;
use x501::name::Name;

/// TrustAnchorInfo ::= SEQUENCE {
/// version TrustAnchorInfoVersion DEFAULT v1,
/// pubKey SubjectPublicKeyInfo,
/// keyId KeyIdentifier,
/// taTitle TrustAnchorTitle OPTIONAL,
/// certPath CertPathControls OPTIONAL,
/// exts \[1\] EXPLICIT Extensions OPTIONAL,
/// taTitleLangTag \[2\] UTF8String OPTIONAL }
///
/// TrustAnchorInfoVersion ::= INTEGER { v1(1) }
///
/// TrustAnchorTitle ::= UTF8String (SIZE (1..64))
#[derive(Clone, Eq, PartialEq)]
pub struct TrustAnchorInfo<'a> {
/// version TrustAnchorInfoVersion DEFAULT v1,
pub version: Option<u8>,

/// pubKey SubjectPublicKeyInfo,
pub pub_key: SubjectPublicKeyInfo<'a>,

/// keyId KeyIdentifier,
pub key_id: OctetString<'a>,

/// taTitle TrustAnchorTitle OPTIONAL,
pub ta_title: Option<Utf8String<'a>>,

/// certPath CertPathControls OPTIONAL,
pub cert_path: Option<CertPathControls<'a>>,

/// exts \[1\] EXPLICIT Extensions OPTIONAL,
pub extensions: Option<Extensions<'a>>,

/// taTitleLangTag \[2\] UTF8String OPTIONAL }
pub ta_title_lang_tag: Option<Utf8String<'a>>,
}

// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> {
// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let version = match decoder.decode()? {
Some(v) => Some(v),
_ => Some(1),
};

let pub_key = decoder.decode()?;
let key_id = decoder.decode()?;
let ta_title = decoder.decode()?;
let cert_path = decoder.decode()?;
let extensions =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
let ta_title_lang_tag =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)?
.map(|cs| cs.value);
Ok(Self {
version,
pub_key,
key_id,
ta_title,
cert_path,
extensions,
ta_title_lang_tag,
})
}
}
Comment on lines +50 to +77

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this one use the proc macro now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you left a note here:

Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific

It should probably work now.


const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1);
const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0);
impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
#[allow(unused_imports)]
use core::convert::TryFrom;
f(&[
&::der::asn1::OptionalRef(if self.version == Some(1) {
None
} else {
Some(&self.version)
}),
&self.pub_key,
&self.key_id,
&self.ta_title,
&self.cert_path,
&self.extensions.as_ref().map(|exts| ContextSpecific {
tag_number: TAF_EXTENSIONS_TAG,
tag_mode: TagMode::Explicit,
value: exts.clone(),
}),
&self
.ta_title_lang_tag
.as_ref()
.map(|ta_title_lang_tag| ContextSpecific {
tag_number: TA_TITLE_LANG_TAG,
tag_mode: TagMode::Implicit,
value: *ta_title_lang_tag,
}),
])
}
}

impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?;
f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?;
f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?;
f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?;
f.write_fmt(format_args!(
"\tTA title language tag: {:?}\n",
self.ta_title_lang_tag
))?;
f.write_fmt(format_args!(
"\tCertificate path controls: {:?}\n",
self.cert_path
))?;
if let Some(exts) = self.extensions.as_ref() {
for (i, e) in exts.iter().enumerate() {
f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?;
}
} else {
f.write_fmt(format_args!("\tExtensions: None\n"))?;
}
Ok(())
}
}

/// CertPathControls ::= SEQUENCE {
/// taName Name,
/// certificate \[0\] Certificate OPTIONAL,
/// policySet \[1\] CertificatePolicies OPTIONAL,
/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
/// nameConstr \[3\] NameConstraints OPTIONAL,
/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct CertPathControls<'a> {
/// taName Name,
pub ta_name: Name<'a>,

/// certificate \[0\] Certificate OPTIONAL,
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub certificate: Option<Certificate<'a>>,

/// policySet \[1\] CertificatePolicies OPTIONAL,
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub policy_set: Option<CertificatePolicies<'a>>,

/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
pub policy_flags: Option<CertPolicyFlags<'a>>,

/// nameConstr \[3\] NameConstraints OPTIONAL,
#[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")]
pub name_constr: Option<NameConstraints<'a>>,

/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")]
pub path_len_constraint: Option<u32>,
}

/// CertPolicyFlags ::= BIT STRING {
/// inhibitPolicyMapping (0),
/// requireExplicitPolicy (1),
/// inhibitAnyPolicy (2) }
pub type CertPolicyFlags<'a> = BitString<'a>;

/// TrustAnchorChoice ::= CHOICE {
/// certificate Certificate,
/// tbsCert \[1\] EXPLICIT TBSCertificate,
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorChoice<'a> {
/// certificate Certificate,
Certificate(Certificate<'a>),
// Not supporting TBSCertificate option
// tbsCert \[1\] EXPLICIT TBSCertificate,
//TbsCertificate(TBSCertificate<'a>),
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
TaInfo(TrustAnchorInfo<'a>),
}

//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1);
const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2);

impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let t = decoder.peek_tag()?;
let o = t.octet();
// Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here
match o {
0x30 => {
let cert = decoder.decode()?;
Ok(TrustAnchorChoice::Certificate(cert))
}
// TODO - need DecodeValue on TBSCertificate to support this
// 0xA1 => {
// let on = decoder
// .context_specific::<TBSCertificate<'a>>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?;
// match on {
// Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)),
// _ => Err(ErrorKind::Failed.into()),
// }
// }
0xA2 => {
let on = decoder
.context_specific::<TrustAnchorInfo<'a>>(TAC_TA_INFO_TAG, TagMode::Explicit)?;
match on {
Some(on) => Ok(TrustAnchorChoice::TaInfo(on)),
_ => Err(ErrorKind::Failed.into()),
}
}
_ => Err(ErrorKind::TagUnknown { byte: o }.into()),
}
}
}

impl<'a> EncodeValue for TrustAnchorChoice<'a> {
fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> {
match self {
Self::Certificate(certificate) => certificate.encode(encoder),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encode(encoder),
Self::TaInfo(variant) => variant.encode(encoder),
}
}
fn value_len(&self) -> ::der::Result<::der::Length> {
match self {
Self::Certificate(certificate) => certificate.encoded_len(),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encoded_len(),
Self::TaInfo(variant) => variant.encoded_len(),
}
}
}

//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag
impl<'a> FixedTag for TrustAnchorChoice<'a> {
const TAG: Tag = ::der::Tag::ContextSpecific {
constructed: true,
number: TAC_TA_INFO_TAG,
};
}

// Not supporting these structures
// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice
//
// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1)
// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
// id-smime(16) id-ct(1) 34 }
Binary file addedx509/tests/examples/eca.der
Binary file not shown.
Binary file addedx509/tests/examples/eca_policies.ta
Binary file not shown.
Binary file addedx509/tests/examples/entrust.der
Binary file not shown.
Binary file addedx509/tests/examples/entrust_dnConstraint.ta
Binary file not shown.
Binary file addedx509/tests/examples/exostar.der
Binary file not shown.
Binary file addedx509/tests/examples/exostar_policyFlags.ta
Binary file not shown.
Binary file addedx509/tests/examples/raytheon.der
Binary file not shown.
Binary file not shown.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' add support for RFC5914 structs. by carl-wallace · Pull Request #356 · RustCrypto/formats · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions x509/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,7 @@ mod general_name;
pub mod pkix_extensions;
pub mod pkix_oids;
mod time;
pub mod trust_anchor_format;
mod validity;

pub use crate::{
Expand Down
49 changes: 3 additions & 46 deletions x509/src/pkix_extensions.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -326,60 +326,17 @@ pub struct PolicyMapping {
/// ```
///
/// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct NameConstraints<'a> {
/// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
//#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub permitted_subtrees: Option<GeneralSubtrees<'a>>,

/// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub excluded_subtrees: Option<GeneralSubtrees<'a>>,
}

const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0);
const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1);

impl<'a> ::der::Decodable<'a> for NameConstraints<'a> {
fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
decoder.sequence(|decoder| {
let permitted_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)?
.map(|cs| cs.value);
let excluded_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
Ok(Self {
permitted_subtrees,
excluded_subtrees,
})
})
}
}

impl<'a> ::der::Sequence<'a> for NameConstraints<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
f(&[
&self
.permitted_subtrees
.as_ref()
.map(|elem| ContextSpecific {
tag_number: PERMITTED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
&self.excluded_subtrees.as_ref().map(|elem| ContextSpecific {
tag_number: EXCLUDED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
])
}
}

/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension.
///
/// ```text
Expand Down
268 changes: 268 additions & 0 deletions x509/src/trust_anchor_format.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
//! Trust anchor-related structures as defined in RFC 5914

use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints};
use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String};
use der::{
DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Sequence, Tag,
TagMode, TagNumber,
};
use spki::SubjectPublicKeyInfo;
use x501::name::Name;

/// TrustAnchorInfo ::= SEQUENCE {
/// version TrustAnchorInfoVersion DEFAULT v1,
/// pubKey SubjectPublicKeyInfo,
/// keyId KeyIdentifier,
/// taTitle TrustAnchorTitle OPTIONAL,
/// certPath CertPathControls OPTIONAL,
/// exts \[1\] EXPLICIT Extensions OPTIONAL,
/// taTitleLangTag \[2\] UTF8String OPTIONAL }
///
/// TrustAnchorInfoVersion ::= INTEGER { v1(1) }
///
/// TrustAnchorTitle ::= UTF8String (SIZE (1..64))
#[derive(Clone, Eq, PartialEq)]
pub struct TrustAnchorInfo<'a> {
/// version TrustAnchorInfoVersion DEFAULT v1,
pub version: Option<u8>,

/// pubKey SubjectPublicKeyInfo,
pub pub_key: SubjectPublicKeyInfo<'a>,

/// keyId KeyIdentifier,
pub key_id: OctetString<'a>,

/// taTitle TrustAnchorTitle OPTIONAL,
pub ta_title: Option<Utf8String<'a>>,

/// certPath CertPathControls OPTIONAL,
pub cert_path: Option<CertPathControls<'a>>,

/// exts \[1\] EXPLICIT Extensions OPTIONAL,
pub extensions: Option<Extensions<'a>>,

/// taTitleLangTag \[2\] UTF8String OPTIONAL }
pub ta_title_lang_tag: Option<Utf8String<'a>>,
}

// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> {
// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let version = match decoder.decode()? {
Some(v) => Some(v),
_ => Some(1),
};

let pub_key = decoder.decode()?;
let key_id = decoder.decode()?;
let ta_title = decoder.decode()?;
let cert_path = decoder.decode()?;
let extensions =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
let ta_title_lang_tag =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)?
.map(|cs| cs.value);
Ok(Self {
version,
pub_key,
key_id,
ta_title,
cert_path,
extensions,
ta_title_lang_tag,
})
}
}
Comment on lines +50 to +77

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this one use the proc macro now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you left a note here:

Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific

It should probably work now.


const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1);
const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0);
impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
#[allow(unused_imports)]
use core::convert::TryFrom;
f(&[
&::der::asn1::OptionalRef(if self.version == Some(1) {
None
} else {
Some(&self.version)
}),
&self.pub_key,
&self.key_id,
&self.ta_title,
&self.cert_path,
&self.extensions.as_ref().map(|exts| ContextSpecific {
tag_number: TAF_EXTENSIONS_TAG,
tag_mode: TagMode::Explicit,
value: exts.clone(),
}),
&self
.ta_title_lang_tag
.as_ref()
.map(|ta_title_lang_tag| ContextSpecific {
tag_number: TA_TITLE_LANG_TAG,
tag_mode: TagMode::Implicit,
value: *ta_title_lang_tag,
}),
])
}
}

impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?;
f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?;
f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?;
f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?;
f.write_fmt(format_args!(
"\tTA title language tag: {:?}\n",
self.ta_title_lang_tag
))?;
f.write_fmt(format_args!(
"\tCertificate path controls: {:?}\n",
self.cert_path
))?;
if let Some(exts) = self.extensions.as_ref() {
for (i, e) in exts.iter().enumerate() {
f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?;
}
} else {
f.write_fmt(format_args!("\tExtensions: None\n"))?;
}
Ok(())
}
}

/// CertPathControls ::= SEQUENCE {
/// taName Name,
/// certificate \[0\] Certificate OPTIONAL,
/// policySet \[1\] CertificatePolicies OPTIONAL,
/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
/// nameConstr \[3\] NameConstraints OPTIONAL,
/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct CertPathControls<'a> {
/// taName Name,
pub ta_name: Name<'a>,

/// certificate \[0\] Certificate OPTIONAL,
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub certificate: Option<Certificate<'a>>,

/// policySet \[1\] CertificatePolicies OPTIONAL,
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub policy_set: Option<CertificatePolicies<'a>>,

/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
pub policy_flags: Option<CertPolicyFlags<'a>>,

/// nameConstr \[3\] NameConstraints OPTIONAL,
#[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")]
pub name_constr: Option<NameConstraints<'a>>,

/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")]
pub path_len_constraint: Option<u32>,
}

/// CertPolicyFlags ::= BIT STRING {
/// inhibitPolicyMapping (0),
/// requireExplicitPolicy (1),
/// inhibitAnyPolicy (2) }
pub type CertPolicyFlags<'a> = BitString<'a>;

/// TrustAnchorChoice ::= CHOICE {
/// certificate Certificate,
/// tbsCert \[1\] EXPLICIT TBSCertificate,
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorChoice<'a> {
/// certificate Certificate,
Certificate(Certificate<'a>),
// Not supporting TBSCertificate option
// tbsCert \[1\] EXPLICIT TBSCertificate,
//TbsCertificate(TBSCertificate<'a>),
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
TaInfo(TrustAnchorInfo<'a>),
}

//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1);
const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2);

impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let t = decoder.peek_tag()?;
let o = t.octet();
// Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here
match o {
0x30 => {
let cert = decoder.decode()?;
Ok(TrustAnchorChoice::Certificate(cert))
}
// TODO - need DecodeValue on TBSCertificate to support this
// 0xA1 => {
// let on = decoder
// .context_specific::<TBSCertificate<'a>>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?;
// match on {
// Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)),
// _ => Err(ErrorKind::Failed.into()),
// }
// }
0xA2 => {
let on = decoder
.context_specific::<TrustAnchorInfo<'a>>(TAC_TA_INFO_TAG, TagMode::Explicit)?;
match on {
Some(on) => Ok(TrustAnchorChoice::TaInfo(on)),
_ => Err(ErrorKind::Failed.into()),
}
}
_ => Err(ErrorKind::TagUnknown { byte: o }.into()),
}
}
}

impl<'a> EncodeValue for TrustAnchorChoice<'a> {
fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> {
match self {
Self::Certificate(certificate) => certificate.encode(encoder),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encode(encoder),
Self::TaInfo(variant) => variant.encode(encoder),
}
}
fn value_len(&self) -> ::der::Result<::der::Length> {
match self {
Self::Certificate(certificate) => certificate.encoded_len(),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encoded_len(),
Self::TaInfo(variant) => variant.encoded_len(),
}
}
}

//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag
impl<'a> FixedTag for TrustAnchorChoice<'a> {
const TAG: Tag = ::der::Tag::ContextSpecific {
constructed: true,
number: TAC_TA_INFO_TAG,
};
}

// Not supporting these structures
// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice
//
// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1)
// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
// id-smime(16) id-ct(1) 34 }
Binary file addedx509/tests/examples/eca.der
Binary file not shown.
Binary file addedx509/tests/examples/eca_policies.ta
Binary file not shown.
Binary file addedx509/tests/examples/entrust.der
Binary file not shown.
Binary file addedx509/tests/examples/entrust_dnConstraint.ta
Binary file not shown.
Binary file addedx509/tests/examples/exostar.der
Binary file not shown.
Binary file addedx509/tests/examples/exostar_policyFlags.ta
Binary file not shown.
Binary file addedx509/tests/examples/raytheon.der
Binary file not shown.
Binary file not shown.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' add support for RFC5914 structs. by carl-wallace · Pull Request #356 · RustCrypto/formats · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions x509/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,7 @@ mod general_name;
pub mod pkix_extensions;
pub mod pkix_oids;
mod time;
pub mod trust_anchor_format;
mod validity;

pub use crate::{
Expand Down
49 changes: 3 additions & 46 deletions x509/src/pkix_extensions.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -326,60 +326,17 @@ pub struct PolicyMapping {
/// ```
///
/// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct NameConstraints<'a> {
/// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
//#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub permitted_subtrees: Option<GeneralSubtrees<'a>>,

/// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub excluded_subtrees: Option<GeneralSubtrees<'a>>,
}

const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0);
const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1);

impl<'a> ::der::Decodable<'a> for NameConstraints<'a> {
fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
decoder.sequence(|decoder| {
let permitted_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)?
.map(|cs| cs.value);
let excluded_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
Ok(Self {
permitted_subtrees,
excluded_subtrees,
})
})
}
}

impl<'a> ::der::Sequence<'a> for NameConstraints<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
f(&[
&self
.permitted_subtrees
.as_ref()
.map(|elem| ContextSpecific {
tag_number: PERMITTED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
&self.excluded_subtrees.as_ref().map(|elem| ContextSpecific {
tag_number: EXCLUDED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
])
}
}

/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension.
///
/// ```text
Expand Down
268 changes: 268 additions & 0 deletions x509/src/trust_anchor_format.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
//! Trust anchor-related structures as defined in RFC 5914

use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints};
use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String};
use der::{
DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Sequence, Tag,
TagMode, TagNumber,
};
use spki::SubjectPublicKeyInfo;
use x501::name::Name;

/// TrustAnchorInfo ::= SEQUENCE {
/// version TrustAnchorInfoVersion DEFAULT v1,
/// pubKey SubjectPublicKeyInfo,
/// keyId KeyIdentifier,
/// taTitle TrustAnchorTitle OPTIONAL,
/// certPath CertPathControls OPTIONAL,
/// exts \[1\] EXPLICIT Extensions OPTIONAL,
/// taTitleLangTag \[2\] UTF8String OPTIONAL }
///
/// TrustAnchorInfoVersion ::= INTEGER { v1(1) }
///
/// TrustAnchorTitle ::= UTF8String (SIZE (1..64))
#[derive(Clone, Eq, PartialEq)]
pub struct TrustAnchorInfo<'a> {
/// version TrustAnchorInfoVersion DEFAULT v1,
pub version: Option<u8>,

/// pubKey SubjectPublicKeyInfo,
pub pub_key: SubjectPublicKeyInfo<'a>,

/// keyId KeyIdentifier,
pub key_id: OctetString<'a>,

/// taTitle TrustAnchorTitle OPTIONAL,
pub ta_title: Option<Utf8String<'a>>,

/// certPath CertPathControls OPTIONAL,
pub cert_path: Option<CertPathControls<'a>>,

/// exts \[1\] EXPLICIT Extensions OPTIONAL,
pub extensions: Option<Extensions<'a>>,

/// taTitleLangTag \[2\] UTF8String OPTIONAL }
pub ta_title_lang_tag: Option<Utf8String<'a>>,
}

// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> {
// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let version = match decoder.decode()? {
Some(v) => Some(v),
_ => Some(1),
};

let pub_key = decoder.decode()?;
let key_id = decoder.decode()?;
let ta_title = decoder.decode()?;
let cert_path = decoder.decode()?;
let extensions =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
let ta_title_lang_tag =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)?
.map(|cs| cs.value);
Ok(Self {
version,
pub_key,
key_id,
ta_title,
cert_path,
extensions,
ta_title_lang_tag,
})
}
}
Comment on lines +50 to +77

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this one use the proc macro now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you left a note here:

Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific

It should probably work now.


const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1);
const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0);
impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
#[allow(unused_imports)]
use core::convert::TryFrom;
f(&[
&::der::asn1::OptionalRef(if self.version == Some(1) {
None
} else {
Some(&self.version)
}),
&self.pub_key,
&self.key_id,
&self.ta_title,
&self.cert_path,
&self.extensions.as_ref().map(|exts| ContextSpecific {
tag_number: TAF_EXTENSIONS_TAG,
tag_mode: TagMode::Explicit,
value: exts.clone(),
}),
&self
.ta_title_lang_tag
.as_ref()
.map(|ta_title_lang_tag| ContextSpecific {
tag_number: TA_TITLE_LANG_TAG,
tag_mode: TagMode::Implicit,
value: *ta_title_lang_tag,
}),
])
}
}

impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?;
f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?;
f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?;
f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?;
f.write_fmt(format_args!(
"\tTA title language tag: {:?}\n",
self.ta_title_lang_tag
))?;
f.write_fmt(format_args!(
"\tCertificate path controls: {:?}\n",
self.cert_path
))?;
if let Some(exts) = self.extensions.as_ref() {
for (i, e) in exts.iter().enumerate() {
f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?;
}
} else {
f.write_fmt(format_args!("\tExtensions: None\n"))?;
}
Ok(())
}
}

/// CertPathControls ::= SEQUENCE {
/// taName Name,
/// certificate \[0\] Certificate OPTIONAL,
/// policySet \[1\] CertificatePolicies OPTIONAL,
/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
/// nameConstr \[3\] NameConstraints OPTIONAL,
/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct CertPathControls<'a> {
/// taName Name,
pub ta_name: Name<'a>,

/// certificate \[0\] Certificate OPTIONAL,
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub certificate: Option<Certificate<'a>>,

/// policySet \[1\] CertificatePolicies OPTIONAL,
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub policy_set: Option<CertificatePolicies<'a>>,

/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
pub policy_flags: Option<CertPolicyFlags<'a>>,

/// nameConstr \[3\] NameConstraints OPTIONAL,
#[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")]
pub name_constr: Option<NameConstraints<'a>>,

/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")]
pub path_len_constraint: Option<u32>,
}

/// CertPolicyFlags ::= BIT STRING {
/// inhibitPolicyMapping (0),
/// requireExplicitPolicy (1),
/// inhibitAnyPolicy (2) }
pub type CertPolicyFlags<'a> = BitString<'a>;

/// TrustAnchorChoice ::= CHOICE {
/// certificate Certificate,
/// tbsCert \[1\] EXPLICIT TBSCertificate,
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorChoice<'a> {
/// certificate Certificate,
Certificate(Certificate<'a>),
// Not supporting TBSCertificate option
// tbsCert \[1\] EXPLICIT TBSCertificate,
//TbsCertificate(TBSCertificate<'a>),
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
TaInfo(TrustAnchorInfo<'a>),
}

//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1);
const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2);

impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let t = decoder.peek_tag()?;
let o = t.octet();
// Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here
match o {
0x30 => {
let cert = decoder.decode()?;
Ok(TrustAnchorChoice::Certificate(cert))
}
// TODO - need DecodeValue on TBSCertificate to support this
// 0xA1 => {
// let on = decoder
// .context_specific::<TBSCertificate<'a>>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?;
// match on {
// Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)),
// _ => Err(ErrorKind::Failed.into()),
// }
// }
0xA2 => {
let on = decoder
.context_specific::<TrustAnchorInfo<'a>>(TAC_TA_INFO_TAG, TagMode::Explicit)?;
match on {
Some(on) => Ok(TrustAnchorChoice::TaInfo(on)),
_ => Err(ErrorKind::Failed.into()),
}
}
_ => Err(ErrorKind::TagUnknown { byte: o }.into()),
}
}
}

impl<'a> EncodeValue for TrustAnchorChoice<'a> {
fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> {
match self {
Self::Certificate(certificate) => certificate.encode(encoder),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encode(encoder),
Self::TaInfo(variant) => variant.encode(encoder),
}
}
fn value_len(&self) -> ::der::Result<::der::Length> {
match self {
Self::Certificate(certificate) => certificate.encoded_len(),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encoded_len(),
Self::TaInfo(variant) => variant.encoded_len(),
}
}
}

//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag
impl<'a> FixedTag for TrustAnchorChoice<'a> {
const TAG: Tag = ::der::Tag::ContextSpecific {
constructed: true,
number: TAC_TA_INFO_TAG,
};
}

// Not supporting these structures
// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice
//
// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1)
// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
// id-smime(16) id-ct(1) 34 }
Binary file addedx509/tests/examples/eca.der
Binary file not shown.
Binary file addedx509/tests/examples/eca_policies.ta
Binary file not shown.
Binary file addedx509/tests/examples/entrust.der
Binary file not shown.
Binary file addedx509/tests/examples/entrust_dnConstraint.ta
Binary file not shown.
Binary file addedx509/tests/examples/exostar.der
Binary file not shown.
Binary file addedx509/tests/examples/exostar_policyFlags.ta
Binary file not shown.
Binary file addedx509/tests/examples/raytheon.der
Binary file not shown.
Binary file not shown.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); add support for RFC5914 structs. by carl-wallace · Pull Request #356 · RustCrypto/formats · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions x509/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,7 @@ mod general_name;
pub mod pkix_extensions;
pub mod pkix_oids;
mod time;
pub mod trust_anchor_format;
mod validity;

pub use crate::{
Expand Down
49 changes: 3 additions & 46 deletions x509/src/pkix_extensions.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -326,60 +326,17 @@ pub struct PolicyMapping {
/// ```
///
/// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct NameConstraints<'a> {
/// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
//#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub permitted_subtrees: Option<GeneralSubtrees<'a>>,

/// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub excluded_subtrees: Option<GeneralSubtrees<'a>>,
}

const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0);
const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1);

impl<'a> ::der::Decodable<'a> for NameConstraints<'a> {
fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
decoder.sequence(|decoder| {
let permitted_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)?
.map(|cs| cs.value);
let excluded_subtrees =
::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
Ok(Self {
permitted_subtrees,
excluded_subtrees,
})
})
}
}

impl<'a> ::der::Sequence<'a> for NameConstraints<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
f(&[
&self
.permitted_subtrees
.as_ref()
.map(|elem| ContextSpecific {
tag_number: PERMITTED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
&self.excluded_subtrees.as_ref().map(|elem| ContextSpecific {
tag_number: EXCLUDED_SUBTREES_TAG,
tag_mode: TagMode::Implicit,
value: elem.clone(),
}),
])
}
}

/// GeneralSubtrees as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension.
///
/// ```text
Expand Down
268 changes: 268 additions & 0 deletions x509/src/trust_anchor_format.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
//! Trust anchor-related structures as defined in RFC 5914

use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints};
use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String};
use der::{
DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Sequence, Tag,
TagMode, TagNumber,
};
use spki::SubjectPublicKeyInfo;
use x501::name::Name;

/// TrustAnchorInfo ::= SEQUENCE {
/// version TrustAnchorInfoVersion DEFAULT v1,
/// pubKey SubjectPublicKeyInfo,
/// keyId KeyIdentifier,
/// taTitle TrustAnchorTitle OPTIONAL,
/// certPath CertPathControls OPTIONAL,
/// exts \[1\] EXPLICIT Extensions OPTIONAL,
/// taTitleLangTag \[2\] UTF8String OPTIONAL }
///
/// TrustAnchorInfoVersion ::= INTEGER { v1(1) }
///
/// TrustAnchorTitle ::= UTF8String (SIZE (1..64))
#[derive(Clone, Eq, PartialEq)]
pub struct TrustAnchorInfo<'a> {
/// version TrustAnchorInfoVersion DEFAULT v1,
pub version: Option<u8>,

/// pubKey SubjectPublicKeyInfo,
pub pub_key: SubjectPublicKeyInfo<'a>,

/// keyId KeyIdentifier,
pub key_id: OctetString<'a>,

/// taTitle TrustAnchorTitle OPTIONAL,
pub ta_title: Option<Utf8String<'a>>,

/// certPath CertPathControls OPTIONAL,
pub cert_path: Option<CertPathControls<'a>>,

/// exts \[1\] EXPLICIT Extensions OPTIONAL,
pub extensions: Option<Extensions<'a>>,

/// taTitleLangTag \[2\] UTF8String OPTIONAL }
pub ta_title_lang_tag: Option<Utf8String<'a>>,
}

// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> {
// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result<Self> {
impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let version = match decoder.decode()? {
Some(v) => Some(v),
_ => Some(1),
};

let pub_key = decoder.decode()?;
let key_id = decoder.decode()?;
let ta_title = decoder.decode()?;
let cert_path = decoder.decode()?;
let extensions =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)?
.map(|cs| cs.value);
let ta_title_lang_tag =
::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)?
.map(|cs| cs.value);
Ok(Self {
version,
pub_key,
key_id,
ta_title,
cert_path,
extensions,
ta_title_lang_tag,
})
}
}
Comment on lines +50 to +77

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this one use the proc macro now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you left a note here:

Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific

It should probably work now.


const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1);
const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0);
impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> {
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result<T>,
{
#[allow(unused_imports)]
use core::convert::TryFrom;
f(&[
&::der::asn1::OptionalRef(if self.version == Some(1) {
None
} else {
Some(&self.version)
}),
&self.pub_key,
&self.key_id,
&self.ta_title,
&self.cert_path,
&self.extensions.as_ref().map(|exts| ContextSpecific {
tag_number: TAF_EXTENSIONS_TAG,
tag_mode: TagMode::Explicit,
value: exts.clone(),
}),
&self
.ta_title_lang_tag
.as_ref()
.map(|ta_title_lang_tag| ContextSpecific {
tag_number: TA_TITLE_LANG_TAG,
tag_mode: TagMode::Implicit,
value: *ta_title_lang_tag,
}),
])
}
}

impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?;
f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?;
f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?;
f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?;
f.write_fmt(format_args!(
"\tTA title language tag: {:?}\n",
self.ta_title_lang_tag
))?;
f.write_fmt(format_args!(
"\tCertificate path controls: {:?}\n",
self.cert_path
))?;
if let Some(exts) = self.extensions.as_ref() {
for (i, e) in exts.iter().enumerate() {
f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?;
}
} else {
f.write_fmt(format_args!("\tExtensions: None\n"))?;
}
Ok(())
}
}

/// CertPathControls ::= SEQUENCE {
/// taName Name,
/// certificate \[0\] Certificate OPTIONAL,
/// policySet \[1\] CertificatePolicies OPTIONAL,
/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
/// nameConstr \[3\] NameConstraints OPTIONAL,
/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct CertPathControls<'a> {
/// taName Name,
pub ta_name: Name<'a>,

/// certificate \[0\] Certificate OPTIONAL,
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
pub certificate: Option<Certificate<'a>>,

/// policySet \[1\] CertificatePolicies OPTIONAL,
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
pub policy_set: Option<CertificatePolicies<'a>>,

/// policyFlags \[2\] CertPolicyFlags OPTIONAL,
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
pub policy_flags: Option<CertPolicyFlags<'a>>,

/// nameConstr \[3\] NameConstraints OPTIONAL,
#[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")]
pub name_constr: Option<NameConstraints<'a>>,

/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL}
#[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")]
pub path_len_constraint: Option<u32>,
}

/// CertPolicyFlags ::= BIT STRING {
/// inhibitPolicyMapping (0),
/// requireExplicitPolicy (1),
/// inhibitAnyPolicy (2) }
pub type CertPolicyFlags<'a> = BitString<'a>;

/// TrustAnchorChoice ::= CHOICE {
/// certificate Certificate,
/// tbsCert \[1\] EXPLICIT TBSCertificate,
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TrustAnchorChoice<'a> {
/// certificate Certificate,
Certificate(Certificate<'a>),
// Not supporting TBSCertificate option
// tbsCert \[1\] EXPLICIT TBSCertificate,
//TbsCertificate(TBSCertificate<'a>),
/// taInfo \[2\] EXPLICIT TrustAnchorInfo }
TaInfo(TrustAnchorInfo<'a>),
}

//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1);
const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2);

impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> {
fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result<Self> {
let t = decoder.peek_tag()?;
let o = t.octet();
// Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here
match o {
0x30 => {
let cert = decoder.decode()?;
Ok(TrustAnchorChoice::Certificate(cert))
}
// TODO - need DecodeValue on TBSCertificate to support this
// 0xA1 => {
// let on = decoder
// .context_specific::<TBSCertificate<'a>>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?;
// match on {
// Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)),
// _ => Err(ErrorKind::Failed.into()),
// }
// }
0xA2 => {
let on = decoder
.context_specific::<TrustAnchorInfo<'a>>(TAC_TA_INFO_TAG, TagMode::Explicit)?;
match on {
Some(on) => Ok(TrustAnchorChoice::TaInfo(on)),
_ => Err(ErrorKind::Failed.into()),
}
}
_ => Err(ErrorKind::TagUnknown { byte: o }.into()),
}
}
}

impl<'a> EncodeValue for TrustAnchorChoice<'a> {
fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> {
match self {
Self::Certificate(certificate) => certificate.encode(encoder),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encode(encoder),
Self::TaInfo(variant) => variant.encode(encoder),
}
}
fn value_len(&self) -> ::der::Result<::der::Length> {
match self {
Self::Certificate(certificate) => certificate.encoded_len(),
// Self::TbsCertificate(variant) => ContextSpecific {
// tag_number: TAC_TBS_CERTIFICATE_TAG,
// tag_mode: TagMode::Explicit,
// value: variant.clone(),
// }.encoded_len(),
Self::TaInfo(variant) => variant.encoded_len(),
}
}
}

//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag
impl<'a> FixedTag for TrustAnchorChoice<'a> {
const TAG: Tag = ::der::Tag::ContextSpecific {
constructed: true,
number: TAC_TA_INFO_TAG,
};
}

// Not supporting these structures
// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice
//
// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1)
// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
// id-smime(16) id-ct(1) 34 }
Binary file addedx509/tests/examples/eca.der
Binary file not shown.
Binary file addedx509/tests/examples/eca_policies.ta
Binary file not shown.
Binary file addedx509/tests/examples/entrust.der
Binary file not shown.
Binary file addedx509/tests/examples/entrust_dnConstraint.ta
Binary file not shown.
Binary file addedx509/tests/examples/exostar.der
Binary file not shown.
Binary file addedx509/tests/examples/exostar_policyFlags.ta
Binary file not shown.
Binary file addedx509/tests/examples/raytheon.der
Binary file not shown.
Binary file not shown.
Loading