#[cfg(feature = "serde-support")]
use alloc::string::String;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt;
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "serde-support")]
use serde::{Deserialize, Serialize};
use crate::capsule_frag::CapsuleFrag;
use crate::curve::{CompressedPointSize, CurvePoint, CurveScalar, NonZeroCurveScalar};
use crate::hashing_ds::{hash_capsule_points, hash_to_polynomial_arg, hash_to_shared_secret};
use crate::keys::{PublicKey, SecretKey};
use crate::params::Parameters;
use crate::secret_box::SecretBox;
use crate::traits::fmt_public;
#[cfg(feature = "default-serialization")]
use crate::{DefaultDeserialize, DefaultSerialize};
#[derive(Debug, PartialEq, Eq)]
pub enum OpenReencryptedError {
NoCapsuleFrags,
MismatchedCapsuleFrags,
RepeatingCapsuleFrags,
ValidationFailed,
}
impl fmt::Display for OpenReencryptedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoCapsuleFrags => write!(f, "Empty CapsuleFrag sequence"),
Self::MismatchedCapsuleFrags => write!(f, "CapsuleFrags are not pairwise consistent"),
Self::RepeatingCapsuleFrags => write!(f, "Some of the CapsuleFrags are repeated"),
Self::ValidationFailed => write!(f, "Internal validation failed"),
}
}
}
#[cfg(feature = "serde-support")]
#[derive(Serialize, Deserialize)]
struct SerializedCapsule {
point_e: CurvePoint,
point_v: CurvePoint,
signature: CurveScalar,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde-support", serde(try_from = "SerializedCapsule"))]
#[cfg_attr(feature = "serde-support", serde(into = "SerializedCapsule"))]
pub struct Capsule {
pub(crate) params: Parameters,
pub(crate) point_e: CurvePoint,
pub(crate) point_v: CurvePoint,
pub(crate) signature: CurveScalar,
}
#[cfg(feature = "serde-support")]
impl TryFrom<SerializedCapsule> for Capsule {
type Error = String;
fn try_from(source: SerializedCapsule) -> Result<Self, Self::Error> {
Self::new_verified(source.point_e, source.point_v, source.signature)
.ok_or_else(|| "Capsule self-verification failed".into())
}
}
#[cfg(feature = "serde-support")]
impl From<Capsule> for SerializedCapsule {
fn from(source: Capsule) -> Self {
Self {
point_e: source.point_e,
point_v: source.point_v,
signature: source.signature,
}
}
}
#[cfg(feature = "default-serialization")]
impl DefaultSerialize for Capsule {}
#[cfg(feature = "default-serialization")]
impl<'de> DefaultDeserialize<'de> for Capsule {}
impl fmt::Display for Capsule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_public("Capsule", &self.signature.to_array(), f)
}
}
pub(crate) type KeySeed = GenericArray<u8, CompressedPointSize>;
impl Capsule {
fn new(point_e: CurvePoint, point_v: CurvePoint, signature: CurveScalar) -> Self {
let params = Parameters::new();
Self {
params,
point_e,
point_v,
signature,
}
}
#[cfg(feature = "serde-support")]
pub(crate) fn new_verified(
point_e: CurvePoint,
point_v: CurvePoint,
signature: CurveScalar,
) -> Option<Self> {
let capsule = Self::new(point_e, point_v, signature);
match capsule.verify() {
false => None,
true => Some(capsule),
}
}
pub fn to_bytes_simple(&self) -> Box<[u8]> {
let e = self.point_e.to_compressed_array();
let v = self.point_v.to_compressed_array();
let s = self.signature.to_array();
let v: &[&[u8]] = &[&e, &v, &s];
v.concat().into()
}
#[cfg(feature = "serde-support")]
fn verify(&self) -> bool {
let g = CurvePoint::generator();
let h = hash_capsule_points(&self.point_e, &self.point_v);
&g * &self.signature == &self.point_v + &(&self.point_e * &h)
}
pub(crate) fn from_public_key(
rng: &mut (impl CryptoRng + RngCore),
delegating_pk: &PublicKey,
) -> (Capsule, SecretBox<KeySeed>) {
let g = CurvePoint::generator();
let priv_r = SecretBox::new(NonZeroCurveScalar::random(rng));
let pub_r = &g * priv_r.as_secret();
let priv_u = SecretBox::new(NonZeroCurveScalar::random(rng));
let pub_u = &g * priv_u.as_secret();
let h = hash_capsule_points(&pub_r, &pub_u);
let s = priv_u.as_secret() + &(priv_r.as_secret() * &h);
let shared_key =
SecretBox::new(&delegating_pk.to_point() * &(priv_r.as_secret() + priv_u.as_secret()));
let capsule = Self::new(pub_r, pub_u, s);
(
capsule,
SecretBox::new(shared_key.as_secret().to_compressed_array()),
)
}
pub(crate) fn open_original(&self, delegating_sk: &SecretKey) -> SecretBox<KeySeed> {
let shared_key = SecretBox::new(
&(&self.point_e + &self.point_v) * delegating_sk.to_secret_scalar().as_secret(),
);
SecretBox::new(shared_key.as_secret().to_compressed_array())
}
#[allow(clippy::many_single_char_names)]
pub(crate) fn open_reencrypted(
&self,
receiving_sk: &SecretKey,
delegating_pk: &PublicKey,
cfrags: &[CapsuleFrag],
) -> Result<SecretBox<KeySeed>, OpenReencryptedError> {
if cfrags.is_empty() {
return Err(OpenReencryptedError::NoCapsuleFrags);
}
let precursor = cfrags[0].precursor;
if !cfrags.iter().all(|cfrag| cfrag.precursor == precursor) {
return Err(OpenReencryptedError::MismatchedCapsuleFrags);
}
let pub_key = receiving_sk.public_key().to_point();
let dh_point = &precursor * receiving_sk.to_secret_scalar().as_secret();
let mut lc = Vec::<NonZeroCurveScalar>::with_capacity(cfrags.len());
for cfrag in cfrags {
let coeff = hash_to_polynomial_arg(&precursor, &pub_key, &dh_point, &cfrag.kfrag_id);
lc.push(coeff);
}
let mut e_prime = CurvePoint::identity();
let mut v_prime = CurvePoint::identity();
for (i, cfrag) in cfrags.iter().enumerate() {
let lambda_i =
lambda_coeff(&lc, i).ok_or(OpenReencryptedError::RepeatingCapsuleFrags)?;
e_prime = &e_prime + &(&cfrag.point_e1 * &lambda_i);
v_prime = &v_prime + &(&cfrag.point_v1 * &lambda_i);
}
let d = hash_to_shared_secret(&precursor, &pub_key, &dh_point);
let s = self.signature;
let h = hash_capsule_points(&self.point_e, &self.point_v);
let orig_pub_key = delegating_pk.to_point();
let inv_d = d.invert();
if &orig_pub_key * &(&s * &inv_d) != &(&e_prime * &h) + &v_prime {
return Err(OpenReencryptedError::ValidationFailed);
}
let shared_key = SecretBox::new(&(&e_prime + &v_prime) * &d);
Ok(SecretBox::new(shared_key.as_secret().to_compressed_array()))
}
}
fn lambda_coeff(xs: &[NonZeroCurveScalar], i: usize) -> Option<CurveScalar> {
let mut res = CurveScalar::one();
for j in 0..xs.len() {
if j != i {
let inv_diff_opt: Option<CurveScalar> = (&xs[j] - &xs[i]).invert().into();
let inv_diff = inv_diff_opt?;
res = &(&res * &xs[j]) * &inv_diff;
}
}
Some(res)
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use rand_core::OsRng;
use super::{Capsule, OpenReencryptedError};
use crate::{generate_kfrags, reencrypt, SecretKey, Signer};
#[cfg(feature = "serde-support")]
use crate::serde_bytes::tests::check_serialization_roundtrip;
#[test]
fn test_open_reencrypted() {
let delegating_sk = SecretKey::random();
let delegating_pk = delegating_sk.public_key();
let signer = Signer::new(SecretKey::random());
let receiving_sk = SecretKey::random();
let receiving_pk = receiving_sk.public_key();
let (capsule, key_seed) = Capsule::from_public_key(&mut OsRng, &delegating_pk);
let kfrags = generate_kfrags(&delegating_sk, &receiving_pk, &signer, 2, 3, true, true);
let vcfrags: Vec<_> = kfrags
.iter()
.map(|kfrag| reencrypt(&capsule, kfrag.clone()))
.collect();
let cfrags = [vcfrags[0].clone().unverify(), vcfrags[1].clone().unverify()];
let key_seed_reenc = capsule
.open_reencrypted(&receiving_sk, &delegating_pk, &cfrags)
.unwrap();
assert_eq!(key_seed.as_secret(), key_seed_reenc.as_secret());
let result = capsule.open_reencrypted(&receiving_sk, &delegating_pk, &[]);
assert_eq!(
result.map(|x| *x.as_secret()),
Err(OpenReencryptedError::NoCapsuleFrags)
);
let kfrags2 = generate_kfrags(&delegating_sk, &receiving_pk, &signer, 2, 3, true, true);
let vcfrags2: Vec<_> = kfrags2
.iter()
.map(|kfrag| reencrypt(&capsule, kfrag.clone()))
.collect();
let mismatched_cfrags = [
vcfrags[0].clone().unverify(),
vcfrags2[1].clone().unverify(),
];
let result = capsule.open_reencrypted(&receiving_sk, &delegating_pk, &mismatched_cfrags);
assert_eq!(
result.map(|x| *x.as_secret()),
Err(OpenReencryptedError::MismatchedCapsuleFrags)
);
let (capsule2, _key_seed) = Capsule::from_public_key(&mut OsRng, &delegating_pk);
let result = capsule2.open_reencrypted(&receiving_sk, &delegating_pk, &cfrags);
assert_eq!(
result.map(|x| *x.as_secret()),
Err(OpenReencryptedError::ValidationFailed)
);
}
#[cfg(feature = "serde-support")]
#[test]
fn test_serde_serialization() {
let delegating_sk = SecretKey::random();
let delegating_pk = delegating_sk.public_key();
let (capsule, _key_seed) = Capsule::from_public_key(&mut OsRng, &delegating_pk);
check_serialization_roundtrip(&capsule);
}
}