2025-12-16 20:46:16 -05:00
|
|
|
use common::HashType;
|
2025-11-07 16:21:14 +02:00
|
|
|
use k256::{Scalar, elliptic_curve::PrimeField};
|
2026-01-21 17:27:23 -05:00
|
|
|
use nssa_core::{NullifierPublicKey, encryption::ViewingPublicKey};
|
2025-11-07 16:21:14 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
2025-12-15 17:24:59 -05:00
|
|
|
use sha2::{Digest, digest::FixedOutput};
|
2025-11-07 16:21:14 +02:00
|
|
|
|
|
|
|
|
use crate::key_management::{
|
2025-11-10 16:29:33 +02:00
|
|
|
KeyChain,
|
2025-11-07 16:21:14 +02:00
|
|
|
key_tree::traits::KeyNode,
|
2025-11-10 16:29:33 +02:00
|
|
|
secret_holders::{PrivateKeyHolder, SecretSpendingKey},
|
2025-11-07 16:21:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
|
|
|
pub struct ChildKeysPrivate {
|
2025-11-10 16:29:33 +02:00
|
|
|
pub value: (KeyChain, nssa::Account),
|
2025-11-07 16:21:14 +02:00
|
|
|
pub ccc: [u8; 32],
|
2025-11-26 07:07:58 +02:00
|
|
|
/// Can be [`None`] if root
|
2025-11-07 16:21:14 +02:00
|
|
|
pub cci: Option<u32>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl KeyNode for ChildKeysPrivate {
|
|
|
|
|
fn root(seed: [u8; 64]) -> Self {
|
2026-01-21 17:27:23 -05:00
|
|
|
let hash_value = hmac_sha512::HMAC::mac(seed, b"LEE_master_priv");
|
2025-11-07 16:21:14 +02:00
|
|
|
|
2025-11-26 14:27:09 +02:00
|
|
|
let ssk = SecretSpendingKey(
|
|
|
|
|
*hash_value
|
|
|
|
|
.first_chunk::<32>()
|
|
|
|
|
.expect("hash_value is 64 bytes, must be safe to get first 32"),
|
|
|
|
|
);
|
|
|
|
|
let ccc = *hash_value
|
|
|
|
|
.last_chunk::<32>()
|
|
|
|
|
.expect("hash_value is 64 bytes, must be safe to get last 32");
|
2025-11-07 16:21:14 +02:00
|
|
|
|
2026-01-21 17:48:10 -05:00
|
|
|
// TODO: check these generations
|
2025-11-07 16:21:14 +02:00
|
|
|
let nsk = ssk.generate_nullifier_secret_key();
|
2026-01-21 17:27:23 -05:00
|
|
|
let vsk = ssk.generate_viewing_secret_key();
|
2025-11-07 16:21:14 +02:00
|
|
|
|
2025-12-16 20:46:16 -05:00
|
|
|
let npk: NullifierPublicKey = {
|
2025-12-15 17:24:59 -05:00
|
|
|
let mut hasher = sha2::Sha256::new();
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
hasher.update("LEE/keys");
|
2025-12-15 17:24:59 -05:00
|
|
|
hasher.update(nsk);
|
|
|
|
|
hasher.update([7u8]);
|
2026-01-21 17:27:23 -05:00
|
|
|
hasher.update([0u8; 23]);
|
2025-12-15 17:24:59 -05:00
|
|
|
|
2026-01-22 18:24:19 -05:00
|
|
|
NullifierPublicKey(<HashType>::from(hasher.finalize_fixed()))
|
2025-12-15 17:24:59 -05:00
|
|
|
};
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
let vpk = ViewingPublicKey::from_scalar(vsk);
|
2025-11-07 16:21:14 +02:00
|
|
|
|
|
|
|
|
Self {
|
2025-11-10 16:29:33 +02:00
|
|
|
value: (
|
|
|
|
|
KeyChain {
|
|
|
|
|
secret_spending_key: ssk,
|
|
|
|
|
nullifer_public_key: npk,
|
2026-01-21 17:27:23 -05:00
|
|
|
viewing_public_key: vpk,
|
2025-11-10 16:29:33 +02:00
|
|
|
private_key_holder: PrivateKeyHolder {
|
|
|
|
|
nullifier_secret_key: nsk,
|
2026-01-21 17:27:23 -05:00
|
|
|
viewing_secret_key: vsk,
|
2025-11-10 16:29:33 +02:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
nssa::Account::default(),
|
|
|
|
|
),
|
2025-11-07 16:21:14 +02:00
|
|
|
ccc,
|
|
|
|
|
cci: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-26 07:07:58 +02:00
|
|
|
fn nth_child(&self, cci: u32) -> Self {
|
2026-01-21 17:27:23 -05:00
|
|
|
let parent_pt =
|
|
|
|
|
Scalar::from_repr(self.value.0.private_key_holder.nullifier_secret_key.into())
|
2025-11-26 14:27:09 +02:00
|
|
|
.expect("Key generated as scalar, must be valid representation")
|
2026-01-21 17:27:23 -05:00
|
|
|
* Scalar::from_repr(self.value.0.private_key_holder.viewing_secret_key.into())
|
|
|
|
|
.expect("Key generated as scalar, must be valid representation");
|
2025-12-22 19:50:03 -05:00
|
|
|
let mut input = vec![];
|
2026-01-21 17:27:23 -05:00
|
|
|
|
|
|
|
|
input.extend_from_slice(b"LEE_seed_priv");
|
2025-11-07 16:21:14 +02:00
|
|
|
input.extend_from_slice(&parent_pt.to_bytes());
|
|
|
|
|
input.extend_from_slice(&cci.to_le_bytes());
|
|
|
|
|
|
|
|
|
|
let hash_value = hmac_sha512::HMAC::mac(input, self.ccc);
|
|
|
|
|
|
2025-11-26 14:27:09 +02:00
|
|
|
let ssk = SecretSpendingKey(
|
|
|
|
|
*hash_value
|
|
|
|
|
.first_chunk::<32>()
|
|
|
|
|
.expect("hash_value is 64 bytes, must be safe to get first 32"),
|
|
|
|
|
);
|
|
|
|
|
let ccc = *hash_value
|
|
|
|
|
.last_chunk::<32>()
|
|
|
|
|
.expect("hash_value is 64 bytes, must be safe to get last 32");
|
2025-11-07 16:21:14 +02:00
|
|
|
|
2025-12-15 17:24:59 -05:00
|
|
|
let nsk = ssk.generate_child_nullifier_secret_key(cci);
|
2026-01-21 17:27:23 -05:00
|
|
|
let vsk = ssk.generate_child_viewing_secret_key(cci);
|
2025-12-15 17:24:59 -05:00
|
|
|
|
|
|
|
|
let npk: NullifierPublicKey = {
|
|
|
|
|
let mut hasher = sha2::Sha256::new();
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
hasher.update("LEE/chain");
|
2025-12-15 17:24:59 -05:00
|
|
|
hasher.update(nsk);
|
|
|
|
|
hasher.update([7u8]);
|
|
|
|
|
hasher.update([0u8; 22]);
|
|
|
|
|
|
2026-01-22 18:25:57 -05:00
|
|
|
NullifierPublicKey(<HashType>::from(hasher.finalize_fixed()))
|
2025-12-15 17:24:59 -05:00
|
|
|
};
|
2025-11-07 16:21:14 +02:00
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
let vpk = ViewingPublicKey::from_scalar(vsk);
|
2025-11-07 16:21:14 +02:00
|
|
|
|
|
|
|
|
Self {
|
2025-11-10 16:29:33 +02:00
|
|
|
value: (
|
|
|
|
|
KeyChain {
|
|
|
|
|
secret_spending_key: ssk,
|
|
|
|
|
nullifer_public_key: npk,
|
2026-01-21 17:27:23 -05:00
|
|
|
viewing_public_key: vpk,
|
2025-11-10 16:29:33 +02:00
|
|
|
private_key_holder: PrivateKeyHolder {
|
|
|
|
|
nullifier_secret_key: nsk,
|
2026-01-21 17:27:23 -05:00
|
|
|
viewing_secret_key: vsk,
|
2025-11-10 16:29:33 +02:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
nssa::Account::default(),
|
|
|
|
|
),
|
2025-11-07 16:21:14 +02:00
|
|
|
ccc,
|
|
|
|
|
cci: Some(cci),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn chain_code(&self) -> &[u8; 32] {
|
|
|
|
|
&self.ccc
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-26 07:07:58 +02:00
|
|
|
fn child_index(&self) -> Option<u32> {
|
|
|
|
|
self.cci
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
|
|
|
|
|
2025-11-27 13:46:35 +02:00
|
|
|
fn account_id(&self) -> nssa::AccountId {
|
|
|
|
|
nssa::AccountId::from(&self.value.0.nullifer_public_key)
|
2025-11-10 16:29:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> From<&'a ChildKeysPrivate> for &'a (KeyChain, nssa::Account) {
|
|
|
|
|
fn from(value: &'a ChildKeysPrivate) -> Self {
|
|
|
|
|
&value.value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> From<&'a mut ChildKeysPrivate> for &'a mut (KeyChain, nssa::Account) {
|
|
|
|
|
fn from(value: &'a mut ChildKeysPrivate) -> Self {
|
|
|
|
|
&mut value.value
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2026-01-21 17:27:23 -05:00
|
|
|
use nssa_core::{NullifierPublicKey, NullifierSecretKey};
|
2025-12-16 20:46:16 -05:00
|
|
|
|
2025-11-07 16:21:14 +02:00
|
|
|
use super::*;
|
2026-01-21 17:27:23 -05:00
|
|
|
use crate::key_management::{self, secret_holders::ViewingSecretKey};
|
2025-11-07 16:21:14 +02:00
|
|
|
|
2025-12-16 20:46:16 -05:00
|
|
|
#[test]
|
|
|
|
|
fn test_master_key_generation() {
|
|
|
|
|
let seed: [u8; 64] = [
|
|
|
|
|
252, 56, 204, 83, 232, 123, 209, 188, 187, 167, 39, 213, 71, 39, 58, 65, 125, 134, 255,
|
|
|
|
|
49, 43, 108, 92, 53, 173, 164, 94, 142, 150, 74, 21, 163, 43, 144, 226, 87, 199, 18,
|
|
|
|
|
129, 223, 176, 198, 5, 150, 157, 70, 210, 254, 14, 105, 89, 191, 246, 27, 52, 170, 56,
|
|
|
|
|
114, 39, 38, 118, 197, 205, 225,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let keys = ChildKeysPrivate::root(seed);
|
|
|
|
|
|
|
|
|
|
let expected_ssk: SecretSpendingKey = key_management::secret_holders::SecretSpendingKey([
|
2026-01-21 17:27:23 -05:00
|
|
|
246, 79, 26, 124, 135, 95, 52, 51, 201, 27, 48, 194, 2, 144, 51, 219, 245, 128, 139,
|
|
|
|
|
222, 42, 195, 105, 33, 115, 97, 186, 0, 97, 14, 218, 191,
|
2025-12-16 20:46:16 -05:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let expected_ccc = [
|
2026-01-21 17:27:23 -05:00
|
|
|
56, 114, 70, 249, 67, 169, 206, 9, 192, 11, 180, 168, 149, 129, 42, 95, 43, 157, 130,
|
|
|
|
|
111, 13, 5, 195, 75, 20, 255, 162, 85, 40, 251, 8, 168,
|
2025-12-16 20:46:16 -05:00
|
|
|
];
|
2026-01-21 17:27:23 -05:00
|
|
|
|
2025-12-16 20:46:16 -05:00
|
|
|
let expected_nsk: NullifierSecretKey = [
|
2026-01-21 17:27:23 -05:00
|
|
|
154, 102, 103, 5, 34, 235, 227, 13, 22, 182, 226, 11, 7, 67, 110, 162, 99, 193, 174,
|
|
|
|
|
34, 234, 19, 222, 2, 22, 12, 163, 252, 88, 11, 0, 163,
|
2025-12-16 20:46:16 -05:00
|
|
|
];
|
2026-01-21 17:27:23 -05:00
|
|
|
|
2025-12-16 20:46:16 -05:00
|
|
|
let expected_npk: NullifierPublicKey = nssa_core::NullifierPublicKey([
|
2026-01-21 17:27:23 -05:00
|
|
|
7, 123, 125, 191, 233, 183, 201, 4, 20, 214, 155, 210, 45, 234, 27, 240, 194, 111, 97,
|
|
|
|
|
247, 155, 113, 122, 246, 192, 0, 70, 61, 76, 71, 70, 2,
|
2025-12-16 20:46:16 -05:00
|
|
|
]);
|
2026-01-21 17:27:23 -05:00
|
|
|
let expected_vsk: ViewingSecretKey = [
|
|
|
|
|
155, 90, 54, 75, 228, 130, 68, 201, 129, 251, 180, 195, 250, 64, 34, 230, 241, 204,
|
|
|
|
|
216, 50, 149, 156, 10, 67, 208, 74, 9, 10, 47, 59, 50, 202,
|
2025-12-16 20:46:16 -05:00
|
|
|
];
|
2026-01-21 17:27:23 -05:00
|
|
|
|
|
|
|
|
let expected_vpk_as_bytes: [u8; 33] = [
|
|
|
|
|
2, 191, 99, 102, 114, 40, 131, 109, 166, 8, 222, 186, 107, 29, 156, 106, 206, 96, 127,
|
|
|
|
|
80, 170, 66, 217, 79, 38, 80, 11, 74, 147, 123, 221, 159, 166,
|
2025-12-16 20:46:16 -05:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
assert!(expected_ssk == keys.value.0.secret_spending_key);
|
|
|
|
|
assert!(expected_ccc == keys.ccc);
|
|
|
|
|
assert!(expected_nsk == keys.value.0.private_key_holder.nullifier_secret_key);
|
|
|
|
|
assert!(expected_npk == keys.value.0.nullifer_public_key);
|
2026-01-21 17:27:23 -05:00
|
|
|
assert!(expected_vsk == keys.value.0.private_key_holder.viewing_secret_key);
|
|
|
|
|
assert!(expected_vpk_as_bytes == keys.value.0.viewing_public_key.to_bytes());
|
2025-12-16 20:46:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_child_keys_generation() {
|
2025-12-22 19:50:03 -05:00
|
|
|
let seed: [u8; 64] = [
|
2026-01-21 17:27:23 -05:00
|
|
|
252, 56, 204, 83, 232, 123, 209, 188, 187, 167, 39, 213, 71, 39, 58, 65, 125, 134, 255,
|
|
|
|
|
49, 43, 108, 92, 53, 173, 164, 94, 142, 150, 74, 21, 163, 43, 144, 226, 87, 199, 18,
|
|
|
|
|
129, 223, 176, 198, 5, 150, 157, 70, 210, 254, 14, 105, 89, 191, 246, 27, 52, 170, 56,
|
|
|
|
|
114, 39, 38, 118, 197, 205, 225,
|
2025-12-22 19:50:03 -05:00
|
|
|
];
|
2025-12-16 20:46:16 -05:00
|
|
|
|
|
|
|
|
let root_node = ChildKeysPrivate::root(seed);
|
|
|
|
|
let child_node = ChildKeysPrivate::nth_child(&root_node, 42u32);
|
|
|
|
|
|
2025-12-22 19:50:03 -05:00
|
|
|
let expected_ccc: [u8; 32] = [
|
2026-01-21 17:27:23 -05:00
|
|
|
145, 59, 225, 32, 54, 168, 14, 45, 60, 253, 57, 202, 31, 86, 142, 234, 51, 57, 154, 88,
|
|
|
|
|
132, 200, 92, 191, 220, 144, 42, 184, 108, 35, 226, 146,
|
2025-12-22 19:50:03 -05:00
|
|
|
];
|
2025-12-16 20:46:16 -05:00
|
|
|
|
|
|
|
|
let expected_nsk: NullifierSecretKey = [
|
2026-01-21 17:27:23 -05:00
|
|
|
82, 238, 58, 161, 96, 201, 25, 193, 53, 101, 100, 173, 183, 167, 165, 141, 252, 214,
|
|
|
|
|
214, 3, 176, 186, 62, 112, 56, 54, 6, 197, 29, 178, 88, 214,
|
2025-12-22 19:50:03 -05:00
|
|
|
];
|
|
|
|
|
|
2025-12-16 20:46:16 -05:00
|
|
|
let expected_npk: NullifierPublicKey = nssa_core::NullifierPublicKey([
|
2026-01-21 17:27:23 -05:00
|
|
|
40, 104, 183, 124, 101, 11, 61, 45, 140, 53, 3, 155, 139, 134, 105, 108, 60, 229, 165,
|
|
|
|
|
195, 187, 246, 14, 88, 76, 69, 137, 154, 29, 113, 205, 153,
|
2025-12-22 19:50:03 -05:00
|
|
|
]);
|
|
|
|
|
|
2026-01-21 17:27:23 -05:00
|
|
|
let expected_vsk: ViewingSecretKey = [
|
|
|
|
|
14, 114, 31, 116, 147, 114, 62, 111, 176, 100, 211, 68, 38, 47, 250, 34, 224, 249, 25,
|
|
|
|
|
40, 35, 37, 237, 224, 161, 58, 228, 154, 44, 162, 128, 138,
|
2025-12-22 19:50:03 -05:00
|
|
|
];
|
2026-01-21 17:27:23 -05:00
|
|
|
let expected_vpk_as_bytes: [u8; 33] = [
|
|
|
|
|
3, 243, 200, 219, 91, 171, 128, 76, 173, 117, 255, 212, 233, 71, 205, 204, 89, 104, 92,
|
|
|
|
|
187, 249, 154, 197, 102, 241, 66, 15, 55, 194, 189, 16, 124, 176,
|
2025-12-22 19:50:03 -05:00
|
|
|
];
|
2025-12-16 20:46:16 -05:00
|
|
|
|
|
|
|
|
assert!(expected_ccc == child_node.ccc);
|
|
|
|
|
assert!(expected_nsk == child_node.value.0.private_key_holder.nullifier_secret_key);
|
|
|
|
|
assert!(expected_npk == child_node.value.0.nullifer_public_key);
|
2026-01-21 17:27:23 -05:00
|
|
|
assert!(expected_vsk == child_node.value.0.private_key_holder.viewing_secret_key);
|
|
|
|
|
assert!(expected_vpk_as_bytes == child_node.value.0.viewing_public_key.to_bytes());
|
2025-12-16 20:46:16 -05:00
|
|
|
}
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|