ouisync/protocol/
proof.rs

1use super::repository::RepositoryId;
2use crate::{
3    crypto::{
4        sign::{Keypair, PublicKey, Signature},
5        Hash, Hashable,
6    },
7    version_vector::VersionVector,
8};
9use serde::{Deserialize, Serialize};
10use std::ops::Deref;
11use thiserror::Error;
12
13/// Information that prove that a snapshot was created by a replica that has write access to the
14/// repository.
15#[derive(Clone, Eq, PartialEq, Debug)]
16pub struct Proof(UntrustedProof);
17
18impl Proof {
19    /// Create new proof signed with the given write keys.
20    pub fn new(
21        writer_id: PublicKey,
22        version_vector: VersionVector,
23        hash: Hash,
24        write_keys: &Keypair,
25    ) -> Self {
26        let signature_material = signature_material(&writer_id, &version_vector, &hash);
27        let signature = write_keys.sign(signature_material.as_ref());
28
29        Self::new_unchecked(writer_id, version_vector, hash, signature)
30    }
31
32    /// Create new proof form a pre-existing signature without checking whether the signature
33    /// is valid. Use only when loading proofs from the local db, never when receiving them from
34    /// remote replicas.
35    pub fn new_unchecked(
36        writer_id: PublicKey,
37        version_vector: VersionVector,
38        hash: Hash,
39        signature: Signature,
40    ) -> Self {
41        Self(UntrustedProof {
42            writer_id,
43            version_vector,
44            hash,
45            signature,
46        })
47    }
48
49    pub fn into_version_vector(self) -> VersionVector {
50        self.0.version_vector
51    }
52}
53
54impl Deref for Proof {
55    type Target = UntrustedProof;
56
57    fn deref(&self) -> &Self::Target {
58        &self.0
59    }
60}
61
62#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
63pub struct UntrustedProof {
64    pub writer_id: PublicKey,
65    pub version_vector: VersionVector,
66    pub hash: Hash,
67    pub signature: Signature,
68}
69
70impl UntrustedProof {
71    pub fn verify(self, repository_id: &RepositoryId) -> Result<Proof, ProofError> {
72        let signature_material =
73            signature_material(&self.writer_id, &self.version_vector, &self.hash);
74        if repository_id
75            .write_public_key()
76            .verify(signature_material.as_ref(), &self.signature)
77        {
78            Ok(Proof(self))
79        } else {
80            Err(ProofError)
81        }
82    }
83}
84
85impl From<Proof> for UntrustedProof {
86    fn from(proof: Proof) -> Self {
87        proof.0
88    }
89}
90
91fn signature_material(writer_id: &PublicKey, version_vector: &VersionVector, hash: &Hash) -> Hash {
92    (writer_id, version_vector, hash).hash()
93}
94
95#[derive(Debug, Error)]
96#[error("proof is invalid")]
97pub struct ProofError;