fast.go raw

   1  package secp256k1
   2  
   3  // The hot path is verification, and for 32-byte messages (the only length
   4  // Nostr uses) it is delegated to btcec's BIP-340 implementation. That package
   5  // carries the same pure-Go 5x52/10x26 field arithmetic that orly's portable
   6  // code was adapted from, and is substantially faster than math/big.
   7  //
   8  // The math/big implementation in curve.go is kept for two jobs: signing
   9  // arbitrary-length messages as required by the full BIP-340 vector suite, and
  10  // cross-checking the fast path in tests. Both implementations are exercised
  11  // against each other there.
  12  
  13  import (
  14  	"git.smesh.lol/torly/pkg/crypto/btcec"
  15  	"git.smesh.lol/torly/pkg/crypto/btcec/schnorr"
  16  )
  17  
  18  // verifyFast verifies a BIP-340 signature over a 32-byte message.
  19  func verifyFast(pub [32]byte, msg [32]byte, sig [64]byte) bool {
  20  	pk, err := schnorr.ParsePubKey(pub[:])
  21  	if err != nil {
  22  		return false
  23  	}
  24  	s, err := schnorr.ParseSignature(sig[:])
  25  	if err != nil {
  26  		return false
  27  	}
  28  	return s.Verify(msg[:], pk)
  29  }
  30  
  31  // signFastWithAux signs a 32-byte message with an explicit BIP-340 aux_rand,
  32  // which is what the official vectors pin down.
  33  func signFastWithAux(sk [32]byte, msg [32]byte, aux [32]byte) ([64]byte, bool) {
  34  	var out [64]byte
  35  	priv, _ := btcec.PrivKeyFromBytes(sk[:])
  36  	if priv == nil {
  37  		return out, false
  38  	}
  39  	sig, err := schnorr.Sign(priv, msg[:], schnorr.CustomNonce(aux))
  40  	if err != nil {
  41  		return out, false
  42  	}
  43  	serialized := sig.Serialize()
  44  	if len(serialized) != 64 {
  45  		return out, false
  46  	}
  47  	copy(out[:], serialized)
  48  	var pub [32]byte
  49  	copy(pub[:], schnorr.SerializePubKey(priv.PubKey()))
  50  	if !verifyFast(pub, msg, out) {
  51  		return out, false
  52  	}
  53  	return out, true
  54  }
  55