diff --git a/Cargo.toml b/Cargo.toml index 5f2ee32..c21c9a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ ark-ff = "0.5.0" tiny-keccak = { version = "2.0.2", features = ["keccak"] } base64 = "0.22.1" bip39 = "2.2.0" +hmac-sha512 = "1.1.7" rocksdb = { version = "0.21.0", default-features = false, features = [ "snappy", diff --git a/key_protocol/Cargo.toml b/key_protocol/Cargo.toml index 8b9701a..d624505 100644 --- a/key_protocol/Cargo.toml +++ b/key_protocol/Cargo.toml @@ -16,6 +16,7 @@ hex.workspace = true aes-gcm.workspace = true lazy_static.workspace = true bip39.workspace = true +hmac-sha512.workspace = true [dependencies.common] path = "../common" diff --git a/key_protocol/src/key_management/constants_types.rs b/key_protocol/src/key_management/constants_types.rs deleted file mode 100644 index f4fca86..0000000 --- a/key_protocol/src/key_management/constants_types.rs +++ /dev/null @@ -1,22 +0,0 @@ -use elliptic_curve::{ - consts::{B0, B1}, - generic_array::GenericArray, -}; -use lazy_static::lazy_static; -use sha2::digest::typenum::{UInt, UTerm}; - -lazy_static! { - pub static ref NULLIFIER_SECRET_CONST: [u8; 32] = - hex::decode(std::env::var("NULLIFIER_SECRET_CONST").unwrap()) - .unwrap() - .try_into() - .unwrap(); - pub static ref VIEWING_SECRET_CONST: [u8; 32] = - hex::decode(std::env::var("VIEWING_SECRET_CONST").unwrap()) - .unwrap() - .try_into() - .unwrap(); -} - -pub type CipherText = Vec; -pub type Nonce = GenericArray, B1>, B0>, B0>>; diff --git a/key_protocol/src/key_management/ephemeral_key_holder.rs b/key_protocol/src/key_management/ephemeral_key_holder.rs index ecfb09e..6392678 100644 --- a/key_protocol/src/key_management/ephemeral_key_holder.rs +++ b/key_protocol/src/key_management/ephemeral_key_holder.rs @@ -5,7 +5,7 @@ use k256::{AffinePoint, FieldBytes, Scalar}; use log::info; use rand::{rngs::OsRng, RngCore}; -use super::constants_types::{CipherText, Nonce}; +use super::types::{CipherText, Nonce}; #[derive(Debug)] ///Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral public keys. Can produce shared secret for sender. diff --git a/key_protocol/src/key_management/mod.rs b/key_protocol/src/key_management/mod.rs index bc6d14c..fb75dff 100644 --- a/key_protocol/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -1,29 +1,32 @@ use std::collections::HashMap; use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; -use constants_types::{CipherText, Nonce}; +use common::merkle_tree_public::TreeHashType; +use elliptic_curve::group::GroupEncoding; use elliptic_curve::point::AffineCoordinates; use k256::AffinePoint; use log::info; -use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; +use secret_holders::{PrivateKeyHolder, SeedHolder, TopSecretKeyHolder}; use serde::{Deserialize, Serialize}; +use sha2::{digest::FixedOutput, Digest}; +use types::{CipherText, Nonce}; use crate::key_protocol_core::PublicKey; pub type PublicAccountSigningKey = [u8; 32]; -pub mod constants_types; 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, - pub utxo_secret_key_holder: UTXOSecretKeyHolder, + pub private_key_holder: PrivateKeyHolder, ///Map for all users accounts pub_account_signing_keys: HashMap, - pub nullifer_public_key: PublicKey, - pub viewing_public_key: PublicKey, + pub nullifer_public_key: [u8; 32], + pub incoming_viewing_public_key: PublicKey, } impl KeyChain { @@ -33,16 +36,16 @@ impl KeyChain { let seed_holder = SeedHolder::new_os_random(); let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); - let utxo_secret_key_holder = top_secret_key_holder.produce_utxo_secret_holder(); + let private_key_holder = top_secret_key_holder.produce_private_key_holder(); - let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); - let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); + 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, - utxo_secret_key_holder, + private_key_holder, nullifer_public_key, - viewing_public_key, + incoming_viewing_public_key, pub_account_signing_keys: HashMap::new(), } } @@ -53,20 +56,29 @@ impl KeyChain { let seed_holder = SeedHolder::new_os_random(); let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); - let utxo_secret_key_holder = top_secret_key_holder.produce_utxo_secret_holder(); + let private_key_holder = top_secret_key_holder.produce_private_key_holder(); - let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); - let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); + 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, - utxo_secret_key_holder, + private_key_holder, nullifer_public_key, - viewing_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()); + + ::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)); @@ -112,14 +124,18 @@ impl KeyChain { ); info!( "Nulifier secret key is {:?}", + hex::encode(serde_json::to_vec(&self.private_key_holder.nullifier_secret_key).unwrap()), + ); + info!( + "Viewing secret key is {:?}", hex::encode( - serde_json::to_vec(&self.utxo_secret_key_holder.nullifier_secret_key).unwrap() + serde_json::to_vec(&self.private_key_holder.incoming_viewing_secret_key).unwrap() ), ); info!( "Viewing secret key is {:?}", hex::encode( - serde_json::to_vec(&self.utxo_secret_key_holder.viewing_secret_key).unwrap() + serde_json::to_vec(&self.private_key_holder.outgoing_viewing_secret_key).unwrap() ), ); info!( @@ -128,7 +144,7 @@ impl KeyChain { ); info!( "Viewing public key is {:?}", - hex::encode(serde_json::to_vec(&self.viewing_public_key).unwrap()), + hex::encode(serde_json::to_vec(&self.incoming_viewing_public_key).unwrap()), ); } } @@ -139,12 +155,11 @@ mod tests { aead::{Aead, KeyInit, OsRng}, Aes256Gcm, }; - use constants_types::{CipherText, Nonce}; - use constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST}; 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; @@ -156,11 +171,9 @@ 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.nullifer_public_key.is_identity() - )); - assert!(!Into::::into( - address_key_holder.viewing_public_key.is_identity() + address_key_holder.incoming_viewing_public_key.is_identity() )); } @@ -211,20 +224,6 @@ mod tests { assert_eq!(decrypted_data, plaintext); } - #[test] - fn test_new_os_random_initialization() { - // Ensure that KeyChain is initialized correctly - let address_key_holder = KeyChain::new_os_random(); - - // Check that key holder fields are initialized with expected types and values - assert!(!Into::::into( - address_key_holder.nullifer_public_key.is_identity() - )); - assert!(!Into::::into( - address_key_holder.viewing_public_key.is_identity() - )); - } - #[test] fn test_calculate_shared_secret_with_identity_point() { let address_key_holder = KeyChain::new_os_random(); @@ -359,10 +358,10 @@ mod tests { let seed_holder = SeedHolder::new_os_random(); let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); - let utxo_secret_key_holder = top_secret_key_holder.produce_utxo_secret_holder(); + let utxo_secret_key_holder = top_secret_key_holder.produce_private_key_holder(); let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); - let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); + let viewing_public_key = utxo_secret_key_holder.generate_incoming_viewing_public_key(); let pub_account_signing_key = nssa::PrivateKey::new_os_random(); @@ -377,11 +376,6 @@ mod tests { "Group generator {:?}", hex::encode(serde_json::to_vec(&AffinePoint::GENERATOR).unwrap()) ); - println!( - "Nullifier constant {:?}", - hex::encode(*NULLIFIER_SECRET_CONST) - ); - println!("Viewing constatnt {:?}", hex::encode(*VIEWING_SECRET_CONST)); println!(); println!("======Holders======"); diff --git a/key_protocol/src/key_management/secret_holders.rs b/key_protocol/src/key_management/secret_holders.rs index 4fc1a63..a5258b5 100644 --- a/key_protocol/src/key_management/secret_holders.rs +++ b/key_protocol/src/key_management/secret_holders.rs @@ -1,31 +1,31 @@ use bip39::Mnemonic; use common::merkle_tree_public::TreeHashType; use elliptic_curve::PrimeField; -use k256::{AffinePoint, FieldBytes, Scalar}; +use k256::{AffinePoint, Scalar}; use rand::{rngs::OsRng, RngCore}; use serde::{Deserialize, Serialize}; use sha2::{digest::FixedOutput, Digest}; -use super::constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST}; - #[derive(Debug)] ///Seed holder. Non-clonable to ensure that different holders use different seeds. /// Produces `TopSecretKeyHolder` objects. pub struct SeedHolder { - seed: Scalar, + //ToDo: Needs to be vec as serde derives is not implemented for [u8; 64] + pub(crate) seed: Vec, } #[derive(Serialize, Deserialize, Debug, Clone)] -///Secret spending key holder. Produces `UTXOSecretKeyHolder` objects. +///Secret spending key holder. Produces `PrivateKeyHolder` objects. pub struct TopSecretKeyHolder { - pub secret_spending_key: Scalar, + pub(crate) secret_spending_key: [u8; 32], } #[derive(Serialize, Deserialize, Debug, Clone)] -///Nullifier secret key and viewing secret key holder. Produces public keys. Can produce address. Can produce shared secret for recepient. -pub struct UTXOSecretKeyHolder { - pub nullifier_secret_key: Scalar, - pub viewing_secret_key: Scalar, +///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, } impl SeedHolder { @@ -34,73 +34,134 @@ impl SeedHolder { OsRng.fill_bytes(&mut enthopy_bytes); let mnemonic = Mnemonic::from_entropy(&enthopy_bytes).unwrap(); - let seed = mnemonic.to_seed(""); - - let field_bytes = FieldBytes::from_slice(&seed); + let seed_wide = mnemonic.to_seed("mnemonic"); Self { - seed: Scalar::from_repr(*field_bytes).unwrap(), + seed: seed_wide.to_vec(), } } pub fn generate_secret_spending_key_hash(&self) -> TreeHashType { - let mut hasher = sha2::Sha256::new(); + let mut hash = hmac_sha512::HMAC::mac(&self.seed, "NSSA_seed"); - hasher.update(self.seed.to_bytes()); + for _ in 1..2048 { + hash = hmac_sha512::HMAC::mac(&hash, "NSSA_seed"); + } - ::from(hasher.finalize_fixed()) - } - - pub fn generate_secret_spending_key_scalar(&self) -> Scalar { - let hash = self.generate_secret_spending_key_hash(); - - Scalar::from_repr(hash.into()).unwrap() + //Safe unwrap + *hash.first_chunk::<32>().unwrap() } pub fn produce_top_secret_key_holder(&self) -> TopSecretKeyHolder { TopSecretKeyHolder { - secret_spending_key: self.generate_secret_spending_key_scalar(), + secret_spending_key: self.generate_secret_spending_key_hash(), } } } impl TopSecretKeyHolder { - pub fn generate_nullifier_secret_key(&self) -> Scalar { + pub fn generate_nullifier_secret_key(&self) -> [u8; 32] { let mut hasher = sha2::Sha256::new(); - hasher.update(self.secret_spending_key.to_bytes()); - hasher.update(*NULLIFIER_SECRET_CONST); + hasher.update("NSSA_keys"); + hasher.update(&self.secret_spending_key); + hasher.update([1u8]); + hasher.update([0u8; 176]); + + ::from(hasher.finalize_fixed()) + } + + pub fn generate_incloming_viewing_secret_key(&self) -> Scalar { + let mut hasher = sha2::Sha256::new(); + + hasher.update("NSSA_keys"); + hasher.update(&self.secret_spending_key); + hasher.update([2u8]); + hasher.update([0u8; 176]); let hash = ::from(hasher.finalize_fixed()); Scalar::from_repr(hash.into()).unwrap() } - pub fn generate_viewing_secret_key(&self) -> Scalar { + pub fn generate_outgoing_viewing_secret_key(&self) -> Scalar { let mut hasher = sha2::Sha256::new(); - hasher.update(self.secret_spending_key.to_bytes()); - hasher.update(*VIEWING_SECRET_CONST); + hasher.update("NSSA_keys"); + hasher.update(&self.secret_spending_key); + hasher.update([3u8]); + hasher.update([0u8; 176]); let hash = ::from(hasher.finalize_fixed()); Scalar::from_repr(hash.into()).unwrap() } - pub fn produce_utxo_secret_holder(&self) -> UTXOSecretKeyHolder { - UTXOSecretKeyHolder { + pub fn produce_private_key_holder(&self) -> PrivateKeyHolder { + PrivateKeyHolder { nullifier_secret_key: self.generate_nullifier_secret_key(), - viewing_secret_key: self.generate_viewing_secret_key(), + incoming_viewing_secret_key: self.generate_incloming_viewing_secret_key(), + outgoing_viewing_secret_key: self.generate_outgoing_viewing_secret_key(), } } } -impl UTXOSecretKeyHolder { - pub fn generate_nullifier_public_key(&self) -> AffinePoint { - (AffinePoint::GENERATOR * self.nullifier_secret_key).into() +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_viewing_public_key(&self) -> AffinePoint { - (AffinePoint::GENERATOR * self.viewing_secret_key).into() + pub fn generate_incoming_viewing_public_key(&self) -> AffinePoint { + (AffinePoint::GENERATOR * self.incoming_viewing_secret_key).into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn seed_generation_test() { + let seed_holder = SeedHolder::new_os_random(); + + assert_eq!(seed_holder.seed.len(), 64); + } + + #[test] + fn ssk_generation_test() { + let seed_holder = SeedHolder::new_os_random(); + + assert_eq!(seed_holder.seed.len(), 64); + + let _ = seed_holder.generate_secret_spending_key_hash(); + } + + #[test] + fn ivs_generation_test() { + let seed_holder = SeedHolder::new_os_random(); + + assert_eq!(seed_holder.seed.len(), 64); + + let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); + + let _ = top_secret_key_holder.generate_incloming_viewing_secret_key(); + } + + #[test] + fn ovs_generation_test() { + let seed_holder = SeedHolder::new_os_random(); + + assert_eq!(seed_holder.seed.len(), 64); + + let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); + + let _ = top_secret_key_holder.generate_outgoing_viewing_secret_key(); } } diff --git a/key_protocol/src/key_management/types.rs b/key_protocol/src/key_management/types.rs new file mode 100644 index 0000000..8878f25 --- /dev/null +++ b/key_protocol/src/key_management/types.rs @@ -0,0 +1,8 @@ +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>>;