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