Skip to content

Commit

Permalink
Feat/reader auth cn (#79)
Browse files Browse the repository at this point in the history
* rebase onto feat/mdoc-auth

* rebase and use mdoc-auth functions

* wip experiment with cert building

* small clean up

* Fix inconsistency. (#78)

* validated request improvements

---------

Co-authored-by: Jacob <jacob.ward@spruceid.com>
  • Loading branch information
justAnIdentity and cobward authored Mar 20, 2024
1 parent eb591a0 commit 2bfcf8c
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 54 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ path = "src/bin/utils.rs"

[dependencies]
anyhow = "1.0"
ecdsa = { version = "0.16.0", features = ["serde"] }
ecdsa = { version = "0.16.0", features = ["serde","verifying"] }
p256 = { version = "0.13.0", features = ["serde", "ecdh"] }
p384 = { version = "0.13.0", features = ["serde", "ecdh"] }
rand = { version = "0.8.5", features = ["getrandom"] }
Expand All @@ -39,12 +39,12 @@ async-signature = "0.3.0"
#tracing = "0.1"
base64 = "0.13"
pem-rfc7468 = "0.7.0"
x509-cert = { version = "0.2.3", features = ["std"] }
const-oid = "0.9.2"
x509-cert = { version = "0.2.4", features = ["pem", "builder"] }
ssi-jwk = { version = "0.1" }
isomdl-macros = { version = "0.1.0", path = "macros" }
clap = { version = "4", features = ["derive"] }
clap-stdin = "0.2.1"
const-oid = "0.9.2"
der = { version = "0.7", features = ["std", "derive", "alloc"] }
hex = "0.4.3"
asn1-rs = { version = "0.5.2", features = ["bits"] }
Expand Down
1 change: 1 addition & 0 deletions src/definitions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod mso;
pub mod namespaces;
pub mod session;
pub mod traits;
pub mod validated_request;
pub mod validated_response;
pub mod validity_info;
pub mod x509;
Expand Down
18 changes: 18 additions & 0 deletions src/definitions/validated_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::{definitions::ValidationErrors, presentation::device::RequestedItems};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Default)]
pub struct ValidatedRequest {
pub items_requests: RequestedItems,
pub common_name: Option<String>,
pub reader_authentication: Status,
pub errors: ValidationErrors,
}

#[derive(Serialize, Deserialize, Default)]
pub enum Status {
#[default]
Unchecked,
Invalid,
Valid,
}
15 changes: 8 additions & 7 deletions src/definitions/x509/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ const OID_BASIC_CONSTRAINTS: &str = "2.5.29.19";
const OID_CRL_DISTRIBUTION_POINTS: &str = "2.5.29.31";
const OID_EXTENDED_KEY_USAGE: &str = "2.5.29.37";

// A Specific OID defined for mDL signing
const VALUE_EXTENDED_KEY_USAGE: &str = "1.0.18013.5.1.2";

// -- 18013-5 IACA SPECIFIC ROOT EXTENSION VALUE CHECKS -- //
// Key Usage: 5, 6 (keyCertSign, crlSign)
// Basic Constraints: Pathlen:0
Expand Down Expand Up @@ -112,7 +109,10 @@ pub fn validate_iaca_root_extensions(root_extensions: Vec<Extension>) -> Vec<Err
x509_errors
}

