Closed ping-localhost closed 3 years ago
btcec.ParseSignature
doesn't parse CompactSignatures, and I don't think the btcsuite offers a function to do so. You can extract that logic from RecoverCompact
Small script that I hacked together that should not be used in any production environment, but is used to explain the points above:
package main
import (
"encoding/base64"
"errors"
"fmt"
"math/big"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcutil"
)
const (
message = "Hello"
address = "bc1qsdjne3y6ljndzvg9z9qrhje8k7p2m5yas704hn"
electrumSignature = "H0wOFGpArXjGOdT57eb902p9SsZ4ELAtMLL8hATpeQerCdheTSHf6qP8y+x83nhV30MEXsa9Gji8+Z9OZKdSs2E="
)
func main() {
addr, err := btcutil.DecodeAddress(address, &chaincfg.MainNetParams)
if err != nil {
panic(err)
}
//TODO: use compactLen. This works now because the message isn't long
magicMessage := "\x18Bitcoin Signed Message:\n" + string(len(message)) + message
messageHash := chainhash.DoubleHashB([]byte(magicMessage))
signatureEncoded, err := base64.StdEncoding.DecodeString(electrumSignature)
if err != nil {
panic(err)
}
pubKey, comp, err := btcec.RecoverCompact(btcec.S256(), signatureEncoded, messageHash)
if err != nil {
panic(err)
}
fmt.Println("Pubkey is compressed?", comp)
// TODO: switch if not compressed or better if the recovery flag in the signature tells us to do so
// TODO: add P2WPKH-P2SH checking
pubkeyHash := btcutil.Hash160(pubKey.SerializeCompressed())
p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubkeyHash, &chaincfg.MainNetParams)
if err != nil {
panic(err)
}
signature, err := parseCompact(signatureEncoded, btcec.S256())
if err != nil {
panic(err)
}
verified := signature.Verify(messageHash, pubKey)
fmt.Println("Signature Verified?", verified)
// TODO: check for P2PKH/P2WPKH-P2SH/P2WPKH
fmt.Println("Addresses are the same?", addr.String() == p2wkhAddr.String())
}
func parseCompact(signature []byte, curve *btcec.KoblitzCurve) (*btcec.Signature, error) {
bitlen := (curve.BitSize + 7) / 8
if len(signature) != 1+bitlen*2 {
return nil, errors.New("invalid compact signature size")
}
sig := &btcec.Signature{
R: new(big.Int).SetBytes(signature[1 : bitlen+1]),
S: new(big.Int).SetBytes(signature[bitlen+1:]),
}
return sig, nil
}
Ah yes, that makes so much more sense @Joukehofman. Most of the things you've in your snippet I have seen, was just unable to connect it all together. Thank you so much for this initial snippet.
Hello,
I've been trying to validate a signed bech32 message (from Electrum, BIP-0137) but I keep running into walls, since my knowledge related to ECDSA, addresses and signatures is pretty limited.
I was able to extract PublicKey using the code below, but it doesn't match
address
andsignature, err := btcec.ParseSignature(signatureEncoded, btcec.S256())
errors out withmalformed signature: no header magic
. I have a feeling that it's because the signature isn't provided in the proper format, not sure how to get there though.If anyone could help me out that would be greatly appreciated. I tried to find a Slack/Mattermost/Gitter channel but haven't found any otherwise I would've asked there.