diff --git a/rust/rbac-registration/Cargo.toml b/rust/rbac-registration/Cargo.toml index 22d03698c..51600db80 100644 --- a/rust/rbac-registration/Cargo.toml +++ b/rust/rbac-registration/Cargo.toml @@ -2,7 +2,7 @@ name = "rbac-registration" description = "Role Based Access Control Registration" keywords = ["cardano", "catalyst", "rbac registration"] -version = "0.0.1" +version = "0.0.2" authors = [ "Arissara Chotivichit " ] diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index e88528f46..e65d08184 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -46,6 +46,31 @@ pub struct Cip509 { pub validation_signature: Vec, // bytes size (1..64) } +/// Validation value for CIP509 metadatum. +#[allow(clippy::struct_excessive_bools, clippy::module_name_repetitions)] +#[derive(Debug, PartialEq, Clone, Default)] +pub struct Cip509Validation { + /// Boolean value for the validity of the transaction inputs hash. + pub valid_txn_inputs_hash: bool, + /// Boolean value for the validity of the auxiliary data. + pub valid_aux: bool, + /// Boolean value for the validity of the public key. + pub valid_public_key: bool, + /// Boolean value for the validity of the payment key. + pub valid_payment_key: bool, + /// Boolean value for the validity of the signing key. + pub signing_key: bool, + /// Additional data from the CIP509 validation.. + pub additional_data: AdditionalData, +} + +/// Additional data from the CIP509 validation. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct AdditionalData { + /// Bytes of precomputed auxiliary data. + pub precomputed_aux: Vec, +} + /// `UUIDv4` representing in 16 bytes. #[derive(Debug, PartialEq, Clone, Default)] pub struct UuidV4([u8; 16]); @@ -189,10 +214,13 @@ impl Cip509 { /// # Parameters /// * `txn` - Transaction data was attached to and to be validated/decoded against. /// * `validation_report` - Validation report to store the validation result. - pub fn validate(&self, txn: &MultiEraTx, validation_report: &mut Vec) -> bool { + pub fn validate( + &self, txn: &MultiEraTx, validation_report: &mut Vec, + ) -> Cip509Validation { let tx_input_validate = validate_txn_inputs_hash(self, txn, validation_report).unwrap_or(false); - let aux_validate = validate_aux(txn, validation_report).unwrap_or(false); + let (aux_validate, precomputed_aux) = + validate_aux(txn, validation_report).unwrap_or_default(); let mut stake_key_validate = true; let mut payment_key_validate = true; let mut signing_key = true; @@ -209,10 +237,13 @@ impl Cip509 { } } } - tx_input_validate - && aux_validate - && stake_key_validate - && payment_key_validate - && signing_key + Cip509Validation { + valid_txn_inputs_hash: tx_input_validate, + valid_aux: aux_validate, + valid_public_key: stake_key_validate, + valid_payment_key: payment_key_validate, + signing_key, + additional_data: AdditionalData { precomputed_aux }, + } } } diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 1be2dee1f..7870f530e 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -41,6 +41,7 @@ use super::{ utils::cip19::{compare_key_hash, extract_cip19_hash, extract_key_hash}, Cip509, TxInputHash, TxWitness, }; +use crate::utils::general::zero_out_last_n_bytes; /// Context-specific primitive type with tag number 6 (`raw_tag` 134) for /// uniform resource identifier (URI) in the subject alternative name extension. @@ -282,7 +283,10 @@ pub(crate) fn validate_stake_public_key( // ------------------------ Validate Aux ------------------------ /// Validate the auxiliary data with the auxiliary data hash in the transaction body. -pub(crate) fn validate_aux(txn: &MultiEraTx, validation_report: &mut Vec) -> Option { +/// Also return the pre-computed hash where the validation signature (99) set to +pub(crate) fn validate_aux( + txn: &MultiEraTx, validation_report: &mut Vec, +) -> Option<(bool, Vec)> { let function_name = "Validate Aux"; // CIP-0509 should only be in conway era @@ -313,13 +317,19 @@ pub(crate) fn validate_aux(txn: &MultiEraTx, validation_report: &mut Vec } /// Helper function for auxiliary data validation. +/// Also compute The pre-computed hash. fn validate_aux_helper( original_aux: &[u8], aux_data_hash: &Bytes, validation_report: &mut Vec, -) -> Option { +) -> Option<(bool, Vec)> { + let mut vec_aux = original_aux.to_vec(); + + // Pre-computed aux with the last 64 bytes set to zero + zero_out_last_n_bytes(&mut vec_aux, 64); + // Compare the hash match blake2b_256(original_aux) { Ok(original_hash) => { - return Some(aux_data_hash.as_ref() == original_hash); + return Some((aux_data_hash.as_ref() == original_hash, vec_aux)); }, Err(e) => { validation_report.push(format!("Cannot hash auxiliary data {e}")); @@ -539,7 +549,7 @@ mod tests { .expect("Failed to get transaction index"); validate_aux(tx, &mut validation_report); - assert!(validate_aux(tx, &mut validation_report).unwrap()); + assert!(validate_aux(tx, &mut validation_report).unwrap().0); } #[test] diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index 3895d8971..b18adc4a1 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -29,7 +29,7 @@ use crate::{ pub_key::{Ed25519PublicKey, SimplePublicKeyType}, CertKeyHash, }, - Cip509, UuidV4, + Cip509, Cip509Validation, UuidV4, }, utils::general::decremented_index, }; @@ -184,8 +184,10 @@ impl RegistrationChainInner { } let mut validation_report = Vec::new(); + let validation_data = cip509.validate(txn, &mut validation_report); + // Do the CIP509 validation, ensuring the basic validation pass. - if !cip509.validate(txn, &mut validation_report) { + if !is_valid_cip509(&validation_data) { // Log out the error if any error!("CIP509 validation failed: {:?}", validation_report); bail!("CIP509 validation failed, {:?}", validation_report); @@ -240,8 +242,10 @@ impl RegistrationChainInner { let mut new_inner = self.clone(); let mut validation_report = Vec::new(); + let validation_data = cip509.validate(txn, &mut validation_report); + // Do the CIP509 validation, ensuring the basic validation pass. - if !cip509.validate(txn, &mut validation_report) { + if !is_valid_cip509(&validation_data) { error!("CIP509 validation failed: {:?}", validation_report); bail!("CIP509 validation failed, {:?}", validation_report); } @@ -286,6 +290,15 @@ impl RegistrationChainInner { } } +/// Check if the CIP509 is valid. +fn is_valid_cip509(validation_data: &Cip509Validation) -> bool { + validation_data.valid_aux + && validation_data.valid_txn_inputs_hash + && validation_data.valid_public_key + && validation_data.valid_payment_key + && validation_data.signing_key +} + /// Process x509 certificate for chain root. fn chain_root_x509_certs( x509_certs: Option>, point_tx_idx: &PointTxIdx, diff --git a/rust/rbac-registration/src/utils/general.rs b/rust/rbac-registration/src/utils/general.rs index 3596b6680..0af72c7d9 100644 --- a/rust/rbac-registration/src/utils/general.rs +++ b/rust/rbac-registration/src/utils/general.rs @@ -25,3 +25,10 @@ pub(crate) fn decode_utf8(content: &[u8]) -> anyhow::Result { ) }) } + +/// Zero out the last n bytes +pub(crate) fn zero_out_last_n_bytes(vec: &mut [u8], n: usize) { + if let Some(slice) = vec.get_mut(vec.len().saturating_sub(n)..) { + slice.fill(0); + } +}