1use super::cid::Cid;
10use super::diff::DiffPage;
11use super::error::{Diff, Error};
12use super::key;
13use super::node::Node;
14use super::range::{RangeCursor, RangePage};
15use super::store::AsyncStore;
16use super::store::Store;
17use super::tree::Tree;
18use super::AsyncProlly;
19use super::Prolly;
20use serde::{Deserialize, Serialize};
21use sha2::{Digest, Sha256};
22use std::collections::{HashMap, HashSet};
23
24const PROOF_BUNDLE_VERSION: u64 = 1;
25const PROOF_BUNDLE_KIND_KEY: u8 = 1;
26const PROOF_BUNDLE_KIND_MULTI_KEY: u8 = 2;
27const PROOF_BUNDLE_KIND_RANGE: u8 = 3;
28const PROOF_BUNDLE_KIND_RANGE_PAGE: u8 = 4;
29const PROOF_BUNDLE_KIND_DIFF_PAGE: u8 = 5;
30const AUTHENTICATED_PROOF_ENVELOPE_VERSION: u64 = 1;
31const AUTHENTICATED_PROOF_ENVELOPE_ALGORITHM_HMAC_SHA256: &str = "hmac-sha256";
32const AUTHENTICATED_PROOF_ENVELOPE_DOMAIN: &[u8] = b"trail.prolly.authenticated-proof-envelope.v1";
33
34#[derive(Serialize, Deserialize)]
35struct ProofBundleWire {
36 version: u64,
37 kind: u8,
38 root: Option<Vec<u8>>,
39 keys: Vec<Vec<u8>>,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
41 start: Option<Vec<u8>>,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
43 end: Option<Vec<u8>>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 after: Option<Vec<u8>>,
46 path_node_bytes: Vec<Vec<u8>>,
47}
48
49#[derive(Serialize, Deserialize)]
50struct AuthenticatedProofEnvelopeSigningWire {
51 version: u64,
52 algorithm: String,
53 key_id: Vec<u8>,
54 proof_bundle: Vec<u8>,
55 context: Vec<u8>,
56 issued_at_millis: Option<u64>,
57 expires_at_millis: Option<u64>,
58 nonce: Vec<u8>,
59}
60
61#[derive(Serialize, Deserialize)]
62struct AuthenticatedProofEnvelopeWire {
63 version: u64,
64 algorithm: String,
65 key_id: Vec<u8>,
66 proof_bundle: Vec<u8>,
67 context: Vec<u8>,
68 issued_at_millis: Option<u64>,
69 expires_at_millis: Option<u64>,
70 nonce: Vec<u8>,
71 signature: Vec<u8>,
72}
73
74#[derive(Serialize, Deserialize)]
75struct DiffPageProofBundleWire {
76 version: u64,
77 kind: u8,
78 requested_end: Option<Vec<u8>>,
79 limit: u64,
80 base_range_page_proof: Vec<u8>,
81 other_range_page_proof: Vec<u8>,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
83 lookahead_base_key_proof: Option<Vec<u8>>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 lookahead_other_key_proof: Option<Vec<u8>>,
86}
87
88#[derive(Clone, Debug, PartialEq)]
90pub struct KeyProof {
91 pub root: Option<Cid>,
93 pub key: Vec<u8>,
95 pub path: Vec<Node>,
97}
98
99#[derive(Clone, Debug, PartialEq, Eq)]
101pub struct KeyProofVerification {
102 pub valid: bool,
104 pub root: Option<Cid>,
106 pub key: Vec<u8>,
108 pub value: Option<Vec<u8>>,
111}
112
113#[derive(Clone, Debug, PartialEq)]
119pub struct MultiKeyProof {
120 pub root: Option<Cid>,
123 pub keys: Vec<Vec<u8>>,
125 pub path: Vec<Node>,
128}
129
130#[derive(Clone, Debug, PartialEq, Eq)]
132pub struct MultiKeyProofVerification {
133 pub valid: bool,
135 pub root: Option<Cid>,
137 pub results: Vec<KeyProofVerification>,
139}
140
141#[derive(Clone, Debug, PartialEq)]
147pub struct RangeProof {
148 pub root: Option<Cid>,
151 pub start: Vec<u8>,
153 pub end: Option<Vec<u8>>,
155 pub path: Vec<Node>,
157}
158
159#[derive(Clone, Debug, PartialEq)]
165pub struct RangePageProof {
166 pub root: Option<Cid>,
169 pub after: Option<Vec<u8>>,
172 pub end: Option<Vec<u8>>,
174 pub path: Vec<Node>,
176}
177
178#[derive(Clone, Debug, PartialEq)]
180pub struct ProvedRangePage {
181 pub page: RangePage,
184 pub proof: RangePageProof,
187}
188
189#[derive(Clone, Debug, PartialEq)]
196pub struct DiffPageProof {
197 pub base: RangePageProof,
199 pub other: RangePageProof,
201 pub lookahead_base: Option<KeyProof>,
204 pub lookahead_other: Option<KeyProof>,
207 pub requested_end: Option<Vec<u8>>,
209 pub limit: usize,
211}
212
213#[derive(Clone, Debug, PartialEq)]
215pub struct ProvedDiffPage {
216 pub page: DiffPage,
219 pub proof: DiffPageProof,
222}
223
224#[derive(Clone, Debug, PartialEq, Eq)]
232pub struct AuthenticatedProofEnvelope {
233 pub algorithm: String,
235 pub key_id: Vec<u8>,
237 pub proof_bundle: Vec<u8>,
240 pub context: Vec<u8>,
243 pub issued_at_millis: Option<u64>,
245 pub expires_at_millis: Option<u64>,
247 pub nonce: Vec<u8>,
249 pub signature: Vec<u8>,
251}
252
253#[derive(Clone, Debug, PartialEq, Eq)]
255pub struct AuthenticatedProofEnvelopeVerification {
256 pub valid: bool,
259 pub signature_valid: bool,
262 pub time_valid: bool,
265 pub not_yet_valid: bool,
267 pub expired: bool,
270 pub algorithm: String,
272 pub key_id: Vec<u8>,
274 pub proof_bundle: Vec<u8>,
276 pub context: Vec<u8>,
278 pub issued_at_millis: Option<u64>,
280 pub expires_at_millis: Option<u64>,
282 pub nonce: Vec<u8>,
284}
285
286#[derive(Clone, Copy, Debug, PartialEq, Eq)]
288pub enum ProofBundleKind {
289 Key,
291 MultiKey,
293 Range,
295 RangePage,
297 DiffPage,
299}
300
301impl ProofBundleKind {
302 pub fn as_str(self) -> &'static str {
304 match self {
305 ProofBundleKind::Key => "key",
306 ProofBundleKind::MultiKey => "multi_key",
307 ProofBundleKind::Range => "range",
308 ProofBundleKind::RangePage => "range_page",
309 ProofBundleKind::DiffPage => "diff_page",
310 }
311 }
312}
313
314#[derive(Clone, Debug, PartialEq, Eq)]
320pub struct ProofBundleSummary {
321 pub version: u64,
323 pub kind: ProofBundleKind,
325 pub root: Option<Cid>,
327 pub other_root: Option<Cid>,
329 pub key_count: usize,
331 pub path_node_count: usize,
334 pub start: Option<Vec<u8>>,
336 pub end: Option<Vec<u8>>,
338 pub after: Option<Vec<u8>>,
340 pub requested_end: Option<Vec<u8>>,
342 pub limit: Option<usize>,
344 pub has_lookahead: bool,
346}
347
348#[derive(Clone, Debug, PartialEq, Eq)]
355pub struct ProofBundleVerification {
356 pub summary: ProofBundleSummary,
358 pub valid: bool,
360 pub exists_count: usize,
362 pub absence_count: usize,
364 pub entry_count: usize,
366 pub diff_count: usize,
368 pub next_cursor: Option<RangeCursor>,
370}
371
372#[derive(Clone, Debug, PartialEq, Eq)]
379pub struct AuthenticatedProofBundleVerification {
380 pub valid: bool,
382 pub envelope: AuthenticatedProofEnvelopeVerification,
384 pub proof: Option<ProofBundleVerification>,
386 pub proof_error: Option<String>,
388}
389
390#[derive(Clone, Debug, PartialEq, Eq)]
392pub struct RangeProofVerification {
393 pub valid: bool,
395 pub root: Option<Cid>,
397 pub start: Vec<u8>,
399 pub end: Option<Vec<u8>>,
401 pub entries: Vec<(Vec<u8>, Vec<u8>)>,
403}
404
405#[derive(Clone, Debug, PartialEq, Eq)]
407pub struct RangePageProofVerification {
408 pub valid: bool,
411 pub root: Option<Cid>,
413 pub after: Option<Vec<u8>>,
415 pub end: Option<Vec<u8>>,
417 pub entries: Vec<(Vec<u8>, Vec<u8>)>,
419}
420
421#[derive(Clone, Debug, PartialEq, Eq)]
423pub struct DiffPageProofVerification {
424 pub valid: bool,
427 pub base_valid: bool,
429 pub other_valid: bool,
431 pub lookahead_valid: bool,
434 pub base_root: Option<Cid>,
436 pub other_root: Option<Cid>,
438 pub after: Option<Vec<u8>>,
440 pub requested_end: Option<Vec<u8>>,
442 pub proof_end: Option<Vec<u8>>,
446 pub limit: usize,
448 pub diffs: Vec<Diff>,
450 pub next_cursor: Option<RangeCursor>,
452}
453
454impl KeyProofVerification {
455 pub fn exists(&self) -> bool {
457 self.valid && self.value.is_some()
458 }
459
460 pub fn is_absence(&self) -> bool {
462 self.valid && self.value.is_none()
463 }
464}
465
466impl MultiKeyProofVerification {
467 pub fn all_valid(&self) -> bool {
469 self.valid && self.results.iter().all(|result| result.valid)
470 }
471}
472
473impl RangeProofVerification {
474 pub fn is_empty(&self) -> bool {
476 self.valid && self.entries.is_empty()
477 }
478}
479
480impl RangePageProofVerification {
481 pub fn is_empty(&self) -> bool {
483 self.valid && self.entries.is_empty()
484 }
485}
486
487impl DiffPageProofVerification {
488 pub fn is_empty(&self) -> bool {
490 self.valid && self.diffs.is_empty()
491 }
492}
493
494impl AuthenticatedProofEnvelope {
495 pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
497 authenticated_proof_envelope_to_bytes(self)
498 }
499
500 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
503 authenticated_proof_envelope_from_bytes(bytes)
504 }
505
506 pub fn verify(
509 &self,
510 secret: &[u8],
511 now_millis: Option<u64>,
512 ) -> AuthenticatedProofEnvelopeVerification {
513 verify_authenticated_proof_envelope(self, secret, now_millis)
514 }
515}
516
517impl ProofBundleSummary {
518 pub fn kind_name(&self) -> &'static str {
520 self.kind.as_str()
521 }
522}
523
524impl ProofBundleVerification {
525 pub fn kind_name(&self) -> &'static str {
527 self.summary.kind_name()
528 }
529}
530
531impl KeyProof {
532 pub fn verify(&self) -> KeyProofVerification {
534 verify_key_proof(self)
535 }
536
537 pub fn path_node_bytes(&self) -> Vec<Vec<u8>> {
539 self.path.iter().map(Node::to_bytes).collect()
540 }
541
542 pub fn from_node_bytes(
544 root: Option<Cid>,
545 key: impl Into<Vec<u8>>,
546 path_node_bytes: Vec<Vec<u8>>,
547 ) -> Result<Self, Error> {
548 let path = path_node_bytes
549 .iter()
550 .map(|bytes| Node::from_bytes(bytes))
551 .collect::<Result<Vec<_>, _>>()?;
552 Ok(Self {
553 root,
554 key: key.into(),
555 path,
556 })
557 }
558
559 pub fn to_bundle_bytes(&self) -> Result<Vec<u8>, Error> {
561 proof_bundle_to_bytes(ProofBundleWire {
562 version: PROOF_BUNDLE_VERSION,
563 kind: PROOF_BUNDLE_KIND_KEY,
564 root: self.root.as_ref().map(|cid| cid.as_bytes().to_vec()),
565 keys: vec![self.key.clone()],
566 start: None,
567 end: None,
568 after: None,
569 path_node_bytes: self.path_node_bytes(),
570 })
571 }
572
573 pub fn from_bundle_bytes(bytes: &[u8]) -> Result<Self, Error> {
575 let wire = proof_bundle_from_bytes(bytes)?;
576 if wire.kind != PROOF_BUNDLE_KIND_KEY {
577 return Err(proof_bundle_deserialize("proof bundle is not a key proof"));
578 }
579 if wire.keys.len() != 1 {
580 return Err(proof_bundle_deserialize(
581 "key proof bundle must contain exactly one key",
582 ));
583 }
584 Self::from_node_bytes(
585 cid_from_bundle_root(wire.root)?,
586 wire.keys.into_iter().next().unwrap(),
587 wire.path_node_bytes,
588 )
589 }
590}
591
592impl MultiKeyProof {
593 pub fn verify(&self) -> MultiKeyProofVerification {
595 verify_multi_key_proof(self)
596 }
597
598 pub fn path_node_bytes(&self) -> Vec<Vec<u8>> {
600 self.path.iter().map(Node::to_bytes).collect()
601 }
602
603 pub fn from_node_bytes(
605 root: Option<Cid>,
606 keys: Vec<Vec<u8>>,
607 path_node_bytes: Vec<Vec<u8>>,
608 ) -> Result<Self, Error> {
609 let path = path_node_bytes
610 .iter()
611 .map(|bytes| Node::from_bytes(bytes))
612 .collect::<Result<Vec<_>, _>>()?;
613 Ok(Self { root, keys, path })
614 }
615
616 pub fn to_bundle_bytes(&self) -> Result<Vec<u8>, Error> {
618 proof_bundle_to_bytes(ProofBundleWire {
619 version: PROOF_BUNDLE_VERSION,
620 kind: PROOF_BUNDLE_KIND_MULTI_KEY,
621 root: self.root.as_ref().map(|cid| cid.as_bytes().to_vec()),
622 keys: self.keys.clone(),
623 start: None,
624 end: None,
625 after: None,
626 path_node_bytes: self.path_node_bytes(),
627 })
628 }
629
630 pub fn from_bundle_bytes(bytes: &[u8]) -> Result<Self, Error> {
632 let wire = proof_bundle_from_bytes(bytes)?;
633 if wire.kind != PROOF_BUNDLE_KIND_MULTI_KEY {
634 return Err(proof_bundle_deserialize(
635 "proof bundle is not a multi-key proof",
636 ));
637 }
638 Self::from_node_bytes(
639 cid_from_bundle_root(wire.root)?,
640 wire.keys,
641 wire.path_node_bytes,
642 )
643 }
644}
645
646impl RangeProof {
647 pub fn verify(&self) -> RangeProofVerification {
649 verify_range_proof(self)
650 }
651
652 pub fn path_node_bytes(&self) -> Vec<Vec<u8>> {
654 self.path.iter().map(Node::to_bytes).collect()
655 }
656
657 pub fn from_node_bytes(
659 root: Option<Cid>,
660 start: impl Into<Vec<u8>>,
661 end: Option<Vec<u8>>,
662 path_node_bytes: Vec<Vec<u8>>,
663 ) -> Result<Self, Error> {
664 let path = path_node_bytes
665 .iter()
666 .map(|bytes| Node::from_bytes(bytes))
667 .collect::<Result<Vec<_>, _>>()?;
668 Ok(Self {
669 root,
670 start: start.into(),
671 end,
672 path,
673 })
674 }
675
676 pub fn to_bundle_bytes(&self) -> Result<Vec<u8>, Error> {
678 proof_bundle_to_bytes(ProofBundleWire {
679 version: PROOF_BUNDLE_VERSION,
680 kind: PROOF_BUNDLE_KIND_RANGE,
681 root: self.root.as_ref().map(|cid| cid.as_bytes().to_vec()),
682 keys: Vec::new(),
683 start: Some(self.start.clone()),
684 end: self.end.clone(),
685 after: None,
686 path_node_bytes: self.path_node_bytes(),
687 })
688 }
689
690 pub fn from_bundle_bytes(bytes: &[u8]) -> Result<Self, Error> {
692 let wire = proof_bundle_from_bytes(bytes)?;
693 if wire.kind != PROOF_BUNDLE_KIND_RANGE {
694 return Err(proof_bundle_deserialize(
695 "proof bundle is not a range proof",
696 ));
697 }
698 let Some(start) = wire.start else {
699 return Err(proof_bundle_deserialize(
700 "range proof bundle must contain a start key",
701 ));
702 };
703 Self::from_node_bytes(
704 cid_from_bundle_root(wire.root)?,
705 start,
706 wire.end,
707 wire.path_node_bytes,
708 )
709 }
710}
711
712impl RangePageProof {
713 pub fn verify(&self) -> RangePageProofVerification {
715 verify_range_page_proof(self)
716 }
717
718 pub fn path_node_bytes(&self) -> Vec<Vec<u8>> {
720 self.path.iter().map(Node::to_bytes).collect()
721 }
722
723 pub fn from_node_bytes(
725 root: Option<Cid>,
726 after: Option<Vec<u8>>,
727 end: Option<Vec<u8>>,
728 path_node_bytes: Vec<Vec<u8>>,
729 ) -> Result<Self, Error> {
730 let path = path_node_bytes
731 .iter()
732 .map(|bytes| Node::from_bytes(bytes))
733 .collect::<Result<Vec<_>, _>>()?;
734 Ok(Self {
735 root,
736 after,
737 end,
738 path,
739 })
740 }
741
742 pub fn to_bundle_bytes(&self) -> Result<Vec<u8>, Error> {
744 proof_bundle_to_bytes(ProofBundleWire {
745 version: PROOF_BUNDLE_VERSION,
746 kind: PROOF_BUNDLE_KIND_RANGE_PAGE,
747 root: self.root.as_ref().map(|cid| cid.as_bytes().to_vec()),
748 keys: Vec::new(),
749 start: None,
750 end: self.end.clone(),
751 after: self.after.clone(),
752 path_node_bytes: self.path_node_bytes(),
753 })
754 }
755
756 pub fn from_bundle_bytes(bytes: &[u8]) -> Result<Self, Error> {
758 let wire = proof_bundle_from_bytes(bytes)?;
759 if wire.kind != PROOF_BUNDLE_KIND_RANGE_PAGE {
760 return Err(proof_bundle_deserialize(
761 "proof bundle is not a range page proof",
762 ));
763 }
764 Self::from_node_bytes(
765 cid_from_bundle_root(wire.root)?,
766 wire.after,
767 wire.end,
768 wire.path_node_bytes,
769 )
770 }
771}
772
773impl DiffPageProof {
774 pub fn verify(&self) -> DiffPageProofVerification {
776 verify_diff_page_proof(self)
777 }
778
779 pub fn to_bundle_bytes(&self) -> Result<Vec<u8>, Error> {
782 let limit = u64::try_from(self.limit)
783 .map_err(|_| Error::Serialize("diff page proof limit is too large".to_string()))?;
784 let base_range_page_proof = self.base.to_bundle_bytes()?;
785 let other_range_page_proof = self.other.to_bundle_bytes()?;
786 let lookahead_base_key_proof = self
787 .lookahead_base
788 .as_ref()
789 .map(KeyProof::to_bundle_bytes)
790 .transpose()?;
791 let lookahead_other_key_proof = self
792 .lookahead_other
793 .as_ref()
794 .map(KeyProof::to_bundle_bytes)
795 .transpose()?;
796
797 serde_cbor::ser::to_vec_packed(&DiffPageProofBundleWire {
798 version: PROOF_BUNDLE_VERSION,
799 kind: PROOF_BUNDLE_KIND_DIFF_PAGE,
800 requested_end: self.requested_end.clone(),
801 limit,
802 base_range_page_proof,
803 other_range_page_proof,
804 lookahead_base_key_proof,
805 lookahead_other_key_proof,
806 })
807 .map_err(|err| Error::Serialize(err.to_string()))
808 }
809
810 pub fn from_bundle_bytes(bytes: &[u8]) -> Result<Self, Error> {
812 let wire = diff_page_proof_bundle_from_bytes(bytes)?;
813 let limit = usize::try_from(wire.limit)
814 .map_err(|_| proof_bundle_deserialize("diff page proof bundle limit is too large"))?;
815
816 Ok(Self {
817 base: RangePageProof::from_bundle_bytes(&wire.base_range_page_proof)?,
818 other: RangePageProof::from_bundle_bytes(&wire.other_range_page_proof)?,
819 lookahead_base: wire
820 .lookahead_base_key_proof
821 .map(|proof| KeyProof::from_bundle_bytes(&proof))
822 .transpose()?,
823 lookahead_other: wire
824 .lookahead_other_key_proof
825 .map(|proof| KeyProof::from_bundle_bytes(&proof))
826 .transpose()?,
827 requested_end: wire.requested_end,
828 limit,
829 })
830 }
831}
832
833impl<S: Store> Prolly<S> {
834 pub fn prove_key(&self, tree: &Tree, key: &[u8]) -> Result<KeyProof, Error> {
839 let ready_store = self.engine.store.clone();
840 let future = self.engine.prove_key(tree, key);
841 super::engine::ready::run_ready(ready_store.ready(future))
842 }
843
844 pub fn prove_keys<K: AsRef<[u8]>>(
850 &self,
851 tree: &Tree,
852 keys: &[K],
853 ) -> Result<MultiKeyProof, Error> {
854 let ready_store = self.engine.store.clone();
855 let future = self.engine.prove_keys(tree, keys);
856 super::engine::ready::run_ready(ready_store.ready(future))
857 }
858
859 pub fn prove_range(
864 &self,
865 tree: &Tree,
866 start: &[u8],
867 end: Option<&[u8]>,
868 ) -> Result<RangeProof, Error> {
869 let ready_store = self.engine.store.clone();
870 let future = self.engine.prove_range(tree, start, end);
871 super::engine::ready::run_ready(ready_store.ready(future))
872 }
873
874 pub fn prove_prefix(&self, tree: &Tree, prefix: &[u8]) -> Result<RangeProof, Error> {
880 let ready_store = self.engine.store.clone();
881 let future = self.engine.prove_prefix(tree, prefix);
882 super::engine::ready::run_ready(ready_store.ready(future))
883 }
884
885 pub fn prove_range_page(
891 &self,
892 tree: &Tree,
893 cursor: &RangeCursor,
894 end: Option<&[u8]>,
895 limit: usize,
896 ) -> Result<ProvedRangePage, Error> {
897 let ready_store = self.engine.store.clone();
898 let future = self.engine.prove_range_page(tree, cursor, end, limit);
899 super::engine::ready::run_ready(ready_store.ready(future))
900 }
901
902 pub fn prove_diff_page(
908 &self,
909 base: &Tree,
910 other: &Tree,
911 cursor: &RangeCursor,
912 end: Option<&[u8]>,
913 limit: usize,
914 ) -> Result<ProvedDiffPage, Error> {
915 let ready_store = self.engine.store.clone();
916 let future = self.engine.prove_diff_page(base, other, cursor, end, limit);
917 super::engine::ready::run_ready(ready_store.ready(future))
918 }
919
920 #[cfg(test)]
921 #[expect(
922 dead_code,
923 reason = "retained only as a correctness oracle for async proofs"
924 )]
925 fn prove_range_page_window(
926 &self,
927 tree: &Tree,
928 after: Option<&[u8]>,
929 end: Option<&[u8]>,
930 ) -> Result<RangePageProof, Error> {
931 let Some(root_cid) = &tree.root else {
932 return Ok(RangePageProof {
933 root: None,
934 after: after.map(<[u8]>::to_vec),
935 end: end.map(<[u8]>::to_vec),
936 path: Vec::new(),
937 });
938 };
939
940 if page_range_is_empty_by_bounds(after, end) {
941 return Ok(RangePageProof {
942 root: Some(root_cid.clone()),
943 after: after.map(<[u8]>::to_vec),
944 end: end.map(<[u8]>::to_vec),
945 path: Vec::new(),
946 });
947 }
948
949 let mut seen = HashSet::new();
950 let mut path = Vec::new();
951 self.collect_range_page_proof_nodes(root_cid, after, end, &mut seen, &mut path)?;
952
953 Ok(RangePageProof {
954 root: Some(root_cid.clone()),
955 after: after.map(<[u8]>::to_vec),
956 end: end.map(<[u8]>::to_vec),
957 path,
958 })
959 }
960
961 #[cfg(test)]
962 #[expect(
963 dead_code,
964 reason = "retained only as a correctness oracle for async proofs"
965 )]
966 fn collect_range_proof_nodes(
967 &self,
968 cid: &Cid,
969 start: &[u8],
970 end: Option<&[u8]>,
971 seen: &mut HashSet<Cid>,
972 path: &mut Vec<Node>,
973 ) -> Result<(), Error> {
974 let node = self.load(cid)?;
975 if !seen.insert(cid.clone()) {
976 return Ok(());
977 }
978 path.push(node.clone());
979
980 if node.leaf {
981 return Ok(());
982 }
983
984 for idx in overlapping_child_index_range(&node, start, end) {
985 let child_start = node.keys[idx].as_slice();
986 let child_end = child_span_end(&node, idx, None);
987 if !span_overlaps_range(child_start, child_end, start, end) {
988 if range_ends_before_or_at(end, child_start) {
989 break;
990 }
991 continue;
992 }
993 let child_cid = cid_from_child_bytes(node.vals.get(idx).ok_or(Error::InvalidNode)?)
994 .ok_or(Error::InvalidNode)?;
995 self.collect_range_proof_nodes(&child_cid, start, end, seen, path)?;
996 }
997
998 Ok(())
999 }
1000
1001 #[cfg(test)]
1002 #[expect(
1003 dead_code,
1004 reason = "retained only as a correctness oracle for async proofs"
1005 )]
1006 fn collect_range_page_proof_nodes(
1007 &self,
1008 cid: &Cid,
1009 after: Option<&[u8]>,
1010 end: Option<&[u8]>,
1011 seen: &mut HashSet<Cid>,
1012 path: &mut Vec<Node>,
1013 ) -> Result<(), Error> {
1014 let node = self.load(cid)?;
1015 if !seen.insert(cid.clone()) {
1016 return Ok(());
1017 }
1018 path.push(node.clone());
1019
1020 if node.leaf {
1021 return Ok(());
1022 }
1023
1024 let traversal_start = after.unwrap_or(&[]);
1025 for idx in overlapping_child_index_range(&node, traversal_start, end) {
1026 let child_start = node.keys[idx].as_slice();
1027 let child_end = child_span_end(&node, idx, None);
1028 if !span_overlaps_page_range(child_start, child_end, after, end) {
1029 if range_ends_before_or_at(end, child_start) {
1030 break;
1031 }
1032 continue;
1033 }
1034 let child_cid = cid_from_child_bytes(node.vals.get(idx).ok_or(Error::InvalidNode)?)
1035 .ok_or(Error::InvalidNode)?;
1036 self.collect_range_page_proof_nodes(&child_cid, after, end, seen, path)?;
1037 }
1038
1039 Ok(())
1040 }
1041}
1042impl<S> AsyncProlly<S>
1043where
1044 S: AsyncStore,
1045 S::Error: Send + Sync,
1046{
1047 pub async fn prove_key(&self, tree: &Tree, key: &[u8]) -> Result<KeyProof, Error> {
1052 let mut path = Vec::new();
1053
1054 let Some(root_cid) = &tree.root else {
1055 return Ok(KeyProof {
1056 root: None,
1057 key: key.to_vec(),
1058 path,
1059 });
1060 };
1061
1062 let mut cid = root_cid.clone();
1063 loop {
1064 let node = self.load_arc(&cid).await?;
1065 let is_leaf = node.leaf;
1066 let child_index = path_child_index(&node, key);
1067 path.push((*node).clone());
1068
1069 if is_leaf {
1070 break;
1071 }
1072
1073 let Some(child_bytes) = node.vals.get(child_index) else {
1074 return Err(Error::InvalidNode);
1075 };
1076 cid = cid_from_child_bytes(child_bytes).ok_or(Error::InvalidNode)?;
1077 }
1078
1079 Ok(KeyProof {
1080 root: Some(root_cid.clone()),
1081 key: key.to_vec(),
1082 path,
1083 })
1084 }
1085
1086 pub async fn prove_keys<K: AsRef<[u8]>>(
1092 &self,
1093 tree: &Tree,
1094 keys: &[K],
1095 ) -> Result<MultiKeyProof, Error> {
1096 let keys = keys
1097 .iter()
1098 .map(|key| key.as_ref().to_vec())
1099 .collect::<Vec<_>>();
1100 let mut path = Vec::new();
1101
1102 let Some(root_cid) = &tree.root else {
1103 return Ok(MultiKeyProof {
1104 root: None,
1105 keys,
1106 path,
1107 });
1108 };
1109
1110 if keys.is_empty() {
1111 return Ok(MultiKeyProof {
1112 root: Some(root_cid.clone()),
1113 keys,
1114 path,
1115 });
1116 }
1117
1118 let mut seen = HashSet::new();
1119 for key in &keys {
1120 let key_proof = self.prove_key(tree, key).await?;
1121 for node in key_proof.path {
1122 let cid = node.cid();
1123 if seen.insert(cid) {
1124 path.push(node);
1125 }
1126 }
1127 }
1128
1129 Ok(MultiKeyProof {
1130 root: Some(root_cid.clone()),
1131 keys,
1132 path,
1133 })
1134 }
1135
1136 pub async fn prove_range(
1141 &self,
1142 tree: &Tree,
1143 start: &[u8],
1144 end: Option<&[u8]>,
1145 ) -> Result<RangeProof, Error> {
1146 let mut path = Vec::new();
1147
1148 let Some(root_cid) = &tree.root else {
1149 return Ok(RangeProof {
1150 root: None,
1151 start: start.to_vec(),
1152 end: end.map(<[u8]>::to_vec),
1153 path,
1154 });
1155 };
1156
1157 if range_is_empty_by_bounds(start, end) {
1158 return Ok(RangeProof {
1159 root: Some(root_cid.clone()),
1160 start: start.to_vec(),
1161 end: end.map(<[u8]>::to_vec),
1162 path,
1163 });
1164 }
1165
1166 let mut seen = HashSet::new();
1167 self.collect_range_proof_nodes(root_cid, start, end, &mut seen, &mut path)
1168 .await?;
1169
1170 Ok(RangeProof {
1171 root: Some(root_cid.clone()),
1172 start: start.to_vec(),
1173 end: end.map(<[u8]>::to_vec),
1174 path,
1175 })
1176 }
1177
1178 pub async fn prove_prefix(&self, tree: &Tree, prefix: &[u8]) -> Result<RangeProof, Error> {
1180 let (start, end) = key::prefix_range(prefix);
1181 self.prove_range(tree, &start, end.as_deref()).await
1182 }
1183
1184 pub async fn prove_range_page(
1186 &self,
1187 tree: &Tree,
1188 cursor: &RangeCursor,
1189 end: Option<&[u8]>,
1190 limit: usize,
1191 ) -> Result<ProvedRangePage, Error> {
1192 let after = cursor.after().map(<[u8]>::to_vec);
1193
1194 if limit == 0 {
1195 let proof_end = after.clone().or_else(|| Some(Vec::new()));
1196 return Ok(ProvedRangePage {
1197 page: RangePage {
1198 entries: Vec::new(),
1199 next_cursor: Some(cursor.clone()),
1200 },
1201 proof: RangePageProof {
1202 root: tree.root.clone(),
1203 after,
1204 end: proof_end,
1205 path: Vec::new(),
1206 },
1207 });
1208 }
1209
1210 let mut iter = self.range_from_cursor(tree, cursor, end).await?;
1211 let mut entries = Vec::with_capacity(limit);
1212
1213 for _ in 0..limit {
1214 let Some(item) = iter.next().await else {
1215 let proof = self
1216 .prove_range_page_window(tree, after.as_deref(), end)
1217 .await?;
1218 return Ok(ProvedRangePage {
1219 page: RangePage {
1220 entries,
1221 next_cursor: None,
1222 },
1223 proof,
1224 });
1225 };
1226 entries.push(item?);
1227 }
1228
1229 let lookahead = iter.next().await.transpose()?;
1230 let proof_end = lookahead
1231 .as_ref()
1232 .map(|(key, _)| key.clone())
1233 .or_else(|| end.map(<[u8]>::to_vec));
1234 let proof = self
1235 .prove_range_page_window(tree, after.as_deref(), proof_end.as_deref())
1236 .await?;
1237 let next_cursor = lookahead.as_ref().and_then(|_| {
1238 entries
1239 .last()
1240 .map(|(key, _)| RangeCursor::after_key(key.clone()))
1241 });
1242
1243 Ok(ProvedRangePage {
1244 page: RangePage {
1245 entries,
1246 next_cursor,
1247 },
1248 proof,
1249 })
1250 }
1251
1252 pub async fn prove_diff_page(
1255 &self,
1256 base: &Tree,
1257 other: &Tree,
1258 cursor: &RangeCursor,
1259 end: Option<&[u8]>,
1260 limit: usize,
1261 ) -> Result<ProvedDiffPage, Error> {
1262 let after = cursor.after().map(<[u8]>::to_vec);
1263
1264 if limit == 0 {
1265 let proof_end = after.clone().or_else(|| Some(Vec::new()));
1266 return Ok(ProvedDiffPage {
1267 page: DiffPage {
1268 diffs: Vec::new(),
1269 next_cursor: Some(cursor.clone()),
1270 },
1271 proof: DiffPageProof {
1272 base: RangePageProof {
1273 root: base.root.clone(),
1274 after: after.clone(),
1275 end: proof_end.clone(),
1276 path: Vec::new(),
1277 },
1278 other: RangePageProof {
1279 root: other.root.clone(),
1280 after,
1281 end: proof_end,
1282 path: Vec::new(),
1283 },
1284 lookahead_base: None,
1285 lookahead_other: None,
1286 requested_end: end.map(<[u8]>::to_vec),
1287 limit,
1288 },
1289 });
1290 }
1291
1292 let mut all_diffs = self.diff_from_cursor(base, other, cursor, end).await?;
1293 let has_more = all_diffs.len() > limit;
1294 let lookahead_key = has_more.then(|| all_diffs[limit].key().to_vec());
1295 if has_more {
1296 all_diffs.truncate(limit);
1297 }
1298
1299 let next_cursor = if has_more {
1300 all_diffs
1301 .last()
1302 .map(|diff| RangeCursor::after_key(diff.key().to_vec()))
1303 } else {
1304 None
1305 };
1306 let proof_end = lookahead_key.clone().or_else(|| end.map(<[u8]>::to_vec));
1307 let lookahead_base = match &lookahead_key {
1308 Some(key) => Some(self.prove_key(base, key).await?),
1309 None => None,
1310 };
1311 let lookahead_other = match &lookahead_key {
1312 Some(key) => Some(self.prove_key(other, key).await?),
1313 None => None,
1314 };
1315
1316 Ok(ProvedDiffPage {
1317 page: DiffPage {
1318 diffs: all_diffs,
1319 next_cursor,
1320 },
1321 proof: DiffPageProof {
1322 base: self
1323 .prove_range_page_window(base, after.as_deref(), proof_end.as_deref())
1324 .await?,
1325 other: self
1326 .prove_range_page_window(other, after.as_deref(), proof_end.as_deref())
1327 .await?,
1328 lookahead_base,
1329 lookahead_other,
1330 requested_end: end.map(<[u8]>::to_vec),
1331 limit,
1332 },
1333 })
1334 }
1335
1336 async fn prove_range_page_window(
1337 &self,
1338 tree: &Tree,
1339 after: Option<&[u8]>,
1340 end: Option<&[u8]>,
1341 ) -> Result<RangePageProof, Error> {
1342 let Some(root_cid) = &tree.root else {
1343 return Ok(RangePageProof {
1344 root: None,
1345 after: after.map(<[u8]>::to_vec),
1346 end: end.map(<[u8]>::to_vec),
1347 path: Vec::new(),
1348 });
1349 };
1350
1351 if page_range_is_empty_by_bounds(after, end) {
1352 return Ok(RangePageProof {
1353 root: Some(root_cid.clone()),
1354 after: after.map(<[u8]>::to_vec),
1355 end: end.map(<[u8]>::to_vec),
1356 path: Vec::new(),
1357 });
1358 }
1359
1360 let mut seen = HashSet::new();
1361 let mut path = Vec::new();
1362 self.collect_range_page_proof_nodes(root_cid, after, end, &mut seen, &mut path)
1363 .await?;
1364
1365 Ok(RangePageProof {
1366 root: Some(root_cid.clone()),
1367 after: after.map(<[u8]>::to_vec),
1368 end: end.map(<[u8]>::to_vec),
1369 path,
1370 })
1371 }
1372
1373 async fn collect_range_proof_nodes(
1374 &self,
1375 cid: &Cid,
1376 start: &[u8],
1377 end: Option<&[u8]>,
1378 seen: &mut HashSet<Cid>,
1379 path: &mut Vec<Node>,
1380 ) -> Result<(), Error> {
1381 let mut stack = vec![cid.clone()];
1382 while let Some(cid) = stack.pop() {
1383 if !seen.insert(cid.clone()) {
1384 continue;
1385 }
1386 let node = self.load_arc(&cid).await?;
1387 path.push((*node).clone());
1388
1389 if node.leaf {
1390 continue;
1391 }
1392
1393 let mut child_cids = Vec::new();
1394 for idx in overlapping_child_index_range(&node, start, end) {
1395 let child_start = node.keys[idx].as_slice();
1396 let child_end = child_span_end(&node, idx, None);
1397 if !span_overlaps_range(child_start, child_end, start, end) {
1398 if range_ends_before_or_at(end, child_start) {
1399 break;
1400 }
1401 continue;
1402 }
1403 child_cids.push(
1404 cid_from_child_bytes(node.vals.get(idx).ok_or(Error::InvalidNode)?)
1405 .ok_or(Error::InvalidNode)?,
1406 );
1407 }
1408 child_cids.reverse();
1409 stack.extend(child_cids);
1410 }
1411
1412 Ok(())
1413 }
1414
1415 async fn collect_range_page_proof_nodes(
1416 &self,
1417 cid: &Cid,
1418 after: Option<&[u8]>,
1419 end: Option<&[u8]>,
1420 seen: &mut HashSet<Cid>,
1421 path: &mut Vec<Node>,
1422 ) -> Result<(), Error> {
1423 let mut stack = vec![cid.clone()];
1424 while let Some(cid) = stack.pop() {
1425 if !seen.insert(cid.clone()) {
1426 continue;
1427 }
1428 let node = self.load_arc(&cid).await?;
1429 path.push((*node).clone());
1430
1431 if node.leaf {
1432 continue;
1433 }
1434
1435 let traversal_start = after.unwrap_or(&[]);
1436 let mut child_cids = Vec::new();
1437 for idx in overlapping_child_index_range(&node, traversal_start, end) {
1438 let child_start = node.keys[idx].as_slice();
1439 let child_end = child_span_end(&node, idx, None);
1440 if !span_overlaps_page_range(child_start, child_end, after, end) {
1441 if range_ends_before_or_at(end, child_start) {
1442 break;
1443 }
1444 continue;
1445 }
1446 child_cids.push(
1447 cid_from_child_bytes(node.vals.get(idx).ok_or(Error::InvalidNode)?)
1448 .ok_or(Error::InvalidNode)?,
1449 );
1450 }
1451 child_cids.reverse();
1452 stack.extend(child_cids);
1453 }
1454
1455 Ok(())
1456 }
1457}
1458
1459pub fn verify_key_proof(proof: &KeyProof) -> KeyProofVerification {
1461 let valid = proof_is_consistent(proof);
1462 let value = if valid {
1463 verified_leaf_value(proof.path.last(), &proof.key)
1464 } else {
1465 None
1466 };
1467
1468 KeyProofVerification {
1469 valid,
1470 root: proof.root.clone(),
1471 key: proof.key.clone(),
1472 value,
1473 }
1474}
1475
1476pub fn verify_multi_key_proof(proof: &MultiKeyProof) -> MultiKeyProofVerification {
1478 let mut results = proof
1479 .keys
1480 .iter()
1481 .map(|key| KeyProofVerification {
1482 valid: false,
1483 root: proof.root.clone(),
1484 key: key.clone(),
1485 value: None,
1486 })
1487 .collect::<Vec<_>>();
1488
1489 let node_map = match proof_node_map(proof) {
1490 Some(node_map) => node_map,
1491 None => {
1492 return MultiKeyProofVerification {
1493 valid: false,
1494 root: proof.root.clone(),
1495 results,
1496 };
1497 }
1498 };
1499
1500 match &proof.root {
1501 None => {
1502 let valid = proof.path.is_empty();
1503 for result in &mut results {
1504 result.valid = valid;
1505 }
1506 MultiKeyProofVerification {
1507 valid,
1508 root: proof.root.clone(),
1509 results,
1510 }
1511 }
1512 Some(root) if proof.keys.is_empty() => {
1513 let valid = proof.path.is_empty() || node_map.contains_key(root);
1514 MultiKeyProofVerification {
1515 valid,
1516 root: proof.root.clone(),
1517 results,
1518 }
1519 }
1520 Some(root) => {
1521 let mut all_valid = true;
1522 for result in &mut results {
1523 match verified_value_from_node_set(root, &result.key, &node_map) {
1524 Some(value) => {
1525 result.valid = true;
1526 result.value = value;
1527 }
1528 None => {
1529 all_valid = false;
1530 }
1531 }
1532 }
1533 MultiKeyProofVerification {
1534 valid: all_valid,
1535 root: proof.root.clone(),
1536 results,
1537 }
1538 }
1539 }
1540}
1541
1542pub fn verify_range_proof(proof: &RangeProof) -> RangeProofVerification {
1544 let mut entries = Vec::new();
1545
1546 if range_is_empty_by_bounds(&proof.start, proof.end.as_deref()) {
1547 return RangeProofVerification {
1548 valid: proof.path.is_empty(),
1549 root: proof.root.clone(),
1550 start: proof.start.clone(),
1551 end: proof.end.clone(),
1552 entries,
1553 };
1554 }
1555
1556 let node_map = match range_proof_node_map(proof) {
1557 Some(node_map) => node_map,
1558 None => {
1559 return RangeProofVerification {
1560 valid: false,
1561 root: proof.root.clone(),
1562 start: proof.start.clone(),
1563 end: proof.end.clone(),
1564 entries,
1565 };
1566 }
1567 };
1568
1569 let valid = match &proof.root {
1570 None => proof.path.is_empty(),
1571 Some(root) => {
1572 let mut stack = HashSet::new();
1573 verify_range_node(
1574 root,
1575 &proof.start,
1576 proof.end.as_deref(),
1577 &node_map,
1578 &mut stack,
1579 &mut entries,
1580 )
1581 }
1582 };
1583
1584 if !valid {
1585 entries.clear();
1586 }
1587
1588 RangeProofVerification {
1589 valid,
1590 root: proof.root.clone(),
1591 start: proof.start.clone(),
1592 end: proof.end.clone(),
1593 entries,
1594 }
1595}
1596
1597pub fn verify_range_page_proof(proof: &RangePageProof) -> RangePageProofVerification {
1599 let mut entries = Vec::new();
1600
1601 if page_range_is_empty_by_bounds(proof.after.as_deref(), proof.end.as_deref()) {
1602 return RangePageProofVerification {
1603 valid: proof.path.is_empty(),
1604 root: proof.root.clone(),
1605 after: proof.after.clone(),
1606 end: proof.end.clone(),
1607 entries,
1608 };
1609 }
1610
1611 let node_map = match range_page_proof_node_map(proof) {
1612 Some(node_map) => node_map,
1613 None => {
1614 return RangePageProofVerification {
1615 valid: false,
1616 root: proof.root.clone(),
1617 after: proof.after.clone(),
1618 end: proof.end.clone(),
1619 entries,
1620 };
1621 }
1622 };
1623
1624 let valid = match &proof.root {
1625 None => proof.path.is_empty(),
1626 Some(root) => {
1627 let mut stack = HashSet::new();
1628 verify_range_page_node(
1629 root,
1630 proof.after.as_deref(),
1631 proof.end.as_deref(),
1632 &node_map,
1633 &mut stack,
1634 &mut entries,
1635 )
1636 }
1637 };
1638
1639 if !valid {
1640 entries.clear();
1641 }
1642
1643 RangePageProofVerification {
1644 valid,
1645 root: proof.root.clone(),
1646 after: proof.after.clone(),
1647 end: proof.end.clone(),
1648 entries,
1649 }
1650}
1651
1652pub fn verify_diff_page_proof(proof: &DiffPageProof) -> DiffPageProofVerification {
1654 let base = verify_range_page_proof(&proof.base);
1655 let other = verify_range_page_proof(&proof.other);
1656 let same_bounds = proof.base.after == proof.other.after && proof.base.end == proof.other.end;
1657 let after = proof.base.after.clone();
1658 let proof_end = proof.base.end.clone();
1659 let mut diffs = Vec::new();
1660
1661 let mut lookahead_valid = false;
1662 let mut next_cursor = None;
1663 let mut valid = base.valid && other.valid && same_bounds;
1664
1665 if valid {
1666 diffs = diff_verified_entries(&base.entries, &other.entries);
1667 match (&proof.lookahead_base, &proof.lookahead_other) {
1668 (Some(base_lookahead), Some(other_lookahead)) => {
1669 let base_lookahead = verify_key_proof(base_lookahead);
1670 let other_lookahead = verify_key_proof(other_lookahead);
1671 lookahead_valid = verify_diff_page_lookahead(
1672 &base_lookahead,
1673 &other_lookahead,
1674 after.as_deref(),
1675 proof.requested_end.as_deref(),
1676 proof_end.as_deref(),
1677 );
1678 valid = valid && lookahead_valid && proof.limit > 0 && diffs.len() == proof.limit;
1679 if valid {
1680 next_cursor = diffs
1681 .last()
1682 .map(|diff| RangeCursor::after_key(diff.key().to_vec()));
1683 }
1684 }
1685 (None, None) => {
1686 lookahead_valid = true;
1687 if proof.limit == 0 {
1688 valid = valid && diffs.is_empty();
1689 if valid {
1690 next_cursor = Some(
1691 after
1692 .clone()
1693 .map(RangeCursor::after_key)
1694 .unwrap_or_else(RangeCursor::start),
1695 );
1696 }
1697 } else {
1698 valid = valid && proof_end == proof.requested_end && diffs.len() <= proof.limit;
1699 }
1700 }
1701 _ => {
1702 valid = false;
1703 }
1704 }
1705 }
1706
1707 if !valid {
1708 diffs.clear();
1709 next_cursor = None;
1710 }
1711
1712 DiffPageProofVerification {
1713 valid,
1714 base_valid: base.valid,
1715 other_valid: other.valid,
1716 lookahead_valid,
1717 base_root: base.root,
1718 other_root: other.root,
1719 after,
1720 requested_end: proof.requested_end.clone(),
1721 proof_end,
1722 limit: proof.limit,
1723 diffs,
1724 next_cursor,
1725 }
1726}
1727
1728pub fn inspect_proof_bundle(bytes: &[u8]) -> Result<ProofBundleSummary, Error> {
1735 match proof_bundle_from_bytes(bytes) {
1736 Ok(wire) => proof_bundle_summary_from_wire(wire),
1737 Err(primary_error) => match diff_page_proof_bundle_from_bytes(bytes) {
1738 Ok(wire) => diff_page_proof_bundle_summary_from_wire(wire),
1739 Err(_) => Err(primary_error),
1740 },
1741 }
1742}
1743
1744pub fn verify_proof_bundle(bytes: &[u8]) -> Result<ProofBundleVerification, Error> {
1750 let summary = inspect_proof_bundle(bytes)?;
1751 match summary.kind {
1752 ProofBundleKind::Key => {
1753 let verified = KeyProof::from_bundle_bytes(bytes)?.verify();
1754 Ok(ProofBundleVerification {
1755 summary,
1756 valid: verified.valid,
1757 exists_count: usize::from(verified.exists()),
1758 absence_count: usize::from(verified.is_absence()),
1759 entry_count: 0,
1760 diff_count: 0,
1761 next_cursor: None,
1762 })
1763 }
1764 ProofBundleKind::MultiKey => {
1765 let verified = MultiKeyProof::from_bundle_bytes(bytes)?.verify();
1766 let (exists_count, absence_count) = if verified.valid {
1767 (
1768 verified
1769 .results
1770 .iter()
1771 .filter(|result| result.exists())
1772 .count(),
1773 verified
1774 .results
1775 .iter()
1776 .filter(|result| result.is_absence())
1777 .count(),
1778 )
1779 } else {
1780 (0, 0)
1781 };
1782 Ok(ProofBundleVerification {
1783 summary,
1784 valid: verified.valid,
1785 exists_count,
1786 absence_count,
1787 entry_count: 0,
1788 diff_count: 0,
1789 next_cursor: None,
1790 })
1791 }
1792 ProofBundleKind::Range => {
1793 let verified = RangeProof::from_bundle_bytes(bytes)?.verify();
1794 Ok(ProofBundleVerification {
1795 summary,
1796 valid: verified.valid,
1797 exists_count: 0,
1798 absence_count: 0,
1799 entry_count: if verified.valid {
1800 verified.entries.len()
1801 } else {
1802 0
1803 },
1804 diff_count: 0,
1805 next_cursor: None,
1806 })
1807 }
1808 ProofBundleKind::RangePage => {
1809 let verified = RangePageProof::from_bundle_bytes(bytes)?.verify();
1810 Ok(ProofBundleVerification {
1811 summary,
1812 valid: verified.valid,
1813 exists_count: 0,
1814 absence_count: 0,
1815 entry_count: if verified.valid {
1816 verified.entries.len()
1817 } else {
1818 0
1819 },
1820 diff_count: 0,
1821 next_cursor: None,
1822 })
1823 }
1824 ProofBundleKind::DiffPage => {
1825 let verified = DiffPageProof::from_bundle_bytes(bytes)?.verify();
1826 Ok(ProofBundleVerification {
1827 summary,
1828 valid: verified.valid,
1829 exists_count: 0,
1830 absence_count: 0,
1831 entry_count: 0,
1832 diff_count: if verified.valid {
1833 verified.diffs.len()
1834 } else {
1835 0
1836 },
1837 next_cursor: verified.next_cursor,
1838 })
1839 }
1840 }
1841}
1842
1843pub fn sign_proof_bundle_hmac_sha256(
1845 proof_bundle: impl Into<Vec<u8>>,
1846 key_id: impl Into<Vec<u8>>,
1847 secret: &[u8],
1848 context: impl Into<Vec<u8>>,
1849 issued_at_millis: Option<u64>,
1850 expires_at_millis: Option<u64>,
1851 nonce: impl Into<Vec<u8>>,
1852) -> Result<AuthenticatedProofEnvelope, Error> {
1853 let envelope = AuthenticatedProofEnvelope {
1854 algorithm: AUTHENTICATED_PROOF_ENVELOPE_ALGORITHM_HMAC_SHA256.to_string(),
1855 key_id: key_id.into(),
1856 proof_bundle: proof_bundle.into(),
1857 context: context.into(),
1858 issued_at_millis,
1859 expires_at_millis,
1860 nonce: nonce.into(),
1861 signature: Vec::new(),
1862 };
1863 let signature = hmac_sha256(
1864 secret,
1865 &authenticated_proof_envelope_signing_bytes(&envelope)?,
1866 );
1867 Ok(AuthenticatedProofEnvelope {
1868 signature: signature.to_vec(),
1869 ..envelope
1870 })
1871}
1872
1873pub fn verify_authenticated_proof_envelope(
1878 envelope: &AuthenticatedProofEnvelope,
1879 secret: &[u8],
1880 now_millis: Option<u64>,
1881) -> AuthenticatedProofEnvelopeVerification {
1882 let algorithm_supported =
1883 envelope.algorithm == AUTHENTICATED_PROOF_ENVELOPE_ALGORITHM_HMAC_SHA256;
1884 let signature_valid = algorithm_supported
1885 && authenticated_proof_envelope_signing_bytes(envelope)
1886 .map(|bytes| {
1887 let expected = hmac_sha256(secret, &bytes);
1888 constant_time_eq(&expected, &envelope.signature)
1889 })
1890 .unwrap_or(false);
1891 let (not_yet_valid, expired) = match now_millis {
1892 Some(now) => (
1893 envelope
1894 .issued_at_millis
1895 .is_some_and(|issued_at| issued_at > now),
1896 envelope
1897 .expires_at_millis
1898 .is_some_and(|expires_at| expires_at <= now),
1899 ),
1900 None => (false, false),
1901 };
1902 let time_valid = !not_yet_valid && !expired;
1903
1904 AuthenticatedProofEnvelopeVerification {
1905 valid: signature_valid && time_valid,
1906 signature_valid,
1907 time_valid,
1908 not_yet_valid,
1909 expired,
1910 algorithm: envelope.algorithm.clone(),
1911 key_id: envelope.key_id.clone(),
1912 proof_bundle: envelope.proof_bundle.clone(),
1913 context: envelope.context.clone(),
1914 issued_at_millis: envelope.issued_at_millis,
1915 expires_at_millis: envelope.expires_at_millis,
1916 nonce: envelope.nonce.clone(),
1917 }
1918}
1919
1920pub fn verify_authenticated_proof_bundle(
1929 envelope_bytes: &[u8],
1930 secret: &[u8],
1931 now_millis: Option<u64>,
1932) -> Result<AuthenticatedProofBundleVerification, Error> {
1933 let envelope = AuthenticatedProofEnvelope::from_bytes(envelope_bytes)?;
1934 let envelope = verify_authenticated_proof_envelope(&envelope, secret, now_millis);
1935
1936 if !envelope.valid {
1937 return Ok(AuthenticatedProofBundleVerification {
1938 valid: false,
1939 envelope,
1940 proof: None,
1941 proof_error: None,
1942 });
1943 }
1944
1945 match verify_proof_bundle(&envelope.proof_bundle) {
1946 Ok(proof) => Ok(AuthenticatedProofBundleVerification {
1947 valid: proof.valid,
1948 envelope,
1949 proof: Some(proof),
1950 proof_error: None,
1951 }),
1952 Err(err) => Ok(AuthenticatedProofBundleVerification {
1953 valid: false,
1954 envelope,
1955 proof: None,
1956 proof_error: Some(err.to_string()),
1957 }),
1958 }
1959}
1960
1961fn proof_node_map(proof: &MultiKeyProof) -> Option<HashMap<Cid, &Node>> {
1962 let mut node_map = HashMap::with_capacity(proof.path.len());
1963 for node in &proof.path {
1964 if !node_shape_is_valid(node) {
1965 return None;
1966 }
1967 node_map.insert(node.cid(), node);
1968 }
1969 Some(node_map)
1970}
1971
1972fn range_proof_node_map(proof: &RangeProof) -> Option<HashMap<Cid, &Node>> {
1973 let mut node_map = HashMap::with_capacity(proof.path.len());
1974 for node in &proof.path {
1975 if !node_shape_is_valid(node) {
1976 return None;
1977 }
1978 node_map.insert(node.cid(), node);
1979 }
1980 Some(node_map)
1981}
1982
1983fn range_page_proof_node_map(proof: &RangePageProof) -> Option<HashMap<Cid, &Node>> {
1984 let mut node_map = HashMap::with_capacity(proof.path.len());
1985 for node in &proof.path {
1986 if !node_shape_is_valid(node) {
1987 return None;
1988 }
1989 node_map.insert(node.cid(), node);
1990 }
1991 Some(node_map)
1992}
1993
1994fn diff_verified_entries(base: &[(Vec<u8>, Vec<u8>)], other: &[(Vec<u8>, Vec<u8>)]) -> Vec<Diff> {
1995 let mut diffs = Vec::new();
1996 let mut base_idx = 0;
1997 let mut other_idx = 0;
1998
1999 while base_idx < base.len() && other_idx < other.len() {
2000 let (base_key, base_value) = &base[base_idx];
2001 let (other_key, other_value) = &other[other_idx];
2002 match base_key.cmp(other_key) {
2003 std::cmp::Ordering::Less => {
2004 diffs.push(Diff::Removed {
2005 key: base_key.clone(),
2006 val: base_value.clone(),
2007 });
2008 base_idx += 1;
2009 }
2010 std::cmp::Ordering::Greater => {
2011 diffs.push(Diff::Added {
2012 key: other_key.clone(),
2013 val: other_value.clone(),
2014 });
2015 other_idx += 1;
2016 }
2017 std::cmp::Ordering::Equal => {
2018 if base_value != other_value {
2019 diffs.push(Diff::Changed {
2020 key: base_key.clone(),
2021 old: base_value.clone(),
2022 new: other_value.clone(),
2023 });
2024 }
2025 base_idx += 1;
2026 other_idx += 1;
2027 }
2028 }
2029 }
2030
2031 for (key, value) in &base[base_idx..] {
2032 diffs.push(Diff::Removed {
2033 key: key.clone(),
2034 val: value.clone(),
2035 });
2036 }
2037 for (key, value) in &other[other_idx..] {
2038 diffs.push(Diff::Added {
2039 key: key.clone(),
2040 val: value.clone(),
2041 });
2042 }
2043
2044 diffs
2045}
2046
2047fn verify_diff_page_lookahead(
2048 base: &KeyProofVerification,
2049 other: &KeyProofVerification,
2050 after: Option<&[u8]>,
2051 requested_end: Option<&[u8]>,
2052 proof_end: Option<&[u8]>,
2053) -> bool {
2054 if !base.valid || !other.valid || base.key != other.key {
2055 return false;
2056 }
2057 let key = base.key.as_slice();
2058 if proof_end != Some(key) || !key_in_page_range(key, after, requested_end) {
2059 return false;
2060 }
2061 match (&base.value, &other.value) {
2062 (None, None) => false,
2063 (Some(left), Some(right)) => left != right,
2064 _ => true,
2065 }
2066}
2067
2068fn verify_range_node(
2069 cid: &Cid,
2070 start: &[u8],
2071 end: Option<&[u8]>,
2072 node_map: &HashMap<Cid, &Node>,
2073 stack: &mut HashSet<Cid>,
2074 entries: &mut Vec<(Vec<u8>, Vec<u8>)>,
2075) -> bool {
2076 if !stack.insert(cid.clone()) {
2077 return false;
2078 }
2079
2080 let Some(node) = node_map.get(cid).copied() else {
2081 stack.remove(cid);
2082 return false;
2083 };
2084
2085 if node.leaf {
2086 for (key, value) in node.keys.iter().zip(&node.vals) {
2087 if key_in_range(key, start, end) {
2088 entries.push((key.clone(), value.clone()));
2089 }
2090 }
2091 stack.remove(cid);
2092 return true;
2093 }
2094
2095 for idx in overlapping_child_index_range(node, start, end) {
2096 let child_start = node.keys[idx].as_slice();
2097 let child_end = child_span_end(node, idx, None);
2098 if !span_overlaps_range(child_start, child_end, start, end) {
2099 if range_ends_before_or_at(end, child_start) {
2100 break;
2101 }
2102 continue;
2103 }
2104
2105 let Some(child_cid) = node
2106 .vals
2107 .get(idx)
2108 .and_then(|bytes| cid_from_child_bytes(bytes))
2109 else {
2110 stack.remove(cid);
2111 return false;
2112 };
2113 let Some(child) = node_map.get(&child_cid).copied() else {
2114 stack.remove(cid);
2115 return false;
2116 };
2117 if node.level != child.level.saturating_add(1) {
2118 stack.remove(cid);
2119 return false;
2120 }
2121 if !verify_range_node(&child_cid, start, end, node_map, stack, entries) {
2122 stack.remove(cid);
2123 return false;
2124 }
2125 }
2126
2127 stack.remove(cid);
2128 true
2129}
2130
2131fn verify_range_page_node(
2132 cid: &Cid,
2133 after: Option<&[u8]>,
2134 end: Option<&[u8]>,
2135 node_map: &HashMap<Cid, &Node>,
2136 stack: &mut HashSet<Cid>,
2137 entries: &mut Vec<(Vec<u8>, Vec<u8>)>,
2138) -> bool {
2139 if !stack.insert(cid.clone()) {
2140 return false;
2141 }
2142
2143 let Some(node) = node_map.get(cid).copied() else {
2144 stack.remove(cid);
2145 return false;
2146 };
2147
2148 if node.leaf {
2149 for (key, value) in node.keys.iter().zip(&node.vals) {
2150 if key_in_page_range(key, after, end) {
2151 entries.push((key.clone(), value.clone()));
2152 }
2153 }
2154 stack.remove(cid);
2155 return true;
2156 }
2157
2158 let traversal_start = after.unwrap_or(&[]);
2159 for idx in overlapping_child_index_range(node, traversal_start, end) {
2160 let child_start = node.keys[idx].as_slice();
2161 let child_end = child_span_end(node, idx, None);
2162 if !span_overlaps_page_range(child_start, child_end, after, end) {
2163 if range_ends_before_or_at(end, child_start) {
2164 break;
2165 }
2166 continue;
2167 }
2168
2169 let Some(child_cid) = node
2170 .vals
2171 .get(idx)
2172 .and_then(|bytes| cid_from_child_bytes(bytes))
2173 else {
2174 stack.remove(cid);
2175 return false;
2176 };
2177 let Some(child) = node_map.get(&child_cid).copied() else {
2178 stack.remove(cid);
2179 return false;
2180 };
2181 if node.level != child.level.saturating_add(1) {
2182 stack.remove(cid);
2183 return false;
2184 }
2185 if !verify_range_page_node(&child_cid, after, end, node_map, stack, entries) {
2186 stack.remove(cid);
2187 return false;
2188 }
2189 }
2190
2191 stack.remove(cid);
2192 true
2193}
2194
2195fn verified_value_from_node_set(
2196 root: &Cid,
2197 key: &[u8],
2198 node_map: &HashMap<Cid, &Node>,
2199) -> Option<Option<Vec<u8>>> {
2200 let mut cid = root.clone();
2201 let mut visited = HashSet::new();
2202
2203 for _ in 0..=node_map.len() {
2204 if !visited.insert(cid.clone()) {
2205 return None;
2206 }
2207
2208 let node = *node_map.get(&cid)?;
2209 if node.leaf {
2210 return Some(verified_leaf_value(Some(node), key));
2211 }
2212
2213 let child_index = path_child_index(node, key);
2214 let child_cid = cid_from_child_bytes(node.vals.get(child_index)?)?;
2215 let child = *node_map.get(&child_cid)?;
2216 if node.level != child.level.saturating_add(1) {
2217 return None;
2218 }
2219 cid = child_cid;
2220 }
2221
2222 None
2223}
2224
2225fn proof_is_consistent(proof: &KeyProof) -> bool {
2226 match (&proof.root, proof.path.as_slice()) {
2227 (None, []) => return true,
2228 (None, _) | (Some(_), []) => return false,
2229 (Some(root), [first, ..]) if &first.cid() != root => return false,
2230 _ => {}
2231 }
2232
2233 for (depth, node) in proof.path.iter().enumerate() {
2234 if !node_shape_is_valid(node) {
2235 return false;
2236 }
2237
2238 let is_last = depth + 1 == proof.path.len();
2239 if is_last {
2240 return node.leaf;
2241 }
2242
2243 if node.leaf {
2244 return false;
2245 }
2246
2247 let next = &proof.path[depth + 1];
2248 if node.level != next.level.saturating_add(1) {
2249 return false;
2250 }
2251
2252 let child_index = path_child_index(node, &proof.key);
2253 let Some(child_bytes) = node.vals.get(child_index) else {
2254 return false;
2255 };
2256 let Some(child_cid) = cid_from_child_bytes(child_bytes) else {
2257 return false;
2258 };
2259 if next.cid() != child_cid {
2260 return false;
2261 }
2262 }
2263
2264 false
2265}
2266
2267fn verified_leaf_value(leaf: Option<&Node>, key: &[u8]) -> Option<Vec<u8>> {
2268 let leaf = leaf?;
2269 if !leaf.leaf {
2270 return None;
2271 }
2272 match leaf.search(key) {
2273 Ok(index) => leaf.vals.get(index).cloned(),
2274 Err(_) => None,
2275 }
2276}
2277
2278fn node_shape_is_valid(node: &Node) -> bool {
2279 if node.keys.is_empty() || node.keys.len() != node.vals.len() {
2280 return false;
2281 }
2282
2283 if !node.keys.windows(2).all(|window| window[0] < window[1]) {
2284 return false;
2285 }
2286
2287 node.leaf || node.vals.iter().all(|value| value.len() == 32)
2288}
2289
2290fn path_child_index(node: &Node, key: &[u8]) -> usize {
2291 node.keys
2292 .partition_point(|candidate| candidate.as_slice() <= key)
2293 .saturating_sub(1)
2294}
2295
2296fn overlapping_child_index_range(
2297 node: &Node,
2298 range_start: &[u8],
2299 range_end: Option<&[u8]>,
2300) -> std::ops::Range<usize> {
2301 let start = node
2302 .keys
2303 .partition_point(|candidate| candidate.as_slice() < range_start)
2304 .saturating_sub(1);
2305 let end = range_end.map_or(node.len(), |end| {
2306 node.keys
2307 .partition_point(|candidate| candidate.as_slice() < end)
2308 });
2309 start..end.max(start).min(node.len())
2310}
2311
2312fn child_span_end<'a>(node: &'a Node, idx: usize, span_end: Option<&'a [u8]>) -> Option<&'a [u8]> {
2313 node.keys.get(idx + 1).map(Vec::as_slice).or(span_end)
2314}
2315
2316fn span_overlaps_range(
2317 span_start: &[u8],
2318 span_end: Option<&[u8]>,
2319 range_start: &[u8],
2320 range_end: Option<&[u8]>,
2321) -> bool {
2322 !span_ends_before_or_at(span_end, range_start)
2323 && !range_ends_before_or_at(range_end, span_start)
2324}
2325
2326fn span_ends_before_or_at(end: Option<&[u8]>, start: &[u8]) -> bool {
2327 end.is_some_and(|end| end <= start)
2328}
2329
2330fn range_ends_before_or_at(end: Option<&[u8]>, start: &[u8]) -> bool {
2331 end.is_some_and(|end| end <= start)
2332}
2333
2334fn range_is_empty_by_bounds(start: &[u8], end: Option<&[u8]>) -> bool {
2335 end.is_some_and(|end| end <= start)
2336}
2337
2338fn page_range_is_empty_by_bounds(after: Option<&[u8]>, end: Option<&[u8]>) -> bool {
2339 match (after, end) {
2340 (Some(after), Some(end)) => end <= after,
2341 (None, Some(end)) => end.is_empty(),
2342 _ => false,
2343 }
2344}
2345
2346fn key_in_range(key: &[u8], start: &[u8], end: Option<&[u8]>) -> bool {
2347 key >= start
2348 && match end {
2349 Some(end) => key < end,
2350 None => true,
2351 }
2352}
2353
2354fn key_in_page_range(key: &[u8], after: Option<&[u8]>, end: Option<&[u8]>) -> bool {
2355 after.map_or(true, |after| key > after)
2356 && match end {
2357 Some(end) => key < end,
2358 None => true,
2359 }
2360}
2361
2362fn span_overlaps_page_range(
2363 span_start: &[u8],
2364 span_end: Option<&[u8]>,
2365 after: Option<&[u8]>,
2366 end: Option<&[u8]>,
2367) -> bool {
2368 !after.is_some_and(|after| span_ends_before_or_at(span_end, after))
2369 && !range_ends_before_or_at(end, span_start)
2370}
2371
2372fn cid_from_child_bytes(bytes: &[u8]) -> Option<Cid> {
2373 bytes.try_into().ok().map(Cid)
2374}
2375
2376fn proof_bundle_to_bytes(wire: ProofBundleWire) -> Result<Vec<u8>, Error> {
2377 serde_cbor::ser::to_vec_packed(&wire).map_err(|err| Error::Serialize(err.to_string()))
2378}
2379
2380fn proof_bundle_from_bytes(bytes: &[u8]) -> Result<ProofBundleWire, Error> {
2381 let wire: ProofBundleWire =
2382 serde_cbor::from_slice(bytes).map_err(|err| Error::Deserialize(err.to_string()))?;
2383 if wire.version != PROOF_BUNDLE_VERSION {
2384 return Err(proof_bundle_deserialize(format!(
2385 "unsupported proof bundle version {}",
2386 wire.version
2387 )));
2388 }
2389 match wire.kind {
2390 PROOF_BUNDLE_KIND_KEY
2391 | PROOF_BUNDLE_KIND_MULTI_KEY
2392 | PROOF_BUNDLE_KIND_RANGE
2393 | PROOF_BUNDLE_KIND_RANGE_PAGE => Ok(wire),
2394 other => Err(proof_bundle_deserialize(format!(
2395 "unsupported proof bundle kind {other}"
2396 ))),
2397 }
2398}
2399
2400fn diff_page_proof_bundle_from_bytes(bytes: &[u8]) -> Result<DiffPageProofBundleWire, Error> {
2401 let wire: DiffPageProofBundleWire =
2402 serde_cbor::from_slice(bytes).map_err(|err| Error::Deserialize(err.to_string()))?;
2403 if wire.version != PROOF_BUNDLE_VERSION {
2404 return Err(proof_bundle_deserialize(format!(
2405 "unsupported diff page proof bundle version {}",
2406 wire.version
2407 )));
2408 }
2409 if wire.kind != PROOF_BUNDLE_KIND_DIFF_PAGE {
2410 return Err(proof_bundle_deserialize(
2411 "proof bundle is not a diff page proof",
2412 ));
2413 }
2414 Ok(wire)
2415}
2416
2417fn proof_bundle_summary_from_wire(wire: ProofBundleWire) -> Result<ProofBundleSummary, Error> {
2418 Ok(ProofBundleSummary {
2419 version: wire.version,
2420 kind: proof_bundle_kind_from_u8(wire.kind)?,
2421 root: cid_from_bundle_root(wire.root)?,
2422 other_root: None,
2423 key_count: wire.keys.len(),
2424 path_node_count: wire.path_node_bytes.len(),
2425 start: wire.start,
2426 end: wire.end,
2427 after: wire.after,
2428 requested_end: None,
2429 limit: None,
2430 has_lookahead: false,
2431 })
2432}
2433
2434fn diff_page_proof_bundle_summary_from_wire(
2435 wire: DiffPageProofBundleWire,
2436) -> Result<ProofBundleSummary, Error> {
2437 let limit = usize::try_from(wire.limit)
2438 .map_err(|_| proof_bundle_deserialize("diff page proof bundle limit is too large"))?;
2439 let base = proof_bundle_from_bytes(&wire.base_range_page_proof)?;
2440 if base.kind != PROOF_BUNDLE_KIND_RANGE_PAGE {
2441 return Err(proof_bundle_deserialize(
2442 "diff page proof base proof must be a range page proof",
2443 ));
2444 }
2445 let other = proof_bundle_from_bytes(&wire.other_range_page_proof)?;
2446 if other.kind != PROOF_BUNDLE_KIND_RANGE_PAGE {
2447 return Err(proof_bundle_deserialize(
2448 "diff page proof other proof must be a range page proof",
2449 ));
2450 }
2451
2452 let mut path_node_count = base.path_node_bytes.len() + other.path_node_bytes.len();
2453 let mut has_lookahead = false;
2454 if let Some(lookahead) = &wire.lookahead_base_key_proof {
2455 let lookahead = proof_bundle_from_bytes(lookahead)?;
2456 if lookahead.kind != PROOF_BUNDLE_KIND_KEY {
2457 return Err(proof_bundle_deserialize(
2458 "diff page proof base lookahead must be a key proof",
2459 ));
2460 }
2461 path_node_count += lookahead.path_node_bytes.len();
2462 has_lookahead = true;
2463 }
2464 if let Some(lookahead) = &wire.lookahead_other_key_proof {
2465 let lookahead = proof_bundle_from_bytes(lookahead)?;
2466 if lookahead.kind != PROOF_BUNDLE_KIND_KEY {
2467 return Err(proof_bundle_deserialize(
2468 "diff page proof other lookahead must be a key proof",
2469 ));
2470 }
2471 path_node_count += lookahead.path_node_bytes.len();
2472 has_lookahead = true;
2473 }
2474
2475 Ok(ProofBundleSummary {
2476 version: wire.version,
2477 kind: ProofBundleKind::DiffPage,
2478 root: cid_from_bundle_root(base.root)?,
2479 other_root: cid_from_bundle_root(other.root)?,
2480 key_count: 0,
2481 path_node_count,
2482 start: None,
2483 end: base.end,
2484 after: base.after,
2485 requested_end: wire.requested_end,
2486 limit: Some(limit),
2487 has_lookahead,
2488 })
2489}
2490
2491fn proof_bundle_kind_from_u8(kind: u8) -> Result<ProofBundleKind, Error> {
2492 match kind {
2493 PROOF_BUNDLE_KIND_KEY => Ok(ProofBundleKind::Key),
2494 PROOF_BUNDLE_KIND_MULTI_KEY => Ok(ProofBundleKind::MultiKey),
2495 PROOF_BUNDLE_KIND_RANGE => Ok(ProofBundleKind::Range),
2496 PROOF_BUNDLE_KIND_RANGE_PAGE => Ok(ProofBundleKind::RangePage),
2497 PROOF_BUNDLE_KIND_DIFF_PAGE => Ok(ProofBundleKind::DiffPage),
2498 other => Err(proof_bundle_deserialize(format!(
2499 "unsupported proof bundle kind {other}"
2500 ))),
2501 }
2502}
2503
2504fn cid_from_bundle_root(root: Option<Vec<u8>>) -> Result<Option<Cid>, Error> {
2505 root.map(|bytes| {
2506 bytes
2507 .try_into()
2508 .map(Cid)
2509 .map_err(|_| proof_bundle_deserialize("proof bundle root CID must be 32 bytes"))
2510 })
2511 .transpose()
2512}
2513
2514fn proof_bundle_deserialize(message: impl Into<String>) -> Error {
2515 Error::Deserialize(format!("invalid proof bundle: {}", message.into()))
2516}
2517
2518fn authenticated_proof_envelope_to_bytes(
2519 envelope: &AuthenticatedProofEnvelope,
2520) -> Result<Vec<u8>, Error> {
2521 serde_cbor::ser::to_vec_packed(&AuthenticatedProofEnvelopeWire {
2522 version: AUTHENTICATED_PROOF_ENVELOPE_VERSION,
2523 algorithm: envelope.algorithm.clone(),
2524 key_id: envelope.key_id.clone(),
2525 proof_bundle: envelope.proof_bundle.clone(),
2526 context: envelope.context.clone(),
2527 issued_at_millis: envelope.issued_at_millis,
2528 expires_at_millis: envelope.expires_at_millis,
2529 nonce: envelope.nonce.clone(),
2530 signature: envelope.signature.clone(),
2531 })
2532 .map_err(|err| Error::Serialize(err.to_string()))
2533}
2534
2535fn authenticated_proof_envelope_from_bytes(
2536 bytes: &[u8],
2537) -> Result<AuthenticatedProofEnvelope, Error> {
2538 let wire: AuthenticatedProofEnvelopeWire =
2539 serde_cbor::from_slice(bytes).map_err(|err| Error::Deserialize(err.to_string()))?;
2540 if wire.version != AUTHENTICATED_PROOF_ENVELOPE_VERSION {
2541 return Err(authenticated_proof_envelope_deserialize(format!(
2542 "unsupported envelope version {}",
2543 wire.version
2544 )));
2545 }
2546 if wire.algorithm != AUTHENTICATED_PROOF_ENVELOPE_ALGORITHM_HMAC_SHA256 {
2547 return Err(authenticated_proof_envelope_deserialize(format!(
2548 "unsupported envelope algorithm {}",
2549 wire.algorithm
2550 )));
2551 }
2552 if wire.signature.len() != 32 {
2553 return Err(authenticated_proof_envelope_deserialize(
2554 "HMAC-SHA256 signature must be 32 bytes",
2555 ));
2556 }
2557 Ok(AuthenticatedProofEnvelope {
2558 algorithm: wire.algorithm,
2559 key_id: wire.key_id,
2560 proof_bundle: wire.proof_bundle,
2561 context: wire.context,
2562 issued_at_millis: wire.issued_at_millis,
2563 expires_at_millis: wire.expires_at_millis,
2564 nonce: wire.nonce,
2565 signature: wire.signature,
2566 })
2567}
2568
2569fn authenticated_proof_envelope_signing_bytes(
2570 envelope: &AuthenticatedProofEnvelope,
2571) -> Result<Vec<u8>, Error> {
2572 let mut bytes = AUTHENTICATED_PROOF_ENVELOPE_DOMAIN.to_vec();
2573 bytes.push(0);
2574 let payload = serde_cbor::ser::to_vec_packed(&AuthenticatedProofEnvelopeSigningWire {
2575 version: AUTHENTICATED_PROOF_ENVELOPE_VERSION,
2576 algorithm: envelope.algorithm.clone(),
2577 key_id: envelope.key_id.clone(),
2578 proof_bundle: envelope.proof_bundle.clone(),
2579 context: envelope.context.clone(),
2580 issued_at_millis: envelope.issued_at_millis,
2581 expires_at_millis: envelope.expires_at_millis,
2582 nonce: envelope.nonce.clone(),
2583 })
2584 .map_err(|err| Error::Serialize(err.to_string()))?;
2585 bytes.extend(payload);
2586 Ok(bytes)
2587}
2588
2589fn authenticated_proof_envelope_deserialize(message: impl Into<String>) -> Error {
2590 Error::Deserialize(format!(
2591 "invalid authenticated proof envelope: {}",
2592 message.into()
2593 ))
2594}
2595
2596fn hmac_sha256(secret: &[u8], message: &[u8]) -> [u8; 32] {
2597 const BLOCK_SIZE: usize = 64;
2598
2599 let mut key_block = [0u8; BLOCK_SIZE];
2600 if secret.len() > BLOCK_SIZE {
2601 let digest = Sha256::digest(secret);
2602 key_block[..digest.len()].copy_from_slice(&digest);
2603 } else {
2604 key_block[..secret.len()].copy_from_slice(secret);
2605 }
2606
2607 let mut inner_pad = [0x36u8; BLOCK_SIZE];
2608 let mut outer_pad = [0x5cu8; BLOCK_SIZE];
2609 for idx in 0..BLOCK_SIZE {
2610 inner_pad[idx] ^= key_block[idx];
2611 outer_pad[idx] ^= key_block[idx];
2612 }
2613
2614 let mut inner = Sha256::new();
2615 inner.update(inner_pad);
2616 inner.update(message);
2617 let inner_digest = inner.finalize();
2618
2619 let mut outer = Sha256::new();
2620 outer.update(outer_pad);
2621 outer.update(inner_digest);
2622 outer.finalize().into()
2623}
2624
2625fn constant_time_eq(left: &[u8], right: &[u8]) -> bool {
2626 if left.len() != right.len() {
2627 return false;
2628 }
2629 let mut diff = 0u8;
2630 for (&left_byte, &right_byte) in left.iter().zip(right) {
2631 diff |= left_byte ^ right_byte;
2632 }
2633 diff == 0
2634}