elastic_elgamal/keys/
impls.rs

1//! Operations on public / secret keys.
2
3use core::iter;
4
5use elliptic_curve::rand_core::{CryptoRng, RngCore};
6use merlin::Transcript;
7
8use crate::{
9    Ciphertext, DiscreteLogTable, LogEqualityProof, PreparedRange, PublicKey, RangeProof,
10    RingProof, RingProofBuilder, SecretKey, VerificationError, alloc::vec,
11    encryption::ExtendedCiphertext, group::Group,
12};
13
14impl<G: Group> PublicKey<G> {
15    /// Encrypts a value for this key.
16    pub fn encrypt<T, R: CryptoRng + RngCore>(&self, value: T, rng: &mut R) -> Ciphertext<G>
17    where
18        G::Scalar: From<T>,
19    {
20        let scalar = G::Scalar::from(value);
21        let element = G::mul_generator(&scalar);
22        ExtendedCiphertext::new(element, self, rng).inner
23    }
24
25    /// Encrypts a group element.
26    pub fn encrypt_element<R: CryptoRng + RngCore>(
27        &self,
28        value: G::Element,
29        rng: &mut R,
30    ) -> Ciphertext<G> {
31        ExtendedCiphertext::new(value, self, rng).inner
32    }
33
34    /// Encrypts zero value and provides a zero-knowledge proof of encryption correctness.
35    pub fn encrypt_zero<R>(&self, rng: &mut R) -> (Ciphertext<G>, LogEqualityProof<G>)
36    where
37        R: CryptoRng + RngCore,
38    {
39        let random_scalar = SecretKey::<G>::generate(rng);
40        let random_element = G::mul_generator(&random_scalar.0);
41        let blinded_element = self.element * &random_scalar.0;
42        let ciphertext = Ciphertext {
43            random_element,
44            blinded_element,
45        };
46
47        let proof = LogEqualityProof::new(
48            self,
49            &random_scalar,
50            (random_element, blinded_element),
51            &mut Transcript::new(b"zero_encryption"),
52            rng,
53        );
54
55        (ciphertext, proof)
56    }
57
58    /// Verifies that this is an encryption of a zero value.
59    ///
60    /// # Errors
61    ///
62    /// Returns an error if the `proof` does not verify.
63    pub fn verify_zero(
64        &self,
65        ciphertext: Ciphertext<G>,
66        proof: &LogEqualityProof<G>,
67    ) -> Result<(), VerificationError> {
68        proof.verify(
69            self,
70            (ciphertext.random_element, ciphertext.blinded_element),
71            &mut Transcript::new(b"zero_encryption"),
72        )
73    }
74
75    /// Encrypts a boolean value (0 or 1) and provides a zero-knowledge proof of encryption
76    /// correctness.
77    ///
78    /// # Examples
79    ///
80    /// See [`Ciphertext`] docs for an example of usage.
81    pub fn encrypt_bool<R: CryptoRng + RngCore>(
82        &self,
83        value: bool,
84        rng: &mut R,
85    ) -> (Ciphertext<G>, RingProof<G>) {
86        let mut transcript = Transcript::new(b"bool_encryption");
87        let admissible_values = [G::identity(), G::generator()];
88        let mut ring_responses = vec![G::Scalar::default(); 2];
89        let mut builder = RingProofBuilder::new(self, 1, &mut ring_responses, &mut transcript, rng);
90        let ciphertext = builder.add_value(&admissible_values, usize::from(value));
91        let proof = RingProof::new(builder.build(), ring_responses);
92        (ciphertext.inner, proof)
93    }
94
95    /// Verifies a proof of encryption correctness of a boolean value, which was presumably
96    /// obtained via [`Self::encrypt_bool()`].
97    ///
98    /// # Errors
99    ///
100    /// Returns an error if the `proof` does not verify.
101    ///
102    /// # Examples
103    ///
104    /// See [`Ciphertext`] docs for an example of usage.
105    pub fn verify_bool(
106        &self,
107        ciphertext: Ciphertext<G>,
108        proof: &RingProof<G>,
109    ) -> Result<(), VerificationError> {
110        let admissible_values = [G::identity(), G::generator()];
111        proof.verify(
112            self,
113            iter::once(&admissible_values as &[_]),
114            iter::once(ciphertext),
115            &mut Transcript::new(b"bool_encryption"),
116        )
117    }
118
119    /// Encrypts `value` and provides a zero-knowledge proof that it lies in the specified `range`.
120    ///
121    /// # Panics
122    ///
123    /// Panics if `value` is out of `range`.
124    ///
125    /// # Examples
126    ///
127    /// See [`Ciphertext`] docs for an example of usage.
128    pub fn encrypt_range<R: CryptoRng + RngCore>(
129        &self,
130        range: &PreparedRange<G>,
131        value: u64,
132        rng: &mut R,
133    ) -> (Ciphertext<G>, RangeProof<G>) {
134        let mut transcript = Transcript::new(b"ciphertext_range");
135        let (ciphertext, proof) = RangeProof::new(self, range, value, &mut transcript, rng);
136        (ciphertext.into(), proof)
137    }
138
139    /// Verifies `proof` that `ciphertext` encrypts a value lying in `range`.
140    ///
141    /// The `proof` should be created with a call to [`Self::encrypt_range()`] with the same
142    /// [`PreparedRange`]; otherwise, the proof will not verify.
143    ///
144    /// # Errors
145    ///
146    /// Returns an error if the `proof` does not verify.
147    pub fn verify_range(
148        &self,
149        range: &PreparedRange<G>,
150        ciphertext: Ciphertext<G>,
151        proof: &RangeProof<G>,
152    ) -> Result<(), VerificationError> {
153        let mut transcript = Transcript::new(b"ciphertext_range");
154        proof.verify(self, range, ciphertext, &mut transcript)
155    }
156}
157
158impl<G: Group> SecretKey<G> {
159    /// Decrypts the provided ciphertext and returns the produced group element.
160    ///
161    /// As the ciphertext does not include a MAC or another way to assert integrity,
162    /// this operation cannot fail. If the ciphertext is not produced properly (e.g., it targets
163    /// another receiver), the returned group element will be garbage.
164    pub fn decrypt_to_element(&self, encrypted: Ciphertext<G>) -> G::Element {
165        let dh_element = encrypted.random_element * &self.0;
166        encrypted.blinded_element - dh_element
167    }
168
169    /// Decrypts the provided ciphertext and returns the original encrypted value.
170    ///
171    /// `lookup_table` is used to find encrypted values based on the original decrypted
172    /// group element. That is, it must contain all valid plaintext values. If the value
173    /// is not in the table, this method will return `None`.
174    pub fn decrypt(
175        &self,
176        encrypted: Ciphertext<G>,
177        lookup_table: &DiscreteLogTable<G>,
178    ) -> Option<u64> {
179        lookup_table.get(&self.decrypt_to_element(encrypted))
180    }
181}