Skip to content

Commit

Permalink
feat(rust/rbac-registration): RBAC registration (#85)
Browse files Browse the repository at this point in the history
* feat: move cip509 from cardano chain follower

Signed-off-by: bkioshn <[email protected]>

* fix: remove key ref and use key local ref

Signed-off-by: bkioshn <[email protected]>

* fix: structure of x509, c509, and public key

Signed-off-by: bkioshn <[email protected]>

* fix: validation

Signed-off-by: bkioshn <[email protected]>

* fix: update x509 and c509 certs

Signed-off-by: bkioshn <[email protected]>

* fix: key local ref

* fix: c509 cert

Signed-off-by: bkioshn <[email protected]>

* fix: to string

Signed-off-by: bkioshn <[email protected]>

* fix: rearrange utils

Signed-off-by: bkioshn <[email protected]>

* fix: cargo toml

Signed-off-by: bkioshn <[email protected]>

* fix: add new type + comment

Signed-off-by: bkioshn <[email protected]>

* fix: linter

Signed-off-by: bkioshn <[email protected]>

* fix: smart pointer

Signed-off-by: bkioshn <[email protected]>

* chore: fix format

Signed-off-by: bkioshn <[email protected]>

* fix: cleanup cargo.toml

Signed-off-by: bkioshn <[email protected]>

* fix: add rbac-registration to earthly rust

* fix: move cip509 validation

Signed-off-by: bkioshn <[email protected]>

* fix: rust ci

Signed-off-by: bkioshn <[email protected]>

* feat: add role signing key validation

Signed-off-by: bkioshn <[email protected]>

* fix: add Bytes conversion for Pubkey

Signed-off-by: bkioshn <[email protected]>

* fix: error return

* fix: use Hash from pallas for tx hash

Signed-off-by: bkioshn <[email protected]>

* fix: format

Signed-off-by: bkioshn <[email protected]>

* fix: bytes to pubkey conversion

* fix: add Bytes conversion for Pubkey

Signed-off-by: bkioshn <[email protected]>

* fix: add rbac-reg to semantic pull request

* fix: test data

Signed-off-by: bkioshn <[email protected]>

* fix: signing and encryption key

Signed-off-by: bkioshn <[email protected]>

* fix: format

Signed-off-by: bkioshn <[email protected]>

* fix: witness test data to be compatible with new test data

Signed-off-by: bkioshn <[email protected]>

* fix: validate stake public key witness and add test for role signing key

Signed-off-by: bkioshn <[email protected]>

* chore: doc about context specific

Signed-off-by: bkioshn <[email protected]>

* chore: fix format

Signed-off-by: bkioshn <[email protected]>

* fix: remove test since cip509 decode are done in validation test

* fix: test

* fix: test compression data

Signed-off-by: bkioshn <[email protected]>

* feat(rust/rbac-registration): add cardano registration chain (#90)

* feat: add cardano registration chain

Signed-off-by: bkioshn <[email protected]>

* feat: add wrapper to registration chain inner

Signed-off-by: bkioshn <[email protected]>

* fix: format

Signed-off-by: bkioshn <[email protected]>

* fix: comment

Signed-off-by: bkioshn <[email protected]>

* fix: signing and encryption key

Signed-off-by: bkioshn <[email protected]>

* chore: fix format

Signed-off-by: bkioshn <[email protected]>

* fix: move struct to its own file and private the inner regis

Signed-off-by: bkioshn <[email protected]>

---------

Signed-off-by: bkioshn <[email protected]>

* fix: remove txn index and change test data to chain root

Signed-off-by: bkioshn <[email protected]>

* chore: fix comment

Signed-off-by: bkioshn <[email protected]>

* fix: remove tx index from cip-509 validate function

Signed-off-by: bkioshn <[email protected]>

* fix: test data

Signed-off-by: bkioshn <[email protected]>

* chore: fix format

* fix: remove txn index from validate

Signed-off-by: bkioshn <[email protected]>

* fix: format

* fix(rust/rbac-registration): Tracking payment key type (#93)

* fix: payment key shelley address and add test for registration chain

Signed-off-by: bkioshn <[email protected]>

* chore: comment

* fix: format

* fix: use ShelleyPaymentPart

Signed-off-by: bkioshn <[email protected]>

* fix: track ShelleyAddress

Signed-off-by: bkioshn <[email protected]>

* fix: remove tracking_payment_key and use tracking_payment_history

Signed-off-by: bkioshn <[email protected]>

---------

Signed-off-by: bkioshn <[email protected]>
Co-authored-by: Steven Johnson <[email protected]>

---------

Signed-off-by: bkioshn <[email protected]>
Co-authored-by: Steven Johnson <[email protected]>
  • Loading branch information
bkioshn and stevenj authored Dec 2, 2024
1 parent e3b9407 commit 149bf11
Show file tree
Hide file tree
Showing 32 changed files with 3,236 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/semantic_pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
rust/vote-tx-v2
rust/cbork
rust/hermes-ipfs
rust/rbac-registration
dart
docs
general
1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"immutable-ledger",
"vote-tx-v1",
"vote-tx-v2",
"rbac-registration",
]

[workspace.package]
Expand Down
3 changes: 2 additions & 1 deletion rust/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ COPY_SRC:
catalyst-voting vote-tx-v1 vote-tx-v2 \
cbork cbork-abnf-parser cbork-cddl-parser \
hermes-ipfs \
rbac-registration \
immutable-ledger .

# builder : Set up our target toolchains, and copy our files.
Expand Down Expand Up @@ -54,7 +55,7 @@ build:
--args1="--libs=c509-certificate --libs=cardano-chain-follower --libs=hermes-ipfs" \
--args2="--libs=cbork-cddl-parser --libs=cbork-abnf-parser" \
--args3="--libs=catalyst-voting --libs=vote-tx-v1 --libs=vote-tx-v2" \
--args4="--bins=cbork/cbork" \
--args4="--bins=cbork/cbork --libs=rbac-registration" \
--args5="--cov_report=$HOME/build/coverage-report.info" \
--output="release/[^\./]+" \
--junit="cat-libs.junit-report.xml" \
Expand Down
36 changes: 36 additions & 0 deletions rust/rbac-registration/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "rbac-registration"
description = "Role Based Access Control Registration"
keywords = ["cardano", "catalyst", "rbac registration"]
version = "0.0.1"
authors = [
"Arissara Chotivichit <[email protected]>"
]
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[lib]
crate-type = ["cdylib", "rlib"]

[lints]
workspace = true

[dependencies]
hex = "0.4.3"
anyhow = "1.0.89"
strum_macros = "0.26.4"
regex = "1.11.0"
minicbor = { version = "0.25.1", features = ["alloc", "derive", "half"] }
brotli = "7.0.0"
zstd = "0.13.2"
x509-cert = "0.2.5"
der-parser = "9.0.0"
bech32 = "0.11.0"
dashmap = "6.1.0"
blake2b_simd = "1.0.2"
tracing = "0.1.40"

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" }
218 changes: 218 additions & 0 deletions rust/rbac-registration/src/cardano/cip509/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
//! Cardano Improvement Proposal 509 (CIP-509) metadata module.
//! Doc Reference: <https://github.com/input-output-hk/catalyst-CIPs/tree/x509-envelope-metadata/CIP-XXXX>
//! CDDL Reference: <https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/x509-envelope.cddl>
// cspell: words pkix

pub mod rbac;
pub(crate) mod utils;
pub(crate) mod validation;
pub mod x509_chunks;

use minicbor::{
decode::{self},
Decode, Decoder,
};
use pallas::{crypto::hash::Hash, ledger::traverse::MultiEraTx};
use strum_macros::FromRepr;
use validation::{
validate_aux, validate_payment_key, validate_role_singing_key, validate_stake_public_key,
validate_txn_inputs_hash,
};
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},
hashing::{blake2b_128, blake2b_256},
};

