Skip to content

Commit

Permalink
Introduce Cip0134UriList type
Browse files Browse the repository at this point in the history
  • Loading branch information
stanislav-tkach committed Dec 15, 2024
1 parent 0aeadb9 commit fa8bf59
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 164 deletions.
2 changes: 1 addition & 1 deletion rust/rbac-registration/src/cardano/cip509/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use x509_chunks::X509Chunks;
use super::transaction::witness::TxWitness;
use crate::utils::{
decode_helper::{decode_bytes, decode_helper, decode_map_len},
general::{decode_utf8, decremented_index},
general::decremented_index,
hashing::{blake2b_128, blake2b_256},
};

Expand Down
8 changes: 8 additions & 0 deletions rust/rbac-registration/src/cardano/cip509/utils/cip134/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Utilities for [CIP-134] (Cardano URIs - Address Representation).
//!
//! [CIP-134]: https://github.com/cardano-foundation/CIPs/tree/master/CIP-0134
pub use self::{uri::Cip0134Uri, uri_list::Cip0134UriList};

mod uri;
mod uri_list;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Utility functions for CIP-0134 address.
//! An URI in the CIP-0134 format.
// Ignore URIs that are used in tests and doc-examples.
// cSpell:ignoreRegExp web\+cardano:.+
Expand All @@ -14,6 +14,7 @@ use pallas::ledger::addresses::Address;
///
/// [proposal]: https://github.com/cardano-foundation/CIPs/pull/888
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct Cip0134Uri {
/// A URI string.
uri: String,
Expand Down
177 changes: 177 additions & 0 deletions rust/rbac-registration/src/cardano/cip509/utils/cip134/uri_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//! A list of [`Cip0134Uri`].
use std::sync::Arc;

use anyhow::{anyhow, Result};
use c509_certificate::{
extensions::{alt_name::GeneralNamesOrText, extension::ExtensionValue},
general_names::general_name::{GeneralNameTypeRegistry, GeneralNameValue},
C509ExtensionType,
};
use der_parser::der::parse_der_sequence;
use pallas::ledger::traverse::MultiEraTx;
use tracing::debug;
use x509_cert::der::{oid::db::rfc5912::ID_CE_SUBJECT_ALT_NAME, Decode};

use crate::{
cardano::cip509::{
rbac::{
certs::{C509Cert, X509DerCert},
Cip509RbacMetadata,
},
utils::Cip0134Uri,
validation::URI,
Cip509,
},
utils::general::decode_utf8,
};

/// A list of [`Cip0134Uri`].
///
/// This structure uses [`Arc`] internally, so it is cheap to clone.
#[derive(Debug, Clone)]
#[allow(clippy::module_name_repetitions)]
pub struct Cip0134UriList {
/// An internal list of URIs.
uris: Arc<[Cip0134Uri]>,
}

impl Cip0134UriList {
/// Creates a new `Cip0134UriList` instance from the given `Cip509`.
///
/// # Errors
/// - Unsupported transaction era.
pub fn new(cip509: &Cip509, tx: &MultiEraTx) -> Result<Self> {
if !matches!(tx, MultiEraTx::Conway(_)) {
return Err(anyhow!("Unsupported transaction era ({})", tx.era()));
}

let metadata = &cip509.x509_chunks.0;
let mut uris = process_x509_certificates(metadata);
uris.extend(process_c509_certificates(metadata));

Ok(Self { uris: uris.into() })
}

/// Returns an iterator over the contained Cip0134 URIs.
pub fn iter(&self) -> impl Iterator<Item = &Cip0134Uri> {
self.uris.iter()
}

/// Returns a slice with all URIs in the list.
#[must_use]
pub fn as_slice(&self) -> &[Cip0134Uri] {
&self.uris
}
}

/// Iterates over X509 certificates and extracts CIP-0134 URIs.
fn process_x509_certificates(metadata: &Cip509RbacMetadata) -> Vec<Cip0134Uri> {
let mut result = Vec::new();

for cert in metadata.x509_certs.iter().flatten() {
let X509DerCert::X509Cert(cert) = cert else {
continue;
};
let cert = match x509_cert::Certificate::from_der(cert) {
Ok(cert) => cert,
Err(e) => {
debug!("Failed to decode x509 certificate DER: {e:?}");
continue;
},
};
// Find the "subject alternative name" extension.
let Some(extension) = cert
.tbs_certificate
.extensions
.iter()
.flatten()
.find(|e| e.extn_id == ID_CE_SUBJECT_ALT_NAME)
else {
continue;
};
let der = match parse_der_sequence(extension.extn_value.as_bytes()) {
Ok((_, der)) => der,
Err(e) => {
debug!("Failed to parse DER sequence for Subject Alternative Name ({extension:?}): {e:?}");
continue;
},
};
for data in der.ref_iter() {
if data.header.raw_tag() != Some(&[URI]) {
continue;
}
let content = match data.content.as_slice() {
Ok(c) => c,
Err(e) => {
debug!("Unable to process content for {data:?}: {e:?}");
continue;
},
};
let address = match decode_utf8(content) {
Ok(a) => a,
Err(e) => {
debug!("Failed to decode content of {data:?}: {e:?}");
continue;
},
};
match Cip0134Uri::parse(&address) {
Ok(a) => result.push(a),
Err(e) => {
debug!("Failed to parse CIP-0134 address ({address}): {e:?}");
},
}
}
}

result
}

/// Iterates over C509 certificates and extracts CIP-0134 URIs.
fn process_c509_certificates(metadata: &Cip509RbacMetadata) -> Vec<Cip0134Uri> {
let mut result = Vec::new();

for cert in metadata.c509_certs.iter().flatten() {
let cert = match cert {
C509Cert::C509Certificate(c) => c,
C509Cert::C509CertInMetadatumReference(_) => {
debug!("Ignoring unsupported metadatum reference");
continue;
},
_ => continue,
};

for extension in cert.tbs_cert().extensions().extensions() {
if extension.registered_oid().c509_oid().oid()
!= &C509ExtensionType::SubjectAlternativeName.oid()
{
continue;
}
let ExtensionValue::AlternativeName(alt_name) = extension.value() else {
debug!("Unexpected extension value type for {extension:?}");
continue;
};
let GeneralNamesOrText::GeneralNames(gen_names) = alt_name.general_name() else {
debug!("Unexpected general name type: {extension:?}");
continue;
};
for name in gen_names.general_names() {
if *name.gn_type() != GeneralNameTypeRegistry::UniformResourceIdentifier {
continue;
}
let GeneralNameValue::Text(address) = name.gn_value() else {
debug!("Unexpected general name value format: {name:?}");
continue;
};
match Cip0134Uri::parse(address) {
Ok(a) => result.push(a),
Err(e) => {
debug!("Failed to parse CIP-0134 address ({address}): {e:?}");
},
}
}
}
}

result
}
2 changes: 1 addition & 1 deletion rust/rbac-registration/src/cardano/cip509/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Utility functions for CIP-509
pub mod cip19;
pub use cip134::Cip0134Uri;
pub use cip134::{Cip0134Uri, Cip0134UriList};

mod cip134;
Loading

0 comments on commit fa8bf59

Please sign in to comment.