pub fn validate_iaca_signer_extensions(leaf_extensions: Vec<Extension>) -> Vec<Error> {
pub fn validate_iaca_signer_extensions(
leaf_extensions: Vec<Extension>,
value_extended_key_usage: &str,
) -> Vec<Error> {
let disallowed = iaca_disallowed_x509_extensions();
let mut x509_errors: Vec<Error> = vec![];
let mut errors: Vec<Error> = vec![];
Expand Down Expand Up @@ -148,10 +148,11 @@ pub fn validate_iaca_signer_extensions(leaf_extensions: Vec<Extension>) -> Vec<E
// Extended Key Usage 2.5.29.37
if let Some(extended_key_usage) = leaf_crit_extensions
.iter()
.find(|ext| ext.extn_id.to_string() == *OID_EXTENDED_KEY_USAGE)
.find(|ext| ext.extn_id.to_string() == OID_EXTENDED_KEY_USAGE)
{
x509_errors.append(&mut validate_extended_key_usage(
extended_key_usage.extn_value.as_bytes().to_vec(),
value_extended_key_usage,
));
} else {
x509_errors.push(Error::ValidationError(
Expand Down Expand Up @@ -254,14 +255,14 @@ pub fn validate_root_key_usage(bytes: Vec<u8>) -> Vec<Error> {
/* Extended key usage in the signer certificate should be set to this OID meant specifically for mDL signing.
Note that this value will be different for other types of mdocs */

pub fn validate_extended_key_usage(bytes: Vec<u8>) -> Vec<Error> {
pub fn validate_extended_key_usage(bytes: Vec<u8>, value_extended_key_usage: &str) -> Vec<Error> {
let extended_key_usage = ExtendedKeyUsage::from_der(&bytes);
match extended_key_usage {
Ok(eku) => {
if !eku
.0
.into_iter()
.any(|oid| oid.to_string() == VALUE_EXTENDED_KEY_USAGE)
.any(|oid| oid.to_string() == value_extended_key_usage)
{
return vec![Error::ValidationError(
"Invalid extended key usage, expected: 1.0.18013.5.1.2".to_string(),
Expand Down
37 changes: 31 additions & 6 deletions src/definitions/x509/trust_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ use x509_cert::attr::AttributeTypeAndValue;
use x509_cert::certificate::CertificateInner;
use x509_cert::der::Decode;

const MDOC_VALUE_EXTENDED_KEY_USAGE: &str = "1.0.18013.5.1.2";
const READER_VALUE_EXTENDED_KEY_USAGE: &str = "1.0.18013.5.1.6";

#[derive(Serialize, Deserialize, Clone)]
pub enum TrustAnchor {
Iaca(X509),
Aamva(X509),
Custom(X509, ValidationRuleSet),
IacaReader(X509),
}

#[derive(Serialize, Deserialize, Clone)]
Expand Down Expand Up @@ -128,20 +132,27 @@ pub fn validate_with_ruleset(
}
};
}
TrustAnchor::Custom(certificate, ruleset) => {
TrustAnchor::IacaReader(certificate) => {
let rule_set = ValidationRuleSet {
distinguished_names: vec!["2.5.4.3".to_string()],
typ: RuleSetType::ReaderAuth,
};
match x509_cert::Certificate::from_der(&certificate.bytes) {
Ok(root_certificate) => {
errors.append(&mut process_validation_outcomes(
leaf_certificate,
root_certificate,
ruleset,
rule_set,
));
}
Err(e) => {
errors.push(e.into());
}
};
}
TrustAnchor::Custom(_certificate, _ruleset) => {
//TODO
}
}
errors
}
Expand Down Expand Up @@ -267,7 +278,10 @@ fn apply_ruleset(
//Under the IACA ruleset, the values for S or ST should be the same in subject and issuer if they are present in both
RuleSetType::IACA => {
let mut extension_errors = validate_iaca_root_extensions(root_extensions);
extension_errors.append(&mut validate_iaca_signer_extensions(leaf_extensions));
extension_errors.append(&mut validate_iaca_signer_extensions(
leaf_extensions,
MDOC_VALUE_EXTENDED_KEY_USAGE,
));
for dn in leaf_distinguished_names {
if dn.oid.to_string() == *"2.5.4.8" {
let state_or_province =
Expand All @@ -288,7 +302,10 @@ fn apply_ruleset(
//Under the AAMVA ruleset, S/ST is mandatory and should be the same in the subject and issuer
RuleSetType::AAMVA => {
let mut extension_errors = validate_iaca_root_extensions(root_extensions);
extension_errors.append(&mut validate_iaca_signer_extensions(leaf_extensions));
extension_errors.append(&mut validate_iaca_signer_extensions(
leaf_extensions,
MDOC_VALUE_EXTENDED_KEY_USAGE,
));
for dn in leaf_distinguished_names {
let Some(_root_dn) = root_distinguished_names.iter().find(|r| r == &&dn) else {
return Err(X509Error::ValidationError(format!("Mismatch between supplied certificate issuer attribute: {:?} and the trust anchor registry.", dn.value)));
Expand All @@ -306,8 +323,10 @@ fn apply_ruleset(
}
RuleSetType::ReaderAuth => {
//TODO
Err(X509Error::ValidationError(
"Unimplemented ruleset".to_string(),

Ok(validate_iaca_signer_extensions(
leaf_extensions,
READER_VALUE_EXTENDED_KEY_USAGE,
))
}
}
Expand Down Expand Up @@ -344,6 +363,12 @@ pub fn find_anchor(
Err(_) => false,
}
}
TrustAnchor::IacaReader(certificate) => {
match x509_cert::Certificate::from_der(&certificate.bytes) {
Ok(root_cert) => root_cert.tbs_certificate.subject == leaf_issuer,
Err(_) => false,
}
}
})
else {
return Err(X509Error::ValidationError(
Expand Down
5 changes: 2 additions & 3 deletions src/definitions/x509/x5chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl X509 {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct X5Chain(NonEmptyVec<X509>);

impl From<NonEmptyVec<X509>> for X5Chain {
Expand Down Expand Up @@ -161,8 +161,7 @@ impl X5Chain {
}

//validate the last certificate in the chain against trust anchor
let last_in_chain = x5chain.last();
if let Some(x509) = last_in_chain {
if let Some(x509) = x5chain.last() {
match x509_cert::Certificate::from_der(&x509.bytes) {
Ok(cert) => {
// if the issuer of the signer certificate is known in the trust anchor registry, do the validation.
Expand Down
Loading

0 comments on commit 2bfcf8c

Please sign in to comment.