signature.go raw

   1  //go:build !libsecp256k1
   2  
   3  package nostr
   4  
   5  import (
   6  	"crypto/sha256"
   7  	"encoding/hex"
   8  	"fmt"
   9  
  10  	"github.com/btcsuite/btcd/btcec/v2"
  11  	"github.com/btcsuite/btcd/btcec/v2/schnorr"
  12  )
  13  
  14  // CheckSignature checks if the event signature is valid for the given event.
  15  // It won't look at the ID field, instead it will recompute the id from the entire event body.
  16  // If the signature is invalid bool will be false and err will be set.
  17  func (evt Event) CheckSignature() (bool, error) {
  18  	// read and check pubkey
  19  	pk, err := hex.DecodeString(evt.PubKey)
  20  	if err != nil {
  21  		return false, fmt.Errorf("event pubkey '%s' is invalid hex: %w", evt.PubKey, err)
  22  	}
  23  
  24  	pubkey, err := schnorr.ParsePubKey(pk)
  25  	if err != nil {
  26  		return false, fmt.Errorf("event has invalid pubkey '%s': %w", evt.PubKey, err)
  27  	}
  28  
  29  	// read signature
  30  	s, err := hex.DecodeString(evt.Sig)
  31  	if err != nil {
  32  		return false, fmt.Errorf("signature '%s' is invalid hex: %w", evt.Sig, err)
  33  	}
  34  	sig, err := schnorr.ParseSignature(s)
  35  	if err != nil {
  36  		return false, fmt.Errorf("failed to parse signature: %w", err)
  37  	}
  38  
  39  	// check signature
  40  	hash := sha256.Sum256(evt.Serialize())
  41  	return sig.Verify(hash[:], pubkey), nil
  42  }
  43  
  44  // Sign signs an event with a given privateKey.
  45  // It sets the event's ID, PubKey, and Sig fields.
  46  // Returns an error if the private key is invalid or if signing fails.
  47  func (evt *Event) Sign(secretKey string) error {
  48  	s, err := hex.DecodeString(secretKey)
  49  	if err != nil {
  50  		return fmt.Errorf("Sign called with invalid secret key '%s': %w", secretKey, err)
  51  	}
  52  
  53  	if evt.Tags == nil {
  54  		evt.Tags = make(Tags, 0)
  55  	}
  56  
  57  	sk, pk := btcec.PrivKeyFromBytes(s)
  58  	pkBytes := pk.SerializeCompressed()
  59  	evt.PubKey = hex.EncodeToString(pkBytes[1:])
  60  
  61  	h := sha256.Sum256(evt.Serialize())
  62  	sig, err := schnorr.Sign(sk, h[:], schnorr.FastSign())
  63  	if err != nil {
  64  		return err
  65  	}
  66  
  67  	evt.ID = hex.EncodeToString(h[:])
  68  	evt.Sig = hex.EncodeToString(sig.Serialize())
  69  
  70  	return nil
  71  }
  72