-
Notifications
You must be signed in to change notification settings - Fork 0
/
flosig.go
167 lines (144 loc) · 4.56 KB
/
flosig.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package flosig
import (
"bytes"
"encoding/base64"
"github.com/bitspill/flod/chaincfg"
"github.com/bitspill/flod/chaincfg/chainhash"
"github.com/bitspill/flod/floec"
"github.com/bitspill/flod/flojson"
"github.com/bitspill/flod/wire"
"github.com/bitspill/floutil"
)
type keyType uint32
const (
P2PKH keyType = iota
P2WPKH
P2WSH
)
func CheckSignature(checkAddress string, checkSignature string, message string, coinName string, net *chaincfg.Params) (bool, error) {
// Function body largely lifted from BTCD with modifications for Trezor compatibility
// https://github.com/btcsuite/btcd/blob/807d344/rpcserver.go#L3607
// https://github.com/trezor/trezor-mcu/blob/19c7c8b/firmware/crypto.c#L151
// Decode the provided address.
addr, err := floutil.DecodeAddress(checkAddress, net)
if err != nil {
return false, &flojson.RPCError{
Code: flojson.ErrRPCInvalidAddressOrKey,
Message: "Invalid address or key: " + err.Error(),
}
}
// Decode base64 signature.
sig, err := base64.StdEncoding.DecodeString(checkSignature)
if err != nil {
return false, &flojson.RPCError{
Code: flojson.ErrRPCParse.Code,
Message: "Malformed base64 encoding: " + err.Error(),
}
}
if len(sig) == 0 {
return false, &flojson.RPCError{
Code: flojson.ErrRPCParse.Code,
Message: "Malformed signature",
}
}
if sig[0] < 27 || sig[0] > 43 {
return false, &flojson.RPCError{
Code: flojson.ErrRPCType,
Message: "Invalid signature prefix",
}
}
var kt keyType
// pay-to-pubkey-hash (P2PKH)
if sig[0] >= 27 && sig[0] <= 34 {
kt = P2PKH
if _, ok := addr.(*floutil.AddressPubKeyHash); !ok {
return false, &flojson.RPCError{
Code: flojson.ErrRPCType,
Message: "Address is not a pay-to-pubkey-hash address",
}
}
}
// pay-to-witness-pubkey-hash (P2WPKH) (base58)
if sig[0] >= 35 && sig[0] <= 38 {
kt = P2WPKH
if _, ok := addr.(*floutil.AddressScriptHash); !ok {
return false, &flojson.RPCError{
Code: flojson.ErrRPCType,
Message: "Address is not a pay-to-witness-pubkey-hash address",
}
}
}
// pay-to-witness-script-hash (P2WSH) (bech32)
if sig[0] >= 39 && sig[0] <= 42 {
kt = P2WSH
return false, &flojson.RPCError{
Code: flojson.ErrRPCType,
Message: "Address of type pay-to-witness-script-hash not supported",
}
}
// Validate the signature - this just shows that it was valid at all.
// we will compare it with the key next.
var buf bytes.Buffer
_ = wire.WriteVarString(&buf, 0, coinName+" Signed Message:\n")
_ = wire.WriteVarString(&buf, 0, message)
expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
pk, wasCompressed, err := floec.RecoverCompact(floec.S256(), sig,
expectedMessageHash)
if err != nil {
// Mirror Bitcoin Core behavior, which treats error in
// RecoverCompact as invalid signature.
return false, nil
}
// Reconstruct the pubkey hash.
var serializedPK []byte
if wasCompressed {
serializedPK = pk.SerializeCompressed()
} else {
serializedPK = pk.SerializeUncompressed()
}
var address floutil.Address
switch kt {
case P2PKH:
address, err = floutil.NewAddressPubKey(serializedPK, net)
case P2WPKH:
address, err = floutil.NewAddressScriptHash(serializedPK, net)
if err != nil {
// Again mirror Bitcoin Core behavior, which treats error in public key
// reconstruction as invalid signature.
return false, nil
}
address, err = floutil.NewAddressScriptHash(append([]byte{0, 20}, address.ScriptAddress()...), net)
case P2WSH:
// Not Supported, caught higher up
// return false to be safe
return false, nil
}
if err != nil {
// Again mirror Bitcoin Core behavior, which treats error in public key
// reconstruction as invalid signature.
return false, nil
}
// Return boolean if addresses match.
return address.EncodeAddress() == checkAddress, nil
}
func SignMessage(msg string, coinName string, wif string) (string, error) {
w, err := floutil.DecodeWIF(wif)
if err != nil {
return "", err
}
return SignMessagePk(msg, coinName, w.PrivKey, w.CompressPubKey)
}
func SignMessagePk(msg string, coinName string, prv *floec.PrivateKey, compressed bool) (string, error) {
var buf bytes.Buffer
_ = wire.WriteVarString(&buf, 0, coinName+" Signed Message:\n")
_ = wire.WriteVarString(&buf, 0, msg)
messageHash := chainhash.DoubleHashB(buf.Bytes())
sig, err := floec.SignCompact(floec.S256(), prv, messageHash, compressed)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(sig), nil
}