/
pubkey.go
148 lines (123 loc) · 4.01 KB
/
pubkey.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
package ecc
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"github.com/tokenbankteam/eos-go/btcsuite/btcd/btcec"
"github.com/tokenbankteam/eos-go/btcsuite/btcutil/base58"
"golang.org/x/crypto/ripemd160"
)
const PublicKeyPrefix = "PUB_"
const PublicKeyPrefixCompat = "EOS"
type PublicKey struct {
Curve CurveID
Content []byte
}
func NewPublicKey(pubKey string) (out PublicKey, err error) {
if len(pubKey) < 8 {
return out, fmt.Errorf("invalid format")
}
var pubKeyMaterial string
var curveID CurveID
if strings.HasPrefix(pubKey, PublicKeyPrefix) {
pubKeyMaterial = pubKey[len(PublicKeyPrefix):] // strip "PUB_"
curvePrefix := pubKeyMaterial[:3]
switch curvePrefix {
case "K1_":
curveID = CurveK1
case "R1_":
curveID = CurveR1
default:
return out, fmt.Errorf("unsupported curve prefix %q", curvePrefix)
}
pubKeyMaterial = pubKeyMaterial[3:] // strip "K1_"
} else if strings.HasPrefix(pubKey, PublicKeyPrefixCompat) { // "EOS"
pubKeyMaterial = pubKey[len(PublicKeyPrefixCompat):] // strip "EOS"
curveID = CurveK1
} else {
return out, fmt.Errorf("public key should start with %q (or the old %q)", PublicKeyPrefix, PublicKeyPrefixCompat)
}
pubDecoded, err := checkDecode(pubKeyMaterial, curveID)
if err != nil {
return out, fmt.Errorf("checkDecode: %s", err)
}
return PublicKey{Curve: curveID, Content: pubDecoded}, nil
}
func MustNewPublicKey(pubKey string) PublicKey {
key, err := NewPublicKey(pubKey)
if err != nil {
panic(err.Error())
}
return key
}
// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
func checkDecode(input string, curve CurveID) (result []byte, err error) {
decoded := base58.Decode(input)
if len(decoded) < 5 {
return nil, fmt.Errorf("invalid format")
}
var cksum [4]byte
copy(cksum[:], decoded[len(decoded)-4:])
///// WARN: ok the ripemd160checksum should include the prefix in CERTAIN situations,
// like when we imported the PubKey without a prefix ?! tied to the string representation
// or something ? weird.. checksum shouldn't change based on the string reprsentation.
if bytes.Compare(ripemd160checksum(decoded[:len(decoded)-4], curve), cksum[:]) != 0 {
return nil, fmt.Errorf("invalid checksum")
}
// perhaps bitcoin has a leading net ID / version, but EOS doesn't
payload := decoded[:len(decoded)-4]
result = append(result, payload...)
return
}
func ripemd160checksum(in []byte, curve CurveID) []byte {
h := ripemd160.New()
_, _ = h.Write(in) // this implementation has no error path
// if curve != CurveK1 {
// _, _ = h.Write([]byte(curve.String())) // conditionally ?
// }
sum := h.Sum(nil)
return sum[:4]
}
func Ripemd160checksumHashCurve(in []byte, curve CurveID) []byte {
h := ripemd160.New()
_, _ = h.Write(in) // this implementation has no error path
// FIXME: this seems to be only rolled out to the `SIG_` things..
// proper support for importing `EOS` keys isn't rolled out into `dawn4`.
_, _ = h.Write([]byte(curve.String())) // conditionally ?
sum := h.Sum(nil)
return sum[:4]
}
func (p PublicKey) Key() (*btcec.PublicKey, error) {
// TODO: implement the curve switch according to `p.Curve`
key, err := btcec.ParsePubKey(p.Content, btcec.S256())
if err != nil {
return nil, fmt.Errorf("parsePubKey: %s", err)
}
return key, nil
}
func (p PublicKey) String() string {
//hash := ripemd160checksum(append([]byte{byte(p.Curve)}, p.Content...)) does the checksum include the curve ID?!
hash := ripemd160checksum(p.Content, p.Curve)
rawkey := append(p.Content, hash[:4]...)
return PublicKeyPrefixCompat + base58.Encode(rawkey)
// FIXME: when we decide to go ahead with the new representation.
//return PublicKeyPrefix + p.Curve.StringPrefix() + base58.Encode(rawkey)
}
func (p PublicKey) MarshalJSON() ([]byte, error) {
s := p.String()
return json.Marshal(s)
}
func (p *PublicKey) UnmarshalJSON(data []byte) error {
var s string
err := json.Unmarshal(data, &s)
if err != nil {
return err
}
newKey, err := NewPublicKey(s)
if err != nil {
return err
}
*p = newKey
return nil
}