use getrandom::SysRng;
use p256::elliptic_curve::sec1::{FromSec1Point, ToSec1Point};
use p256::{AffinePoint, ProjectivePoint, Scalar};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ciphertext {
pub c1_hex: String,
pub c2_hex: String,
}
#[derive(Debug, Clone)]
pub struct ReEncryptionKey {
pub rk: Scalar,
}
pub fn generate_rk(alice_sk: &Scalar, _bob_pk: &AffinePoint) -> ReEncryptionKey {
ReEncryptionKey { rk: *alice_sk }
}
pub fn encrypt_point(pk: &AffinePoint, message: &AffinePoint) -> Ciphertext {
use p256::elliptic_curve::Field;
use p256::elliptic_curve::rand_core::UnwrapErr;
let r = Scalar::random(&mut UnwrapErr(SysRng));
let c1 = (ProjectivePoint::GENERATOR * r).to_affine();
let c2 = (ProjectivePoint::from(*pk) * r + ProjectivePoint::from(*message)).to_affine();
Ciphertext {
c1_hex: hex::encode(c1.to_sec1_point(true).as_bytes()),
c2_hex: hex::encode(c2.to_sec1_point(true).as_bytes()),
}
}
pub fn decrypt_point(sk: &Scalar, ct: &Ciphertext) -> Option<AffinePoint> {
let c1 = decode_point(&ct.c1_hex)?;
let c2 = decode_point(&ct.c2_hex)?;
let sk_c1 = ProjectivePoint::from(c1) * sk;
let m = ProjectivePoint::from(c2) - sk_c1;
Some(m.to_affine())
}
pub fn re_encrypt(rk: &ReEncryptionKey, ct: &Ciphertext) -> Ciphertext {
let c1 = decode_point(&ct.c1_hex).unwrap();
let new_c1 = (ProjectivePoint::from(c1) * rk.rk).to_affine();
Ciphertext {
c1_hex: hex::encode(new_c1.to_sec1_point(true).as_bytes()),
c2_hex: ct.c2_hex.clone(),
}
}
fn decode_point(hex_str: &str) -> Option<AffinePoint> {
let bytes = hex::decode(hex_str).ok()?;
let encoded =
p256::elliptic_curve::sec1::Sec1Point::<p256::NistP256>::from_bytes(&bytes).ok()?;
Option::<AffinePoint>::from(AffinePoint::from_sec1_point(&encoded))
}
#[cfg(test)]
mod tests {
use super::*;
use p256::elliptic_curve::Field;
use p256::elliptic_curve::rand_core::UnwrapErr;
fn random_keypair() -> (Scalar, AffinePoint) {
let sk = Scalar::random(&mut UnwrapErr(SysRng));
let pk = (ProjectivePoint::GENERATOR * sk).to_affine();
(sk, pk)
}
#[test]
fn encrypt_decrypt_round_trips() {
let (sk, pk) = random_keypair();
let msg = (ProjectivePoint::GENERATOR * Scalar::from(42u32)).to_affine();
let ct = encrypt_point(&pk, &msg);
let recovered = decrypt_point(&sk, &ct).unwrap();
assert_eq!(recovered, msg);
}
#[test]
fn wrong_key_fails() {
let (_sk1, pk1) = random_keypair();
let (_, _pk2) = random_keypair();
let msg = (ProjectivePoint::GENERATOR * Scalar::from(42u32)).to_affine();
let ct = encrypt_point(&pk1, &msg);
let (sk2, _) = random_keypair();
let recovered = decrypt_point(&sk2, &ct).unwrap();
assert_ne!(recovered, msg);
}
#[test]
fn ciphertext_differs_per_encryption() {
let (_, pk) = random_keypair();
let msg = (ProjectivePoint::GENERATOR * Scalar::from(99u32)).to_affine();
let ct1 = encrypt_point(&pk, &msg);
let ct2 = encrypt_point(&pk, &msg);
assert_ne!(ct1.c1_hex, ct2.c1_hex);
}
#[test]
fn re_encrypt_preserves_format() {
let (sk, pk) = random_keypair();
let msg = (ProjectivePoint::GENERATOR * Scalar::from(7u32)).to_affine();
let ct = encrypt_point(&pk, &msg);
let rk = generate_rk(&sk, &pk);
let re_ct = re_encrypt(&rk, &ct);
assert!(!re_ct.c1_hex.is_empty());
assert!(!re_ct.c2_hex.is_empty());
}
#[test]
fn rk_carries_secret() {
let (sk, _) = random_keypair();
let rk = generate_rk(&sk, &(ProjectivePoint::GENERATOR).to_affine());
assert_eq!(rk.rk, sk);
}
}