@@ -8,16 +8,19 @@ import (
88 "encoding/base64"
99 "encoding/binary"
1010 "encoding/hex"
11- "errors "
11+ "flag "
1212 "fmt"
1313 "hash"
1414 "math/big"
15- "sync"
16- "time"
15+ "os"
16+ "slices"
17+ "strings"
18+ "syscall"
1719
1820 "github.com/btcsuite/btcd/btcutil/bech32"
1921 "github.com/btcsuite/golangcrypto/ripemd160"
2022 "github.com/mr-tron/base58"
23+ "golang.org/x/term"
2124)
2225
2326var (
4447 GenPoint : NewJacobianPoint (genPointX , genPointY , one ),
4548 }
4649 identityPoint = NewJacobianPoint (pCurve , zero , one )
47- addressTypes = [3 ]string {"legacy" , "nested" , "segwit" }
50+ addressTypes = []string {"legacy" , "nested" , "segwit" }
4851 headers = [5 ][4 ]byte {
4952 {0x1b , 0x1c , 0x1d , 0x1e }, // 27 - 30 P2PKH uncompressed
5053 {0x1f , 0x20 , 0x21 , 0x22 }, // 31 - 34 P2PKH compressed
5356 {0x2b , 0x2c , 0x2d , 0x2e }, // TODO 43 - 46 P2TR
5457 }
5558 OutOfRangeError = & PrivateKeyError {Message : "scalar is out of range" }
59+ flags = flag .NewFlagSet ("bitcoin message tool" , flag .ExitOnError )
60+ usagePrefix = `
61+ ██████╗ ███╗ ███╗████████╗
62+ ██╔══██╗████╗ ████║╚══██╔══╝
63+ ██████╔╝██╔████╔██║ ██║
64+ ██╔══██╗██║╚██╔╝██║ ██║
65+ ██████╔╝██║ ╚═╝ ██║ ██║
66+ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
67+
68+ Bitcoin Message Tool by shadowy-pycoder
69+
70+ GitHub: https://github.com/shadowy-pycoder
71+
72+ Usage: bmt [OPTIONS] COMMAND
73+ Options:
74+ `
75+ usageCommands = `
76+ Commands:
77+
78+ sign Create bitcoin message
79+ verify Verify bitcoin message
80+ `
81+ signUsagePrefix = `Usage bmt sign [-h] -p -a {legacy, nested, segwit} -m [MESSAGE ...] [-d] [-e]
82+ Options:
83+ `
84+ signUsageExamples = `
85+ Examples:
86+
87+ Deterministic signature for compressed private key and legacy address
88+
89+ bmt sign -p -a legacy -d -m "ECDSA is the most fun I have ever experienced"
90+ PrivateKey (WIF): L3V9AFB763LKWWsMh8CyosSG8QV8KDTjYeXqkt4WX5Xyz2aNqLAY
91+ -----BEGIN BITCOIN SIGNED MESSAGE-----
92+ ECDSA is the most fun I have ever experienced
93+ -----BEGIN BITCOIN SIGNATURE-----
94+ 16wrm6zJek6REbxbJSLsBHehn3Lj1vo57t
95+
96+ H3x5bM2MpXK9MyLLbIGWQjZQNTP6lfuIjmPqMrU7YZ5CCm5bS9L+zCtrfIOJaloDb0mf9QBSEDIs4UCd/jou1VI=
97+ -----END BITCOIN SIGNATURE-----
98+ `
99+ verifyUsagePrefix = `Usage bmt verify [-h] -a ADDRESS -m [MESSAGE ...] -s SIGNATURE [-e] [-v] [-r]
100+ Options:
101+ `
102+ verifyUsageExamples = `
103+ Examples:
104+
105+ Message verification in verbose mode
106+
107+ bmt verify -a 175A5YsPUdM71mnNCC3i8faxxYJgBonjWL \
108+ -m "ECDSA is the most fun I have ever experienced" \
109+ -s HyiLDcQQ1p2bKmyqM0e5oIBQtKSZds4kJQ+VbZWpr0kYA6Qkam2MlUeTr+lm1teUGHuLapfa43JjyrRqdSA0pxs= \
110+ -v
111+ true
112+ message verified to be from 175A5YsPUdM71mnNCC3i8faxxYJgBonjWL
113+
114+ Display a recovered public key
115+
116+ bmt verify -a 175A5YsPUdM71mnNCC3i8faxxYJgBonjWL \
117+ -m "ECDSA is the most fun I have ever experienced" \
118+ -s HyiLDcQQ1p2bKmyqM0e5oIBQtKSZds4kJQ+VbZWpr0kYA6Qkam2MlUeTr+lm1teUGHuLapfa43JjyrRqdSA0pxs= \
119+ -r
120+ true
121+ 024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1
122+ `
56123)
57124
58125type PrivateKeyError struct {
@@ -410,6 +477,9 @@ func (k *PrivateKey) SplitBytes() (version []byte, payload []byte, checkSum []by
410477 return nil , nil , nil , err
411478 }
412479 pkLen := len (privkey )
480+ if pkLen - 4 < 1 {
481+ return nil , nil , nil , & PrivateKeyError {Message : "too short" }
482+ }
413483 return privkey [:1 ], privkey [1 : pkLen - 4 ], privkey [pkLen - 4 :], nil
414484}
415485
@@ -1103,83 +1173,204 @@ func SignMessage(pk *PrivateKey, addrType, message string, deterministic, electr
11031173 return nil , & SignatureError {Message : "invalid signature parameters" }
11041174}
11051175
1106- func main () {
1176+ func printMessage (bm * BitcoinMessage ) {
1177+ fmt .Println ("-----BEGIN BITCOIN SIGNED MESSAGE-----" )
1178+ fmt .Println (bm .Data )
1179+ fmt .Println ("-----BEGIN BITCOIN SIGNATURE-----" )
1180+ fmt .Println (bm .Address )
1181+ fmt .Println ()
1182+ fmt .Println (string (bm .Signature ))
1183+ fmt .Println ("-----END BITCOIN SIGNATURE-----" )
1184+ }
1185+ 1186+ func NewSignCommand () * SignCommand {
1187+ sc := & SignCommand {
1188+ fs : flag .NewFlagSet ("sign" , flag .ContinueOnError ),
1189+ }
1190+ sc .fs .BoolVar (& sc .help , "h" , false , "show this help message and exit" )
1191+ sc .fs .BoolVar (& sc .deterministic , "d" , false , "sign deterministically (RFC6979)" )
1192+ sc .fs .BoolVar (& sc .electrum , "e" , false , "create electrum-like signature" )
1193+ sc .fs .StringVar (& sc .message , "m" , "" , "[MESSAGE ...] message to sign" )
1194+ sc .fs .BoolFunc ("p" , "private key in wallet import format (WIF)" , func (flagValue string ) error {
1195+ fmt .Print ("PrivateKey (WIF): " )
1196+ 1197+ bytepw , err := term .ReadPassword (syscall .Stdin )
1198+ if err != nil {
1199+ os .Exit (1 )
1200+ }
1201+ pk := string (bytepw )
1202+ sc .pk = pk
1203+ fmt .Println ()
1204+ return nil
1205+ })
1206+ sc .fs .Func ("a" , "type of bitcoin address (legacy, nested, segwit)" , func (flagValue string ) error {
1207+ flagValue = strings .ToLower (flagValue )
1208+ if ! slices .Contains (addressTypes , flagValue ) {
1209+ fmt .Fprintf (os .Stderr , "bmt: invalid argument '%s' for -a flag. See 'bmt sign -h'\n " , flagValue )
1210+ os .Exit (2 )
1211+ }
1212+ sc .addrType = flagValue
1213+ return nil
1214+ })
1215+ 1216+ return sc
1217+ }
1218+ 1219+ type SignCommand struct {
1220+ fs * flag.FlagSet
1221+ 1222+ help bool
1223+ pk string
1224+ addrType string
1225+ deterministic bool
1226+ electrum bool
1227+ message string
1228+ }
11071229
1108- //key := big.NewInt(1000)
1109- start := time .Now ()
1110- // raw, _ := createRawPubKey(key)
1111- // pk := createPubKey(raw, false)
1112- // fmt.Println(createAddress(pk))
1113- // fmt.Println(createNestedSegwit(pk))
1114- // fmt.Println(hex.EncodeToString([]byte{5, 00, 20}))
1115- // addr, _ := createNativeSegwit(pk)
1116- // fmt.Println(addr)
1117- // fmt.Printf("uint64: %v\n", uint64(18446744073709551615))
1118- 1119- // fmt.Println(hex.EncodeToString(varInt(12356333474345788523)))
1120- // fmt.Println(hex.EncodeToString(msgMagic("语言处理")))
1121- // a := secp256k1.GenPoint
1122- // b := identityPoint
1123- // fmt.Println(!a.Eq(b))
1124- // fmt.Printf("%p%p\n", a.X, &secp256k1.GenPoint.X)
1125- 1126- // fmt.Println(deriveAddress(pk, "segwit"))
1127- // fmt.Printf("%p\n", secp256k1.GenPoint.X)
1128- // fmt.Println(secp256k1.GenPoint)
1129- // fmt.Printf("%p\n", secp256k1.GenPoint.X)
1130- // fmt.Println(secp256k1.GenPoint)
1131- // var pr JacobianPoint
1132- // fmt.Println(pr.Mul(key, pr.Mul(key, secp256k1.GenPoint)))
1133- // fmt.Println(secp256k1.GenPoint)
1134- // message := &BitcoinMessage{Address: "17PDgca9K59ed6RDtXaqje2gWUr8aLvbig",
1135- // Data: "ECDSA is the most fun I have ever experienced",
1136- // Signature: []byte("IPxvWLg99SQxAXQd8/cdhR7x1kShMvF3Fw999+hNio2hEuvKH80FAO2YhiatHvt4ugdyB2Yo1NiZzcBm7ZLg5/w="),
1137- // }
1138- // fmt.Println(VerifyMessage(message, false))
1139- //ppkk, _ := new(big.Int).SetString("f2693ef943df4b25eec8310ec3ea4bc146e084f5c524b1ea7cca390e1469c46b", 16)
1140- w := "L38WQxF2njcyvgjAZ1f4n1cN7yp793VEopHaxav8er7HN5JLeriu"
1141- sd , err := NewPrivateKey (nil , & w )
1230+ func (sc * SignCommand ) Name () string {
1231+ return sc .fs .Name ()
1232+ }
1233+ 1234+ func (sc * SignCommand ) Init (args []string ) error {
1235+ sc .fs .Usage = func () {
1236+ fmt .Println (signUsagePrefix )
1237+ sc .fs .PrintDefaults ()
1238+ fmt .Println (signUsageExamples )
1239+ }
1240+ return sc .fs .Parse (args )
1241+ }
1242+ 1243+ func (sc * SignCommand ) Run () error {
1244+ if sc .help {
1245+ sc .fs .Usage ()
1246+ os .Exit (0 )
1247+ }
1248+ required := []string {"p" , "a" , "m" }
1249+ seen := make (map [string ]bool )
1250+ sc .fs .Visit (func (f * flag.Flag ) { seen [f .Name ] = true })
1251+ for _ , req := range required {
1252+ if ! seen [req ] {
1253+ return fmt .Errorf ("bmt: missing required -%s flag. See 'bmt sign -h'" , req )
1254+ }
1255+ }
1256+ pk , err := NewPrivateKey (nil , & sc .pk )
11421257 if err != nil {
1143- fmt .Println (err )
1144- }
1145- fmt .Println (sd .raw )
1146- var wg sync.WaitGroup
1147- for range 10 {
1148- wg .Add (1 )
1149- go func () {
1150- defer wg .Done ()
1151- ssd , err := NewPrivateKey (nil , nil )
1152- if err != nil {
1153- panic (err )
1154- }
1155- msg , err := SignMessage (ssd , "legacy" , "ECDSA is the most fun I have ever experienced" , true , false )
1156- if err != nil {
1157- panic (err )
1158- }
1159- fmt .Println (string (msg .Signature ))
1258+ return fmt .Errorf ("bmt: invalid private key: %w" , err )
1259+ }
1260+ bm , err := SignMessage (pk , sc .addrType , sc .message , sc .deterministic , sc .electrum )
1261+ if err != nil {
1262+ return fmt .Errorf ("bmt: failed signing message: %w" , err )
1263+ }
1264+ printMessage (bm )
1265+ return nil
1266+ }
11601267
1161- _ , _ , _ , err = VerifyMessage (msg , false )
1162- if err != nil {
1163- panic (err )
1164- }
1268+ func NewVerifyCommand () * VerifyCommand {
1269+ vc := & VerifyCommand {
1270+ fs : flag .NewFlagSet ("verify" , flag .ContinueOnError ),
1271+ }
1272+ vc .message = & BitcoinMessage {}
1273+ vc .fs .BoolVar (& vc .help , "h" , false , "show this help message and exit" )
1274+ vc .fs .BoolVar (& vc .electrum , "e" , false , "verify electrum-like signature" )
1275+ vc .fs .BoolVar (& vc .recpub , "r" , false , "recover public key" )
1276+ vc .fs .BoolVar (& vc .verbose , "v" , false , "show full message" )
1277+ vc .fs .StringVar (& vc .message .Address , "a" , "" , "ADDRESS bitcoin address" )
1278+ vc .fs .StringVar (& vc .message .Data , "m" , "" , "[MESSAGE ...] message to verify" )
1279+ vc .fs .Func ("s" , "SIGNATURE bitcoin signature in base64 format" , func (flagValue string ) error {
1280+ vc .message .Signature = []byte (flagValue )
1281+ return nil
1282+ })
1283+ return vc
1284+ }
11651285
1166- //fmt.Println(verified, pub, message)
1167- }()
1286+ type VerifyCommand struct {
1287+ fs * flag.FlagSet
1288+ 1289+ help bool
1290+ electrum bool
1291+ recpub bool
1292+ verbose bool
1293+ message * BitcoinMessage
1294+ }
1295+ 1296+ func (vc * VerifyCommand ) Name () string {
1297+ return vc .fs .Name ()
1298+ }
11681299
1300+ func (vc * VerifyCommand ) Init (args []string ) error {
1301+ vc .fs .Usage = func () {
1302+ fmt .Println (verifyUsagePrefix )
1303+ vc .fs .PrintDefaults ()
1304+ fmt .Println (verifyUsageExamples )
11691305 }
1170- wg .Wait ()
1171- fmt .Println (bitsToInt ([]byte ("Hello jshakdhasjkdhaskjdhkasjhdkjashdjkashdjks" ), 1123 ))
1172- fmt .Println (hex .EncodeToString (intToOct (big .NewInt (65533 ), 3 )))
1173- fmt .Println (hex .EncodeToString (bitsToOct ([]byte ("Hello jshakdhasjkdhaskjdhkasjhdkjashdjkashdjks" ), big .NewInt (6533 ), 36 , 12 )))
1174- fmt .Println (2 + 3 >> 2 )
1175- var d big.Int
1176- fmt .Println (d .Add (two , three ).Rsh (& d , 2 ))
1177- fmt .Println (rfcSign (big .NewInt (6553365533564754 ), big .NewInt (65533 )))
1178- _ , err = NewPrivateKey (big .NewInt (1000 ), & w )
1179- fmt .Println (errors .Unwrap (err ))
1306+ return vc .fs .Parse (args )
1307+ }
11801308
1181- _ , _ , _ , err = VerifyMessage (& BitcoinMessage {Address : "ss" , Data : "dddf" , Signature : []byte ("Gdggdgd" )}, false )
1182- //var dddd *SignatureError
1183- fmt .Println (time .Since (start ))
1309+ func (vc * VerifyCommand ) Run () error {
1310+ if vc .help {
1311+ vc .fs .Usage ()
1312+ os .Exit (0 )
1313+ }
1314+ required := []string {"s" , "a" , "m" }
1315+ seen := make (map [string ]bool )
1316+ vc .fs .Visit (func (f * flag.Flag ) { seen [f .Name ] = true })
1317+ for _ , req := range required {
1318+ if ! seen [req ] {
1319+ return fmt .Errorf ("bmt: missing required -%s flag. See 'bmt verify -h'" , req )
1320+ }
1321+ }
1322+ verified , pubkey , result , err := VerifyMessage (vc .message , vc .electrum )
1323+ if err != nil {
1324+ return fmt .Errorf ("bmt: failed verifying message: %w" , err )
1325+ }
1326+ fmt .Println (verified )
1327+ if vc .verbose {
1328+ fmt .Println (result )
1329+ }
1330+ if vc .recpub {
1331+ fmt .Println (pubkey )
1332+ }
1333+ return nil
1334+ }
11841335
1336+ type Runner interface {
1337+ Init ([]string ) error
1338+ Run () error
1339+ Name () string
1340+ }
1341+ 1342+ func root (args []string ) error {
1343+ flags .Usage = func () {
1344+ fmt .Println (usagePrefix )
1345+ flags .PrintDefaults ()
1346+ fmt .Println (usageCommands )
1347+ }
1348+ flags .Bool ("h" , false , "show this help message and exit" )
1349+ 1350+ if len (args ) < 1 || slices .Contains ([]string {"-h" , "--h" }, args [0 ]) {
1351+ flags .Usage ()
1352+ os .Exit (0 )
1353+ }
1354+ cmds := []Runner {
1355+ NewSignCommand (),
1356+ NewVerifyCommand (),
1357+ }
1358+ 1359+ subcommand := args [0 ]
1360+ 1361+ for _ , cmd := range cmds {
1362+ if cmd .Name () == subcommand {
1363+ cmd .Init (os .Args [2 :])
1364+ return cmd .Run ()
1365+ }
1366+ }
1367+ return fmt .Errorf ("bmt: '%s' is not a bmt command. See 'bmt -h'" , subcommand )
1368+ }
1369+ 1370+ func main () {
1371+ 1372+ if err := root (os .Args [1 :]); err != nil {
1373+ fmt .Fprintln (os .Stderr , err )
1374+ os .Exit (2 )
1375+ }
11851376}
0 commit comments