libsecp256k1_purego.go raw

   1  //go:build !js && !wasm && !tinygo && !wasm32
   2  
   3  package p256k1
   4  
   5  import (
   6  	"errors"
   7  	"sync"
   8  
   9  	"github.com/ebitengine/purego"
  10  )
  11  
  12  // LibSecp256k1 wraps the native libsecp256k1.so library using purego for CGO-free operation.
  13  // This provides a way to benchmark against the C implementation without CGO.
  14  type LibSecp256k1 struct {
  15  	lib    uintptr
  16  	ctx    uintptr
  17  	loaded bool
  18  	mu     sync.RWMutex
  19  
  20  	// Function pointers
  21  	contextCreate        func(uint) uintptr
  22  	contextDestroy       func(uintptr)
  23  	contextRandomize     func(uintptr, *byte) int
  24  	schnorrsigSign32     func(uintptr, *byte, *byte, *byte, *byte) int
  25  	schnorrsigVerify     func(uintptr, *byte, *byte, uint, *byte) int
  26  	keypairCreate        func(uintptr, *byte, *byte) int
  27  	keypairXonlyPub      func(uintptr, *byte, *int, *byte) int
  28  	xonlyPubkeyParse     func(uintptr, *byte, *byte) int
  29  	ecPubkeyCreate       func(uintptr, *byte, *byte) int
  30  	ecPubkeyParse        func(uintptr, *byte, *byte, uint) int
  31  	ecPubkeySerialize    func(uintptr, *byte, *uint, *byte, uint) int
  32  	xonlyPubkeySerialize func(uintptr, *byte, *byte) int
  33  	ecdh                 func(uintptr, *byte, *byte, *byte, uintptr, uintptr) int
  34  
  35  	// ECDSA function pointers
  36  	ecdsaSign                    func(uintptr, *byte, *byte, *byte, uintptr, uintptr) int
  37  	ecdsaVerify                  func(uintptr, *byte, *byte, *byte) int
  38  	ecdsaSignatureParseDER       func(uintptr, *byte, *byte, uint) int
  39  	ecdsaSignatureSerializeDER   func(uintptr, *byte, *uint, *byte) int
  40  	ecdsaSignatureParseCompact   func(uintptr, *byte, *byte) int
  41  	ecdsaSignatureSerializeCompact func(uintptr, *byte, *byte) int
  42  	ecdsaSignatureNormalize      func(uintptr, *byte, *byte) int
  43  	ecdsaRecover                 func(uintptr, *byte, *byte, *byte, int) int
  44  	ecdsaRecoverableSignatureParseCompact func(uintptr, *byte, *byte, int) int
  45  	ecdsaRecoverableSignatureSerializeCompact func(uintptr, *byte, *int, *byte) int
  46  	ecdsaSignRecoverable         func(uintptr, *byte, *byte, *byte, uintptr, uintptr) int
  47  }
  48  
  49  // Secp256k1 context flags
  50  // In modern libsecp256k1, SECP256K1_CONTEXT_NONE = 1 is the only valid flag.
  51  // The old SIGN (256) and VERIFY (257) flags are deprecated.
  52  const (
  53  	libContextNone = 1
  54  )
  55  
  56  // Global instance
  57  var (
  58  	libSecp        *LibSecp256k1
  59  	libSecpOnce    sync.Once
  60  	libSecpInitErr error
  61  )
  62  
  63  // GetLibSecp256k1 returns the global LibSecp256k1 instance, loading it if necessary.
  64  // Returns nil and an error if the library cannot be loaded.
  65  func GetLibSecp256k1() (*LibSecp256k1, error) {
  66  	libSecpOnce.Do(func() {
  67  		libSecp = &LibSecp256k1{}
  68  		// Try multiple paths to find the library
  69  		paths := []string{
  70  			"./libsecp256k1.so",
  71  			"../libsecp256k1.so",
  72  			"/home/mleku/src/p256k1.mleku.dev/libsecp256k1.so",
  73  			"libsecp256k1.so",
  74  		}
  75  		for _, path := range paths {
  76  			err := libSecp.Load(path)
  77  			if err == nil {
  78  				libSecpInitErr = nil
  79  				return
  80  			}
  81  			libSecpInitErr = err
  82  		}
  83  	})
  84  	if libSecpInitErr != nil {
  85  		return nil, libSecpInitErr
  86  	}
  87  	return libSecp, nil
  88  }
  89  
  90  // Load loads the libsecp256k1.so library from the given path.
  91  func (l *LibSecp256k1) Load(path string) error {
  92  	l.mu.Lock()
  93  	defer l.mu.Unlock()
  94  
  95  	if l.loaded {
  96  		return nil
  97  	}
  98  
  99  	lib, err := purego.Dlopen(path, purego.RTLD_NOW|purego.RTLD_GLOBAL)
 100  	if err != nil {
 101  		return err
 102  	}
 103  	l.lib = lib
 104  
 105  	// Register function pointers
 106  	purego.RegisterLibFunc(&l.contextCreate, lib, "secp256k1_context_create")
 107  	purego.RegisterLibFunc(&l.contextDestroy, lib, "secp256k1_context_destroy")
 108  	purego.RegisterLibFunc(&l.contextRandomize, lib, "secp256k1_context_randomize")
 109  	purego.RegisterLibFunc(&l.schnorrsigSign32, lib, "secp256k1_schnorrsig_sign32")
 110  	purego.RegisterLibFunc(&l.schnorrsigVerify, lib, "secp256k1_schnorrsig_verify")
 111  	purego.RegisterLibFunc(&l.keypairCreate, lib, "secp256k1_keypair_create")
 112  	purego.RegisterLibFunc(&l.keypairXonlyPub, lib, "secp256k1_keypair_xonly_pub")
 113  	purego.RegisterLibFunc(&l.xonlyPubkeyParse, lib, "secp256k1_xonly_pubkey_parse")
 114  	purego.RegisterLibFunc(&l.ecPubkeyCreate, lib, "secp256k1_ec_pubkey_create")
 115  	purego.RegisterLibFunc(&l.ecPubkeyParse, lib, "secp256k1_ec_pubkey_parse")
 116  	purego.RegisterLibFunc(&l.ecPubkeySerialize, lib, "secp256k1_ec_pubkey_serialize")
 117  	purego.RegisterLibFunc(&l.xonlyPubkeySerialize, lib, "secp256k1_xonly_pubkey_serialize")
 118  	purego.RegisterLibFunc(&l.ecdh, lib, "secp256k1_ecdh")
 119  
 120  	// Register ECDSA function pointers
 121  	purego.RegisterLibFunc(&l.ecdsaSign, lib, "secp256k1_ecdsa_sign")
 122  	purego.RegisterLibFunc(&l.ecdsaVerify, lib, "secp256k1_ecdsa_verify")
 123  	purego.RegisterLibFunc(&l.ecdsaSignatureParseDER, lib, "secp256k1_ecdsa_signature_parse_der")
 124  	purego.RegisterLibFunc(&l.ecdsaSignatureSerializeDER, lib, "secp256k1_ecdsa_signature_serialize_der")
 125  	purego.RegisterLibFunc(&l.ecdsaSignatureParseCompact, lib, "secp256k1_ecdsa_signature_parse_compact")
 126  	purego.RegisterLibFunc(&l.ecdsaSignatureSerializeCompact, lib, "secp256k1_ecdsa_signature_serialize_compact")
 127  	purego.RegisterLibFunc(&l.ecdsaSignatureNormalize, lib, "secp256k1_ecdsa_signature_normalize")
 128  
 129  	// Register ECDSA recovery functions (may not be available in all builds)
 130  	// These are optional - the library may not have the recovery module enabled
 131  	// We use a helper that ignores errors for optional functions
 132  	registerOptional := func(fn interface{}, lib uintptr, name string) {
 133  		defer func() { recover() }() // Ignore panic from missing symbol
 134  		purego.RegisterLibFunc(fn, lib, name)
 135  	}
 136  	registerOptional(&l.ecdsaRecover, lib, "secp256k1_ecdsa_recover")
 137  	registerOptional(&l.ecdsaRecoverableSignatureParseCompact, lib, "secp256k1_ecdsa_recoverable_signature_parse_compact")
 138  	registerOptional(&l.ecdsaRecoverableSignatureSerializeCompact, lib, "secp256k1_ecdsa_recoverable_signature_serialize_compact")
 139  	registerOptional(&l.ecdsaSignRecoverable, lib, "secp256k1_ecdsa_sign_recoverable")
 140  
 141  	// Create context (modern libsecp256k1 uses SECP256K1_CONTEXT_NONE = 1)
 142  	l.ctx = l.contextCreate(libContextNone)
 143  	if l.ctx == 0 {
 144  		return errors.New("failed to create secp256k1 context")
 145  	}
 146  
 147  	// Randomize context for better security
 148  	var seed [32]byte
 149  	// Use zero seed for deterministic benchmarks
 150  	l.contextRandomize(l.ctx, &seed[0])
 151  
 152  	l.loaded = true
 153  	return nil
 154  }
 155  
 156  // Close releases the library resources.
 157  func (l *LibSecp256k1) Close() {
 158  	l.mu.Lock()
 159  	defer l.mu.Unlock()
 160  
 161  	if !l.loaded {
 162  		return
 163  	}
 164  
 165  	if l.ctx != 0 {
 166  		l.contextDestroy(l.ctx)
 167  		l.ctx = 0
 168  	}
 169  
 170  	if l.lib != 0 {
 171  		purego.Dlclose(l.lib)
 172  		l.lib = 0
 173  	}
 174  
 175  	l.loaded = false
 176  }
 177  
 178  // IsLoaded returns true if the library is loaded.
 179  func (l *LibSecp256k1) IsLoaded() bool {
 180  	l.mu.RLock()
 181  	defer l.mu.RUnlock()
 182  	return l.loaded
 183  }
 184  
 185  // SchnorrSign signs a 32-byte message using a 32-byte secret key.
 186  // Returns a 64-byte signature.
 187  func (l *LibSecp256k1) SchnorrSign(msg32, seckey32 []byte) ([]byte, error) {
 188  	l.mu.RLock()
 189  	defer l.mu.RUnlock()
 190  
 191  	if !l.loaded {
 192  		return nil, errors.New("library not loaded")
 193  	}
 194  	if len(msg32) != 32 {
 195  		return nil, errors.New("message must be 32 bytes")
 196  	}
 197  	if len(seckey32) != 32 {
 198  		return nil, errors.New("secret key must be 32 bytes")
 199  	}
 200  
 201  	// Create keypair from secret key
 202  	keypair := make([]byte, 96) // secp256k1_keypair is 96 bytes
 203  	if l.keypairCreate(l.ctx, &keypair[0], &seckey32[0]) != 1 {
 204  		return nil, errors.New("failed to create keypair")
 205  	}
 206  
 207  	// Sign
 208  	sig := make([]byte, 64)
 209  	if l.schnorrsigSign32(l.ctx, &sig[0], &msg32[0], &keypair[0], nil) != 1 {
 210  		return nil, errors.New("signing failed")
 211  	}
 212  
 213  	return sig, nil
 214  }
 215  
 216  // SchnorrVerify verifies a Schnorr signature.
 217  func (l *LibSecp256k1) SchnorrVerify(sig64, msg32, pubkey32 []byte) bool {
 218  	l.mu.RLock()
 219  	defer l.mu.RUnlock()
 220  
 221  	if !l.loaded {
 222  		return false
 223  	}
 224  	if len(sig64) != 64 || len(msg32) != 32 || len(pubkey32) != 32 {
 225  		return false
 226  	}
 227  
 228  	// Parse x-only pubkey using secp256k1_xonly_pubkey_parse
 229  	xonlyPubkey := make([]byte, 64) // secp256k1_xonly_pubkey is 64 bytes
 230  	if l.xonlyPubkeyParse(l.ctx, &xonlyPubkey[0], &pubkey32[0]) != 1 {
 231  		return false
 232  	}
 233  
 234  	result := l.schnorrsigVerify(l.ctx, &sig64[0], &msg32[0], 32, &xonlyPubkey[0])
 235  	return result == 1
 236  }
 237  
 238  // CreatePubkey derives a public key from a secret key.
 239  // Returns the 32-byte x-only public key.
 240  func (l *LibSecp256k1) CreatePubkey(seckey32 []byte) ([]byte, error) {
 241  	l.mu.RLock()
 242  	defer l.mu.RUnlock()
 243  
 244  	if !l.loaded {
 245  		return nil, errors.New("library not loaded")
 246  	}
 247  	if len(seckey32) != 32 {
 248  		return nil, errors.New("secret key must be 32 bytes")
 249  	}
 250  
 251  	// Create keypair
 252  	keypair := make([]byte, 96)
 253  	if l.keypairCreate(l.ctx, &keypair[0], &seckey32[0]) != 1 {
 254  		return nil, errors.New("failed to create keypair")
 255  	}
 256  
 257  	// Extract x-only pubkey (internal representation is 64 bytes)
 258  	xonlyPubkey := make([]byte, 64)
 259  	var parity int
 260  	if l.keypairXonlyPub(l.ctx, &xonlyPubkey[0], &parity, &keypair[0]) != 1 {
 261  		return nil, errors.New("failed to extract x-only pubkey")
 262  	}
 263  
 264  	// Serialize to get the 32-byte x-coordinate
 265  	pubkey32 := make([]byte, 32)
 266  	if l.xonlyPubkeySerialize(l.ctx, &pubkey32[0], &xonlyPubkey[0]) != 1 {
 267  		return nil, errors.New("failed to serialize x-only pubkey")
 268  	}
 269  
 270  	return pubkey32, nil
 271  }
 272  
 273  // CreatePubkeyCompressed derives a compressed public key (33 bytes) from a secret key.
 274  // Returns (compressed_pubkey, parity) where parity is 0 for even Y, 1 for odd Y.
 275  func (l *LibSecp256k1) CreatePubkeyCompressed(seckey32 []byte) ([]byte, int, error) {
 276  	l.mu.RLock()
 277  	defer l.mu.RUnlock()
 278  
 279  	if !l.loaded {
 280  		return nil, 0, errors.New("library not loaded")
 281  	}
 282  	if len(seckey32) != 32 {
 283  		return nil, 0, errors.New("secret key must be 32 bytes")
 284  	}
 285  
 286  	// Create pubkey using ec_pubkey_create
 287  	pubkey := make([]byte, 64) // secp256k1_pubkey internal format
 288  	if l.ecPubkeyCreate(l.ctx, &pubkey[0], &seckey32[0]) != 1 {
 289  		return nil, 0, errors.New("failed to create pubkey")
 290  	}
 291  
 292  	// Serialize as compressed (33 bytes)
 293  	compressed := make([]byte, 33)
 294  	var outputLen uint = 33
 295  	const SECP256K1_EC_COMPRESSED = 258
 296  	if l.ecPubkeySerialize(l.ctx, &compressed[0], &outputLen, &pubkey[0], SECP256K1_EC_COMPRESSED) != 1 {
 297  		return nil, 0, errors.New("failed to serialize pubkey")
 298  	}
 299  
 300  	// Parity from prefix: 0x02 = even (0), 0x03 = odd (1)
 301  	parity := 0
 302  	if compressed[0] == 0x03 {
 303  		parity = 1
 304  	}
 305  
 306  	return compressed, parity, nil
 307  }
 308  
 309  // CreatePubkeyUncompressed derives an uncompressed public key (65 bytes) from a secret key.
 310  func (l *LibSecp256k1) CreatePubkeyUncompressed(seckey32 []byte) ([]byte, error) {
 311  	l.mu.RLock()
 312  	defer l.mu.RUnlock()
 313  
 314  	if !l.loaded {
 315  		return nil, errors.New("library not loaded")
 316  	}
 317  	if len(seckey32) != 32 {
 318  		return nil, errors.New("secret key must be 32 bytes")
 319  	}
 320  
 321  	// Create pubkey using ec_pubkey_create
 322  	pubkey := make([]byte, 64) // secp256k1_pubkey internal format
 323  	if l.ecPubkeyCreate(l.ctx, &pubkey[0], &seckey32[0]) != 1 {
 324  		return nil, errors.New("failed to create pubkey")
 325  	}
 326  
 327  	// Serialize as uncompressed (65 bytes)
 328  	uncompressed := make([]byte, 65)
 329  	var outputLen uint = 65
 330  	const SECP256K1_EC_UNCOMPRESSED = 2
 331  	if l.ecPubkeySerialize(l.ctx, &uncompressed[0], &outputLen, &pubkey[0], SECP256K1_EC_UNCOMPRESSED) != 1 {
 332  		return nil, errors.New("failed to serialize pubkey")
 333  	}
 334  
 335  	return uncompressed, nil
 336  }
 337  
 338  // ECDH computes the shared secret using ECDH.
 339  func (l *LibSecp256k1) ECDH(seckey32, pubkey33 []byte) ([]byte, error) {
 340  	l.mu.RLock()
 341  	defer l.mu.RUnlock()
 342  
 343  	if !l.loaded {
 344  		return nil, errors.New("library not loaded")
 345  	}
 346  	if len(seckey32) != 32 {
 347  		return nil, errors.New("secret key must be 32 bytes")
 348  	}
 349  	if len(pubkey33) != 33 && len(pubkey33) != 65 {
 350  		return nil, errors.New("public key must be 33 or 65 bytes")
 351  	}
 352  
 353  	// Parse pubkey
 354  	pubkey := make([]byte, 64) // secp256k1_pubkey is 64 bytes
 355  	if l.ecPubkeyParse(l.ctx, &pubkey[0], &pubkey33[0], uint(len(pubkey33))) != 1 {
 356  		return nil, errors.New("failed to parse public key")
 357  	}
 358  
 359  	// Compute ECDH
 360  	output := make([]byte, 32)
 361  	if l.ecdh(l.ctx, &output[0], &pubkey[0], &seckey32[0], 0, 0) != 1 {
 362  		return nil, errors.New("ECDH failed")
 363  	}
 364  
 365  	return output, nil
 366  }
 367  
 368  // ECDSASign signs a 32-byte message hash with a secret key.
 369  // Returns a 64-byte compact signature (r || s).
 370  func (l *LibSecp256k1) ECDSASign(msghash32, seckey32 []byte) ([]byte, error) {
 371  	l.mu.RLock()
 372  	defer l.mu.RUnlock()
 373  
 374  	if !l.loaded {
 375  		return nil, errors.New("library not loaded")
 376  	}
 377  	if len(msghash32) != 32 {
 378  		return nil, errors.New("message hash must be 32 bytes")
 379  	}
 380  	if len(seckey32) != 32 {
 381  		return nil, errors.New("secret key must be 32 bytes")
 382  	}
 383  
 384  	// Sign (internal signature format is 64 bytes)
 385  	sig := make([]byte, 64)
 386  	if l.ecdsaSign(l.ctx, &sig[0], &msghash32[0], &seckey32[0], 0, 0) != 1 {
 387  		return nil, errors.New("ECDSA signing failed")
 388  	}
 389  
 390  	// Normalize to low-S
 391  	l.ecdsaSignatureNormalize(l.ctx, &sig[0], &sig[0])
 392  
 393  	// Serialize to compact format
 394  	compact := make([]byte, 64)
 395  	if l.ecdsaSignatureSerializeCompact(l.ctx, &compact[0], &sig[0]) != 1 {
 396  		return nil, errors.New("failed to serialize signature")
 397  	}
 398  
 399  	return compact, nil
 400  }
 401  
 402  // ECDSAVerify verifies an ECDSA signature.
 403  // sig64 is a 64-byte compact signature (r || s).
 404  // msghash32 is a 32-byte message hash.
 405  // pubkey33 is a 33-byte compressed public key (or 65-byte uncompressed).
 406  func (l *LibSecp256k1) ECDSAVerify(sig64, msghash32, pubkey33 []byte) bool {
 407  	l.mu.RLock()
 408  	defer l.mu.RUnlock()
 409  
 410  	if !l.loaded {
 411  		return false
 412  	}
 413  	if len(sig64) != 64 || len(msghash32) != 32 {
 414  		return false
 415  	}
 416  	if len(pubkey33) != 33 && len(pubkey33) != 65 {
 417  		return false
 418  	}
 419  
 420  	// Parse compact signature
 421  	sig := make([]byte, 64)
 422  	if l.ecdsaSignatureParseCompact(l.ctx, &sig[0], &sig64[0]) != 1 {
 423  		return false
 424  	}
 425  
 426  	// Parse public key
 427  	pubkey := make([]byte, 64)
 428  	if l.ecPubkeyParse(l.ctx, &pubkey[0], &pubkey33[0], uint(len(pubkey33))) != 1 {
 429  		return false
 430  	}
 431  
 432  	return l.ecdsaVerify(l.ctx, &sig[0], &msghash32[0], &pubkey[0]) == 1
 433  }
 434  
 435  // ECDSASignDER signs and returns a DER-encoded signature.
 436  func (l *LibSecp256k1) ECDSASignDER(msghash32, seckey32 []byte) ([]byte, error) {
 437  	l.mu.RLock()
 438  	defer l.mu.RUnlock()
 439  
 440  	if !l.loaded {
 441  		return nil, errors.New("library not loaded")
 442  	}
 443  	if len(msghash32) != 32 {
 444  		return nil, errors.New("message hash must be 32 bytes")
 445  	}
 446  	if len(seckey32) != 32 {
 447  		return nil, errors.New("secret key must be 32 bytes")
 448  	}
 449  
 450  	// Sign
 451  	sig := make([]byte, 64)
 452  	if l.ecdsaSign(l.ctx, &sig[0], &msghash32[0], &seckey32[0], 0, 0) != 1 {
 453  		return nil, errors.New("ECDSA signing failed")
 454  	}
 455  
 456  	// Normalize to low-S
 457  	l.ecdsaSignatureNormalize(l.ctx, &sig[0], &sig[0])
 458  
 459  	// Serialize to DER format (max 72 bytes)
 460  	der := make([]byte, 72)
 461  	var derLen uint = 72
 462  	if l.ecdsaSignatureSerializeDER(l.ctx, &der[0], &derLen, &sig[0]) != 1 {
 463  		return nil, errors.New("failed to serialize DER signature")
 464  	}
 465  
 466  	return der[:derLen], nil
 467  }
 468  
 469  // ECDSAVerifyDER verifies a DER-encoded ECDSA signature.
 470  func (l *LibSecp256k1) ECDSAVerifyDER(sigDER, msghash32, pubkey33 []byte) bool {
 471  	l.mu.RLock()
 472  	defer l.mu.RUnlock()
 473  
 474  	if !l.loaded {
 475  		return false
 476  	}
 477  	if len(msghash32) != 32 {
 478  		return false
 479  	}
 480  	if len(pubkey33) != 33 && len(pubkey33) != 65 {
 481  		return false
 482  	}
 483  
 484  	// Parse DER signature
 485  	sig := make([]byte, 64)
 486  	if l.ecdsaSignatureParseDER(l.ctx, &sig[0], &sigDER[0], uint(len(sigDER))) != 1 {
 487  		return false
 488  	}
 489  
 490  	// Parse public key
 491  	pubkey := make([]byte, 64)
 492  	if l.ecPubkeyParse(l.ctx, &pubkey[0], &pubkey33[0], uint(len(pubkey33))) != 1 {
 493  		return false
 494  	}
 495  
 496  	return l.ecdsaVerify(l.ctx, &sig[0], &msghash32[0], &pubkey[0]) == 1
 497  }
 498  
 499  // ECDSASignRecoverable signs and returns a recoverable signature (65 bytes: 64 + recid).
 500  func (l *LibSecp256k1) ECDSASignRecoverable(msghash32, seckey32 []byte) ([]byte, int, error) {
 501  	l.mu.RLock()
 502  	defer l.mu.RUnlock()
 503  
 504  	if !l.loaded {
 505  		return nil, 0, errors.New("library not loaded")
 506  	}
 507  	if len(msghash32) != 32 {
 508  		return nil, 0, errors.New("message hash must be 32 bytes")
 509  	}
 510  	if len(seckey32) != 32 {
 511  		return nil, 0, errors.New("secret key must be 32 bytes")
 512  	}
 513  
 514  	// Sign recoverable (internal format is 65 bytes)
 515  	sig := make([]byte, 65)
 516  	if l.ecdsaSignRecoverable(l.ctx, &sig[0], &msghash32[0], &seckey32[0], 0, 0) != 1 {
 517  		return nil, 0, errors.New("ECDSA recoverable signing failed")
 518  	}
 519  
 520  	// Serialize to compact format with recovery id
 521  	compact := make([]byte, 64)
 522  	var recid int
 523  	if l.ecdsaRecoverableSignatureSerializeCompact(l.ctx, &compact[0], &recid, &sig[0]) != 1 {
 524  		return nil, 0, errors.New("failed to serialize recoverable signature")
 525  	}
 526  
 527  	return compact, recid, nil
 528  }
 529  
 530  // ECDSARecover recovers a public key from a signature.
 531  // sig64 is a 64-byte compact signature, recid is the recovery id (0-3).
 532  // Returns a 33-byte compressed public key.
 533  func (l *LibSecp256k1) ECDSARecover(sig64, msghash32 []byte, recid int) ([]byte, error) {
 534  	l.mu.RLock()
 535  	defer l.mu.RUnlock()
 536  
 537  	if !l.loaded {
 538  		return nil, errors.New("library not loaded")
 539  	}
 540  	if len(sig64) != 64 {
 541  		return nil, errors.New("signature must be 64 bytes")
 542  	}
 543  	if len(msghash32) != 32 {
 544  		return nil, errors.New("message hash must be 32 bytes")
 545  	}
 546  	if recid < 0 || recid > 3 {
 547  		return nil, errors.New("recovery id must be 0-3")
 548  	}
 549  
 550  	// Parse recoverable signature
 551  	sig := make([]byte, 65)
 552  	if l.ecdsaRecoverableSignatureParseCompact(l.ctx, &sig[0], &sig64[0], recid) != 1 {
 553  		return nil, errors.New("failed to parse recoverable signature")
 554  	}
 555  
 556  	// Recover public key
 557  	pubkey := make([]byte, 64)
 558  	if l.ecdsaRecover(l.ctx, &pubkey[0], &sig[0], &msghash32[0], recid) != 1 {
 559  		return nil, errors.New("public key recovery failed")
 560  	}
 561  
 562  	// Serialize as compressed
 563  	compressed := make([]byte, 33)
 564  	var outputLen uint = 33
 565  	const SECP256K1_EC_COMPRESSED = 258
 566  	if l.ecPubkeySerialize(l.ctx, &compressed[0], &outputLen, &pubkey[0], SECP256K1_EC_COMPRESSED) != 1 {
 567  		return nil, errors.New("failed to serialize recovered pubkey")
 568  	}
 569  
 570  	return compressed, nil
 571  }
 572