Skip to content

Commit 7ec0005

Browse files
Add a basic ElGamal encryption gadget (#44)
* Basic elgamal encryption gadget * fmt Co-authored-by: Weikeng Chen <[email protected]>
1 parent 41ed850 commit 7ec0005

File tree

5 files changed

+491
-0
lines changed

5 files changed

+491
-0
lines changed

src/encryption/constraints.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use crate::encryption::AsymmetricEncryptionScheme;
2+
3+
use ark_r1cs_std::prelude::*;
4+
use ark_relations::r1cs::SynthesisError;
5+
use core::fmt::Debug;
6+
7+
use ark_ff::fields::Field;
8+
9+
pub trait AsymmetricEncryptionGadget<C: AsymmetricEncryptionScheme, ConstraintF: Field> {
10+
type OutputVar: AllocVar<C::Ciphertext, ConstraintF>
11+
+ EqGadget<ConstraintF>
12+
+ Clone
13+
+ Sized
14+
+ Debug;
15+
type ParametersVar: AllocVar<C::Parameters, ConstraintF> + Clone;
16+
type PlaintextVar: AllocVar<C::Plaintext, ConstraintF> + Clone;
17+
type PublicKeyVar: AllocVar<C::PublicKey, ConstraintF> + Clone;
18+
type RandomnessVar: AllocVar<C::Randomness, ConstraintF> + Clone;
19+
20+
fn encrypt(
21+
parameters: &Self::ParametersVar,
22+
message: &Self::PlaintextVar,
23+
randomness: &Self::RandomnessVar,
24+
public_key: &Self::PublicKeyVar,
25+
) -> Result<Self::OutputVar, SynthesisError>;
26+
}
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
use ark_r1cs_std::prelude::*;
2+
use ark_relations::r1cs::{Namespace, SynthesisError};
3+
4+
use crate::encryption::elgamal::{
5+
Ciphertext, ElGamal, Parameters, Plaintext, PublicKey, Randomness,
6+
};
7+
use crate::encryption::AsymmetricEncryptionGadget;
8+
use ark_ec::ProjectiveCurve;
9+
use ark_ff::{
10+
fields::{Field, PrimeField},
11+
to_bytes, Zero,
12+
};
13+
use core::{borrow::Borrow, marker::PhantomData};
14+
15+
pub type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
16+
17+
#[derive(Clone, Debug)]
18+
pub struct RandomnessVar<F: Field>(Vec<UInt8<F>>);
19+
20+
impl<C, F> AllocVar<Randomness<C>, F> for RandomnessVar<F>
21+
where
22+
C: ProjectiveCurve,
23+
F: PrimeField,
24+
{
25+
fn new_variable<T: Borrow<Randomness<C>>>(
26+
cs: impl Into<Namespace<F>>,
27+
f: impl FnOnce() -> Result<T, SynthesisError>,
28+
mode: AllocationMode,
29+
) -> Result<Self, SynthesisError> {
30+
let r = to_bytes![&f().map(|b| b.borrow().0).unwrap_or(C::ScalarField::zero())].unwrap();
31+
match mode {
32+
AllocationMode::Constant => Ok(Self(UInt8::constant_vec(&r))),
33+
AllocationMode::Input => UInt8::new_input_vec(cs, &r).map(Self),
34+
AllocationMode::Witness => UInt8::new_witness_vec(cs, &r).map(Self),
35+
}
36+
}
37+
}
38+
39+
#[derive(Derivative)]
40+
#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
41+
pub struct ParametersVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
42+
where
43+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
44+
{
45+
generator: GG,
46+
#[doc(hidden)]
47+
_curve: PhantomData<C>,
48+
}
49+
50+
impl<C, GG> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GG>
51+
where
52+
C: ProjectiveCurve,
53+
GG: CurveVar<C, ConstraintF<C>>,
54+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
55+
{
56+
fn new_variable<T: Borrow<Parameters<C>>>(
57+
cs: impl Into<Namespace<ConstraintF<C>>>,
58+
f: impl FnOnce() -> Result<T, SynthesisError>,
59+
mode: AllocationMode,
60+
) -> Result<Self, SynthesisError> {
61+
let generator = GG::new_variable(cs, || f().map(|g| g.borrow().generator), mode)?;
62+
Ok(Self {
63+
generator,
64+
_curve: PhantomData,
65+
})
66+
}
67+
}
68+
69+
#[derive(Derivative)]
70+
#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
71+
pub struct PlaintextVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
72+
where
73+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
74+
{
75+
pub plaintext: GG,
76+
#[doc(hidden)]
77+
_curve: PhantomData<C>,
78+
}
79+
80+
impl<C, GG> AllocVar<Plaintext<C>, ConstraintF<C>> for PlaintextVar<C, GG>
81+
where
82+
C: ProjectiveCurve,
83+
GG: CurveVar<C, ConstraintF<C>>,
84+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
85+
{
86+
fn new_variable<T: Borrow<Plaintext<C>>>(
87+
cs: impl Into<Namespace<ConstraintF<C>>>,
88+
f: impl FnOnce() -> Result<T, SynthesisError>,
89+
mode: AllocationMode,
90+
) -> Result<Self, SynthesisError> {
91+
let plaintext = GG::new_variable(cs, f, mode)?;
92+
Ok(Self {
93+
plaintext,
94+
_curve: PhantomData,
95+
})
96+
}
97+
}
98+
99+
#[derive(Derivative)]
100+
#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
101+
pub struct PublicKeyVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
102+
where
103+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
104+
{
105+
pub pk: GG,
106+
#[doc(hidden)]
107+
_curve: PhantomData<C>,
108+
}
109+
110+
impl<C, GG> AllocVar<PublicKey<C>, ConstraintF<C>> for PublicKeyVar<C, GG>
111+
where
112+
C: ProjectiveCurve,
113+
GG: CurveVar<C, ConstraintF<C>>,
114+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
115+
{
116+
fn new_variable<T: Borrow<PublicKey<C>>>(
117+
cs: impl Into<Namespace<ConstraintF<C>>>,
118+
f: impl FnOnce() -> Result<T, SynthesisError>,
119+
mode: AllocationMode,
120+
) -> Result<Self, SynthesisError> {
121+
let pk = GG::new_variable(cs, f, mode)?;
122+
Ok(Self {
123+
pk,
124+
_curve: PhantomData,
125+
})
126+
}
127+
}
128+
129+
#[derive(Derivative, Debug)]
130+
#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
131+
pub struct OutputVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
132+
where
133+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
134+
{
135+
pub c1: GG,
136+
pub c2: GG,
137+
#[doc(hidden)]
138+
_curve: PhantomData<C>,
139+
}
140+
141+
impl<C, GG> AllocVar<Ciphertext<C>, ConstraintF<C>> for OutputVar<C, GG>
142+
where
143+
C: ProjectiveCurve,
144+
GG: CurveVar<C, ConstraintF<C>>,
145+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
146+
{
147+
fn new_variable<T: Borrow<Ciphertext<C>>>(
148+
cs: impl Into<Namespace<ConstraintF<C>>>,
149+
f: impl FnOnce() -> Result<T, SynthesisError>,
150+
mode: AllocationMode,
151+
) -> Result<Self, SynthesisError> {
152+
let ns = cs.into();
153+
let cs = ns.cs();
154+
let prep = f().map(|g| *g.borrow());
155+
let c1 = GG::new_variable(cs.clone(), || prep.map(|g| g.borrow().0), mode)?;
156+
let c2 = GG::new_variable(cs.clone(), || prep.map(|g| g.borrow().1), mode)?;
157+
Ok(Self {
158+
c1,
159+
c2,
160+
_curve: PhantomData,
161+
})
162+
}
163+
}
164+
165+
impl<C, GC> EqGadget<ConstraintF<C>> for OutputVar<C, GC>
166+
where
167+
C: ProjectiveCurve,
168+
GC: CurveVar<C, ConstraintF<C>>,
169+
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
170+
{
171+
#[inline]
172+
fn is_eq(&self, other: &Self) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
173+
self.c1.is_eq(&other.c1)?.and(&self.c2.is_eq(&other.c2)?)
174+
}
175+
}
176+
177+
pub struct ElGamalEncGadget<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
178+
where
179+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
180+
{
181+
#[doc(hidden)]
182+
_curve: PhantomData<*const C>,
183+
_group_var: PhantomData<*const GG>,
184+
}
185+
186+
impl<C, GG> AsymmetricEncryptionGadget<ElGamal<C>, ConstraintF<C>> for ElGamalEncGadget<C, GG>
187+
where
188+
C: ProjectiveCurve,
189+
GG: CurveVar<C, ConstraintF<C>>,
190+
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
191+
ConstraintF<C>: PrimeField,
192+
{
193+
type OutputVar = OutputVar<C, GG>;
194+
type ParametersVar = ParametersVar<C, GG>;
195+
type PlaintextVar = PlaintextVar<C, GG>;
196+
type PublicKeyVar = PublicKeyVar<C, GG>;
197+
type RandomnessVar = RandomnessVar<ConstraintF<C>>;
198+
199+
fn encrypt(
200+
parameters: &Self::ParametersVar,
201+
message: &Self::PlaintextVar,
202+
randomness: &Self::RandomnessVar,
203+
public_key: &Self::PublicKeyVar,
204+
) -> Result<Self::OutputVar, SynthesisError> {
205+
// flatten randomness to little-endian bit vector
206+
let randomness = randomness
207+
.0
208+
.iter()
209+
.flat_map(|b| b.to_bits_le().unwrap())
210+
.collect::<Vec<_>>();
211+
212+
// compute s = randomness*pk
213+
let s = public_key.pk.clone().scalar_mul_le(randomness.iter())?;
214+
215+
// compute c1 = randomness*generator
216+
let c1 = parameters
217+
.generator
218+
.clone()
219+
.scalar_mul_le(randomness.iter())?;
220+
221+
// compute c2 = m + s
222+
let c2 = message.plaintext.clone() + s;
223+
224+
Ok(Self::OutputVar {
225+
c1,
226+
c2,
227+
_curve: PhantomData,
228+
})
229+
}
230+
}

