use alloc::string::{String, ToString};
use sha2::digest::Digest;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::curve::CurvePoint;
use crate::hashing::BackendDigestOutput;
use crate::hashing_ds::{hash_to_cfrag_verification, kfrag_signature_message};
use crate::keys::digest_for_signing;
use crate::params::Parameters;
use crate::{Capsule, PublicKey, VerifiedCapsuleFrag};
#[cfg(docsrs)]
use crate::CapsuleFrag;
#[cfg(feature = "default-serialization")]
use crate::{DefaultDeserialize, DefaultSerialize};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ReencryptionEvidence {
pub e: CurvePoint,
pub ez: CurvePoint,
pub e1: CurvePoint,
pub e1h: CurvePoint,
pub e2: CurvePoint,
pub v: CurvePoint,
pub vz: CurvePoint,
pub v1: CurvePoint,
pub v1h: CurvePoint,
pub v2: CurvePoint,
pub uz: CurvePoint,
pub u1: CurvePoint,
pub u1h: CurvePoint,
pub u2: CurvePoint,
#[cfg_attr(feature = "serde", serde(with = "crate::serde_bytes::as_hex"))]
pub kfrag_validity_message_hash: BackendDigestOutput,
pub kfrag_signature_v: bool,
}
impl ReencryptionEvidence {
pub fn new(
capsule: &Capsule,
vcfrag: &VerifiedCapsuleFrag,
verifying_pk: &PublicKey,
delegating_pk: &PublicKey,
receiving_pk: &PublicKey,
) -> Result<Self, String> {
let params = Parameters::new();
let cfrag = vcfrag.clone().unverify();
let u = params.u;
let u1 = cfrag.proof.kfrag_commitment;
let u2 = cfrag.proof.kfrag_pok;
let h = hash_to_cfrag_verification(
&capsule.point_e,
&cfrag.point_e1,
&cfrag.proof.point_e2,
&capsule.point_v,
&cfrag.point_v1,
&cfrag.proof.point_v2,
¶ms.u,
&cfrag.proof.kfrag_commitment,
&cfrag.proof.kfrag_pok,
);
let e1h = &cfrag.point_e1 * &h;
let v1h = &cfrag.point_v1 * &h;
let u1h = &u1 * &h;
let z = cfrag.proof.signature;
let ez = &capsule.point_e * &z;
let vz = &capsule.point_v * &z;
let uz = &u * &z;
let kfrag_message = kfrag_signature_message(
&cfrag.kfrag_id,
&u1,
&cfrag.precursor,
Some(delegating_pk),
Some(receiving_pk),
);
let kfrag_message_hash = digest_for_signing(&kfrag_message).finalize();
let recovery_id = cfrag
.proof
.kfrag_signature
.get_recovery_id(verifying_pk, &kfrag_message)
.ok_or_else(|| {
"Could not find the recovery ID for the kfrag signature: mismatched verifying key?"
.to_string()
})?;
let kfrag_signature_v = recovery_id.is_y_odd();
Ok(Self {
e: capsule.point_e,
ez,
e1: cfrag.point_e1,
e1h,
e2: cfrag.proof.point_e2,
v: capsule.point_v,
vz,
v1: cfrag.point_v1,
v1h,
v2: cfrag.proof.point_v2,
uz,
u1,
u1h,
u2,
kfrag_validity_message_hash: kfrag_message_hash,
kfrag_signature_v,
})
}
}
#[cfg(feature = "default-serialization")]
impl DefaultSerialize for ReencryptionEvidence {}
#[cfg(feature = "default-serialization")]
impl<'de> DefaultDeserialize<'de> for ReencryptionEvidence {}
#[cfg(test)]
mod tests {
use super::ReencryptionEvidence;
use crate::{
curve::CurveScalar, encrypt, generate_kfrags, hash_to_cfrag_verification, reencrypt,
Parameters, PublicKey, RecoverableSignature, SecretKey, Signature, Signer,
};
fn assert_eq_byte_refs(x: &(impl AsRef<[u8]> + ?Sized), y: &(impl AsRef<[u8]> + ?Sized)) {
assert_eq!(x.as_ref(), y.as_ref());
}
#[test]
fn contract() {
let threshold: usize = 2;
let num_frags: usize = threshold + 1;
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 vkfrags = generate_kfrags(
&delegating_sk,
&receiving_pk,
&signer,
threshold,
num_frags,
true,
true,
);
let vcfrag = reencrypt(&capsule, vkfrags[0].clone());
let evidence = ReencryptionEvidence::new(
&capsule,
&vcfrag,
&verifying_pk,
&delegating_pk,
&receiving_pk,
)
.unwrap();
let capsule_bytes = capsule.to_bytes_simple();
let cfrag_bytes = vcfrag.to_bytes_simple();
assert_eq_byte_refs(&capsule_bytes[0..33], &evidence.e.to_compressed_array());
assert_eq_byte_refs(&capsule_bytes[33..66], &evidence.v.to_compressed_array());
assert_eq_byte_refs(&cfrag_bytes[0..33], &evidence.e1.to_compressed_array());
assert_eq_byte_refs(&cfrag_bytes[33..66], &evidence.v1.to_compressed_array());
assert_eq_byte_refs(&cfrag_bytes[131..164], &evidence.e2.to_compressed_array());
assert_eq_byte_refs(&cfrag_bytes[164..197], &evidence.v2.to_compressed_array());
assert_eq_byte_refs(&cfrag_bytes[197..230], &evidence.u1.to_compressed_array());
assert_eq_byte_refs(&cfrag_bytes[230..263], &evidence.u2.to_compressed_array());
let z = CurveScalar::try_from_bytes(&cfrag_bytes[263..(263 + 32)]).unwrap();
let sig = Signature::try_from_be_bytes(&cfrag_bytes[295..(295 + 64)]).unwrap();
let rsig = RecoverableSignature::from_normalized(sig, evidence.kfrag_signature_v);
let vkey =
PublicKey::recover_from_prehash(&evidence.kfrag_validity_message_hash, &rsig).unwrap();
assert_eq!(vkey, verifying_pk);
let params = Parameters::new();
let h = hash_to_cfrag_verification(
&evidence.e,
&evidence.e1,
&evidence.e2,
&evidence.v,
&evidence.v1,
&evidence.v2,
¶ms.u,
&evidence.u1,
&evidence.u2,
);
assert_eq!(&evidence.e * &z, &(&evidence.e1 * &h) + &evidence.e2);
assert_eq!(&evidence.v * &z, &(&evidence.v1 * &h) + &evidence.v2);
assert_eq!(¶ms.u * &z, &(&evidence.u1 * &h) + &evidence.u2);
}
}