From d81fb2665fb55b059396cbc815f7837289b6ef2d Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 12 Sep 2025 15:06:49 +0300 Subject: [PATCH 1/3] fix: commnets fix cleanup --- .../privacy_preserving_transaction.rs | 41 ++++++++++++++++- .../privacy_preserving_transaction/circuit.rs | 45 ++----------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/nssa/src/encoding/privacy_preserving_transaction.rs b/nssa/src/encoding/privacy_preserving_transaction.rs index 7c49938..8123166 100644 --- a/nssa/src/encoding/privacy_preserving_transaction.rs +++ b/nssa/src/encoding/privacy_preserving_transaction.rs @@ -1,10 +1,12 @@ use std::io::{Cursor, Read}; use nssa_core::{ - Commitment, Nullifier, + Commitment, Nullifier, PrivacyPreservingCircuitOutput, account::Account, encryption::{Ciphertext, EphemeralPublicKey}, }; +use program_methods::PRIVACY_PRESERVING_CIRCUIT_ID; +use risc0_zkvm::{InnerReceipt, Receipt}; use crate::{ Address, PrivacyPreservingTransaction, PublicKey, Signature, @@ -224,3 +226,40 @@ impl PrivacyPreservingTransaction { Ok(PrivacyPreservingTransaction::new(message, witness_set)) } } + +impl Proof { + pub(crate) fn is_valid_for(&self, circuit_output: &PrivacyPreservingCircuitOutput) -> bool { + let inner: InnerReceipt = borsh::from_slice(&self.0).unwrap(); + let receipt = Receipt::new(inner, circuit_output.to_bytes()); + receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID).is_ok() + } + + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + let proof_len = self.0.len() as u32; + bytes.extend_from_slice(&proof_len.to_le_bytes()); + bytes.extend_from_slice(&self.0); + bytes + } + + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { + let proof_len = u32_from_cursor(cursor) as usize; + let mut proof = Vec::with_capacity(proof_len); + + for _ in 0..proof_len { + let mut one_byte_buf = [0u8]; + + cursor.read_exact(&mut one_byte_buf)?; + + proof.push(one_byte_buf[0]); + } + Ok(Self(proof)) + } +} + +// TODO: Improve error handling. Remove unwraps. +pub fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { + let mut word_buf = [0u8; 4]; + cursor.read_exact(&mut word_buf).unwrap(); + u32::from_le_bytes(word_buf) +} diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index a8e37cf..ca7b6ae 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -1,57 +1,18 @@ -use std::io::{Cursor, Read}; - use nssa_core::{ MembershipProof, NullifierPublicKey, NullifierSecretKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, SharedSecretKey, account::AccountWithMetadata, program::{InstructionData, ProgramOutput}, }; -use risc0_zkvm::{ExecutorEnv, InnerReceipt, Receipt, default_prover}; +use risc0_zkvm::{ExecutorEnv, Receipt, default_prover}; use crate::{error::NssaError, program::Program}; -use program_methods::{PRIVACY_PRESERVING_CIRCUIT_ELF, PRIVACY_PRESERVING_CIRCUIT_ID}; +use program_methods::PRIVACY_PRESERVING_CIRCUIT_ELF; /// Proof of the privacy preserving execution circuit #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Proof(pub(super) Vec); - -impl Proof { - pub(crate) fn is_valid_for(&self, circuit_output: &PrivacyPreservingCircuitOutput) -> bool { - let inner: InnerReceipt = borsh::from_slice(&self.0).unwrap(); - let receipt = Receipt::new(inner, circuit_output.to_bytes()); - receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID).is_ok() - } - - pub fn to_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let proof_len = self.0.len() as u32; - bytes.extend_from_slice(&proof_len.to_le_bytes()); - bytes.extend_from_slice(&self.0); - bytes - } - - pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { - let proof_len = u32_from_cursor(cursor) as usize; - let mut proof = Vec::with_capacity(proof_len); - - for _ in 0..proof_len { - let mut one_byte_buf = [0u8]; - - cursor.read_exact(&mut one_byte_buf)?; - - proof.push(one_byte_buf[0]); - } - Ok(Self(proof)) - } -} - -// TODO: Improve error handling. Remove unwraps. -pub fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { - let mut word_buf = [0u8; 4]; - cursor.read_exact(&mut word_buf).unwrap(); - u32::from_le_bytes(word_buf) -} +pub struct Proof(pub(crate) Vec); /// Generates a proof of the execution of a NSSA program inside the privacy preserving execution /// circuit From a11f3720917b7f5e912c5f80b3d772b33d88833f Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Mon, 15 Sep 2025 10:39:57 +0300 Subject: [PATCH 2/3] fix: comments fix --- nssa/src/encoding/privacy_preserving_transaction.rs | 10 +--------- nssa/src/privacy_preserving_transaction/circuit.rs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/nssa/src/encoding/privacy_preserving_transaction.rs b/nssa/src/encoding/privacy_preserving_transaction.rs index 8123166..2e5ea14 100644 --- a/nssa/src/encoding/privacy_preserving_transaction.rs +++ b/nssa/src/encoding/privacy_preserving_transaction.rs @@ -1,12 +1,10 @@ use std::io::{Cursor, Read}; use nssa_core::{ - Commitment, Nullifier, PrivacyPreservingCircuitOutput, + Commitment, Nullifier, account::Account, encryption::{Ciphertext, EphemeralPublicKey}, }; -use program_methods::PRIVACY_PRESERVING_CIRCUIT_ID; -use risc0_zkvm::{InnerReceipt, Receipt}; use crate::{ Address, PrivacyPreservingTransaction, PublicKey, Signature, @@ -228,12 +226,6 @@ impl PrivacyPreservingTransaction { } impl Proof { - pub(crate) fn is_valid_for(&self, circuit_output: &PrivacyPreservingCircuitOutput) -> bool { - let inner: InnerReceipt = borsh::from_slice(&self.0).unwrap(); - let receipt = Receipt::new(inner, circuit_output.to_bytes()); - receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID).is_ok() - } - pub fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); let proof_len = self.0.len() as u32; diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index ca7b6ae..1421f62 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -4,11 +4,11 @@ use nssa_core::{ account::AccountWithMetadata, program::{InstructionData, ProgramOutput}, }; -use risc0_zkvm::{ExecutorEnv, Receipt, default_prover}; +use risc0_zkvm::{ExecutorEnv, InnerReceipt, Receipt, default_prover}; use crate::{error::NssaError, program::Program}; -use program_methods::PRIVACY_PRESERVING_CIRCUIT_ELF; +use program_methods::{PRIVACY_PRESERVING_CIRCUIT_ELF, PRIVACY_PRESERVING_CIRCUIT_ID}; /// Proof of the privacy preserving execution circuit #[derive(Debug, Clone, PartialEq, Eq)] @@ -78,6 +78,14 @@ fn execute_and_prove_program( .receipt) } +impl Proof { + pub(crate) fn is_valid_for(&self, circuit_output: &PrivacyPreservingCircuitOutput) -> bool { + let inner: InnerReceipt = borsh::from_slice(&self.0).unwrap(); + let receipt = Receipt::new(inner, circuit_output.to_bytes()); + receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID).is_ok() + } +} + #[cfg(test)] mod tests { use nssa_core::{ From d07b813739c658c629d40cb19c90c4c013a1327f Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Mon, 15 Sep 2025 14:04:49 +0300 Subject: [PATCH 3/3] fix: deviations adjustments --- key_protocol/Cargo.toml | 1 + .../key_management/ephemeral_key_holder.rs | 29 ++- key_protocol/src/key_management/mod.rs | 239 ++---------------- .../src/key_management/secret_holders.rs | 69 +++-- key_protocol/src/key_management/types.rs | 8 - nssa/Cargo.toml | 1 + .../src/encryption/shared_key_derivation.rs | 7 +- nssa/core/src/nullifier.rs | 6 + .../privacy_preserving_transaction/circuit.rs | 7 +- .../privacy_preserving_transaction/message.rs | 5 +- nssa/src/state.rs | 15 +- 11 files changed, 102 insertions(+), 285 deletions(-) delete mode 100644 key_protocol/src/key_management/types.rs diff --git a/key_protocol/Cargo.toml b/key_protocol/Cargo.toml index 3ce874d..d453753 100644 --- a/key_protocol/Cargo.toml +++ b/key_protocol/Cargo.toml @@ -17,6 +17,7 @@ aes-gcm.workspace = true lazy_static.workspace = true bip39.workspace = true hmac-sha512.workspace = true +nssa-core = { path = "../nssa/core", features = ["host"] } [dependencies.common] path = "../common" diff --git a/key_protocol/src/key_management/ephemeral_key_holder.rs b/key_protocol/src/key_management/ephemeral_key_holder.rs index 108a41e..e62d9b6 100644 --- a/key_protocol/src/key_management/ephemeral_key_holder.rs +++ b/key_protocol/src/key_management/ephemeral_key_holder.rs @@ -1,24 +1,30 @@ use elliptic_curve::PrimeField; -use k256::{AffinePoint, Scalar}; +use k256::Scalar; use log::info; +use nssa_core::{ + NullifierPublicKey, SharedSecretKey, + encryption::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey}, +}; use sha2::Digest; +use crate::key_management::secret_holders::OutgoingViewingSecretKey; + #[derive(Debug)] ///Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral public keys. Can produce shared secret for sender. pub struct EphemeralKeyHolder { - ephemeral_secret_key: Scalar, + ephemeral_secret_key: EphemeralSecretKey, } impl EphemeralKeyHolder { pub fn new( - receiver_nullifier_public_key: [u8; 32], - sender_outgoing_viewing_secret_key: Scalar, + receiver_nullifier_public_key: NullifierPublicKey, + sender_outgoing_viewing_secret_key: OutgoingViewingSecretKey, nonce: u64, ) -> Self { let mut hasher = sha2::Sha256::new(); hasher.update(receiver_nullifier_public_key); hasher.update(nonce.to_le_bytes()); - hasher.update([0; 192]); + hasher.update([0; 24]); let hash_recepient = hasher.finalize(); @@ -31,15 +37,18 @@ impl EphemeralKeyHolder { } } - pub fn generate_ephemeral_public_key(&self) -> AffinePoint { - (AffinePoint::GENERATOR * self.ephemeral_secret_key).into() + pub fn generate_ephemeral_public_key(&self) -> EphemeralPublicKey { + EphemeralPublicKey::from_scalar(self.ephemeral_secret_key) } pub fn calculate_shared_secret_sender( &self, - receiver_incoming_viewing_public_key: Scalar, - ) -> Scalar { - receiver_incoming_viewing_public_key * self.ephemeral_secret_key + receiver_incoming_viewing_public_key: IncomingViewingPublicKey, + ) -> SharedSecretKey { + SharedSecretKey::new( + &self.ephemeral_secret_key, + &receiver_incoming_viewing_public_key, + ) } pub fn log(&self) { diff --git a/key_protocol/src/key_management/mod.rs b/key_protocol/src/key_management/mod.rs index 5de3826..bcd3796 100644 --- a/key_protocol/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -1,28 +1,25 @@ -use aes_gcm::{Aes256Gcm, KeyInit, aead::Aead}; use common::TreeHashType; -use elliptic_curve::group::GroupEncoding; -use elliptic_curve::point::AffineCoordinates; -use k256::AffinePoint; use log::info; -use secret_holders::{PrivateKeyHolder, SeedHolder, TopSecretKeyHolder}; +use nssa_core::{ + NullifierPublicKey, SharedSecretKey, + encryption::{EphemeralPublicKey, IncomingViewingPublicKey}, +}; +use secret_holders::{PrivateKeyHolder, SecretSpendingKey, SeedHolder}; use serde::{Deserialize, Serialize}; use sha2::{Digest, digest::FixedOutput}; -use types::{CipherText, Nonce}; -use crate::key_protocol_core::PublicKey; pub type PublicAccountSigningKey = [u8; 32]; pub mod ephemeral_key_holder; pub mod secret_holders; -pub mod types; #[derive(Serialize, Deserialize, Clone, Debug)] ///Entrypoint to key management pub struct KeyChain { - top_secret_key_holder: TopSecretKeyHolder, + secret_spending_key: SecretSpendingKey, pub private_key_holder: PrivateKeyHolder, - pub nullifer_public_key: [u8; 32], - pub incoming_viewing_public_key: PublicKey, + pub nullifer_public_key: NullifierPublicKey, + pub incoming_viewing_public_key: IncomingViewingPublicKey, } impl KeyChain { @@ -30,15 +27,15 @@ impl KeyChain { //Currently dropping SeedHolder at the end of initialization. //Now entirely sure if we need it in the future. let seed_holder = SeedHolder::new_os_random(); - let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); + let secret_spending_key = seed_holder.produce_top_secret_key_holder(); - let private_key_holder = top_secret_key_holder.produce_private_key_holder(); + let private_key_holder = secret_spending_key.produce_private_key_holder(); let nullifer_public_key = private_key_holder.generate_nullifier_public_key(); let incoming_viewing_public_key = private_key_holder.generate_incoming_viewing_public_key(); Self { - top_secret_key_holder, + secret_spending_key, private_key_holder, nullifer_public_key, incoming_viewing_public_key, @@ -48,7 +45,7 @@ impl KeyChain { pub fn produce_user_address(&self) -> [u8; 32] { let mut hasher = sha2::Sha256::new(); - hasher.update(self.nullifer_public_key); + hasher.update(&self.nullifer_public_key); hasher.update(self.incoming_viewing_public_key.to_bytes()); ::from(hasher.finalize_fixed()) @@ -56,33 +53,20 @@ impl KeyChain { pub fn calculate_shared_secret_receiver( &self, - ephemeral_public_key_sender: AffinePoint, - ) -> AffinePoint { - (ephemeral_public_key_sender - * self - .top_secret_key_holder - .generate_incloming_viewing_secret_key()) - .into() - } - - pub fn decrypt_data( - &self, - ephemeral_public_key_sender: AffinePoint, - ciphertext: CipherText, - nonce: Nonce, - ) -> Result, aes_gcm::Error> { - let shared_secret = self.calculate_shared_secret_receiver(ephemeral_public_key_sender); - let cipher = Aes256Gcm::new(&shared_secret.x()); - - cipher.decrypt(&nonce, ciphertext.as_slice()) + ephemeral_public_key_sender: EphemeralPublicKey, + ) -> SharedSecretKey { + SharedSecretKey::new( + &self + .secret_spending_key + .generate_incoming_viewing_secret_key(), + &ephemeral_public_key_sender, + ) } pub fn log(&self) { info!( "Secret spending key is {:?}", - hex::encode( - serde_json::to_vec(&self.top_secret_key_holder.secret_spending_key).unwrap() - ), + hex::encode(serde_json::to_vec(&self.secret_spending_key).unwrap()), ); info!( "Nulifier secret key is {:?}", @@ -113,17 +97,9 @@ impl KeyChain { #[cfg(test)] mod tests { - use aes_gcm::{ - Aes256Gcm, - aead::{Aead, KeyInit, OsRng}, - }; + use aes_gcm::aead::OsRng; use elliptic_curve::ff::Field; - use elliptic_curve::group::prime::PrimeCurveAffine; - use elliptic_curve::point::AffineCoordinates; - use k256::{AffinePoint, ProjectivePoint, Scalar}; - use types::{CipherText, Nonce}; - - use crate::key_management::ephemeral_key_holder::EphemeralKeyHolder; + use k256::{AffinePoint, Scalar}; use super::*; @@ -133,10 +109,7 @@ mod tests { let address_key_holder = KeyChain::new_os_random(); // Check that key holder fields are initialized with expected types - assert_ne!(address_key_holder.nullifer_public_key, [0u8; 32]); - assert!(!Into::::into( - address_key_holder.incoming_viewing_public_key.is_identity() - )); + assert_ne!(address_key_holder.nullifer_public_key.as_ref(), &[0u8; 32]); } #[test] @@ -145,171 +118,11 @@ mod tests { // Generate a random ephemeral public key sender let scalar = Scalar::random(&mut OsRng); - let ephemeral_public_key_sender = (ProjectivePoint::GENERATOR * scalar).to_affine(); + let ephemeral_public_key_sender = EphemeralPublicKey::from_scalar(scalar); // Calculate shared secret - let shared_secret = + let _shared_secret = address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender); - - // Ensure the shared secret is not an identity point (suggesting non-zero output) - assert!(!Into::::into(shared_secret.is_identity())); - } - - #[test] - fn test_decrypt_data() { - let address_key_holder = KeyChain::new_os_random(); - - let test_receiver_nullifier_public_key = [42; 32]; - let sender_outgoing_viewing_key = address_key_holder - .top_secret_key_holder - .generate_outgoing_viewing_secret_key(); - let nonce = 0; - - // Generate an ephemeral key and shared secret - let ephemeral_public_key_sender = EphemeralKeyHolder::new( - test_receiver_nullifier_public_key, - sender_outgoing_viewing_key, - nonce, - ) - .generate_ephemeral_public_key(); - let shared_secret = - address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender); - - // Encrypt sample data - let cipher = Aes256Gcm::new(&shared_secret.x()); - let nonce = Nonce::from_slice(b"unique nonce"); - let plaintext = b"Sensitive data"; - let ciphertext = cipher - .encrypt(nonce, plaintext.as_ref()) - .expect("encryption failure"); - - // Attempt decryption - let decrypted_data: Vec = address_key_holder - .decrypt_data( - ephemeral_public_key_sender, - CipherText::from(ciphertext), - *nonce, - ) - .unwrap(); - - // Verify decryption is successful and matches original plaintext - assert_eq!(decrypted_data, plaintext); - } - - #[test] - fn test_calculate_shared_secret_with_identity_point() { - let address_key_holder = KeyChain::new_os_random(); - - // Use identity point as ephemeral public key - let identity_point = AffinePoint::identity(); - - // Calculate shared secret - let shared_secret = address_key_holder.calculate_shared_secret_receiver(identity_point); - - // The shared secret with the identity point should also result in the identity point - assert!(Into::::into(shared_secret.is_identity())); - } - - #[test] - #[should_panic] - fn test_decrypt_data_with_incorrect_nonce() { - let address_key_holder = KeyChain::new_os_random(); - - // Generate ephemeral public key and shared secret - let scalar = Scalar::random(OsRng); - let ephemeral_public_key_sender = (ProjectivePoint::GENERATOR * scalar).to_affine(); - let shared_secret = - address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender); - - // Encrypt sample data with a specific nonce - let cipher = Aes256Gcm::new(&shared_secret.x()); - let nonce = Nonce::from_slice(b"unique nonce"); - let plaintext = b"Sensitive data"; - let ciphertext = cipher - .encrypt(nonce, plaintext.as_ref()) - .expect("encryption failure"); - - // Attempt decryption with an incorrect nonce - let incorrect_nonce = Nonce::from_slice(b"wrong nonce"); - let decrypted_data = address_key_holder - .decrypt_data( - ephemeral_public_key_sender, - CipherText::from(ciphertext.clone()), - *incorrect_nonce, - ) - .unwrap(); - - // The decryption should fail or produce incorrect output due to nonce mismatch - assert_ne!(decrypted_data, plaintext); - } - - #[test] - #[should_panic] - fn test_decrypt_data_with_incorrect_ciphertext() { - let address_key_holder = KeyChain::new_os_random(); - - // Generate ephemeral public key and shared secret - let scalar = Scalar::random(OsRng); - let ephemeral_public_key_sender = (ProjectivePoint::GENERATOR * scalar).to_affine(); - let shared_secret = - address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender); - - // Encrypt sample data - let cipher = Aes256Gcm::new(&shared_secret.x()); - let nonce = Nonce::from_slice(b"unique nonce"); - let plaintext = b"Sensitive data"; - let ciphertext = cipher - .encrypt(nonce, plaintext.as_ref()) - .expect("encryption failure"); - - // Tamper with the ciphertext to simulate corruption - let mut corrupted_ciphertext = ciphertext.clone(); - corrupted_ciphertext[0] ^= 1; // Flip a bit in the ciphertext - - // Attempt decryption - let result = address_key_holder - .decrypt_data( - ephemeral_public_key_sender, - CipherText::from(corrupted_ciphertext), - *nonce, - ) - .unwrap(); - - // The decryption should fail or produce incorrect output due to tampered ciphertext - assert_ne!(result, plaintext); - } - - #[test] - fn test_encryption_decryption_round_trip() { - let address_key_holder = KeyChain::new_os_random(); - - // Generate ephemeral key and shared secret - let scalar = Scalar::random(OsRng); - let ephemeral_public_key_sender = (ProjectivePoint::GENERATOR * scalar).to_affine(); - - // Encrypt sample data - let plaintext = b"Round-trip test data"; - let nonce = Nonce::from_slice(b"unique nonce"); - - let shared_secret = - address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender); - let cipher = Aes256Gcm::new(&shared_secret.x()); - - let ciphertext = cipher - .encrypt(nonce, plaintext.as_ref()) - .expect("encryption failure"); - - // Decrypt the data using the `KeyChain` instance - let decrypted_data = address_key_holder - .decrypt_data( - ephemeral_public_key_sender, - CipherText::from(ciphertext), - *nonce, - ) - .unwrap(); - - // Verify the decrypted data matches the original plaintext - assert_eq!(decrypted_data, plaintext); } #[test] diff --git a/key_protocol/src/key_management/secret_holders.rs b/key_protocol/src/key_management/secret_holders.rs index 5c2e522..eb59b40 100644 --- a/key_protocol/src/key_management/secret_holders.rs +++ b/key_protocol/src/key_management/secret_holders.rs @@ -1,7 +1,8 @@ use bip39::Mnemonic; use common::TreeHashType; use elliptic_curve::PrimeField; -use k256::{AffinePoint, Scalar}; +use k256::Scalar; +use nssa_core::{NullifierPublicKey, NullifierSecretKey, encryption::IncomingViewingPublicKey}; use rand::{RngCore, rngs::OsRng}; use serde::{Deserialize, Serialize}; use sha2::{Digest, digest::FixedOutput}; @@ -15,17 +16,18 @@ pub struct SeedHolder { } #[derive(Serialize, Deserialize, Debug, Clone)] -///Secret spending key holder. Produces `PrivateKeyHolder` objects. -pub struct TopSecretKeyHolder { - pub(crate) secret_spending_key: [u8; 32], -} +///Secret spending key object. Can produce `PrivateKeyHolder` objects. +pub struct SecretSpendingKey(pub(crate) [u8; 32]); + +pub type IncomingViewingSecretKey = Scalar; +pub type OutgoingViewingSecretKey = Scalar; #[derive(Serialize, Deserialize, Debug, Clone)] ///Private key holder. Produces public keys. Can produce address. Can produce shared secret for recepient. pub struct PrivateKeyHolder { - pub(crate) nullifier_secret_key: [u8; 32], - pub(crate) incoming_viewing_secret_key: Scalar, - pub(crate) outgoing_viewing_secret_key: Scalar, + pub(crate) nullifier_secret_key: NullifierSecretKey, + pub(crate) incoming_viewing_secret_key: IncomingViewingSecretKey, + pub(crate) outgoing_viewing_secret_key: OutgoingViewingSecretKey, } impl SeedHolder { @@ -52,74 +54,65 @@ impl SeedHolder { *hash.first_chunk::<32>().unwrap() } - pub fn produce_top_secret_key_holder(&self) -> TopSecretKeyHolder { - TopSecretKeyHolder { - secret_spending_key: self.generate_secret_spending_key_hash(), - } + pub fn produce_top_secret_key_holder(&self) -> SecretSpendingKey { + SecretSpendingKey(self.generate_secret_spending_key_hash()) } } -impl TopSecretKeyHolder { - pub fn generate_nullifier_secret_key(&self) -> [u8; 32] { +impl SecretSpendingKey { + pub fn generate_nullifier_secret_key(&self) -> NullifierSecretKey { let mut hasher = sha2::Sha256::new(); hasher.update("NSSA_keys"); - hasher.update(self.secret_spending_key); + hasher.update(self.0); hasher.update([1u8]); - hasher.update([0u8; 176]); + hasher.update([0u8; 22]); - ::from(hasher.finalize_fixed()) + ::from(hasher.finalize_fixed()) } - pub fn generate_incloming_viewing_secret_key(&self) -> Scalar { + pub fn generate_incoming_viewing_secret_key(&self) -> IncomingViewingSecretKey { let mut hasher = sha2::Sha256::new(); hasher.update("NSSA_keys"); - hasher.update(self.secret_spending_key); + hasher.update(self.0); hasher.update([2u8]); - hasher.update([0u8; 176]); + hasher.update([0u8; 22]); let hash = ::from(hasher.finalize_fixed()); - Scalar::from_repr(hash.into()).unwrap() + IncomingViewingSecretKey::from_repr(hash.into()).unwrap() } - pub fn generate_outgoing_viewing_secret_key(&self) -> Scalar { + pub fn generate_outgoing_viewing_secret_key(&self) -> OutgoingViewingSecretKey { let mut hasher = sha2::Sha256::new(); hasher.update("NSSA_keys"); - hasher.update(self.secret_spending_key); + hasher.update(self.0); hasher.update([3u8]); - hasher.update([0u8; 176]); + hasher.update([0u8; 22]); let hash = ::from(hasher.finalize_fixed()); - Scalar::from_repr(hash.into()).unwrap() + OutgoingViewingSecretKey::from_repr(hash.into()).unwrap() } pub fn produce_private_key_holder(&self) -> PrivateKeyHolder { PrivateKeyHolder { nullifier_secret_key: self.generate_nullifier_secret_key(), - incoming_viewing_secret_key: self.generate_incloming_viewing_secret_key(), + incoming_viewing_secret_key: self.generate_incoming_viewing_secret_key(), outgoing_viewing_secret_key: self.generate_outgoing_viewing_secret_key(), } } } impl PrivateKeyHolder { - pub fn generate_nullifier_public_key(&self) -> [u8; 32] { - let mut hasher = sha2::Sha256::new(); - - hasher.update("NSSA_keys"); - hasher.update(self.nullifier_secret_key); - hasher.update([7u8]); - hasher.update([0u8; 176]); - - ::from(hasher.finalize_fixed()) + pub fn generate_nullifier_public_key(&self) -> NullifierPublicKey { + (&self.nullifier_secret_key).into() } - pub fn generate_incoming_viewing_public_key(&self) -> AffinePoint { - (AffinePoint::GENERATOR * self.incoming_viewing_secret_key).into() + pub fn generate_incoming_viewing_public_key(&self) -> IncomingViewingPublicKey { + IncomingViewingPublicKey::from_scalar(self.incoming_viewing_secret_key) } } @@ -151,7 +144,7 @@ mod tests { let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); - let _ = top_secret_key_holder.generate_incloming_viewing_secret_key(); + let _ = top_secret_key_holder.generate_incoming_viewing_secret_key(); } #[test] diff --git a/key_protocol/src/key_management/types.rs b/key_protocol/src/key_management/types.rs deleted file mode 100644 index 8878f25..0000000 --- a/key_protocol/src/key_management/types.rs +++ /dev/null @@ -1,8 +0,0 @@ -use elliptic_curve::{ - consts::{B0, B1}, - generic_array::GenericArray, -}; -use sha2::digest::typenum::{UInt, UTerm}; - -pub type CipherText = Vec; -pub type Nonce = GenericArray, B1>, B0>, B0>>; diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 3163902..96f6530 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -14,6 +14,7 @@ secp256k1 = "0.31.1" rand = "0.8" borsh = "1.5.7" hex = "0.4.3" +k256 = "0.13.3" [dev-dependencies] test-program-methods = { path = "test_program_methods" } diff --git a/nssa/core/src/encryption/shared_key_derivation.rs b/nssa/core/src/encryption/shared_key_derivation.rs index c735105..6392d6a 100644 --- a/nssa/core/src/encryption/shared_key_derivation.rs +++ b/nssa/core/src/encryption/shared_key_derivation.rs @@ -14,7 +14,7 @@ use crate::SharedSecretKey; pub struct Secp256k1Point(pub(crate) Vec); impl Secp256k1Point { - pub fn from_scalar(value: [u8; 32]) -> Secp256k1Point { + pub fn from_scalar(value: Scalar) -> Secp256k1Point { let x_bytes: FieldBytes = value.into(); let x = Scalar::from_repr(x_bytes).unwrap(); @@ -26,7 +26,7 @@ impl Secp256k1Point { } } -pub type EphemeralSecretKey = [u8; 32]; +pub type EphemeralSecretKey = Scalar; pub type EphemeralPublicKey = Secp256k1Point; pub type IncomingViewingPublicKey = Secp256k1Point; impl From<&EphemeralSecretKey> for EphemeralPublicKey { @@ -36,8 +36,7 @@ impl From<&EphemeralSecretKey> for EphemeralPublicKey { } impl SharedSecretKey { - pub fn new(scalar: &[u8; 32], point: &Secp256k1Point) -> Self { - let scalar = Scalar::from_repr((*scalar).into()).unwrap(); + pub fn new(scalar: &Scalar, point: &Secp256k1Point) -> Self { let point: [u8; 33] = point.0.clone().try_into().unwrap(); let encoded = EncodedPoint::from_bytes(point).unwrap(); diff --git a/nssa/core/src/nullifier.rs b/nssa/core/src/nullifier.rs index d1410de..bc73d8c 100644 --- a/nssa/core/src/nullifier.rs +++ b/nssa/core/src/nullifier.rs @@ -7,6 +7,12 @@ use crate::Commitment; #[cfg_attr(any(feature = "host", test), derive(Debug, Clone, Hash))] pub struct NullifierPublicKey(pub(super) [u8; 32]); +impl AsRef<[u8]> for NullifierPublicKey { + fn as_ref(&self) -> &[u8] { + self.0.as_slice() + } +} + impl From<&NullifierSecretKey> for NullifierPublicKey { fn from(value: &NullifierSecretKey) -> Self { let mut bytes = Vec::new(); diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 1421f62..f350ccf 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -88,6 +88,7 @@ impl Proof { #[cfg(test)] mod tests { + use k256::{Scalar, elliptic_curve::PrimeField}; use nssa_core::{ Commitment, EncryptionScheme, Nullifier, account::{Account, AccountWithMetadata}, @@ -139,7 +140,7 @@ mod tests { let expected_sender_pre = sender.clone(); let recipient_keys = test_private_account_keys_1(); - let esk = [3; 32]; + let esk = Scalar::from_repr([3; 32].into()).unwrap(); let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk()); let (output, proof) = execute_and_prove( @@ -220,10 +221,10 @@ mod tests { Commitment::new(&recipient_keys.npk(), &expected_private_account_2), ]; - let esk_1 = [3; 32]; + let esk_1 = Scalar::from_repr([3; 32].into()).unwrap(); let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk()); - let esk_2 = [5; 32]; + let esk_2 = Scalar::from_repr([5; 32].into()).unwrap(); let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.ivk()); let (output, proof) = execute_and_prove( diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index 244a81f..9023e52 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -90,6 +90,7 @@ impl Message { #[cfg(test)] pub mod tests { + use k256::{Scalar, elliptic_curve::PrimeField}; use std::io::Cursor; use nssa_core::{ @@ -151,10 +152,10 @@ pub mod tests { #[test] fn test_encrypted_account_data_constructor() { let npk = NullifierPublicKey::from(&[1; 32]); - let ivk = IncomingViewingPublicKey::from(&[2; 32]); + let ivk = IncomingViewingPublicKey::from(&Scalar::from_repr([2; 32].into()).unwrap()); let account = Account::default(); let commitment = Commitment::new(&npk, &account); - let esk = [3; 32]; + let esk = Scalar::from_repr([3; 32].into()).unwrap(); let shared_secret = SharedSecretKey::new(&esk, &ivk); let epk = EphemeralPublicKey::from_scalar(esk); let ciphertext = EncryptionScheme::encrypt(&account, &shared_secret, &commitment, 2); diff --git a/nssa/src/state.rs b/nssa/src/state.rs index f980370..71e5f5e 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -227,6 +227,7 @@ pub mod tests { signature::PrivateKey, }; + use k256::{Scalar, elliptic_curve::PrimeField}; use nssa_core::{ Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, account::{Account, AccountWithMetadata, Nonce}, @@ -743,7 +744,7 @@ pub mod tests { pub struct TestPrivateKeys { pub nsk: NullifierSecretKey, - pub isk: [u8; 32], + pub isk: Scalar, } impl TestPrivateKeys { @@ -759,14 +760,14 @@ pub mod tests { pub fn test_private_account_keys_1() -> TestPrivateKeys { TestPrivateKeys { nsk: [13; 32], - isk: [31; 32], + isk: Scalar::from_repr([31; 32].into()).unwrap(), } } pub fn test_private_account_keys_2() -> TestPrivateKeys { TestPrivateKeys { nsk: [38; 32], - isk: [83; 32], + isk: Scalar::from_repr([83; 32].into()).unwrap(), } } @@ -788,7 +789,7 @@ pub mod tests { is_authorized: false, }; - let esk = [3; 32]; + let esk = Scalar::from_repr([3; 32].into()).unwrap(); let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk()); let epk = EphemeralPublicKey::from_scalar(esk); @@ -834,11 +835,11 @@ pub mod tests { is_authorized: false, }; - let esk_1 = [3; 32]; + let esk_1 = Scalar::from_repr([3; 32].into()).unwrap(); let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk()); let epk_1 = EphemeralPublicKey::from_scalar(esk_1); - let esk_2 = [3; 32]; + let esk_2 = Scalar::from_repr([3; 32].into()).unwrap(); let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.ivk()); let epk_2 = EphemeralPublicKey::from_scalar(esk_2); @@ -894,7 +895,7 @@ pub mod tests { is_authorized: false, }; - let esk = [3; 32]; + let esk = Scalar::from_repr([3; 32].into()).unwrap(); let shared_secret = SharedSecretKey::new(&esk, &sender_keys.ivk()); let epk = EphemeralPublicKey::from_scalar(esk);