From 1747c61fb16ae6c6e3c0128687f0d422594f1532 Mon Sep 17 00:00:00 2001 From: bkioshn <35752733+bkioshn@users.noreply.github.com> Date: Fri, 6 Dec 2024 21:26:58 +0700 Subject: [PATCH] fix(rust/rbac-registration): extract type in CIP509 to its own file and implement conversion (#97) * fix: extrat type to its own type and impl conversion Signed-off-by: bkioshn * fix: make util public Signed-off-by: bkioshn * fix: make cip19 utils public Signed-off-by: bkioshn * fix: remove uuidv4 and ed25519pubkey type Signed-off-by: bkioshn * chore: fix naming Signed-off-by: bkioshn * fix: naming Signed-off-by: bkioshn * fix: use ShelleyAddress instead of Shelley Payment part Signed-off-by: bkioshn * fix: naming Signed-off-by: bkioshn --------- Signed-off-by: bkioshn --- rust/rbac-registration/Cargo.toml | 2 + .../src/cardano/cip509/mod.rs | 96 +++++-------------- .../src/cardano/cip509/rbac/certs.rs | 6 +- .../src/cardano/cip509/rbac/mod.rs | 11 +-- .../src/cardano/cip509/rbac/pub_key.rs | 44 ++------- .../src/cardano/cip509/types/cert_key_hash.rs | 37 +++++++ .../src/cardano/cip509/types/mod.rs | 4 + .../src/cardano/cip509/types/tx_input_hash.rs | 37 +++++++ .../src/cardano/cip509/utils/mod.rs | 2 +- .../src/cardano/cip509/validation.rs | 2 +- .../src/registration/cardano/mod.rs | 38 ++++---- .../src/registration/cardano/role_data.rs | 8 +- 12 files changed, 141 insertions(+), 146 deletions(-) create mode 100644 rust/rbac-registration/src/cardano/cip509/types/cert_key_hash.rs create mode 100644 rust/rbac-registration/src/cardano/cip509/types/mod.rs create mode 100644 rust/rbac-registration/src/cardano/cip509/types/tx_input_hash.rs diff --git a/rust/rbac-registration/Cargo.toml b/rust/rbac-registration/Cargo.toml index 51600db80..9b1936d68 100644 --- a/rust/rbac-registration/Cargo.toml +++ b/rust/rbac-registration/Cargo.toml @@ -31,6 +31,8 @@ bech32 = "0.11.0" dashmap = "6.1.0" blake2b_simd = "1.0.2" tracing = "0.1.40" +ed25519-dalek = "2.1.1" +uuid = "1.11.0" c509-certificate = { version = "0.0.3", git = "https://github.com/input-output-hk/catalyst-libs.git" , tag = "v0.0.3" } pallas = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" } diff --git a/rust/rbac-registration/src/cardano/cip509/mod.rs b/rust/rbac-registration/src/cardano/cip509/mod.rs index e65d08184..421cf9852 100644 --- a/rust/rbac-registration/src/cardano/cip509/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/mod.rs @@ -5,7 +5,8 @@ // cspell: words pkix pub mod rbac; -pub(crate) mod utils; +pub mod types; +pub mod utils; pub(crate) mod validation; pub mod x509_chunks; @@ -15,6 +16,8 @@ use minicbor::{ }; use pallas::{crypto::hash::Hash, ledger::traverse::MultiEraTx}; use strum_macros::FromRepr; +use types::tx_input_hash::TxInputHash; +use uuid::Uuid; use validation::{ validate_aux, validate_payment_key, validate_role_singing_key, validate_stake_public_key, validate_txn_inputs_hash, @@ -35,7 +38,7 @@ pub const LABEL: u64 = 509; #[derive(Debug, PartialEq, Clone, Default)] pub struct Cip509 { /// `UUIDv4` Purpose . - pub purpose: UuidV4, // (bytes .size 16) + pub purpose: Uuid, // (bytes .size 16) /// Transaction inputs hash. pub txn_inputs_hash: TxInputHash, // bytes .size 16 /// Optional previous transaction ID. @@ -51,15 +54,15 @@ pub struct Cip509 { #[derive(Debug, PartialEq, Clone, Default)] pub struct Cip509Validation { /// Boolean value for the validity of the transaction inputs hash. - pub valid_txn_inputs_hash: bool, + pub is_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, + pub is_valid_aux: bool, + /// Boolean value for the validity of the stake public key. + pub is_valid_stake_public_key: bool, /// Boolean value for the validity of the payment key. - pub valid_payment_key: bool, + pub is_valid_payment_key: bool, /// Boolean value for the validity of the signing key. - pub signing_key: bool, + pub is_valid_signing_key: bool, /// Additional data from the CIP509 validation.. pub additional_data: AdditionalData, } @@ -71,54 +74,6 @@ pub struct AdditionalData { pub precomputed_aux: Vec, } -/// `UUIDv4` representing in 16 bytes. -#[derive(Debug, PartialEq, Clone, Default)] -pub struct UuidV4([u8; 16]); - -impl From<[u8; 16]> for UuidV4 { - fn from(bytes: [u8; 16]) -> Self { - UuidV4(bytes) - } -} - -impl TryFrom> for UuidV4 { - type Error = &'static str; - - fn try_from(vec: Vec) -> Result { - if vec.len() == 16 { - let mut array = [0u8; 16]; - array.copy_from_slice(&vec); - Ok(UuidV4(array)) - } else { - Err("Input Vec must be exactly 16 bytes") - } - } -} - -/// Transaction input hash representing in 16 bytes. -#[derive(Debug, PartialEq, Clone, Default)] -pub struct TxInputHash([u8; 16]); - -impl From<[u8; 16]> for TxInputHash { - fn from(bytes: [u8; 16]) -> Self { - TxInputHash(bytes) - } -} - -impl TryFrom> for TxInputHash { - type Error = &'static str; - - fn try_from(vec: Vec) -> Result { - if vec.len() == 16 { - let mut array = [0u8; 16]; - array.copy_from_slice(&vec); - Ok(TxInputHash(array)) - } else { - Err("Input Vec must be exactly 16 bytes") - } - } -} - /// Enum of CIP509 metadatum with its associated unsigned integer value. #[allow(clippy::module_name_repetitions)] #[derive(FromRepr, Debug, PartialEq)] @@ -147,7 +102,7 @@ impl Decode<'_, ()> for Cip509 { match key { Cip509IntIdentifier::Purpose => { cip509_metadatum.purpose = - UuidV4::try_from(decode_bytes(d, "CIP509 purpose")?).map_err(|_| { + Uuid::try_from(decode_bytes(d, "CIP509 purpose")?).map_err(|_| { decode::Error::message("Invalid data size of Purpose") })?; }, @@ -217,32 +172,31 @@ impl Cip509 { pub fn validate( &self, txn: &MultiEraTx, validation_report: &mut Vec, ) -> Cip509Validation { - let tx_input_validate = + let is_valid_txn_inputs_hash = validate_txn_inputs_hash(self, txn, validation_report).unwrap_or(false); - let (aux_validate, precomputed_aux) = + let (is_valid_aux, 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; - // Validate the role 0 + let mut is_valid_stake_public_key = true; + let mut is_valid_payment_key = true; + let mut is_valid_signing_key = true; if let Some(role_set) = &self.x509_chunks.0.role_set { // Validate only role 0 for role in role_set { if role.role_number == 0 { - stake_key_validate = + is_valid_stake_public_key = validate_stake_public_key(self, txn, validation_report).unwrap_or(false); - payment_key_validate = + is_valid_payment_key = validate_payment_key(txn, role, validation_report).unwrap_or(false); - signing_key = validate_role_singing_key(role, validation_report); + is_valid_signing_key = validate_role_singing_key(role, validation_report); } } } 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, + is_valid_txn_inputs_hash, + is_valid_aux, + is_valid_stake_public_key, + is_valid_payment_key, + is_valid_signing_key, additional_data: AdditionalData { precomputed_aux }, } } diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs index 08a3aa9b8..de34cd392 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/certs.rs @@ -100,11 +100,11 @@ impl Decode<'_, ()> for C509Cert { #[derive(Debug, PartialEq, Clone)] pub struct C509CertInMetadatumReference { /// Transaction output field. - txn_output_field: u8, + pub txn_output_field: u8, /// Transaction output index. - txn_output_index: u64, + pub txn_output_index: u64, /// Optional certificate reference. - cert_ref: Option>, + pub cert_ref: Option>, } impl Decode<'_, ()> for C509CertInMetadatumReference { diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs b/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs index 151110dff..34675d80c 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/mod.rs @@ -15,6 +15,7 @@ use pub_key::SimplePublicKeyType; use role_data::RoleData; use strum_macros::FromRepr; +use super::types::cert_key_hash::CertKeyHash; use crate::utils::decode_helper::{ decode_any, decode_array_len, decode_bytes, decode_helper, decode_map_len, }; @@ -38,16 +39,6 @@ pub struct Cip509RbacMetadata { pub purpose_key_data: HashMap>, } -/// Certificate key hash use in revocation list. -#[derive(Debug, PartialEq, Clone, Default)] -pub struct CertKeyHash([u8; 16]); - -impl From<[u8; 16]> for CertKeyHash { - fn from(bytes: [u8; 16]) -> Self { - CertKeyHash(bytes) - } -} - /// The first valid purpose key. const FIRST_PURPOSE_KEY: u16 = 200; /// The last valid purpose key. diff --git a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs index 2f2f3047d..80320e287 100644 --- a/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs +++ b/rust/rbac-registration/src/cardano/cip509/rbac/pub_key.rs @@ -1,7 +1,7 @@ //! Public key type for RBAC metadata +use ed25519_dalek::VerifyingKey; use minicbor::{decode, Decode, Decoder}; -use pallas::codec::utils::Bytes; use super::tag::KeyTag; use crate::utils::decode_helper::{decode_bytes, decode_tag}; @@ -15,42 +15,7 @@ pub enum SimplePublicKeyType { /// Deleted indicates the key is deleted. Deleted, /// Ed25519 public key. - Ed25519(Ed25519PublicKey), -} - -/// 32 bytes Ed25519 public key. -#[derive(Debug, PartialEq, Clone, Default, Eq, Hash)] -pub struct Ed25519PublicKey([u8; 32]); - -impl From<[u8; 32]> for Ed25519PublicKey { - fn from(bytes: [u8; 32]) -> Self { - Ed25519PublicKey(bytes) - } -} - -impl TryFrom for Ed25519PublicKey { - type Error = &'static str; - - fn try_from(bytes: Bytes) -> Result { - let byte_vec: Vec = bytes.into(); - - if byte_vec.len() != 32 { - return Err("Invalid length for Ed25519 public key: expected 32 bytes."); - } - - let byte_array: [u8; 32] = byte_vec - .try_into() - .map_err(|_| "Failed to convert Vec to [u8; 32]")?; - - Ok(Ed25519PublicKey::from(byte_array)) - } -} - -impl From for Bytes { - fn from(val: Ed25519PublicKey) -> Self { - let vec: Vec = val.0.to_vec(); - Bytes::from(vec) - } + Ed25519(VerifyingKey), } impl Decode<'_, ()> for SimplePublicKeyType { @@ -65,7 +30,10 @@ impl Decode<'_, ()> for SimplePublicKeyType { let mut ed25519 = [0u8; 32]; if bytes.len() == 32 { ed25519.copy_from_slice(&bytes); - Ok(Self::Ed25519(Ed25519PublicKey(ed25519))) + let pubkey = VerifyingKey::from_bytes(&ed25519).map_err(|e| { + decode::Error::message(format!("Failed to convert Ed25519 public key in SimplePublicKeyType {e}")) + })?; + Ok(Self::Ed25519(pubkey)) } else { Err(decode::Error::message(format!( "Invalid length for Ed25519 key, got {}", diff --git a/rust/rbac-registration/src/cardano/cip509/types/cert_key_hash.rs b/rust/rbac-registration/src/cardano/cip509/types/cert_key_hash.rs new file mode 100644 index 000000000..238fa8b78 --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/types/cert_key_hash.rs @@ -0,0 +1,37 @@ +//! Certificate key hash type + +/// Certificate key hash use in revocation list. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct CertKeyHash([u8; 16]); + +impl From<[u8; 16]> for CertKeyHash { + fn from(bytes: [u8; 16]) -> Self { + CertKeyHash(bytes) + } +} + +impl TryFrom> for CertKeyHash { + type Error = &'static str; + + fn try_from(vec: Vec) -> Result { + if vec.len() == 16 { + let mut array = [0u8; 16]; + array.copy_from_slice(&vec); + Ok(CertKeyHash(array)) + } else { + Err("Input Vec must be exactly 16 bytes") + } + } +} + +impl From for Vec { + fn from(val: CertKeyHash) -> Self { + val.0.to_vec() + } +} + +impl From for [u8; 16] { + fn from(val: CertKeyHash) -> Self { + val.0 + } +} diff --git a/rust/rbac-registration/src/cardano/cip509/types/mod.rs b/rust/rbac-registration/src/cardano/cip509/types/mod.rs new file mode 100644 index 000000000..2b0a3811e --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/types/mod.rs @@ -0,0 +1,4 @@ +//! Types use in CIP-509 + +pub mod cert_key_hash; +pub mod tx_input_hash; diff --git a/rust/rbac-registration/src/cardano/cip509/types/tx_input_hash.rs b/rust/rbac-registration/src/cardano/cip509/types/tx_input_hash.rs new file mode 100644 index 000000000..bcc0c0bc4 --- /dev/null +++ b/rust/rbac-registration/src/cardano/cip509/types/tx_input_hash.rs @@ -0,0 +1,37 @@ +//! Transaction input hash type + +/// Transaction input hash representing in 16 bytes. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct TxInputHash([u8; 16]); + +impl From<[u8; 16]> for TxInputHash { + fn from(bytes: [u8; 16]) -> Self { + TxInputHash(bytes) + } +} + +impl TryFrom> for TxInputHash { + type Error = &'static str; + + fn try_from(vec: Vec) -> Result { + if vec.len() == 16 { + let mut array = [0u8; 16]; + array.copy_from_slice(&vec); + Ok(TxInputHash(array)) + } else { + Err("Input Vec must be exactly 16 bytes") + } + } +} + +impl From for Vec { + fn from(val: TxInputHash) -> Self { + val.0.to_vec() + } +} + +impl From for [u8; 16] { + fn from(val: TxInputHash) -> Self { + val.0 + } +} diff --git a/rust/rbac-registration/src/cardano/cip509/utils/mod.rs b/rust/rbac-registration/src/cardano/cip509/utils/mod.rs index 27d0e5205..54e683f16 100644 --- a/rust/rbac-registration/src/cardano/cip509/utils/mod.rs +++ b/rust/rbac-registration/src/cardano/cip509/utils/mod.rs @@ -1,3 +1,3 @@ //! Utility functions for CIP-509 -pub(crate) mod cip19; +pub mod cip19; diff --git a/rust/rbac-registration/src/cardano/cip509/validation.rs b/rust/rbac-registration/src/cardano/cip509/validation.rs index 7870f530e..d549c4d45 100644 --- a/rust/rbac-registration/src/cardano/cip509/validation.rs +++ b/rust/rbac-registration/src/cardano/cip509/validation.rs @@ -95,7 +95,7 @@ pub(crate) fn validate_txn_inputs_hash( return None; }, }; - Some(TxInputHash(inputs_hash) == cip509.txn_inputs_hash) + Some(TxInputHash::from(inputs_hash) == cip509.txn_inputs_hash) } else { validation_report.push(format!("{function_name}, Unsupported transaction era for")); None diff --git a/rust/rbac-registration/src/registration/cardano/mod.rs b/rust/rbac-registration/src/registration/cardano/mod.rs index b18adc4a1..eeac8dd42 100644 --- a/rust/rbac-registration/src/registration/cardano/mod.rs +++ b/rust/rbac-registration/src/registration/cardano/mod.rs @@ -8,10 +8,11 @@ use std::{collections::HashMap, sync::Arc}; use anyhow::bail; use c509_certificate::c509::C509; +use ed25519_dalek::VerifyingKey; use pallas::{ crypto::hash::Hash, ledger::{ - addresses::{Address, ShelleyAddress, ShelleyPaymentPart}, + addresses::{Address, ShelleyAddress}, traverse::MultiEraTx, }, network::miniprotocols::Point, @@ -20,16 +21,17 @@ use payment_history::PaymentHistory; use point_tx_idx::PointTxIdx; use role_data::RoleData; use tracing::error; +use uuid::Uuid; use crate::{ cardano::cip509::{ self, rbac::{ certs::{C509Cert, X509DerCert}, - pub_key::{Ed25519PublicKey, SimplePublicKeyType}, - CertKeyHash, + pub_key::SimplePublicKeyType, }, - Cip509, Cip509Validation, UuidV4, + types::cert_key_hash::CertKeyHash, + Cip509, Cip509Validation, }, utils::general::decremented_index, }; @@ -94,7 +96,7 @@ impl RegistrationChain { /// Get a list of purpose for this registration chain. #[must_use] - pub fn purpose(&self) -> &[UuidV4] { + pub fn purpose(&self) -> &[Uuid] { &self.inner.purpose } @@ -112,7 +114,7 @@ impl RegistrationChain { /// Get the map of index in array to point, transaction index, and public key. #[must_use] - pub fn simple_keys(&self) -> &HashMap { + pub fn simple_keys(&self) -> &HashMap { &self.inner.simple_keys } @@ -141,7 +143,7 @@ struct RegistrationChainInner { /// The current transaction ID hash (32 bytes) current_tx_id_hash: Hash<32>, /// List of purpose for this registration chain - purpose: Vec, + purpose: Vec, // RBAC /// Map of index in array to point, transaction index, and x509 certificate. @@ -149,7 +151,7 @@ struct RegistrationChainInner { /// Map of index in array to point, transaction index, and c509 certificate. c509_certs: HashMap, /// Map of index in array to point, transaction index, and public key. - simple_keys: HashMap, + simple_keys: HashMap, /// List of point, transaction index, and certificate key hash. revocations: Vec<(PointTxIdx, CertKeyHash)>, @@ -292,11 +294,11 @@ 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 + validation_data.is_valid_aux + && validation_data.is_valid_txn_inputs_hash + && validation_data.is_valid_stake_public_key + && validation_data.is_valid_payment_key + && validation_data.is_valid_signing_key } /// Process x509 certificate for chain root. @@ -389,13 +391,13 @@ fn update_c509_certs( /// Process public keys for chain root. fn chain_root_public_keys( pub_keys: Option>, point_tx_idx: &PointTxIdx, -) -> HashMap { +) -> HashMap { let mut map = HashMap::new(); if let Some(key_list) = pub_keys { for (idx, key) in key_list.iter().enumerate() { // Chain root, expect only the public key not undefined or delete if let cip509::rbac::pub_key::SimplePublicKeyType::Ed25519(key) = key { - map.insert(idx, (point_tx_idx.clone(), key.clone())); + map.insert(idx, (point_tx_idx.clone(), *key)); } } } @@ -420,7 +422,7 @@ fn update_public_keys( cip509::rbac::pub_key::SimplePublicKeyType::Ed25519(key) => { new_inner .simple_keys - .insert(idx, (point_tx_idx.clone(), key.clone())); + .insert(idx, (point_tx_idx.clone(), *key)); }, } } @@ -524,7 +526,7 @@ fn update_role_data( /// Helper function for retrieving the Shelley address from the transaction. fn get_payment_addr_from_tx( txn: &MultiEraTx, payment_key_ref: Option, -) -> anyhow::Result> { +) -> anyhow::Result> { // The index should exist since it pass the basic validation if let Some(key_ref) = payment_key_ref { if let MultiEraTx::Conway(tx) = txn { @@ -541,7 +543,7 @@ fn get_payment_addr_from_tx( Address::from_bytes(&o.address).map_err(|e| anyhow::anyhow!(e))?; if let Address::Shelley(addr) = address { - return Ok(Some(addr.payment().clone())); + return Ok(Some(addr.clone())); } bail!("Unsupported address type in payment key reference"); }, diff --git a/rust/rbac-registration/src/registration/cardano/role_data.rs b/rust/rbac-registration/src/registration/cardano/role_data.rs index b6b4d947d..3df6a05df 100644 --- a/rust/rbac-registration/src/registration/cardano/role_data.rs +++ b/rust/rbac-registration/src/registration/cardano/role_data.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; -use pallas::ledger::addresses::ShelleyPaymentPart; +use pallas::ledger::addresses::ShelleyAddress; use crate::cardano::cip509::rbac::role_data::KeyLocalRef; @@ -14,7 +14,7 @@ pub struct RoleData { /// An encryption keys to the data within registration. encryption_ref: Option, /// A payment key where reward will be distributed to. - payment_key: Option, + payment_key: Option, /// Map of role extended data (10-99) to its data role_extended_data: HashMap>, } @@ -23,7 +23,7 @@ impl RoleData { /// Create an instance of role data. pub(crate) fn new( signing_key_ref: Option, encryption_ref: Option, - payment_key: Option, role_extended_data: HashMap>, + payment_key: Option, role_extended_data: HashMap>, ) -> Self { RoleData { signing_key_ref, @@ -47,7 +47,7 @@ impl RoleData { /// Get the payment key. #[must_use] - pub fn payment_key(&self) -> &Option { + pub fn payment_key(&self) -> &Option { &self.payment_key }