-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
ed25519.go
231 lines (189 loc) · 6.58 KB
/
ed25519.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package ed25519
import (
"crypto/ed25519"
"crypto/subtle"
"fmt"
"io"
"github.com/cometbft/cometbft/crypto"
"github.com/cometbft/cometbft/crypto/tmhash"
"github.com/hdevalence/ed25519consensus"
errorsmod "cosmossdk.io/errors"
"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/types/errors"
)
//-------------------------------------
const (
PrivKeyName = "tendermint/PrivKeyEd25519"
PubKeyName = "tendermint/PubKeyEd25519"
// PubKeySize is is the size, in bytes, of public keys as used in this package.
PubKeySize = 32
// PrivKeySize is the size, in bytes, of private keys as used in this package.
PrivKeySize = 64
// Size of an Edwards25519 signature. Namely the size of a compressed
// Edwards25519 point, and a field element. Both of which are 32 bytes.
SignatureSize = 64
// SeedSize is the size, in bytes, of private key seeds. These are the
// private key representations used by RFC 8032.
SeedSize = 32
keyType = "ed25519"
)
var (
_ cryptotypes.PrivKey = &PrivKey{}
_ codec.AminoMarshaler = &PrivKey{}
)
// Bytes returns the privkey byte format.
func (privKey *PrivKey) Bytes() []byte {
return privKey.Key
}
// Sign produces a signature on the provided message.
// This assumes the privkey is wellformed in the golang format.
// The first 32 bytes should be random,
// corresponding to the normal ed25519 private key.
// The latter 32 bytes should be the compressed public key.
// If these conditions aren't met, Sign will panic or produce an
// incorrect signature.
func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) {
return ed25519.Sign(privKey.Key, msg), nil
}
// PubKey gets the corresponding public key from the private key.
//
// Panics if the private key is not initialized.
func (privKey *PrivKey) PubKey() cryptotypes.PubKey {
// If the latter 32 bytes of the privkey are all zero, privkey is not
// initialized.
initialized := false
for _, v := range privKey.Key[32:] {
if v != 0 {
initialized = true
break
}
}
if !initialized {
panic("Expected ed25519 PrivKey to include concatenated pubkey bytes")
}
pubkeyBytes := make([]byte, PubKeySize)
copy(pubkeyBytes, privKey.Key[32:])
return &PubKey{Key: pubkeyBytes}
}
// Equals - you probably don't need to use this.
// Runs in constant time based on length of the keys.
func (privKey *PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool {
if privKey.Type() != other.Type() {
return false
}
return subtle.ConstantTimeCompare(privKey.Bytes(), other.Bytes()) == 1
}
func (privKey *PrivKey) Type() string {
return keyType
}
// MarshalAmino overrides Amino binary marshaling.
func (privKey PrivKey) MarshalAmino() ([]byte, error) {
return privKey.Key, nil
}
// UnmarshalAmino overrides Amino binary marshaling.
func (privKey *PrivKey) UnmarshalAmino(bz []byte) error {
if len(bz) != PrivKeySize {
return fmt.Errorf("invalid privkey size")
}
privKey.Key = bz
return nil
}
// MarshalAminoJSON overrides Amino JSON marshaling.
func (privKey PrivKey) MarshalAminoJSON() ([]byte, error) {
// When we marshal to Amino JSON, we don't marshal the "key" field itself,
// just its contents (i.e. the key bytes).
return privKey.MarshalAmino()
}
// UnmarshalAminoJSON overrides Amino JSON marshaling.
func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error {
return privKey.UnmarshalAmino(bz)
}
// GenPrivKey generates a new ed25519 private key. These ed25519 keys must not
// be used in SDK apps except in a tendermint validator context.
// It uses OS randomness in conjunction with the current global random seed
// in tendermint/libs/common to generate the private key.
func GenPrivKey() *PrivKey {
return genPrivKey(crypto.CReader())
}
// genPrivKey generates a new ed25519 private key using the provided reader.
func genPrivKey(rand io.Reader) *PrivKey {
seed := make([]byte, SeedSize)
_, err := io.ReadFull(rand, seed)
if err != nil {
panic(err)
}
return &PrivKey{Key: ed25519.NewKeyFromSeed(seed)}
}
// GenPrivKeyFromSecret hashes the secret with SHA2, and uses
// that 32 byte output to create the private key.
// NOTE: ed25519 keys must not be used in SDK apps except in a tendermint validator context.
// NOTE: secret should be the output of a KDF like bcrypt,
// if it's derived from user input.
func GenPrivKeyFromSecret(secret []byte) *PrivKey {
seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes.
return &PrivKey{Key: ed25519.NewKeyFromSeed(seed)}
}
//-------------------------------------
var (
_ cryptotypes.PubKey = &PubKey{}
_ codec.AminoMarshaler = &PubKey{}
)
// Address is the SHA256-20 of the raw pubkey bytes.
// It doesn't implement ADR-28 addresses and it must not be used
// in SDK except in a tendermint validator context.
func (pubKey *PubKey) Address() crypto.Address {
if len(pubKey.Key) != PubKeySize {
panic("pubkey is incorrect size")
}
// For ADR-28 compatible address we would need to
// return address.Hash(proto.MessageName(pubKey), pubKey.Key)
return crypto.Address(tmhash.SumTruncated(pubKey.Key))
}
// Bytes returns the PubKey byte format.
func (pubKey *PubKey) Bytes() []byte {
return pubKey.Key
}
func (pubKey *PubKey) VerifySignature(msg, sig []byte) bool {
// make sure we use the same algorithm to sign
if len(sig) != SignatureSize {
return false
}
// uses https://github.com/hdevalence/ed25519consensus.Verify to comply with zip215 verification rules
return ed25519consensus.Verify(pubKey.Key, msg, sig)
}
// String returns Hex representation of a pubkey with it's type
func (pubKey *PubKey) String() string {
return fmt.Sprintf("PubKeyEd25519{%X}", pubKey.Key)
}
func (pubKey *PubKey) Type() string {
return keyType
}
func (pubKey *PubKey) Equals(other cryptotypes.PubKey) bool {
if pubKey.Type() != other.Type() {
return false
}
return subtle.ConstantTimeCompare(pubKey.Bytes(), other.Bytes()) == 1
}
// MarshalAmino overrides Amino binary marshaling.
func (pubKey PubKey) MarshalAmino() ([]byte, error) {
return pubKey.Key, nil
}
// UnmarshalAmino overrides Amino binary marshaling.
func (pubKey *PubKey) UnmarshalAmino(bz []byte) error {
if len(bz) != PubKeySize {
return errorsmod.Wrap(errors.ErrInvalidPubKey, "invalid pubkey size")
}
pubKey.Key = bz
return nil
}
// MarshalAminoJSON overrides Amino JSON marshaling.
func (pubKey PubKey) MarshalAminoJSON() ([]byte, error) {
// When we marshal to Amino JSON, we don't marshal the "key" field itself,
// just its contents (i.e. the key bytes).
return pubKey.MarshalAmino()
}
// UnmarshalAminoJSON overrides Amino JSON marshaling.
func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error {
return pubKey.UnmarshalAmino(bz)
}