2025-09-15 14:04:49 +03:00
|
|
|
use nssa_core::{
|
|
|
|
|
NullifierPublicKey, SharedSecretKey,
|
2026-01-21 17:27:23 -05:00
|
|
|
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
2025-09-15 14:04:49 +03:00
|
|
|
};
|
|
|
|
|
use secret_holders::{PrivateKeyHolder, SecretSpendingKey, SeedHolder};
|
2025-05-30 15:20:29 -04:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-10-25 09:41:43 +03:00
|
|
|
|
2025-07-16 10:04:23 -03:00
|
|
|
pub type PublicAccountSigningKey = [u8; 32];
|
2024-11-25 07:26:16 +02:00
|
|
|
|
2024-10-30 12:32:36 +02:00
|
|
|
pub mod ephemeral_key_holder;
|
2025-11-05 15:15:29 +02:00
|
|
|
pub mod key_tree;
|
2024-10-30 12:32:36 +02:00
|
|
|
pub mod secret_holders;
|
2024-10-25 09:41:43 +03:00
|
|
|
|
2025-07-29 14:20:03 +03:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
2025-11-26 00:27:20 +03:00
|
|
|
/// Entrypoint to key management
|
2025-08-15 14:27:36 +03:00
|
|
|
pub struct KeyChain {
|
2025-11-10 16:29:33 +02:00
|
|
|
pub secret_spending_key: SecretSpendingKey,
|
2025-09-05 14:47:58 +03:00
|
|
|
pub private_key_holder: PrivateKeyHolder,
|
2025-09-15 14:04:49 +03:00
|
|
|
pub nullifer_public_key: NullifierPublicKey,
|
2026-01-21 17:27:23 -05:00
|
|
|
pub viewing_public_key: ViewingPublicKey,
|
2024-10-25 09:41:43 +03:00
|
|
|
}
|
|
|
|
|
|
2025-08-15 14:27:36 +03:00
|
|
|
impl KeyChain {
|
2024-10-25 09:41:43 +03:00
|
|
|
pub fn new_os_random() -> Self {
|
2025-11-26 00:27:20 +03:00
|
|
|
// Currently dropping SeedHolder at the end of initialization.
|
|
|
|
|
// Now entirely sure if we need it in the future.
|
2024-10-25 09:41:43 +03:00
|
|
|
let seed_holder = SeedHolder::new_os_random();
|
2025-09-15 14:04:49 +03:00
|
|
|
let secret_spending_key = seed_holder.produce_top_secret_key_holder();
|
2024-10-25 09:41:43 +03:00
|
|
|
|
2026-01-27 16:00:42 -05:00
|
|
|
let private_key_holder = secret_spending_key.produce_private_key_holder(None);
|
2024-10-25 09:41:43 +03:00
|
|
|
|
2025-09-05 14:47:58 +03:00
|
|
|
let nullifer_public_key = private_key_holder.generate_nullifier_public_key();
|
2026-01-21 17:27:23 -05:00
|
|
|
let viewing_public_key = private_key_holder.generate_viewing_public_key();
|
2025-11-04 16:09:04 +02:00
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
secret_spending_key,
|
|
|
|
|
private_key_holder,
|
|
|
|
|
nullifer_public_key,
|
2026-01-21 17:27:23 -05:00
|
|
|
viewing_public_key,
|
2025-11-04 16:09:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn new_mnemonic(passphrase: String) -> Self {
|
2025-11-26 07:07:58 +02:00
|
|
|
// Currently dropping SeedHolder at the end of initialization.
|
|
|
|
|
// Not entirely sure if we need it in the future.
|
2025-11-04 16:09:04 +02:00
|
|
|
let seed_holder = SeedHolder::new_mnemonic(passphrase);
|
|
|
|
|
let secret_spending_key = seed_holder.produce_top_secret_key_holder();
|
|
|
|
|
|
2026-01-27 16:00:42 -05:00
|
|
|
let private_key_holder = secret_spending_key.produce_private_key_holder(None);
|
2025-11-04 16:09:04 +02:00
|
|
|
|
|
|
|
|
let nullifer_public_key = private_key_holder.generate_nullifier_public_key();
|
2026-01-21 17:27:23 -05:00
|
|
|
let viewing_public_key = private_key_holder.generate_viewing_public_key();
|
2024-10-25 09:41:43 +03:00
|
|
|
|
2025-08-18 16:15:25 +03:00
|
|
|
Self {
|
2025-09-15 14:04:49 +03:00
|
|
|
secret_spending_key,
|
2025-09-05 14:47:58 +03:00
|
|
|
private_key_holder,
|
2025-08-18 16:15:25 +03:00
|
|
|
nullifer_public_key,
|
2026-01-21 17:27:23 -05:00
|
|
|
viewing_public_key,
|
2024-10-25 09:41:43 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn calculate_shared_secret_receiver(
|
|
|
|
|
&self,
|
2025-09-15 14:04:49 +03:00
|
|
|
ephemeral_public_key_sender: EphemeralPublicKey,
|
2026-03-05 12:35:18 +02:00
|
|
|
index: Option<u32>,
|
2025-09-15 14:04:49 +03:00
|
|
|
) -> SharedSecretKey {
|
|
|
|
|
SharedSecretKey::new(
|
2026-03-05 12:35:18 +02:00
|
|
|
&self.secret_spending_key.generate_viewing_secret_key(index),
|
2025-09-15 14:04:49 +03:00
|
|
|
&ephemeral_public_key_sender,
|
|
|
|
|
)
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
2024-10-25 09:41:43 +03:00
|
|
|
}
|
2024-10-25 14:15:00 +03:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2025-09-15 14:04:49 +03:00
|
|
|
use aes_gcm::aead::OsRng;
|
2025-10-23 17:33:25 +03:00
|
|
|
use base58::ToBase58;
|
2025-11-26 00:27:20 +03:00
|
|
|
use k256::{AffinePoint, elliptic_curve::group::GroupEncoding};
|
2025-09-17 08:59:14 +03:00
|
|
|
use rand::RngCore;
|
2025-07-15 12:49:19 +03:00
|
|
|
|
2024-10-25 14:15:00 +03:00
|
|
|
use super::*;
|
2026-03-05 15:20:22 +02:00
|
|
|
use crate::key_management::ephemeral_key_holder::EphemeralKeyHolder;
|
2024-10-25 14:15:00 +03:00
|
|
|
|
2024-11-02 01:34:04 +01:00
|
|
|
#[test]
|
|
|
|
|
fn test_new_os_random() {
|
2025-08-15 14:27:36 +03:00
|
|
|
// Ensure that a new KeyChain instance can be created without errors.
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id_key_holder = KeyChain::new_os_random();
|
2024-11-02 01:40:44 +01:00
|
|
|
|
2024-11-02 01:34:04 +01:00
|
|
|
// Check that key holder fields are initialized with expected types
|
2025-11-24 17:09:30 +03:00
|
|
|
assert_ne!(
|
|
|
|
|
account_id_key_holder.nullifer_public_key.as_ref(),
|
|
|
|
|
&[0u8; 32]
|
|
|
|
|
);
|
2024-11-02 01:34:04 +01:00
|
|
|
}
|
|
|
|
|
|
2024-11-02 01:34:34 +01:00
|
|
|
#[test]
|
|
|
|
|
fn test_calculate_shared_secret_receiver() {
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id_key_holder = KeyChain::new_os_random();
|
2024-11-02 01:34:34 +01:00
|
|
|
|
|
|
|
|
// Generate a random ephemeral public key sender
|
2025-09-17 08:59:14 +03:00
|
|
|
let mut scalar = [0; 32];
|
|
|
|
|
OsRng.fill_bytes(&mut scalar);
|
2025-09-15 14:04:49 +03:00
|
|
|
let ephemeral_public_key_sender = EphemeralPublicKey::from_scalar(scalar);
|
2024-11-02 01:34:34 +01:00
|
|
|
|
|
|
|
|
// Calculate shared secret
|
2026-03-05 12:35:18 +02:00
|
|
|
let _shared_secret = account_id_key_holder
|
|
|
|
|
.calculate_shared_secret_receiver(ephemeral_public_key_sender, None);
|
2024-11-02 01:38:15 +01:00
|
|
|
}
|
|
|
|
|
|
2024-10-25 14:15:00 +03:00
|
|
|
#[test]
|
|
|
|
|
fn key_generation_test() {
|
|
|
|
|
let seed_holder = SeedHolder::new_os_random();
|
|
|
|
|
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder();
|
|
|
|
|
|
2026-01-27 16:00:42 -05:00
|
|
|
let utxo_secret_key_holder = top_secret_key_holder.produce_private_key_holder(None);
|
2024-10-25 14:15:00 +03:00
|
|
|
|
|
|
|
|
let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key();
|
2026-01-21 17:27:23 -05:00
|
|
|
let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key();
|
2024-10-25 14:15:00 +03:00
|
|
|
|
2025-08-18 16:15:25 +03:00
|
|
|
let pub_account_signing_key = nssa::PrivateKey::new_os_random();
|
2025-07-29 14:20:03 +03:00
|
|
|
|
2025-08-13 01:33:11 -03:00
|
|
|
let public_key = nssa::PublicKey::new_from_private_key(&pub_account_signing_key);
|
2025-07-29 14:20:03 +03:00
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let account = nssa::AccountId::from(&public_key);
|
2025-07-29 14:20:03 +03:00
|
|
|
|
2024-10-25 14:15:00 +03:00
|
|
|
println!("======Prerequisites======");
|
|
|
|
|
println!();
|
|
|
|
|
|
2024-10-25 14:19:42 +03:00
|
|
|
println!(
|
|
|
|
|
"Group generator {:?}",
|
2025-09-25 11:53:42 +03:00
|
|
|
hex::encode(AffinePoint::GENERATOR.to_bytes())
|
2024-10-25 14:19:42 +03:00
|
|
|
);
|
2024-10-25 14:15:00 +03:00
|
|
|
println!();
|
|
|
|
|
|
|
|
|
|
println!("======Holders======");
|
|
|
|
|
println!();
|
|
|
|
|
|
|
|
|
|
println!("{seed_holder:?}");
|
|
|
|
|
println!("{top_secret_key_holder:?}");
|
|
|
|
|
println!("{utxo_secret_key_holder:?}");
|
|
|
|
|
println!();
|
|
|
|
|
|
|
|
|
|
println!("======Public data======");
|
|
|
|
|
println!();
|
2025-11-24 17:09:30 +03:00
|
|
|
println!("Account {:?}", account.value().to_base58());
|
2024-10-25 14:19:42 +03:00
|
|
|
println!(
|
|
|
|
|
"Nulifier public key {:?}",
|
2025-09-25 11:53:42 +03:00
|
|
|
hex::encode(nullifer_public_key.to_byte_array())
|
2024-10-25 14:19:42 +03:00
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"Viewing public key {:?}",
|
2025-09-25 11:53:42 +03:00
|
|
|
hex::encode(viewing_public_key.to_bytes())
|
2024-10-25 14:19:42 +03:00
|
|
|
);
|
2024-10-25 14:15:00 +03:00
|
|
|
}
|
2026-03-05 15:20:22 +02:00
|
|
|
|
|
|
|
|
fn account_with_chain_index_2_for_tests() -> KeyChain {
|
|
|
|
|
let key_chain_raw = r#"
|
|
|
|
|
{
|
|
|
|
|
"secret_spending_key": [
|
|
|
|
|
208,
|
|
|
|
|
155,
|
|
|
|
|
82,
|
|
|
|
|
128,
|
|
|
|
|
101,
|
|
|
|
|
206,
|
|
|
|
|
20,
|
|
|
|
|
95,
|
|
|
|
|
241,
|
|
|
|
|
147,
|
|
|
|
|
159,
|
|
|
|
|
231,
|
|
|
|
|
207,
|
|
|
|
|
78,
|
|
|
|
|
152,
|
|
|
|
|
28,
|
|
|
|
|
114,
|
|
|
|
|
111,
|
|
|
|
|
61,
|
|
|
|
|
69,
|
|
|
|
|
254,
|
|
|
|
|
51,
|
|
|
|
|
242,
|
|
|
|
|
28,
|
|
|
|
|
28,
|
|
|
|
|
195,
|
|
|
|
|
170,
|
|
|
|
|
242,
|
|
|
|
|
160,
|
|
|
|
|
24,
|
|
|
|
|
47,
|
|
|
|
|
189
|
|
|
|
|
],
|
|
|
|
|
"private_key_holder": {
|
|
|
|
|
"nullifier_secret_key": [
|
|
|
|
|
142,
|
|
|
|
|
76,
|
|
|
|
|
154,
|
|
|
|
|
157,
|
|
|
|
|
42,
|
|
|
|
|
40,
|
|
|
|
|
174,
|
|
|
|
|
199,
|
|
|
|
|
151,
|
|
|
|
|
63,
|
|
|
|
|
2,
|
|
|
|
|
216,
|
|
|
|
|
52,
|
|
|
|
|
103,
|
|
|
|
|
81,
|
|
|
|
|
42,
|
|
|
|
|
200,
|
|
|
|
|
177,
|
|
|
|
|
189,
|
|
|
|
|
49,
|
|
|
|
|
81,
|
|
|
|
|
39,
|
|
|
|
|
166,
|
|
|
|
|
139,
|
|
|
|
|
203,
|
|
|
|
|
154,
|
|
|
|
|
156,
|
|
|
|
|
166,
|
|
|
|
|
88,
|
|
|
|
|
159,
|
|
|
|
|
11,
|
|
|
|
|
151
|
|
|
|
|
],
|
|
|
|
|
"viewing_secret_key": [
|
|
|
|
|
122,
|
|
|
|
|
94,
|
|
|
|
|
159,
|
|
|
|
|
21,
|
|
|
|
|
28,
|
|
|
|
|
49,
|
|
|
|
|
169,
|
|
|
|
|
79,
|
|
|
|
|
12,
|
|
|
|
|
156,
|
|
|
|
|
171,
|
|
|
|
|
90,
|
|
|
|
|
41,
|
|
|
|
|
216,
|
|
|
|
|
203,
|
|
|
|
|
75,
|
|
|
|
|
251,
|
|
|
|
|
192,
|
|
|
|
|
204,
|
|
|
|
|
217,
|
|
|
|
|
18,
|
|
|
|
|
49,
|
|
|
|
|
28,
|
|
|
|
|
219,
|
|
|
|
|
213,
|
|
|
|
|
147,
|
|
|
|
|
244,
|
|
|
|
|
194,
|
|
|
|
|
205,
|
|
|
|
|
237,
|
|
|
|
|
134,
|
|
|
|
|
36
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"nullifer_public_key": [
|
|
|
|
|
235,
|
|
|
|
|
24,
|
|
|
|
|
62,
|
|
|
|
|
99,
|
|
|
|
|
243,
|
|
|
|
|
236,
|
|
|
|
|
137,
|
|
|
|
|
35,
|
|
|
|
|
153,
|
|
|
|
|
149,
|
|
|
|
|
6,
|
|
|
|
|
10,
|
|
|
|
|
118,
|
|
|
|
|
239,
|
|
|
|
|
117,
|
|
|
|
|
188,
|
|
|
|
|
64,
|
|
|
|
|
8,
|
|
|
|
|
33,
|
|
|
|
|
52,
|
|
|
|
|
220,
|
|
|
|
|
231,
|
|
|
|
|
11,
|
|
|
|
|
39,
|
|
|
|
|
180,
|
|
|
|
|
117,
|
|
|
|
|
1,
|
|
|
|
|
22,
|
|
|
|
|
62,
|
|
|
|
|
199,
|
|
|
|
|
164,
|
|
|
|
|
169
|
|
|
|
|
],
|
|
|
|
|
"viewing_public_key": [
|
|
|
|
|
2,
|
|
|
|
|
253,
|
|
|
|
|
204,
|
|
|
|
|
5,
|
|
|
|
|
212,
|
|
|
|
|
86,
|
|
|
|
|
249,
|
|
|
|
|
156,
|
|
|
|
|
132,
|
|
|
|
|
143,
|
|
|
|
|
1,
|
|
|
|
|
172,
|
|
|
|
|
80,
|
|
|
|
|
61,
|
|
|
|
|
18,
|
|
|
|
|
185,
|
|
|
|
|
233,
|
|
|
|
|
36,
|
|
|
|
|
221,
|
|
|
|
|
58,
|
|
|
|
|
64,
|
|
|
|
|
110,
|
|
|
|
|
89,
|
|
|
|
|
242,
|
|
|
|
|
202,
|
|
|
|
|
230,
|
|
|
|
|
154,
|
|
|
|
|
66,
|
|
|
|
|
45,
|
|
|
|
|
252,
|
|
|
|
|
138,
|
|
|
|
|
174,
|
|
|
|
|
37
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
"#;
|
|
|
|
|
|
|
|
|
|
serde_json::from_str(key_chain_raw).unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_non_trivial_chain_index() {
|
|
|
|
|
let keys = account_with_chain_index_2_for_tests();
|
|
|
|
|
|
|
|
|
|
let eph_key_holder = EphemeralKeyHolder::new(&keys.nullifer_public_key);
|
|
|
|
|
|
|
|
|
|
let key_sender = eph_key_holder.calculate_shared_secret_sender(&keys.viewing_public_key);
|
|
|
|
|
let key_receiver = keys.calculate_shared_secret_receiver(
|
|
|
|
|
eph_key_holder.generate_ephemeral_public_key(),
|
|
|
|
|
Some(2),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(key_sender.0, key_receiver.0);
|
|
|
|
|
}
|
2024-10-25 14:15:00 +03:00
|
|
|
}
|