elastic_elgamal/
decryption.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
//! Verifiable decryption.

use merlin::Transcript;
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "serde")]
use crate::serde::ElementHelper;
use crate::{
    alloc::{vec, Vec},
    group::Group,
    proofs::{LogEqualityProof, TranscriptForGroup},
    Ciphertext, DiscreteLogTable, Keypair, PublicKey, VerificationError,
};

/// Verifiable decryption for a certain [`Ciphertext`] in the ElGamal encryption scheme.
/// Usable both for standalone proofs and in threshold encryption.
///
/// # Construction
///
/// Decryption is represented by a single group element – the result of combining
/// a [`SecretKey`](crate::SecretKey) scalar `x` with the random element of the ciphertext `R`
/// (i.e., `D = [x]R`, the Diffie – Hellman construction).
/// This element can retrieved using [`Self::as_element()`] and applied to a ciphertext using
/// [`Self::decrypt()`] or [`Self::decrypt_to_element()`].
///
/// The decryption can be proven with the help of a standard [`LogEqualityProof`]. Indeed,
/// to prove the validity of decryption, it is sufficient to prove `dlog_R(D) = dlog_G(K)`,
/// where `G` is the conventional group generator and `K = [x]G` is the public key for encryption.
///
/// # Examples
///
/// `VerifiableDecryption` can be used either within the threshold encryption scheme provided by
/// the [`sharing`](crate::sharing) module, or independently (for example, if another approach
/// to secret sharing is used, or if the encryption key is not shared at all).
/// An example of standalone usage is outlined below:
///
/// ```
/// # use elastic_elgamal::{
/// #     group::Ristretto, CandidateDecryption, VerifiableDecryption, Keypair, DiscreteLogTable,
/// # };
/// # use merlin::Transcript;
/// # use rand::thread_rng;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut rng = thread_rng();
/// let keys = Keypair::<Ristretto>::generate(&mut rng);
/// // Suppose the `keys` holder wants to prove decryption
/// // of the following ciphertext:
/// let ciphertext = keys.public().encrypt(42_u64, &mut rng);
/// let (decryption, proof) = VerifiableDecryption::new(
///     ciphertext,
///     &keys,
///     &mut Transcript::new(b"decryption"),
///     &mut rng,
/// );
///
/// // This proof can then be universally verified:
/// let candidate_decryption = CandidateDecryption::from(decryption);
/// let decryption = candidate_decryption.verify(
///     ciphertext,
///     keys.public(),
///     &proof,
///     &mut Transcript::new(b"decryption"),
/// )?;
/// assert_eq!(
///     decryption.decrypt(ciphertext, &DiscreteLogTable::new(0..50)),
///     Some(42)
/// );
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = ""))]
pub struct VerifiableDecryption<G: Group> {
    #[cfg_attr(feature = "serde", serde(with = "ElementHelper::<G>"))]
    dh_element: G::Element,
}

impl<G: Group> VerifiableDecryption<G> {
    pub(crate) fn from_element(dh_element: G::Element) -> Self {
        Self { dh_element }
    }

    /// Creates a decryption for the specified `ciphertext` under `keys` together with
    /// a zero-knowledge proof of validity.
    ///
    /// See [`CandidateDecryption::verify()`] for the verification counterpart.
    pub fn new<R: CryptoRng + RngCore>(
        ciphertext: Ciphertext<G>,
        keys: &Keypair<G>,
        transcript: &mut Transcript,
        rng: &mut R,
    ) -> (Self, LogEqualityProof<G>) {
        // All inputs except from `ciphertext.blinded_element` are committed in the `proof`,
        // and it is not necessary to commit in order to allow iteratively recomputing
        // the ciphertext.
        transcript.start_proof(b"decryption_with_custom_key");

        let dh_element = ciphertext.random_element * keys.secret().expose_scalar();
        let proof = LogEqualityProof::new(
            &PublicKey::from_element(ciphertext.random_element),
            keys.secret(),
            (keys.public().as_element(), dh_element),
            transcript,
            rng,
        );

        (Self { dh_element }, proof)
    }

    /// Returns the group element encapsulated in this decryption.
    pub fn as_element(&self) -> &G::Element {
        &self.dh_element
    }

    /// Serializes this decryption into bytes.
    pub fn to_bytes(self) -> Vec<u8> {
        let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
        G::serialize_element(&self.dh_element, &mut bytes);
        bytes
    }

    /// Decrypts the provided ciphertext and returns the produced group element.
    ///
    /// As the ciphertext does not include a MAC or another way to assert integrity,
    /// this operation cannot fail. If the ciphertext is not produced properly (e.g., it targets
    /// another receiver), the returned group element will be garbage.
    pub fn decrypt_to_element(&self, encrypted: Ciphertext<G>) -> G::Element {
        encrypted.blinded_element - self.dh_element
    }

    /// Decrypts the provided ciphertext and returns the original encrypted value.
    ///
    /// `lookup_table` is used to find encrypted values based on the original decrypted
    /// group element. That is, it must contain all valid plaintext values. If the value
    /// is not in the table, this method will return `None`.
    pub fn decrypt(
        &self,
        encrypted: Ciphertext<G>,
        lookup_table: &DiscreteLogTable<G>,
    ) -> Option<u64> {
        lookup_table.get(&self.decrypt_to_element(encrypted))
    }
}

/// Candidate for a [`VerifiableDecryption`] that is not yet verified. This presentation should be
/// used for decryption data retrieved from an untrusted source.
///
/// Decryption data can be verified using [`Self::verify()`]. The threshold encryption scheme
/// implemented in the [`sharing`](crate::sharing) module has its own verification procedure
/// in [`PublicKeySet`].
///
/// [`PublicKeySet`]: crate::sharing::PublicKeySet
///
/// # Examples
///
/// See [`VerifiableDecryption`] for an example of usage.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent, bound = ""))]
pub struct CandidateDecryption<G: Group> {
    inner: VerifiableDecryption<G>,
}

impl<G: Group> CandidateDecryption<G> {
    /// Deserializes decryption data from `bytes`. Returns `None` if the data is malformed.
    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
        if bytes.len() == G::ELEMENT_SIZE {
            let dh_element = G::deserialize_element(bytes)?;
            Some(Self {
                inner: VerifiableDecryption { dh_element },
            })
        } else {
            None
        }
    }

    pub(super) fn dh_element(self) -> G::Element {
        self.inner.dh_element
    }

    /// Verifies this as decryption for `ciphertext` under `key` using the provided
    /// zero-knowledge `proof`.
    ///
    /// # Errors
    ///
    /// Returns an error if `proof` does not verify.
    pub fn verify(
        self,
        ciphertext: Ciphertext<G>,
        key: &PublicKey<G>,
        proof: &LogEqualityProof<G>,
        transcript: &mut Transcript,
    ) -> Result<VerifiableDecryption<G>, VerificationError> {
        transcript.start_proof(b"decryption_with_custom_key");

        let dh_element = self.dh_element();
        proof.verify(
            &PublicKey::from_element(ciphertext.random_element),
            (key.as_element(), dh_element),
            transcript,
        )?;
        Ok(self.inner)
    }

    /// Converts this candidate decryption into a [`VerifiableDecryption`]
    /// **without** verifying it.
    /// This is only semantically correct if the data was verified in some other way.
    pub fn into_unchecked(self) -> VerifiableDecryption<G> {
        self.inner
    }
}

impl<G: Group> From<VerifiableDecryption<G>> for CandidateDecryption<G> {
    fn from(decryption: VerifiableDecryption<G>) -> Self {
        Self { inner: decryption }
    }
}