2025-09-04 17:49:55 +03:00
|
|
|
use bip39::Mnemonic;
|
2025-10-17 16:04:09 -03:00
|
|
|
use common::HashType;
|
2025-09-17 08:59:14 +03:00
|
|
|
use nssa_core::{
|
|
|
|
|
NullifierPublicKey, NullifierSecretKey,
|
2026-01-21 17:27:23 -05:00
|
|
|
encryption::{Scalar, ViewingPublicKey},
|
2025-09-17 08:59:14 +03:00
|
|
|
};
|
2025-09-08 15:23:32 +03:00
|
|
|
use rand::{RngCore, rngs::OsRng};
|
2025-05-30 15:20:52 -04:00
|
|
|
use serde::{Deserialize, Serialize};
|
2025-09-04 14:38:41 +03:00
|
|
|
use sha2::{Digest, digest::FixedOutput};
|
2024-10-30 12:32:36 +02:00
|
|
|
|
2025-11-26 14:53:26 +02:00
|
|
|
const NSSA_ENTROPY_BYTES: [u8; 32] = [0; 32];
|
|
|
|
|
|
2024-10-30 12:32:36 +02:00
|
|
|
#[derive(Debug)]
|
2025-11-26 00:27:20 +03:00
|
|
|
/// Seed holder. Non-clonable to ensure that different holders use different seeds.
|
2024-10-30 12:32:36 +02:00
|
|
|
/// Produces `TopSecretKeyHolder` objects.
|
|
|
|
|
pub struct SeedHolder {
|
2025-11-26 00:27:20 +03:00
|
|
|
// ToDo: Needs to be vec as serde derives is not implemented for [u8; 64]
|
2025-09-05 14:47:58 +03:00
|
|
|
pub(crate) seed: Vec<u8>,
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
2025-11-26 00:27:20 +03:00
|
|
|
/// Secret spending key object. Can produce `PrivateKeyHolder` objects.
|
2025-09-15 14:04:49 +03:00
|
|
|
pub struct SecretSpendingKey(pub(crate) [u8; 32]);
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
pub type ViewingSecretKey = Scalar;
|
2025-09-15 14:04:49 +03:00
|
|
|
pub type OutgoingViewingSecretKey = Scalar;
|
2024-10-30 12:32:36 +02:00
|
|
|
|
2025-05-30 15:20:52 -04:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
2025-11-26 00:27:20 +03:00
|
|
|
/// Private key holder. Produces public keys. Can produce account_id. Can produce shared secret for
|
|
|
|
|
/// recepient.
|
2025-09-05 14:47:58 +03:00
|
|
|
pub struct PrivateKeyHolder {
|
2025-09-16 14:53:00 +03:00
|
|
|
pub nullifier_secret_key: NullifierSecretKey,
|
2026-01-21 17:27:23 -05:00
|
|
|
pub(crate) viewing_secret_key: ViewingSecretKey,
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SeedHolder {
|
|
|
|
|
pub fn new_os_random() -> Self {
|
2025-09-04 17:49:55 +03:00
|
|
|
let mut enthopy_bytes: [u8; 32] = [0; 32];
|
|
|
|
|
OsRng.fill_bytes(&mut enthopy_bytes);
|
2024-10-30 12:32:36 +02:00
|
|
|
|
2025-11-26 14:53:26 +02:00
|
|
|
let mnemonic = Mnemonic::from_entropy(&enthopy_bytes)
|
|
|
|
|
.expect("Enthropy must be a multiple of 32 bytes");
|
2025-09-05 14:47:58 +03:00
|
|
|
let seed_wide = mnemonic.to_seed("mnemonic");
|
2024-10-30 12:32:36 +02:00
|
|
|
|
|
|
|
|
Self {
|
2025-09-05 14:47:58 +03:00
|
|
|
seed: seed_wide.to_vec(),
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-04 16:09:04 +02:00
|
|
|
pub fn new_mnemonic(passphrase: String) -> Self {
|
2025-11-26 14:53:26 +02:00
|
|
|
let mnemonic = Mnemonic::from_entropy(&NSSA_ENTROPY_BYTES)
|
|
|
|
|
.expect("Enthropy must be a multiple of 32 bytes");
|
2025-11-04 16:09:04 +02:00
|
|
|
let seed_wide = mnemonic.to_seed(passphrase);
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
seed: seed_wide.to_vec(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-17 16:04:09 -03:00
|
|
|
pub fn generate_secret_spending_key_hash(&self) -> HashType {
|
2025-09-05 14:47:58 +03:00
|
|
|
let mut hash = hmac_sha512::HMAC::mac(&self.seed, "NSSA_seed");
|
2024-10-30 12:32:36 +02:00
|
|
|
|
2025-09-05 14:47:58 +03:00
|
|
|
for _ in 1..2048 {
|
2025-09-08 14:48:58 +03:00
|
|
|
hash = hmac_sha512::HMAC::mac(hash, "NSSA_seed");
|
2025-09-05 14:47:58 +03:00
|
|
|
}
|
2024-10-30 12:32:36 +02:00
|
|
|
|
2025-11-26 00:27:20 +03:00
|
|
|
// Safe unwrap
|
2025-09-05 14:47:58 +03:00
|
|
|
*hash.first_chunk::<32>().unwrap()
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-15 14:04:49 +03:00
|
|
|
pub fn produce_top_secret_key_holder(&self) -> SecretSpendingKey {
|
|
|
|
|
SecretSpendingKey(self.generate_secret_spending_key_hash())
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-15 14:04:49 +03:00
|
|
|
impl SecretSpendingKey {
|
|
|
|
|
pub fn generate_nullifier_secret_key(&self) -> NullifierSecretKey {
|
2024-10-30 12:32:36 +02:00
|
|
|
let mut hasher = sha2::Sha256::new();
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
hasher.update("LEE/keys");
|
2025-09-15 14:04:49 +03:00
|
|
|
hasher.update(self.0);
|
2025-09-05 14:47:58 +03:00
|
|
|
hasher.update([1u8]);
|
2026-01-21 17:27:23 -05:00
|
|
|
hasher.update([0u8; 23]);
|
2025-09-05 14:47:58 +03:00
|
|
|
|
2025-09-15 14:04:49 +03:00
|
|
|
<NullifierSecretKey>::from(hasher.finalize_fixed())
|
2025-09-05 14:47:58 +03:00
|
|
|
}
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
pub fn generate_viewing_secret_key(&self) -> ViewingSecretKey {
|
2025-09-05 14:47:58 +03:00
|
|
|
let mut hasher = sha2::Sha256::new();
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
hasher.update("LEE/keys");
|
2025-09-15 14:04:49 +03:00
|
|
|
hasher.update(self.0);
|
2025-09-05 14:47:58 +03:00
|
|
|
hasher.update([2u8]);
|
2026-01-21 17:27:23 -05:00
|
|
|
hasher.update([0u8; 23]);
|
2024-10-30 12:32:36 +02:00
|
|
|
|
2025-10-17 16:04:09 -03:00
|
|
|
<HashType>::from(hasher.finalize_fixed())
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 14:47:58 +03:00
|
|
|
pub fn produce_private_key_holder(&self) -> PrivateKeyHolder {
|
|
|
|
|
PrivateKeyHolder {
|
2024-10-30 12:32:36 +02:00
|
|
|
nullifier_secret_key: self.generate_nullifier_secret_key(),
|
2026-01-21 17:27:23 -05:00
|
|
|
viewing_secret_key: self.generate_viewing_secret_key(),
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-15 17:24:59 -05:00
|
|
|
|
|
|
|
|
pub fn generate_child_nullifier_secret_key(&self, cci: u32) -> NullifierSecretKey {
|
|
|
|
|
let mut key = vec![];
|
2026-01-21 17:27:23 -05:00
|
|
|
key.extend_from_slice(b"LEE/chain");
|
2025-12-15 17:24:59 -05:00
|
|
|
|
|
|
|
|
let mut input = vec![];
|
|
|
|
|
|
|
|
|
|
input.extend_from_slice(&self.0);
|
|
|
|
|
input.extend_from_slice(&[1u8]);
|
|
|
|
|
input.extend_from_slice(&cci.to_le_bytes());
|
2025-12-22 19:50:03 -05:00
|
|
|
input.extend_from_slice(&[0u8; 22]);
|
2025-12-15 17:24:59 -05:00
|
|
|
|
|
|
|
|
let hash_value = hmac_sha512::HMAC::mac(input, key);
|
|
|
|
|
|
|
|
|
|
*hash_value
|
|
|
|
|
.first_chunk::<32>()
|
2025-12-22 19:50:03 -05:00
|
|
|
.expect("hash_value is 64 bytes, must be safe to get first 32")
|
2025-12-15 17:24:59 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
pub fn generate_child_viewing_secret_key(&self, cci: u32) -> ViewingSecretKey {
|
2025-12-15 17:24:59 -05:00
|
|
|
let mut key = vec![];
|
2026-01-21 17:27:23 -05:00
|
|
|
key.extend_from_slice(b"LEE/chain");
|
2025-12-15 17:24:59 -05:00
|
|
|
|
|
|
|
|
let mut input = vec![];
|
|
|
|
|
|
|
|
|
|
input.extend_from_slice(&self.0);
|
|
|
|
|
input.extend_from_slice(&[2u8]);
|
|
|
|
|
input.extend_from_slice(&cci.to_le_bytes());
|
2025-12-22 19:50:03 -05:00
|
|
|
input.extend_from_slice(&[0u8; 22]);
|
2025-12-15 17:24:59 -05:00
|
|
|
|
|
|
|
|
let hash_value = hmac_sha512::HMAC::mac(input, key);
|
|
|
|
|
|
|
|
|
|
*hash_value
|
|
|
|
|
.first_chunk::<32>()
|
2025-12-22 19:50:03 -05:00
|
|
|
.expect("hash_value is 64 bytes, must be safe to get first 32")
|
2025-12-15 17:24:59 -05:00
|
|
|
}
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 14:47:58 +03:00
|
|
|
impl PrivateKeyHolder {
|
2025-09-15 14:04:49 +03:00
|
|
|
pub fn generate_nullifier_public_key(&self) -> NullifierPublicKey {
|
2026-01-21 17:27:23 -05:00
|
|
|
(&self.nullifier_secret_key).into()
|
2025-09-05 14:47:58 +03:00
|
|
|
}
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
pub fn generate_viewing_public_key(&self) -> ViewingPublicKey {
|
|
|
|
|
ViewingPublicKey::from_scalar(self.viewing_secret_key)
|
2025-09-05 14:47:58 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
2026-01-21 17:48:10 -05:00
|
|
|
// TODO? are these necessary?
|
2025-09-05 14:47:58 +03:00
|
|
|
#[test]
|
2026-01-21 17:27:23 -05:00
|
|
|
fn seed_generation_test() {
|
2025-09-05 14:47:58 +03:00
|
|
|
let seed_holder = SeedHolder::new_os_random();
|
|
|
|
|
|
|
|
|
|
assert_eq!(seed_holder.seed.len(), 64);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2026-01-21 17:27:23 -05:00
|
|
|
fn ssk_generation_test() {
|
2025-09-05 14:47:58 +03:00
|
|
|
let seed_holder = SeedHolder::new_os_random();
|
|
|
|
|
|
|
|
|
|
assert_eq!(seed_holder.seed.len(), 64);
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
let _ = seed_holder.generate_secret_spending_key_hash();
|
2025-09-05 14:47:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2026-01-21 17:27:23 -05:00
|
|
|
fn ivs_generation_test() {
|
2025-09-05 14:47:58 +03:00
|
|
|
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();
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
let _ = top_secret_key_holder.generate_viewing_secret_key();
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|
2025-11-11 12:15:20 +02:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn two_seeds_generated_same_from_same_mnemonic() {
|
|
|
|
|
let mnemonic = "test_pass";
|
|
|
|
|
|
|
|
|
|
let seed_holder1 = SeedHolder::new_mnemonic(mnemonic.to_string());
|
|
|
|
|
let seed_holder2 = SeedHolder::new_mnemonic(mnemonic.to_string());
|
|
|
|
|
|
|
|
|
|
assert_eq!(seed_holder1.seed, seed_holder2.seed);
|
|
|
|
|
}
|
2024-10-30 12:32:36 +02:00
|
|
|
}
|