/// CIP509 label.
pub const LABEL: u64 = 509;

/// CIP509.
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Cip509 {
/// `UUIDv4` Purpose .
pub purpose: UuidV4, // (bytes .size 16)
/// Transaction inputs hash.
pub txn_inputs_hash: TxInputHash, // bytes .size 16
/// Optional previous transaction ID.
pub prv_tx_id: Option<Hash<32>>, // bytes .size 32
/// x509 chunks.
pub x509_chunks: X509Chunks, // chunk_type => [ + x509_chunk ]
/// Validation signature.
pub validation_signature: Vec<u8>, // bytes size (1..64)
}

/// `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<Vec<u8>> for UuidV4 {
type Error = &'static str;

fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
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<Vec<u8>> for TxInputHash {
type Error = &'static str;

fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
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)]
#[repr(u8)]
pub(crate) enum Cip509IntIdentifier {
/// Purpose.
Purpose = 0,
/// Transaction inputs hash.
TxInputsHash = 1,
/// Previous transaction ID.
PreviousTxId = 2,
/// Validation signature.
ValidationSignature = 99,
}

impl Decode<'_, ()> for Cip509 {
fn decode(d: &mut Decoder, ctx: &mut ()) -> Result<Self, decode::Error> {
let map_len = decode_map_len(d, "CIP509")?;
let mut cip509_metadatum = Cip509::default();
for _ in 0..map_len {
// Use probe to peak
let key = d.probe().u8()?;
if let Some(key) = Cip509IntIdentifier::from_repr(key) {
// Consuming the int
let _: u8 = decode_helper(d, "CIP509", ctx)?;
match key {
Cip509IntIdentifier::Purpose => {
cip509_metadatum.purpose =
UuidV4::try_from(decode_bytes(d, "CIP509 purpose")?).map_err(|_| {
decode::Error::message("Invalid data size of Purpose")
})?;
},
Cip509IntIdentifier::TxInputsHash => {
cip509_metadatum.txn_inputs_hash =
TxInputHash::try_from(decode_bytes(d, "CIP509 txn inputs hash")?)
.map_err(|_| {
decode::Error::message("Invalid data size of TxInputsHash")
})?;
},
Cip509IntIdentifier::PreviousTxId => {
let prv_tx_hash: [u8; 32] = decode_bytes(d, "CIP509 previous tx ID")?
.try_into()
.map_err(|_| {
decode::Error::message("Invalid data size of PreviousTxId")
})?;
cip509_metadatum.prv_tx_id = Some(Hash::from(prv_tx_hash));
},
Cip509IntIdentifier::ValidationSignature => {
let validation_signature = decode_bytes(d, "CIP509 validation signature")?;
if validation_signature.is_empty() || validation_signature.len() > 64 {
return Err(decode::Error::message(
"Invalid data size of ValidationSignature",
));
}
cip509_metadatum.validation_signature = validation_signature;
},
}
} else {
// Handle the x509 chunks 10 11 12
let x509_chunks = X509Chunks::decode(d, ctx)?;
cip509_metadatum.x509_chunks = x509_chunks;
}
}
Ok(cip509_metadatum)
}
}

