fix: keys corect generatin

This commit is contained in:
Oleksandr Pravdyvyi 2025-09-05 14:47:58 +03:00
parent ebe616247f
commit 324f477b63
No known key found for this signature in database
GPG Key ID: 9F8955C63C443871
7 changed files with 150 additions and 107 deletions

View File

@ -42,6 +42,7 @@ ark-ff = "0.5.0"
tiny-keccak = { version = "2.0.2", features = ["keccak"] } tiny-keccak = { version = "2.0.2", features = ["keccak"] }
base64 = "0.22.1" base64 = "0.22.1"
bip39 = "2.2.0" bip39 = "2.2.0"
hmac-sha512 = "1.1.7"
rocksdb = { version = "0.21.0", default-features = false, features = [ rocksdb = { version = "0.21.0", default-features = false, features = [
"snappy", "snappy",

View File

@ -16,6 +16,7 @@ hex.workspace = true
aes-gcm.workspace = true aes-gcm.workspace = true
lazy_static.workspace = true lazy_static.workspace = true
bip39.workspace = true bip39.workspace = true
hmac-sha512.workspace = true
[dependencies.common] [dependencies.common]
path = "../common" path = "../common"

View File

@ -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<u8>;
pub type Nonce = GenericArray<u8, UInt<UInt<UInt<UInt<UTerm, B1>, B1>, B0>, B0>>;

View File

@ -5,7 +5,7 @@ use k256::{AffinePoint, FieldBytes, Scalar};
use log::info; use log::info;
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::OsRng, RngCore};
use super::constants_types::{CipherText, Nonce}; use super::types::{CipherText, Nonce};
#[derive(Debug)] #[derive(Debug)]
///Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral public keys. Can produce shared secret for sender. ///Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral public keys. Can produce shared secret for sender.

View File

@ -1,29 +1,32 @@
use std::collections::HashMap; use std::collections::HashMap;
use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; 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 elliptic_curve::point::AffineCoordinates;
use k256::AffinePoint; use k256::AffinePoint;
use log::info; use log::info;
use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; use secret_holders::{PrivateKeyHolder, SeedHolder, TopSecretKeyHolder};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{digest::FixedOutput, Digest};
use types::{CipherText, Nonce};
use crate::key_protocol_core::PublicKey; use crate::key_protocol_core::PublicKey;
pub type PublicAccountSigningKey = [u8; 32]; pub type PublicAccountSigningKey = [u8; 32];
pub mod constants_types;
pub mod ephemeral_key_holder; pub mod ephemeral_key_holder;
pub mod secret_holders; pub mod secret_holders;
pub mod types;
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
///Entrypoint to key management ///Entrypoint to key management
pub struct KeyChain { pub struct KeyChain {
top_secret_key_holder: TopSecretKeyHolder, top_secret_key_holder: TopSecretKeyHolder,
pub utxo_secret_key_holder: UTXOSecretKeyHolder, pub private_key_holder: PrivateKeyHolder,
///Map for all users accounts ///Map for all users accounts
pub_account_signing_keys: HashMap<nssa::Address, nssa::PrivateKey>, pub_account_signing_keys: HashMap<nssa::Address, nssa::PrivateKey>,
pub nullifer_public_key: PublicKey, pub nullifer_public_key: [u8; 32],
pub viewing_public_key: PublicKey, pub incoming_viewing_public_key: PublicKey,
} }
impl KeyChain { impl KeyChain {
@ -33,16 +36,16 @@ impl KeyChain {
let seed_holder = SeedHolder::new_os_random(); let seed_holder = SeedHolder::new_os_random();
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); 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 nullifer_public_key = private_key_holder.generate_nullifier_public_key();
let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); let incoming_viewing_public_key = private_key_holder.generate_incoming_viewing_public_key();
Self { Self {
top_secret_key_holder, top_secret_key_holder,
utxo_secret_key_holder, private_key_holder,
nullifer_public_key, nullifer_public_key,
viewing_public_key, incoming_viewing_public_key,
pub_account_signing_keys: HashMap::new(), pub_account_signing_keys: HashMap::new(),
} }
} }
@ -53,20 +56,29 @@ impl KeyChain {
let seed_holder = SeedHolder::new_os_random(); let seed_holder = SeedHolder::new_os_random();
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); 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 nullifer_public_key = private_key_holder.generate_nullifier_public_key();
let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); let incoming_viewing_public_key = private_key_holder.generate_incoming_viewing_public_key();
Self { Self {
top_secret_key_holder, top_secret_key_holder,
utxo_secret_key_holder, private_key_holder,
nullifer_public_key, nullifer_public_key,
viewing_public_key, incoming_viewing_public_key,
pub_account_signing_keys: accounts, 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());
<TreeHashType>::from(hasher.finalize_fixed())
}
pub fn generate_new_private_key(&mut self) -> nssa::Address { pub fn generate_new_private_key(&mut self) -> nssa::Address {
let private_key = nssa::PrivateKey::new_os_random(); let private_key = nssa::PrivateKey::new_os_random();
let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key)); let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key));
@ -112,14 +124,18 @@ impl KeyChain {
); );
info!( info!(
"Nulifier secret key is {:?}", "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( 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!( info!(
"Viewing secret key is {:?}", "Viewing secret key is {:?}",
hex::encode( 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!( info!(
@ -128,7 +144,7 @@ impl KeyChain {
); );
info!( info!(
"Viewing public key is {:?}", "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}, aead::{Aead, KeyInit, OsRng},
Aes256Gcm, Aes256Gcm,
}; };
use constants_types::{CipherText, Nonce};
use constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST};
use elliptic_curve::ff::Field; use elliptic_curve::ff::Field;
use elliptic_curve::group::prime::PrimeCurveAffine; use elliptic_curve::group::prime::PrimeCurveAffine;
use elliptic_curve::point::AffineCoordinates; use elliptic_curve::point::AffineCoordinates;
use k256::{AffinePoint, ProjectivePoint, Scalar}; use k256::{AffinePoint, ProjectivePoint, Scalar};
use types::{CipherText, Nonce};
use crate::key_management::ephemeral_key_holder::EphemeralKeyHolder; use crate::key_management::ephemeral_key_holder::EphemeralKeyHolder;
@ -156,11 +171,9 @@ mod tests {
let address_key_holder = KeyChain::new_os_random(); let address_key_holder = KeyChain::new_os_random();
// Check that key holder fields are initialized with expected types // Check that key holder fields are initialized with expected types
assert_ne!(address_key_holder.nullifer_public_key, [0u8; 32]);
assert!(!Into::<bool>::into( assert!(!Into::<bool>::into(
address_key_holder.nullifer_public_key.is_identity() address_key_holder.incoming_viewing_public_key.is_identity()
));
assert!(!Into::<bool>::into(
address_key_holder.viewing_public_key.is_identity()
)); ));
} }
@ -211,20 +224,6 @@ mod tests {
assert_eq!(decrypted_data, plaintext); 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::<bool>::into(
address_key_holder.nullifer_public_key.is_identity()
));
assert!(!Into::<bool>::into(
address_key_holder.viewing_public_key.is_identity()
));
}
#[test] #[test]
fn test_calculate_shared_secret_with_identity_point() { fn test_calculate_shared_secret_with_identity_point() {
let address_key_holder = KeyChain::new_os_random(); let address_key_holder = KeyChain::new_os_random();
@ -359,10 +358,10 @@ mod tests {
let seed_holder = SeedHolder::new_os_random(); let seed_holder = SeedHolder::new_os_random();
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); 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 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(); let pub_account_signing_key = nssa::PrivateKey::new_os_random();
@ -377,11 +376,6 @@ mod tests {
"Group generator {:?}", "Group generator {:?}",
hex::encode(serde_json::to_vec(&AffinePoint::GENERATOR).unwrap()) 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!();
println!("======Holders======"); println!("======Holders======");

View File

@ -1,31 +1,31 @@
use bip39::Mnemonic; use bip39::Mnemonic;
use common::merkle_tree_public::TreeHashType; use common::merkle_tree_public::TreeHashType;
use elliptic_curve::PrimeField; use elliptic_curve::PrimeField;
use k256::{AffinePoint, FieldBytes, Scalar}; use k256::{AffinePoint, Scalar};
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::OsRng, RngCore};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{digest::FixedOutput, Digest}; use sha2::{digest::FixedOutput, Digest};
use super::constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST};
#[derive(Debug)] #[derive(Debug)]
///Seed holder. Non-clonable to ensure that different holders use different seeds. ///Seed holder. Non-clonable to ensure that different holders use different seeds.
/// Produces `TopSecretKeyHolder` objects. /// Produces `TopSecretKeyHolder` objects.
pub struct SeedHolder { pub struct SeedHolder {
seed: Scalar, //ToDo: Needs to be vec as serde derives is not implemented for [u8; 64]
pub(crate) seed: Vec<u8>,
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
///Secret spending key holder. Produces `UTXOSecretKeyHolder` objects. ///Secret spending key holder. Produces `PrivateKeyHolder` objects.
pub struct TopSecretKeyHolder { pub struct TopSecretKeyHolder {
pub secret_spending_key: Scalar, pub(crate) secret_spending_key: [u8; 32],
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[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. ///Private key holder. Produces public keys. Can produce address. Can produce shared secret for recepient.
pub struct UTXOSecretKeyHolder { pub struct PrivateKeyHolder {
pub nullifier_secret_key: Scalar, pub(crate) nullifier_secret_key: [u8; 32],
pub viewing_secret_key: Scalar, pub(crate) incoming_viewing_secret_key: Scalar,
pub(crate) outgoing_viewing_secret_key: Scalar,
} }
impl SeedHolder { impl SeedHolder {
@ -34,73 +34,134 @@ impl SeedHolder {
OsRng.fill_bytes(&mut enthopy_bytes); OsRng.fill_bytes(&mut enthopy_bytes);
let mnemonic = Mnemonic::from_entropy(&enthopy_bytes).unwrap(); let mnemonic = Mnemonic::from_entropy(&enthopy_bytes).unwrap();
let seed = mnemonic.to_seed(""); let seed_wide = mnemonic.to_seed("mnemonic");
let field_bytes = FieldBytes::from_slice(&seed);
Self { Self {
seed: Scalar::from_repr(*field_bytes).unwrap(), seed: seed_wide.to_vec(),
} }
} }
pub fn generate_secret_spending_key_hash(&self) -> TreeHashType { 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");
}
<TreeHashType>::from(hasher.finalize_fixed()) //Safe unwrap
} *hash.first_chunk::<32>().unwrap()
pub fn generate_secret_spending_key_scalar(&self) -> Scalar {
let hash = self.generate_secret_spending_key_hash();
Scalar::from_repr(hash.into()).unwrap()
} }
pub fn produce_top_secret_key_holder(&self) -> TopSecretKeyHolder { pub fn produce_top_secret_key_holder(&self) -> TopSecretKeyHolder {
TopSecretKeyHolder { TopSecretKeyHolder {
secret_spending_key: self.generate_secret_spending_key_scalar(), secret_spending_key: self.generate_secret_spending_key_hash(),
} }
} }
} }
impl TopSecretKeyHolder { 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(); let mut hasher = sha2::Sha256::new();
hasher.update(self.secret_spending_key.to_bytes()); hasher.update("NSSA_keys");
hasher.update(*NULLIFIER_SECRET_CONST); hasher.update(&self.secret_spending_key);
hasher.update([1u8]);
hasher.update([0u8; 176]);
<TreeHashType>::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 = <TreeHashType>::from(hasher.finalize_fixed()); let hash = <TreeHashType>::from(hasher.finalize_fixed());
Scalar::from_repr(hash.into()).unwrap() 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(); let mut hasher = sha2::Sha256::new();
hasher.update(self.secret_spending_key.to_bytes()); hasher.update("NSSA_keys");
hasher.update(*VIEWING_SECRET_CONST); hasher.update(&self.secret_spending_key);
hasher.update([3u8]);
hasher.update([0u8; 176]);
let hash = <TreeHashType>::from(hasher.finalize_fixed()); let hash = <TreeHashType>::from(hasher.finalize_fixed());
Scalar::from_repr(hash.into()).unwrap() Scalar::from_repr(hash.into()).unwrap()
} }
pub fn produce_utxo_secret_holder(&self) -> UTXOSecretKeyHolder { pub fn produce_private_key_holder(&self) -> PrivateKeyHolder {
UTXOSecretKeyHolder { PrivateKeyHolder {
nullifier_secret_key: self.generate_nullifier_secret_key(), 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 { impl PrivateKeyHolder {
pub fn generate_nullifier_public_key(&self) -> AffinePoint { pub fn generate_nullifier_public_key(&self) -> [u8; 32] {
(AffinePoint::GENERATOR * self.nullifier_secret_key).into() let mut hasher = sha2::Sha256::new();
hasher.update("NSSA_keys");
hasher.update(&self.nullifier_secret_key);
hasher.update([7u8]);
hasher.update([0u8; 176]);
<TreeHashType>::from(hasher.finalize_fixed())
} }
pub fn generate_viewing_public_key(&self) -> AffinePoint { pub fn generate_incoming_viewing_public_key(&self) -> AffinePoint {
(AffinePoint::GENERATOR * self.viewing_secret_key).into() (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();
} }
} }

View File

@ -0,0 +1,8 @@
use elliptic_curve::{
consts::{B0, B1},
generic_array::GenericArray,
};
use sha2::digest::typenum::{UInt, UTerm};
pub type CipherText = Vec<u8>;
pub type Nonce = GenericArray<u8, UInt<UInt<UInt<UInt<UTerm, B1>, B1>, B0>, B0>>;