mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
parent
70e17f09cd
commit
a46e6a9a25
5 changed files with 277 additions and 156 deletions
159
p2p/enr/enr.go
159
p2p/enr/enr.go
|
|
@ -29,21 +29,16 @@ package enr
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const SizeLimit = 300 // maximum encoded size of a node record in bytes
|
||||
|
||||
const ID_SECP256k1_KECCAK = ID("secp256k1-keccak") // the default identity scheme
|
||||
|
||||
var (
|
||||
errNoID = errors.New("unknown or unspecified identity scheme")
|
||||
errInvalidSig = errors.New("invalid signature")
|
||||
|
|
@ -80,8 +75,8 @@ func (r *Record) Seq() uint64 {
|
|||
}
|
||||
|
||||
// SetSeq updates the record sequence number. This invalidates any signature on the record.
|
||||
// Calling SetSeq is usually not required because signing the redord increments the
|
||||
// sequence number.
|
||||
// Calling SetSeq is usually not required because setting any key in a signed record
|
||||
// increments the sequence number.
|
||||
func (r *Record) SetSeq(s uint64) {
|
||||
r.signature = nil
|
||||
r.raw = nil
|
||||
|
|
@ -104,33 +99,42 @@ func (r *Record) Load(e Entry) error {
|
|||
return &KeyError{Key: e.ENRKey(), Err: errNotFound}
|
||||
}
|
||||
|
||||
// Set adds or updates the given entry in the record.
|
||||
// It panics if the value can't be encoded.
|
||||
// Set adds or updates the given entry in the record. It panics if the value can't be
|
||||
// encoded. If the record is signed, Set increments the sequence number and invalidates
|
||||
// the sequence number.
|
||||
func (r *Record) Set(e Entry) {
|
||||
r.signature = nil
|
||||
r.raw = nil
|
||||
blob, err := rlp.EncodeToBytes(e)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("enr: can't encode %s: %v", e.ENRKey(), err))
|
||||
}
|
||||
r.invalidate()
|
||||
|
||||
i := sort.Search(len(r.pairs), func(i int) bool { return r.pairs[i].k >= e.ENRKey() })
|
||||
|
||||
if i < len(r.pairs) && r.pairs[i].k == e.ENRKey() {
|
||||
pairs := make([]pair, len(r.pairs))
|
||||
copy(pairs, r.pairs)
|
||||
i := sort.Search(len(pairs), func(i int) bool { return pairs[i].k >= e.ENRKey() })
|
||||
switch {
|
||||
case i < len(pairs) && pairs[i].k == e.ENRKey():
|
||||
// element is present at r.pairs[i]
|
||||
r.pairs[i].v = blob
|
||||
return
|
||||
} else if i < len(r.pairs) {
|
||||
pairs[i].v = blob
|
||||
case i < len(r.pairs):
|
||||
// insert pair before i-th elem
|
||||
el := pair{e.ENRKey(), blob}
|
||||
r.pairs = append(r.pairs, pair{})
|
||||
copy(r.pairs[i+1:], r.pairs[i:])
|
||||
r.pairs[i] = el
|
||||
return
|
||||
pairs = append(pairs, pair{})
|
||||
copy(pairs[i+1:], pairs[i:])
|
||||
pairs[i] = el
|
||||
default:
|
||||
// element should be placed at the end of r.pairs
|
||||
pairs = append(pairs, pair{e.ENRKey(), blob})
|
||||
}
|
||||
r.pairs = pairs
|
||||
}
|
||||
|
||||
// element should be placed at the end of r.pairs
|
||||
r.pairs = append(r.pairs, pair{e.ENRKey(), blob})
|
||||
func (r *Record) invalidate() {
|
||||
if r.signature == nil {
|
||||
r.seq++
|
||||
}
|
||||
r.signature = nil
|
||||
r.raw = nil
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder. Encoding fails if
|
||||
|
|
@ -196,39 +200,55 @@ func (r *Record) DecodeRLP(s *rlp.Stream) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Verify signature.
|
||||
if err = dec.verifySignature(); err != nil {
|
||||
_, scheme := dec.idScheme()
|
||||
if scheme == nil {
|
||||
return errNoID
|
||||
}
|
||||
if err := scheme.Verify(&dec, dec.signature); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = dec
|
||||
return nil
|
||||
}
|
||||
|
||||
type s256raw []byte
|
||||
|
||||
func (s256raw) ENRKey() string { return "secp256k1" }
|
||||
|
||||
// NodeAddr returns the node address. The return value will be nil if the record is
|
||||
// unsigned.
|
||||
func (r *Record) NodeAddr() []byte {
|
||||
var entry s256raw
|
||||
if r.Load(&entry) != nil {
|
||||
_, scheme := r.idScheme()
|
||||
if scheme == nil {
|
||||
return nil
|
||||
}
|
||||
return crypto.Keccak256(entry)
|
||||
return scheme.NodeAddr(r)
|
||||
}
|
||||
|
||||
// Sign signs the record with the given private key. It updates the record's identity
|
||||
// scheme, public key and increments the sequence number. Sign returns an error if the
|
||||
// encoded record is larger than the size limit.
|
||||
func (r *Record) Sign(privkey *ecdsa.PrivateKey) error {
|
||||
r.seq = r.seq + 1
|
||||
r.Set(ID_SECP256k1_KECCAK)
|
||||
r.Set(Secp256k1(privkey.PublicKey))
|
||||
return r.signAndEncode(privkey)
|
||||
// SetSig sets the record signature. It returns an error if the encoded record is larger
|
||||
// than the size limit or if the signature is invalid according to the passed scheme.
|
||||
func (r *Record) SetSig(idscheme string, sig []byte) error {
|
||||
// Check that "id" is set and matches the given scheme. This panics because
|
||||
// inconsitencies here are always implementation bugs in the signing function calling
|
||||
// this method.
|
||||
id, s := r.idScheme()
|
||||
if s == nil {
|
||||
panic(errNoID)
|
||||
}
|
||||
if id != idscheme {
|
||||
panic(fmt.Errorf("identity scheme mismatch in Sign: record has %s, want %s", id, idscheme))
|
||||
}
|
||||
|
||||
// Verify against the scheme.
|
||||
if err := s.Verify(r, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := r.encode(sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.signature, r.raw = sig, raw
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Record) appendPairs(list []interface{}) []interface{} {
|
||||
// AppendElements appends the sequence number and entries to the given slice.
|
||||
func (r *Record) AppendElements(list []interface{}) []interface{} {
|
||||
list = append(list, r.seq)
|
||||
for _, p := range r.pairs {
|
||||
list = append(list, p.k, p.v)
|
||||
|
|
@ -236,56 +256,23 @@ func (r *Record) appendPairs(list []interface{}) []interface{} {
|
|||
return list
|
||||
}
|
||||
|
||||
func (r *Record) signAndEncode(privkey *ecdsa.PrivateKey) error {
|
||||
// Put record elements into a flat list. Leave room for the signature.
|
||||
list := make([]interface{}, 1, len(r.pairs)*2+2)
|
||||
list = r.appendPairs(list)
|
||||
|
||||
// Sign the tail of the list.
|
||||
h := sha3.NewLegacyKeccak256()
|
||||
if err := rlp.Encode(h, list[1:]); err != nil {
|
||||
return err
|
||||
func (r *Record) encode(sig []byte) (raw []byte, err error) {
|
||||
list := make([]interface{}, 1, 2*len(r.pairs)+1)
|
||||
list[0] = sig
|
||||
list = r.AppendElements(list)
|
||||
if raw, err = rlp.EncodeToBytes(list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig, err := crypto.Sign(h.Sum(nil), privkey)
|
||||
if err != nil {
|
||||
return err
|
||||
if len(raw) > SizeLimit {
|
||||
return nil, errTooBig
|
||||
}
|
||||
sig = sig[:len(sig)-1] // remove v
|
||||
|
||||
// Put signature in front.
|
||||
r.signature, list[0] = sig, sig
|
||||
r.raw, err = rlp.EncodeToBytes(list)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(r.raw) > SizeLimit {
|
||||
return errTooBig
|
||||
}
|
||||
return nil
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (r *Record) verifySignature() error {
|
||||
// Get identity scheme, public key, signature.
|
||||
func (r *Record) idScheme() (string, IdentityScheme) {
|
||||
var id ID
|
||||
var entry s256raw
|
||||
if err := r.Load(&id); err != nil {
|
||||
return err
|
||||
} else if id != ID_SECP256k1_KECCAK {
|
||||
return errNoID
|
||||
return "", nil
|
||||
}
|
||||
if err := r.Load(&entry); err != nil {
|
||||
return err
|
||||
} else if len(entry) != 33 {
|
||||
return errors.New("invalid public key")
|
||||
}
|
||||
|
||||
// Verify the signature.
|
||||
list := make([]interface{}, 0, len(r.pairs)*2+1)
|
||||
list = r.appendPairs(list)
|
||||
h := sha3.NewLegacyKeccak256()
|
||||
rlp.Encode(h, list)
|
||||
if !crypto.VerifySignature(entry, h.Sum(nil), r.signature) {
|
||||
return errInvalidSig
|
||||
}
|
||||
return nil
|
||||
return string(id), FindIdentityScheme(string(id))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,35 +54,35 @@ func TestGetSetID(t *testing.T) {
|
|||
assert.Equal(t, id, id2)
|
||||
}
|
||||
|
||||
// TestGetSetIP4 tests encoding/decoding and setting/getting of the IP4 key.
|
||||
// TestGetSetIP4 tests encoding/decoding and setting/getting of the IP key.
|
||||
func TestGetSetIP4(t *testing.T) {
|
||||
ip := IP4{192, 168, 0, 3}
|
||||
ip := IP{192, 168, 0, 3}
|
||||
var r Record
|
||||
r.Set(ip)
|
||||
|
||||
var ip2 IP4
|
||||
var ip2 IP
|
||||
require.NoError(t, r.Load(&ip2))
|
||||
assert.Equal(t, ip, ip2)
|
||||
}
|
||||
|
||||
// TestGetSetIP6 tests encoding/decoding and setting/getting of the IP6 key.
|
||||
// TestGetSetIP6 tests encoding/decoding and setting/getting of the IP key.
|
||||
func TestGetSetIP6(t *testing.T) {
|
||||
ip := IP6{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}
|
||||
ip := IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}
|
||||
var r Record
|
||||
r.Set(ip)
|
||||
|
||||
var ip2 IP6
|
||||
var ip2 IP
|
||||
require.NoError(t, r.Load(&ip2))
|
||||
assert.Equal(t, ip, ip2)
|
||||
}
|
||||
|
||||
// TestGetSetDiscPort tests encoding/decoding and setting/getting of the DiscPort key.
|
||||
func TestGetSetDiscPort(t *testing.T) {
|
||||
port := DiscPort(30309)
|
||||
// TestGetSetUDP tests encoding/decoding and setting/getting of the DiscPort key.
|
||||
func TestGetSetUDP(t *testing.T) {
|
||||
port := UDP(30309)
|
||||
var r Record
|
||||
r.Set(port)
|
||||
|
||||
var port2 DiscPort
|
||||
var port2 UDP
|
||||
require.NoError(t, r.Load(&port2))
|
||||
assert.Equal(t, port, port2)
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ func TestGetSetDiscPort(t *testing.T) {
|
|||
// TestGetSetSecp256k1 tests encoding/decoding and setting/getting of the Secp256k1 key.
|
||||
func TestGetSetSecp256k1(t *testing.T) {
|
||||
var r Record
|
||||
if err := r.Sign(privkey); err != nil {
|
||||
if err := SignV4(&r, privkey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
@ -101,16 +101,16 @@ func TestGetSetSecp256k1(t *testing.T) {
|
|||
|
||||
func TestLoadErrors(t *testing.T) {
|
||||
var r Record
|
||||
ip4 := IP4{127, 0, 0, 1}
|
||||
ip4 := IP{127, 0, 0, 1}
|
||||
r.Set(ip4)
|
||||
|
||||
// Check error for missing keys.
|
||||
var ip6 IP6
|
||||
err := r.Load(&ip6)
|
||||
var udp UDP
|
||||
err := r.Load(&udp)
|
||||
if !IsNotFound(err) {
|
||||
t.Error("IsNotFound should return true for missing key")
|
||||
}
|
||||
assert.Equal(t, &KeyError{Key: ip6.ENRKey(), Err: errNotFound}, err)
|
||||
assert.Equal(t, &KeyError{Key: udp.ENRKey(), Err: errNotFound}, err)
|
||||
|
||||
// Check error for invalid keys.
|
||||
var list []uint
|
||||
|
|
@ -174,7 +174,7 @@ func TestDirty(t *testing.T) {
|
|||
t.Errorf("expected errEncodeUnsigned, got %#v", err)
|
||||
}
|
||||
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
if !r.Signed() {
|
||||
t.Error("Signed return false for signed record")
|
||||
}
|
||||
|
|
@ -194,13 +194,13 @@ func TestDirty(t *testing.T) {
|
|||
func TestGetSetOverwrite(t *testing.T) {
|
||||
var r Record
|
||||
|
||||
ip := IP4{192, 168, 0, 3}
|
||||
ip := IP{192, 168, 0, 3}
|
||||
r.Set(ip)
|
||||
|
||||
ip2 := IP4{192, 168, 0, 4}
|
||||
ip2 := IP{192, 168, 0, 4}
|
||||
r.Set(ip2)
|
||||
|
||||
var ip3 IP4
|
||||
var ip3 IP
|
||||
require.NoError(t, r.Load(&ip3))
|
||||
assert.Equal(t, ip2, ip3)
|
||||
}
|
||||
|
|
@ -208,9 +208,9 @@ func TestGetSetOverwrite(t *testing.T) {
|
|||
// TestSignEncodeAndDecode tests signing, RLP encoding and RLP decoding of a record.
|
||||
func TestSignEncodeAndDecode(t *testing.T) {
|
||||
var r Record
|
||||
r.Set(DiscPort(30303))
|
||||
r.Set(IP4{127, 0, 0, 1})
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
r.Set(UDP(30303))
|
||||
r.Set(IP{127, 0, 0, 1})
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
|
||||
blob, err := rlp.EncodeToBytes(r)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -230,12 +230,12 @@ func TestNodeAddr(t *testing.T) {
|
|||
t.Errorf("wrong address on empty record: got %v, want %v", addr, nil)
|
||||
}
|
||||
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
expected := "caaa1485d83b18b32ed9ad666026151bf0cae8a0a88c857ae2d4c5be2daa6726"
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
expected := "a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7"
|
||||
assert.Equal(t, expected, hex.EncodeToString(r.NodeAddr()))
|
||||
}
|
||||
|
||||
var pyRecord, _ = hex.DecodeString("f896b840954dc36583c1f4b69ab59b1375f362f06ee99f3723cd77e64b6de6d211c27d7870642a79d4516997f94091325d2a7ca6215376971455fb221d34f35b277149a1018664697363763582765f82696490736563703235366b312d6b656363616b83697034847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138")
|
||||
var pyRecord, _ = hex.DecodeString("f884b8407098ad865b00a582051940cb9cf36836572411a47278783077011599ed5cd16b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11df72ecf1145ccb9c01826964827634826970847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31388375647082765f")
|
||||
|
||||
// TestPythonInterop checks that we can decode and verify a record produced by the Python
|
||||
// implementation.
|
||||
|
|
@ -246,10 +246,10 @@ func TestPythonInterop(t *testing.T) {
|
|||
}
|
||||
|
||||
var (
|
||||
wantAddr, _ = hex.DecodeString("caaa1485d83b18b32ed9ad666026151bf0cae8a0a88c857ae2d4c5be2daa6726")
|
||||
wantSeq = uint64(1)
|
||||
wantIP = IP4{127, 0, 0, 1}
|
||||
wantDiscport = DiscPort(30303)
|
||||
wantAddr, _ = hex.DecodeString("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7")
|
||||
wantSeq = uint64(1)
|
||||
wantIP = IP{127, 0, 0, 1}
|
||||
wantUDP = UDP(30303)
|
||||
)
|
||||
if r.Seq() != wantSeq {
|
||||
t.Errorf("wrong seq: got %d, want %d", r.Seq(), wantSeq)
|
||||
|
|
@ -257,7 +257,7 @@ func TestPythonInterop(t *testing.T) {
|
|||
if addr := r.NodeAddr(); !bytes.Equal(addr, wantAddr) {
|
||||
t.Errorf("wrong addr: got %x, want %x", addr, wantAddr)
|
||||
}
|
||||
want := map[Entry]interface{}{new(IP4): &wantIP, new(DiscPort): &wantDiscport}
|
||||
want := map[Entry]interface{}{new(IP): &wantIP, new(UDP): &wantUDP}
|
||||
for k, v := range want {
|
||||
desc := fmt.Sprintf("loading key %q", k.ENRKey())
|
||||
if assert.NoError(t, r.Load(k), desc) {
|
||||
|
|
@ -272,14 +272,14 @@ func TestRecordTooBig(t *testing.T) {
|
|||
key := randomString(10)
|
||||
|
||||
// set a big value for random key, expect error
|
||||
r.Set(WithEntry(key, randomString(300)))
|
||||
if err := r.Sign(privkey); err != errTooBig {
|
||||
r.Set(WithEntry(key, randomString(SizeLimit)))
|
||||
if err := SignV4(&r, privkey); err != errTooBig {
|
||||
t.Fatalf("expected to get errTooBig, got %#v", err)
|
||||
}
|
||||
|
||||
// set an acceptable value for random key, expect no error
|
||||
r.Set(WithEntry(key, randomString(100)))
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
}
|
||||
|
||||
// TestSignEncodeAndDecodeRandom tests encoding/decoding of records containing random key/value pairs.
|
||||
|
|
@ -295,7 +295,7 @@ func TestSignEncodeAndDecodeRandom(t *testing.T) {
|
|||
r.Set(WithEntry(key, &value))
|
||||
}
|
||||
|
||||
require.NoError(t, r.Sign(privkey))
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
_, err := rlp.EncodeToBytes(r)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
|||
|
|
@ -57,59 +57,43 @@ func WithEntry(k string, v interface{}) Entry {
|
|||
return &generic{key: k, value: v}
|
||||
}
|
||||
|
||||
// DiscPort is the "discv5" key, which holds the UDP port for discovery v5.
|
||||
type DiscPort uint16
|
||||
// TCP is the "tcp" key, which holds the TCP port of the node.
|
||||
type TCP uint16
|
||||
|
||||
func (v DiscPort) ENRKey() string { return "discv5" }
|
||||
func (v TCP) ENRKey() string { return "tcp" }
|
||||
|
||||
// UDP is the "udp" key, which holds the UDP port of the node.
|
||||
type UDP uint16
|
||||
|
||||
func (v UDP) ENRKey() string { return "udp" }
|
||||
|
||||
// ID is the "id" key, which holds the name of the identity scheme.
|
||||
type ID string
|
||||
|
||||
const IDv4 = ID("v4") // the default identity scheme
|
||||
|
||||
func (v ID) ENRKey() string { return "id" }
|
||||
|
||||
// IP4 is the "ip4" key, which holds a 4-byte IPv4 address.
|
||||
type IP4 net.IP
|
||||
// IP is the "ip" key, which holds the IP address of the node.
|
||||
type IP net.IP
|
||||
|
||||
func (v IP4) ENRKey() string { return "ip4" }
|
||||
func (v IP) ENRKey() string { return "ip" }
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (v IP4) EncodeRLP(w io.Writer) error {
|
||||
ip4 := net.IP(v).To4()
|
||||
if ip4 == nil {
|
||||
return fmt.Errorf("invalid IPv4 address: %v", v)
|
||||
func (v IP) EncodeRLP(w io.Writer) error {
|
||||
if ip4 := net.IP(v).To4(); ip4 != nil {
|
||||
return rlp.Encode(w, ip4)
|
||||
}
|
||||
return rlp.Encode(w, ip4)
|
||||
return rlp.Encode(w, net.IP(v))
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (v *IP4) DecodeRLP(s *rlp.Stream) error {
|
||||
func (v *IP) DecodeRLP(s *rlp.Stream) error {
|
||||
if err := s.Decode((*net.IP)(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(*v) != 4 {
|
||||
return fmt.Errorf("invalid IPv4 address, want 4 bytes: %v", *v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IP6 is the "ip6" key, which holds a 16-byte IPv6 address.
|
||||
type IP6 net.IP
|
||||
|
||||
func (v IP6) ENRKey() string { return "ip6" }
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (v IP6) EncodeRLP(w io.Writer) error {
|
||||
ip6 := net.IP(v)
|
||||
return rlp.Encode(w, ip6)
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (v *IP6) DecodeRLP(s *rlp.Stream) error {
|
||||
if err := s.Decode((*net.IP)(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(*v) != 16 {
|
||||
return fmt.Errorf("invalid IPv6 address, want 16 bytes: %v", *v)
|
||||
if len(*v) != 4 && len(*v) != 16 {
|
||||
return fmt.Errorf("invalid IP address, want 4 or 16 bytes: %v", *v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
114
p2p/enr/idscheme.go
Normal file
114
p2p/enr/idscheme.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enr
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Registry of known identity schemes.
|
||||
var schemes sync.Map
|
||||
|
||||
// An IdentityScheme is capable of verifying record signatures and
|
||||
// deriving node addresses.
|
||||
type IdentityScheme interface {
|
||||
Verify(r *Record, sig []byte) error
|
||||
NodeAddr(r *Record) []byte
|
||||
}
|
||||
|
||||
// RegisterIdentityScheme adds an identity scheme to the global registry.
|
||||
func RegisterIdentityScheme(name string, scheme IdentityScheme) {
|
||||
if _, loaded := schemes.LoadOrStore(name, scheme); loaded {
|
||||
panic("identity scheme " + name + " already registered")
|
||||
}
|
||||
}
|
||||
|
||||
// FindIdentityScheme resolves name to an identity scheme in the global registry.
|
||||
func FindIdentityScheme(name string) IdentityScheme {
|
||||
s, ok := schemes.Load(name)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return s.(IdentityScheme)
|
||||
}
|
||||
|
||||
// v4ID is the "v4" identity scheme.
|
||||
type v4ID struct{}
|
||||
|
||||
func init() {
|
||||
RegisterIdentityScheme("v4", v4ID{})
|
||||
}
|
||||
|
||||
// SignV4 signs a record using the v4 scheme.
|
||||
func SignV4(r *Record, privkey *ecdsa.PrivateKey) error {
|
||||
// Copy r to avoid modifying it if signing fails.
|
||||
cpy := *r
|
||||
cpy.Set(ID("v4"))
|
||||
cpy.Set(Secp256k1(privkey.PublicKey))
|
||||
|
||||
h := sha3.NewLegacyKeccak256()
|
||||
rlp.Encode(h, cpy.AppendElements(nil))
|
||||
sig, err := crypto.Sign(h.Sum(nil), privkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sig = sig[:len(sig)-1] // remove v
|
||||
if err = cpy.SetSig("v4", sig); err == nil {
|
||||
*r = cpy
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// s256raw is an unparsed secp256k1 public key entry.
|
||||
type s256raw []byte
|
||||
|
||||
func (s256raw) ENRKey() string { return "secp256k1" }
|
||||
|
||||
func (v4ID) Verify(r *Record, sig []byte) error {
|
||||
var entry s256raw
|
||||
if err := r.Load(&entry); err != nil {
|
||||
return err
|
||||
} else if len(entry) != 33 {
|
||||
return fmt.Errorf("invalid public key")
|
||||
}
|
||||
|
||||
h := sha3.NewLegacyKeccak256()
|
||||
rlp.Encode(h, r.AppendElements(nil))
|
||||
if !crypto.VerifySignature(entry, h.Sum(nil), sig) {
|
||||
return errInvalidSig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v4ID) NodeAddr(r *Record) []byte {
|
||||
var pubkey Secp256k1
|
||||
err := r.Load(&pubkey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
buf := make([]byte, 64)
|
||||
math.ReadBits(pubkey.X, buf[:32])
|
||||
math.ReadBits(pubkey.Y, buf[32:])
|
||||
return crypto.Keccak256(buf)
|
||||
}
|
||||
36
p2p/enr/idscheme_test.go
Normal file
36
p2p/enr/idscheme_test.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enr
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Checks that failure to sign leaves the record unmodified.
|
||||
func TestSignError(t *testing.T) {
|
||||
invalidKey := &ecdsa.PrivateKey{D: new(big.Int), PublicKey: *pubkey}
|
||||
|
||||
var r Record
|
||||
if err := SignV4(&r, invalidKey); err == nil {
|
||||
t.Fatal("expected error from SignV4")
|
||||
}
|
||||
if len(r.pairs) > 0 {
|
||||
t.Fatal("expected empty record, have", r.pairs)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue