From 6fec57605f1ded412b152e1007e299a658f88c0b Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:30:11 -0400 Subject: [PATCH 1/3] initialize branch --- key_protocol/Cargo.toml | 2 - .../key_management/key_tree/keys_private.rs | 66 +++++----- .../key_management/key_tree/keys_public.rs | 117 +++++++++--------- key_protocol/src/key_management/mod.rs | 18 +-- .../src/key_management/secret_holders.rs | 6 +- key_protocol/src/key_protocol_core/mod.rs | 2 +- nssa/Cargo.toml | 1 + nssa/src/error.rs | 2 +- nssa/src/signature/mod.rs | 64 +++++++++- nssa/src/signature/private_key.rs | 2 +- nssa/src/signature/public_key.rs | 22 ++-- 11 files changed, 188 insertions(+), 114 deletions(-) diff --git a/key_protocol/Cargo.toml b/key_protocol/Cargo.toml index 7a16b627..b38e66c9 100644 --- a/key_protocol/Cargo.toml +++ b/key_protocol/Cargo.toml @@ -8,8 +8,6 @@ license = { workspace = true } workspace = true [dependencies] -secp256k1 = "0.31.1" - nssa.workspace = true nssa_core.workspace = true common.workspace = true diff --git a/key_protocol/src/key_management/key_tree/keys_private.rs b/key_protocol/src/key_management/key_tree/keys_private.rs index 3fcbb0d2..071d596c 100644 --- a/key_protocol/src/key_management/key_tree/keys_private.rs +++ b/key_protocol/src/key_management/key_tree/keys_private.rs @@ -39,7 +39,7 @@ impl KeyNode for ChildKeysPrivate { value: ( KeyChain { secret_spending_key: ssk, - nullifer_public_key: npk, + nullifier_public_key: npk, viewing_public_key: vpk, private_key_holder: PrivateKeyHolder { nullifier_secret_key: nsk, @@ -54,10 +54,7 @@ impl KeyNode for ChildKeysPrivate { } fn nth_child(&self, cci: u32) -> Self { - #[expect( - clippy::arithmetic_side_effects, - reason = "Multiplying finite field scalars gives no unexpected side effects" - )] + #[expect(clippy::arithmetic_side_effects, reason = "TODO: fix later")] let parent_pt = Scalar::from_repr(self.value.0.private_key_holder.nullifier_secret_key.into()) .expect("Key generated as scalar, must be valid representation") @@ -67,7 +64,8 @@ impl KeyNode for ChildKeysPrivate { input.extend_from_slice(b"LEE_seed_priv"); input.extend_from_slice(&parent_pt.to_bytes()); - input.extend_from_slice(&cci.to_le_bytes()); + #[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")] + input.extend_from_slice(&cci.to_be_bytes()); let hash_value = hmac_sha512::HMAC::mac(input, self.ccc); @@ -90,7 +88,7 @@ impl KeyNode for ChildKeysPrivate { value: ( KeyChain { secret_spending_key: ssk, - nullifer_public_key: npk, + nullifier_public_key: npk, viewing_public_key: vpk, private_key_holder: PrivateKeyHolder { nullifier_secret_key: nsk, @@ -113,31 +111,39 @@ impl KeyNode for ChildKeysPrivate { } fn account_id(&self) -> nssa::AccountId { - nssa::AccountId::from(&self.value.0.nullifer_public_key) + nssa::AccountId::from(&self.value.0.nullifier_public_key) } } -impl<'keys> From<&'keys ChildKeysPrivate> for &'keys (KeyChain, nssa::Account) { - fn from(value: &'keys ChildKeysPrivate) -> Self { +#[expect( + clippy::single_char_lifetime_names, + reason = "TODO add meaningful name" +)] +impl<'a> From<&'a ChildKeysPrivate> for &'a (KeyChain, nssa::Account) { + fn from(value: &'a ChildKeysPrivate) -> Self { &value.value } } -impl<'keys> From<&'keys mut ChildKeysPrivate> for &'keys mut (KeyChain, nssa::Account) { - fn from(value: &'keys mut ChildKeysPrivate) -> Self { +#[expect( + clippy::single_char_lifetime_names, + reason = "TODO add meaningful name" +)] +impl<'a> From<&'a mut ChildKeysPrivate> for &'a mut (KeyChain, nssa::Account) { + fn from(value: &'a mut ChildKeysPrivate) -> Self { &mut value.value } } #[cfg(test)] mod tests { - use nssa_core::NullifierSecretKey; + use nssa_core::{NullifierPublicKey, NullifierSecretKey}; use super::*; use crate::key_management::{self, secret_holders::ViewingSecretKey}; #[test] - fn master_key_generation() { + 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, @@ -147,7 +153,7 @@ mod tests { let keys = ChildKeysPrivate::root(seed); - let expected_ssk = key_management::secret_holders::SecretSpendingKey([ + let expected_ssk: SecretSpendingKey = key_management::secret_holders::SecretSpendingKey([ 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, ]); @@ -162,7 +168,7 @@ mod tests { 34, 234, 19, 222, 2, 22, 12, 163, 252, 88, 11, 0, 163, ]; - let expected_npk = nssa_core::NullifierPublicKey([ + let expected_npk: NullifierPublicKey = nssa_core::NullifierPublicKey([ 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, ]); @@ -179,13 +185,13 @@ mod tests { 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); + assert!(expected_npk == keys.value.0.nullifier_public_key); 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()); } #[test] - fn child_keys_generation() { + fn test_child_keys_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, @@ -197,31 +203,31 @@ mod tests { let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32); let expected_ccc: [u8; 32] = [ - 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, + 27, 73, 133, 213, 214, 63, 217, 184, 164, 17, 172, 140, 223, 95, 255, 157, 11, 0, 58, + 53, 82, 147, 121, 120, 199, 50, 30, 28, 103, 24, 121, 187, ]; let expected_nsk: NullifierSecretKey = [ - 19, 100, 119, 73, 191, 225, 234, 219, 129, 88, 40, 229, 63, 225, 189, 136, 69, 172, - 221, 186, 147, 83, 150, 207, 70, 17, 228, 70, 113, 87, 227, 31, + 124, 61, 40, 92, 33, 135, 3, 41, 200, 234, 3, 69, 102, 184, 57, 191, 106, 151, 194, + 192, 103, 132, 141, 112, 249, 108, 192, 117, 24, 48, 70, 216, ]; - let expected_npk = nssa_core::NullifierPublicKey([ - 133, 235, 223, 151, 12, 69, 26, 222, 60, 125, 235, 125, 167, 212, 201, 168, 101, 242, - 111, 239, 1, 228, 12, 252, 146, 53, 75, 17, 187, 255, 122, 181, + let expected_npk: NullifierPublicKey = nssa_core::NullifierPublicKey([ + 116, 231, 246, 189, 145, 240, 37, 59, 219, 223, 216, 246, 116, 171, 223, 55, 197, 200, + 134, 192, 221, 40, 218, 167, 239, 5, 11, 95, 147, 247, 162, 226, ]); let expected_vsk: ViewingSecretKey = [ - 218, 219, 193, 132, 160, 6, 178, 194, 139, 248, 199, 81, 17, 133, 37, 201, 58, 104, 49, - 222, 187, 46, 156, 93, 14, 118, 209, 243, 38, 101, 77, 45, + 33, 155, 68, 60, 102, 70, 47, 105, 194, 129, 44, 26, 143, 198, 44, 244, 185, 31, 236, + 252, 205, 89, 138, 107, 39, 38, 154, 73, 109, 166, 41, 114, ]; let expected_vpk_as_bytes: [u8; 33] = [ - 3, 164, 65, 167, 88, 167, 179, 51, 159, 27, 241, 174, 77, 174, 142, 106, 128, 96, 69, - 74, 117, 231, 42, 193, 235, 153, 206, 116, 102, 7, 101, 192, 45, + 2, 78, 213, 113, 117, 105, 162, 248, 175, 68, 128, 232, 106, 204, 208, 159, 11, 78, 48, + 244, 127, 112, 46, 0, 93, 184, 1, 77, 132, 160, 75, 152, 88, ]; 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); + assert!(expected_npk == child_node.value.0.nullifier_public_key); 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()); } diff --git a/key_protocol/src/key_management/key_tree/keys_public.rs b/key_protocol/src/key_management/key_tree/keys_public.rs index 470acaaa..74caabf3 100644 --- a/key_protocol/src/key_management/key_tree/keys_public.rs +++ b/key_protocol/src/key_management/key_tree/keys_public.rs @@ -1,4 +1,4 @@ -use secp256k1::Scalar; +use k256::elliptic_curve::{PrimeField as _, sec1::ToEncodedPoint as _}; use serde::{Deserialize, Serialize}; use crate::key_management::key_tree::traits::KeyNode; @@ -16,14 +16,21 @@ impl ChildKeysPublic { fn compute_hash_value(&self, cci: u32) -> [u8; 64] { let mut hash_input = vec![]; - if 2_u32.pow(31) > cci { - // Non-harden - hash_input.extend_from_slice(self.cpk.value()); + if ((2_u32).pow(31)).cmp(&cci) == std::cmp::Ordering::Greater { + // Non-harden. + // BIP-032 compatibility requires 1-byte header from the public_key; + // Not stored in `self.cpk.value()`. + let sk = k256::SecretKey::from_bytes(self.csk.value().into()) + .expect("32 bytes, within curve order"); + let pk = sk.public_key(); + hash_input.extend_from_slice(&pk.to_encoded_point(true).as_bytes()); } else { - // Harden + // Harden. + hash_input.extend_from_slice(&[0_u8]); hash_input.extend_from_slice(self.csk.value()); } - hash_input.extend_from_slice(&cci.to_le_bytes()); + + hash_input.extend_from_slice(&cci.to_be_bytes()); hmac_sha512::HMAC::mac(hash_input, self.ccc) } @@ -33,7 +40,12 @@ impl KeyNode for ChildKeysPublic { fn root(seed: [u8; 64]) -> Self { let hash_value = hmac_sha512::HMAC::mac(seed, "LEE_master_pub"); - let csk = nssa::PrivateKey::try_new(*hash_value.first_chunk::<32>().unwrap()).unwrap(); + let csk = nssa::PrivateKey::try_new( + *hash_value + .first_chunk::<32>() + .expect("hash_value is 64 bytes, must be safe to get first 32"), + ) + .unwrap(); let ccc = *hash_value.last_chunk::<32>().unwrap(); let cpk = nssa::PublicKey::new_from_private_key(&csk); @@ -48,24 +60,20 @@ impl KeyNode for ChildKeysPublic { fn nth_child(&self, cci: u32) -> Self { let hash_value = self.compute_hash_value(cci); - let csk = secp256k1::SecretKey::from_byte_array( - *hash_value + let csk = nssa::PrivateKey::try_new({ + let hash_value = hash_value .first_chunk::<32>() - .expect("hash_value is 64 bytes, must be safe to get first 32"), - ) - .unwrap(); + .expect("hash_value is 64 bytes, must be safe to get first 32"); - let csk = nssa::PrivateKey::try_new( - csk.add_tweak(&Scalar::from_le_bytes(*self.csk.value()).unwrap()) - .expect("Expect a valid Scalar") - .secret_bytes(), - ) - .unwrap(); + let value_1 = + k256::Scalar::from_repr((*hash_value).into()).expect("Expect a valid k256 scalar"); + let value_2 = k256::Scalar::from_repr((*self.csk.value()).into()) + .expect("Expect a valid k256 scalar"); - assert!( - secp256k1::constants::CURVE_ORDER >= *csk.value(), - "Secret key cannot exceed curve order" - ); + let sum = value_1.add(&value_2); + sum.to_bytes().into() + }) + .expect("Expect a valid private key"); let ccc = *hash_value .last_chunk::<32>() @@ -94,8 +102,12 @@ impl KeyNode for ChildKeysPublic { } } -impl<'keys> From<&'keys ChildKeysPublic> for &'keys nssa::PrivateKey { - fn from(value: &'keys ChildKeysPublic) -> Self { +#[expect( + clippy::single_char_lifetime_names, + reason = "TODO add meaningful name" +)] +impl<'a> From<&'a ChildKeysPublic> for &'a nssa::PrivateKey { + fn from(value: &'a ChildKeysPublic) -> Self { &value.csk } } @@ -107,7 +119,7 @@ mod tests { use super::*; #[test] - fn master_keys_generation() { + fn test_master_keys_generation() { let seed = [ 88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173, 134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87, @@ -126,6 +138,7 @@ mod tests { 202, 148, 181, 228, 35, 222, 58, 84, 156, 24, 146, 86, ]) .unwrap(); + let expected_cpk: PublicKey = PublicKey::try_new([ 219, 141, 130, 105, 11, 203, 187, 124, 112, 75, 223, 22, 11, 164, 153, 127, 59, 247, 244, 166, 75, 66, 242, 224, 35, 156, 161, 75, 41, 51, 76, 245, @@ -138,7 +151,7 @@ mod tests { } #[test] - fn harden_child_keys_generation() { + fn test_harden_child_keys_generation() { let seed = [ 88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173, 134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87, @@ -149,26 +162,20 @@ mod tests { let cci = (2_u32).pow(31) + 13; let child_keys = ChildKeysPublic::nth_child(&root_keys, cci); - print!( - "{} {}", - child_keys.csk.value()[0], - child_keys.csk.value()[1] - ); - let expected_ccc = [ - 126, 175, 244, 41, 41, 173, 134, 103, 139, 140, 195, 86, 194, 147, 116, 48, 71, 107, - 253, 235, 114, 139, 60, 115, 226, 205, 215, 248, 240, 190, 196, 6, + 149, 226, 13, 4, 194, 12, 69, 29, 9, 234, 209, 119, 98, 4, 128, 91, 37, 103, 192, 31, + 130, 126, 123, 20, 90, 34, 173, 209, 101, 248, 155, 36, ]; let expected_csk: PrivateKey = PrivateKey::try_new([ - 128, 148, 53, 165, 222, 155, 163, 108, 186, 182, 124, 67, 90, 86, 59, 123, 95, 224, - 171, 4, 51, 131, 254, 57, 241, 178, 82, 161, 204, 206, 79, 107, + 9, 65, 33, 228, 25, 82, 219, 117, 91, 217, 11, 223, 144, 85, 246, 26, 123, 216, 107, + 213, 33, 52, 188, 22, 198, 246, 71, 46, 245, 174, 16, 47, ]) .unwrap(); let expected_cpk: PublicKey = PublicKey::try_new([ - 149, 240, 55, 15, 178, 67, 245, 254, 44, 141, 95, 223, 238, 62, 85, 11, 248, 9, 11, 40, - 69, 211, 116, 13, 189, 35, 8, 95, 233, 154, 129, 58, + 142, 143, 238, 159, 105, 165, 224, 252, 108, 62, 53, 209, 176, 219, 249, 38, 90, 241, + 201, 81, 194, 146, 236, 5, 83, 152, 238, 243, 138, 16, 229, 15, ]) .unwrap(); @@ -178,7 +185,7 @@ mod tests { } #[test] - fn nonharden_child_keys_generation() { + fn test_nonharden_child_keys_generation() { let seed = [ 88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173, 134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87, @@ -189,26 +196,20 @@ mod tests { let cci = 13; let child_keys = ChildKeysPublic::nth_child(&root_keys, cci); - print!( - "{} {}", - child_keys.csk.value()[0], - child_keys.csk.value()[1] - ); - let expected_ccc = [ - 50, 29, 113, 102, 49, 130, 64, 0, 247, 95, 135, 187, 118, 162, 65, 65, 194, 53, 189, - 242, 66, 178, 168, 2, 51, 193, 155, 72, 209, 2, 207, 251, + 79, 228, 242, 119, 211, 203, 198, 175, 95, 36, 4, 234, 139, 45, 137, 138, 54, 211, 187, + 16, 28, 79, 80, 232, 216, 101, 145, 19, 101, 220, 217, 141, ]; let expected_csk: PrivateKey = PrivateKey::try_new([ - 162, 32, 211, 190, 180, 74, 151, 246, 189, 93, 8, 57, 182, 239, 125, 245, 192, 255, 24, - 186, 251, 23, 194, 186, 252, 121, 190, 54, 147, 199, 1, 109, + 185, 147, 32, 242, 145, 91, 123, 77, 42, 33, 134, 84, 12, 165, 117, 70, 158, 201, 95, + 153, 14, 12, 92, 235, 128, 156, 194, 169, 68, 35, 165, 127, ]) .unwrap(); let expected_cpk: PublicKey = PublicKey::try_new([ - 183, 48, 207, 170, 221, 111, 118, 9, 40, 67, 123, 162, 159, 169, 34, 157, 23, 37, 232, - 102, 231, 187, 199, 191, 205, 146, 159, 22, 79, 100, 10, 223, + 119, 16, 145, 121, 97, 244, 186, 35, 136, 34, 140, 171, 206, 139, 11, 208, 207, 121, + 158, 45, 28, 22, 140, 98, 161, 179, 212, 173, 238, 220, 2, 34, ]) .unwrap(); @@ -218,7 +219,7 @@ mod tests { } #[test] - fn edge_case_child_keys_generation_2_power_31() { + fn test_edge_case_child_keys_generation_2_power_31() { let seed = [ 88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173, 134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87, @@ -230,19 +231,19 @@ mod tests { let child_keys = ChildKeysPublic::nth_child(&root_keys, cci); let expected_ccc = [ - 101, 15, 69, 152, 144, 22, 105, 89, 175, 21, 13, 50, 160, 167, 93, 80, 94, 99, 192, - 252, 1, 126, 196, 217, 149, 164, 60, 75, 237, 90, 104, 83, + 221, 208, 47, 189, 174, 152, 33, 25, 151, 114, 233, 191, 57, 15, 40, 140, 46, 87, 126, + 58, 215, 40, 246, 111, 166, 113, 183, 145, 173, 11, 27, 182, ]; let expected_csk: PrivateKey = PrivateKey::try_new([ - 46, 196, 131, 199, 190, 180, 250, 222, 41, 188, 221, 156, 255, 239, 251, 207, 239, 202, - 166, 216, 107, 236, 195, 48, 167, 69, 97, 13, 132, 117, 76, 89, + 223, 29, 87, 189, 126, 24, 117, 225, 190, 57, 0, 143, 207, 168, 231, 139, 170, 192, 81, + 254, 126, 10, 115, 42, 141, 157, 70, 171, 199, 231, 198, 132, ]) .unwrap(); let expected_cpk: PublicKey = PublicKey::try_new([ - 93, 151, 154, 238, 175, 198, 53, 146, 255, 43, 37, 52, 214, 165, 69, 161, 38, 20, 68, - 166, 143, 80, 149, 216, 124, 203, 240, 114, 168, 111, 33, 83, + 96, 123, 245, 51, 214, 216, 215, 205, 70, 145, 105, 221, 166, 169, 122, 27, 94, 112, + 228, 110, 249, 177, 85, 173, 180, 248, 185, 199, 112, 246, 83, 33, ]) .unwrap(); diff --git a/key_protocol/src/key_management/mod.rs b/key_protocol/src/key_management/mod.rs index e29e5862..dcdaff45 100644 --- a/key_protocol/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -16,7 +16,7 @@ pub type PublicAccountSigningKey = [u8; 32]; pub struct KeyChain { pub secret_spending_key: SecretSpendingKey, pub private_key_holder: PrivateKeyHolder, - pub nullifer_public_key: NullifierPublicKey, + pub nullifier_public_key: NullifierPublicKey, pub viewing_public_key: ViewingPublicKey, } @@ -30,13 +30,13 @@ impl KeyChain { let private_key_holder = secret_spending_key.produce_private_key_holder(None); - let nullifer_public_key = private_key_holder.generate_nullifier_public_key(); + let nullifier_public_key = private_key_holder.generate_nullifier_public_key(); let viewing_public_key = private_key_holder.generate_viewing_public_key(); Self { secret_spending_key, private_key_holder, - nullifer_public_key, + nullifier_public_key, viewing_public_key, } } @@ -50,13 +50,13 @@ impl KeyChain { let private_key_holder = secret_spending_key.produce_private_key_holder(None); - let nullifer_public_key = private_key_holder.generate_nullifier_public_key(); + let nullifier_public_key = private_key_holder.generate_nullifier_public_key(); let viewing_public_key = private_key_holder.generate_viewing_public_key(); Self { secret_spending_key, private_key_holder, - nullifer_public_key, + nullifier_public_key, viewing_public_key, } } @@ -93,7 +93,7 @@ mod tests { // Check that key holder fields are initialized with expected types assert_ne!( - account_id_key_holder.nullifer_public_key.as_ref(), + account_id_key_holder.nullifier_public_key.as_ref(), &[0_u8; 32] ); } @@ -119,7 +119,7 @@ mod tests { let utxo_secret_key_holder = top_secret_key_holder.produce_private_key_holder(None); - let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); + let nullifier_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); let pub_account_signing_key = nssa::PrivateKey::new_os_random(); @@ -150,7 +150,7 @@ mod tests { println!("Account {:?}", account.value().to_base58()); println!( "Nulifier public key {:?}", - hex::encode(nullifer_public_key.to_byte_array()) + hex::encode(nullifier_public_key.to_byte_array()) ); println!( "Viewing public key {:?}", @@ -183,7 +183,7 @@ mod tests { fn non_trivial_chain_index() { let keys = account_with_chain_index_2_for_tests(); - let eph_key_holder = EphemeralKeyHolder::new(&keys.nullifer_public_key); + let eph_key_holder = EphemeralKeyHolder::new(&keys.nullifier_public_key); let key_sender = eph_key_holder.calculate_shared_secret_sender(&keys.viewing_public_key); let key_receiver = keys.calculate_shared_secret_receiver( diff --git a/key_protocol/src/key_management/secret_holders.rs b/key_protocol/src/key_management/secret_holders.rs index db39757e..9643abe0 100644 --- a/key_protocol/src/key_management/secret_holders.rs +++ b/key_protocol/src/key_management/secret_holders.rs @@ -79,6 +79,7 @@ impl SeedHolder { impl SecretSpendingKey { #[must_use] + #[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")] pub fn generate_nullifier_secret_key(&self, index: Option) -> NullifierSecretKey { const PREFIX: &[u8; 8] = b"LEE/keys"; const SUFFIX_1: &[u8; 1] = &[1]; @@ -93,13 +94,14 @@ impl SecretSpendingKey { hasher.update(PREFIX); hasher.update(self.0); hasher.update(SUFFIX_1); - hasher.update(index.to_le_bytes()); + hasher.update(index.to_be_bytes()); hasher.update(SUFFIX_2); ::from(hasher.finalize_fixed()) } #[must_use] + #[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")] pub fn generate_viewing_secret_key(&self, index: Option) -> ViewingSecretKey { const PREFIX: &[u8; 8] = b"LEE/keys"; const SUFFIX_1: &[u8; 1] = &[2]; @@ -114,7 +116,7 @@ impl SecretSpendingKey { hasher.update(PREFIX); hasher.update(self.0); hasher.update(SUFFIX_1); - hasher.update(index.to_le_bytes()); + hasher.update(index.to_be_bytes()); hasher.update(SUFFIX_2); hasher.finalize_fixed().into() diff --git a/key_protocol/src/key_protocol_core/mod.rs b/key_protocol/src/key_protocol_core/mod.rs index abc1135f..65c592e3 100644 --- a/key_protocol/src/key_protocol_core/mod.rs +++ b/key_protocol/src/key_protocol_core/mod.rs @@ -46,7 +46,7 @@ impl NSSAUserData { ) -> bool { let mut check_res = true; for (account_id, (key, _)) in accounts_keys_map { - let expected_account_id = nssa::AccountId::from(&key.nullifer_public_key); + let expected_account_id = nssa::AccountId::from(&key.nullifier_public_key); if expected_account_id != *account_id { println!("{expected_account_id}, {account_id}"); check_res = false; diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index e1b6805f..1d7fc46c 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -18,6 +18,7 @@ sha2.workspace = true rand.workspace = true borsh.workspace = true hex.workspace = true +k256.workspace = true secp256k1 = "0.31.1" risc0-binfmt = "3.0.2" log.workspace = true diff --git a/nssa/src/error.rs b/nssa/src/error.rs index 3576b366..5eedb154 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -29,7 +29,7 @@ pub enum NssaError { Io(#[from] io::Error), #[error("Invalid Public Key")] - InvalidPublicKey(#[source] secp256k1::Error), + InvalidPublicKey(#[source] k256::schnorr::Error), #[error("Risc0 error: {0}")] ProgramWriteInputFailed(String), diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 63377f15..891d12af 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -1,4 +1,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; +use k256::ecdsa::signature::RandomizedSigner; +//use k256::schnorr::signature::Verifier; pub use private_key::PrivateKey; pub use public_key::PublicKey; use rand::{RngCore as _, rngs::OsRng}; @@ -25,6 +27,31 @@ impl Signature { Self::new_with_aux_random(key, message, aux_random) } +pub(crate) fn new_with_aux_random( + key: &PrivateKey, + message: &[u8], + mut aux_random: [u8; 32], +) -> Self { + let value = { + // Create signing key from raw bytes + let signing_key = k256::schnorr::SigningKey::from_bytes(key.value()).unwrap(); + + // k256 expects a 32-byte message digest for Schnorr (BIP-340) + let msg: &[u8; 32] = message.try_into().expect("message must be 32 bytes"); + + // Convert aux_random into the expected type + let aux: k256::elliptic_curve::FieldBytes = aux_random.into(); + + // Sign with auxiliary randomness + let signature: k256::schnorr::Signature = signing_key.sign_with_aux_rng(&aux, msg); + + signature.to_bytes() + }; + + Self { value } +} + + /* pub(crate) fn new_with_aux_random( key: &PrivateKey, message: &[u8], @@ -39,14 +66,47 @@ impl Signature { }; Self { value } } - +*/ #[must_use] - pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool { + pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool /*{ + // Convert signature bytes into Signature object + let sig_slice: &[u8] = &self.value; + let sig = match k256::schnorr::Signature::try_from(sig_slice) { + Ok(s) => s, + Err(_) => {panic!("TEST"); //return false + }, + }; + + // Convert x-only public key to VerifyingKey + let vk = match k256::schnorr::VerifyingKey::from_bytes(public_key.value()) { + Ok(vk) => vk, + Err(_) => {panic!("TEST"); //return false + }, + }; + + // Verify the signature + // vk.verify(bytes, &sig).is_ok() + + // let msg = hex32(&v.message); + // let sig_bytes = hex64(&v.signature); + // let sig = Signature::try_from(&sig_bytes[..]).unwrap(); + + // let vk_bytes = hex32(&v.public_key); +// let vk = VerifyingKey::from_bytes(&vk_bytes).unwrap(); + + // --- VERIFY --- + // let verify_ok = vk.verify_prehash(&msg, &sig).is_ok(); + + + }*/{ + let pk = secp256k1::XOnlyPublicKey::from_byte_array(*public_key.value()).unwrap(); let secp = secp256k1::Secp256k1::new(); let sig = secp256k1::schnorr::Signature::from_byte_array(self.value); secp.verify_schnorr(&sig, bytes, &pk).is_ok() } + + } #[cfg(test)] diff --git a/nssa/src/signature/private_key.rs b/nssa/src/signature/private_key.rs index d8ece0e0..c82499fe 100644 --- a/nssa/src/signature/private_key.rs +++ b/nssa/src/signature/private_key.rs @@ -21,7 +21,7 @@ impl PrivateKey { } fn is_valid_key(value: [u8; 32]) -> bool { - secp256k1::SecretKey::from_byte_array(value).is_ok() + k256::SecretKey::from_bytes(&value.into()).is_ok() } pub fn try_new(value: [u8; 32]) -> Result { diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 9cdac761..873b243b 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -1,4 +1,5 @@ use borsh::{BorshDeserialize, BorshSerialize}; +use k256::elliptic_curve::sec1::ToEncodedPoint as _; use nssa_core::account::AccountId; use serde::{Deserialize, Serialize}; use sha2::{Digest as _, Sha256}; @@ -27,19 +28,24 @@ impl PublicKey { #[must_use] pub fn new_from_private_key(key: &PrivateKey) -> Self { let value = { - let secret_key = secp256k1::SecretKey::from_byte_array(*key.value()).unwrap(); - let public_key = - secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &secret_key); - let (x_only, _) = public_key.x_only_public_key(); - x_only.serialize() + let secret_key = k256::SecretKey::from_bytes(&(*key.value()).into()) + .expect("Expect a valid private key"); + + let encoded = secret_key.public_key().to_encoded_point(false); + let x_only = encoded + .x() + .expect("Expect k256 point to have a x-coordinate"); + + *x_only.first_chunk().expect("x_only is exactly 32 bytes") }; Self(value) } pub fn try_new(value: [u8; 32]) -> Result { - // Check point is valid - let _ = secp256k1::XOnlyPublicKey::from_byte_array(value) - .map_err(NssaError::InvalidPublicKey)?; + // Check point is a valid x-only public key + let _ = + k256::schnorr::VerifyingKey::from_bytes(&value).map_err(NssaError::InvalidPublicKey)?; + Ok(Self(value)) } From cfac69fffbf2b3a6c72dc0f944b549b66ce0b65e Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Mon, 23 Mar 2026 18:28:50 -0400 Subject: [PATCH 2/3] fix signatures to use k256 --- nssa/Cargo.toml | 1 - nssa/src/signature/mod.rs | 103 +++++++++++++------------------------- 2 files changed, 34 insertions(+), 70 deletions(-) diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 1d7fc46c..239322a5 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -19,7 +19,6 @@ rand.workspace = true borsh.workspace = true hex.workspace = true k256.workspace = true -secp256k1 = "0.31.1" risc0-binfmt = "3.0.2" log.workspace = true diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 891d12af..3a594da6 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -1,6 +1,6 @@ +use std::str::FromStr; + use borsh::{BorshDeserialize, BorshSerialize}; -use k256::ecdsa::signature::RandomizedSigner; -//use k256::schnorr::signature::Verifier; pub use private_key::PrivateKey; pub use public_key::PublicKey; use rand::{RngCore as _, rngs::OsRng}; @@ -14,11 +14,27 @@ pub struct Signature { } impl std::fmt::Debug for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +impl std::fmt::Display for Signature { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", hex::encode(self.value)) } } +impl FromStr for Signature { + type Err = hex::FromHexError; + + fn from_str(s: &str) -> Result { + let mut bytes = [0_u8; 64]; + hex::decode_to_slice(s, &mut bytes)?; + Ok(Self { value: bytes }) + } +} + impl Signature { #[must_use] pub fn new(key: &PrivateKey, message: &[u8]) -> Self { @@ -27,86 +43,35 @@ impl Signature { Self::new_with_aux_random(key, message, aux_random) } -pub(crate) fn new_with_aux_random( - key: &PrivateKey, - message: &[u8], - mut aux_random: [u8; 32], -) -> Self { - let value = { - // Create signing key from raw bytes - let signing_key = k256::schnorr::SigningKey::from_bytes(key.value()).unwrap(); - - // k256 expects a 32-byte message digest for Schnorr (BIP-340) - let msg: &[u8; 32] = message.try_into().expect("message must be 32 bytes"); - - // Convert aux_random into the expected type - let aux: k256::elliptic_curve::FieldBytes = aux_random.into(); - - // Sign with auxiliary randomness - let signature: k256::schnorr::Signature = signing_key.sign_with_aux_rng(&aux, msg); - - signature.to_bytes() - }; - - Self { value } -} - - /* pub(crate) fn new_with_aux_random( key: &PrivateKey, message: &[u8], aux_random: [u8; 32], ) -> Self { let value = { - let secp = secp256k1::Secp256k1::new(); - let secret_key = secp256k1::SecretKey::from_byte_array(*key.value()).unwrap(); - let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key); - let signature = secp.sign_schnorr_with_aux_rand(message, &keypair, &aux_random); - signature.to_byte_array() + let signing_key = k256::schnorr::SigningKey::from_bytes(key.value()) + .expect("Expect valid signing key"); + signing_key + .sign_raw(message, &aux_random) + .expect("Expect to produce a valid signature") + .to_bytes() }; + Self { value } } -*/ + #[must_use] - pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool /*{ - // Convert signature bytes into Signature object - let sig_slice: &[u8] = &self.value; - let sig = match k256::schnorr::Signature::try_from(sig_slice) { - Ok(s) => s, - Err(_) => {panic!("TEST"); //return false - }, - }; + pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool { + let Ok(pk) = k256::schnorr::VerifyingKey::from_bytes(public_key.value()) else { + return false; + }; - // Convert x-only public key to VerifyingKey - let vk = match k256::schnorr::VerifyingKey::from_bytes(public_key.value()) { - Ok(vk) => vk, - Err(_) => {panic!("TEST"); //return false - }, - }; + let Ok(sig) = k256::schnorr::Signature::try_from(self.value.as_slice()) else { + return false; + }; - // Verify the signature - // vk.verify(bytes, &sig).is_ok() - - // let msg = hex32(&v.message); - // let sig_bytes = hex64(&v.signature); - // let sig = Signature::try_from(&sig_bytes[..]).unwrap(); - - // let vk_bytes = hex32(&v.public_key); -// let vk = VerifyingKey::from_bytes(&vk_bytes).unwrap(); - - // --- VERIFY --- - // let verify_ok = vk.verify_prehash(&msg, &sig).is_ok(); - - - }*/{ - - let pk = secp256k1::XOnlyPublicKey::from_byte_array(*public_key.value()).unwrap(); - let secp = secp256k1::Secp256k1::new(); - let sig = secp256k1::schnorr::Signature::from_byte_array(self.value); - secp.verify_schnorr(&sig, bytes, &pk).is_ok() + pk.verify_raw(bytes, &sig).is_ok() } - - } #[cfg(test)] From 8a6409d43bb33c89d6d77da3097c31a640565e06 Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:06:40 -0400 Subject: [PATCH 3/3] fixed error --- nssa/src/error.rs | 3 +++ nssa/src/signature/public_key.rs | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nssa/src/error.rs b/nssa/src/error.rs index 5eedb154..60ca603c 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -31,6 +31,9 @@ pub enum NssaError { #[error("Invalid Public Key")] InvalidPublicKey(#[source] k256::schnorr::Error), + #[error("Invalid hex for public key")] + InvalidHexPublicKey(hex::FromHexError), + #[error("Risc0 error: {0}")] ProgramWriteInputFailed(String), diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 78852dda..ebec6b62 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -28,8 +28,7 @@ impl FromStr for PublicKey { fn from_str(s: &str) -> Result { let mut bytes = [0_u8; 32]; - hex::decode_to_slice(s, &mut bytes) - .map_err(|_err| NssaError::InvalidPublicKey(secp256k1::Error::InvalidPublicKey))?; + hex::decode_to_slice(s, &mut bytes).map_err(NssaError::InvalidHexPublicKey)?; Self::try_new(bytes) } }