elastic_elgamal/keys/
impls.rs

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