Skip to content

Commit

Permalink
core: Add sender_pk to the Note constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
moCello committed Jun 19, 2024
1 parent ccc2294 commit bb24806
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 184 deletions.
4 changes: 4 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add `encrypt_sender` function to encrypt the sender with the npk [#214]
- Add `decrypt_sender` method to the `Note` [#214]
- Add `elgamal::encrypt` and `elgamal::decrypt`
- Add `stealth_address` function directly to note [#208]
- Add function `value_commitment` [#201]
Expand All @@ -20,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Rename `tx_max_fee` to `max_fee` [#214]
- Add `sender_enc` field to the `Note` [#214]
- Add `sender_blinder` parameter for `Note` contructors [#214]
- Add `sender_pk` parameter for `Note` contructors [#214]
- Add `sender_enc` parameter for `Note::transparent_stealth` [#214]
- Rename `encryption_blinder` to `value_blinder` [#214]
- Rename `NOTE_ENCRYPTION_SIZE` to `NOTE_VALUE_ENC_SIZE` [#214]
- Move `OUTPUT_NOTES` to crate root
Expand Down
6 changes: 4 additions & 2 deletions core/src/encryption/elgamal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ pub fn encrypt(
/// Returns a JubJubExtended plaintext.
pub fn decrypt(
secret_key: &JubJubScalar,
ciphertext_1: &JubJubExtended,
ciphertext_2: &JubJubExtended,
ciphertext: &(JubJubExtended, JubJubExtended),
) -> JubJubExtended {
let ciphertext_1 = ciphertext.0;
let ciphertext_2 = ciphertext.1;

// return the plaintext
ciphertext_2 - ciphertext_1 * secret_key
}
4 changes: 3 additions & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ pub use keys::hash;
pub use keys::public::PublicKey;
pub use keys::secret::SecretKey;
pub use keys::view::ViewKey;
pub use note::{Note, NoteType, VALUE_ENC_SIZE as NOTE_VAL_ENC_SIZE};
pub use note::{
encrypt_sender, Note, NoteType, VALUE_ENC_SIZE as NOTE_VAL_ENC_SIZE,
};
pub use stealth_address::StealthAddress;

#[cfg(feature = "alloc")]
Expand Down
112 changes: 77 additions & 35 deletions core/src/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@

use core::convert::{TryFrom, TryInto};

use crate::{
encryption::elgamal, transparent_value_commitment, value_commitment, Error,
PublicKey, SecretKey, StealthAddress, ViewKey,
};
use dusk_bls12_381::BlsScalar;
use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
use dusk_jubjub::{dhke, JubJubAffine, JubJubScalar, GENERATOR_NUMS_EXTENDED};

use crate::aes;

use dusk_poseidon::{Domain, Hash};
use ff::Field;
use jubjub_schnorr::{PublicKey as NotePublicKey, SecretKey as NoteSecretKey};
use rand::{CryptoRng, RngCore};

use crate::{
aes, elgamal, transparent_value_commitment, value_commitment, Error,
PublicKey, SecretKey, StealthAddress, ViewKey,
};

#[cfg(feature = "rkyv-impl")]
use rkyv::{Archive, Deserialize, Serialize};

Expand Down Expand Up @@ -79,7 +78,6 @@ pub struct Note {
pub(crate) stealth_address: StealthAddress,
pub(crate) pos: u64,
pub(crate) value_enc: [u8; VALUE_ENC_SIZE],
// the elgamal encryption of the sender_pk encrypted using the output_npk
pub(crate) sender_enc: [(JubJubAffine, JubJubAffine); 2],
}

Expand All @@ -96,13 +94,14 @@ impl Note {
pub fn new<R: RngCore + CryptoRng>(
rng: &mut R,
note_type: NoteType,
pk: &PublicKey,
sender_pk: &PublicKey,
receiver_pk: &PublicKey,
value: u64,
value_blinder: JubJubScalar,
sender_blinder: [JubJubScalar; 2],
) -> Self {
let r = JubJubScalar::random(&mut *rng);
let stealth_address = pk.gen_stealth_address(&r);
let stealth_address = receiver_pk.gen_stealth_address(&r);

let value_commitment = value_commitment(value, value_blinder);

Expand All @@ -117,7 +116,7 @@ impl Note {
value_enc
}
NoteType::Obfuscated => {
let shared_secret = dhke(&r, pk.A());
let shared_secret = dhke(&r, receiver_pk.A());
let value_blinder = BlsScalar::from(value_blinder);

let mut plaintext = value.to_bytes().to_vec();
Expand All @@ -128,29 +127,17 @@ impl Note {
}
};

let sender_enc_A = elgamal::encrypt(
pk.A(),
stealth_address.note_pk.as_ref(),
&sender_blinder[0],
);

let sender_enc_B = elgamal::encrypt(
pk.B(),
stealth_address.note_pk.as_ref(),
&sender_blinder[1],
);
let sender_enc_A: (JubJubAffine, JubJubAffine) =
(sender_enc_A.0.into(), sender_enc_A.1.into());
let sender_enc_B: (JubJubAffine, JubJubAffine) =
(sender_enc_B.0.into(), sender_enc_B.1.into());

Note {
note_type,
value_commitment,
stealth_address,
pos,
value_enc,
sender_enc: [sender_enc_A, sender_enc_B],
sender_enc: encrypt_sender(
stealth_address.note_pk(),
sender_pk,
&sender_blinder,
),
}
}

Expand All @@ -161,14 +148,16 @@ impl Note {
/// notes, so this can be trivially treated as a constant.
pub fn transparent<R: RngCore + CryptoRng>(
rng: &mut R,
pk: &PublicKey,
sender_pk: &PublicKey,
receiver_pk: &PublicKey,
value: u64,
sender_blinder: [JubJubScalar; 2],
) -> Self {
Self::new(
rng,
NoteType::Transparent,
pk,
sender_pk,
receiver_pk,
value,
TRANSPARENT_BLINDER,
sender_blinder,
Expand Down Expand Up @@ -210,15 +199,17 @@ impl Note {
/// knowledge of the value commitment of this note.
pub fn obfuscated<R: RngCore + CryptoRng>(
rng: &mut R,
pk: &PublicKey,
sender_pk: &PublicKey,
receiver_pk: &PublicKey,
value: u64,
value_blinder: JubJubScalar,
sender_blinder: [JubJubScalar; 2],
) -> Self {
Self::new(
rng,
NoteType::Obfuscated,
pk,
sender_pk,
receiver_pk,
value,
value_blinder,
sender_blinder,
Expand All @@ -237,7 +228,7 @@ impl Note {
}
}

fn decrypt_data(
fn decrypt_value(
&self,
vk: &ViewKey,
) -> Result<(u64, JubJubScalar), BytesError> {
Expand Down Expand Up @@ -327,6 +318,13 @@ impl Note {
&self.value_enc
}

/// Returns elgamal encryption of the sender's [`PublicKey`] encrypted using
/// the [`StealthAddress::note_pk`] so only the receiver of the [`Note`]
/// can decrypt.
pub const fn sender_enc(&self) -> &[(JubJubAffine, JubJubAffine); 2] {
&self.sender_enc
}

/// Attempt to decrypt the note value provided a [`ViewKey`]. Always
/// succeeds for transparent notes, might fails or return random values for
/// obfuscated notes if the provided view key is wrong.
Expand All @@ -338,7 +336,7 @@ impl Note {
Ok(value)
}
(NoteType::Obfuscated, Some(vk)) => self
.decrypt_data(vk)
.decrypt_value(vk)
.map(|(value, _)| value)
.map_err(|_| Error::InvalidEncryption),
_ => Err(Error::MissingViewKey),
Expand All @@ -355,12 +353,56 @@ impl Note {
match (self.note_type, vk) {
(NoteType::Transparent, _) => Ok(TRANSPARENT_BLINDER),
(NoteType::Obfuscated, Some(vk)) => self
.decrypt_data(vk)
.decrypt_value(vk)
.map(|(_, value_blinder)| value_blinder)
.map_err(|_| Error::InvalidEncryption),
_ => Err(Error::MissingViewKey),
}
}

/// Decrypts the [`PublicKey`] of the sender of the [`Note`], using the
/// [`NoteSecretKey`] generated by the receiver's [`SecretKey`] and the
/// [`StealthAddress`] of the [`Note`].
///
/// Note: Decryption with an incorrect [`NoteSecretKey`] will still yield a
/// [`PublicKey`], but it will a random one that has nothing to do with the
/// sender's [`PublicKey`].
pub fn decrypt_sender(&self, note_sk: &NoteSecretKey) -> PublicKey {
let sender_enc_A = self.sender_enc()[0];
let sender_enc_B = self.sender_enc()[1];

let decrypt_A = elgamal::decrypt(
note_sk.as_ref(),
&(sender_enc_A.0.into(), sender_enc_A.1.into()),
);
let decrypt_B = elgamal::decrypt(
note_sk.as_ref(),
&(sender_enc_B.0.into(), sender_enc_B.1.into()),
);

PublicKey::new(decrypt_A, decrypt_B)
}
}

/// Encrypt the sender [`PublicKey`] in a way that only the receiver of the note
/// can decrypt.
pub fn encrypt_sender(
note_pk: &NotePublicKey,
sender_pk: &PublicKey,
blinder: &[JubJubScalar; 2],
) -> [(JubJubAffine, JubJubAffine); 2] {
let sender_enc_A =
elgamal::encrypt(note_pk.as_ref(), sender_pk.A(), &blinder[0]);

let sender_enc_B =
elgamal::encrypt(note_pk.as_ref(), sender_pk.B(), &blinder[1]);

let sender_enc_A: (JubJubAffine, JubJubAffine) =
(sender_enc_A.0.into(), sender_enc_A.1.into());
let sender_enc_B: (JubJubAffine, JubJubAffine) =
(sender_enc_B.0.into(), sender_enc_B.1.into());

[sender_enc_A, sender_enc_B]
}

const SIZE: usize = 1
Expand Down
4 changes: 2 additions & 2 deletions core/tests/encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ fn test_elgamal_encrypt_and_decrypt() {
let (c1, c2) = elgamal::encrypt(pk.A(), &message, &blinder);

// Assert decryption
let dec_message = elgamal::decrypt(sk.a(), &c1, &c2);
let dec_message = elgamal::decrypt(sk.a(), &(c1, c2));
assert_eq!(message, dec_message);

// Assert decryption using an incorrect key
let dec_message_wrong = elgamal::decrypt(sk.b(), &c1, &c2);
let dec_message_wrong = elgamal::decrypt(sk.b(), &(c1, c2));
assert_ne!(message, dec_message_wrong);
}
27 changes: 17 additions & 10 deletions core/tests/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,31 +62,38 @@ fn keys_consistency() {

let r = JubJubScalar::random(&mut rng);

let sk = SecretKey::random(&mut rng);
let pk = PublicKey::from(&sk);
let vk = ViewKey::from(&sk);
let sender_pk = PublicKey::from(&SecretKey::random(&mut rng));
let receiver_sk = SecretKey::random(&mut rng);
let receiver_pk = PublicKey::from(&receiver_sk);
let receiver_vk = ViewKey::from(&receiver_sk);

let sender_blinder = [
JubJubScalar::random(&mut rng),
JubJubScalar::random(&mut rng),
];
let note = Note::transparent(&mut rng, &pk, NOTE_VALUE, sender_blinder);
let note = Note::transparent(
&mut rng,
&sender_pk,
&receiver_pk,
NOTE_VALUE,
sender_blinder,
);

assert!(vk.owns(&note));
assert!(sk.owns(&note));
assert!(receiver_vk.owns(&note));
assert!(receiver_sk.owns(&note));

let wrong_sk = SecretKey::random(&mut rng);
let wrong_vk = ViewKey::from(&wrong_sk);

assert_ne!(sk, wrong_sk);
assert_ne!(vk, wrong_vk);
assert_ne!(receiver_sk, wrong_sk);
assert_ne!(receiver_vk, wrong_vk);

assert!(!wrong_vk.owns(&note));
assert!(!wrong_sk.owns(&note));

let sa = pk.gen_stealth_address(&r);
let sa = receiver_pk.gen_stealth_address(&r);

let note_sk = sk.gen_note_sk(&sa);
let note_sk = receiver_sk.gen_note_sk(&sa);
let wrong_note_sk = wrong_sk.gen_note_sk(&sa);

assert_eq!(
Expand Down
Loading

0 comments on commit bb24806

Please sign in to comment.