utils.go raw

   1  package secp
   2  
   3  import (
   4  	"crypto/rand"
   5  	"fmt"
   6  )
   7  
   8  // GeneratePrivateKey generates a random 32-byte private key
   9  func GeneratePrivateKey() (privKey []byte, err error) {
  10  	privKey = make([]byte, PrivateKeySize)
  11  	if _, err = rand.Read(privKey); err != nil {
  12  		err = fmt.Errorf("failed to generate random key: %w", err)
  13  		return
  14  	}
  15  	return
  16  }
  17  
  18  // PublicKeyFromPrivate generates a public key from a private key
  19  // Returns the serialized public key in compressed format
  20  func PublicKeyFromPrivate(privKey []byte, compressed bool) (pubKey []byte, err error) {
  21  	ctx, err := NewContext(ContextSign)
  22  	if err != nil {
  23  		return
  24  	}
  25  	defer ctx.Destroy()
  26  
  27  	internalPubKey, err := ctx.CreatePublicKey(privKey)
  28  	if err != nil {
  29  		return
  30  	}
  31  
  32  	pubKey, err = ctx.SerializePublicKey(internalPubKey, compressed)
  33  	return
  34  }
  35  
  36  // SignMessage signs a 32-byte message hash with a private key
  37  // Returns the signature in compact format (64 bytes)
  38  func SignMessage(msgHash []byte, privKey []byte) (sig []byte, err error) {
  39  	ctx, err := NewContext(ContextSign)
  40  	if err != nil {
  41  		return
  42  	}
  43  	defer ctx.Destroy()
  44  
  45  	internalSig, err := ctx.Sign(msgHash, privKey)
  46  	if err != nil {
  47  		return
  48  	}
  49  
  50  	sig, err = ctx.SerializeSignatureCompact(internalSig)
  51  	return
  52  }
  53  
  54  // VerifyMessage verifies a compact signature against a message hash and serialized public key
  55  func VerifyMessage(msgHash []byte, compactSig []byte, serializedPubKey []byte) (valid bool, err error) {
  56  	ctx, err := NewContext(ContextVerify)
  57  	if err != nil {
  58  		return
  59  	}
  60  	defer ctx.Destroy()
  61  
  62  	pubKey, err := ctx.ParsePublicKey(serializedPubKey)
  63  	if err != nil {
  64  		return
  65  	}
  66  
  67  	sig, err := ctx.ParseSignatureCompact(compactSig)
  68  	if err != nil {
  69  		return
  70  	}
  71  
  72  	valid, err = ctx.Verify(msgHash, sig, pubKey)
  73  	return
  74  }
  75  
  76  // SignMessageDER signs a message and returns DER-encoded signature
  77  func SignMessageDER(msgHash []byte, privKey []byte) (derSig []byte, err error) {
  78  	ctx, err := NewContext(ContextSign)
  79  	if err != nil {
  80  		return
  81  	}
  82  	defer ctx.Destroy()
  83  
  84  	internalSig, err := ctx.Sign(msgHash, privKey)
  85  	if err != nil {
  86  		return
  87  	}
  88  
  89  	derSig, err = ctx.SerializeSignatureDER(internalSig)
  90  	return
  91  }
  92  
  93  // VerifyMessageDER verifies a DER-encoded signature
  94  func VerifyMessageDER(msgHash []byte, derSig []byte, serializedPubKey []byte) (valid bool, err error) {
  95  	ctx, err := NewContext(ContextVerify)
  96  	if err != nil {
  97  		return
  98  	}
  99  	defer ctx.Destroy()
 100  
 101  	pubKey, err := ctx.ParsePublicKey(serializedPubKey)
 102  	if err != nil {
 103  		return
 104  	}
 105  
 106  	sig, err := ctx.ParseSignatureDER(derSig)
 107  	if err != nil {
 108  		return
 109  	}
 110  
 111  	valid, err = ctx.Verify(msgHash, sig, pubKey)
 112  	return
 113  }
 114  
 115  // SchnorrSign signs a message with Schnorr signature (BIP-340)
 116  // Returns 64-byte Schnorr signature
 117  func SchnorrSign(msgHash []byte, privKey []byte, auxRand []byte) (sig []byte, err error) {
 118  	ctx, err := NewContext(ContextSign)
 119  	if err != nil {
 120  		return
 121  	}
 122  	defer ctx.Destroy()
 123  
 124  	keypair, err := ctx.CreateKeypair(privKey)
 125  	if err != nil {
 126  		return
 127  	}
 128  
 129  	sig, err = ctx.SchnorrSign(msgHash, keypair, auxRand)
 130  	return
 131  }
 132  
 133  // SchnorrVerifyWithPubKey verifies a Schnorr signature (BIP-340)
 134  // xonlyPubKey should be 32 bytes
 135  func SchnorrVerifyWithPubKey(msgHash []byte, sig []byte, xonlyPubKey []byte) (valid bool, err error) {
 136  	ctx, err := NewContext(ContextVerify)
 137  	if err != nil {
 138  		return
 139  	}
 140  	defer ctx.Destroy()
 141  
 142  	valid, err = ctx.SchnorrVerify(sig, msgHash, xonlyPubKey)
 143  	return
 144  }
 145  
 146  // XOnlyPubKeyFromPrivate generates an x-only public key from a private key
 147  func XOnlyPubKeyFromPrivate(privKey []byte) (xonly []byte, pkParity int32, err error) {
 148  	ctx, err := NewContext(ContextSign)
 149  	if err != nil {
 150  		return
 151  	}
 152  	defer ctx.Destroy()
 153  
 154  	keypair, err := ctx.CreateKeypair(privKey)
 155  	if err != nil {
 156  		return
 157  	}
 158  
 159  	var xonlyInternal XOnlyPublicKey
 160  	xonlyInternal, pkParity, err = ctx.KeypairXOnlyPub(keypair)
 161  	if err != nil {
 162  		return
 163  	}
 164  
 165  	xonly = xonlyInternal[:]
 166  	return
 167  }
 168  
 169  // ComputeECDH computes an ECDH shared secret
 170  func ComputeECDH(serializedPubKey []byte, privKey []byte) (secret []byte, err error) {
 171  	ctx, err := NewContext(ContextSign)
 172  	if err != nil {
 173  		return
 174  	}
 175  	defer ctx.Destroy()
 176  
 177  	pubKey, err := ctx.ParsePublicKey(serializedPubKey)
 178  	if err != nil {
 179  		return
 180  	}
 181  
 182  	secret, err = ctx.ECDH(pubKey, privKey)
 183  	return
 184  }
 185  
 186  // SignRecoverableCompact signs a message with a recoverable signature
 187  // Returns compact signature (64 bytes) and recovery ID
 188  func SignRecoverableCompact(msgHash []byte, privKey []byte) (sig []byte, recID int32, err error) {
 189  	ctx, err := NewContext(ContextSign)
 190  	if err != nil {
 191  		return
 192  	}
 193  	defer ctx.Destroy()
 194  
 195  	recSig, err := ctx.SignRecoverable(msgHash, privKey)
 196  	if err != nil {
 197  		return
 198  	}
 199  
 200  	sig, recID, err = ctx.SerializeRecoverableSignatureCompact(recSig)
 201  	return
 202  }
 203  
 204  // RecoverPubKey recovers a public key from a recoverable signature
 205  // Returns serialized public key in compressed format
 206  func RecoverPubKey(msgHash []byte, compactSig []byte, recID int32, compressed bool) (pubKey []byte, err error) {
 207  	ctx, err := NewContext(ContextSign)
 208  	if err != nil {
 209  		return
 210  	}
 211  	defer ctx.Destroy()
 212  
 213  	recSig, err := ctx.ParseRecoverableSignatureCompact(compactSig, recID)
 214  	if err != nil {
 215  		return
 216  	}
 217  
 218  	recoveredPubKey, err := ctx.Recover(recSig, msgHash)
 219  	if err != nil {
 220  		return
 221  	}
 222  
 223  	pubKey, err = ctx.SerializePublicKey(recoveredPubKey, compressed)
 224  	return
 225  }
 226  
 227  // ValidatePrivateKey checks if a private key is valid
 228  func ValidatePrivateKey(privKey []byte) (valid bool, err error) {
 229  	if len(privKey) != PrivateKeySize {
 230  		err = fmt.Errorf("private key must be %d bytes", PrivateKeySize)
 231  		return
 232  	}
 233  
 234  	ctx, err := NewContext(ContextSign)
 235  	if err != nil {
 236  		return
 237  	}
 238  	defer ctx.Destroy()
 239  
 240  	_, err = ctx.CreatePublicKey(privKey)
 241  	if err != nil {
 242  		valid = false
 243  		err = nil
 244  		return
 245  	}
 246  
 247  	valid = true
 248  	return
 249  }
 250  
 251  // IsPublicKeyValid checks if a serialized public key is valid
 252  func IsPublicKeyValid(serializedPubKey []byte) (valid bool, err error) {
 253  	ctx, err := NewContext(ContextVerify)
 254  	if err != nil {
 255  		return
 256  	}
 257  	defer ctx.Destroy()
 258  
 259  	_, err = ctx.ParsePublicKey(serializedPubKey)
 260  	if err != nil {
 261  		valid = false
 262  		err = nil
 263  		return
 264  	}
 265  
 266  	valid = true
 267  	return
 268  }
 269