types.go raw

   1  // Package types provides fixed-size cryptographic types for Nostr.
   2  //
   3  // These types use fixed-size arrays instead of slices, which provides:
   4  //   - Stack allocation (no heap escape for copies)
   5  //   - Implicit copy-on-assignment (safe concurrent use)
   6  //   - Type safety (compiler enforces correct sizes)
   7  //
   8  // Migration: Existing code using []byte continues to work via Bytes() methods.
   9  // New code should prefer the fixed types for better performance and safety.
  10  package types
  11  
  12  import (
  13  	"encoding/hex"
  14  )
  15  
  16  // Size constants for Nostr cryptographic values
  17  const (
  18  	EventIDSize   = 32 // SHA256 hash
  19  	PubkeySize    = 32 // secp256k1 x-coordinate (32 bytes)
  20  	SignatureSize = 64 // Schnorr signature
  21  	SecretKeySize = 32 // secp256k1 secret key
  22  )
  23  
  24  // EventID is a 32-byte SHA256 hash of an event's canonical serialization.
  25  // Stack-allocated, copied on assignment.
  26  type EventID [EventIDSize]byte
  27  
  28  // Pubkey is a 32-byte secp256k1 public key (x-coordinate only, y is derived).
  29  // Stack-allocated, copied on assignment.
  30  type Pubkey [PubkeySize]byte
  31  
  32  // Signature is a 64-byte Schnorr signature.
  33  // Stack-allocated, copied on assignment.
  34  type Signature [SignatureSize]byte
  35  
  36  // SecretKey is a 32-byte secp256k1 secret key.
  37  // Stack-allocated, copied on assignment.
  38  type SecretKey [SecretKeySize]byte
  39  
  40  // --- EventID methods ---
  41  
  42  // Bytes returns a slice view of the EventID.
  43  // WARNING: The returned slice shares memory with the EventID.
  44  // Use Copy() if you need an independent slice.
  45  // Pointer receiver ensures the slice references the original, not a copy.
  46  func (id *EventID) Bytes() []byte { return id[:] }
  47  
  48  // Copy returns an independent heap-allocated copy as a slice.
  49  // Use when you need to pass to APIs that may retain or mutate the slice.
  50  func (id EventID) Copy() []byte {
  51  	b := make([]byte, EventIDSize)
  52  	copy(b, id[:])
  53  	return b
  54  }
  55  
  56  // Hex returns the lowercase hex encoding of the EventID.
  57  func (id EventID) Hex() string { return hex.EncodeToString(id[:]) }
  58  
  59  // IsZero returns true if the EventID is all zeros (uninitialized).
  60  func (id EventID) IsZero() bool { return id == EventID{} }
  61  
  62  // EventIDFromBytes creates an EventID from a byte slice.
  63  // If src is shorter than 32 bytes, remaining bytes are zero.
  64  // If src is longer, extra bytes are ignored.
  65  func EventIDFromBytes(src []byte) (id EventID) {
  66  	copy(id[:], src)
  67  	return
  68  }
  69  
  70  // EventIDFromHex creates an EventID from a hex string.
  71  // Returns zero EventID and error if hex is invalid.
  72  func EventIDFromHex(s string) (id EventID, err error) {
  73  	var b []byte
  74  	b, err = hex.DecodeString(s)
  75  	if err != nil {
  76  		return
  77  	}
  78  	copy(id[:], b)
  79  	return
  80  }
  81  
  82  // --- Pubkey methods ---
  83  
  84  // Bytes returns a slice view of the Pubkey.
  85  // WARNING: The returned slice shares memory with the Pubkey.
  86  // Pointer receiver ensures the slice references the original, not a copy.
  87  func (pk *Pubkey) Bytes() []byte { return pk[:] }
  88  
  89  // Copy returns an independent heap-allocated copy as a slice.
  90  func (pk Pubkey) Copy() []byte {
  91  	b := make([]byte, PubkeySize)
  92  	copy(b, pk[:])
  93  	return b
  94  }
  95  
  96  // Hex returns the lowercase hex encoding of the Pubkey.
  97  func (pk Pubkey) Hex() string { return hex.EncodeToString(pk[:]) }
  98  
  99  // IsZero returns true if the Pubkey is all zeros (uninitialized).
 100  func (pk Pubkey) IsZero() bool { return pk == Pubkey{} }
 101  
 102  // PubkeyFromBytes creates a Pubkey from a byte slice.
 103  func PubkeyFromBytes(src []byte) (pk Pubkey) {
 104  	copy(pk[:], src)
 105  	return
 106  }
 107  
 108  // PubkeyFromHex creates a Pubkey from a hex string.
 109  func PubkeyFromHex(s string) (pk Pubkey, err error) {
 110  	var b []byte
 111  	b, err = hex.DecodeString(s)
 112  	if err != nil {
 113  		return
 114  	}
 115  	copy(pk[:], b)
 116  	return
 117  }
 118  
 119  // --- Signature methods ---
 120  
 121  // Bytes returns a slice view of the Signature.
 122  // WARNING: The returned slice shares memory with the Signature.
 123  // Pointer receiver ensures the slice references the original, not a copy.
 124  func (sig *Signature) Bytes() []byte { return sig[:] }
 125  
 126  // Copy returns an independent heap-allocated copy as a slice.
 127  func (sig Signature) Copy() []byte {
 128  	b := make([]byte, SignatureSize)
 129  	copy(b, sig[:])
 130  	return b
 131  }
 132  
 133  // Hex returns the lowercase hex encoding of the Signature.
 134  func (sig Signature) Hex() string { return hex.EncodeToString(sig[:]) }
 135  
 136  // IsZero returns true if the Signature is all zeros (uninitialized).
 137  func (sig Signature) IsZero() bool { return sig == Signature{} }
 138  
 139  // SignatureFromBytes creates a Signature from a byte slice.
 140  func SignatureFromBytes(src []byte) (sig Signature) {
 141  	copy(sig[:], src)
 142  	return
 143  }
 144  
 145  // SignatureFromHex creates a Signature from a hex string.
 146  func SignatureFromHex(s string) (sig Signature, err error) {
 147  	var b []byte
 148  	b, err = hex.DecodeString(s)
 149  	if err != nil {
 150  		return
 151  	}
 152  	copy(sig[:], b)
 153  	return
 154  }
 155  
 156  // --- SecretKey methods ---
 157  
 158  // Bytes returns a slice view of the SecretKey.
 159  // WARNING: The returned slice shares memory with the SecretKey.
 160  // Pointer receiver ensures the slice references the original, not a copy.
 161  func (sk *SecretKey) Bytes() []byte { return sk[:] }
 162  
 163  // Copy returns an independent heap-allocated copy as a slice.
 164  func (sk SecretKey) Copy() []byte {
 165  	b := make([]byte, SecretKeySize)
 166  	copy(b, sk[:])
 167  	return b
 168  }
 169  
 170  // Hex returns the lowercase hex encoding of the SecretKey.
 171  // WARNING: Be careful with secret key hex representations.
 172  func (sk SecretKey) Hex() string { return hex.EncodeToString(sk[:]) }
 173  
 174  // IsZero returns true if the SecretKey is all zeros (uninitialized).
 175  func (sk SecretKey) IsZero() bool { return sk == SecretKey{} }
 176  
 177  // SecretKeyFromBytes creates a SecretKey from a byte slice.
 178  func SecretKeyFromBytes(src []byte) (sk SecretKey) {
 179  	copy(sk[:], src)
 180  	return
 181  }
 182  
 183  // SecretKeyFromHex creates a SecretKey from a hex string.
 184  func SecretKeyFromHex(s string) (sk SecretKey, err error) {
 185  	var b []byte
 186  	b, err = hex.DecodeString(s)
 187  	if err != nil {
 188  		return
 189  	}
 190  	copy(sk[:], b)
 191  	return
 192  }
 193  
 194  // Zero clears the secret key memory.
 195  // Call this when done with a secret key to reduce exposure window.
 196  func (sk *SecretKey) Zero() {
 197  	for i := range sk {
 198  		sk[i] = 0
 199  	}
 200  }
 201