github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/tools/btcversig/btcversig.go (about)

     1  // This tool is able to verify whether a message was signed with the given bitcoin address
     2  package main
     3  
     4  import (
     5  	"bytes"
     6  	"flag"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  
    11  	"github.com/piotrnar/gocoin/lib/btc"
    12  	"github.com/piotrnar/gocoin/lib/others/ltc"
    13  )
    14  
    15  var (
    16  	addr     = flag.String("a", "", "base58 encoded bitcoin address that supposedly signed the message (required)")
    17  	sign     = flag.String("s", "", "base64 encoded signature of the message (required)")
    18  	mess     = flag.String("m", "", "the message (optional)")
    19  	mfil     = flag.String("f", "", "the filename containing a signed message (optional)")
    20  	unix     = flag.Bool("u", false, "remove all \\r characters from the message (optional)")
    21  	help     = flag.Bool("h", false, "print this help")
    22  	verb     = flag.Bool("v", false, "verbose mode")
    23  	litecoin = flag.Bool("ltc", false, "litecoin mode")
    24  )
    25  
    26  func main() {
    27  	var msg []byte
    28  
    29  	flag.Parse()
    30  
    31  	if *help || *addr == "" || *sign == "" {
    32  		flag.PrintDefaults()
    33  		return
    34  	}
    35  
    36  	ad, er := btc.NewAddrFromString(*addr)
    37  	if !*litecoin && ad != nil && ad.Version == ltc.AddrVerPubkey(false) {
    38  		*litecoin = true
    39  	}
    40  	if er != nil {
    41  		println("Address:", er.Error())
    42  		flag.PrintDefaults()
    43  		return
    44  	}
    45  	if ad.SegwitProg != nil && len(ad.SegwitProg.Program) == 20 {
    46  		copy(ad.Hash160[:], ad.SegwitProg.Program)
    47  	}
    48  
    49  	nv, btcsig, er := btc.ParseMessageSignature(*sign)
    50  	if er != nil {
    51  		println("ParseMessageSignature:", er.Error())
    52  		return
    53  	}
    54  
    55  	if *mess != "" {
    56  		msg = []byte(*mess)
    57  	} else if *mfil != "" {
    58  		msg, er = ioutil.ReadFile(*mfil)
    59  		if er != nil {
    60  			println(er.Error())
    61  			return
    62  		}
    63  	} else {
    64  		if *verb {
    65  			fmt.Println("Enter the message:")
    66  		}
    67  		msg, _ = ioutil.ReadAll(os.Stdin)
    68  	}
    69  
    70  	if *unix {
    71  		if *verb {
    72  			fmt.Println("Enforcing Unix text format")
    73  		}
    74  		msg = bytes.Replace(msg, []byte{'\r'}, nil, -1)
    75  	}
    76  
    77  	hash := make([]byte, 32)
    78  	if *litecoin {
    79  		ltc.HashFromMessage(msg, hash)
    80  	} else {
    81  		btc.HashFromMessage(msg, hash)
    82  	}
    83  
    84  	compressed := false
    85  	if nv >= 31 {
    86  		if *verb {
    87  			fmt.Println("compressed key")
    88  		}
    89  		nv -= 4
    90  		compressed = true
    91  	}
    92  
    93  	pub := btcsig.RecoverPublicKey(hash[:], int(nv-27))
    94  	if pub != nil {
    95  		pk := pub.Bytes(compressed)
    96  		ok := btc.EcdsaVerify(pk, btcsig.Bytes(), hash)
    97  		if ok {
    98  			sa := btc.NewAddrFromPubkey(pk, ad.Version)
    99  			if ad.Version == btc.AddrVerScript(true) || ad.Version == btc.AddrVerScript(false) {
   100  				// a trick to make the segwit P2SH addresses to work
   101  				tmp := btc.Rimp160AfterSha256(append([]byte{0, 20}, sa.Hash160[:]...))
   102  				copy(sa.Hash160[:], tmp[:])
   103  			}
   104  			if ad.Hash160 != sa.Hash160 {
   105  				fmt.Println("BAD signature for", ad.String())
   106  				if bytes.IndexByte(msg, '\r') != -1 {
   107  					fmt.Println("You have CR chars in the message. Try to verify with -u switch.")
   108  				}
   109  				os.Exit(1)
   110  			} else {
   111  				fmt.Println("Signature OK")
   112  			}
   113  		} else {
   114  			println("BAD signature")
   115  			os.Exit(1)
   116  		}
   117  	} else {
   118  		println("BAD, BAD, BAD signature")
   119  		os.Exit(1)
   120  	}
   121  }