-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathkey.go
More file actions
166 lines (144 loc) · 4.54 KB
/
key.go
File metadata and controls
166 lines (144 loc) · 4.54 KB
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
// Copyright (c) 2020 IoTeX
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
// License 2.0 that can be found in the LICENSE file.
package crypto
import (
"encoding/hex"
"io/ioutil"
"github.com/erigontech/secp256k1"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/iotexproject/iotex-address/address"
"github.com/pkg/errors"
"github.com/iotexproject/go-pkgs/util"
)
// const
const (
// 64-byte (r, s) format
Secp256k1SigSize = 64
// 65-byte (r, s, v) format, where v is the recovery id
Secp256k1SigSizeWithRecID = 65
)
const (
secp256pubKeyLength = 65
secp256prvKeyLength = 32
)
var (
// ErrInvalidKey is the error that the key format is invalid
ErrInvalidKey = errors.New("invalid key format")
// ErrPublicKey indicates the error of public key
ErrPublicKey = errors.New("invalid public key")
// ErrPrivateKey indicates the error of private key
ErrPrivateKey = errors.New("invalid private key")
)
type (
// PublicKey represents a public key
PublicKey interface {
Bytes() []byte
HexString() string
EcdsaPublicKey() interface{}
Hash() []byte
Verify([]byte, []byte) bool
Address() address.Address
}
// PrivateKey represents a private key
PrivateKey interface {
Bytes() []byte
HexString() string
EcdsaPrivateKey() interface{}
PublicKey() PublicKey
Sign([]byte) ([]byte, error)
Zero()
}
)
// GenerateKey generates a SECP256k1 PrivateKey
func GenerateKey() (PrivateKey, error) {
return newSecp256k1PrvKey()
}
// GenerateKeySm2 generates a P256sm2 PrivateKey
func GenerateKeySm2() (PrivateKey, error) {
return newP256sm2PrvKey()
}
// HexStringToPublicKey decodes a string to PublicKey
func HexStringToPublicKey(pubKey string) (PublicKey, error) {
b, err := hex.DecodeString(util.Remove0xPrefix(pubKey))
if err != nil {
return nil, errors.Wrapf(err, "failed to decode public key %s", pubKey)
}
return BytesToPublicKey(b)
}
// HexStringToPrivateKey decodes a string to PrivateKey
func HexStringToPrivateKey(prvKey string) (PrivateKey, error) {
b, err := hex.DecodeString(util.Remove0xPrefix(prvKey))
if err != nil {
return nil, errors.Wrapf(err, "failed to decode public key %s", prvKey)
}
return BytesToPrivateKey(b)
}
// BytesToPublicKey converts a byte slice to SECP256K1 PublicKey
func BytesToPublicKey(pubKey []byte) (PublicKey, error) {
if len(pubKey) == secp256pubKeyLength-1 {
pubKey = append([]byte{4}, pubKey...)
}
// check against P256k1
if len(pubKey) == secp256pubKeyLength {
return newSecp256k1PubKeyFromBytes(pubKey)
}
// check against P256sm2
if k, err := newP256sm2PubKeyFromBytes(pubKey); err == nil {
return k, nil
}
return nil, ErrPublicKey
}
// BytesToPrivateKey converts a byte slice to SECP256K1 PrivateKey
func BytesToPrivateKey(prvKey []byte) (PrivateKey, error) {
// check against P256sm2
if len(prvKey) == secp256prvKeyLength {
return newSecp256k1PrvKeyFromBytes(prvKey)
}
// check against P256sm2
if k, err := newP256sm2PrvKeyFromBytes(prvKey); err == nil {
return k, nil
}
return nil, ErrPrivateKey
}
// KeystoreToPrivateKey generates PrivateKey from Keystore account
func KeystoreToPrivateKey(account accounts.Account, password string) (PrivateKey, error) {
// load the key from the keystore
keyJSON, err := ioutil.ReadFile(account.URL.Path)
if err != nil {
return nil, err
}
key, err := keystore.DecryptKey(keyJSON, password)
if err != nil {
return nil, err
}
return &secp256k1PrvKey{
PrivateKey: key.PrivateKey,
}, nil
}
// RecoverPubkey recovers the public key from signature
func RecoverPubkey(msg, sig []byte) (PublicKey, error) {
if pk, err := recoverSecp256k1(msg, sig); err == nil {
return pk, nil
}
// TODO: implement recover key for sm2
return nil, ErrInvalidKey
}
func recoverSecp256k1(msg, sig []byte) (PublicKey, error) {
if len(sig) >= Secp256k1SigSizeWithRecID && sig[Secp256k1SigSize] >= 27 {
// when an Ethereum signature is calculated, 27 is added to recovery id
// https://github.com/ethereum/go-ethereum/commit/b59c8399fbe42390a3d41e945d03b1f21c1a9b8d#diff-31c4aa3a4249d4755fc652d3e0087b98R226-R232
sig[Secp256k1SigSize] -= 27
defer func() {
sig[Secp256k1SigSize] += 27
}()
}
pk, err := secp256k1.RecoverPubkey(msg, sig)
if err != nil {
return nil, err
}
return newSecp256k1PubKeyFromBytes(pk)
}