umbral_pre/
capsule_frag.rs

1use alloc::boxed::Box;
2use core::fmt;
3
4use rand_core::{CryptoRng, RngCore};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9use crate::capsule::Capsule;
10use crate::curve::{CurvePoint, CurveScalar, NonZeroCurveScalar};
11use crate::hashing_ds::{hash_to_cfrag_verification, kfrag_signature_message};
12use crate::key_frag::{KeyFrag, KeyFragID};
13use crate::keys::{PublicKey, Signature};
14use crate::secret_box::SecretBox;
15use crate::traits::fmt_public;
16
17#[cfg(feature = "default-serialization")]
18use crate::{DefaultDeserialize, DefaultSerialize};
19
20#[derive(Clone, Debug, PartialEq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub(crate) struct CapsuleFragProof {
23    pub(crate) point_e2: CurvePoint,
24    pub(crate) point_v2: CurvePoint,
25    pub(crate) kfrag_commitment: CurvePoint,
26    pub(crate) kfrag_pok: CurvePoint,
27    pub(crate) signature: CurveScalar,
28    pub(crate) kfrag_signature: Signature,
29}
30
31impl CapsuleFragProof {
32    #[allow(clippy::many_single_char_names)]
33    fn from_kfrag_and_cfrag(
34        rng: &mut (impl CryptoRng + RngCore),
35        capsule: &Capsule,
36        kfrag: KeyFrag,
37        cfrag_e1: &CurvePoint,
38        cfrag_v1: &CurvePoint,
39    ) -> Self {
40        let params = capsule.params;
41
42        let rk = kfrag.key;
43        let t = SecretBox::new(NonZeroCurveScalar::random(rng));
44
45        // Here are the formulaic constituents shared with `CapsuleFrag::verify()`.
46
47        let e = capsule.point_e;
48        let v = capsule.point_v;
49
50        let e1 = cfrag_e1;
51        let v1 = cfrag_v1;
52
53        let u = params.u;
54        let u1 = kfrag.proof.commitment;
55
56        let e2 = &e * t.as_secret();
57        let v2 = &v * t.as_secret();
58        let u2 = &u * t.as_secret();
59
60        let h = hash_to_cfrag_verification(&e, e1, &e2, &v, v1, &v2, &u, &u1, &u2);
61
62        ////////
63
64        let z3 = &(&rk * &h) + t.as_secret();
65
66        Self {
67            point_e2: e2,
68            point_v2: v2,
69            kfrag_commitment: u1,
70            kfrag_pok: u2,
71            signature: z3,
72            kfrag_signature: kfrag.proof.signature_for_receiver,
73        }
74    }
75}
76
77/// A reencrypted fragment of a [`Capsule`] created by a proxy.
78#[derive(Clone, Debug, PartialEq)]
79#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
80pub struct CapsuleFrag {
81    pub(crate) point_e1: CurvePoint,
82    pub(crate) point_v1: CurvePoint,
83    pub(crate) kfrag_id: KeyFragID,
84    pub(crate) precursor: CurvePoint,
85    pub(crate) proof: CapsuleFragProof,
86}
87
88impl fmt::Display for CapsuleFrag {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        fmt_public("CapsuleFrag", &self.kfrag_id, f)
91    }
92}
93
94/// Possible errors that can be returned by [`CapsuleFrag::verify`].
95#[derive(Debug, Copy, Clone, PartialEq, Eq)]
96pub enum CapsuleFragVerificationError {
97    /// Inconsistent internal state leading to signature verification failure.
98    IncorrectKeyFragSignature,
99    /// Inconsistent internal state leading to commitment verification failure.
100    IncorrectReencryption,
101}
102
103impl fmt::Display for CapsuleFragVerificationError {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        match self {
106            Self::IncorrectKeyFragSignature => write!(f, "Invalid CapsuleFrag signature"),
107            Self::IncorrectReencryption => write!(f, "Failed to verify reencryption proof"),
108        }
109    }
110}
111
112impl CapsuleFrag {
113    fn reencrypted(
114        rng: &mut (impl CryptoRng + RngCore),
115        capsule: &Capsule,
116        kfrag: KeyFrag,
117    ) -> Self {
118        let rk = kfrag.key;
119        let e1 = &capsule.point_e * &rk;
120        let v1 = &capsule.point_v * &rk;
121        let id = kfrag.id;
122        let precursor = kfrag.precursor;
123        let proof = CapsuleFragProof::from_kfrag_and_cfrag(rng, capsule, kfrag, &e1, &v1);
124
125        Self {
126            point_e1: e1,
127            point_v1: v1,
128            kfrag_id: id,
129            precursor,
130            proof,
131        }
132    }
133
134    /// Serializes the capsule frag by concatenating the byte represenation of its constituents:
135    /// - `e1` (compressed curve point, 33 bytes),
136    /// - `v1` (compressed curve point, 33 bytes),
137    /// - `kfrag_id` (32 bytes),
138    /// - `precursor` (compressed curve point, 33 bytes),
139    /// - `e2` (compressed curve point, 33 bytes),
140    /// - `v2` (compressed curve point, 33 bytes),
141    /// - `u1`, the kfrag PoK commitment (compressed curve point, 33 bytes),
142    /// - `u2`, the kfrag PoK (compressed curve point, 33 bytes),
143    /// - `signature` (big-endian scalar, 32 bytes),
144    /// - `kfrag_signature` (ECDSA signature serialized as `r` and `s`,
145    ///    each a 32 byte big-endian scalar).
146    pub fn to_bytes_simple(&self) -> Box<[u8]> {
147        let e1 = self.point_e1.to_compressed_array();
148        let v1 = self.point_v1.to_compressed_array();
149        let precursor = self.precursor.to_compressed_array();
150
151        let e2 = self.proof.point_e2.to_compressed_array();
152        let v2 = self.proof.point_v2.to_compressed_array();
153        let commitment = self.proof.kfrag_commitment.to_compressed_array();
154        let pok = self.proof.kfrag_pok.to_compressed_array();
155        let sig = self.proof.signature.to_array();
156        let kfrag_sig = self.proof.kfrag_signature.to_be_bytes();
157
158        let v: &[&[u8]] = &[
159            &e1,
160            &v1,
161            self.kfrag_id.as_ref(),
162            &precursor,
163            &e2,
164            &v2,
165            &commitment,
166            &pok,
167            &sig,
168            &kfrag_sig,
169        ];
170        v.concat().into()
171    }
172
173    /// Verifies the integrity of the capsule fragment, given the original capsule,
174    /// the encrypting party's key, the decrypting party's key, and the signing key.
175    #[allow(clippy::many_single_char_names)]
176    #[allow(clippy::result_large_err)]
177    pub fn verify(
178        self,
179        capsule: &Capsule,
180        verifying_pk: &PublicKey,
181        delegating_pk: &PublicKey,
182        receiving_pk: &PublicKey,
183    ) -> Result<VerifiedCapsuleFrag, (CapsuleFragVerificationError, Self)> {
184        let params = capsule.params;
185
186        // Here are the formulaic constituents shared with
187        // `CapsuleFragProof::from_kfrag_and_cfrag`.
188
189        let e = capsule.point_e;
190        let v = capsule.point_v;
191
192        let e1 = self.point_e1;
193        let v1 = self.point_v1;
194
195        let u = params.u;
196        let u1 = self.proof.kfrag_commitment;
197
198        let e2 = self.proof.point_e2;
199        let v2 = self.proof.point_v2;
200        let u2 = self.proof.kfrag_pok;
201
202        let h = hash_to_cfrag_verification(&e, &e1, &e2, &v, &v1, &v2, &u, &u1, &u2);
203
204        ///////
205
206        let precursor = self.precursor;
207        let kfrag_id = self.kfrag_id;
208
209        if !self.proof.kfrag_signature.verify(
210            verifying_pk,
211            kfrag_signature_message(
212                &kfrag_id,
213                &u1,
214                &precursor,
215                Some(delegating_pk),
216                Some(receiving_pk),
217            )
218            .as_ref(),
219        ) {
220            return Err((
221                CapsuleFragVerificationError::IncorrectKeyFragSignature,
222                self,
223            ));
224        }
225
226        // TODO (#46): if one or more of the values here are incorrect,
227        // we'll get the wrong `h` (since they're all hashed into it),
228        // so perhaps it's enough to check only one of these equations.
229        let z = self.proof.signature;
230        let correct_reencryption_of_e = &e * &z == &e2 + &(&e1 * &h);
231        let correct_reencryption_of_v = &v * &z == &v2 + &(&v1 * &h);
232        let correct_rk_commitment = &u * &z == &u2 + &(&u1 * &h);
233
234        if !(correct_reencryption_of_e & correct_reencryption_of_v & correct_rk_commitment) {
235            return Err((CapsuleFragVerificationError::IncorrectReencryption, self));
236        }
237
238        Ok(VerifiedCapsuleFrag { cfrag: self })
239    }
240
241    /// Explicitly skips [`CapsuleFrag::verify`] call.
242    /// Useful in cases when the verifying keys are impossible to obtain independently,
243    /// or when this capsule frag came from a trusted storage.
244    ///
245    /// **Warning:** make sure you considered the implications of not enforcing verification.
246    pub fn skip_verification(self) -> VerifiedCapsuleFrag {
247        VerifiedCapsuleFrag { cfrag: self }
248    }
249}
250
251#[cfg(feature = "default-serialization")]
252impl DefaultSerialize for CapsuleFrag {}
253
254#[cfg(feature = "default-serialization")]
255impl<'de> DefaultDeserialize<'de> for CapsuleFrag {}
256
257/// Verified capsule fragment, good for dencryption.
258/// Can be serialized, but cannot be deserialized directly.
259/// It can only be obtained from [`CapsuleFrag::verify`] or [`CapsuleFrag::skip_verification`].
260#[derive(Debug, Clone, PartialEq)]
261#[cfg_attr(feature = "serde", derive(Serialize))]
262#[cfg_attr(feature = "serde", serde(transparent))]
263pub struct VerifiedCapsuleFrag {
264    cfrag: CapsuleFrag,
265}
266
267impl fmt::Display for VerifiedCapsuleFrag {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        fmt_public("VerifiedCapsuleFrag", &self.cfrag.kfrag_id, f)
270    }
271}
272
273impl VerifiedCapsuleFrag {
274    pub(crate) fn reencrypted(
275        rng: &mut (impl CryptoRng + RngCore),
276        capsule: &Capsule,
277        kfrag: KeyFrag,
278    ) -> Self {
279        VerifiedCapsuleFrag {
280            cfrag: CapsuleFrag::reencrypted(rng, capsule, kfrag),
281        }
282    }
283
284    /// Clears the verification status from the capsule frag.
285    /// Useful for the cases where it needs to be put in the protocol structure
286    /// containing [`CapsuleFrag`] types (since those are the ones
287    /// that can be serialized/deserialized freely).
288    pub fn unverify(self) -> CapsuleFrag {
289        self.cfrag
290    }
291
292    /// Returns the same thing as [`CapsuleFrag::to_bytes_simple`].
293    pub fn to_bytes_simple(&self) -> Box<[u8]> {
294        self.cfrag.to_bytes_simple()
295    }
296}
297
298#[cfg(feature = "default-serialization")]
299impl DefaultSerialize for VerifiedCapsuleFrag {}
300
301#[cfg(test)]
302mod tests {
303
304    use alloc::boxed::Box;
305    use alloc::vec::Vec;
306
307    use super::VerifiedCapsuleFrag;
308
309    use crate::{encrypt, generate_kfrags, reencrypt, Capsule, PublicKey, SecretKey, Signer};
310
311    #[cfg(feature = "serde")]
312    use crate::serde_bytes::tests::check_serialization_roundtrip;
313
314    fn prepare_cfrags() -> (
315        PublicKey,
316        PublicKey,
317        PublicKey,
318        Capsule,
319        Box<[VerifiedCapsuleFrag]>,
320    ) {
321        let delegating_sk = SecretKey::random();
322        let delegating_pk = delegating_sk.public_key();
323
324        let signer = Signer::new(SecretKey::random());
325        let verifying_pk = signer.verifying_key();
326
327        let receiving_sk = SecretKey::random();
328        let receiving_pk = receiving_sk.public_key();
329
330        let plaintext = b"peace at dawn";
331        let (capsule, _ciphertext) = encrypt(&delegating_pk, plaintext).unwrap();
332
333        let kfrags = generate_kfrags(&delegating_sk, &receiving_pk, &signer, 2, 3, true, true);
334
335        let verified_cfrags: Vec<_> = kfrags
336            .iter()
337            .map(|kfrag| reencrypt(&capsule, kfrag.clone()))
338            .collect();
339
340        (
341            delegating_pk,
342            receiving_pk,
343            verifying_pk,
344            capsule,
345            verified_cfrags.into_boxed_slice(),
346        )
347    }
348
349    #[test]
350    fn test_verify() {
351        let (delegating_pk, receiving_pk, verifying_pk, capsule, verified_cfrags) =
352            prepare_cfrags();
353
354        for verified_cfrag in verified_cfrags.iter() {
355            let cfrag = verified_cfrag.clone().unverify();
356            let verified_cfrag_back = cfrag
357                .verify(&capsule, &verifying_pk, &delegating_pk, &receiving_pk)
358                .unwrap();
359
360            assert_eq!(&verified_cfrag_back, verified_cfrag);
361        }
362    }
363
364    #[cfg(feature = "serde")]
365    #[test]
366    fn test_serde_serialization() {
367        let (_delegating_pk, _receiving_pk, _verifying_pk, _capsule, verified_cfrags) =
368            prepare_cfrags();
369
370        let cfrag = verified_cfrags[0].clone().unverify();
371
372        // Check that the cfrag serializes to the same thing as the verified cfrag
373        let cfrag_bytes = rmp_serde::to_vec(&cfrag).unwrap();
374        let vcfrag_bytes = rmp_serde::to_vec(&verified_cfrags[0]).unwrap();
375        assert_eq!(vcfrag_bytes, cfrag_bytes);
376
377        check_serialization_roundtrip(&cfrag);
378    }
379}