Skip to content
This repository was archived by the owner on Aug 2, 2021. It is now read-only.

Commit 80a7dd3

Browse files
fjlgbalint
authored andcommitted
p2p/enr: updates for discovery v4 compatibility (#16679)
This applies spec changes from ethereum/EIPs#1049 and adds support for pluggable identity schemes. Some care has been taken to make the "v4" scheme standalone. It uses public APIs only and could be moved out of package enr at any time. A couple of minor changes were needed to make identity schemes work: - The sequence number is now updated in Set instead of when signing. - Record is now copy-safe, i.e. calling Set on a shallow copy doesn't modify the record it was copied from.
1 parent caf7944 commit 80a7dd3

File tree

5 files changed

+277
-154
lines changed

5 files changed

+277
-154
lines changed

p2p/enr/enr.go

+74-85
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,16 @@ package enr
2929

3030
import (
3131
"bytes"
32-
"crypto/ecdsa"
3332
"errors"
3433
"fmt"
3534
"io"
3635
"sort"
3736

38-
"github.com/ethereum/go-ethereum/crypto"
39-
"github.com/ethereum/go-ethereum/crypto/sha3"
4037
"github.com/ethereum/go-ethereum/rlp"
4138
)
4239

4340
const SizeLimit = 300 // maximum encoded size of a node record in bytes
4441

45-
const ID_SECP256k1_KECCAK = ID("secp256k1-keccak") // the default identity scheme
46-
4742
var (
4843
errNoID = errors.New("unknown or unspecified identity scheme")
4944
errInvalidSig = errors.New("invalid signature")
@@ -80,8 +75,8 @@ func (r *Record) Seq() uint64 {
8075
}
8176

8277
// SetSeq updates the record sequence number. This invalidates any signature on the record.
83-
// Calling SetSeq is usually not required because signing the redord increments the
84-
// sequence number.
78+
// Calling SetSeq is usually not required because setting any key in a signed record
79+
// increments the sequence number.
8580
func (r *Record) SetSeq(s uint64) {
8681
r.signature = nil
8782
r.raw = nil
@@ -104,33 +99,42 @@ func (r *Record) Load(e Entry) error {
10499
return &KeyError{Key: e.ENRKey(), Err: errNotFound}
105100
}
106101

107-
// Set adds or updates the given entry in the record.
108-
// It panics if the value can't be encoded.
102+
// Set adds or updates the given entry in the record. It panics if the value can't be
103+
// encoded. If the record is signed, Set increments the sequence number and invalidates
104+
// the sequence number.
109105
func (r *Record) Set(e Entry) {
110-
r.signature = nil
111-
r.raw = nil
112106
blob, err := rlp.EncodeToBytes(e)
113107
if err != nil {
114108
panic(fmt.Errorf("enr: can't encode %s: %v", e.ENRKey(), err))
115109
}
110+
r.invalidate()
116111

117-
i := sort.Search(len(r.pairs), func(i int) bool { return r.pairs[i].k >= e.ENRKey() })
118-
119-
if i < len(r.pairs) && r.pairs[i].k == e.ENRKey() {
112+
pairs := make([]pair, len(r.pairs))
113+
copy(pairs, r.pairs)
114+
i := sort.Search(len(pairs), func(i int) bool { return pairs[i].k >= e.ENRKey() })
115+
switch {
116+
case i < len(pairs) && pairs[i].k == e.ENRKey():
120117
// element is present at r.pairs[i]
121-
r.pairs[i].v = blob
122-
return
123-
} else if i < len(r.pairs) {
118+
pairs[i].v = blob
119+
case i < len(r.pairs):
124120
// insert pair before i-th elem
125121
el := pair{e.ENRKey(), blob}
126-
r.pairs = append(r.pairs, pair{})
127-
copy(r.pairs[i+1:], r.pairs[i:])
128-
r.pairs[i] = el
129-
return
122+
pairs = append(pairs, pair{})
123+
copy(pairs[i+1:], pairs[i:])
124+
pairs[i] = el
125+
default:
126+
// element should be placed at the end of r.pairs
127+
pairs = append(pairs, pair{e.ENRKey(), blob})
130128
}
129+
r.pairs = pairs
130+
}
131131

132-
// element should be placed at the end of r.pairs
133-
r.pairs = append(r.pairs, pair{e.ENRKey(), blob})
132+
func (r *Record) invalidate() {
133+
if r.signature == nil {
134+
r.seq++
135+
}
136+
r.signature = nil
137+
r.raw = nil
134138
}
135139

136140
// EncodeRLP implements rlp.Encoder. Encoding fails if
@@ -196,94 +200,79 @@ func (r *Record) DecodeRLP(s *rlp.Stream) error {
196200
return err
197201
}
198202

199-
// Verify signature.
200-
if err = dec.verifySignature(); err != nil {
203+
_, scheme := dec.idScheme()
204+
if scheme == nil {
205+
return errNoID
206+
}
207+
if err := scheme.Verify(&dec, dec.signature); err != nil {
201208
return err
202209
}
203210
*r = dec
204211
return nil
205212
}
206213

207-
type s256raw []byte
208-
209-
func (s256raw) ENRKey() string { return "secp256k1" }
210-
211214
// NodeAddr returns the node address. The return value will be nil if the record is
212-
// unsigned.
215+
// unsigned or uses an unknown identity scheme.
213216
func (r *Record) NodeAddr() []byte {
214-
var entry s256raw
215-
if r.Load(&entry) != nil {
217+
_, scheme := r.idScheme()
218+
if scheme == nil {
216219
return nil
217220
}
218-
return crypto.Keccak256(entry)
221+
return scheme.NodeAddr(r)
219222
}
220223

221-
// Sign signs the record with the given private key. It updates the record's identity
222-
// scheme, public key and increments the sequence number. Sign returns an error if the
223-
// encoded record is larger than the size limit.
224-
func (r *Record) Sign(privkey *ecdsa.PrivateKey) error {
225-
r.seq = r.seq + 1
226-
r.Set(ID_SECP256k1_KECCAK)
227-
r.Set(Secp256k1(privkey.PublicKey))
228-
return r.signAndEncode(privkey)
224+
// SetSig sets the record signature. It returns an error if the encoded record is larger
225+
// than the size limit or if the signature is invalid according to the passed scheme.
226+
func (r *Record) SetSig(idscheme string, sig []byte) error {
227+
// Check that "id" is set and matches the given scheme. This panics because
228+
// inconsitencies here are always implementation bugs in the signing function calling
229+
// this method.
230+
id, s := r.idScheme()
231+
if s == nil {
232+
panic(errNoID)
233+
}
234+
if id != idscheme {
235+
panic(fmt.Errorf("identity scheme mismatch in Sign: record has %s, want %s", id, idscheme))
236+
}
237+
238+
// Verify against the scheme.
239+
if err := s.Verify(r, sig); err != nil {
240+
return err
241+
}
242+
raw, err := r.encode(sig)
243+
if err != nil {
244+
return err
245+
}
246+
r.signature, r.raw = sig, raw
247+
return nil
229248
}
230249

231-
func (r *Record) appendPairs(list []interface{}) []interface{} {
250+
// AppendElements appends the sequence number and entries to the given slice.
251+
func (r *Record) AppendElements(list []interface{}) []interface{} {
232252
list = append(list, r.seq)
233253
for _, p := range r.pairs {
234254
list = append(list, p.k, p.v)
235255
}
236256
return list
237257
}
238258

239-
func (r *Record) signAndEncode(privkey *ecdsa.PrivateKey) error {
240-
// Put record elements into a flat list. Leave room for the signature.
241-
list := make([]interface{}, 1, len(r.pairs)*2+2)
242-
list = r.appendPairs(list)
243-
244-
// Sign the tail of the list.
245-
h := sha3.NewKeccak256()
246-
rlp.Encode(h, list[1:])
247-
sig, err := crypto.Sign(h.Sum(nil), privkey)
248-
if err != nil {
249-
return err
250-
}
251-
sig = sig[:len(sig)-1] // remove v
252-
253-
// Put signature in front.
254-
r.signature, list[0] = sig, sig
255-
r.raw, err = rlp.EncodeToBytes(list)
256-
if err != nil {
257-
return err
259+
func (r *Record) encode(sig []byte) (raw []byte, err error) {
260+
list := make([]interface{}, 1, 2*len(r.pairs)+1)
261+
list[0] = sig
262+
list = r.AppendElements(list)
263+
if raw, err = rlp.EncodeToBytes(list); err != nil {
264+
return nil, err
258265
}
259-
if len(r.raw) > SizeLimit {
260-
return errTooBig
266+
if len(raw) > SizeLimit {
267+
return nil, errTooBig
261268
}
262-
return nil
269+
return raw, nil
263270
}
264271

265-
func (r *Record) verifySignature() error {
266-
// Get identity scheme, public key, signature.
272+
func (r *Record) idScheme() (string, IdentityScheme) {
267273
var id ID
268-
var entry s256raw
269274
if err := r.Load(&id); err != nil {
270-
return err
271-
} else if id != ID_SECP256k1_KECCAK {
272-
return errNoID
275+
return "", nil
273276
}
274-
if err := r.Load(&entry); err != nil {
275-
return err
276-
} else if len(entry) != 33 {
277-
return fmt.Errorf("invalid public key")
278-
}
279-
280-
// Verify the signature.
281-
list := make([]interface{}, 0, len(r.pairs)*2+1)
282-
list = r.appendPairs(list)
283-
h := sha3.NewKeccak256()
284-
rlp.Encode(h, list)
285-
if !crypto.VerifySignature(entry, h.Sum(nil), r.signature) {
286-
return errInvalidSig
287-
}
288-
return nil
277+
return string(id), FindIdentityScheme(string(id))
289278
}

0 commit comments

Comments
 (0)