@@ -25,28 +25,43 @@ use validation::{
2525use x509_chunks:: X509Chunks ;
2626
2727use super :: transaction:: witness:: TxWitness ;
28- use crate :: utils:: {
29- decode_helper:: { decode_bytes, decode_helper, decode_map_len} ,
30- general:: { decode_utf8, decremented_index} ,
31- hashing:: { blake2b_128, blake2b_256} ,
28+ use crate :: {
29+ cardano:: cip509:: { rbac:: Cip509RbacMetadata , types:: ValidationSignature } ,
30+ utils:: {
31+ decode_helper:: { decode_bytes, decode_helper, decode_map_len} ,
32+ general:: decremented_index,
33+ hashing:: { blake2b_128, blake2b_256} ,
34+ } ,
3235} ;
3336
3437/// CIP509 label.
3538pub const LABEL : u64 = 509 ;
3639
37- /// CIP509.
38- #[ derive( Debug , PartialEq , Clone , Default ) ]
40+ /// A x509 metadata envelope.
41+ ///
42+ /// The envelope is required to prevent replayability attacks. See [this document] for
43+ /// more details.
44+ ///
45+ /// [this document]: https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/README.md
46+ #[ derive( Debug , PartialEq , Clone ) ]
3947pub struct Cip509 {
40- /// `UUIDv4` Purpose .
41- pub purpose : Uuid , // (bytes .size 16)
48+ /// A registration purpose (`UUIDv4`).
49+ ///
50+ /// The purpose is defined by the consuming dApp.
51+ pub purpose : Uuid ,
4252 /// Transaction inputs hash.
43- pub txn_inputs_hash : TxInputHash , // bytes .size 16
44- /// Optional previous transaction ID.
45- pub prv_tx_id : Option < Hash < 32 > > , // bytes .size 32
46- /// x509 chunks.
47- pub x509_chunks : X509Chunks , // chunk_type => [ + x509_chunk ]
53+ pub txn_inputs_hash : TxInputHash ,
54+ /// An optional hash of the previous transaction.
55+ ///
56+ /// The hash must always be present except for the first registration transaction.
57+ // TODO: Use the `Blake2b256Hash` type from the `cardano-blockchain-types` crate.
58+ pub prv_tx_id : Option < Hash < 32 > > ,
59+ /// Metadata.
60+ ///
61+ /// This field encoded in chunks. See [`X509Chunks`] for more details.
62+ pub metadata : Cip509RbacMetadata ,
4863 /// Validation signature.
49- pub validation_signature : Vec < u8 > , // bytes size (1..64)
64+ pub validation_signature : ValidationSignature ,
5065}
5166
5267/// Validation value for CIP509 metadatum.
@@ -92,7 +107,13 @@ pub(crate) enum Cip509IntIdentifier {
92107impl Decode < ' _ , ( ) > for Cip509 {
93108 fn decode ( d : & mut Decoder , ctx : & mut ( ) ) -> Result < Self , decode:: Error > {
94109 let map_len = decode_map_len ( d, "CIP509" ) ?;
95- let mut cip509_metadatum = Cip509 :: default ( ) ;
110+
111+ let mut purpose = Uuid :: default ( ) ;
112+ let mut txn_inputs_hash = TxInputHash :: default ( ) ;
113+ let mut prv_tx_id = None ;
114+ let mut metadata = None ;
115+ let mut validation_signature = Vec :: new ( ) ;
116+
96117 for _ in 0 ..map_len {
97118 // Use probe to peak
98119 let key = d. probe ( ) . u8 ( ) ?;
@@ -101,43 +122,57 @@ impl Decode<'_, ()> for Cip509 {
101122 let _: u8 = decode_helper ( d, "CIP509" , ctx) ?;
102123 match key {
103124 Cip509IntIdentifier :: Purpose => {
104- cip509_metadatum. purpose =
105- Uuid :: try_from ( decode_bytes ( d, "CIP509 purpose" ) ?) . map_err ( |_| {
106- decode:: Error :: message ( "Invalid data size of Purpose" )
107- } ) ?;
125+ purpose = Uuid :: try_from ( decode_bytes ( d, "CIP509 purpose" ) ?)
126+ . map_err ( |_| decode:: Error :: message ( "Invalid data size of Purpose" ) ) ?;
108127 } ,
109128 Cip509IntIdentifier :: TxInputsHash => {
110- cip509_metadatum . txn_inputs_hash =
129+ txn_inputs_hash =
111130 TxInputHash :: try_from ( decode_bytes ( d, "CIP509 txn inputs hash" ) ?)
112131 . map_err ( |_| {
113132 decode:: Error :: message ( "Invalid data size of TxInputsHash" )
114133 } ) ?;
115134 } ,
116135 Cip509IntIdentifier :: PreviousTxId => {
117- let prv_tx_hash : [ u8 ; 32 ] = decode_bytes ( d, "CIP509 previous tx ID" ) ?
136+ let hash : [ u8 ; 32 ] = decode_bytes ( d, "CIP509 previous tx ID" ) ?
118137 . try_into ( )
119138 . map_err ( |_| {
120- decode:: Error :: message ( "Invalid data size of PreviousTxId" )
121- } ) ?;
122- cip509_metadatum . prv_tx_id = Some ( Hash :: from ( prv_tx_hash ) ) ;
139+ decode:: Error :: message ( "Invalid data size of PreviousTxId" )
140+ } ) ?;
141+ prv_tx_id = Some ( Hash :: from ( hash ) ) ;
123142 } ,
124143 Cip509IntIdentifier :: ValidationSignature => {
125- let validation_signature = decode_bytes ( d, "CIP509 validation signature" ) ?;
126- if validation_signature. is_empty ( ) || validation_signature. len ( ) > 64 {
127- return Err ( decode:: Error :: message (
128- "Invalid data size of ValidationSignature" ,
129- ) ) ;
130- }
131- cip509_metadatum. validation_signature = validation_signature;
144+ let signature = decode_bytes ( d, "CIP509 validation signature" ) ?;
145+ validation_signature = signature;
132146 } ,
133147 }
134148 } else {
135149 // Handle the x509 chunks 10 11 12
136150 let x509_chunks = X509Chunks :: decode ( d, ctx) ?;
137- cip509_metadatum. x509_chunks = x509_chunks;
151+ // Technically it is possible to store multiple copies (or different instances) of
152+ // metadata, but it isn't allowed. See this link for more details:
153+ // https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/README.md#keys-10-11-or-12---x509-chunked-data
154+ if metadata. is_some ( ) {
155+ return Err ( decode:: Error :: message (
156+ "Only one instance of the chunked metadata should be present" ,
157+ ) ) ;
158+ }
159+ metadata = Some ( x509_chunks. into ( ) ) ;
138160 }
139161 }
140- Ok ( cip509_metadatum)
162+
163+ let metadata =
164+ metadata. ok_or_else ( || decode:: Error :: message ( "Missing metadata in CIP509" ) ) ?;
165+ let validation_signature = validation_signature
166+ . try_into ( )
167+ . map_err ( |e| decode:: Error :: message ( format ! ( "Invalid validation signature: {e:?}" ) ) ) ?;
168+
169+ Ok ( Self {
170+ purpose,
171+ txn_inputs_hash,
172+ prv_tx_id,
173+ metadata,
174+ validation_signature,
175+ } )
141176 }
142177}
143178
@@ -179,16 +214,14 @@ impl Cip509 {
179214 let mut is_valid_stake_public_key = true ;
180215 let mut is_valid_payment_key = true ;
181216 let mut is_valid_signing_key = true ;
182- if let Some ( role_set) = & self . x509_chunks . 0 . role_set {
183- // Validate only role 0
184- for role in role_set {
185- if role. role_number == 0 {
186- is_valid_stake_public_key =
187- validate_stake_public_key ( self , txn, validation_report) . unwrap_or ( false ) ;
188- is_valid_payment_key =
189- validate_payment_key ( txn, role, validation_report) . unwrap_or ( false ) ;
190- is_valid_signing_key = validate_role_singing_key ( role, validation_report) ;
191- }
217+ // Validate only role 0
218+ for role in & self . metadata . role_set {
219+ if role. role_number == 0 {
220+ is_valid_stake_public_key =
221+ validate_stake_public_key ( self , txn, validation_report) . unwrap_or ( false ) ;
222+ is_valid_payment_key =
223+ validate_payment_key ( txn, role, validation_report) . unwrap_or ( false ) ;
224+ is_valid_signing_key = validate_role_singing_key ( role, validation_report) ;
192225 }
193226 }
194227 Cip509Validation {
0 commit comments