From 23f2bbeac92a088bbfb0fe97d29ca6d5b43d2aea Mon Sep 17 00:00:00 2001 From: parazyd Date: Tue, 20 Feb 2024 13:56:19 +0100 Subject: [PATCH] sdk/crypto: Add "_unsafe" suffix to ElGamalEncryptedNote functions The reasoning is explained in the rustdoc for those functions. --- bin/drk/src/dao.rs | 2 +- src/contract/dao/src/client/auth_xfer.rs | 4 ++-- src/contract/dao/src/client/vote.rs | 3 ++- src/contract/dao/tests/integration.rs | 6 ++--- src/sdk/src/crypto/note.rs | 29 ++++++++++++++++++++---- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/bin/drk/src/dao.rs b/bin/drk/src/dao.rs index ecb0125d4..21f4a8f75 100644 --- a/bin/drk/src/dao.rs +++ b/bin/drk/src/dao.rs @@ -799,7 +799,7 @@ impl Drk { for vote in new_dao_votes { for dao in &daos { // TODO: we shouldn't decrypt with all DAOs here - let note = vote.0.note.decrypt(&dao.secret_key); + let note = vote.0.note.decrypt_unsafe(&dao.secret_key); eprintln!("Managed to decrypt DAO proposal vote note"); let daos_proposals = self.get_dao_proposals(dao.id).await?; let mut proposal_id = None; diff --git a/src/contract/dao/src/client/auth_xfer.rs b/src/contract/dao/src/client/auth_xfer.rs index ef09e627f..784609352 100644 --- a/src/contract/dao/src/client/auth_xfer.rs +++ b/src/contract/dao/src/client/auth_xfer.rs @@ -72,7 +72,7 @@ impl DaoAuthMoneyTransferCall { coin_attrs.blind.inner(), ]; let enc_note = - ElGamalEncryptedNote::encrypt(note, &ephem_secret, &coin_attrs.public_key); + ElGamalEncryptedNote::encrypt_unsafe(note, &ephem_secret, &coin_attrs.public_key); let prover_witnesses = vec![ Witness::EcNiPoint(Value::known(coin_attrs.public_key.inner())), @@ -118,7 +118,7 @@ impl DaoAuthMoneyTransferCall { self.dao_coin_attrs.blind.inner(), ]; let dao_change_attrs = - ElGamalEncryptedNote::encrypt(note, &ephem_secret, &self.dao.public_key); + ElGamalEncryptedNote::encrypt_unsafe(note, &ephem_secret, &self.dao.public_key); let params = DaoAuthMoneyTransferParams { enc_attrs, dao_change_attrs }; diff --git a/src/contract/dao/src/client/vote.rs b/src/contract/dao/src/client/vote.rs index 840c4fd32..770a092f2 100644 --- a/src/contract/dao/src/client/vote.rs +++ b/src/contract/dao/src/client/vote.rs @@ -249,7 +249,8 @@ impl DaoVoteCall { let proposal_bulla = self.proposal.to_bulla(); let note = [vote_option, yes_vote_blind.inner(), all_vote_value_fp, all_vote_blind.inner()]; - let enc_note = ElGamalEncryptedNote::encrypt(note, &ephem_secret, &self.dao_keypair.public); + let enc_note = + ElGamalEncryptedNote::encrypt_unsafe(note, &ephem_secret, &self.dao_keypair.public); let public_inputs = vec![ token_commit, diff --git a/src/contract/dao/tests/integration.rs b/src/contract/dao/tests/integration.rs index ba330c70f..94ea3f3ef 100644 --- a/src/contract/dao/tests/integration.rs +++ b/src/contract/dao/tests/integration.rs @@ -392,9 +392,9 @@ fn integration_test() -> Result<()> { } // Gather and decrypt all vote notes - let vote_note_1 = alice_vote_params.note.decrypt(&dao_keypair.secret); - let vote_note_2 = bob_vote_params.note.decrypt(&dao_keypair.secret); - let vote_note_3 = charlie_vote_params.note.decrypt(&dao_keypair.secret); + let vote_note_1 = alice_vote_params.note.decrypt_unsafe(&dao_keypair.secret); + let vote_note_2 = bob_vote_params.note.decrypt_unsafe(&dao_keypair.secret); + let vote_note_3 = charlie_vote_params.note.decrypt_unsafe(&dao_keypair.secret); // Count the votes let mut total_yes_vote_value = 0; diff --git a/src/sdk/src/crypto/note.rs b/src/sdk/src/crypto/note.rs index 9b1c034b2..a4c0488dd 100644 --- a/src/sdk/src/crypto/note.rs +++ b/src/sdk/src/crypto/note.rs @@ -81,15 +81,26 @@ impl AeadEncryptedNote { } } -/// An encrypted note using an ElGamal scheme verifiable in ZK +/// An encrypted note using an ElGamal scheme verifiable in ZK. +/// +/// **WARNING:** +/// Without ZK, there is no authentication of the ciphertexts so these should +/// not be used without a corresponding ZK proof. #[derive(Debug, Copy, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)] pub struct ElGamalEncryptedNote { + /// The values encrypted with the derived shared secret using Diffie-Hellman pub encrypted_values: [pallas::Base; N], + /// The ephemeral public key used for Diffie-Hellman key derivation pub ephem_public: PublicKey, } impl ElGamalEncryptedNote { - pub fn encrypt( + /// Encrypt given values to the given `PublicKey` using a `SecretKey` for Diffie-Hellman + /// + /// Note that this does not do any message authentication. + /// This means that alterations of the ciphertexts lead to the same alterations + /// on the plaintexts. + pub fn encrypt_unsafe( values: [pallas::Base; N], ephem_secret: &SecretKey, public: &PublicKey, @@ -99,11 +110,13 @@ impl ElGamalEncryptedNote { let (ss_x, ss_y) = PublicKey::from(public.inner() * fp_mod_fv(ephem_secret.inner())).xy(); let shared_secret = poseidon_hash([ss_x, ss_y]); + // Derive the blinds using the shared secret and incremental nonces let mut blinds = [pallas::Base::ZERO; N]; for (i, item) in blinds.iter_mut().enumerate().take(N) { *item = poseidon_hash([shared_secret, pallas::Base::from(i as u64 + 1)]); } + // Encrypt the values let mut encrypted_values = [pallas::Base::ZERO; N]; for i in 0..N { encrypted_values[i] = values[i] + blinds[i]; @@ -112,7 +125,13 @@ impl ElGamalEncryptedNote { Self { encrypted_values, ephem_public } } - pub fn decrypt(&self, secret: &SecretKey) -> [pallas::Base; N] { + /// Decrypt the `ElGamalEncryptedNote` using a `SecretKey` for shared secret derivation + /// using Diffie-Hellman + /// + /// Note that this does not do any message authentication. + /// This means that alterations of the ciphertexts lead to the same alterations + /// on the plaintexts. + pub fn decrypt_unsafe(&self, secret: &SecretKey) -> [pallas::Base; N] { // Derive shared secret using DH let (ss_x, ss_y) = PublicKey::from(self.ephem_public.inner() * fp_mod_fv(secret.inner())).xy(); @@ -162,9 +181,9 @@ mod tests { let ephem_secret = SecretKey::random(&mut OsRng); let encrypted_note = - ElGamalEncryptedNote::encrypt(plain_values, &ephem_secret, &keypair.public); + ElGamalEncryptedNote::encrypt_unsafe(plain_values, &ephem_secret, &keypair.public); - let decrypted_values = encrypted_note.decrypt(&keypair.secret); + let decrypted_values = encrypted_note.decrypt_unsafe(&keypair.secret); assert_eq!(plain_values, decrypted_values); }