Skip to main content

nostro2_nips/
lib.rs

1#![warn(
2    clippy::all,
3    clippy::missing_errors_doc,
4    clippy::style,
5    clippy::unseparated_literal_suffix,
6    clippy::pedantic,
7    clippy::nursery
8)]
9mod nip_17;
10mod nip_44;
11mod nip_46;
12mod nip_59;
13
14pub use nip_17::*;
15pub use nip_44::*;
16pub use nip_46::*;
17pub use nip_59::*;
18#[cfg(test)]
19mod tests {
20    use nostro2_traits::{NostrKeypair, NostrSigner, SignerError};
21
22    /// Test-only keypair that wraps k256 directly. Not part of the public API.
23    #[derive(Clone)]
24    pub struct NipTester(k256::schnorr::SigningKey);
25
26    impl NipTester {
27        pub fn from_hex(s: &str) -> Option<Self> {
28            use nostro2_traits::hex::FromHex as _;
29            let bytes = s.decode_hex().ok()?;
30            let field_bytes: &k256::FieldBytes = bytes.as_slice().try_into().ok()?;
31            k256::schnorr::SigningKey::from_bytes(field_bytes)
32                .ok()
33                .map(Self)
34        }
35        pub fn _peer_one() -> Self {
36            Self::from_hex("30af2e27172df3fa2c202cf6a49bed35a2e0cb7994d9b437b2d945a92824c22a")
37                .unwrap()
38        }
39        pub fn _peer_two() -> Self {
40            Self::from_hex("dd33562d81e8d00bfbe14708acdff85dffe6e6b6ca073ba3acdc6adb140cb8f1")
41                .unwrap()
42        }
43        pub fn _peer_three() -> Self {
44            Self::from_hex("3410d9bd915643276a30795d4669a93469810a76901ce58f148c2cb84fcdc1b6")
45                .unwrap()
46        }
47    }
48
49    impl std::str::FromStr for NipTester {
50        type Err = ();
51        fn from_str(s: &str) -> Result<Self, Self::Err> {
52            Self::from_hex(s).ok_or(())
53        }
54    }
55
56    impl NostrSigner for NipTester {
57        // Mirrors `K256Keypair::sign_prehash`: BIP-340 §3.2 aux randomness must
58        // be freshly drawn per call. The deterministic `PrehashSigner::sign_prehash`
59        // path is *not* what production uses and must not be copied here, even
60        // for tests — a developer reading this file as a template would inherit
61        // the wrong invariant.
62        fn sign_prehash(&self, id: &[u8; 32]) -> Result<[u8; 64], SignerError> {
63            let mut aux_rand = [0_u8; 32];
64            getrandom::fill(&mut aux_rand)
65                .map_err(|e| SignerError::Backend(format!("getrandom: {e}")))?;
66            let sig = self
67                .0
68                .sign_raw(id, &aux_rand)
69                .map_err(|_| SignerError::InvalidSignature)?;
70            Ok(sig.to_bytes())
71        }
72        fn pubkey_bytes(&self) -> [u8; 32] {
73            self.0.verifying_key().to_bytes().into()
74        }
75    }
76
77    impl NostrKeypair for NipTester {
78        fn secret_bytes(&self) -> [u8; 32] {
79            self.0.to_bytes().into()
80        }
81        fn generate() -> Self {
82            let mut secret = [0_u8; 32];
83            getrandom::fill(&mut secret).expect("getrandom failed");
84            let field_bytes = k256::FieldBytes::from(secret);
85            Self(k256::schnorr::SigningKey::from_bytes(&field_bytes).expect("invalid key bytes"))
86        }
87        fn ecdh_x(&self, peer_xonly: &[u8; 32]) -> Result<[u8; 32], SignerError> {
88            let mut compressed = [0_u8; 33];
89            compressed[0] = 0x02;
90            compressed[1..].copy_from_slice(peer_xonly);
91            let public_key = k256::PublicKey::from_sec1_bytes(&compressed)
92                .map_err(|_| SignerError::InvalidPublicKey)?;
93            let secret_key = k256::SecretKey::from_slice(&self.0.to_bytes())
94                .map_err(|_| SignerError::InvalidSignature)?;
95            let shared =
96                k256::ecdh::diffie_hellman(secret_key.to_nonzero_scalar(), public_key.as_affine());
97            let mut point = [0_u8; 32];
98            point.copy_from_slice(shared.raw_secret_bytes().as_slice());
99            Ok(point)
100        }
101    }
102
103    // Nip44 / Nip17 / Nip46 / Nip59 are blanket-implemented for every
104    // `NostrKeypair`, so `NipTester` gets them all for free.
105}