1use core::{fmt, marker::PhantomData, ops};
4
5use elliptic_curve::{
6 rand_core::{CryptoRng, RngCore},
7 zeroize::{Zeroize, Zeroizing},
8};
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[cfg(feature = "serde")]
13use crate::serde::ElementHelper;
14use crate::{
15 PublicKey, SecretKey,
16 alloc::{HashMap, Vec, vec},
17 group::{Group, ScalarOps},
18};
19
20#[derive(Clone, Copy)]
95#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
96pub struct Ciphertext<G: Group> {
97 #[cfg_attr(feature = "serde", serde(with = "ElementHelper::<G>"))]
98 pub(crate) random_element: G::Element,
99 #[cfg_attr(feature = "serde", serde(with = "ElementHelper::<G>"))]
100 pub(crate) blinded_element: G::Element,
101}
102
103impl<G: Group> fmt::Debug for Ciphertext<G> {
104 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105 formatter
106 .debug_struct("Ciphertext")
107 .field("random_element", &self.random_element)
108 .field("blinded_element", &self.blinded_element)
109 .finish()
110 }
111}
112
113impl<G: Group> Ciphertext<G> {
114 pub fn from_elements(random_element: G::Element, blinded_element: G::Element) -> Self {
116 Self {
117 random_element,
118 blinded_element,
119 }
120 }
121
122 pub fn zero() -> Self {
124 Self {
125 random_element: G::identity(),
126 blinded_element: G::identity(),
127 }
128 }
129
130 pub fn non_blinded<T>(value: T) -> Self
133 where
134 G::Scalar: From<T>,
135 {
136 let scalar = Zeroizing::new(G::Scalar::from(value));
137 Self {
138 random_element: G::identity(),
139 blinded_element: G::mul_generator(&scalar),
140 }
141 }
142
143 pub fn random_element(&self) -> &G::Element {
145 &self.random_element
146 }
147
148 pub fn blinded_element(&self) -> &G::Element {
150 &self.blinded_element
151 }
152
153 pub fn to_bytes(self) -> Vec<u8> {
156 let mut bytes = vec![0_u8; 2 * G::ELEMENT_SIZE];
157 G::serialize_element(&self.random_element, &mut bytes[..G::ELEMENT_SIZE]);
158 G::serialize_element(&self.blinded_element, &mut bytes[G::ELEMENT_SIZE..]);
159 bytes
160 }
161}
162
163impl<G: Group> ops::Add for Ciphertext<G> {
164 type Output = Self;
165
166 fn add(self, rhs: Self) -> Self {
167 Self {
168 random_element: self.random_element + rhs.random_element,
169 blinded_element: self.blinded_element + rhs.blinded_element,
170 }
171 }
172}
173
174impl<G: Group> ops::AddAssign for Ciphertext<G> {
175 fn add_assign(&mut self, rhs: Self) {
176 *self = *self + rhs;
177 }
178}
179
180impl<G: Group> ops::Sub for Ciphertext<G> {
181 type Output = Self;
182
183 fn sub(self, rhs: Self) -> Self {
184 Self {
185 random_element: self.random_element - rhs.random_element,
186 blinded_element: self.blinded_element - rhs.blinded_element,
187 }
188 }
189}
190
191impl<G: Group> ops::SubAssign for Ciphertext<G> {
192 fn sub_assign(&mut self, rhs: Self) {
193 *self = *self - rhs;
194 }
195}
196
197impl<G: Group> ops::Mul<&G::Scalar> for Ciphertext<G> {
198 type Output = Self;
199
200 fn mul(self, rhs: &G::Scalar) -> Self {
201 Self {
202 random_element: self.random_element * rhs,
203 blinded_element: self.blinded_element * rhs,
204 }
205 }
206}
207
208impl<G: Group> ops::Mul<u64> for Ciphertext<G> {
209 type Output = Self;
210
211 fn mul(self, rhs: u64) -> Self {
212 let scalar = G::Scalar::from(rhs);
213 self * &scalar
214 }
215}
216
217impl<G: Group> ops::Neg for Ciphertext<G> {
218 type Output = Self;
219
220 fn neg(self) -> Self::Output {
221 Self {
222 random_element: -self.random_element,
223 blinded_element: -self.blinded_element,
224 }
225 }
226}
227
228#[derive(Debug, Clone)]
260pub struct DiscreteLogTable<G: Group> {
261 inner: HashMap<Vec<u8>, u64>,
262 _t: PhantomData<G>,
263}
264
265impl<G: Group> DiscreteLogTable<G> {
266 pub fn new(values: impl IntoIterator<Item = u64>) -> Self {
268 let lookup_table = values
269 .into_iter()
270 .filter(|&value| value != 0)
271 .map(|i| {
272 let element = G::vartime_mul_generator(&G::Scalar::from(i));
273 let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
274 G::serialize_element(&element, &mut bytes);
275 (bytes, i)
276 })
277 .collect();
278
279 Self {
280 inner: lookup_table,
281 _t: PhantomData,
282 }
283 }
284
285 pub fn get(&self, decrypted_element: &G::Element) -> Option<u64> {
288 if G::is_identity(decrypted_element) {
289 Some(0)
292 } else {
293 let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
294 G::serialize_element(decrypted_element, &mut bytes);
295 self.inner.get(&bytes).copied()
296 }
297 }
298}
299
300#[derive(Debug, Clone)]
302#[doc(hidden)] pub struct ExtendedCiphertext<G: Group> {
304 pub(crate) inner: Ciphertext<G>,
305 pub(crate) random_scalar: SecretKey<G>,
306}
307
308impl<G: Group> ExtendedCiphertext<G> {
309 pub(crate) fn new<R: CryptoRng + RngCore>(
311 value: G::Element,
312 receiver: &PublicKey<G>,
313 rng: &mut R,
314 ) -> Self {
315 let random_scalar = SecretKey::<G>::generate(rng);
316 let random_element = G::mul_generator(random_scalar.expose_scalar());
317 let dh_element = receiver.as_element() * random_scalar.expose_scalar();
318 let blinded_element = value + dh_element;
319
320 Self {
321 inner: Ciphertext {
322 random_element,
323 blinded_element,
324 },
325 random_scalar,
326 }
327 }
328
329 pub(crate) fn zero() -> Self {
330 Self {
331 inner: Ciphertext::zero(),
332 random_scalar: SecretKey::new(G::Scalar::from(0_u64)),
333 }
334 }
335
336 pub(crate) fn with_value<V>(self, value: V) -> CiphertextWithValue<G, V>
337 where
338 V: Zeroize,
339 G::Scalar: From<V>,
340 {
341 CiphertextWithValue {
342 inner: self,
343 value: Zeroizing::new(value),
344 }
345 }
346}
347
348impl<G: Group> ops::Add for ExtendedCiphertext<G> {
349 type Output = Self;
350
351 fn add(self, rhs: Self) -> Self::Output {
352 Self {
353 inner: self.inner + rhs.inner,
354 random_scalar: self.random_scalar + rhs.random_scalar,
355 }
356 }
357}
358
359impl<G: Group> ops::AddAssign for ExtendedCiphertext<G> {
360 fn add_assign(&mut self, rhs: Self) {
361 self.inner += rhs.inner;
362 self.random_scalar += rhs.random_scalar;
363 }
364}
365
366impl<G: Group> ops::Sub for ExtendedCiphertext<G> {
367 type Output = Self;
368
369 fn sub(self, rhs: Self) -> Self::Output {
370 Self {
371 inner: self.inner - rhs.inner,
372 random_scalar: self.random_scalar - rhs.random_scalar,
373 }
374 }
375}
376
377#[derive(Debug)]
383pub struct CiphertextWithValue<G: Group, V: Zeroize = <G as ScalarOps>::Scalar> {
384 inner: ExtendedCiphertext<G>,
385 value: Zeroizing<V>,
386}
387
388impl<G: Group, V: Zeroize> From<CiphertextWithValue<G, V>> for Ciphertext<G> {
389 fn from(ciphertext: CiphertextWithValue<G, V>) -> Self {
390 ciphertext.inner.inner
391 }
392}
393
394impl<G: Group, V> CiphertextWithValue<G, V>
395where
396 V: Copy + Zeroize,
397 G::Scalar: From<V>,
398{
399 pub fn new<R: CryptoRng + RngCore>(value: V, receiver: &PublicKey<G>, rng: &mut R) -> Self {
404 let scalar = Zeroizing::new(G::Scalar::from(value));
405 let element = G::mul_generator(&scalar);
406 ExtendedCiphertext::new(element, receiver, rng).with_value(value)
407 }
408
409 pub fn generalize(self) -> CiphertextWithValue<G> {
411 CiphertextWithValue {
412 inner: self.inner,
413 value: Zeroizing::new(G::Scalar::from(*self.value)),
414 }
415 }
416}
417
418impl<G: Group, V> CiphertextWithValue<G, V>
419where
420 V: Zeroize,
421 G::Scalar: From<V>,
422{
423 pub fn inner(&self) -> &Ciphertext<G> {
425 &self.inner.inner
426 }
427
428 pub(crate) fn extended_ciphertext(&self) -> &ExtendedCiphertext<G> {
429 &self.inner
430 }
431
432 pub(crate) fn randomness(&self) -> &SecretKey<G> {
433 &self.inner.random_scalar
434 }
435
436 pub(crate) fn value(&self) -> &V {
437 &self.value
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use rand::Rng;
444
445 use super::*;
446 use crate::{Keypair, curve25519::scalar::Scalar as Curve25519Scalar, group::Ristretto};
447
448 #[test]
449 fn ciphertext_addition() {
450 let mut rng = rand::rng();
451 let numbers: Vec<_> = (0..10).map(|_| u64::from(rng.random::<u32>())).collect();
452 let sum = numbers.iter().copied().sum::<u64>();
453
454 let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
455 let ciphertexts = numbers.into_iter().map(|x| pk.encrypt(x, &mut rng));
456 let sum_ciphertext = ciphertexts.reduce(ops::Add::add).unwrap();
457 let decrypted = sk.decrypt_to_element(sum_ciphertext);
458
459 assert_eq!(decrypted, Ristretto::vartime_mul_generator(&sum.into()));
460 }
461
462 #[test]
463 fn ciphertext_mul_by_u64() {
464 let mut rng = rand::rng();
465 let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
466 for _ in 0..100 {
467 let x = rng.random::<u64>();
468 let multiplier = rng.random::<u64>();
469 let ciphertext = pk.encrypt(x, &mut rng);
470 let decrypted = sk.decrypt_to_element(ciphertext * multiplier);
471
472 let expected_decryption =
473 Curve25519Scalar::from(x) * Curve25519Scalar::from(multiplier);
474 assert_eq!(
475 decrypted,
476 Ristretto::vartime_mul_generator(&expected_decryption)
477 );
478 }
479 }
480
481 #[test]
482 fn ciphertext_negation() {
483 let mut rng = rand::rng();
484 let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
485 for _ in 0..100 {
486 let x = rng.random::<u64>();
487 let ciphertext = pk.encrypt(x, &mut rng);
488 let neg_ciphertext = -ciphertext;
489 let decrypted = sk.decrypt_to_element(neg_ciphertext);
490
491 assert_eq!(
492 decrypted,
493 Ristretto::vartime_mul_generator(&-Curve25519Scalar::from(x))
494 );
495 }
496 }
497
498 #[test]
499 fn non_blinded_ciphertext() {
500 let mut rng = rand::rng();
501 let (_, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
502 for _ in 0..100 {
503 let x = rng.random::<u64>();
504 let ciphertext = Ciphertext::non_blinded(x);
505 let decrypted = sk.decrypt_to_element(ciphertext);
506
507 assert_eq!(
508 decrypted,
509 Ristretto::vartime_mul_generator(&Curve25519Scalar::from(x))
510 );
511 }
512 }
513}