Skip to content

Commit

Permalink
Merge pull request #209 from dusk-network/recipient-params-core
Browse files Browse the repository at this point in the history
Move elgamal encryption and `RecipientParameters` to `phoenix-core`
  • Loading branch information
Eduardo Leegwater Simões authored Jun 13, 2024
2 parents a8987f7 + 565e59d commit dec7e9e
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 170 deletions.
5 changes: 5 additions & 0 deletions circuits/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Rename `skeleton_hash` to `payload_hash` [#188]
- Make `TxCircuit` to use the Recipient gadget

### Removed

- Remove `RecipientParameters`
- Remove `elgamal::encrypt` and `elgamal::decrypt`

## [0.1.0] - 2024-05-22

### Added
Expand Down
31 changes: 1 addition & 30 deletions circuits/src/encryption/elgamal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,9 @@
//!
//! Reference: https://link.springer.com/chapter/10.1007/3-540-39568-7_2

use dusk_jubjub::{JubJubExtended, JubJubScalar, GENERATOR};
use dusk_jubjub::{JubJubScalar, GENERATOR};
use dusk_plonk::prelude::*;

/// Encrypts a JubJubExtended plaintext given a public key and a fresh random
/// number 'r'.
///
/// ## Return
/// Returns a ciphertext (JubJubExtended, JubJubExtended).
pub fn encrypt(
public_key: &JubJubExtended,
plaintext: &JubJubExtended,
r: &JubJubScalar,
) -> (JubJubExtended, JubJubExtended) {
let ciphertext_1 = GENERATOR * r;
let ciphertext_2 = plaintext + public_key * r;

(ciphertext_1, ciphertext_2)
}

/// Decrypts a ciphertext given a secret key.
///
/// ## Return
/// Returns a JubJubExtended plaintext.
pub fn decrypt(
secret_key: &JubJubScalar,
ciphertext_1: &JubJubExtended,
ciphertext_2: &JubJubExtended,
) -> JubJubExtended {
// return the plaintext
ciphertext_2 - ciphertext_1 * secret_key
}

/// Encrypt in-circuit a plaintext WitnessPoint.
///
/// ## Return
Expand Down
3 changes: 0 additions & 3 deletions circuits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,5 @@ mod recipient;
/// Transaction structs, and circuit
pub mod transaction;

/// Recipient Parameters
pub use recipient::RecipientParameters;

/// ElGamal asymmetric cipher
pub use encryption::elgamal;
116 changes: 2 additions & 114 deletions circuits/src/recipient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,123 +6,11 @@

#![allow(non_snake_case)]

use dusk_jubjub::JubJubScalar;
use dusk_plonk::prelude::*;
use ff::Field;
use jubjub_schnorr::{gadgets, SecretKey as SchnorrSecretKey, Signature};
use rand::{CryptoRng, RngCore};
use jubjub_schnorr::gadgets;
use phoenix_core::RecipientParameters;

use crate::elgamal;
use phoenix_core::{PublicKey, SecretKey};

const TX_OUTPUT_NOTES: usize = 2;

/// Parameters needed to prove a recipient in-circuit
#[derive(Debug, Clone, Copy)]
pub struct RecipientParameters {
/// Public key of the transaction sender
pub sender_pk: PublicKey,
/// Note public keys of each note recipient
pub output_npk: [JubJubAffine; TX_OUTPUT_NOTES],
/// Signatures of 'payload_hash' verifiable using 'pk_A' and 'pk_B'
pub sig: [Signature; TX_OUTPUT_NOTES],
/// Asymmetric encryption of 'pk_A' using both recipients 'npk'
pub enc_A: [(JubJubExtended, JubJubExtended); TX_OUTPUT_NOTES],
/// Asymmetric encryption of 'pk_B' using both recipients 'npk'
pub enc_B: [(JubJubExtended, JubJubExtended); TX_OUTPUT_NOTES],
/// Randomness needed to encrypt/decrypt 'pk_A'
pub r_A: [JubJubScalar; TX_OUTPUT_NOTES],
/// Randomness needed to encrypt/decrypt 'pk_B'
pub r_B: [JubJubScalar; TX_OUTPUT_NOTES],
}

impl Default for RecipientParameters {
fn default() -> Self {
let sk =
SecretKey::new(JubJubScalar::default(), JubJubScalar::default());
let sender_pk = PublicKey::from(&sk);

Self {
sender_pk,
output_npk: [JubJubAffine::default(), JubJubAffine::default()],
sig: [Signature::default(), Signature::default()],
enc_A: [(JubJubExtended::default(), JubJubExtended::default());
TX_OUTPUT_NOTES],
enc_B: [(JubJubExtended::default(), JubJubExtended::default());
TX_OUTPUT_NOTES],
r_A: [JubJubScalar::default(); TX_OUTPUT_NOTES],
r_B: [JubJubScalar::default(); TX_OUTPUT_NOTES],
}
}
}

impl RecipientParameters {
/// Create the recipient parameter
pub fn new(
rng: &mut (impl RngCore + CryptoRng),
sender_sk: &SecretKey,
output_npk: [JubJubAffine; TX_OUTPUT_NOTES],
payload_hash: BlsScalar,
) -> Self {
// Encrypt the public key of the sender. We need to encrypt
// both 'A' and 'B', using both tx output note public keys.
let sender_pk = PublicKey::from(sender_sk);

let r_A = [
JubJubScalar::random(&mut *rng),
JubJubScalar::random(&mut *rng),
];
let r_B = [
JubJubScalar::random(&mut *rng),
JubJubScalar::random(&mut *rng),
];

let (A_enc_1_c1, A_enc_1_c2) = elgamal::encrypt(
&output_npk[0].into(), // note_pk_1.as_ref(),
sender_pk.A(),
&r_A[0],
);

let (B_enc_1_c1, B_enc_1_c2) = elgamal::encrypt(
&output_npk[0].into(), // note_pk_1.as_ref(),
sender_pk.B(),
&r_B[0],
);
let (A_enc_2_c1, A_enc_2_c2) = elgamal::encrypt(
&output_npk[1].into(), // note_pk_2.as_ref(),
sender_pk.A(),
&r_A[1],
);

let (B_enc_2_c1, B_enc_2_c2) = elgamal::encrypt(
&output_npk[1].into(), // note_pk_2.as_ref(),
sender_pk.B(),
&r_B[1],
);

let enc_A = [(A_enc_1_c1, A_enc_1_c2), (A_enc_2_c1, A_enc_2_c2)];
let enc_B = [(B_enc_1_c1, B_enc_1_c2), (B_enc_2_c1, B_enc_2_c2)];

// Sign the payload hash using both 'a' and 'b'
let schnorr_sk_a = SchnorrSecretKey::from(sender_sk.a());
let sig_A = schnorr_sk_a.sign(rng, payload_hash);

let schnorr_sk_b = SchnorrSecretKey::from(sender_sk.b());
let sig_B = schnorr_sk_b.sign(rng, payload_hash);

let sig = [sig_A, sig_B];

RecipientParameters {
sender_pk,
output_npk,
sig,
enc_A,
enc_B,
r_A,
r_B,
}
}
}

/// Gadget to prove a valid origin for a given transaction.
pub(crate) fn gadget(
Expand Down
6 changes: 4 additions & 2 deletions circuits/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ use rand::{CryptoRng, RngCore, SeedableRng};
extern crate alloc;
use alloc::vec::Vec;

use phoenix_core::{Error as PhoenixError, Note, SecretKey, ViewKey};
use phoenix_core::{
Error as PhoenixError, Note, RecipientParameters, SecretKey, ViewKey,
};

use crate::{recipient, recipient::RecipientParameters};
use crate::recipient;

const TX_OUTPUT_NOTES: usize = 2;

Expand Down
14 changes: 5 additions & 9 deletions circuits/tests/elgamal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use dusk_jubjub::{JubJubAffine, JubJubScalar, GENERATOR_EXTENDED};
use dusk_plonk::prelude::*;
use ff::Field;
use phoenix_circuits::elgamal;
use phoenix_core::{PublicKey, SecretKey};
use phoenix_circuits::elgamal::{decrypt_gadget, encrypt_gadget};
use phoenix_core::{elgamal, PublicKey, SecretKey};
use rand::rngs::StdRng;
use rand::SeedableRng;

Expand Down Expand Up @@ -77,19 +77,15 @@ impl Circuit for ElGamalCircuit {

// ENCRYPT
let (ciphertext_1, ciphertext_2) =
elgamal::encrypt_gadget(composer, public_key, plaintext, r)?;
encrypt_gadget(composer, public_key, plaintext, r)?;

// ASSERT RESULT MAKING THE CIPHERTEXT PUBLIC
composer.assert_equal_public_point(ciphertext_1, self.ciphertext_1);
composer.assert_equal_public_point(ciphertext_2, self.ciphertext_2);

// DECRYPT
let dec_plaintext = elgamal::decrypt_gadget(
composer,
secret_key,
ciphertext_1,
ciphertext_2,
);
let dec_plaintext =
decrypt_gadget(composer, secret_key, ciphertext_1, ciphertext_2);

// ASSERT RESULTING PLAINTEXT
composer.assert_equal_point(dec_plaintext, plaintext);
Expand Down
11 changes: 3 additions & 8 deletions circuits/tests/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ use dusk_plonk::prelude::*;
use ff::Field;
use poseidon_merkle::{Item, Tree};

use phoenix_circuits::{
transaction::{TxCircuit, TxInputNote, TxOutputNote},
RecipientParameters,
use phoenix_circuits::transaction::{TxCircuit, TxInputNote, TxOutputNote};
use phoenix_core::{
value_commitment, Note, PublicKey, RecipientParameters, SecretKey,
};
use phoenix_core::{value_commitment, Note, PublicKey, SecretKey};

#[macro_use]
extern crate lazy_static;
Expand Down Expand Up @@ -151,10 +150,6 @@ fn test_transfer_circuit_1_2() {
TxOutputNote::new(value1, commitment1, blinder1),
TxOutputNote::new(value2, commitment2, blinder2),
];
// let tx_output_notes = [
// TxOutputNote::new(10, JubJubScalar::random(&mut rng)),
// TxOutputNote::new(5, JubJubScalar::random(&mut rng)),
// ];

let (proof, public_inputs) = prover
.prove(
Expand Down
3 changes: 3 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add `RecipientParameters`
- Add `elgamal::encrypt` and `elgamal::decrypt`
- Add `stealth_address` and `sync_address` functions directly to note [#208]
- Add a light sync method in the `ViewKey` [#199]
- Add function `value_commitment` [#201]
- Add function `transparent_value_commitment` [#201]

### Changed

- Move `OUTPUT_NOTES` to crate root
- Change `owns` and `owns_unchecked` to take `&Note` [#208]
- Change `gen_note_sk` to take `&StealthAddress` [#208]
- Rename `crossover` to `deposit` [#190]
Expand Down
41 changes: 41 additions & 0 deletions core/src/encryption/elgamal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

//! This module implements the ElGamal asymmetric cipher. It allows to
//! encrypt and decrypt.
//!
//! Reference: https://link.springer.com/chapter/10.1007/3-540-39568-7_2

use dusk_jubjub::{JubJubExtended, JubJubScalar, GENERATOR};

/// Encrypts a JubJubExtended plaintext given a public key and a fresh random
/// number 'r'.
///
/// ## Return
/// Returns a ciphertext (JubJubExtended, JubJubExtended).
pub fn encrypt(
public_key: &JubJubExtended,
plaintext: &JubJubExtended,
r: &JubJubScalar,
) -> (JubJubExtended, JubJubExtended) {
let ciphertext_1 = GENERATOR * r;
let ciphertext_2 = plaintext + public_key * r;

(ciphertext_1, ciphertext_2)
}

/// Decrypts a ciphertext given a secret key.
///
/// ## Return
/// Returns a JubJubExtended plaintext.
pub fn decrypt(
secret_key: &JubJubScalar,
ciphertext_1: &JubJubExtended,
ciphertext_2: &JubJubExtended,
) -> JubJubExtended {
// return the plaintext
ciphertext_2 - ciphertext_1 * secret_key
}
2 changes: 2 additions & 0 deletions core/src/encryption/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@

/// AES symmetric cipher
pub mod aes;
/// ElGamal asymmetric encryption
pub mod elgamal;
7 changes: 6 additions & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,24 @@ mod encryption;
mod error;
mod keys;
mod note;
mod recipient;

#[cfg(feature = "alloc")]
mod transaction;

/// The number of output notes in a transaction
pub const OUTPUT_NOTES: usize = 2;

pub use addresses::stealth::StealthAddress;
pub use addresses::sync::SyncAddress;
pub use encryption::aes;
pub use encryption::{aes, elgamal};
pub use error::Error;
pub use keys::hash;
pub use keys::public::PublicKey;
pub use keys::secret::SecretKey;
pub use keys::view::ViewKey;
pub use note::{Note, NoteType, ENCRYPTION_SIZE as NOTE_ENCRYPTION_SIZE};
pub use recipient::RecipientParameters;

#[cfg(feature = "alloc")]
/// Transaction Skeleton used by the phoenix transaction model
Expand Down
Loading

0 comments on commit dec7e9e

Please sign in to comment.