@@ -10,8 +10,7 @@ use std::{
1010
1111use catalyst_signed_doc:: { Builder , CatalystSignedDocument , KidUri , Metadata } ;
1212use clap:: Parser ;
13- use coset:: CborSerializable ;
14- use ed25519_dalek:: { ed25519:: signature:: Signer , pkcs8:: DecodePrivateKey } ;
13+ use ed25519_dalek:: pkcs8:: { DecodePrivateKey , DecodePublicKey } ;
1514
1615fn main ( ) {
1716 if let Err ( err) = Cli :: parse ( ) . exec ( ) {
@@ -52,6 +51,16 @@ enum Cli {
5251 /// Hex-formatted COSE SIGN Bytes
5352 cose_sign_hex : String ,
5453 } ,
54+ /// Validates a signature by Key ID and verifying key
55+ Verify {
56+ /// Path to the formed (could be empty, without any signatures) COSE document
57+ /// This exact file would be modified and new signature would be added
58+ path : PathBuf ,
59+ /// Path to the verifying key in PEM format
60+ pk : PathBuf ,
61+ /// Signer kid
62+ kid : KidUri ,
63+ } ,
5564}
5665
5766impl Cli {
@@ -68,53 +77,80 @@ impl Cli {
6877 let payload = serde_json:: to_vec ( & json_doc) ?;
6978 // Start with no signatures.
7079 let signed_doc = Builder :: new ( )
71- . with_content ( payload)
80+ . with_decoded_content ( payload)
7281 . with_metadata ( metadata)
7382 . build ( ) ?;
74- let mut bytes: Vec < u8 > = Vec :: new ( ) ;
75- minicbor:: encode ( signed_doc, & mut bytes)
76- . map_err ( |e| anyhow:: anyhow!( "Failed to encode document: {e}" ) ) ?;
77-
78- write_bytes_to_file ( & bytes, & output) ?;
83+ save_signed_doc ( signed_doc, & output) ?;
7984 } ,
8085 Self :: Sign { sk, doc, kid } => {
8186 let sk = load_secret_key_from_file ( & sk)
8287 . map_err ( |e| anyhow:: anyhow!( "Failed to load SK FILE: {e}" ) ) ?;
83- let mut cose = load_cose_from_file ( & doc)
84- . map_err ( |e| anyhow:: anyhow!( "Failed to load COSE FROM FILE: {e}" ) ) ?;
85- add_signature_to_cose ( & mut cose, & sk, kid. to_string ( ) ) ;
86- store_cose_file ( cose, & doc) ?;
88+ let cose_bytes = read_bytes_from_file ( & doc) ?;
89+ let signed_doc = signed_doc_from_bytes ( cose_bytes. as_slice ( ) ) ?;
90+ let builder = signed_doc. into_builder ( ) ;
91+ let new_signed_doc = builder. add_signature ( sk. to_bytes ( ) , kid) ?. build ( ) ?;
92+ save_signed_doc ( new_signed_doc, & doc) ?;
8793 } ,
8894 Self :: Inspect { path } => {
89- let mut cose_file = File :: open ( path) ?;
90- let mut cose_bytes = Vec :: new ( ) ;
91- cose_file. read_to_end ( & mut cose_bytes) ?;
92- decode_signed_doc ( & cose_bytes) ;
95+ let cose_bytes = read_bytes_from_file ( & path) ?;
96+ inspect_signed_doc ( & cose_bytes) ?;
9397 } ,
9498 Self :: InspectBytes { cose_sign_hex } => {
9599 let cose_bytes = hex:: decode ( & cose_sign_hex) ?;
96- decode_signed_doc ( & cose_bytes) ;
100+ inspect_signed_doc ( & cose_bytes) ?;
101+ } ,
102+ Self :: Verify { path, pk, kid } => {
103+ let pk = load_public_key_from_file ( & pk)
104+ . map_err ( |e| anyhow:: anyhow!( "Failed to load PK FILE {pk:?}: {e}" ) ) ?;
105+ let cose_bytes = read_bytes_from_file ( & path) ?;
106+ let signed_doc = signed_doc_from_bytes ( cose_bytes. as_slice ( ) ) ?;
107+ signed_doc
108+ . verify ( |k| {
109+ if k. to_string ( ) == kid. to_string ( ) {
110+ pk
111+ } else {
112+ k. role0_pk ( )
113+ }
114+ } )
115+ . map_err ( |e| anyhow:: anyhow!( "Catalyst Document Verification failed: {e}" ) ) ?;
116+ println ! ( "Catalyst Signed Document is Verified." ) ;
97117 } ,
98118 }
99119 println ! ( "Done" ) ;
100120 Ok ( ( ) )
101121 }
102122}
103123
104- fn decode_signed_doc ( cose_bytes : & [ u8 ] ) {
124+ fn read_bytes_from_file ( path : & PathBuf ) -> anyhow:: Result < Vec < u8 > > {
125+ let mut cose_file = File :: open ( path) ?;
126+ let mut cose_bytes = Vec :: new ( ) ;
127+ cose_file. read_to_end ( & mut cose_bytes) ?;
128+ Ok ( cose_bytes)
129+ }
130+
131+ fn inspect_signed_doc ( cose_bytes : & [ u8 ] ) -> anyhow:: Result < ( ) > {
105132 println ! (
106- "Decoding {} bytes: {}" ,
133+ "Decoding {} bytes:\n {}" ,
107134 cose_bytes. len( ) ,
108135 hex:: encode( cose_bytes)
109136 ) ;
137+ let cat_signed_doc = signed_doc_from_bytes ( cose_bytes) ?;
138+ println ! ( "This is a valid Catalyst Document." ) ;
139+ println ! ( "{cat_signed_doc}" ) ;
140+ Ok ( ( ) )
141+ }
110142
111- match CatalystSignedDocument :: try_from ( cose_bytes) {
112- Ok ( cat_signed_doc) => {
113- println ! ( "This is a valid Catalyst Document." ) ;
114- println ! ( "{cat_signed_doc}" ) ;
115- } ,
116- Err ( e) => eprintln ! ( "Invalid Catalyst Document, err: {e}" ) ,
117- }
143+ fn save_signed_doc ( signed_doc : CatalystSignedDocument , path : & PathBuf ) -> anyhow:: Result < ( ) > {
144+ let mut bytes: Vec < u8 > = Vec :: new ( ) ;
145+ minicbor:: encode ( signed_doc, & mut bytes)
146+ . map_err ( |e| anyhow:: anyhow!( "Failed to encode document: {e}" ) ) ?;
147+
148+ write_bytes_to_file ( & bytes, path)
149+ }
150+
151+ fn signed_doc_from_bytes ( cose_bytes : & [ u8 ] ) -> anyhow:: Result < CatalystSignedDocument > {
152+ CatalystSignedDocument :: try_from ( cose_bytes)
153+ . map_err ( |e| anyhow:: anyhow!( "Invalid Catalyst Document: {e}" ) )
118154}
119155
120156fn load_json_from_file < T > ( path : & PathBuf ) -> anyhow:: Result < T >
@@ -124,45 +160,20 @@ where T: for<'de> serde::Deserialize<'de> {
124160 Ok ( json)
125161}
126162
127- fn load_cose_from_file ( cose_path : & PathBuf ) -> anyhow:: Result < coset:: CoseSign > {
128- let cose_file_bytes = read_bytes_from_file ( cose_path) ?;
129- let cose = coset:: CoseSign :: from_slice ( & cose_file_bytes) . map_err ( |e| anyhow:: anyhow!( "{e}" ) ) ?;
130- Ok ( cose)
131- }
132-
133- fn read_bytes_from_file ( path : & PathBuf ) -> anyhow:: Result < Vec < u8 > > {
134- let mut file_bytes = Vec :: new ( ) ;
135- File :: open ( path) ?. read_to_end ( & mut file_bytes) ?;
136- Ok ( file_bytes)
137- }
138-
139163fn write_bytes_to_file ( bytes : & [ u8 ] , output : & PathBuf ) -> anyhow:: Result < ( ) > {
140164 File :: create ( output) ?
141165 . write_all ( bytes)
142166 . map_err ( |e| anyhow:: anyhow!( "Failed to write to file {output:?}: {e}" ) )
143167}
144168
145- fn store_cose_file ( cose : coset:: CoseSign , output : & PathBuf ) -> anyhow:: Result < ( ) > {
146- let cose_bytes = cose
147- . to_vec ( )
148- . map_err ( |e| anyhow:: anyhow!( "Failed to Store COSE SIGN: {e}" ) ) ?;
149- write_bytes_to_file ( & cose_bytes, output)
150- }
151-
152169fn load_secret_key_from_file ( sk_path : & PathBuf ) -> anyhow:: Result < ed25519_dalek:: SigningKey > {
153170 let sk_str = read_to_string ( sk_path) ?;
154171 let sk = ed25519_dalek:: SigningKey :: from_pkcs8_pem ( & sk_str) ?;
155172 Ok ( sk)
156173}
157174
158- fn add_signature_to_cose ( cose : & mut coset:: CoseSign , sk : & ed25519_dalek:: SigningKey , kid : String ) {
159- let protected_header = coset:: HeaderBuilder :: new ( )
160- . key_id ( kid. into_bytes ( ) )
161- . algorithm ( coset:: iana:: Algorithm :: EdDSA ) ;
162- let mut signature = coset:: CoseSignatureBuilder :: new ( )
163- . protected ( protected_header. build ( ) )
164- . build ( ) ;
165- let data_to_sign = cose. tbs_data ( & [ ] , & signature) ;
166- signature. signature = sk. sign ( & data_to_sign) . to_vec ( ) ;
167- cose. signatures . push ( signature) ;
175+ fn load_public_key_from_file ( pk_path : & PathBuf ) -> anyhow:: Result < ed25519_dalek:: VerifyingKey > {
176+ let pk_str = read_to_string ( pk_path) ?;
177+ let pk = ed25519_dalek:: VerifyingKey :: from_public_key_pem ( & pk_str) ?;
178+ Ok ( pk)
168179}
0 commit comments