safe_zk_token_sdk/encryption/
pedersen.rs1#[cfg(not(target_os = "solana"))]
4use rand::rngs::OsRng;
5use {
6 core::ops::{Add, Mul, Sub},
7 curve25519_dalek::{
8 constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT},
9 ristretto::{CompressedRistretto, RistrettoPoint},
10 scalar::Scalar,
11 traits::MultiscalarMul,
12 },
13 serde::{Deserialize, Serialize},
14 sha3::Sha3_512,
15 std::convert::TryInto,
16 subtle::{Choice, ConstantTimeEq},
17 zeroize::Zeroize,
18};
19
20lazy_static::lazy_static! {
21 pub static ref G: RistrettoPoint = RISTRETTO_BASEPOINT_POINT;
23 pub static ref H: RistrettoPoint =
25 RistrettoPoint::hash_from_bytes::<Sha3_512>(RISTRETTO_BASEPOINT_COMPRESSED.as_bytes());
26}
27
28pub struct Pedersen;
30impl Pedersen {
31 #[cfg(not(target_os = "solana"))]
36 #[allow(clippy::new_ret_no_self)]
37 pub fn new<T: Into<Scalar>>(amount: T) -> (PedersenCommitment, PedersenOpening) {
38 let opening = PedersenOpening::new_rand();
39 let commitment = Pedersen::with(amount, &opening);
40
41 (commitment, opening)
42 }
43
44 #[allow(non_snake_case)]
49 pub fn with<T: Into<Scalar>>(amount: T, open: &PedersenOpening) -> PedersenCommitment {
50 let x: Scalar = amount.into();
51 let r = open.get_scalar();
52
53 PedersenCommitment(RistrettoPoint::multiscalar_mul(&[x, *r], &[*G, *H]))
54 }
55
56 pub fn encode<T: Into<Scalar>>(amount: T) -> PedersenCommitment {
61 PedersenCommitment(amount.into() * &(*G))
62 }
63}
64
65#[derive(Clone, Debug, Default, Serialize, Deserialize, Zeroize)]
69#[zeroize(drop)]
70pub struct PedersenOpening(pub(crate) Scalar);
71impl PedersenOpening {
72 pub fn get_scalar(&self) -> &Scalar {
73 &self.0
74 }
75
76 #[cfg(not(target_os = "solana"))]
77 pub fn new_rand() -> Self {
78 PedersenOpening(Scalar::random(&mut OsRng))
79 }
80
81 #[allow(clippy::wrong_self_convention)]
82 pub fn as_bytes(&self) -> &[u8; 32] {
83 self.0.as_bytes()
84 }
85
86 #[allow(clippy::wrong_self_convention)]
87 pub fn to_bytes(&self) -> [u8; 32] {
88 self.0.to_bytes()
89 }
90
91 pub fn from_bytes(bytes: &[u8]) -> Option<PedersenOpening> {
92 match bytes.try_into() {
93 Ok(bytes) => Scalar::from_canonical_bytes(bytes).map(PedersenOpening),
94 _ => None,
95 }
96 }
97}
98impl Eq for PedersenOpening {}
99impl PartialEq for PedersenOpening {
100 fn eq(&self, other: &Self) -> bool {
101 self.ct_eq(other).unwrap_u8() == 1u8
102 }
103}
104impl ConstantTimeEq for PedersenOpening {
105 fn ct_eq(&self, other: &Self) -> Choice {
106 self.0.ct_eq(&other.0)
107 }
108}
109
110impl<'a, 'b> Add<&'b PedersenOpening> for &'a PedersenOpening {
111 type Output = PedersenOpening;
112
113 fn add(self, opening: &'b PedersenOpening) -> PedersenOpening {
114 PedersenOpening(&self.0 + &opening.0)
115 }
116}
117
118define_add_variants!(
119 LHS = PedersenOpening,
120 RHS = PedersenOpening,
121 Output = PedersenOpening
122);
123
124impl<'a, 'b> Sub<&'b PedersenOpening> for &'a PedersenOpening {
125 type Output = PedersenOpening;
126
127 fn sub(self, opening: &'b PedersenOpening) -> PedersenOpening {
128 PedersenOpening(&self.0 - &opening.0)
129 }
130}
131
132define_sub_variants!(
133 LHS = PedersenOpening,
134 RHS = PedersenOpening,
135 Output = PedersenOpening
136);
137
138impl<'a, 'b> Mul<&'b Scalar> for &'a PedersenOpening {
139 type Output = PedersenOpening;
140
141 fn mul(self, scalar: &'b Scalar) -> PedersenOpening {
142 PedersenOpening(&self.0 * scalar)
143 }
144}
145
146define_mul_variants!(
147 LHS = PedersenOpening,
148 RHS = Scalar,
149 Output = PedersenOpening
150);
151
152impl<'a, 'b> Mul<&'b PedersenOpening> for &'a Scalar {
153 type Output = PedersenOpening;
154
155 fn mul(self, opening: &'b PedersenOpening) -> PedersenOpening {
156 PedersenOpening(self * &opening.0)
157 }
158}
159
160define_mul_variants!(
161 LHS = Scalar,
162 RHS = PedersenOpening,
163 Output = PedersenOpening
164);
165
166#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
168pub struct PedersenCommitment(pub(crate) RistrettoPoint);
169impl PedersenCommitment {
170 pub fn get_point(&self) -> &RistrettoPoint {
171 &self.0
172 }
173
174 #[allow(clippy::wrong_self_convention)]
175 pub fn to_bytes(&self) -> [u8; 32] {
176 self.0.compress().to_bytes()
177 }
178
179 pub fn from_bytes(bytes: &[u8]) -> Option<PedersenCommitment> {
180 if bytes.len() != 32 {
181 return None;
182 }
183
184 Some(PedersenCommitment(
185 CompressedRistretto::from_slice(bytes).decompress()?,
186 ))
187 }
188}
189
190impl<'a, 'b> Add<&'b PedersenCommitment> for &'a PedersenCommitment {
191 type Output = PedersenCommitment;
192
193 fn add(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
194 PedersenCommitment(&self.0 + &commitment.0)
195 }
196}
197
198define_add_variants!(
199 LHS = PedersenCommitment,
200 RHS = PedersenCommitment,
201 Output = PedersenCommitment
202);
203
204impl<'a, 'b> Sub<&'b PedersenCommitment> for &'a PedersenCommitment {
205 type Output = PedersenCommitment;
206
207 fn sub(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
208 PedersenCommitment(&self.0 - &commitment.0)
209 }
210}
211
212define_sub_variants!(
213 LHS = PedersenCommitment,
214 RHS = PedersenCommitment,
215 Output = PedersenCommitment
216);
217
218impl<'a, 'b> Mul<&'b Scalar> for &'a PedersenCommitment {
219 type Output = PedersenCommitment;
220
221 fn mul(self, scalar: &'b Scalar) -> PedersenCommitment {
222 PedersenCommitment(scalar * &self.0)
223 }
224}
225
226define_mul_variants!(
227 LHS = PedersenCommitment,
228 RHS = Scalar,
229 Output = PedersenCommitment
230);
231
232impl<'a, 'b> Mul<&'b PedersenCommitment> for &'a Scalar {
233 type Output = PedersenCommitment;
234
235 fn mul(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
236 PedersenCommitment(self * &commitment.0)
237 }
238}
239
240define_mul_variants!(
241 LHS = Scalar,
242 RHS = PedersenCommitment,
243 Output = PedersenCommitment
244);
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn test_pedersen_homomorphic_addition() {
252 let amt_0: u64 = 77;
253 let amt_1: u64 = 57;
254
255 let rng = &mut OsRng;
256 let open_0 = PedersenOpening(Scalar::random(rng));
257 let open_1 = PedersenOpening(Scalar::random(rng));
258
259 let comm_0 = Pedersen::with(amt_0, &open_0);
260 let comm_1 = Pedersen::with(amt_1, &open_1);
261 let comm_addition = Pedersen::with(amt_0 + amt_1, &(open_0 + open_1));
262
263 assert_eq!(comm_addition, comm_0 + comm_1);
264 }
265
266 #[test]
267 fn test_pedersen_homomorphic_subtraction() {
268 let amt_0: u64 = 77;
269 let amt_1: u64 = 57;
270
271 let rng = &mut OsRng;
272 let open_0 = PedersenOpening(Scalar::random(rng));
273 let open_1 = PedersenOpening(Scalar::random(rng));
274
275 let comm_0 = Pedersen::with(amt_0, &open_0);
276 let comm_1 = Pedersen::with(amt_1, &open_1);
277 let comm_addition = Pedersen::with(amt_0 - amt_1, &(open_0 - open_1));
278
279 assert_eq!(comm_addition, comm_0 - comm_1);
280 }
281
282 #[test]
283 fn test_pedersen_homomorphic_multiplication() {
284 let amt_0: u64 = 77;
285 let amt_1: u64 = 57;
286
287 let (comm, open) = Pedersen::new(amt_0);
288 let scalar = Scalar::from(amt_1);
289 let comm_addition = Pedersen::with(amt_0 * amt_1, &(open * scalar));
290
291 assert_eq!(comm_addition, comm * scalar);
292 assert_eq!(comm_addition, scalar * comm);
293 }
294
295 #[test]
296 fn test_pedersen_commitment_bytes() {
297 let amt: u64 = 77;
298 let (comm, _) = Pedersen::new(amt);
299
300 let encoded = comm.to_bytes();
301 let decoded = PedersenCommitment::from_bytes(&encoded).unwrap();
302
303 assert_eq!(comm, decoded);
304
305 assert_eq!(PedersenCommitment::from_bytes(&[0; 33]), None);
307 }
308
309 #[test]
310 fn test_pedersen_opening_bytes() {
311 let open = PedersenOpening(Scalar::random(&mut OsRng));
312
313 let encoded = open.to_bytes();
314 let decoded = PedersenOpening::from_bytes(&encoded).unwrap();
315
316 assert_eq!(open, decoded);
317
318 assert_eq!(PedersenOpening::from_bytes(&[0; 33]), None);
320 }
321
322 #[test]
323 fn test_serde_pedersen_commitment() {
324 let amt: u64 = 77;
325 let (comm, _) = Pedersen::new(amt);
326
327 let encoded = bincode::serialize(&comm).unwrap();
328 let decoded: PedersenCommitment = bincode::deserialize(&encoded).unwrap();
329
330 assert_eq!(comm, decoded);
331 }
332
333 #[test]
334 fn test_serde_pedersen_opening() {
335 let open = PedersenOpening(Scalar::random(&mut OsRng));
336
337 let encoded = bincode::serialize(&open).unwrap();
338 let decoded: PedersenOpening = bincode::deserialize(&encoded).unwrap();
339
340 assert_eq!(open, decoded);
341 }
342}