11mod parser;
22
3- use crate :: { Hash , InclusionProof , Signature , SignedTreeHead , SigsumSignature , WitnessCosignature } ;
3+ // This needs to be in scope to write!() to a String
4+ use std:: fmt:: Write as _;
5+
6+ use crate :: {
7+ Hash , InclusionProof , Protoleaf , Signature , SignedTreeHead , SigsumSignature , WitnessCosignature ,
8+ } ;
49pub use parser:: ParseAsciiError ;
510use parser:: { Parser , Result } ;
611
@@ -27,6 +32,22 @@ impl SignedTreeHead {
2732 cosignatures,
2833 } )
2934 }
35+
36+ #[ allow( unused_must_use) ]
37+ pub fn to_ascii ( & self ) -> String {
38+ let mut ascii = String :: new ( ) ;
39+ writeln ! ( ascii, "size={}" , self . size) ;
40+ writeln ! ( ascii, "root_hash={:x}" , self . root_hash) ;
41+ writeln ! ( ascii, "signature={:x}" , self . signature) ;
42+ for cosig in self . cosignatures . iter ( ) {
43+ writeln ! (
44+ ascii,
45+ "cosignature={:x} {} {:x}" ,
46+ cosig. keyhash, cosig. timestamp, cosig. cosignature
47+ ) ;
48+ }
49+ ascii
50+ }
3051}
3152
3253impl InclusionProof {
@@ -43,6 +64,39 @@ impl InclusionProof {
4364 node_hashes,
4465 } )
4566 }
67+
68+ #[ allow( unused_must_use) ]
69+ pub fn to_ascii ( & self ) -> String {
70+ let mut ascii = String :: new ( ) ;
71+ writeln ! ( ascii, "leaf_index={}" , self . leaf_index) ;
72+ for h in self . node_hashes . iter ( ) {
73+ writeln ! ( ascii, "node_hash={h:x}" ) ;
74+ }
75+ ascii
76+ }
77+ }
78+
79+ impl Protoleaf {
80+ pub fn from_ascii ( input : & str ) -> Result < Self > {
81+ let mut p = Parser :: new ( input) ;
82+ let message = p. parse ( "message" ) ?;
83+ let signature = p. parse ( "signature" ) ?;
84+ let public_key = p. parse :: < [ u8 ; 32 ] > ( "public_key" ) ?. into ( ) ;
85+ Ok ( Protoleaf {
86+ message,
87+ signature,
88+ public_key,
89+ } )
90+ }
91+
92+ #[ allow( unused_must_use) ]
93+ pub fn to_ascii ( & self ) -> String {
94+ let mut ascii = String :: new ( ) ;
95+ writeln ! ( ascii, "message={:x}" , self . message) ;
96+ writeln ! ( ascii, "signature={:x}" , self . signature) ;
97+ writeln ! ( ascii, "public_key={:x}" , self . public_key) ;
98+ ascii
99+ }
46100}
47101
48102impl SigsumSignature {
@@ -76,67 +130,239 @@ impl SigsumSignature {
76130 proof,
77131 } )
78132 }
133+
134+ #[ allow( unused_must_use) ]
135+ pub fn to_ascii ( & self ) -> String {
136+ let mut ascii = String :: new ( ) ;
137+ writeln ! ( ascii, "version=2" ) ;
138+ writeln ! ( ascii, "log={:x}" , self . log_keyhash) ;
139+ writeln ! (
140+ ascii,
141+ "leaf={:x} {:x}" ,
142+ self . leaf_keyhash, self . leaf_signature
143+ ) ;
144+ writeln ! ( ascii) ;
145+ writeln ! ( ascii, "size={}" , self . sth. size) ;
146+ writeln ! ( ascii, "root_hash={:x}" , self . sth. root_hash) ;
147+ writeln ! ( ascii, "signature={:x}" , self . sth. signature) ;
148+ for cosig in self . sth . cosignatures . iter ( ) {
149+ writeln ! (
150+ ascii,
151+ "cosignature={:x} {} {:x}" ,
152+ cosig. keyhash, cosig. timestamp, cosig. cosignature
153+ ) ;
154+ }
155+ writeln ! ( ascii) ;
156+ writeln ! ( ascii, "leaf_index={}" , self . proof. leaf_index) ;
157+ for h in self . proof . node_hashes . iter ( ) {
158+ writeln ! ( ascii, "node_hash={h:x}" ) ;
159+ }
160+ ascii
161+ }
79162}
80163
81164#[ cfg( test) ]
82165mod tests {
83166 use super :: * ;
167+ use hex_literal:: hex;
168+ use lazy_static:: lazy_static;
169+
170+ lazy_static ! {
171+ static ref PROTOLEAF : Protoleaf = Protoleaf {
172+ message: hex!( "084c799cd551dd1d8d5c5f9a5d593b2e931f5e36122ee5c793c1d08a19839cc0" ) . into( ) ,
173+ signature: hex!( "e82c007b640da6375657e95bd9e3768017cd9b9478dffb7496a7de69b2e8608ceb35d336b44a3b1a3b0d4493cbc694bf87daf279684473024557dd427d34d900" ) . into( ) ,
174+ public_key: hex!( "16e8e5005d909e941b34d040b646dba5b6608e5e3353c7db860f9b8849eae245" ) . into( ) ,
175+ } ;
176+ }
177+
178+ const PROTOLEAF_ASCII : & str = "\
179+ message=084c799cd551dd1d8d5c5f9a5d593b2e931f5e36122ee5c793c1d08a19839cc0\n \
180+ signature=e82c007b640da6375657e95bd9e3768017cd9b9478dffb7496a7de69b2e8608ceb35d336b44a3b1a3b0d4493cbc694bf87daf279684473024557dd427d34d900\n \
181+ public_key=16e8e5005d909e941b34d040b646dba5b6608e5e3353c7db860f9b8849eae245\n \
182+ ";
183+
184+ #[ test]
185+ fn protoleaf_from_ascii ( ) {
186+ assert_eq ! ( * PROTOLEAF , Protoleaf :: from_ascii( PROTOLEAF_ASCII ) . unwrap( ) ) ;
187+ }
188+
189+ #[ test]
190+ fn protoleaf_to_ascii ( ) {
191+ assert_eq ! ( PROTOLEAF_ASCII , ( * PROTOLEAF ) . to_ascii( ) ) ;
192+ }
193+
194+ lazy_static ! {
195+ static ref STH : SignedTreeHead = SignedTreeHead {
196+ size: 1285 ,
197+ root_hash: hex!( "8100f29c0e9017a7512dab0911bf06a4b5b99cd77d8c710635307b5d217af1f6" ) . into( ) ,
198+ signature: hex!( "e327fe13e5c3d2043cbf69fe1b778f77cb10a8e14fc09309dd375c9af25903f9ec35906cfb2c36ab2d210329eb538a6673487d2d101800370c978634b6f9f70d" ) . into( ) ,
199+ cosignatures: vec![
200+ WitnessCosignature {
201+ keyhash: hex!( "1a450ecf1f49a4e4580c35e4d83316a74deda949dbb7d338e89d4315764d88de" ) . into( ) ,
202+ timestamp: 1687170591 ,
203+ cosignature: hex!( "cacc54d315609b796f72ac1d71d1bbc15667853ed980bd3e0f957de7a875b84bd2dcde6489fc3ed66428190ce588ac1061b0d5748e73cfb887ebf38d0b53060a" ) . into( ) ,
204+ } ,
205+ WitnessCosignature {
206+ keyhash: hex!( "73b6cbe5e3c8e679fb5967b78c59e95db2969a5c13b3423b5e69523e3d52f531" ) . into( ) ,
207+ timestamp: 1687170591 ,
208+ cosignature: hex!( "7f568da17c57ea322a9c2668ae9fc2c1d6ab5556d9a997e7bfa1cbc4dc5cf7b94e0cead42d481bf0d3d90ad2ee0d272e9e687f8f82fddf76d37d722c6815fe0f" ) . into( ) ,
209+ } ,
210+ ] ,
211+ } ;
212+ }
213+
214+ const STH_ASCII : & str = "\
215+ size=1285\n \
216+ root_hash=8100f29c0e9017a7512dab0911bf06a4b5b99cd77d8c710635307b5d217af1f6\n \
217+ signature=e327fe13e5c3d2043cbf69fe1b778f77cb10a8e14fc09309dd375c9af25903f9ec35906cfb2c36ab2d210329eb538a6673487d2d101800370c978634b6f9f70d\n \
218+ cosignature=1a450ecf1f49a4e4580c35e4d83316a74deda949dbb7d338e89d4315764d88de 1687170591 cacc54d315609b796f72ac1d71d1bbc15667853ed980bd3e0f957de7a875b84bd2dcde6489fc3ed66428190ce588ac1061b0d5748e73cfb887ebf38d0b53060a\n \
219+ cosignature=73b6cbe5e3c8e679fb5967b78c59e95db2969a5c13b3423b5e69523e3d52f531 1687170591 7f568da17c57ea322a9c2668ae9fc2c1d6ab5556d9a997e7bfa1cbc4dc5cf7b94e0cead42d481bf0d3d90ad2ee0d272e9e687f8f82fddf76d37d722c6815fe0f\n \
220+ ";
84221
85222 #[ test]
86223 fn sth_from_ascii ( ) {
87- insta:: assert_debug_snapshot!( SignedTreeHead :: from_ascii( include_str!(
88- "testdata/tree-head.ascii"
89- ) )
90- . unwrap( ) ) ;
224+ assert_eq ! ( * STH , SignedTreeHead :: from_ascii( STH_ASCII ) . unwrap( ) ) ;
91225 }
92226
93227 #[ test]
94- fn inclusionproof_from_ascii ( ) {
95- insta:: assert_debug_snapshot!( InclusionProof :: from_ascii( include_str!(
96- "testdata/inclusion-proof.ascii"
97- ) )
98- . unwrap( ) ) ;
228+ fn sth_to_ascii ( ) {
229+ assert_eq ! ( STH_ASCII , ( * STH ) . to_ascii( ) ) ;
230+ }
231+
232+ lazy_static ! {
233+ static ref PROOF : InclusionProof = InclusionProof {
234+ leaf_index: 2 ,
235+ node_hashes: vec![
236+ hex!( "35fd6eb70d46d60679775c346225688e6e84c02c3c7978e5c51daf8decc22d2f" ) . into( ) ,
237+ hex!( "11a2b46fb34efed4abbd144f8666bda8b83ee2ee6f7685062ed5cd68d616412a" ) . into( ) ,
238+ ] ,
239+ } ;
240+ }
241+
242+ const PROOF_ASCII : & str = "\
243+ leaf_index=2\n \
244+ node_hash=35fd6eb70d46d60679775c346225688e6e84c02c3c7978e5c51daf8decc22d2f\n \
245+ node_hash=11a2b46fb34efed4abbd144f8666bda8b83ee2ee6f7685062ed5cd68d616412a\n \
246+ ";
247+
248+ #[ test]
249+ fn proof_from_ascii ( ) {
250+ assert_eq ! ( * PROOF , InclusionProof :: from_ascii( PROOF_ASCII ) . unwrap( ) ) ;
99251 }
100252
253+ #[ test]
254+ fn proof_to_ascii ( ) {
255+ assert_eq ! ( PROOF_ASCII , ( * PROOF ) . to_ascii( ) ) ;
256+ }
257+
258+ lazy_static ! {
259+ static ref SSIG : SigsumSignature = SigsumSignature {
260+ log_keyhash: hex!( "4e89cc51651f0d95f3c6127c15e1a42e3ddf7046c5b17b752689c402e773bb4d" )
261+ . into( ) ,
262+ leaf_keyhash: hex!( "16e8e5005d909e941b34d040b646dba5b6608e5e3353c7db860f9b8849eae245" ) . into( ) ,
263+ leaf_signature: hex!( "e82c007b640da6375657e95bd9e3768017cd9b9478dffb7496a7de69b2e8608ceb35d336b44a3b1a3b0d4493cbc694bf87daf279684473024557dd427d34d900" ) . into( ) ,
264+ sth: ( * STH ) . clone( ) ,
265+ proof: ( * PROOF ) . clone( ) ,
266+ } ;
267+ }
268+
269+ const SSIG_ASCII : & str = "\
270+ version=2\n \
271+ log=4e89cc51651f0d95f3c6127c15e1a42e3ddf7046c5b17b752689c402e773bb4d\n \
272+ leaf=16e8e5005d909e941b34d040b646dba5b6608e5e3353c7db860f9b8849eae245 e82c007b640da6375657e95bd9e3768017cd9b9478dffb7496a7de69b2e8608ceb35d336b44a3b1a3b0d4493cbc694bf87daf279684473024557dd427d34d900\n \
273+ \n \
274+ size=1285\n \
275+ root_hash=8100f29c0e9017a7512dab0911bf06a4b5b99cd77d8c710635307b5d217af1f6\n \
276+ signature=e327fe13e5c3d2043cbf69fe1b778f77cb10a8e14fc09309dd375c9af25903f9ec35906cfb2c36ab2d210329eb538a6673487d2d101800370c978634b6f9f70d\n \
277+ cosignature=1a450ecf1f49a4e4580c35e4d83316a74deda949dbb7d338e89d4315764d88de 1687170591 cacc54d315609b796f72ac1d71d1bbc15667853ed980bd3e0f957de7a875b84bd2dcde6489fc3ed66428190ce588ac1061b0d5748e73cfb887ebf38d0b53060a\n \
278+ cosignature=73b6cbe5e3c8e679fb5967b78c59e95db2969a5c13b3423b5e69523e3d52f531 1687170591 7f568da17c57ea322a9c2668ae9fc2c1d6ab5556d9a997e7bfa1cbc4dc5cf7b94e0cead42d481bf0d3d90ad2ee0d272e9e687f8f82fddf76d37d722c6815fe0f\n \
279+ \n \
280+ leaf_index=2\n \
281+ node_hash=35fd6eb70d46d60679775c346225688e6e84c02c3c7978e5c51daf8decc22d2f\n \
282+ node_hash=11a2b46fb34efed4abbd144f8666bda8b83ee2ee6f7685062ed5cd68d616412a\n \
283+ ";
284+
101285 #[ test]
102286 fn sigsumsig_from_ascii ( ) {
103- insta:: assert_debug_snapshot!( SigsumSignature :: from_ascii( include_str!(
104- "testdata/sigsum-signature.ascii"
105- ) )
106- . unwrap( ) ) ;
287+ assert_eq ! ( * SSIG , SigsumSignature :: from_ascii( SSIG_ASCII ) . unwrap( ) ) ;
288+ }
289+
290+ #[ test]
291+ fn sigsumsig_to_ascii ( ) {
292+ assert_eq ! ( SSIG_ASCII , ( * SSIG ) . to_ascii( ) ) ;
107293 }
108294
109- macro_rules! test_parse_error {
110- ( $testname: ident, $file : literal, $errormsg : literal) => {
295+ macro_rules! test_ssig_parse_error {
296+ ( $testname: ident, $errormsg : literal, $input : literal) => {
111297 #[ test]
112298 fn $testname( ) {
113- let input = include_str!( $file) ;
114299 assert_eq!(
115300 $errormsg,
116- SigsumSignature :: from_ascii( input) . unwrap_err( ) . to_string( )
301+ SigsumSignature :: from_ascii( $ input) . unwrap_err( ) . to_string( )
117302 ) ;
118303 }
119304 } ;
120305 }
121306
122- test_parse_error ! (
123- sigsum_signature_missing_part,
124- "testdata/sigsum-signature_missing-part.ascii" ,
125- "parse error: expected 3 parts, got 2"
126- ) ;
127- test_parse_error ! (
128- sigsum_signature_too_many_parts,
129- "testdata/sigsum-signature_too-many-parts.ascii" ,
130- "parse error: expected 3 parts, got 4"
131- ) ;
132- test_parse_error ! (
133- sigsum_signature_version_not_supported,
134- "testdata/sigsum-signature_version-not-supported.ascii" ,
135- "parse error: version 666 not supported"
136- ) ;
137- test_parse_error ! (
138- sigsum_signature_extra_line,
139- "testdata/sigsum-signature_extra-line.ascii" ,
140- "parse error: expected an empty line after 'leaf'"
141- ) ;
307+ test_ssig_parse_error ! {
308+ ssig_missing_part,
309+ "expected 3 parts, got 2" ,
310+ "\
311+ version=2\n \
312+ log=4e89cc51651f0d95f3c6127c15e1a42e3ddf7046c5b17b752689c402e773bb4d\n \
313+ leaf=16e8e5005d909e941b34d040b646dba5b6608e5e3353c7db860f9b8849eae245 e82c007b640da6375657e95bd9e3768017cd9b9478dffb7496a7de69b2e8608ceb35d336b44a3b1a3b0d4493cbc694bf87daf279684473024557dd427d34d900\n \
314+ \n \
315+ size=4898\n \
316+ root_hash=5a2221ac3be281d2403abb44f9b6a8b1b1b2db97e5838d029a2df19db5e708bd\n \
317+ signature=67de0c13f8ae8a13ca2ad2aaab35326ba55186d240cbb48f4872a79847a41b5d2b43820dc8f4ae43030d432bbf753c365ba7a248fc12ea7be53f05bb72829409\n \
318+ "
319+ }
320+
321+ test_ssig_parse_error ! { sigsum_signature_too_many_parts,
322+ "expected 3 parts, got 4" ,
323+ "\
324+ version=2\n \
325+ log=4e89cc51651f0d95f3c6127c15e1a42e3ddf7046c5b17b752689c402e773bb4d\n \
326+ leaf=16e8e5005d909e941b34d040b646dba5b6608e5e3353c7db860f9b8849eae245 e82c007b640da6375657e95bd9e3768017cd9b9478dffb7496a7de69b2e8608ceb35d336b44a3b1a3b0d4493cbc694bf87daf279684473024557dd427d34d900\n \
327+ \n \
328+ size=1\n \
329+ root_hash=5a2221ac3be281d2403abb44f9b6a8b1b1b2db97e5838d029a2df19db5e708bd\n \
330+ signature=67de0c13f8ae8a13ca2ad2aaab35326ba55186d240cbb48f4872a79847a41b5d2b43820dc8f4ae43030d432bbf753c365ba7a248fc12ea7be53f05bb72829409\n \
331+ \n \
332+ leaf_index=1\n \
333+ \n \
334+ abc=123\n \
335+ "
336+ }
337+
338+ test_ssig_parse_error ! { sigsum_signature_version_not_supported,
339+ "version 666 not supported" ,
340+ "\
341+ version=666\n \
342+ log=4e89cc51651f0d95f3c6127c15e1a42e3ddf7046c5b17b752689c402e773bb4d\n \
343+ leaf=16e8e5005d909e941b34d040b646dba5b6608e5e3353c7db860f9b8849eae245 e82c007b640da6375657e95bd9e3768017cd9b9478dffb7496a7de69b2e8608ceb35d336b44a3b1a3b0d4493cbc694bf87daf279684473024557dd427d34d900\n \
344+ \n \
345+ size=1\n \
346+ root_hash=5a2221ac3be281d2403abb44f9b6a8b1b1b2db97e5838d029a2df19db5e708bd\n \
347+ signature=67de0c13f8ae8a13ca2ad2aaab35326ba55186d240cbb48f4872a79847a41b5d2b43820dc8f4ae43030d432bbf753c365ba7a248fc12ea7be53f05bb72829409\n \
348+ \n \
349+ leaf_index=1\n \
350+ "
351+ }
352+
353+ test_ssig_parse_error ! { sigsum_signature_extra_line,
354+ "expected an empty line after 'leaf'" ,
355+ "\
356+ version=2\n \
357+ log=4e89cc51651f0d95f3c6127c15e1a42e3ddf7046c5b17b752689c402e773bb4d\n \
358+ leaf=16e8e5005d909e941b34d040b646dba5b6608e5e3353c7db860f9b8849eae245 e82c007b640da6375657e95bd9e3768017cd9b9478dffb7496a7de69b2e8608ceb35d336b44a3b1a3b0d4493cbc694bf87daf279684473024557dd427d34d900\n \
359+ abc=123\n \
360+ \n \
361+ size=1\n \
362+ root_hash=5a2221ac3be281d2403abb44f9b6a8b1b1b2db97e5838d029a2df19db5e708bd\n \
363+ signature=67de0c13f8ae8a13ca2ad2aaab35326ba55186d240cbb48f4872a79847a41b5d2b43820dc8f4ae43030d432bbf753c365ba7a248fc12ea7be53f05bb72829409\n \
364+ \n \
365+ leaf_index=1\n \
366+ "
367+ }
142368}
0 commit comments