arnaucube
arnaucube/plonky2-crypto-gadgetsPublic Self-hosted repository
plonky2-crypto-gadgets/ecgfp5/src/elgamal/circuit.rs
ecgfp5/src/elgamal/circuit.rsRaw
  1use plonky2::{
  2    field::extension::Extendable, hash::hash_types::RichField, iop::witness::PartialWitness,
  3    plonk::circuit_builder::CircuitBuilder,
  4};
  5use pod2::backends::plonky2::{
  6    basetypes::{D, F},
  7    primitives::ec::{
  8        bits::{BigUInt320Target, CircuitBuilderBits},
  9        curve::{CircuitBuilderElliptic, Point, PointTarget, WitnessWriteCurve},
 10    },
 11};
 12
 13use crate::elgamal::ECGFP5SecretKey;
 14
 15pub struct ECGFP5SecretKeyTarget(pub BigUInt320Target);
 16
 17pub type PublicKeyTarget = PointTarget;
 18
 19pub trait CircuitBuilderECGFP5PublicKey<F: RichField + Extendable<D>, const D: usize> {
 20    /// Adds secret key target
 21    fn add_secret_key(&mut self) -> ECGFP5SecretKeyTarget;
 22
 23    /// Derives public key from secret key
 24    fn public_key(&mut self, sk: &ECGFP5SecretKeyTarget) -> PublicKeyTarget;
 25}
 26
 27pub trait CircuitBuilderElGamal<F: RichField + Extendable<D>, const D: usize> {
 28    /// Encrypts an EcGFp5 point using a public key and nonce.
 29    fn elgamal_encrypt(
 30        &mut self,
 31        pk: &PublicKeyTarget,
 32        nonce: &BigUInt320Target,
 33        msg: &PointTarget,
 34    ) -> (PointTarget, PointTarget);
 35}
 36
 37impl CircuitBuilderECGFP5PublicKey<F, D> for CircuitBuilder<F, D> {
 38    fn add_secret_key(&mut self) -> ECGFP5SecretKeyTarget {
 39        ECGFP5SecretKeyTarget(self.add_virtual_biguint320_target())
 40    }
 41
 42    fn public_key(&mut self, sk: &ECGFP5SecretKeyTarget) -> PublicKeyTarget {
 43        let generator = self.constant_point(Point::generator());
 44        self.multiply_point(&sk.0.bits, &generator)
 45    }
 46}
 47
 48impl CircuitBuilderElGamal<F, D> for CircuitBuilder<F, D> {
 49    fn elgamal_encrypt(
 50        &mut self,
 51        pk: &PublicKeyTarget,
 52        nonce: &BigUInt320Target,
 53        msg: &PointTarget,
 54    ) -> (PointTarget, PointTarget) {
 55        let generator = self.constant_point(Point::generator());
 56        let nonce_times_gen = self.multiply_point(&nonce.bits, &generator);
 57        let nonce_times_pk = self.multiply_point(&nonce.bits, pk);
 58        (nonce_times_gen, self.add_point(msg, &nonce_times_pk))
 59    }
 60}
 61
 62pub trait PartialWitnessECGFP5PublicKey {
 63    fn set_secret_key_target(
 64        &mut self,
 65        target: &ECGFP5SecretKeyTarget,
 66        value: &ECGFP5SecretKey,
 67    ) -> anyhow::Result<()>;
 68}
 69
 70impl PartialWitnessECGFP5PublicKey for PartialWitness<F> {
 71    fn set_secret_key_target(
 72        &mut self,
 73        target: &ECGFP5SecretKeyTarget,
 74        value: &ECGFP5SecretKey,
 75    ) -> anyhow::Result<()> {
 76        self.set_biguint320_target(&target.0, &value.0)
 77    }
 78}
 79
 80#[cfg(test)]
 81mod tests {
 82    use num::BigUint;
 83    use num_bigint::RandBigInt;
 84    use plonky2::{
 85        iop::witness::PartialWitness,
 86        plonk::{
 87            circuit_builder::CircuitBuilder, circuit_data::CircuitConfig,
 88            config::PoseidonGoldilocksConfig,
 89        },
 90    };
 91    use pod2::backends::plonky2::{
 92        basetypes::{D, F},
 93        primitives::ec::{
 94            bits::CircuitBuilderBits,
 95            curve::{CircuitBuilderElliptic, GROUP_ORDER, Point, WitnessWriteCurve},
 96        },
 97    };
 98    use rand::rngs::OsRng;
 99
100    use crate::elgamal::{
101        ECGFP5SecretKey, TOTAL_BITS,
102        circuit::{
103            CircuitBuilderECGFP5PublicKey, CircuitBuilderElGamal, PartialWitnessECGFP5PublicKey,
104        },
105        elgamal_decrypt, elgamal_encrypt,
106    };
107
108    #[test]
109    fn public_key_calculation() -> anyhow::Result<()> {
110        let config = CircuitConfig::standard_recursion_config();
111        let mut builder = CircuitBuilder::<F, D>::new(config);
112
113        let sk_target = builder.add_secret_key();
114        let pk_target = builder.public_key(&sk_target);
115
116        let data = builder.build::<PoseidonGoldilocksConfig>();
117        (0..10).try_for_each(|_| {
118            let sk = ECGFP5SecretKey::rand();
119            let pk = sk.public_key();
120
121            let mut pw = PartialWitness::new();
122            pw.set_secret_key_target(&sk_target, &sk)?;
123            pw.set_point_target(&pk_target, &pk)?;
124
125            let proof = data.prove(pw)?;
126            data.verify(proof)
127        })
128    }
129
130    #[test]
131    fn elgamal_encryption() -> anyhow::Result<()> {
132        let config = CircuitConfig::standard_recursion_config();
133        let mut builder = CircuitBuilder::<F, D>::new(config);
134
135        let pk_target = builder.add_virtual_point_target();
136        let nonce_target = builder.add_virtual_biguint320_target();
137        let msg_target = builder.add_virtual_point_target();
138
139        let ct_target = builder.elgamal_encrypt(&pk_target, &nonce_target, &msg_target);
140
141        let data = builder.build::<PoseidonGoldilocksConfig>();
142
143        (0..10).try_for_each(|_| {
144            let sk = ECGFP5SecretKey::rand();
145            let pk = sk.public_key();
146
147            let msg = Point::new_rand_from_subgroup();
148            let nonce = OsRng.gen_biguint_below(&GROUP_ORDER);
149
150            let encrypted_msg = elgamal_encrypt(pk, &nonce, msg);
151
152            let mut pw = PartialWitness::new();
153            pw.set_point_target(&pk_target, &pk)?;
154            pw.set_point_target(&msg_target, &msg)?;
155            pw.set_biguint320_target(&nonce_target, &nonce)?;
156            pw.set_point_target(&ct_target.0, &encrypted_msg.0)?;
157            pw.set_point_target(&ct_target.1, &encrypted_msg.1)?;
158
159            let proof = data.prove(pw)?;
160            data.verify(proof)
161        })
162    }
163}