src/encryption/elgamal/mod.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#[cfg(feature = "r1cs")]
2+
pub mod constraints;
3+
4+
use crate::encryption::AsymmetricEncryptionScheme;
5+
use crate::Error;
6+
use ark_ec::{AffineCurve, ProjectiveCurve};
7+
use ark_ff::{fields::PrimeField, UniformRand};
8+
use ark_std::marker::PhantomData;
9+
use ark_std::rand::Rng;
10+
11+
pub struct ElGamal<C: ProjectiveCurve> {
12+
_group: PhantomData<C>,
13+
}
14+
15+
pub struct Parameters<C: ProjectiveCurve> {
16+
pub generator: C::Affine,
17+
}
18+
19+
pub type PublicKey<C> = <C as ProjectiveCurve>::Affine;
20+
21+
pub struct SecretKey<C: ProjectiveCurve>(pub C::ScalarField);
22+
23+
pub struct Randomness<C: ProjectiveCurve>(pub C::ScalarField);
24+
25+
impl<C: ProjectiveCurve> UniformRand for Randomness<C> {
26+
#[inline]
27+
fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self {
28+
Randomness(<C as ProjectiveCurve>::ScalarField::rand(rng))
29+
}
30+
}
31+
32+
pub type Plaintext<C> = <C as ProjectiveCurve>::Affine;
33+
34+
pub type Ciphertext<C> = (
35+
<C as ProjectiveCurve>::Affine,
36+
<C as ProjectiveCurve>::Affine,
37+
);
38+
39+
impl<C: ProjectiveCurve> AsymmetricEncryptionScheme for ElGamal<C>
40+
where
41+
C::ScalarField: PrimeField,
42+
{
43+
type Parameters = Parameters<C>;
44+
type PublicKey = PublicKey<C>;
45+
type SecretKey = SecretKey<C>;
46+
type Randomness = Randomness<C>;
47+
type Plaintext = Plaintext<C>;
48+
type Ciphertext = Ciphertext<C>;
49+
50+
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
51+
// get a random generator
52+
let generator = C::rand(rng).into();
53+
54+
Ok(Parameters { generator })
55+
}
56+
57+
fn keygen<R: Rng>(
58+
pp: &Self::Parameters,
59+
rng: &mut R,
60+
) -> Result<(Self::PublicKey, Self::SecretKey), Error> {
61+
// get a random element from the scalar field
62+
let secret_key: <C as ProjectiveCurve>::ScalarField = C::ScalarField::rand(rng);
63+
64+
// compute secret_key*generator to derive the public key
65+
let public_key = pp.generator.mul(secret_key.clone()).into();
66+
67+
Ok((public_key, SecretKey(secret_key)))
68+
}
69+
70+
fn encrypt(
71+
pp: &Self::Parameters,
72+
pk: &Self::PublicKey,
73+
message: &Self::Plaintext,
74+
r: &Self::Randomness,
75+
) -> Result<Self::Ciphertext, Error> {
76+
// compute s = r*pk
77+
let s = pk.mul(r.0.clone()).into();
78+
79+
// compute c1 = r*generator
80+
let c1 = pp.generator.mul(r.0.clone()).into();
81+
82+
// compute c2 = m + s
83+
let c2 = *message + s;
84+
85+
Ok((c1, c2))
86+
}
87+
88+
fn decrypt(
89+
_pp: &Self::Parameters,
90+
sk: &Self::SecretKey,
91+
ciphertext: &Self::Ciphertext,
92+
) -> Result<Self::Plaintext, Error> {
93+
let c1: <C as ProjectiveCurve>::Affine = ciphertext.0;
94+
let c2: <C as ProjectiveCurve>::Affine = ciphertext.1;
95+
96+
// compute s = secret_key * c1
97+
let s = c1.mul(sk.0.clone());
98+
let s_inv = -s;
99+
100+
// compute message = c2 - s
101+
let m = c2 + s_inv.into_affine();
102+
103+
Ok(m)
104+
}
105+
}

0 commit comments

Comments
 (0)