Skip to main content

pijul_core/pristine/
merkle.rs

1use super::{BASE32, Base32};
2use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
3
4pub(crate) const BASE32_BYTES: usize = 53;
5
6#[doc(hidden)]
7#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8pub enum Merkle {
9    Ed25519(curve25519_dalek::edwards::EdwardsPoint),
10}
11
12impl Default for Merkle {
13    fn default() -> Self {
14        Merkle::zero()
15    }
16}
17
18#[doc(hidden)]
19#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
20#[repr(u8)]
21pub enum MerkleAlgorithm {
22    Ed25519 = 1,
23}
24
25impl std::fmt::Debug for Merkle {
26    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
27        write!(fmt, "{:?}", self.to_base32())
28    }
29}
30
31impl std::fmt::Debug for SerializedMerkle {
32    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
33        let mut hash = [0; 33];
34        hash[..].clone_from_slice(&self.0);
35        hash.rotate_left(1);
36        write!(fmt, "{:?}", BASE32.encode(&hash))
37    }
38}
39
40impl std::hash::Hash for Merkle {
41    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
42        match self {
43            Merkle::Ed25519(x) => x.compress().as_bytes().hash(hasher),
44        }
45    }
46}
47
48impl From<&super::Hash> for curve25519_dalek::scalar::Scalar {
49    fn from(h: &super::Hash) -> Self {
50        match h {
51            super::Hash::Blake3(h) => curve25519_dalek::scalar::Scalar::from_bytes_mod_order(*h),
52            #[cfg(feature = "git2")]
53            super::Hash::GitSha2(h) => curve25519_dalek::scalar::Scalar::from_bytes_mod_order(*h),
54            #[cfg(feature = "git2")]
55            super::Hash::GitSha1(h) => {
56                let mut hh = [0; 32];
57                hh[..super::hash::GIT_SHA1_BYTES].clone_from_slice(h);
58                curve25519_dalek::scalar::Scalar::from_bytes_mod_order(hh)
59            }
60            _ => unreachable!(),
61        }
62    }
63}
64
65impl From<&super::SerializedHash> for curve25519_dalek::scalar::Scalar {
66    fn from(h: &super::SerializedHash) -> Self {
67        let h: super::Hash = h.into();
68        (&h).into()
69    }
70}
71
72impl From<&Merkle> for curve25519_dalek::scalar::Scalar {
73    fn from(h: &Merkle) -> Self {
74        match *h {
75            Merkle::Ed25519(h) => {
76                let h = h.compress();
77                curve25519_dalek::scalar::Scalar::from_bytes_mod_order(*h.as_bytes())
78            }
79        }
80    }
81}
82
83impl From<&super::SerializedMerkle> for curve25519_dalek::scalar::Scalar {
84    fn from(h: &super::SerializedMerkle) -> Self {
85        let h: Merkle = h.into();
86        (&h).into()
87    }
88}
89
90impl Merkle {
91    pub fn zero() -> Self {
92        Merkle::Ed25519(ED25519_BASEPOINT_POINT)
93    }
94    pub fn next<S: Into<curve25519_dalek::scalar::Scalar>>(&self, h: S) -> Self {
95        match self {
96            Merkle::Ed25519(h0) => {
97                let scalar = h.into();
98                Merkle::Ed25519(h0 * scalar)
99            }
100        }
101    }
102    pub fn to_bytes(&self) -> [u8; 32] {
103        match *self {
104            Merkle::Ed25519(ref e) => e.compress().to_bytes(),
105        }
106    }
107
108    pub fn from_prefix(s: &str) -> Option<Self> {
109        let mut b32 = [b'A'; BASE32_BYTES];
110        if s.len() > BASE32_BYTES {
111            return None;
112        }
113        b32[..s.len()].clone_from_slice(s.as_bytes());
114        let bytes = if let Ok(bytes) = BASE32.decode(&b32) {
115            bytes
116        } else {
117            return None;
118        };
119        curve25519_dalek::edwards::CompressedEdwardsY::from_slice(&bytes[..32])
120            .unwrap()
121            .decompress()
122            .map(Merkle::Ed25519)
123    }
124}
125
126impl super::Base32 for Merkle {
127    fn to_base32(&self) -> String {
128        match *self {
129            Merkle::Ed25519(ref s) => {
130                let mut hash = [0; 33];
131                hash[..32].clone_from_slice(s.compress().as_bytes());
132                hash[32] = MerkleAlgorithm::Ed25519 as u8;
133                BASE32.encode(&hash)
134            }
135        }
136    }
137
138    /// Parses a base-32 string into a `Merkle`.
139    fn from_base32(s: &[u8]) -> Option<Self> {
140        let bytes = if let Ok(b) = BASE32.decode(s) {
141            b
142        } else {
143            return None;
144        };
145        if bytes.len() == 33 && *bytes.last().unwrap() == MerkleAlgorithm::Ed25519 as u8 {
146            curve25519_dalek::edwards::CompressedEdwardsY::from_slice(&bytes[..32])
147                .unwrap()
148                .decompress()
149                .map(Merkle::Ed25519)
150        } else {
151            None
152        }
153    }
154}
155
156impl std::str::FromStr for Merkle {
157    type Err = crate::ParseError;
158    fn from_str(s: &str) -> Result<Self, Self::Err> {
159        if let Some(b) = Self::from_base32(s.as_bytes()) {
160            Ok(b)
161        } else {
162            Err(crate::ParseError { s: s.to_string() })
163        }
164    }
165}
166
167#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
168pub struct SerializedMerkle(pub [u8; 33]);
169
170impl SerializedMerkle {
171    pub fn from_prefix(s: &str) -> Option<Self> {
172        let mut b32 = [b'A'; BASE32_BYTES];
173        if s.len() > BASE32_BYTES {
174            return None;
175        }
176        b32[..s.len()].clone_from_slice(s.as_bytes());
177        let mut result = [0; 33];
178        BASE32.decode_mut(&b32, &mut result).ok()?;
179        result.rotate_right(1);
180
181        // If we ever add other algorithms we may want to do the same
182        // as for Hash. This is ok for now though, and faster.
183        result[0] = MerkleAlgorithm::Ed25519 as u8;
184        Some(SerializedMerkle(result))
185    }
186}
187
188impl PartialEq<Merkle> for SerializedMerkle {
189    fn eq(&self, m: &Merkle) -> bool {
190        match m {
191            Merkle::Ed25519(q) => {
192                (self.0)[0] == MerkleAlgorithm::Ed25519 as u8 && {
193                    let q = q.compress();
194                    q.as_bytes() == &(self.0)[1..]
195                }
196            }
197        }
198    }
199}
200
201impl PartialEq<SerializedMerkle> for Merkle {
202    fn eq(&self, m: &SerializedMerkle) -> bool {
203        m.eq(self)
204    }
205}
206
207impl<'a> From<&'a Merkle> for SerializedMerkle {
208    fn from(m: &'a Merkle) -> Self {
209        let mut mm = [0; 33];
210        match m {
211            Merkle::Ed25519(q) => {
212                mm[0] = MerkleAlgorithm::Ed25519 as u8;
213                let q = q.compress();
214                let q = q.as_bytes();
215                mm[1..].copy_from_slice(q);
216                SerializedMerkle(mm)
217            }
218        }
219    }
220}
221
222impl From<Merkle> for SerializedMerkle {
223    fn from(m: Merkle) -> Self {
224        let mut mm = [0; 33];
225        match m {
226            Merkle::Ed25519(q) => {
227                mm[0] = MerkleAlgorithm::Ed25519 as u8;
228                let q = q.compress();
229                let q = q.as_bytes();
230                mm[1..].copy_from_slice(q);
231                SerializedMerkle(mm)
232            }
233        }
234    }
235}
236
237impl<'a> From<&'a SerializedMerkle> for Merkle {
238    fn from(m: &'a SerializedMerkle) -> Self {
239        assert_eq!((m.0)[0], MerkleAlgorithm::Ed25519 as u8);
240        Merkle::Ed25519(
241            curve25519_dalek::edwards::CompressedEdwardsY::from_slice(&(m.0)[1..])
242                .unwrap()
243                .decompress()
244                .unwrap(),
245        )
246    }
247}
248
249impl From<SerializedMerkle> for Merkle {
250    fn from(m: SerializedMerkle) -> Self {
251        assert_eq!((m.0)[0], MerkleAlgorithm::Ed25519 as u8);
252        Merkle::Ed25519(
253            curve25519_dalek::edwards::CompressedEdwardsY::from_slice(&(m.0)[1..])
254                .unwrap()
255                .decompress()
256                .unwrap(),
257        )
258    }
259}