187 lines
5.4 KiB
Rust
Raw Normal View History

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
};
2026-03-04 18:42:33 +03:00
use rand::{RngCore as _, rngs::OsRng};
use serde::{Deserialize, Serialize};
2026-03-04 18:42:33 +03:00
use sha2::{Digest as _, digest::FixedOutput as _};
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.
2026-03-19 18:01:15 +02:00
pub struct SecretSpendingKey(pub [u8; 32]);
2025-09-15 14:04:49 +03:00
2026-01-21 17:27:23 -05:00
pub type ViewingSecretKey = Scalar;
2024-10-30 12:32:36 +02:00
#[derive(Serialize, Deserialize, Debug, Clone)]
2026-03-03 23:21:08 +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 {
pub nullifier_secret_key: NullifierSecretKey,
2026-03-19 18:01:15 +02:00
pub viewing_secret_key: ViewingSecretKey,
2024-10-30 12:32:36 +02:00
}
impl SeedHolder {
2026-03-03 23:21:08 +03:00
#[must_use]
2024-10-30 12:32:36 +02:00
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
}
}
2026-03-03 23:21:08 +03:00
#[must_use]
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(),
}
}
2026-03-03 23:21:08 +03:00
#[must_use]
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
HashType(*hash.first_chunk::<32>().unwrap())
2024-10-30 12:32:36 +02:00
}
2026-03-03 23:21:08 +03:00
#[must_use]
2025-09-15 14:04:49 +03:00
pub fn produce_top_secret_key_holder(&self) -> SecretSpendingKey {
SecretSpendingKey(self.generate_secret_spending_key_hash().into())
2024-10-30 12:32:36 +02:00
}
}
2025-09-15 14:04:49 +03:00
impl SecretSpendingKey {
2026-03-03 23:21:08 +03:00
#[must_use]
2026-03-18 18:44:07 -04:00
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
2026-01-27 16:00:42 -05:00
pub fn generate_nullifier_secret_key(&self, index: Option<u32>) -> NullifierSecretKey {
2026-03-03 23:21:08 +03:00
const PREFIX: &[u8; 8] = b"LEE/keys";
const SUFFIX_1: &[u8; 1] = &[1];
const SUFFIX_2: &[u8; 19] = &[0; 19];
2026-01-27 16:00:42 -05:00
let index = match index {
2026-03-04 18:42:33 +03:00
None => 0_u32,
2026-01-27 16:00:42 -05:00
_ => index.expect("Expect a valid u32"),
};
let mut hasher = sha2::Sha256::new();
hasher.update(PREFIX);
2025-09-15 14:04:49 +03:00
hasher.update(self.0);
2026-01-27 16:00:42 -05:00
hasher.update(SUFFIX_1);
2026-02-25 15:18:27 -05:00
hasher.update(index.to_be_bytes());
2026-01-27 16:00:42 -05:00
hasher.update(SUFFIX_2);
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-03-03 23:21:08 +03:00
#[must_use]
2026-03-18 18:44:07 -04:00
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
2026-01-27 16:00:42 -05:00
pub fn generate_viewing_secret_key(&self, index: Option<u32>) -> ViewingSecretKey {
2026-03-03 23:21:08 +03:00
const PREFIX: &[u8; 8] = b"LEE/keys";
const SUFFIX_1: &[u8; 1] = &[2];
const SUFFIX_2: &[u8; 19] = &[0; 19];
2026-01-27 16:00:42 -05:00
let index = match index {
2026-03-04 18:42:33 +03:00
None => 0_u32,
2026-01-27 16:00:42 -05:00
_ => index.expect("Expect a valid u32"),
};
2025-09-05 14:47:58 +03:00
2026-01-27 16:00:42 -05:00
let mut hasher = sha2::Sha256::new();
hasher.update(PREFIX);
2025-09-15 14:04:49 +03:00
hasher.update(self.0);
2026-01-27 16:00:42 -05:00
hasher.update(SUFFIX_1);
2026-02-25 15:18:27 -05:00
hasher.update(index.to_be_bytes());
2026-01-27 16:00:42 -05:00
hasher.update(SUFFIX_2);
2024-10-30 12:32:36 +02:00
hasher.finalize_fixed().into()
2024-10-30 12:32:36 +02:00
}
2026-03-03 23:21:08 +03:00
#[must_use]
2026-01-27 16:00:42 -05:00
pub fn produce_private_key_holder(&self, index: Option<u32>) -> PrivateKeyHolder {
2025-09-05 14:47:58 +03:00
PrivateKeyHolder {
2026-01-27 16:00:42 -05:00
nullifier_secret_key: self.generate_nullifier_secret_key(index),
viewing_secret_key: self.generate_viewing_secret_key(index),
2024-10-30 12:32:36 +02:00
}
}
}
2025-09-05 14:47:58 +03:00
impl PrivateKeyHolder {
2026-03-03 23:21:08 +03:00
#[must_use]
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-03-03 23:21:08 +03:00
#[must_use]
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-03-04 18:42:33 +03:00
let _hash = 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-03-04 18:42:33 +03:00
let _vsk = top_secret_key_holder.generate_viewing_secret_key(None);
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";
2026-03-04 18:42:33 +03:00
let seed_holder1 = SeedHolder::new_mnemonic(mnemonic.to_owned());
let seed_holder2 = SeedHolder::new_mnemonic(mnemonic.to_owned());
2025-11-11 12:15:20 +02:00
assert_eq!(seed_holder1.seed, seed_holder2.seed);
}
2024-10-30 12:32:36 +02:00
}