keys.go raw

   1  package bech32encoding
   2  
   3  import (
   4  	"bytes"
   5  	"strings"
   6  
   7  	"next.orly.dev/pkg/nostr/crypto/ec"
   8  	"next.orly.dev/pkg/nostr/crypto/ec/bech32"
   9  	"next.orly.dev/pkg/nostr/crypto/ec/schnorr"
  10  	"next.orly.dev/pkg/nostr/crypto/ec/secp256k1"
  11  	"next.orly.dev/pkg/nostr/encoders/hex"
  12  	"next.orly.dev/pkg/nostr/utils"
  13  	"next.orly.dev/pkg/nostr/utils/constraints"
  14  	"next.orly.dev/pkg/lol/chk"
  15  	"next.orly.dev/pkg/lol/log"
  16  )
  17  
  18  const (
  19  	// MinKeyStringLen is 56 because Bech32 needs 52 characters plus 4 for the HRP,
  20  	// any string shorter than this cannot be a nostr key.
  21  	MinKeyStringLen = 56
  22  	// HexKeyLen is the length of a nostr key in hexadecimal.
  23  	HexKeyLen = 64
  24  	// Bech32HRPLen is the length of the standard nostr keys, nsec and npub.
  25  	Bech32HRPLen = 4
  26  )
  27  
  28  var (
  29  	// SecHRP is the standard Human Readable Prefix (HRP) for a nostr secret key in bech32 encoding - nsec
  30  	SecHRP = []byte("nsec")
  31  	// PubHRP is the standard Human Readable Prefix (HRP) for a nostr public key in bech32 encoding - nsec
  32  	PubHRP = []byte("npub")
  33  )
  34  
  35  // ConvertForBech32 performs the bit expansion required for encoding into Bech32.
  36  func ConvertForBech32(b8 []byte) (b5 []byte, err error) {
  37  	return bech32.ConvertBits(
  38  		b8, 8, 5,
  39  		true,
  40  	)
  41  }
  42  
  43  // ConvertFromBech32 collapses together the bit expanded 5 bit numbers encoded in bech32.
  44  func ConvertFromBech32(b5 []byte) (b8 []byte, err error) {
  45  	return bech32.ConvertBits(
  46  		b5, 5, 8,
  47  		true,
  48  	)
  49  }
  50  
  51  // SecretKeyToNsec encodes an secp256k1 secret key as a Bech32 string (nsec).
  52  func SecretKeyToNsec(sk *secp256k1.SecretKey) (encoded []byte, err error) {
  53  	var b5 []byte
  54  	if b5, err = ConvertForBech32(sk.Serialize()); chk.E(err) {
  55  		return
  56  	}
  57  	return bech32.Encode(SecHRP, b5)
  58  }
  59  
  60  // PublicKeyToNpub encodes a public key as a bech32 string (npub).
  61  func PublicKeyToNpub(pk *secp256k1.PublicKey) (encoded []byte, err error) {
  62  	var bits5 []byte
  63  	pubKeyBytes := schnorr.SerializePubKey(pk)
  64  	if bits5, err = ConvertForBech32(pubKeyBytes); chk.E(err) {
  65  		return
  66  	}
  67  	return bech32.Encode(PubHRP, bits5)
  68  }
  69  
  70  // NsecToSecretKey decodes a nostr secret key (nsec) and returns the secp256k1
  71  // secret key.
  72  func NsecToSecretKey[V constraints.Bytes](encoded V) (
  73  	sk *secp256k1.SecretKey, err error,
  74  ) {
  75  	var b8 []byte
  76  	if b8, err = NsecToBytes(encoded); chk.E(err) {
  77  		return
  78  	}
  79  	sk = secp256k1.SecKeyFromBytes(b8)
  80  	return
  81  }
  82  
  83  // NsecToBytes converts a nostr bech32 encoded secret key to raw bytes.
  84  func NsecToBytes[V constraints.Bytes](encoded V) (sk []byte, err error) {
  85  	var b5, hrp []byte
  86  	if hrp, b5, err = bech32.Decode([]byte(encoded)); chk.E(err) {
  87  		return
  88  	}
  89  	if !utils.FastEqual(hrp, SecHRP) {
  90  		err = log.E.Err(
  91  			"wrong human readable part, got '%s' want '%s'",
  92  			hrp, SecHRP,
  93  		)
  94  		return
  95  	}
  96  	if sk, err = ConvertFromBech32(b5); chk.E(err) {
  97  		return
  98  	}
  99  	sk = sk[:secp256k1.SecKeyBytesLen]
 100  	return
 101  }
 102  
 103  // NpubToBytes converts a bech32 encoded public key to raw bytes.
 104  func NpubToBytes[V constraints.Bytes](encoded V) (pk []byte, err error) {
 105  	var b5, hrp []byte
 106  	if hrp, b5, err = bech32.Decode([]byte(encoded)); chk.E(err) {
 107  		return
 108  	}
 109  	if !utils.FastEqual(hrp, PubHRP) {
 110  		err = log.E.Err(
 111  			"wrong human readable part, got '%s' want '%s'",
 112  			hrp, SecHRP,
 113  		)
 114  		return
 115  	}
 116  	if pk, err = ConvertFromBech32(b5); chk.E(err) {
 117  		return
 118  	}
 119  	pk = pk[:schnorr.PubKeyBytesLen]
 120  	return
 121  }
 122  
 123  // NpubToPublicKey decodes an nostr public key (npub) and returns an secp256k1
 124  // public key.
 125  func NpubToPublicKey[V constraints.Bytes](encoded V) (
 126  	pk *secp256k1.PublicKey, err error,
 127  ) {
 128  	var b5, b8, hrp []byte
 129  	if hrp, b5, err = bech32.Decode([]byte(encoded)); chk.E(err) {
 130  		err = log.E.Err("ERROR: '%s'", err)
 131  		return
 132  	}
 133  	if !utils.FastEqual(hrp, PubHRP) {
 134  		err = log.E.Err(
 135  			"wrong human readable part, got '%s' want '%s'",
 136  			hrp, PubHRP,
 137  		)
 138  		return
 139  	}
 140  	if b8, err = ConvertFromBech32(b5); chk.E(err) {
 141  		return
 142  	}
 143  	return schnorr.ParsePubKey(b8[:schnorr.PubKeyBytesLen])
 144  }
 145  
 146  // HexToPublicKey decodes a string that should be a 64 character long hex
 147  // encoded public key into a btcec.PublicKey that can be used to verify a
 148  // signature or encode to Bech32.
 149  func HexToPublicKey[V constraints.Bytes](pk V) (p *btcec.PublicKey, err error) {
 150  	if len(pk) != HexKeyLen {
 151  		err = log.E.Err(
 152  			"secret key is %d bytes, must be %d", len(pk),
 153  			HexKeyLen,
 154  		)
 155  		return
 156  	}
 157  	var pb []byte
 158  	if pb, err = hex.Dec(string(pk)); chk.D(err) {
 159  		return
 160  	}
 161  	if p, err = schnorr.ParsePubKey(pb); chk.D(err) {
 162  		return
 163  	}
 164  	return
 165  }
 166  
 167  func NpubOrHexToPublicKey[V constraints.Bytes](encoded V) (
 168  	pk *btcec.PublicKey, err error,
 169  ) {
 170  	if !strings.HasPrefix(
 171  		"npub", string(encoded),
 172  	) && len(encoded) == HexKeyLen {
 173  		return HexToPublicKey(encoded)
 174  	}
 175  	return NpubToPublicKey(encoded)
 176  }
 177  
 178  func NpubOrHexToPublicKeyBinary[V constraints.Bytes](enc V) (
 179  	pkb []byte, err error,
 180  ) {
 181  	if bytes.HasPrefix([]byte(enc), []byte("npub")) {
 182  		return NpubToBytes(enc)
 183  	}
 184  	return hex.Dec(string(enc))
 185  }
 186  
 187  // HexToSecretKey decodes a string that should be a 64 character long hex
 188  // encoded public key into a btcec.PublicKey that can be used to verify a
 189  // signature or encode to Bech32.
 190  func HexToSecretKey[V constraints.Bytes](sk V) (s *btcec.SecretKey, err error) {
 191  	if len(sk) != HexKeyLen {
 192  		err = log.E.Err(
 193  			"secret key is %d bytes, must be %d", len(sk),
 194  			HexKeyLen,
 195  		)
 196  		return
 197  	}
 198  	pb := make([]byte, schnorr.PubKeyBytesLen)
 199  	if _, err = hex.DecBytes(pb, []byte(sk)); chk.D(err) {
 200  		return
 201  	}
 202  	if s = secp256k1.SecKeyFromBytes(pb); chk.D(err) {
 203  		return
 204  	}
 205  	return
 206  }
 207  
 208  // HexToNpub converts a raw 64 character hex encoded public key (as used in
 209  // standard nostr json events) to a bech32 encoded npub.
 210  func HexToNpub[V constraints.Bytes](publicKeyHex V) (s []byte, err error) {
 211  	b := make([]byte, schnorr.PubKeyBytesLen)
 212  	if _, err = hex.DecBytes(b, []byte(publicKeyHex)); chk.D(err) {
 213  		err = log.E.Err("failed to decode public key hex: %w", err)
 214  		return
 215  	}
 216  	var bits5 []byte
 217  	if bits5, err = bech32.ConvertBits(b, 8, 5, true); chk.D(err) {
 218  		return nil, err
 219  	}
 220  	return bech32.Encode(NpubHRP, bits5)
 221  }
 222  
 223  // BinToNpub converts a raw 32 byte public key to nostr bech32 encoded npub.
 224  func BinToNpub(b []byte) (s []byte, err error) {
 225  	var bits5 []byte
 226  	if bits5, err = bech32.ConvertBits(b, 8, 5, true); chk.D(err) {
 227  		return nil, err
 228  	}
 229  	return bech32.Encode(NpubHRP, bits5)
 230  }
 231  
 232  // HexToNsec converts a hex encoded secret key to a bech32 encoded nsec.
 233  func HexToNsec[V constraints.Bytes](sk V) (nsec []byte, err error) {
 234  	var s *btcec.SecretKey
 235  	if s, err = HexToSecretKey(sk); chk.E(err) {
 236  		return
 237  	}
 238  	if nsec, err = SecretKeyToNsec(s); chk.E(err) {
 239  		return
 240  	}
 241  	return
 242  }
 243  
 244  // BinToNsec converts a binary secret key to a bech32 encoded nsec.
 245  func BinToNsec(sk []byte) (nsec []byte, err error) {
 246  	var s *btcec.SecretKey
 247  	s, _ = btcec.SecKeyFromBytes(sk)
 248  	if nsec, err = SecretKeyToNsec(s); chk.E(err) {
 249  		return
 250  	}
 251  	return
 252  }
 253  
 254  // SecretKeyToHex converts a secret key to the hex encoding.
 255  func SecretKeyToHex(sk *btcec.SecretKey) (hexSec []byte) {
 256  	return hex.EncAppend(nil, sk.Serialize())
 257  }
 258  
 259  // NsecToHex converts a bech32 encoded nostr secret key to a raw hexadecimal
 260  // string.
 261  func NsecToHex[V constraints.Bytes](nsec V) (hexSec []byte, err error) {
 262  	var sk *secp256k1.SecretKey
 263  	if sk, err = NsecToSecretKey(nsec); chk.E(err) {
 264  		return
 265  	}
 266  	hexSec = SecretKeyToHex(sk)
 267  	return
 268  }
 269