1use std::convert::TryInto;
2
3use crate::Error;
4use crate::internal::get_json;
5use crate::jsons;
6use crate::utils::{combine_tree_hash, largest_power_of_2_smaller_than, u8_to_hex};
7use log::trace;
8
9pub fn consistency_proof_parts(from_size: u64, to_size: u64) -> Vec<(u64, u64)> {
34 fn inner(result_store: &mut Vec<(u64, u64)>, subtree: (u64, u64), from_size: u64) {
37 assert!(subtree.0 < subtree.1);
38 assert!(from_size <= subtree.1);
39 if from_size == subtree.1 {
40 result_store.push(subtree);
41 return;
42 }
43 let subtree_size = subtree.1 - subtree.0;
44 let start_of_right_branch = largest_power_of_2_smaller_than(subtree_size);
45 if from_size - subtree.0 <= start_of_right_branch {
46 result_store.push((subtree.0 + start_of_right_branch, subtree.1));
48 inner(
49 result_store,
50 (subtree.0, subtree.0 + start_of_right_branch),
51 from_size,
52 );
53 } else {
54 result_store.push((subtree.0, subtree.0 + start_of_right_branch));
56 inner(
57 result_store,
58 (subtree.0 + start_of_right_branch, subtree.1),
59 from_size,
60 );
61 }
62 }
63 let mut result_store = Vec::new();
64 inner(&mut result_store, (0, to_size), from_size);
65 result_store.reverse();
66 result_store
67}
68
69#[test]
70fn consistency_proof_partial_test() {
71 assert_eq!(consistency_proof_parts(753913835, 753913848).len(), 25);
72 assert_eq!(consistency_proof_parts(6, 6), vec![(0, 6)]);
73 assert_eq!(consistency_proof_parts(7, 7), vec![(0, 7)]);
74
75 assert_eq!(consistency_proof_parts(4, 7), vec![(0, 4), (4, 7)]);
76}
77
78pub struct ConsistencyProofPart {
80 pub subtree: (u64, u64),
82
83 pub server_hash: [u8; 32],
85}
86
87pub fn verify_consistency_proof(
115 perv_size: u64,
116 next_size: u64,
117 server_provided_proof: &[[u8; 32]],
118 perv_root: &[u8; 32],
119 next_root: &[u8; 32],
120) -> Result<Vec<ConsistencyProofPart>, String> {
121 if perv_size > next_size {
124 panic!("perv_size must be <= next_size");
125 }
126 if perv_size == next_size {
127 return Ok(Vec::new());
128 }
129 if perv_size == 0 {
130 return Ok(vec![ConsistencyProofPart {
132 subtree: (0, next_size),
133 server_hash: *next_root,
134 }]);
135 }
136
137 let calculated_proof = consistency_proof_parts(perv_size, next_size);
160
161 let omit_first = u64::is_power_of_two(perv_size);
167
168 let mut expected_proof_len = calculated_proof.len();
169 if omit_first {
170 expected_proof_len -= 1;
171 }
172 if server_provided_proof.len() != expected_proof_len {
173 return Err(format!(
174 "wrong proof length: expected {}, got {}",
175 expected_proof_len,
176 server_provided_proof.len()
177 ));
178 }
179
180 let mut hashes = Vec::new();
181 hashes.reserve(calculated_proof.len());
182 if omit_first {
183 hashes.push(*perv_root);
184 }
185 hashes.extend_from_slice(server_provided_proof);
186 assert_eq!(hashes.len(), calculated_proof.len());
187
188 let mut derived_new_hash = hashes[0];
193 let mut derived_new_hash_subtree = calculated_proof[0];
194 for (subtree, hash) in (1..hashes.len()).map(|i| (calculated_proof[i], &hashes[i])) {
195 assert_ne!(derived_new_hash_subtree.0, subtree.0);
197 if subtree.0 > derived_new_hash_subtree.0 {
208 assert_eq!(subtree.0, derived_new_hash_subtree.1);
210 derived_new_hash = combine_tree_hash(&derived_new_hash, hash);
211 derived_new_hash_subtree = (derived_new_hash_subtree.0, subtree.1);
212 } else {
213 assert_eq!(subtree.1, derived_new_hash_subtree.0);
215 derived_new_hash = combine_tree_hash(hash, &derived_new_hash);
216 derived_new_hash_subtree = (subtree.0, derived_new_hash_subtree.1);
217 }
218 }
219 if derived_new_hash != *next_root {
220 return Err(format!(
221 "calculated tree root {} does not match given tree root {}",
222 u8_to_hex(&derived_new_hash),
223 u8_to_hex(next_root)
224 ));
225 }
226
227 if omit_first {
229 trace!(
231 "consistency checked from {} to {}; previous tree is complete.",
232 &u8_to_hex(perv_root),
233 &u8_to_hex(next_root)
234 );
235 Ok(hashes
236 .iter()
237 .zip(calculated_proof.iter())
238 .skip(1)
239 .map(|(hash, subtree)| ConsistencyProofPart {
240 subtree: *subtree,
241 server_hash: *hash,
242 })
243 .collect())
244 } else {
245 assert!(calculated_proof[0].1 <= perv_size);
247 let mut derived_old_hash: [u8; 32] = hashes[0];
248 let mut derived_old_hash_subtree: (u64, u64) = calculated_proof[0];
249 let mut new_parts = Vec::new();
250 for (subtree, hash) in (1..hashes.len()).map(|i| (calculated_proof[i], &hashes[i])) {
251 if subtree.1 <= perv_size {
252 if subtree.0 > derived_old_hash_subtree.0 {
254 assert_eq!(subtree.0, derived_old_hash_subtree.1);
255 derived_old_hash = combine_tree_hash(&derived_old_hash, hash);
256 derived_old_hash_subtree = (derived_old_hash_subtree.0, subtree.1);
257 } else {
258 assert_eq!(subtree.1, derived_old_hash_subtree.0);
259 derived_old_hash = combine_tree_hash(hash, &derived_old_hash);
260 derived_old_hash_subtree = (subtree.0, derived_old_hash_subtree.1);
261 }
262 } else {
263 assert!(subtree.0 >= perv_size);
265 new_parts.push(ConsistencyProofPart {
266 subtree,
267 server_hash: *hash,
268 });
269 }
270 }
271 if derived_old_hash != *perv_root {
272 return Err(format!(
273 "calculated perv_root {} does not match given perv_root {}",
274 u8_to_hex(&derived_old_hash),
275 u8_to_hex(perv_root)
276 ));
277 }
278
279 trace!(
280 "consistency checked from {} to {}",
281 &u8_to_hex(perv_root),
282 &u8_to_hex(next_root)
283 );
284 Ok(new_parts)
285 }
286}
287
288impl ConsistencyProofPart {
289 pub fn verify(&self, leaf_hashes: &[[u8; 32]]) -> Result<(), String> {
302 let subtree_size = self.subtree.1 - self.subtree.0;
303 if leaf_hashes.len() as u64 != subtree_size {
304 panic!(
305 "expected leaf_hashes to have length {}, got {}",
306 subtree_size,
307 leaf_hashes.len()
308 );
309 }
310 if subtree_size == 1 {
311 return if leaf_hashes[0] != self.server_hash {
312 Err(format!(
313 "expected leaf_hashes to be [{}], got [{}]",
314 u8_to_hex(&self.server_hash),
315 u8_to_hex(&leaf_hashes[0])
316 ))
317 } else {
318 Ok(())
319 };
320 }
321 let mut round_hashes = Vec::from(leaf_hashes);
322 loop {
323 let mut new_round_hashes = Vec::new();
324 new_round_hashes.reserve(round_hashes.len() / 2);
325 for i in 0..(round_hashes.len() / 2) {
326 let hash_left = round_hashes[2 * i];
327 let hash_right = round_hashes[2 * i + 1];
328 new_round_hashes.push(combine_tree_hash(&hash_left, &hash_right));
329 }
330 if round_hashes.len() % 2 != 0 {
331 new_round_hashes.push(*round_hashes.last().unwrap());
332 }
333 round_hashes = new_round_hashes;
334 if round_hashes.len() == 1 {
335 break;
336 }
337 }
338 assert_eq!(round_hashes.len(), 1);
339 let calculated_hash = round_hashes[0];
340 if self.server_hash == calculated_hash {
341 Ok(())
342 } else {
343 Err(format!(
344 "Subtree {:?}: calculated that tree hash should be {}, but got {} from consistency check.",
345 &self.subtree,
346 u8_to_hex(&calculated_hash),
347 u8_to_hex(&self.server_hash)
348 ))
349 }
350 }
351}
352
353#[test]
354fn verify_consistency_proof_new_tree_leaf_hashes_test() {
355 use crate::utils::sha256;
356 fn h(s: &str) -> [u8; 32] {
357 sha256(s.as_bytes())
358 }
359 fn c(a: &[u8; 32], b: &[u8; 32]) -> [u8; 32] {
360 combine_tree_hash(a, b)
361 }
362
363 (ConsistencyProofPart {
364 subtree: (0, 1),
365 server_hash: h("a"),
366 })
367 .verify(&[h("a")])
368 .unwrap();
369
370 (ConsistencyProofPart {
371 subtree: (0, 1),
372 server_hash: h("a"),
373 })
374 .verify(&[h("b")])
375 .expect_err("!");
376
377 (ConsistencyProofPart {
378 subtree: (2, 4),
379 server_hash: c(&h("c"), &h("d")),
380 })
381 .verify(&[h("c"), h("d")])
382 .unwrap();
383
384 (ConsistencyProofPart {
385 subtree: (0, 6),
386 server_hash: c(
387 &c(&c(&h("a"), &h("b")), &c(&h("c"), &h("d"))),
388 &c(&h("e"), &h("f")),
389 ),
390 })
391 .verify(&[h("a"), h("b"), h("c"), h("d"), h("e"), h("f")])
392 .unwrap();
393
394 (ConsistencyProofPart {
395 subtree: (0, 6),
396 server_hash: c(
397 &c(&c(&h("a"), &h("b")), &c(&h("c"), &h("d"))),
398 &c(&h("e"), &h("f")),
399 ),
400 })
401 .verify(&[h("a"), h("b"), h("c"), h("g"), h("e"), h("f")])
402 .expect_err("!");
403
404 (ConsistencyProofPart {
405 subtree: (0, 6),
406 server_hash: c(
407 &c(&c(&h("a"), &h("b")), &c(&h("c"), &h("d"))),
408 &c(&h("e"), &h("f")),
409 ),
410 })
411 .verify(&[h("a"), h("b"), h("c"), h("d"), h("e"), h("g")])
412 .expect_err("!");
413
414 (ConsistencyProofPart {
415 subtree: (0, 4),
416 server_hash: c(&c(&h("a"), &h("b")), &c(&h("c"), &h("d"))),
417 })
418 .verify(&[h("a"), h("b"), h("c"), h("d")])
419 .unwrap();
420
421 (ConsistencyProofPart {
422 subtree: (0, 4),
423 server_hash: c(&c(&h("a"), &h("b")), &c(&h("c"), &h("d"))),
424 })
425 .verify(&[h("c"), h("d"), h("a"), h("b")])
426 .expect_err("!");
427}
428
429pub async fn check_consistency_proof(
450 client: &reqwest::Client,
451 base_url: &reqwest::Url,
452 prev_size: u64,
453 next_size: u64,
454 perv_root: &[u8; 32],
455 next_root: &[u8; 32],
456) -> Result<Vec<ConsistencyProofPart>, Error> {
457 assert!(prev_size < next_size);
458 let server_consistency_proof: jsons::ConsistencyProof = get_json(
459 client,
460 base_url,
461 &format!(
462 "ct/v1/get-sth-consistency?first={}&second={}",
463 prev_size, next_size
464 ),
465 )
466 .await?;
467 let server_consistency_proof = server_consistency_proof.consistency;
468 let mut parsed_server_proof: Vec<[u8; 32]> = Vec::new();
469 parsed_server_proof.reserve(server_consistency_proof.len());
470 let mut n = 0;
471 for i in server_consistency_proof.into_iter() {
472 n += 1;
473 let decoded = base64::decode(&i).map_err(|e| {
474 Error::MalformedResponseBody(format!(
475 "Can not base64 decode consistency proof element: {}",
476 &e
477 ))
478 })?;
479 if decoded.len() != 32 {
480 return Err(Error::MalformedResponseBody(
481 "Consistency proof element has length other than 32.".to_owned(),
482 ));
483 }
484 parsed_server_proof.push(decoded[..].try_into().unwrap());
485 }
486 assert_eq!(parsed_server_proof.len(), n);
487 verify_consistency_proof(
488 prev_size,
489 next_size,
490 &parsed_server_proof,
491 perv_root,
492 next_root,
493 )
494 .map_err(|e| Error::InvalidConsistencyProof {
495 prev_size,
496 new_size: next_size,
497 desc: e,
498 })
499}