1#[cfg(feature = "serde")]
2use alloc::string::String;
3
4use alloc::boxed::Box;
5use alloc::vec::Vec;
6use core::fmt;
7
8use generic_array::GenericArray;
9use rand_core::{CryptoRng, RngCore};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use crate::capsule_frag::CapsuleFrag;
15use crate::curve::{CompressedPointSize, CurvePoint, CurveScalar, NonZeroCurveScalar};
16use crate::hashing_ds::{hash_capsule_points, hash_to_polynomial_arg, hash_to_shared_secret};
17use crate::keys::{PublicKey, SecretKey};
18use crate::params::Parameters;
19use crate::secret_box::SecretBox;
20use crate::traits::fmt_public;
21
22#[cfg(feature = "default-serialization")]
23use crate::{DefaultDeserialize, DefaultSerialize};
24
25#[derive(Debug, PartialEq, Eq)]
27pub enum OpenReencryptedError {
28 NoCapsuleFrags,
30 MismatchedCapsuleFrags,
33 RepeatingCapsuleFrags,
35 ValidationFailed,
39}
40
41impl fmt::Display for OpenReencryptedError {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 match self {
44 Self::NoCapsuleFrags => write!(f, "Empty CapsuleFrag sequence"),
45 Self::MismatchedCapsuleFrags => write!(f, "CapsuleFrags are not pairwise consistent"),
46 Self::RepeatingCapsuleFrags => write!(f, "Some of the CapsuleFrags are repeated"),
47 Self::ValidationFailed => write!(f, "Internal validation failed"),
48 }
49 }
50}
51
52#[cfg(feature = "serde")]
56#[derive(Serialize, Deserialize)]
57struct SerializedCapsule {
58 point_e: CurvePoint,
59 point_v: CurvePoint,
60 signature: CurveScalar,
61}
62
63#[derive(Clone, Debug, PartialEq)]
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
66#[cfg_attr(feature = "serde", serde(try_from = "SerializedCapsule"))]
67#[cfg_attr(feature = "serde", serde(into = "SerializedCapsule"))]
68pub struct Capsule {
69 pub(crate) params: Parameters,
70 pub(crate) point_e: CurvePoint,
71 pub(crate) point_v: CurvePoint,
72 pub(crate) signature: CurveScalar,
73}
74
75#[cfg(feature = "serde")]
76impl TryFrom<SerializedCapsule> for Capsule {
77 type Error = String;
78
79 fn try_from(source: SerializedCapsule) -> Result<Self, Self::Error> {
80 Self::new_verified(source.point_e, source.point_v, source.signature)
81 .ok_or_else(|| "Capsule self-verification failed".into())
82 }
83}
84
85#[cfg(feature = "serde")]
86impl From<Capsule> for SerializedCapsule {
87 fn from(source: Capsule) -> Self {
88 Self {
89 point_e: source.point_e,
90 point_v: source.point_v,
91 signature: source.signature,
92 }
93 }
94}
95
96#[cfg(feature = "default-serialization")]
97impl DefaultSerialize for Capsule {}
98
99#[cfg(feature = "default-serialization")]
100impl<'de> DefaultDeserialize<'de> for Capsule {}
101
102impl fmt::Display for Capsule {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 fmt_public("Capsule", &self.signature.to_array(), f)
105 }
106}
107
108pub(crate) type KeySeed = GenericArray<u8, CompressedPointSize>;
109
110impl Capsule {
111 fn new(point_e: CurvePoint, point_v: CurvePoint, signature: CurveScalar) -> Self {
112 let params = Parameters::new();
113 Self {
114 params,
115 point_e,
116 point_v,
117 signature,
118 }
119 }
120
121 #[cfg(feature = "serde")]
122 pub(crate) fn new_verified(
123 point_e: CurvePoint,
124 point_v: CurvePoint,
125 signature: CurveScalar,
126 ) -> Option<Self> {
127 let capsule = Self::new(point_e, point_v, signature);
128 match capsule.verify() {
129 false => None,
130 true => Some(capsule),
131 }
132 }
133
134 pub fn to_bytes_simple(&self) -> Box<[u8]> {
139 let e = self.point_e.to_compressed_array();
140 let v = self.point_v.to_compressed_array();
141 let s = self.signature.to_array();
142 let v: &[&[u8]] = &[&e, &v, &s];
143 v.concat().into()
144 }
145
146 #[cfg(feature = "serde")]
148 fn verify(&self) -> bool {
149 let g = CurvePoint::generator();
150 let h = hash_capsule_points(&self.point_e, &self.point_v);
151 &g * &self.signature == &self.point_v + &(&self.point_e * &h)
152 }
153
154 pub(crate) fn from_public_key(
156 rng: &mut (impl CryptoRng + RngCore),
157 delegating_pk: &PublicKey,
158 ) -> (Capsule, SecretBox<KeySeed>) {
159 let g = CurvePoint::generator();
160
161 let priv_r = SecretBox::new(NonZeroCurveScalar::random(rng));
162 let pub_r = &g * priv_r.as_secret();
163
164 let priv_u = SecretBox::new(NonZeroCurveScalar::random(rng));
165 let pub_u = &g * priv_u.as_secret();
166
167 let h = hash_capsule_points(&pub_r, &pub_u);
168
169 let s = priv_u.as_secret() + &(priv_r.as_secret() * &h);
170
171 let shared_key =
172 SecretBox::new(&delegating_pk.to_point() * &(priv_r.as_secret() + priv_u.as_secret()));
173
174 let capsule = Self::new(pub_r, pub_u, s);
175
176 (
177 capsule,
178 SecretBox::new(shared_key.as_secret().to_compressed_array()),
179 )
180 }
181
182 pub(crate) fn open_original(&self, delegating_sk: &SecretKey) -> SecretBox<KeySeed> {
184 let shared_key = SecretBox::new(
185 &(&self.point_e + &self.point_v) * delegating_sk.to_secret_scalar().as_secret(),
186 );
187 SecretBox::new(shared_key.as_secret().to_compressed_array())
188 }
189
190 #[allow(clippy::many_single_char_names)]
191 pub(crate) fn open_reencrypted(
192 &self,
193 receiving_sk: &SecretKey,
194 delegating_pk: &PublicKey,
195 cfrags: &[CapsuleFrag],
196 ) -> Result<SecretBox<KeySeed>, OpenReencryptedError> {
197 if cfrags.is_empty() {
198 return Err(OpenReencryptedError::NoCapsuleFrags);
199 }
200
201 let precursor = cfrags[0].precursor;
202
203 if !cfrags.iter().all(|cfrag| cfrag.precursor == precursor) {
204 return Err(OpenReencryptedError::MismatchedCapsuleFrags);
205 }
206
207 let pub_key = receiving_sk.public_key().to_point();
208 let dh_point = &precursor * receiving_sk.to_secret_scalar().as_secret();
209
210 let mut lc = Vec::<NonZeroCurveScalar>::with_capacity(cfrags.len());
212 for cfrag in cfrags {
213 let coeff = hash_to_polynomial_arg(&precursor, &pub_key, &dh_point, &cfrag.kfrag_id);
214 lc.push(coeff);
215 }
216
217 let mut e_prime = CurvePoint::identity();
218 let mut v_prime = CurvePoint::identity();
219 for (i, cfrag) in cfrags.iter().enumerate() {
220 let lambda_i =
223 lambda_coeff(&lc, i).ok_or(OpenReencryptedError::RepeatingCapsuleFrags)?;
224 e_prime = &e_prime + &(&cfrag.point_e1 * &lambda_i);
225 v_prime = &v_prime + &(&cfrag.point_v1 * &lambda_i);
226 }
227
228 let d = hash_to_shared_secret(&precursor, &pub_key, &dh_point);
230
231 let s = self.signature;
232 let h = hash_capsule_points(&self.point_e, &self.point_v);
233
234 let orig_pub_key = delegating_pk.to_point();
235
236 let inv_d = d.invert();
237
238 if &orig_pub_key * &(&s * &inv_d) != &(&e_prime * &h) + &v_prime {
239 return Err(OpenReencryptedError::ValidationFailed);
240 }
241
242 let shared_key = SecretBox::new(&(&e_prime + &v_prime) * &d);
243 Ok(SecretBox::new(shared_key.as_secret().to_compressed_array()))
244 }
245}
246
247fn lambda_coeff(xs: &[NonZeroCurveScalar], i: usize) -> Option<CurveScalar> {
248 let mut res = CurveScalar::one();
249 for j in 0..xs.len() {
250 if j != i {
251 let inv_diff_opt: Option<CurveScalar> = (&xs[j] - &xs[i]).invert().into();
252 let inv_diff = inv_diff_opt?;
253 res = &(&res * &xs[j]) * &inv_diff;
254 }
255 }
256 Some(res)
257}
258
259#[cfg(test)]
260mod tests {
261
262 use alloc::vec::Vec;
263
264 use rand_core::OsRng;
265
266 use super::{Capsule, OpenReencryptedError};
267
268 use crate::{generate_kfrags, reencrypt, SecretKey, Signer};
269
270 #[cfg(feature = "serde")]
271 use crate::serde_bytes::tests::check_serialization_roundtrip;
272
273 #[test]
274 fn test_open_reencrypted() {
275 let delegating_sk = SecretKey::random();
276 let delegating_pk = delegating_sk.public_key();
277
278 let signer = Signer::new(SecretKey::random());
279
280 let receiving_sk = SecretKey::random();
281 let receiving_pk = receiving_sk.public_key();
282
283 let (capsule, key_seed) = Capsule::from_public_key(&mut OsRng, &delegating_pk);
284
285 let kfrags = generate_kfrags(&delegating_sk, &receiving_pk, &signer, 2, 3, true, true);
286
287 let vcfrags: Vec<_> = kfrags
288 .iter()
289 .map(|kfrag| reencrypt(&capsule, kfrag.clone()))
290 .collect();
291
292 let cfrags = [vcfrags[0].clone().unverify(), vcfrags[1].clone().unverify()];
293
294 let key_seed_reenc = capsule
295 .open_reencrypted(&receiving_sk, &delegating_pk, &cfrags)
296 .unwrap();
297 assert_eq!(key_seed.as_secret(), key_seed_reenc.as_secret());
298
299 let result = capsule.open_reencrypted(&receiving_sk, &delegating_pk, &[]);
301 assert_eq!(
302 result.map(|x| *x.as_secret()),
303 Err(OpenReencryptedError::NoCapsuleFrags)
304 );
305
306 let kfrags2 = generate_kfrags(&delegating_sk, &receiving_pk, &signer, 2, 3, true, true);
308
309 let vcfrags2: Vec<_> = kfrags2
310 .iter()
311 .map(|kfrag| reencrypt(&capsule, kfrag.clone()))
312 .collect();
313
314 let mismatched_cfrags = [
315 vcfrags[0].clone().unverify(),
316 vcfrags2[1].clone().unverify(),
317 ];
318
319 let result = capsule.open_reencrypted(&receiving_sk, &delegating_pk, &mismatched_cfrags);
320 assert_eq!(
321 result.map(|x| *x.as_secret()),
322 Err(OpenReencryptedError::MismatchedCapsuleFrags)
323 );
324
325 let (capsule2, _key_seed) = Capsule::from_public_key(&mut OsRng, &delegating_pk);
327 let result = capsule2.open_reencrypted(&receiving_sk, &delegating_pk, &cfrags);
328 assert_eq!(
329 result.map(|x| *x.as_secret()),
330 Err(OpenReencryptedError::ValidationFailed)
331 );
332 }
333
334 #[cfg(feature = "serde")]
335 #[test]
336 fn test_serde_serialization() {
337 let delegating_sk = SecretKey::random();
338 let delegating_pk = delegating_sk.public_key();
339 let (capsule, _key_seed) = Capsule::from_public_key(&mut OsRng, &delegating_pk);
340
341 check_serialization_roundtrip(&capsule);
342 }
343}