@@ -25,28 +25,42 @@ 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 ,
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 `BLAKE2b` hash of the previous transaction.
55+ ///
56+ /// The hash must always be present except for the first registration transaction.
57+ pub prv_tx_id : Option < Hash < 32 > > ,
58+ /// Metadata.
59+ ///
60+ /// This field encoded in chunks. See [`X509Chunks`] for more details.
61+ pub metadata : Cip509RbacMetadata ,
4862 /// Validation signature.
49- pub validation_signature : Vec < u8 > , // bytes size (1..64)
63+ pub validation_signature : Vec < u8 > ,
5064}
5165
5266/// Validation value for CIP509 metadatum.
@@ -92,7 +106,13 @@ pub(crate) enum Cip509IntIdentifier {
92106impl Decode < ' _ , ( ) > for Cip509 {
93107 fn decode ( d : & mut Decoder , ctx : & mut ( ) ) -> Result < Self , decode:: Error > {
94108 let map_len = decode_map_len ( d, "CIP509" ) ?;
95- let mut cip509_metadatum = Cip509 :: default ( ) ;
109+
110+ let mut purpose = Uuid :: default ( ) ;
111+ let mut txn_inputs_hash = TxInputHash :: default ( ) ;
112+ let mut prv_tx_id = None ;
113+ let mut metadata = None ;
114+ let mut validation_signature = Vec :: new ( ) ;
115+
96116 for _ in 0 ..map_len {
97117 // Use probe to peak
98118 let key = d. probe ( ) . u8 ( ) ?;
@@ -101,43 +121,56 @@ impl Decode<'_, ()> for Cip509 {
101121 let _: u8 = decode_helper ( d, "CIP509" , ctx) ?;
102122 match key {
103123 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- } ) ?;
124+ purpose = Uuid :: try_from ( decode_bytes ( d, "CIP509 purpose" ) ?)
125+ . map_err ( |_| decode:: Error :: message ( "Invalid data size of Purpose" ) ) ?;
108126 } ,
109127 Cip509IntIdentifier :: TxInputsHash => {
110- cip509_metadatum . txn_inputs_hash =
128+ txn_inputs_hash =
111129 TxInputHash :: try_from ( decode_bytes ( d, "CIP509 txn inputs hash" ) ?)
112130 . map_err ( |_| {
113131 decode:: Error :: message ( "Invalid data size of TxInputsHash" )
114132 } ) ?;
115133 } ,
116134 Cip509IntIdentifier :: PreviousTxId => {
117- let prv_tx_hash : [ u8 ; 32 ] = decode_bytes ( d, "CIP509 previous tx ID" ) ?
135+ let hash : [ u8 ; 32 ] = decode_bytes ( d, "CIP509 previous tx ID" ) ?
118136 . try_into ( )
119137 . map_err ( |_| {
120- decode:: Error :: message ( "Invalid data size of PreviousTxId" )
121- } ) ?;
122- cip509_metadatum . prv_tx_id = Some ( Hash :: from ( prv_tx_hash ) ) ;
138+ decode:: Error :: message ( "Invalid data size of PreviousTxId" )
139+ } ) ?;
140+ prv_tx_id = Some ( Hash :: from ( hash ) ) ;
123141 } ,
124142 Cip509IntIdentifier :: ValidationSignature => {
125- let validation_signature = decode_bytes ( d, "CIP509 validation signature" ) ?;
126- if validation_signature . is_empty ( ) || validation_signature . len ( ) > 64 {
143+ let signature = decode_bytes ( d, "CIP509 validation signature" ) ?;
144+ if signature . is_empty ( ) || signature . len ( ) > 64 {
127145 return Err ( decode:: Error :: message (
128146 "Invalid data size of ValidationSignature" ,
129147 ) ) ;
130148 }
131- cip509_metadatum . validation_signature = validation_signature ;
149+ validation_signature = signature ;
132150 } ,
133151 }
134152 } else {
135153 // Handle the x509 chunks 10 11 12
136154 let x509_chunks = X509Chunks :: decode ( d, ctx) ?;
137- cip509_metadatum. x509_chunks = x509_chunks;
155+ // Technically it is possible to store multiple copies (or different instances) of
156+ // metadata, but it isn't allowed. See this link for more details:
157+ // https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/README.md#keys-10-11-or-12---x509-chunked-data
158+ if metadata. is_some ( ) {
159+ return Err ( decode:: Error :: message (
160+ "Only one instance of the chunked metadata should be present" ,
161+ ) ) ;
162+ }
163+ metadata = Some ( x509_chunks. into ( ) ) ;
138164 }
139165 }
140- Ok ( cip509_metadatum)
166+
167+ Ok ( Self {
168+ purpose,
169+ txn_inputs_hash,
170+ prv_tx_id,
171+ metadata : metadata. unwrap_or_default ( ) ,
172+ validation_signature,
173+ } )
141174 }
142175}
143176
@@ -179,16 +212,14 @@ impl Cip509 {
179212 let mut is_valid_stake_public_key = true ;
180213 let mut is_valid_payment_key = true ;
181214 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- }
215+ // Validate only role 0
216+ for role in & self . metadata . role_set {
217+ if role. role_number == 0 {
218+ is_valid_stake_public_key =
219+ validate_stake_public_key ( self , txn, validation_report) . unwrap_or ( false ) ;
220+ is_valid_payment_key =
221+ validate_payment_key ( txn, role, validation_report) . unwrap_or ( false ) ;
222+ is_valid_signing_key = validate_role_singing_key ( role, validation_report) ;
192223 }
193224 }
194225 Cip509Validation {
0 commit comments