elastic_elgamal/decryption.rs
1//! Verifiable decryption.
2
3use elliptic_curve::rand_core::{CryptoRng, RngCore};
4use merlin::Transcript;
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8#[cfg(feature = "serde")]
9use crate::serde::ElementHelper;
10use crate::{
11 Ciphertext, DiscreteLogTable, Keypair, PublicKey, VerificationError,
12 alloc::{Vec, vec},
13 group::Group,
14 proofs::{LogEqualityProof, TranscriptForGroup},
15};
16
17/// Verifiable decryption for a certain [`Ciphertext`] in the ElGamal encryption scheme.
18/// Usable both for standalone proofs and in threshold encryption.
19///
20/// # Construction
21///
22/// Decryption is represented by a single group element – the result of combining
23/// a [`SecretKey`](crate::SecretKey) scalar `x` with the random element of the ciphertext `R`
24/// (i.e., `D = [x]R`, the Diffie – Hellman construction).
25/// This element can retrieved using [`Self::as_element()`] and applied to a ciphertext using
26/// [`Self::decrypt()`] or [`Self::decrypt_to_element()`].
27///
28/// The decryption can be proven with the help of a standard [`LogEqualityProof`]. Indeed,
29/// to prove the validity of decryption, it is sufficient to prove `dlog_R(D) = dlog_G(K)`,
30/// where `G` is the conventional group generator and `K = [x]G` is the public key for encryption.
31///
32/// # Examples
33///
34/// `VerifiableDecryption` can be used either within the threshold encryption scheme provided by
35/// the [`sharing`](crate::sharing) module, or independently (for example, if another approach
36/// to secret sharing is used, or if the encryption key is not shared at all).
37/// An example of standalone usage is outlined below:
38///
39/// ```
40/// # use elastic_elgamal::{
41/// # group::Ristretto, CandidateDecryption, VerifiableDecryption, Keypair, DiscreteLogTable,
42/// # };
43/// # use merlin::Transcript;
44/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
45/// let mut rng = rand::rng();
46/// let keys = Keypair::<Ristretto>::generate(&mut rng);
47/// // Suppose the `keys` holder wants to prove decryption
48/// // of the following ciphertext:
49/// let ciphertext = keys.public().encrypt(42_u64, &mut rng);
50/// let (decryption, proof) = VerifiableDecryption::new(
51/// ciphertext,
52/// &keys,
53/// &mut Transcript::new(b"decryption"),
54/// &mut rng,
55/// );
56///
57/// // This proof can then be universally verified:
58/// let candidate_decryption = CandidateDecryption::from(decryption);
59/// let decryption = candidate_decryption.verify(
60/// ciphertext,
61/// keys.public(),
62/// &proof,
63/// &mut Transcript::new(b"decryption"),
64/// )?;
65/// assert_eq!(
66/// decryption.decrypt(ciphertext, &DiscreteLogTable::new(0..50)),
67/// Some(42)
68/// );
69/// # Ok(())
70/// # }
71/// ```
72#[derive(Debug, Clone, Copy)]
73#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
74#[cfg_attr(feature = "serde", serde(bound = ""))]
75pub struct VerifiableDecryption<G: Group> {
76 #[cfg_attr(feature = "serde", serde(with = "ElementHelper::<G>"))]
77 dh_element: G::Element,
78}
79
80impl<G: Group> VerifiableDecryption<G> {
81 pub(crate) fn from_element(dh_element: G::Element) -> Self {
82 Self { dh_element }
83 }
84
85 /// Creates a decryption for the specified `ciphertext` under `keys` together with
86 /// a zero-knowledge proof of validity.
87 ///
88 /// See [`CandidateDecryption::verify()`] for the verification counterpart.
89 pub fn new<R: CryptoRng + RngCore>(
90 ciphertext: Ciphertext<G>,
91 keys: &Keypair<G>,
92 transcript: &mut Transcript,
93 rng: &mut R,
94 ) -> (Self, LogEqualityProof<G>) {
95 // All inputs except from `ciphertext.blinded_element` are committed in the `proof`,
96 // and it is not necessary to commit in order to allow iteratively recomputing
97 // the ciphertext.
98 transcript.start_proof(b"decryption_with_custom_key");
99
100 let dh_element = ciphertext.random_element * keys.secret().expose_scalar();
101 let proof = LogEqualityProof::new(
102 &PublicKey::from_element(ciphertext.random_element),
103 keys.secret(),
104 (keys.public().as_element(), dh_element),
105 transcript,
106 rng,
107 );
108
109 (Self { dh_element }, proof)
110 }
111
112 /// Returns the group element encapsulated in this decryption.
113 pub fn as_element(&self) -> &G::Element {
114 &self.dh_element
115 }
116
117 /// Serializes this decryption into bytes.
118 pub fn to_bytes(self) -> Vec<u8> {
119 let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
120 G::serialize_element(&self.dh_element, &mut bytes);
121 bytes
122 }
123
124 /// Decrypts the provided ciphertext and returns the produced group element.
125 ///
126 /// As the ciphertext does not include a MAC or another way to assert integrity,
127 /// this operation cannot fail. If the ciphertext is not produced properly (e.g., it targets
128 /// another receiver), the returned group element will be garbage.
129 pub fn decrypt_to_element(&self, encrypted: Ciphertext<G>) -> G::Element {
130 encrypted.blinded_element - self.dh_element
131 }
132
133 /// Decrypts the provided ciphertext and returns the original encrypted value.
134 ///
135 /// `lookup_table` is used to find encrypted values based on the original decrypted
136 /// group element. That is, it must contain all valid plaintext values. If the value
137 /// is not in the table, this method will return `None`.
138 pub fn decrypt(
139 &self,
140 encrypted: Ciphertext<G>,
141 lookup_table: &DiscreteLogTable<G>,
142 ) -> Option<u64> {
143 lookup_table.get(&self.decrypt_to_element(encrypted))
144 }
145}
146
147/// Candidate for a [`VerifiableDecryption`] that is not yet verified. This presentation should be
148/// used for decryption data retrieved from an untrusted source.
149///
150/// Decryption data can be verified using [`Self::verify()`]. The threshold encryption scheme
151/// implemented in the [`sharing`](crate::sharing) module has its own verification procedure
152/// in [`PublicKeySet`].
153///
154/// [`PublicKeySet`]: crate::sharing::PublicKeySet
155///
156/// # Examples
157///
158/// See [`VerifiableDecryption`] for an example of usage.
159#[derive(Debug, Clone, Copy)]
160#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
161#[cfg_attr(feature = "serde", serde(transparent, bound = ""))]
162pub struct CandidateDecryption<G: Group> {
163 inner: VerifiableDecryption<G>,
164}
165
166impl<G: Group> CandidateDecryption<G> {
167 /// Deserializes decryption data from `bytes`. Returns `None` if the data is malformed.
168 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
169 if bytes.len() == G::ELEMENT_SIZE {
170 let dh_element = G::deserialize_element(bytes)?;
171 Some(Self {
172 inner: VerifiableDecryption { dh_element },
173 })
174 } else {
175 None
176 }
177 }
178
179 pub(super) fn dh_element(self) -> G::Element {
180 self.inner.dh_element
181 }
182
183 /// Verifies this as decryption for `ciphertext` under `key` using the provided
184 /// zero-knowledge `proof`.
185 ///
186 /// # Errors
187 ///
188 /// Returns an error if `proof` does not verify.
189 pub fn verify(
190 self,
191 ciphertext: Ciphertext<G>,
192 key: &PublicKey<G>,
193 proof: &LogEqualityProof<G>,
194 transcript: &mut Transcript,
195 ) -> Result<VerifiableDecryption<G>, VerificationError> {
196 transcript.start_proof(b"decryption_with_custom_key");
197
198 let dh_element = self.dh_element();
199 proof.verify(
200 &PublicKey::from_element(ciphertext.random_element),
201 (key.as_element(), dh_element),
202 transcript,
203 )?;
204 Ok(self.inner)
205 }
206
207 /// Converts this candidate decryption into a [`VerifiableDecryption`]
208 /// **without** verifying it.
209 /// This is only semantically correct if the data was verified in some other way.
210 pub fn into_unchecked(self) -> VerifiableDecryption<G> {
211 self.inner
212 }
213}
214
215impl<G: Group> From<VerifiableDecryption<G>> for CandidateDecryption<G> {
216 fn from(decryption: VerifiableDecryption<G>) -> Self {
217 Self { inner: decryption }
218 }
219}