use crate::{
core::{
expressions::{expr::Expr, other_expr::OtherExpr},
global_value::{curve_value::CurveValue, value::FieldValue},
mxe_input::{MxeInput, MxeScalarInput},
},
traits::{Invert, Reveal},
utils::{
field::ScalarField,
zkp::pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
},
MxeCurveInput,
};
use std::{
ops::{Add, Mul, Sub},
sync::LazyLock,
};
use zk_elgamal_proof::encryption::pedersen::H;
pub struct ElGamal;
impl ElGamal {
fn keygen() -> ElGamalKeypair {
let s = FieldValue::<ScalarField>::random();
Self::keygen_with_scalar(&s)
}
fn keygen_with_scalar(s: &FieldValue<ScalarField>) -> ElGamalKeypair {
let secret = ElGamalSecretKey(*s);
let public = ElGamalPubkey::new(&secret);
ElGamalKeypair { public, secret }
}
fn encrypt(public: &ElGamalPubkey, amount: FieldValue<ScalarField>) -> ElGamalCiphertext {
let (commitment, opening) = Pedersen::new(amount);
let handle = public.decrypt_handle(&opening);
ElGamalCiphertext { commitment, handle }
}
fn encrypt_with(
amount: FieldValue<ScalarField>,
public: &ElGamalPubkey,
opening: &PedersenOpening,
) -> ElGamalCiphertext {
let commitment = Pedersen::with(amount, opening);
let handle = public.decrypt_handle(opening);
ElGamalCiphertext { commitment, handle }
}
pub fn encode(amount: FieldValue<ScalarField>) -> ElGamalCiphertext {
let commitment = Pedersen::encode(amount);
let handle = DecryptHandle(CurveValue::identity());
ElGamalCiphertext { commitment, handle }
}
fn decrypt(secret: &ElGamalSecretKey, ciphertext: &ElGamalCiphertext) -> CurveValue {
*ciphertext.commitment.get_point() - secret.0 * ciphertext.handle.0
}
}
#[derive(Clone, Copy)]
pub struct ElGamalKeypair {
public: ElGamalPubkey,
secret: ElGamalSecretKey,
}
impl ElGamalKeypair {
pub fn new(secret: ElGamalSecretKey) -> Self {
let public = ElGamalPubkey::new(&secret);
Self { public, secret }
}
pub fn new_rand() -> Self {
ElGamal::keygen()
}
pub fn new_from_inner(public: ElGamalPubkey, secret: ElGamalSecretKey) -> Self {
Self { public, secret }
}
pub fn mxe_keypair() -> Self {
Self {
public: ElGamalPubkey(CurveValue::from_expr(Expr::Other(OtherExpr::MxeKey(
MxeInput::Curve(MxeCurveInput::ElGamalPubkey()),
)))),
secret: ElGamalSecretKey(FieldValue::<ScalarField>::from_expr(Expr::Other(
OtherExpr::MxeKey(MxeInput::ScalarOnly(MxeScalarInput::ElGamalSecretKey())),
))),
}
}
pub fn pubkey(&self) -> &ElGamalPubkey {
&self.public
}
pub fn secret(&self) -> &ElGamalSecretKey {
&self.secret
}
}
#[derive(Clone, Copy, Default)]
pub struct ElGamalPubkey(CurveValue);
impl ElGamalPubkey {
pub fn new(secret: &ElGamalSecretKey) -> Self {
let s = &secret.0;
ElGamalPubkey((s.invert(true) * CurveValue::from(*LazyLock::force(&H))).reveal())
}
pub fn new_from_inner(public: CurveValue) -> Self {
assert!(public.is_plaintext());
Self(public)
}
pub fn get_point(&self) -> &CurveValue {
&self.0
}
pub fn encrypt(&self, amount: FieldValue<ScalarField>) -> ElGamalCiphertext {
ElGamal::encrypt(self, amount)
}
pub fn encrypt_with(
&self,
amount: FieldValue<ScalarField>,
opening: &PedersenOpening,
) -> ElGamalCiphertext {
ElGamal::encrypt_with(amount, self, opening)
}
pub fn decrypt_handle(self, opening: &PedersenOpening) -> DecryptHandle {
DecryptHandle::new(&self, opening)
}
}
#[derive(Clone, Copy)]
pub struct ElGamalSecretKey(FieldValue<ScalarField>);
impl ElGamalSecretKey {
pub fn new(secret: FieldValue<ScalarField>) -> Self {
assert!(!secret.is_plaintext());
Self(secret)
}
pub fn new_rand() -> Self {
ElGamalSecretKey(FieldValue::<ScalarField>::random())
}
pub fn get_scalar(&self) -> &FieldValue<ScalarField> {
&self.0
}
pub fn decrypt(&self, ciphertext: &ElGamalCiphertext) -> CurveValue {
ElGamal::decrypt(self, ciphertext)
}
}
#[derive(Clone, Copy)]
pub struct ElGamalCiphertext {
pub commitment: PedersenCommitment,
pub handle: DecryptHandle,
}
impl ElGamalCiphertext {
pub fn decrypt(&self, secret: &ElGamalSecretKey) -> CurveValue {
ElGamal::decrypt(secret, self)
}
}
impl Add for ElGamalCiphertext {
type Output = ElGamalCiphertext;
fn add(self, ciphertext: ElGamalCiphertext) -> Self::Output {
ElGamalCiphertext {
commitment: self.commitment + ciphertext.commitment,
handle: self.handle + ciphertext.handle,
}
}
}
impl Sub for ElGamalCiphertext {
type Output = ElGamalCiphertext;
fn sub(self, ciphertext: ElGamalCiphertext) -> Self::Output {
ElGamalCiphertext {
commitment: self.commitment - ciphertext.commitment,
handle: self.handle - ciphertext.handle,
}
}
}
impl Mul<ElGamalCiphertext> for FieldValue<ScalarField> {
type Output = ElGamalCiphertext;
fn mul(self, rhs: ElGamalCiphertext) -> Self::Output {
ElGamalCiphertext {
commitment: self * rhs.commitment,
handle: self * rhs.handle,
}
}
}
#[derive(Clone, Copy)]
pub struct DecryptHandle(CurveValue);
impl DecryptHandle {
pub fn new(public: &ElGamalPubkey, opening: &PedersenOpening) -> Self {
Self((*opening.get_scalar() * public.0).reveal())
}
pub fn new_from_inner(handle: CurveValue) -> Self {
assert!(handle.is_plaintext());
Self(handle)
}
pub fn get_point(&self) -> &CurveValue {
&self.0
}
}
impl Add for DecryptHandle {
type Output = DecryptHandle;
fn add(self, handle: DecryptHandle) -> Self::Output {
DecryptHandle(self.0 + handle.0)
}
}
impl Sub for DecryptHandle {
type Output = DecryptHandle;
fn sub(self, handle: DecryptHandle) -> Self::Output {
DecryptHandle(self.0 - handle.0)
}
}
impl Mul<DecryptHandle> for FieldValue<ScalarField> {
type Output = DecryptHandle;
fn mul(self, rhs: DecryptHandle) -> Self::Output {
DecryptHandle(self * rhs.0)
}
}