-
Notifications
You must be signed in to change notification settings - Fork 0
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): Introduce Cip0134UriSet type #119
base: rbac-registration-improvements
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -25,28 +25,42 @@ use validation::{ | |||||
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}, | ||||||
use crate::{ | ||||||
cardano::cip509::rbac::Cip509RbacMetadata, | ||||||
utils::{ | ||||||
decode_helper::{decode_bytes, decode_helper, decode_map_len}, | ||||||
general::decremented_index, | ||||||
hashing::{blake2b_128, blake2b_256}, | ||||||
}, | ||||||
}; | ||||||
|
||||||
/// CIP509 label. | ||||||
pub const LABEL: u64 = 509; | ||||||
|
||||||
/// CIP509. | ||||||
#[derive(Debug, PartialEq, Clone, Default)] | ||||||
/// A x509 metadata envelope. | ||||||
/// | ||||||
/// The envelope is required to prevent replayability attacks. See [this document] for | ||||||
/// more details. | ||||||
/// | ||||||
/// [this document]: https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/README.md | ||||||
#[derive(Debug, PartialEq, Clone)] | ||||||
pub struct Cip509 { | ||||||
/// `UUIDv4` Purpose . | ||||||
pub purpose: Uuid, // (bytes .size 16) | ||||||
/// A registration purpose (`UUIDv4`). | ||||||
/// | ||||||
/// The purpose is defined by the consuming dApp. | ||||||
pub purpose: Uuid, | ||||||
/// 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 ] | ||||||
pub txn_inputs_hash: TxInputHash, | ||||||
/// An optional `BLAKE2b` hash of the previous transaction. | ||||||
/// | ||||||
/// The hash must always be present except for the first registration transaction. | ||||||
pub prv_tx_id: Option<Hash<32>>, | ||||||
/// Metadata. | ||||||
/// | ||||||
/// This field encoded in chunks. See [`X509Chunks`] for more details. | ||||||
pub metadata: Cip509RbacMetadata, | ||||||
/// Validation signature. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
pub validation_signature: Vec<u8>, // bytes size (1..64) | ||||||
pub validation_signature: Vec<u8>, | ||||||
} | ||||||
|
||||||
/// Validation value for CIP509 metadatum. | ||||||
|
@@ -92,7 +106,13 @@ pub(crate) enum Cip509IntIdentifier { | |||||
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(); | ||||||
|
||||||
let mut purpose = Uuid::default(); | ||||||
let mut txn_inputs_hash = TxInputHash::default(); | ||||||
let mut prv_tx_id = None; | ||||||
let mut metadata = None; | ||||||
let mut validation_signature = Vec::new(); | ||||||
|
||||||
for _ in 0..map_len { | ||||||
// Use probe to peak | ||||||
let key = d.probe().u8()?; | ||||||
|
@@ -101,43 +121,59 @@ impl Decode<'_, ()> for Cip509 { | |||||
let _: u8 = decode_helper(d, "CIP509", ctx)?; | ||||||
match key { | ||||||
Cip509IntIdentifier::Purpose => { | ||||||
cip509_metadatum.purpose = | ||||||
Uuid::try_from(decode_bytes(d, "CIP509 purpose")?).map_err(|_| { | ||||||
decode::Error::message("Invalid data size of Purpose") | ||||||
})?; | ||||||
purpose = Uuid::try_from(decode_bytes(d, "CIP509 purpose")?) | ||||||
.map_err(|_| decode::Error::message("Invalid data size of Purpose"))?; | ||||||
}, | ||||||
Cip509IntIdentifier::TxInputsHash => { | ||||||
cip509_metadatum.txn_inputs_hash = | ||||||
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")? | ||||||
let 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)); | ||||||
decode::Error::message("Invalid data size of PreviousTxId") | ||||||
})?; | ||||||
prv_tx_id = Some(Hash::from(hash)); | ||||||
}, | ||||||
Cip509IntIdentifier::ValidationSignature => { | ||||||
let validation_signature = decode_bytes(d, "CIP509 validation signature")?; | ||||||
if validation_signature.is_empty() || validation_signature.len() > 64 { | ||||||
let signature = decode_bytes(d, "CIP509 validation signature")?; | ||||||
if signature.is_empty() || signature.len() > 64 { | ||||||
return Err(decode::Error::message( | ||||||
"Invalid data size of ValidationSignature", | ||||||
)); | ||||||
} | ||||||
cip509_metadatum.validation_signature = validation_signature; | ||||||
validation_signature = signature; | ||||||
}, | ||||||
} | ||||||
} else { | ||||||
// Handle the x509 chunks 10 11 12 | ||||||
let x509_chunks = X509Chunks::decode(d, ctx)?; | ||||||
cip509_metadatum.x509_chunks = x509_chunks; | ||||||
// Technically it is possible to store multiple copies (or different instances) of | ||||||
// metadata, but it isn't allowed. See this link for more details: | ||||||
// https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/README.md#keys-10-11-or-12---x509-chunked-data | ||||||
if metadata.is_some() { | ||||||
return Err(decode::Error::message( | ||||||
"Only one instance of the chunked metadata should be present", | ||||||
)); | ||||||
} | ||||||
metadata = Some(x509_chunks.into()); | ||||||
} | ||||||
} | ||||||
Ok(cip509_metadatum) | ||||||
|
||||||
let metadata = | ||||||
metadata.ok_or_else(|| decode::Error::message("Missing metadata in CIP509"))?; | ||||||
|
||||||
Ok(Self { | ||||||
purpose, | ||||||
txn_inputs_hash, | ||||||
prv_tx_id, | ||||||
metadata, | ||||||
validation_signature, | ||||||
}) | ||||||
} | ||||||
} | ||||||
|
||||||
|
@@ -179,16 +215,14 @@ impl Cip509 { | |||||
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 { | ||||||
is_valid_stake_public_key = | ||||||
validate_stake_public_key(self, txn, validation_report).unwrap_or(false); | ||||||
is_valid_payment_key = | ||||||
validate_payment_key(txn, role, validation_report).unwrap_or(false); | ||||||
is_valid_signing_key = validate_role_singing_key(role, validation_report); | ||||||
} | ||||||
// Validate only role 0 | ||||||
for role in &self.metadata.role_set { | ||||||
if role.role_number == 0 { | ||||||
is_valid_stake_public_key = | ||||||
validate_stake_public_key(self, txn, validation_report).unwrap_or(false); | ||||||
is_valid_payment_key = | ||||||
validate_payment_key(txn, role, validation_report).unwrap_or(false); | ||||||
is_valid_signing_key = validate_role_singing_key(role, validation_report); | ||||||
} | ||||||
} | ||||||
Cip509Validation { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,24 +16,43 @@ 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, | ||
use crate::{ | ||
cardano::cip509::utils::Cip0134UriSet, | ||
utils::decode_helper::{ | ||
decode_any, decode_array_len, decode_bytes, decode_helper, decode_map_len, | ||
}, | ||
}; | ||
|
||
/// Cip509 RBAC metadata. | ||
#[derive(Debug, PartialEq, Clone, Default)] | ||
/// | ||
/// See [this document] for more details. | ||
/// | ||
/// [this document]: https://github.com/input-output-hk/catalyst-CIPs/tree/x509-role-registration-metadata/CIP-XXXX | ||
#[derive(Debug, PartialEq, Clone)] | ||
pub struct Cip509RbacMetadata { | ||
/// Optional list of x509 certificates. | ||
pub x509_certs: Option<Vec<X509DerCert>>, | ||
/// Optional list of c509 certificates. | ||
/// The value can be either the c509 certificate or c509 metadatum reference. | ||
pub c509_certs: Option<Vec<C509Cert>>, | ||
/// Optional list of Public keys. | ||
pub pub_keys: Option<Vec<SimplePublicKeyType>>, | ||
/// Optional list of revocation list. | ||
pub revocation_list: Option<Vec<CertKeyHash>>, | ||
/// Optional list of role data. | ||
pub role_set: Option<Vec<RoleData>>, | ||
/// A potentially empty list of x509 certificates. | ||
pub x509_certs: Vec<X509DerCert>, | ||
/// A potentially empty list of c509 certificates. | ||
pub c509_certs: Vec<C509Cert>, | ||
/// A set of URIs contained in both x509 and c509 certificates. | ||
/// | ||
/// URIs from different certificate types are stored separately and certificate | ||
/// indexes are preserved too. | ||
/// | ||
/// This field isn't present in the encoded format and is populated by processing both | ||
/// `x509_certs` and `c509_certs` fields. | ||
pub certificate_uris: Cip0134UriSet, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like this |
||
/// A list of public keys that can be used instead of storing full certificates. | ||
/// | ||
/// Check [this section] to understand the how certificates and the public keys list | ||
/// are related. | ||
/// | ||
/// [this section]: https://github.com/input-output-hk/catalyst-CIPs/tree/x509-role-registration-metadata/CIP-XXXX#storing-certificates-and-public-key | ||
pub pub_keys: Vec<SimplePublicKeyType>, | ||
/// A potentially empty list of revoked certificates. | ||
pub revocation_list: Vec<CertKeyHash>, | ||
/// A potentially empty list of role data. | ||
pub role_set: Vec<RoleData>, | ||
/// Optional map of purpose key data. | ||
/// Empty map if no purpose key data is present. | ||
pub purpose_key_data: HashMap<u16, Vec<u8>>, | ||
|
@@ -60,86 +79,59 @@ pub enum Cip509RbacMetadataInt { | |
RoleSet = 100, | ||
} | ||
|
||
impl Cip509RbacMetadata { | ||
/// Create a new instance of `Cip509RbacMetadata`. | ||
pub(crate) fn new() -> Self { | ||
Self { | ||
x509_certs: None, | ||
c509_certs: None, | ||
pub_keys: None, | ||
revocation_list: None, | ||
role_set: None, | ||
purpose_key_data: HashMap::new(), | ||
} | ||
} | ||
|
||
/// Set the x509 certificates. | ||
fn set_x509_certs(&mut self, x509_certs: Vec<X509DerCert>) { | ||
self.x509_certs = Some(x509_certs); | ||
} | ||
|
||
/// Set the c509 certificates. | ||
fn set_c509_certs(&mut self, c509_certs: Vec<C509Cert>) { | ||
self.c509_certs = Some(c509_certs); | ||
} | ||
|
||
/// Set the public keys. | ||
fn set_pub_keys(&mut self, pub_keys: Vec<SimplePublicKeyType>) { | ||
self.pub_keys = Some(pub_keys); | ||
} | ||
|
||
/// Set the revocation list. | ||
fn set_revocation_list(&mut self, revocation_list: Vec<CertKeyHash>) { | ||
self.revocation_list = Some(revocation_list); | ||
} | ||
|
||
/// Set the role data set. | ||
fn set_role_set(&mut self, role_set: Vec<RoleData>) { | ||
self.role_set = Some(role_set); | ||
} | ||
} | ||
|
||
impl Decode<'_, ()> for Cip509RbacMetadata { | ||
fn decode(d: &mut Decoder, ctx: &mut ()) -> Result<Self, decode::Error> { | ||
let map_len = decode_map_len(d, "Cip509RbacMetadata")?; | ||
|
||
let mut x509_rbac_metadata = Cip509RbacMetadata::new(); | ||
let mut x509_certs = Vec::new(); | ||
let mut c509_certs = Vec::new(); | ||
let mut pub_keys = Vec::new(); | ||
let mut revocation_list = Vec::new(); | ||
let mut role_set = Vec::new(); | ||
let mut purpose_key_data = HashMap::new(); | ||
|
||
for _ in 0..map_len { | ||
let key: u16 = decode_helper(d, "key in Cip509RbacMetadata", ctx)?; | ||
if let Some(key) = Cip509RbacMetadataInt::from_repr(key) { | ||
match key { | ||
Cip509RbacMetadataInt::X509Certs => { | ||
let x509_certs = decode_array_rbac(d, "x509 certificate")?; | ||
x509_rbac_metadata.set_x509_certs(x509_certs); | ||
x509_certs = decode_array_rbac(d, "x509 certificate")?; | ||
}, | ||
Cip509RbacMetadataInt::C509Certs => { | ||
let c509_certs = decode_array_rbac(d, "c509 certificate")?; | ||
x509_rbac_metadata.set_c509_certs(c509_certs); | ||
c509_certs = decode_array_rbac(d, "c509 certificate")?; | ||
}, | ||
Cip509RbacMetadataInt::PubKeys => { | ||
let pub_keys = decode_array_rbac(d, "public keys")?; | ||
x509_rbac_metadata.set_pub_keys(pub_keys); | ||
pub_keys = decode_array_rbac(d, "public keys")?; | ||
}, | ||
Cip509RbacMetadataInt::RevocationList => { | ||
let revocation_list = decode_revocation_list(d)?; | ||
x509_rbac_metadata.set_revocation_list(revocation_list); | ||
revocation_list = decode_revocation_list(d)?; | ||
}, | ||
Cip509RbacMetadataInt::RoleSet => { | ||
let role_set = decode_array_rbac(d, "role set")?; | ||
x509_rbac_metadata.set_role_set(role_set); | ||
role_set = decode_array_rbac(d, "role set")?; | ||
}, | ||
} | ||
} else { | ||
if !(FIRST_PURPOSE_KEY..=LAST_PURPOSE_KEY).contains(&key) { | ||
return Err(decode::Error::message(format!("Invalid purpose key set, should be with the range {FIRST_PURPOSE_KEY} - {LAST_PURPOSE_KEY}"))); | ||
} | ||
x509_rbac_metadata | ||
.purpose_key_data | ||
.insert(key, decode_any(d, "purpose key")?); | ||
|
||
purpose_key_data.insert(key, decode_any(d, "purpose key")?); | ||
} | ||
} | ||
Ok(x509_rbac_metadata) | ||
|
||
let certificate_uris = Cip0134UriSet::new(&x509_certs, &c509_certs).map_err(|e| { | ||
decode::Error::message(format!("Unable to parse URIs from certificates: {e:?}")) | ||
})?; | ||
|
||
Ok(Self { | ||
x509_certs, | ||
c509_certs, | ||
certificate_uris, | ||
pub_keys, | ||
revocation_list, | ||
role_set, | ||
purpose_key_data, | ||
}) | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
//! 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_set::Cip0134UriSet}; | ||
|
||
mod uri; | ||
mod uri_set; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blake2b256Hash