Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rust/rbac-registration): RBAC registration #85

Merged
merged 49 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
a1b0db1
feat: move cip509 from cardano chain follower
bkioshn Nov 10, 2024
05c5ae3
fix: remove key ref and use key local ref
bkioshn Nov 11, 2024
7f502ca
fix: structure of x509, c509, and public key
bkioshn Nov 11, 2024
becf775
fix: validation
bkioshn Nov 17, 2024
a65cba4
fix: update x509 and c509 certs
bkioshn Nov 17, 2024
94dcefc
fix: key local ref
bkioshn Nov 17, 2024
2c5c077
fix: c509 cert
bkioshn Nov 17, 2024
33d1ad3
fix: to string
bkioshn Nov 18, 2024
f083502
fix: rearrange utils
bkioshn Nov 18, 2024
890689d
fix: cargo toml
bkioshn Nov 19, 2024
b92ecd4
fix: add new type + comment
bkioshn Nov 25, 2024
c04eca4
fix: linter
bkioshn Nov 25, 2024
b874c3e
fix: smart pointer
bkioshn Nov 25, 2024
89f627a
chore: fix format
bkioshn Nov 25, 2024
d1181dd
Merge branch 'main' into feat/rbac-registration
bkioshn Nov 25, 2024
c90e0ee
fix: cleanup cargo.toml
bkioshn Nov 25, 2024
2777246
fix: add rbac-registration to earthly rust
bkioshn Nov 25, 2024
8fae2f0
fix: move cip509 validation
bkioshn Nov 25, 2024
8aba254
fix: rust ci
bkioshn Nov 26, 2024
d84aee0
feat: add role signing key validation
bkioshn Nov 26, 2024
86c163f
fix: add Bytes conversion for Pubkey
bkioshn Nov 26, 2024
afcaa09
fix: error return
bkioshn Nov 26, 2024
76b20ca
fix: use Hash from pallas for tx hash
bkioshn Nov 26, 2024
99d8a86
fix: format
bkioshn Nov 26, 2024
72a1d3a
fix: bytes to pubkey conversion
bkioshn Nov 26, 2024
bb43028
fix: add Bytes conversion for Pubkey
bkioshn Nov 26, 2024
ecaf37e
Merge branch 'main' into feat/rbac-registration
bkioshn Nov 26, 2024
7583aec
fix: add rbac-reg to semantic pull request
bkioshn Nov 27, 2024
5571d1a
fix: test data
bkioshn Nov 29, 2024
3a9f170
fix: signing and encryption key
bkioshn Nov 29, 2024
6cb3b8d
fix: format
bkioshn Nov 29, 2024
7e8ec2d
fix: witness test data to be compatible with new test data
bkioshn Nov 29, 2024
4a07cae
fix: validate stake public key witness and add test for role signing key
bkioshn Nov 29, 2024
f10c230
chore: doc about context specific
bkioshn Nov 29, 2024
3d4ff45
Merge branch 'main' into feat/rbac-registration
bkioshn Nov 29, 2024
00fa07d
chore: fix format
bkioshn Nov 29, 2024
eea1afa
fix: remove test since cip509 decode are done in validation test
bkioshn Nov 29, 2024
6da8f96
fix: test
bkioshn Nov 29, 2024
a1cea39
fix: test compression data
bkioshn Nov 29, 2024
39ff437
feat(rust/rbac-registration): add cardano registration chain (#90)
bkioshn Nov 29, 2024
ccfd89e
fix: remove txn index and change test data to chain root
bkioshn Dec 2, 2024
6356c44
chore: fix comment
bkioshn Dec 2, 2024
efb04e4
fix: remove tx index from cip-509 validate function
bkioshn Dec 2, 2024
b97a095
fix: test data
bkioshn Dec 2, 2024
d0bf563
chore: fix format
bkioshn Dec 2, 2024
18ea3aa
fix: remove txn index from validate
bkioshn Dec 2, 2024
3479ed4
fix: format
bkioshn Dec 2, 2024
8cbe99d
Merge branch 'main' into feat/rbac-registration
stevenj Dec 2, 2024
d60fb50
fix(rust/rbac-registration): Tracking payment key type (#93)
bkioshn Dec 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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