66
77use std:: borrow:: Cow ;
88
9+ use c509_certificate:: c509:: C509 ;
910use cardano_blockchain_types:: { TxnWitness , VKeyHash } ;
1011use catalyst_types:: {
1112 hashes:: { Blake2b128Hash , Blake2b256Hash } ,
13+ id_uri:: IdUri ,
1214 problem_report:: ProblemReport ,
1315} ;
16+ use ed25519_dalek:: { VerifyingKey , PUBLIC_KEY_LENGTH } ;
1417use pallas:: {
1518 codec:: {
1619 minicbor:: { Encode , Encoder } ,
1720 utils:: Bytes ,
1821 } ,
1922 ledger:: { addresses:: Address , primitives:: conway, traverse:: MultiEraTx } ,
2023} ;
24+ use x509_cert:: Certificate ;
2125
2226use super :: utils:: cip19:: compare_key_hash;
2327use crate :: cardano:: cip509:: {
@@ -158,9 +162,9 @@ fn extract_stake_addresses(uris: Option<&Cip0134UriSet>) -> Vec<VKeyHash> {
158162 . collect ( )
159163}
160164
161- /// Checks that only role 0 uses certificates with zero index .
165+ /// Checks the role data .
162166#[ allow( clippy:: similar_names) ]
163- pub fn validate_role_data ( metadata : & Cip509RbacMetadata , report : & ProblemReport ) {
167+ pub fn validate_role_data ( metadata : & Cip509RbacMetadata , report : & ProblemReport ) -> Option < IdUri > {
164168 let context = "Role data validation" ;
165169
166170 if metadata. role_data . contains_key ( & RoleNumber :: ROLE_0 ) {
@@ -226,9 +230,10 @@ pub fn validate_role_data(metadata: &Cip509RbacMetadata, report: &ProblemReport)
226230 ) ;
227231 }
228232
233+ let mut catalyst_id = None ;
229234 for ( number, data) in & metadata. role_data {
230235 if number == & RoleNumber :: ROLE_0 {
231- validate_role_0 ( data, metadata, context, report) ;
236+ catalyst_id = validate_role_0 ( data, metadata, context, report) ;
232237 } else {
233238 if let Some ( signing_key) = data. signing_key ( ) {
234239 if signing_key. key_offset == 0 {
@@ -252,12 +257,13 @@ pub fn validate_role_data(metadata: &Cip509RbacMetadata, report: &ProblemReport)
252257 }
253258 }
254259 }
260+ catalyst_id
255261}
256262
257263/// Checks that the role 0 data is correct.
258264fn validate_role_0 (
259265 role : & RoleData , metadata : & Cip509RbacMetadata , context : & str , report : & ProblemReport ,
260- ) {
266+ ) -> Option < IdUri > {
261267 if let Some ( key) = role. encryption_key ( ) {
262268 report. invalid_value (
263269 "Role 0 encryption key" ,
@@ -269,31 +275,36 @@ fn validate_role_0(
269275
270276 let Some ( signing_key) = role. signing_key ( ) else {
271277 report. missing_field ( "(Role 0) RoleData::signing_key" , context) ;
272- return ;
278+ return None ;
273279 } ;
274280
275281 if signing_key. key_offset != 0 {
276282 report. other (
277283 & format ! ( "The role 0 must reference a certificate with 0 index ({role:?})" ) ,
278284 context,
279285 ) ;
280- return ;
286+ return None ;
281287 }
282288
289+ let mut catalyst_id = None ;
290+ let network = "cardano" ;
291+
283292 match signing_key. local_ref {
284293 LocalRefInt :: X509Certs => {
285294 match metadata. x509_certs . first ( ) {
286- Some ( X509DerCert :: X509Cert ( _ ) ) => {
295+ Some ( X509DerCert :: X509Cert ( cert ) ) => {
287296 // All good: role 0 references a valid X509 certificate.
297+ catalyst_id = x509_cert_key ( cert, context, report) . map ( |k| IdUri :: new ( network, None , k) ) ;
288298 }
289299 Some ( c) => report. other ( & format ! ( "Invalid X509 certificate value ({c:?}) for role 0 ({role:?})" ) , context) ,
290300 None => report. other ( "Role 0 reference X509 certificate at index 0, but there is no such certificate" , context) ,
291301 }
292302 } ,
293303 LocalRefInt :: C509Certs => {
294304 match metadata. c509_certs . first ( ) {
295- Some ( C509Cert :: C509Certificate ( _ ) ) => {
305+ Some ( C509Cert :: C509Certificate ( cert ) ) => {
296306 // All good: role 0 references a valid C509 certificate.
307+ catalyst_id = c509_cert_key ( cert, context, report) . map ( |k| IdUri :: new ( network, None , k) ) ;
297308 }
298309 Some ( c) => report. other ( & format ! ( "Invalid C509 certificate value ({c:?}) for role 0 ({role:?})" ) , context) ,
299310 None => report. other ( "Role 0 reference C509 certificate at index 0, but there is no such certificate" , context) ,
@@ -308,6 +319,77 @@ fn validate_role_0(
308319 ) ;
309320 } ,
310321 }
322+ catalyst_id
323+ }
324+
325+ /// Extracts `VerifyingKey` from the given `X509` certificate.
326+ fn x509_cert_key (
327+ cert : & Certificate , context : & str , report : & ProblemReport ,
328+ ) -> Option < VerifyingKey > {
329+ let Some ( extended_public_key) = cert
330+ . tbs_certificate
331+ . subject_public_key_info
332+ . subject_public_key
333+ . as_bytes ( )
334+ else {
335+ report. invalid_value (
336+ "subject_public_key" ,
337+ "is not octet aligned" ,
338+ "Must not have unused bits" ,
339+ context,
340+ ) ;
341+ return None ;
342+ } ;
343+ verifying_key ( extended_public_key, context, report)
344+ }
345+
346+ /// Extracts `VerifyingKey` from the given `C509` certificate.
347+ fn c509_cert_key ( cert : & C509 , context : & str , report : & ProblemReport ) -> Option < VerifyingKey > {
348+ verifying_key ( cert. tbs_cert ( ) . subject_public_key ( ) , context, report)
349+ }
350+
351+ /// Creates `VerifyingKey` from the given extended public key.
352+ fn verifying_key (
353+ extended_public_key : & [ u8 ] , context : & str , report : & ProblemReport ,
354+ ) -> Option < VerifyingKey > {
355+ /// An extender public key length in bytes.
356+ const EXTENDED_PUBLIC_KEY_LENGTH : usize = 64 ;
357+
358+ if extended_public_key. len ( ) != EXTENDED_PUBLIC_KEY_LENGTH {
359+ report. other (
360+ & format ! ( "Unexpected extended public key length in certificate: {}, expected {EXTENDED_PUBLIC_KEY_LENGTH}" ,
361+ extended_public_key. len( ) ) ,
362+ context,
363+ ) ;
364+ return None ;
365+ }
366+
367+ // This should never fail because of the check above.
368+ let Some ( public_key) = extended_public_key. get ( 0 ..PUBLIC_KEY_LENGTH ) else {
369+ report. other ( "Unable to get public key part" , context) ;
370+ return None ;
371+ } ;
372+
373+ let bytes: & [ u8 ; PUBLIC_KEY_LENGTH ] = match public_key. try_into ( ) {
374+ Ok ( v) => v,
375+ Err ( e) => {
376+ report. other (
377+ & format ! ( "Invalid public key length in X509 certificate: {e:?}" ) ,
378+ context,
379+ ) ;
380+ return None ;
381+ } ,
382+ } ;
383+ match VerifyingKey :: from_bytes ( bytes) {
384+ Ok ( k) => Some ( k) ,
385+ Err ( e) => {
386+ report. other (
387+ & format ! ( "Invalid public key in C509 certificate: {e:?}" ) ,
388+ context,
389+ ) ;
390+ None
391+ } ,
392+ }
311393}
312394
313395#[ cfg( test) ]
0 commit comments