Skip to main content

fips_identity/
lib.rs

1//! FIPS Identity System
2//!
3//! Node identity based on Nostr keypairs (secp256k1). The node_addr is derived
4//! from the public key via SHA-256, and the FIPS address uses an IPv6-compatible
5//! format with the 0xfd prefix.
6
7mod address;
8mod auth;
9mod encoding;
10mod local;
11mod node_addr;
12mod peer;
13
14use std::sync::LazyLock;
15
16use secp256k1::{All, Secp256k1};
17use sha2::{Digest, Sha256};
18use thiserror::Error;
19
20pub use address::FipsAddress;
21pub use auth::{AuthChallenge, AuthResponse};
22pub use encoding::{decode_npub, decode_nsec, decode_secret, encode_npub, encode_nsec};
23pub use local::Identity;
24pub use node_addr::NodeAddr;
25pub use peer::PeerIdentity;
26
27/// Shared secp256k1 context reused by identity signing and verification.
28///
29/// Constructing a context allocates and initializes blinding tables, while
30/// `Secp256k1` is designed to be shared safely after initialization.
31pub(crate) static SECP: LazyLock<Secp256k1<All>> = LazyLock::new(Secp256k1::new);
32
33/// FIPS address prefix (IPv6 ULA range).
34pub const FIPS_ADDRESS_PREFIX: u8 = 0xfd;
35
36/// Errors that can occur in identity operations.
37#[derive(Debug, Error)]
38pub enum IdentityError {
39    #[error("invalid secret key: {0}")]
40    InvalidSecretKey(#[from] secp256k1::Error),
41
42    #[error("signature verification failed")]
43    SignatureVerificationFailed,
44
45    #[error("invalid node_addr length: expected 16, got {0}")]
46    InvalidNodeAddrLength(usize),
47
48    #[error("invalid address length: expected 16, got {0}")]
49    InvalidAddressLength(usize),
50
51    #[error("invalid address prefix: expected 0xfd, got 0x{0:02x}")]
52    InvalidAddressPrefix(u8),
53
54    #[error("bech32 encoding error: {0}")]
55    Bech32Encode(#[from] bech32::EncodeError),
56
57    #[error("bech32 decoding error: {0}")]
58    Bech32Decode(#[from] bech32::DecodeError),
59
60    #[error("invalid npub: expected 'npub' prefix, got '{0}'")]
61    InvalidNpubPrefix(String),
62
63    #[error("invalid npub: expected 32 bytes, got {0}")]
64    InvalidNpubLength(usize),
65
66    #[error("invalid nsec: expected 'nsec' prefix, got '{0}'")]
67    InvalidNsecPrefix(String),
68
69    #[error("invalid nsec: expected 32 bytes, got {0}")]
70    InvalidNsecLength(usize),
71
72    #[error("invalid hex encoding: {0}")]
73    InvalidHex(#[from] hex::FromHexError),
74}
75
76/// Compute SHA-256 hash of data.
77fn sha256(data: &[u8]) -> [u8; 32] {
78    let mut hasher = Sha256::new();
79    hasher.update(data);
80    let result = hasher.finalize();
81    let mut hash = [0u8; 32];
82    hash.copy_from_slice(&result);
83    hash
84}
85
86/// Encode bytes as lowercase hex string.
87fn hex_encode(bytes: &[u8]) -> String {
88    bytes.iter().map(|b| format!("{:02x}", b)).collect()
89}
90
91#[cfg(test)]
92mod tests;