Skip to content

Commit 1819264

Browse files
Add/fix certificates/role checks
1 parent 05ac75e commit 1819264

File tree

5 files changed

+70
-61
lines changed

5 files changed

+70
-61
lines changed

rust/rbac-registration/src/cardano/cip509/cip509.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::{
3535
types::{PaymentHistory, RoleNumber, TxInputHash, ValidationSignature},
3636
utils::Cip0134UriSet,
3737
validation::{
38-
validate_aux, validate_role_signing_key, validate_stake_public_key,
38+
validate_aux, validate_role_data, validate_stake_public_key,
3939
validate_txn_inputs_hash,
4040
},
4141
x509_chunks::X509Chunks,
@@ -161,10 +161,12 @@ impl Cip509 {
161161
txn.transaction_body.auxiliary_data_hash.as_ref(),
162162
&cip509.report,
163163
);
164-
// The following checks are only performed for the role 0.
165-
if let Some(role_data) = cip509.role_data(RoleNumber::ROLE_0) {
164+
if cip509.role_data(RoleNumber::ROLE_0).is_some() {
165+
// The following check is only performed for the role 0.
166166
validate_stake_public_key(txn, cip509.certificate_uris(), &cip509.report);
167-
validate_role_signing_key(role_data, cip509.metadata.as_ref(), &cip509.report);
167+
}
168+
if let Some(metadata) = &cip509.metadata {
169+
validate_role_data(metadata, &cip509.report);
168170
}
169171

170172
Ok(Some(cip509))

rust/rbac-registration/src/cardano/cip509/rbac/metadata.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ pub struct Cip509RbacMetadata {
4141
pub certificate_uris: Cip0134UriSet,
4242
/// A list of public keys that can be used instead of storing full certificates.
4343
///
44-
/// Check [this section] to understand the how certificates and the public keys list
45-
/// are related.
44+
/// Check [this section] to understand how certificates and the public keys list are
45+
/// related.
4646
///
4747
/// [this section]: https://github.com/input-output-hk/catalyst-CIPs/tree/x509-role-registration-metadata/CIP-XXXX#storing-certificates-and-public-key
4848
pub pub_keys: Vec<SimplePublicKeyType>,

rust/rbac-registration/src/cardano/cip509/validation.rs

Lines changed: 61 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@ use pallas::{
3838

3939
use super::utils::cip19::compare_key_hash;
4040
use crate::cardano::cip509::{
41-
rbac::Cip509RbacMetadata, types::TxInputHash, Cip0134UriSet, KeyLocalRef, LocalRefInt, RoleData,
41+
rbac::Cip509RbacMetadata, types::TxInputHash, C509Cert, Cip0134UriSet, LocalRefInt, RoleData,
42+
RoleNumber, X509DerCert,
4243
};
4344

4445
/// Context-specific primitive type with tag number 6 (`raw_tag` 134) for
4546
/// uniform resource identifier (URI) in the subject alternative name extension.
4647
/// Following the ASN.1
4748
/// <https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html>
48-
/// the tag is derive from
49+
/// the tag is derived from
4950
/// | Class (2 bit) | P/C (1 bit) | Tag Number (5 bit) |
5051
/// |`CONTEXT_SPECIFIC` | `PRIMITIVE` `| 6` |
5152
/// |`10` | `0` `| 00110` |
@@ -174,45 +175,77 @@ fn extract_stake_addresses(uris: Option<&Cip0134UriSet>) -> Vec<VKeyHash> {
174175
.collect()
175176
}
176177

177-
/// Validate role singing key for role 0.
178-
/// Must reference certificate not the public key
179-
pub fn validate_role_signing_key(
180-
role_data: &RoleData, metadata: Option<&Cip509RbacMetadata>, report: &ProblemReport,
181-
) {
182-
let context = "Cip509 role0 signing key validation";
178+
/// Checks that only role 0 uses certificates with zero index.
179+
pub fn validate_role_data(metadata: &Cip509RbacMetadata, report: &ProblemReport) {
180+
let context = "Role data validation";
181+
182+
if matches!(
183+
metadata.c509_certs.first(),
184+
Some(C509Cert::C509Certificate(_))
185+
) && matches!(metadata.x509_certs.first(), Some(X509DerCert::X509Cert(_)))
186+
{
187+
report.other("Only one certificate can be defined at index 0", context);
188+
}
183189

184-
let Some(signing_key) = role_data.signing_key() else {
185-
report.missing_field("RoleData::signing_key", context);
190+
for (number, data) in &metadata.role_data {
191+
if number == &RoleNumber::ROLE_0 {
192+
validate_role_0(data, metadata, context, report);
193+
} else {
194+
let Some(signing_key) = data.signing_key() else {
195+
// It is ok for other roles to not have a signing key.
196+
continue;
197+
};
198+
if signing_key.key_offset == 0 {
199+
report.other(
200+
&format!(
201+
"Only role 0 can reference a certificate with 0 index ({number:?} {data:?})"
202+
),
203+
context,
204+
);
205+
}
206+
}
207+
}
208+
}
209+
210+
/// Checks that the role 0 data is correct.
211+
fn validate_role_0(
212+
role: &RoleData, metadata: &Cip509RbacMetadata, context: &str, report: &ProblemReport,
213+
) {
214+
let Some(signing_key) = role.signing_key() else {
215+
report.missing_field("(Role 0) RoleData::signing_key", context);
186216
return;
187217
};
188218

189-
let Some(metadata) = metadata else {
190-
report.other("Missing metadata", context);
219+
if signing_key.key_offset != 0 {
220+
report.other(
221+
&format!("The role 0 must reference a certificate with 0 index ({role:?})"),
222+
context,
223+
);
191224
return;
192-
};
225+
}
193226

194227
match signing_key.local_ref {
195228
LocalRefInt::X509Certs => {
196-
check_key_offset(
197-
signing_key,
198-
metadata.x509_certs.as_slice(),
199-
"X509",
200-
context,
201-
report,
202-
);
229+
match metadata.x509_certs.first() {
230+
Some(X509DerCert::X509Cert(_)) => {
231+
// All good: role 0 references a valid X509 certificate.
232+
}
233+
Some(c) => report.other(&format!("Invalid X509 certificate value ({c:?}) for role 0 ({role:?})"), context),
234+
None => report.other("Role 0 reference X509 certificate at index 0, but there is no such certificate", context),
235+
}
203236
},
204237
LocalRefInt::C509Certs => {
205-
check_key_offset(
206-
signing_key,
207-
metadata.c509_certs.as_slice(),
208-
"C509",
209-
context,
210-
report,
211-
);
238+
match metadata.c509_certs.first() {
239+
Some(C509Cert::C509Certificate(_)) => {
240+
// All good: role 0 references a valid C509 certificate.
241+
}
242+
Some(c) => report.other(&format!("Invalid C509 certificate value ({c:?}) for role 0 ({role:?})"), context),
243+
None => report.other("Role 0 reference C509 certificate at index 0, but there is no such certificate", context),
244+
}
212245
},
213246
LocalRefInt::PubKeys => {
214247
report.invalid_value(
215-
"RoleData::signing_key",
248+
"(Role 0) RoleData::signing_key",
216249
&format!("{signing_key:?}"),
217250
"Role signing key should reference certificate, not public key",
218251
context,
@@ -221,31 +254,6 @@ pub fn validate_role_signing_key(
221254
}
222255
}
223256

224-
/// Add a problem report entry if the key offset is invalid.
225-
fn check_key_offset<T>(
226-
key: &KeyLocalRef, certificates: &[T], certificate_type: &str, context: &str,
227-
report: &ProblemReport,
228-
) {
229-
let Ok(offset) = usize::try_from(key.key_offset) else {
230-
report.invalid_value(
231-
"RoleData::signing_key",
232-
&format!("{key:?}"),
233-
"Role signing key offset is too big",
234-
context,
235-
);
236-
return;
237-
};
238-
239-
if offset >= certificates.len() {
240-
report.invalid_value(
241-
"RoleData::signing_key",
242-
&format!("{key:?}"),
243-
&format!("Role signing key should reference existing certificate, but there are only {} {} certificates in this registration", certificates.len(), certificate_type),
244-
context,
245-
);
246-
}
247-
}
248-
249257
#[cfg(test)]
250258
mod tests {
251259
use super::*;
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
//! Cardano module
2-
32
pub mod cip509;
43
pub mod transaction;

rust/rbac-registration/src/cardano/transaction/raw_aux_data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl RawAuxData {
110110

111111
let _unused = raw_decoded_data.metadata.insert(key, Arc::new(value));
112112

113-
// Look for End Sentinel IF its an indefinite MAP (which we know because entries is
113+
// Look for End Sentinel IF it is an indefinite MAP (which we know because entries is
114114
// u64::MAX).
115115
if entries == u64::MAX {
116116
match decoder.datatype() {

0 commit comments

Comments
 (0)