Skip to main content

fips_identity/
encoding.rs

1//! NIP-19 bech32 encoding for Nostr keys.
2
3use bech32::{Bech32, Hrp};
4use secp256k1::{SecretKey, XOnlyPublicKey};
5
6use super::IdentityError;
7
8/// Human-readable part for npub (NIP-19).
9const NPUB_HRP: Hrp = Hrp::parse_unchecked("npub");
10
11/// Human-readable part for nsec (NIP-19).
12const NSEC_HRP: Hrp = Hrp::parse_unchecked("nsec");
13
14/// Encode an x-only public key as a bech32 npub string (NIP-19).
15pub fn encode_npub(pubkey: &XOnlyPublicKey) -> String {
16    bech32::encode::<Bech32>(NPUB_HRP, &pubkey.serialize()).expect("npub encoding cannot fail")
17}
18
19/// Decode an npub string to an x-only public key.
20pub fn decode_npub(npub: &str) -> Result<XOnlyPublicKey, IdentityError> {
21    let (hrp, data) = bech32::decode(npub)?;
22
23    if hrp != NPUB_HRP {
24        return Err(IdentityError::InvalidNpubPrefix(hrp.to_string()));
25    }
26
27    if data.len() != 32 {
28        return Err(IdentityError::InvalidNpubLength(data.len()));
29    }
30
31    let pubkey = XOnlyPublicKey::from_slice(&data)?;
32    Ok(pubkey)
33}
34
35/// Encode a secret key as a bech32 nsec string (NIP-19).
36pub fn encode_nsec(secret_key: &SecretKey) -> String {
37    bech32::encode::<Bech32>(NSEC_HRP, &secret_key.secret_bytes())
38        .expect("nsec encoding cannot fail")
39}
40
41/// Decode an nsec string to a secret key.
42pub fn decode_nsec(nsec: &str) -> Result<SecretKey, IdentityError> {
43    let (hrp, data) = bech32::decode(nsec)?;
44
45    if hrp != NSEC_HRP {
46        return Err(IdentityError::InvalidNsecPrefix(hrp.to_string()));
47    }
48
49    if data.len() != 32 {
50        return Err(IdentityError::InvalidNsecLength(data.len()));
51    }
52
53    let secret_key = SecretKey::from_slice(&data)?;
54    Ok(secret_key)
55}
56
57/// Decode a secret key from either nsec (bech32) or hex format.
58pub fn decode_secret(s: &str) -> Result<SecretKey, IdentityError> {
59    if s.starts_with("nsec1") {
60        decode_nsec(s)
61    } else {
62        let bytes = hex::decode(s)?;
63        if bytes.len() != 32 {
64            return Err(IdentityError::InvalidNsecLength(bytes.len()));
65        }
66        let secret_key = SecretKey::from_slice(&bytes)?;
67        Ok(secret_key)
68    }
69}