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}