@@ -38,14 +38,15 @@ use pallas::{
3838
3939use super :: utils:: cip19:: compare_key_hash;
4040use 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) ]
250258mod tests {
251259 use super :: * ;
0 commit comments