// 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 . package enode import ( "crypto/ecdsa" "fmt" "io" "github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/p2p/discover/v4wire" "github.com/ledgerwatch/erigon/p2p/enr" "github.com/ledgerwatch/erigon/rlp" "golang.org/x/crypto/sha3" ) // List of known secure identity schemes. var ValidSchemes = enr.SchemeMap{ "v4": V4ID{}, } var ValidSchemesForTesting = enr.SchemeMap{ "v4": V4ID{}, "null": NullID{}, } // v4ID is the "v4" identity scheme. type V4ID struct{} // SignV4 signs a record using the v4 scheme. func SignV4(r *enr.Record, privkey *ecdsa.PrivateKey) error { // Copy r to avoid modifying it if signing fails. cpy := *r cpy.Set(enr.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(V4ID{}, sig); err == nil { *r = cpy } return err } func (V4ID) Verify(r *enr.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 enr.ErrInvalidSig } return nil } func (V4ID) NodeAddr(r *enr.Record) []byte { var pubkey Secp256k1 err := r.Load(&pubkey) if err != nil { return nil } id := PubkeyToIDV4((*ecdsa.PublicKey)(&pubkey)) return id[:] } // PubkeyToIDV4 derives the v4 node address from the given public key. func PubkeyToIDV4(key *ecdsa.PublicKey) ID { return PubkeyEncoded(v4wire.EncodePubkey(key)).ID() } type PubkeyEncoded v4wire.Pubkey // ID returns the node ID corresponding to the public key. func (e PubkeyEncoded) ID() ID { return ID(crypto.Keccak256Hash(e[:])) } // Secp256k1 is the "secp256k1" key, which holds a public key. type Secp256k1 ecdsa.PublicKey func (v Secp256k1) ENRKey() string { return "secp256k1" } // EncodeRLP implements rlp.Encoder. func (v Secp256k1) EncodeRLP(w io.Writer) error { return rlp.Encode(w, crypto.CompressPubkey((*ecdsa.PublicKey)(&v))) } // DecodeRLP implements rlp.Decoder. func (v *Secp256k1) DecodeRLP(s *rlp.Stream) error { buf, err := s.Bytes() if err != nil { return err } pk, err := crypto.DecompressPubkey(buf) if err != nil { return err } *v = (Secp256k1)(*pk) return nil } // s256raw is an unparsed secp256k1 public key entry. type s256raw []byte func (s256raw) ENRKey() string { return "secp256k1" } // v4CompatID is a weaker and insecure version of the "v4" scheme which only checks for the // presence of a secp256k1 public key, but doesn't verify the signature. type v4CompatID struct { V4ID } func (v4CompatID) Verify(r *enr.Record, sig []byte) error { var pubkey Secp256k1 return r.Load(&pubkey) } func signV4Compat(r *enr.Record, pubkey *ecdsa.PublicKey) { r.Set((*Secp256k1)(pubkey)) if err := r.SetSig(v4CompatID{}, []byte{}); err != nil { panic(err) } } // NullID is the "null" ENR identity scheme. This scheme stores the node // ID in the record without any signature. type NullID struct{} func (NullID) Verify(r *enr.Record, sig []byte) error { return nil } func (NullID) NodeAddr(r *enr.Record) []byte { var id ID r.Load(enr.WithEntry("nulladdr", &id)) return id[:] } func SignNull(r *enr.Record, id ID) *Node { r.Set(enr.ID("null")) r.Set(enr.WithEntry("nulladdr", id)) if err := r.SetSig(NullID{}, []byte{}); err != nil { panic(err) } return &Node{r: *r, id: id} }