impl Cip509 {
/// Basic validation for CIP509
/// The validation include the following:
/// * Hashing the transaction inputs within the transaction should match the
/// txn-inputs-hash
/// * Auxiliary data hash within the transaction should match the hash of the
/// auxiliary data itself.
/// * Public key validation for role 0 where public key extracted from x509 and c509
/// subject alternative name should match one of the witness in witness set within
/// the transaction.
/// * Payment key reference validation for role 0 where the reference should be either
/// 1. Negative index reference - reference to transaction output in transaction:
/// should match some of the key within witness set.
/// 2. Positive index reference - reference to the transaction input in
/// transaction: only check whether the index exist within the transaction
/// inputs.
/// * Role signing key validation for role 0 where the signing keys should only be the
/// certificates
///
/// See:
/// * <https://github.com/input-output-hk/catalyst-CIPs/tree/x509-envelope-metadata/CIP-XXXX>
/// * <https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/x509-envelope.cddl>
///
/// Note: This CIP509 is still under development and is subject to change.
///
/// # 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<String>) -> bool {
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 mut stake_key_validate = true;
let mut payment_key_validate = true;
let mut signing_key = true;
// Validate the role 0
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 =
validate_stake_public_key(self, txn, validation_report).unwrap_or(false);
payment_key_validate =
validate_payment_key(txn, role, validation_report).unwrap_or(false);
signing_key = validate_role_singing_key(role, validation_report);
}
}
}
tx_input_validate
&& aux_validate
&& stake_key_validate
&& payment_key_validate
&& signing_key
}
}
Loading

0 comments on commit 149bf11

Please sign in to comment.