use alloc::boxed::Box;
use core::fmt;
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "serde-support")]
use serde::{Deserialize, Serialize};
use crate::capsule::Capsule;
use crate::curve::{CurvePoint, CurveScalar, NonZeroCurveScalar};
use crate::hashing_ds::{hash_to_cfrag_verification, kfrag_signature_message};
use crate::key_frag::{KeyFrag, KeyFragID};
use crate::keys::{PublicKey, Signature};
use crate::secret_box::SecretBox;
use crate::traits::fmt_public;
#[cfg(feature = "default-serialization")]
use crate::{DefaultDeserialize, DefaultSerialize};
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
pub(crate) struct CapsuleFragProof {
point_e2: CurvePoint,
point_v2: CurvePoint,
kfrag_commitment: CurvePoint,
kfrag_pok: CurvePoint,
signature: CurveScalar,
kfrag_signature: Signature,
}
impl CapsuleFragProof {
#[allow(clippy::many_single_char_names)]
fn from_kfrag_and_cfrag(
rng: &mut (impl CryptoRng + RngCore),
capsule: &Capsule,
kfrag: KeyFrag,
cfrag_e1: &CurvePoint,
cfrag_v1: &CurvePoint,
) -> Self {
let params = capsule.params;
let rk = kfrag.key;
let t = SecretBox::new(NonZeroCurveScalar::random(rng));
let e = capsule.point_e;
let v = capsule.point_v;
let e1 = cfrag_e1;
let v1 = cfrag_v1;
let u = params.u;
let u1 = kfrag.proof.commitment;
let e2 = &e * t.as_secret();
let v2 = &v * t.as_secret();
let u2 = &u * t.as_secret();
let h = hash_to_cfrag_verification(&[e, *e1, e2, v, *v1, v2, u, u1, u2]);
let z3 = &(&rk * &h) + t.as_secret();
Self {
point_e2: e2,
point_v2: v2,
kfrag_commitment: u1,
kfrag_pok: u2,
signature: z3,
kfrag_signature: kfrag.proof.signature_for_receiver,
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
pub struct CapsuleFrag {
pub(crate) point_e1: CurvePoint,
pub(crate) point_v1: CurvePoint,
pub(crate) kfrag_id: KeyFragID,
pub(crate) precursor: CurvePoint,
pub(crate) proof: CapsuleFragProof,
}
impl fmt::Display for CapsuleFrag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_public("CapsuleFrag", &self.kfrag_id, f)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CapsuleFragVerificationError {
IncorrectKeyFragSignature,
IncorrectReencryption,
}
impl fmt::Display for CapsuleFragVerificationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IncorrectKeyFragSignature => write!(f, "Invalid CapsuleFrag signature"),
Self::IncorrectReencryption => write!(f, "Failed to verify reencryption proof"),
}
}
}
impl CapsuleFrag {
fn reencrypted(
rng: &mut (impl CryptoRng + RngCore),
capsule: &Capsule,
kfrag: KeyFrag,
) -> Self {
let rk = kfrag.key;
let e1 = &capsule.point_e * &rk;
let v1 = &capsule.point_v * &rk;
let id = kfrag.id;
let precursor = kfrag.precursor;
let proof = CapsuleFragProof::from_kfrag_and_cfrag(rng, capsule, kfrag, &e1, &v1);
Self {
point_e1: e1,
point_v1: v1,
kfrag_id: id,
precursor,
proof,
}
}
pub fn to_bytes_simple(&self) -> Box<[u8]> {
let e1 = self.point_e1.to_compressed_array();
let v1 = self.point_v1.to_compressed_array();
let precursor = self.precursor.to_compressed_array();
let e2 = self.proof.point_e2.to_compressed_array();
let v2 = self.proof.point_v2.to_compressed_array();
let commitment = self.proof.kfrag_commitment.to_compressed_array();
let pok = self.proof.kfrag_pok.to_compressed_array();
let sig = self.proof.signature.to_array();
let kfrag_sig = self.proof.kfrag_signature.to_be_bytes();
let v: &[&[u8]] = &[
&e1,
&v1,
self.kfrag_id.as_ref(),
&precursor,
&e2,
&v2,
&commitment,
&pok,
&sig,
&kfrag_sig,
];
v.concat().into()
}
#[allow(clippy::many_single_char_names)]
#[allow(clippy::result_large_err)]
pub fn verify(
self,
capsule: &Capsule,
verifying_pk: &PublicKey,
delegating_pk: &PublicKey,
receiving_pk: &PublicKey,
) -> Result<VerifiedCapsuleFrag, (CapsuleFragVerificationError, Self)> {
let params = capsule.params;
let e = capsule.point_e;
let v = capsule.point_v;
let e1 = self.point_e1;
let v1 = self.point_v1;
let u = params.u;
let u1 = self.proof.kfrag_commitment;
let e2 = self.proof.point_e2;
let v2 = self.proof.point_v2;
let u2 = self.proof.kfrag_pok;
let h = hash_to_cfrag_verification(&[e, e1, e2, v, v1, v2, u, u1, u2]);
let precursor = self.precursor;
let kfrag_id = self.kfrag_id;
if !self.proof.kfrag_signature.verify(
verifying_pk,
kfrag_signature_message(
&kfrag_id,
&u1,
&precursor,
Some(delegating_pk),
Some(receiving_pk),
)
.as_ref(),
) {
return Err((
CapsuleFragVerificationError::IncorrectKeyFragSignature,
self,
));
}
let z = self.proof.signature;
let correct_reencryption_of_e = &e * &z == &e2 + &(&e1 * &h);
let correct_reencryption_of_v = &v * &z == &v2 + &(&v1 * &h);
let correct_rk_commitment = &u * &z == &u2 + &(&u1 * &h);
if !(correct_reencryption_of_e & correct_reencryption_of_v & correct_rk_commitment) {
return Err((CapsuleFragVerificationError::IncorrectReencryption, self));
}
Ok(VerifiedCapsuleFrag { cfrag: self })
}
pub fn skip_verification(self) -> VerifiedCapsuleFrag {
VerifiedCapsuleFrag { cfrag: self }
}
}
#[cfg(feature = "default-serialization")]
impl DefaultSerialize for CapsuleFrag {}
#[cfg(feature = "default-serialization")]
impl<'de> DefaultDeserialize<'de> for CapsuleFrag {}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde-support", derive(Serialize))]
#[cfg_attr(feature = "serde-support", serde(transparent))]
pub struct VerifiedCapsuleFrag {
cfrag: CapsuleFrag,
}
impl fmt::Display for VerifiedCapsuleFrag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_public("VerifiedCapsuleFrag", &self.cfrag.kfrag_id, f)
}
}
impl VerifiedCapsuleFrag {
pub(crate) fn reencrypted(
rng: &mut (impl CryptoRng + RngCore),
capsule: &Capsule,
kfrag: KeyFrag,
) -> Self {
VerifiedCapsuleFrag {
cfrag: CapsuleFrag::reencrypted(rng, capsule, kfrag),
}
}
pub fn unverify(self) -> CapsuleFrag {
self.cfrag
}
}
#[cfg(feature = "default-serialization")]
impl DefaultSerialize for VerifiedCapsuleFrag {}
#[cfg(test)]
mod tests {
use alloc::boxed::Box;
use alloc::vec::Vec;
use super::VerifiedCapsuleFrag;
use crate::{encrypt, generate_kfrags, reencrypt, Capsule, PublicKey, SecretKey, Signer};
#[cfg(feature = "serde-support")]
use crate::serde_bytes::tests::check_serialization_roundtrip;
fn prepare_cfrags() -> (
PublicKey,
PublicKey,
PublicKey,
Capsule,
Box<[VerifiedCapsuleFrag]>,
) {
let delegating_sk = SecretKey::random();
let delegating_pk = delegating_sk.public_key();
let signer = Signer::new(SecretKey::random());
let verifying_pk = signer.verifying_key();
let receiving_sk = SecretKey::random();
let receiving_pk = receiving_sk.public_key();
let plaintext = b"peace at dawn";
let (capsule, _ciphertext) = encrypt(&delegating_pk, plaintext).unwrap();
let kfrags = generate_kfrags(&delegating_sk, &receiving_pk, &signer, 2, 3, true, true);
let verified_cfrags: Vec<_> = kfrags
.iter()
.map(|kfrag| reencrypt(&capsule, kfrag.clone()))
.collect();
(
delegating_pk,
receiving_pk,
verifying_pk,
capsule,
verified_cfrags.into_boxed_slice(),
)
}
#[test]
fn test_verify() {
let (delegating_pk, receiving_pk, verifying_pk, capsule, verified_cfrags) =
prepare_cfrags();
for verified_cfrag in verified_cfrags.iter() {
let cfrag = verified_cfrag.clone().unverify();
let verified_cfrag_back = cfrag
.verify(&capsule, &verifying_pk, &delegating_pk, &receiving_pk)
.unwrap();
assert_eq!(&verified_cfrag_back, verified_cfrag);
}
}
#[cfg(feature = "serde-support")]
#[test]
fn test_serde_serialization() {
let (_delegating_pk, _receiving_pk, _verifying_pk, _capsule, verified_cfrags) =
prepare_cfrags();
let cfrag = verified_cfrags[0].clone().unverify();
let cfrag_bytes = rmp_serde::to_vec(&cfrag).unwrap();
let vcfrag_bytes = rmp_serde::to_vec(&verified_cfrags[0]).unwrap();
assert_eq!(vcfrag_bytes, cfrag_bytes);
check_serialization_roundtrip(&cfrag);
}
}