rust_elgamal/
ciphertext.rs

1// Ciphertext definitions for rust-elgamal.
2// Copyright 2021 Eleanor McMurtry
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use core::fmt::{Debug, Formatter};
17use core::ops::{Add, Neg, Mul, Sub};
18
19use curve25519_dalek::ristretto::RistrettoPoint;
20use curve25519_dalek::traits::Identity;
21use curve25519_dalek::scalar::Scalar;
22
23#[cfg(feature = "enable-serde")]
24use serde::{Serialize, Deserialize};
25
26/// An ElGamal ciphertext.
27///
28/// Represented as a pair of the form (rG, M + rY) where r is a blinding factor, G is the group
29/// generator, M is the message, and Y is the public key.
30#[derive(Copy, Clone, PartialEq, Eq)]
31#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
32pub struct Ciphertext(pub(crate) RistrettoPoint, pub(crate) RistrettoPoint);
33
34impl Ciphertext {
35    /// Returns the pair-of-points representation of the ciphertext. Intended for advanced use only.
36    pub fn inner(&self) -> (RistrettoPoint, RistrettoPoint) {
37        (self.0, self.1)
38    }
39}
40
41impl Debug for Ciphertext {
42    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
43        write!(f, "Ciphertext({:?}, {:?})", self.0.compress(), self.1.compress())
44    }
45}
46
47// Conversion traits
48
49impl From<(RistrettoPoint, RistrettoPoint)> for Ciphertext {
50    fn from(pair: (RistrettoPoint, RistrettoPoint)) -> Self {
51        Self(pair.0, pair.1)
52    }
53}
54
55// Arithmetic traits for homomorphisms
56
57impl Identity for Ciphertext {
58    fn identity() -> Self {
59        Self(RistrettoPoint::identity(), RistrettoPoint::identity())
60    }
61}
62
63impl Add for Ciphertext {
64    type Output = Self;
65
66    fn add(self, rhs: Self) -> Self::Output {
67        Ciphertext(self.0 + rhs.0, self.1 + rhs.1)
68    }
69}
70
71impl Add for &Ciphertext {
72    type Output = Ciphertext;
73
74    fn add(self, rhs: Self) -> Self::Output {
75        Ciphertext(&self.0 + &rhs.0, &self.1 + &rhs.1)
76    }
77}
78
79impl Add<&Ciphertext> for Ciphertext {
80    type Output = Ciphertext;
81
82    fn add(self, rhs: &Ciphertext) -> Self::Output {
83        Ciphertext(self.0 + &rhs.0, self.1 + &rhs.1)
84    }
85}
86
87impl Add<Ciphertext> for &Ciphertext {
88    type Output = Ciphertext;
89
90    fn add(self, rhs: Ciphertext) -> Self::Output {
91        Ciphertext(&self.0 + rhs.0, &self.1 + rhs.1)
92    }
93}
94
95impl Sub for Ciphertext {
96    type Output = Self;
97
98    fn sub(self, rhs: Self) -> Self::Output {
99        Ciphertext(self.0 - rhs.0, self.1 - rhs.1)
100    }
101}
102
103impl Sub for &Ciphertext {
104    type Output = Ciphertext;
105
106    fn sub(self, rhs: Self) -> Self::Output {
107        Ciphertext(&self.0 - &rhs.0, &self.1 - &rhs.1)
108    }
109}
110
111impl Sub<&Ciphertext> for Ciphertext {
112    type Output = Ciphertext;
113
114    fn sub(self, rhs: &Ciphertext) -> Self::Output {
115        Ciphertext(self.0 - &rhs.0, self.1 - &rhs.1)
116    }
117}
118
119impl Sub<Ciphertext> for &Ciphertext {
120    type Output = Ciphertext;
121
122    fn sub(self, rhs: Ciphertext) -> Self::Output {
123        Ciphertext(&self.0 - rhs.0, &self.1 - rhs.1)
124    }
125}
126
127impl Neg for Ciphertext {
128    type Output = Ciphertext;
129
130    fn neg(self) -> Self::Output {
131        Ciphertext(-self.0, -self.1)
132    }
133}
134
135impl Neg for &Ciphertext {
136    type Output = Ciphertext;
137
138    fn neg(self) -> Self::Output {
139        Ciphertext(-self.0, -self.1)
140    }
141}
142
143impl Mul<Scalar> for Ciphertext {
144    type Output = Ciphertext;
145
146    fn mul(self, rhs: Scalar) -> Self::Output {
147        Ciphertext(self.0 * rhs, self.1 * rhs)
148    }
149}
150
151impl Mul<Scalar> for &Ciphertext {
152    type Output = Ciphertext;
153
154    fn mul(self, rhs: Scalar) -> Self::Output {
155        Ciphertext(self.0 * rhs, self.1 * rhs)
156    }
157}
158
159impl Mul<&Scalar> for Ciphertext {
160    type Output = Ciphertext;
161
162    fn mul(self, rhs: &Scalar) -> Self::Output {
163        Ciphertext(self.0 * rhs, self.1 * rhs)
164    }
165}
166
167impl Mul<&Scalar> for &Ciphertext {
168    type Output = Ciphertext;
169
170    fn mul(self, rhs: &Scalar) -> Self::Output {
171        Ciphertext(self.0 * rhs, self.1 * rhs)
172    }
173}
174
175#[cfg(feature = "enable-serde")]
176#[cfg(test)]
177mod tests {
178    use rand::prelude::StdRng;
179    use rand_core::SeedableRng;
180
181    use crate::{DecryptionKey, RistrettoPoint};
182
183    #[test]
184    fn serde_ciphertext() {
185        const N: usize = 100;
186
187        let mut rng = StdRng::from_entropy();
188        let dk = DecryptionKey::new(&mut rng);
189
190        for _ in 0..N {
191            let ct = dk.encryption_key().encrypt(RistrettoPoint::random(&mut rng), &mut rng);
192            let encoded = bincode::serialize(&ct).unwrap();
193            assert_eq!(encoded.len(), 64);
194
195            let decoded = bincode::deserialize(&encoded).unwrap();
196            assert_eq!(ct, decoded);
197        }
198    }
199}