diff --git a/key_protocol/src/key_management/ephemeral_key_holder.rs b/key_protocol/src/key_management/ephemeral_key_holder.rs index 6392678..108a41e 100644 --- a/key_protocol/src/key_management/ephemeral_key_holder.rs +++ b/key_protocol/src/key_management/ephemeral_key_holder.rs @@ -1,11 +1,7 @@ -use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, KeyInit}; -use elliptic_curve::point::AffineCoordinates; use elliptic_curve::PrimeField; -use k256::{AffinePoint, FieldBytes, Scalar}; +use k256::{AffinePoint, Scalar}; use log::info; -use rand::{rngs::OsRng, RngCore}; - -use super::types::{CipherText, Nonce}; +use sha2::Digest; #[derive(Debug)] ///Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral public keys. Can produce shared secret for sender. @@ -14,13 +10,24 @@ pub struct EphemeralKeyHolder { } impl EphemeralKeyHolder { - pub fn new_os_random() -> Self { - let mut bytes = FieldBytes::default(); + pub fn new( + receiver_nullifier_public_key: [u8; 32], + sender_outgoing_viewing_secret_key: Scalar, + 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]); - OsRng.fill_bytes(&mut bytes); + let hash_recepient = hasher.finalize(); + + let mut hasher = sha2::Sha256::new(); + hasher.update(sender_outgoing_viewing_secret_key.to_bytes()); + hasher.update(hash_recepient); Self { - ephemeral_secret_key: Scalar::from_repr(bytes).unwrap(), + ephemeral_secret_key: Scalar::from_repr(hasher.finalize()).unwrap(), } } @@ -30,21 +37,9 @@ impl EphemeralKeyHolder { pub fn calculate_shared_secret_sender( &self, - viewing_public_key_receiver: AffinePoint, - ) -> AffinePoint { - (viewing_public_key_receiver * self.ephemeral_secret_key).into() - } - - pub fn encrypt_data( - &self, - viewing_public_key_receiver: AffinePoint, - data: &[u8], - ) -> (CipherText, Nonce) { - let shared_secret = self.calculate_shared_secret_sender(viewing_public_key_receiver); - let cipher = Aes256Gcm::new(&shared_secret.x()); - let nonce = Aes256Gcm::generate_nonce(&mut OsRng); - - (cipher.encrypt(&nonce, data).unwrap(), nonce) + receiver_incoming_viewing_public_key: Scalar, + ) -> Scalar { + receiver_incoming_viewing_public_key * self.ephemeral_secret_key } pub fn log(&self) { diff --git a/key_protocol/src/key_management/mod.rs b/key_protocol/src/key_management/mod.rs index fb75dff..d3a4b08 100644 --- a/key_protocol/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; use common::merkle_tree_public::TreeHashType; use elliptic_curve::group::GroupEncoding; @@ -23,8 +21,6 @@ pub mod types; pub struct KeyChain { top_secret_key_holder: TopSecretKeyHolder, pub private_key_holder: PrivateKeyHolder, - ///Map for all users accounts - pub_account_signing_keys: HashMap, pub nullifer_public_key: [u8; 32], pub incoming_viewing_public_key: PublicKey, } @@ -46,61 +42,27 @@ impl KeyChain { private_key_holder, nullifer_public_key, incoming_viewing_public_key, - pub_account_signing_keys: HashMap::new(), - } - } - - pub fn new_os_random_with_accounts(accounts: HashMap) -> Self { - //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 private_key_holder = top_secret_key_holder.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, - private_key_holder, - nullifer_public_key, - incoming_viewing_public_key, - pub_account_signing_keys: accounts, } } pub fn produce_user_address(&self) -> [u8; 32] { let mut hasher = sha2::Sha256::new(); - hasher.update(&self.nullifer_public_key); - hasher.update(&self.incoming_viewing_public_key.to_bytes()); + hasher.update(self.nullifer_public_key); + hasher.update(self.incoming_viewing_public_key.to_bytes()); ::from(hasher.finalize_fixed()) } - pub fn generate_new_private_key(&mut self) -> nssa::Address { - let private_key = nssa::PrivateKey::new_os_random(); - let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key)); - - self.pub_account_signing_keys.insert(address, private_key); - - address - } - - /// Returns the signing key for public transaction signatures - pub fn get_pub_account_signing_key( - &self, - address: &nssa::Address, - ) -> Option<&nssa::PrivateKey> { - self.pub_account_signing_keys.get(address) - } - pub fn calculate_shared_secret_receiver( &self, ephemeral_public_key_sender: AffinePoint, ) -> AffinePoint { - (ephemeral_public_key_sender * self.utxo_secret_key_holder.viewing_secret_key).into() + (ephemeral_public_key_sender + * self + .top_secret_key_holder + .generate_incloming_viewing_secret_key()) + .into() } pub fn decrypt_data( @@ -197,9 +159,19 @@ mod tests { 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_os_random().generate_ephemeral_public_key(); + 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); @@ -340,19 +312,6 @@ mod tests { assert_eq!(decrypted_data, plaintext); } - #[test] - fn test_get_public_account_signing_key() { - let mut address_key_holder = KeyChain::new_os_random(); - - let address = address_key_holder.generate_new_private_key(); - - let is_private_key_generated = address_key_holder - .get_pub_account_signing_key(&address) - .is_some(); - - assert!(is_private_key_generated); - } - #[test] fn key_generation_test() { let seed_holder = SeedHolder::new_os_random(); diff --git a/key_protocol/src/key_management/secret_holders.rs b/key_protocol/src/key_management/secret_holders.rs index a5258b5..5180efb 100644 --- a/key_protocol/src/key_management/secret_holders.rs +++ b/key_protocol/src/key_management/secret_holders.rs @@ -45,7 +45,7 @@ impl SeedHolder { let mut hash = hmac_sha512::HMAC::mac(&self.seed, "NSSA_seed"); for _ in 1..2048 { - hash = hmac_sha512::HMAC::mac(&hash, "NSSA_seed"); + hash = hmac_sha512::HMAC::mac(hash, "NSSA_seed"); } //Safe unwrap @@ -64,7 +64,7 @@ impl TopSecretKeyHolder { let mut hasher = sha2::Sha256::new(); hasher.update("NSSA_keys"); - hasher.update(&self.secret_spending_key); + hasher.update(self.secret_spending_key); hasher.update([1u8]); hasher.update([0u8; 176]); @@ -75,7 +75,7 @@ impl TopSecretKeyHolder { let mut hasher = sha2::Sha256::new(); hasher.update("NSSA_keys"); - hasher.update(&self.secret_spending_key); + hasher.update(self.secret_spending_key); hasher.update([2u8]); hasher.update([0u8; 176]); @@ -88,7 +88,7 @@ impl TopSecretKeyHolder { let mut hasher = sha2::Sha256::new(); hasher.update("NSSA_keys"); - hasher.update(&self.secret_spending_key); + hasher.update(self.secret_spending_key); hasher.update([3u8]); hasher.update([0u8; 176]); @@ -111,7 +111,7 @@ impl PrivateKeyHolder { let mut hasher = sha2::Sha256::new(); hasher.update("NSSA_keys"); - hasher.update(&self.nullifier_secret_key); + hasher.update(self.nullifier_secret_key); hasher.update([7u8]); hasher.update([0u8; 176]); diff --git a/key_protocol/src/key_protocol_core/mod.rs b/key_protocol/src/key_protocol_core/mod.rs index 67cb52c..44d8ff6 100644 --- a/key_protocol/src/key_protocol_core/mod.rs +++ b/key_protocol/src/key_protocol_core/mod.rs @@ -10,17 +10,14 @@ pub type PublicKey = AffinePoint; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct NSSAUserData { - pub key_holder: KeyChain, + ///Map for all user public accounts + pub_account_signing_keys: HashMap, + ///Map for all user private accounts + user_private_accounts: HashMap, } impl NSSAUserData { - pub fn new() -> Self { - let key_holder = KeyChain::new_os_random(); - - Self { key_holder } - } - - fn valid_key_transaction_pairing_check( + fn valid_public_key_transaction_pairing_check( accounts_keys_map: &HashMap, ) -> bool { let mut check_res = true; @@ -32,30 +29,78 @@ impl NSSAUserData { check_res } + fn valid_private_key_transaction_pairing_check( + accounts_keys_map: &HashMap, + ) -> bool { + let mut check_res = true; + for (addr, key) in accounts_keys_map { + if nssa::Address::new(key.produce_user_address()) != *addr { + check_res = false; + } + } + check_res + } + pub fn new_with_accounts( accounts_keys: HashMap, + accounts_key_chains: HashMap, ) -> Result { - if !Self::valid_key_transaction_pairing_check(&accounts_keys) { + if !Self::valid_public_key_transaction_pairing_check(&accounts_keys) { anyhow::bail!("Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"); } - let key_holder = KeyChain::new_os_random_with_accounts(accounts_keys); + if !Self::valid_private_key_transaction_pairing_check(&accounts_key_chains) { + anyhow::bail!("Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"); + } - Ok(Self { key_holder }) + Ok(Self { + pub_account_signing_keys: accounts_keys, + user_private_accounts: accounts_key_chains, + }) } - pub fn generate_new_account(&mut self) -> nssa::Address { - self.key_holder.generate_new_private_key() + /// Generated new private key for public transaction signatures + /// + /// Returns the address of new account + pub fn generate_new_public_transaction_private_key(&mut self) -> nssa::Address { + let private_key = nssa::PrivateKey::new_os_random(); + let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key)); + + self.pub_account_signing_keys.insert(address, private_key); + + address } - pub fn get_account_signing_key(&self, address: &nssa::Address) -> Option<&nssa::PrivateKey> { - self.key_holder.get_pub_account_signing_key(address) + /// Returns the signing key for public transaction signatures + pub fn get_pub_account_signing_key( + &self, + address: &nssa::Address, + ) -> Option<&nssa::PrivateKey> { + self.pub_account_signing_keys.get(address) + } + + /// Generated new private key for privacy preserving transactions + /// + /// Returns the address of new account + pub fn generate_new_privacy_preserving_transaction_key_chain(&mut self) -> nssa::Address { + let key_chain = KeyChain::new_os_random(); + let address = nssa::Address::new(key_chain.produce_user_address()); + + self.user_private_accounts.insert(address, key_chain); + + address + } + + /// Returns the signing key for public transaction signatures + pub fn get_private_account_key_chain(&self, address: &nssa::Address) -> Option<&KeyChain> { + self.user_private_accounts.get(address) } } impl Default for NSSAUserData { fn default() -> Self { - Self::new() + //Safe unwrap as maps are empty + Self::new_with_accounts(HashMap::default(), HashMap::default()).unwrap() } } @@ -65,8 +110,19 @@ mod tests { #[test] fn test_new_account() { - let mut user_data = NSSAUserData::new(); + let mut user_data = NSSAUserData::default(); - let _addr = user_data.generate_new_account(); + let addr_pub = user_data.generate_new_public_transaction_private_key(); + let addr_private = user_data.generate_new_privacy_preserving_transaction_key_chain(); + + let is_private_key_generated = user_data.get_pub_account_signing_key(&addr_pub).is_some(); + + assert!(is_private_key_generated); + + let is_key_chain_generated = user_data + .get_private_account_key_chain(&addr_private) + .is_some(); + + assert!(is_key_chain_generated); } } diff --git a/wallet/src/chain_storage/mod.rs b/wallet/src/chain_storage/mod.rs index 7dfb8a2..e68b4a8 100644 --- a/wallet/src/chain_storage/mod.rs +++ b/wallet/src/chain_storage/mod.rs @@ -24,7 +24,7 @@ impl WalletChainStore { let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); Ok(Self { - user_data: NSSAUserData::new_with_accounts(accounts_keys)?, + user_data: NSSAUserData::new_with_accounts(accounts_keys, HashMap::new())?, utxo_commitments_store, wallet_config: config, }) diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index e2213dc..1cbbb94 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -41,7 +41,9 @@ impl WalletCore { } pub fn create_new_account(&mut self) -> Address { - self.storage.user_data.generate_new_account() + self.storage + .user_data + .generate_new_public_transaction_private_key() } pub fn search_for_initial_account(&self, acc_addr: Address) -> Option { @@ -75,7 +77,7 @@ impl WalletCore { ) .unwrap(); - let signing_key = self.storage.user_data.get_account_signing_key(&from); + let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); if let Some(signing_key) = signing_key { let witness_set =