github.com/ethereum/go-ethereum@v1.16.1/p2p/discover/v5wire/encoding_test.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package v5wire
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"encoding/hex"
    23  	"errors"
    24  	"flag"
    25  	"fmt"
    26  	"net"
    27  	"os"
    28  	"path/filepath"
    29  	"strings"
    30  	"testing"
    31  
    32  	"github.com/davecgh/go-spew/spew"
    33  
    34  	"github.com/ethereum/go-ethereum/common/hexutil"
    35  	"github.com/ethereum/go-ethereum/common/mclock"
    36  	"github.com/ethereum/go-ethereum/crypto"
    37  	"github.com/ethereum/go-ethereum/p2p/enode"
    38  )
    39  
    40  // To regenerate discv5 test vectors, run
    41  //
    42  //	go test -run TestVectors -write-test-vectors
    43  var writeTestVectorsFlag = flag.Bool("write-test-vectors", false, "Overwrite discv5 test vectors in testdata/")
    44  
    45  var (
    46  	testKeyA, _   = crypto.HexToECDSA("eef77acb6c6a6eebc5b363a475ac583ec7eccdb42b6481424c60f59aa326547f")
    47  	testKeyB, _   = crypto.HexToECDSA("66fb62bfbd66b9177a138c1e5cddbe4f7c30c343e94e68df8769459cb1cde628")
    48  	testEphKey, _ = crypto.HexToECDSA("0288ef00023598499cb6c940146d050d2b1fb914198c327f76aad590bead68b6")
    49  	testIDnonce   = [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
    50  )
    51  
    52  // This test checks that the minPacketSize and randomPacketMsgSize constants are well-defined.
    53  func TestMinSizes(t *testing.T) {
    54  	var (
    55  		gcmTagSize = 16
    56  		emptyMsg   = sizeofMessageAuthData + gcmTagSize
    57  	)
    58  	t.Log("static header size", sizeofStaticPacketData)
    59  	t.Log("whoareyou size", sizeofStaticPacketData+sizeofWhoareyouAuthData)
    60  	t.Log("empty msg size", sizeofStaticPacketData+emptyMsg)
    61  	if want := emptyMsg; minMessageSize != want {
    62  		t.Fatalf("wrong minMessageSize %d, want %d", minMessageSize, want)
    63  	}
    64  	if sizeofMessageAuthData+randomPacketMsgSize < minMessageSize {
    65  		t.Fatalf("randomPacketMsgSize %d too small", randomPacketMsgSize)
    66  	}
    67  }
    68  
    69  // This test checks the basic handshake flow where A talks to B and A has no secrets.
    70  func TestHandshake(t *testing.T) {
    71  	t.Parallel()
    72  	net := newHandshakeTest()
    73  	defer net.close()
    74  
    75  	// A -> B   RANDOM PACKET
    76  	packet, _ := net.nodeA.encode(t, net.nodeB, &Findnode{})
    77  	resp := net.nodeB.expectDecode(t, UnknownPacket, packet)
    78  
    79  	// A <- B   WHOAREYOU
    80  	challenge := &Whoareyou{
    81  		Nonce:     resp.(*Unknown).Nonce,
    82  		IDNonce:   testIDnonce,
    83  		RecordSeq: 0,
    84  	}
    85  	whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge)
    86  	net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou)
    87  
    88  	// A -> B   FINDNODE (handshake packet)
    89  	findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{})
    90  	net.nodeB.expectDecode(t, FindnodeMsg, findnode)
    91  	if len(net.nodeB.c.sc.handshakes) > 0 {
    92  		t.Fatalf("node B didn't remove handshake from challenge map")
    93  	}
    94  
    95  	// A <- B   NODES
    96  	nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{RespCount: 1})
    97  	net.nodeA.expectDecode(t, NodesMsg, nodes)
    98  }
    99  
   100  // This test checks that handshake attempts are removed within the timeout.
   101  func TestHandshake_timeout(t *testing.T) {
   102  	t.Parallel()
   103  	net := newHandshakeTest()
   104  	defer net.close()
   105  
   106  	// A -> B   RANDOM PACKET
   107  	packet, _ := net.nodeA.encode(t, net.nodeB, &Findnode{})
   108  	resp := net.nodeB.expectDecode(t, UnknownPacket, packet)
   109  
   110  	// A <- B   WHOAREYOU
   111  	challenge := &Whoareyou{
   112  		Nonce:     resp.(*Unknown).Nonce,
   113  		IDNonce:   testIDnonce,
   114  		RecordSeq: 0,
   115  	}
   116  	whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge)
   117  	net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou)
   118  
   119  	// A -> B   FINDNODE (handshake packet) after timeout
   120  	net.clock.Run(handshakeTimeout + 1)
   121  	findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{})
   122  	net.nodeB.expectDecodeErr(t, errUnexpectedHandshake, findnode)
   123  }
   124  
   125  // This test checks handshake behavior when no record is sent in the auth response.
   126  func TestHandshake_norecord(t *testing.T) {
   127  	t.Parallel()
   128  	net := newHandshakeTest()
   129  	defer net.close()
   130  
   131  	// A -> B   RANDOM PACKET
   132  	packet, _ := net.nodeA.encode(t, net.nodeB, &Findnode{})
   133  	resp := net.nodeB.expectDecode(t, UnknownPacket, packet)
   134  
   135  	// A <- B   WHOAREYOU
   136  	nodeA := net.nodeA.n()
   137  	if nodeA.Seq() == 0 {
   138  		t.Fatal("need non-zero sequence number")
   139  	}
   140  	challenge := &Whoareyou{
   141  		Nonce:     resp.(*Unknown).Nonce,
   142  		IDNonce:   testIDnonce,
   143  		RecordSeq: nodeA.Seq(),
   144  		Node:      nodeA,
   145  	}
   146  	whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge)
   147  	net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou)
   148  
   149  	// A -> B   FINDNODE
   150  	findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{})
   151  	net.nodeB.expectDecode(t, FindnodeMsg, findnode)
   152  
   153  	// A <- B   NODES
   154  	nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{RespCount: 1})
   155  	net.nodeA.expectDecode(t, NodesMsg, nodes)
   156  }
   157  
   158  // In this test, A tries to send FINDNODE with existing secrets but B doesn't know
   159  // anything about A.
   160  func TestHandshake_rekey(t *testing.T) {
   161  	t.Parallel()
   162  	net := newHandshakeTest()
   163  	defer net.close()
   164  
   165  	session := &session{
   166  		readKey:  []byte("BBBBBBBBBBBBBBBB"),
   167  		writeKey: []byte("AAAAAAAAAAAAAAAA"),
   168  	}
   169  	net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), session, net.nodeB.n())
   170  
   171  	// A -> B   FINDNODE (encrypted with zero keys)
   172  	findnode, authTag := net.nodeA.encode(t, net.nodeB, &Findnode{})
   173  	net.nodeB.expectDecode(t, UnknownPacket, findnode)
   174  
   175  	// A <- B   WHOAREYOU
   176  	challenge := &Whoareyou{Nonce: authTag, IDNonce: testIDnonce}
   177  	whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge)
   178  	net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou)
   179  
   180  	// Check that new keys haven't been stored yet.
   181  	sa := net.nodeA.c.sc.session(net.nodeB.id(), net.nodeB.addr())
   182  	if !bytes.Equal(sa.writeKey, session.writeKey) || !bytes.Equal(sa.readKey, session.readKey) {
   183  		t.Fatal("node A stored keys too early")
   184  	}
   185  	if s := net.nodeB.c.sc.session(net.nodeA.id(), net.nodeA.addr()); s != nil {
   186  		t.Fatal("node B stored keys too early")
   187  	}
   188  
   189  	// A -> B   FINDNODE encrypted with new keys
   190  	findnode, _ = net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{})
   191  	net.nodeB.expectDecode(t, FindnodeMsg, findnode)
   192  
   193  	// A <- B   NODES
   194  	nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{RespCount: 1})
   195  	net.nodeA.expectDecode(t, NodesMsg, nodes)
   196  }
   197  
   198  // In this test A and B have different keys before the handshake.
   199  func TestHandshake_rekey2(t *testing.T) {
   200  	t.Parallel()
   201  	net := newHandshakeTest()
   202  	defer net.close()
   203  
   204  	initKeysA := &session{
   205  		readKey:  []byte("BBBBBBBBBBBBBBBB"),
   206  		writeKey: []byte("AAAAAAAAAAAAAAAA"),
   207  	}
   208  	initKeysB := &session{
   209  		readKey:  []byte("CCCCCCCCCCCCCCCC"),
   210  		writeKey: []byte("DDDDDDDDDDDDDDDD"),
   211  	}
   212  	net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), initKeysA, net.nodeB.n())
   213  	net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), initKeysB, net.nodeA.n())
   214  
   215  	// A -> B   FINDNODE encrypted with initKeysA
   216  	findnode, authTag := net.nodeA.encode(t, net.nodeB, &Findnode{Distances: []uint{3}})
   217  	net.nodeB.expectDecode(t, UnknownPacket, findnode)
   218  
   219  	// A <- B   WHOAREYOU
   220  	challenge := &Whoareyou{Nonce: authTag, IDNonce: testIDnonce}
   221  	whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge)
   222  	net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou)
   223  
   224  	// A -> B   FINDNODE (handshake packet)
   225  	findnode, _ = net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{})
   226  	net.nodeB.expectDecode(t, FindnodeMsg, findnode)
   227  
   228  	// A <- B   NODES
   229  	nodes, _ := net.nodeB.encode(t, net.nodeA, &Nodes{RespCount: 1})
   230  	net.nodeA.expectDecode(t, NodesMsg, nodes)
   231  }
   232  
   233  func TestHandshake_BadHandshakeAttack(t *testing.T) {
   234  	t.Parallel()
   235  	net := newHandshakeTest()
   236  	defer net.close()
   237  
   238  	// A -> B   RANDOM PACKET
   239  	packet, _ := net.nodeA.encode(t, net.nodeB, &Findnode{})
   240  	resp := net.nodeB.expectDecode(t, UnknownPacket, packet)
   241  
   242  	// A <- B   WHOAREYOU
   243  	challenge := &Whoareyou{
   244  		Nonce:     resp.(*Unknown).Nonce,
   245  		IDNonce:   testIDnonce,
   246  		RecordSeq: 0,
   247  	}
   248  	whoareyou, _ := net.nodeB.encode(t, net.nodeA, challenge)
   249  	net.nodeA.expectDecode(t, WhoareyouPacket, whoareyou)
   250  
   251  	// A -> B   FINDNODE
   252  	incorrectChallenge := &Whoareyou{
   253  		IDNonce:   [16]byte{5, 6, 7, 8, 9, 6, 11, 12},
   254  		RecordSeq: challenge.RecordSeq,
   255  		Node:      challenge.Node,
   256  		sent:      challenge.sent,
   257  	}
   258  	incorrectFindNode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, incorrectChallenge, &Findnode{})
   259  	incorrectFindNode2 := make([]byte, len(incorrectFindNode))
   260  	copy(incorrectFindNode2, incorrectFindNode)
   261  
   262  	net.nodeB.expectDecodeErr(t, errInvalidNonceSig, incorrectFindNode)
   263  
   264  	// Reject new findnode as previous handshake is now deleted.
   265  	net.nodeB.expectDecodeErr(t, errUnexpectedHandshake, incorrectFindNode2)
   266  
   267  	// The findnode packet is again rejected even with a valid challenge this time.
   268  	findnode, _ := net.nodeA.encodeWithChallenge(t, net.nodeB, challenge, &Findnode{})
   269  	net.nodeB.expectDecodeErr(t, errUnexpectedHandshake, findnode)
   270  }
   271  
   272  // This test checks some malformed packets.
   273  func TestDecodeErrorsV5(t *testing.T) {
   274  	t.Parallel()
   275  	net := newHandshakeTest()
   276  	defer net.close()
   277  
   278  	b := make([]byte, 0)
   279  	net.nodeA.expectDecodeErr(t, errTooShort, b)
   280  
   281  	b = make([]byte, 62)
   282  	net.nodeA.expectDecodeErr(t, errTooShort, b)
   283  
   284  	b = make([]byte, 63)
   285  	net.nodeA.expectDecodeErr(t, errInvalidHeader, b)
   286  
   287  	t.Run("invalid-handshake-datasize", func(t *testing.T) {
   288  		requiredNumber := 108
   289  
   290  		testDataFile := filepath.Join("testdata", "v5.1-ping-handshake"+".txt")
   291  		enc := hexFile(testDataFile)
   292  		//delete some byte from handshake to make it invalid
   293  		enc = enc[:len(enc)-requiredNumber]
   294  		net.nodeB.expectDecodeErr(t, errMsgTooShort, enc)
   295  	})
   296  
   297  	t.Run("invalid-auth-datasize", func(t *testing.T) {
   298  		testPacket := []byte{}
   299  		testDataFiles := []string{"v5.1-whoareyou", "v5.1-ping-handshake"}
   300  		for counter, name := range testDataFiles {
   301  			file := filepath.Join("testdata", name+".txt")
   302  			enc := hexFile(file)
   303  			if counter == 0 {
   304  				//make whoareyou header
   305  				testPacket = enc[:sizeofStaticPacketData-1]
   306  				testPacket = append(testPacket, 255)
   307  			}
   308  			if counter == 1 {
   309  				//append invalid auth size
   310  				testPacket = append(testPacket, enc[sizeofStaticPacketData:]...)
   311  			}
   312  		}
   313  
   314  		wantErr := "invalid auth size"
   315  		if _, err := net.nodeB.decode(testPacket); strings.HasSuffix(err.Error(), wantErr) {
   316  			t.Fatal(fmt.Errorf("(%s) got err %q, want %q", net.nodeB.ln.ID().TerminalString(), err, wantErr))
   317  		}
   318  	})
   319  }
   320  
   321  // This test checks that all test vectors can be decoded.
   322  func TestTestVectorsV5(t *testing.T) {
   323  	var (
   324  		idA     = enode.PubkeyToIDV4(&testKeyA.PublicKey)
   325  		idB     = enode.PubkeyToIDV4(&testKeyB.PublicKey)
   326  		addr    = "127.0.0.1"
   327  		session = &session{
   328  			writeKey: hexutil.MustDecode("0x00000000000000000000000000000000"),
   329  			readKey:  hexutil.MustDecode("0x01010101010101010101010101010101"),
   330  		}
   331  		challenge0A, challenge1A, challenge0B Whoareyou
   332  	)
   333  
   334  	// Create challenge packets.
   335  	c := Whoareyou{
   336  		Nonce:   Nonce{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
   337  		IDNonce: testIDnonce,
   338  	}
   339  	challenge0A, challenge1A, challenge0B = c, c, c
   340  	challenge1A.RecordSeq = 1
   341  	net := newHandshakeTest()
   342  	challenge0A.Node = net.nodeA.n()
   343  	challenge0B.Node = net.nodeB.n()
   344  	challenge1A.Node = net.nodeA.n()
   345  	net.close()
   346  
   347  	type testVectorTest struct {
   348  		name      string               // test vector name
   349  		packet    Packet               // the packet to be encoded
   350  		challenge *Whoareyou           // handshake challenge passed to encoder
   351  		prep      func(*handshakeTest) // called before encode/decode
   352  	}
   353  	tests := []testVectorTest{
   354  		{
   355  			name:   "v5.1-whoareyou",
   356  			packet: &challenge0B,
   357  		},
   358  		{
   359  			name: "v5.1-ping-message",
   360  			packet: &Ping{
   361  				ReqID:  []byte{0, 0, 0, 1},
   362  				ENRSeq: 2,
   363  			},
   364  			prep: func(net *handshakeTest) {
   365  				net.nodeA.c.sc.storeNewSession(idB, addr, session, net.nodeB.n())
   366  				net.nodeB.c.sc.storeNewSession(idA, addr, session.keysFlipped(), net.nodeA.n())
   367  			},
   368  		},
   369  		{
   370  			name: "v5.1-ping-handshake-enr",
   371  			packet: &Ping{
   372  				ReqID:  []byte{0, 0, 0, 1},
   373  				ENRSeq: 1,
   374  			},
   375  			challenge: &challenge0A,
   376  			prep: func(net *handshakeTest) {
   377  				// Update challenge.Header.AuthData.
   378  				net.nodeA.c.Encode(idB, "", &challenge0A, nil)
   379  				net.nodeB.c.sc.storeSentHandshake(idA, addr, &challenge0A)
   380  			},
   381  		},
   382  		{
   383  			name: "v5.1-ping-handshake",
   384  			packet: &Ping{
   385  				ReqID:  []byte{0, 0, 0, 1},
   386  				ENRSeq: 1,
   387  			},
   388  			challenge: &challenge1A,
   389  			prep: func(net *handshakeTest) {
   390  				// Update challenge data.
   391  				net.nodeA.c.Encode(idB, "", &challenge1A, nil)
   392  				net.nodeB.c.sc.storeSentHandshake(idA, addr, &challenge1A)
   393  			},
   394  		},
   395  	}
   396  
   397  	for _, test := range tests {
   398  		t.Run(test.name, func(t *testing.T) {
   399  			net := newHandshakeTest()
   400  			defer net.close()
   401  
   402  			// Override all random inputs.
   403  			net.nodeA.c.sc.nonceGen = func(counter uint32) (Nonce, error) {
   404  				return Nonce{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, nil
   405  			}
   406  			net.nodeA.c.sc.maskingIVGen = func(buf []byte) error {
   407  				return nil // all zero
   408  			}
   409  			net.nodeA.c.sc.ephemeralKeyGen = func() (*ecdsa.PrivateKey, error) {
   410  				return testEphKey, nil
   411  			}
   412  
   413  			// Prime the codec for encoding/decoding.
   414  			if test.prep != nil {
   415  				test.prep(net)
   416  			}
   417  
   418  			file := filepath.Join("testdata", test.name+".txt")
   419  			if *writeTestVectorsFlag {
   420  				// Encode the packet.
   421  				d, nonce := net.nodeA.encodeWithChallenge(t, net.nodeB, test.challenge, test.packet)
   422  				comment := testVectorComment(net, test.packet, test.challenge, nonce)
   423  				writeTestVector(file, comment, d)
   424  			}
   425  			enc := hexFile(file)
   426  			net.nodeB.expectDecode(t, test.packet.Kind(), enc)
   427  		})
   428  	}
   429  }
   430  
   431  // testVectorComment creates the commentary for discv5 test vector files.
   432  func testVectorComment(net *handshakeTest, p Packet, challenge *Whoareyou, nonce Nonce) string {
   433  	o := new(strings.Builder)
   434  	printWhoareyou := func(p *Whoareyou) {
   435  		fmt.Fprintf(o, "whoareyou.challenge-data = %#x\n", p.ChallengeData)
   436  		fmt.Fprintf(o, "whoareyou.request-nonce = %#x\n", p.Nonce[:])
   437  		fmt.Fprintf(o, "whoareyou.id-nonce = %#x\n", p.IDNonce[:])
   438  		fmt.Fprintf(o, "whoareyou.enr-seq = %d\n", p.RecordSeq)
   439  	}
   440  
   441  	fmt.Fprintf(o, "src-node-id = %#x\n", net.nodeA.id().Bytes())
   442  	fmt.Fprintf(o, "dest-node-id = %#x\n", net.nodeB.id().Bytes())
   443  	switch p := p.(type) {
   444  	case *Whoareyou:
   445  		// WHOAREYOU packet.
   446  		printWhoareyou(p)
   447  	case *Ping:
   448  		fmt.Fprintf(o, "nonce = %#x\n", nonce[:])
   449  		fmt.Fprintf(o, "read-key = %#x\n", net.nodeA.c.sc.session(net.nodeB.id(), net.nodeB.addr()).writeKey)
   450  		fmt.Fprintf(o, "ping.req-id = %#x\n", p.ReqID)
   451  		fmt.Fprintf(o, "ping.enr-seq = %d\n", p.ENRSeq)
   452  		if challenge != nil {
   453  			// Handshake message packet.
   454  			fmt.Fprint(o, "\nhandshake inputs:\n\n")
   455  			printWhoareyou(challenge)
   456  			fmt.Fprintf(o, "ephemeral-key = %#x\n", testEphKey.D.Bytes())
   457  			fmt.Fprintf(o, "ephemeral-pubkey = %#x\n", crypto.CompressPubkey(&testEphKey.PublicKey))
   458  		}
   459  	default:
   460  		panic(fmt.Errorf("unhandled packet type %T", p))
   461  	}
   462  	return o.String()
   463  }
   464  
   465  // This benchmark checks performance of handshake packet decoding.
   466  func BenchmarkV5_DecodeHandshakePingSecp256k1(b *testing.B) {
   467  	net := newHandshakeTest()
   468  	defer net.close()
   469  
   470  	var (
   471  		idA       = net.nodeA.id()
   472  		challenge = &Whoareyou{Node: net.nodeB.n()}
   473  		message   = &Ping{ReqID: []byte("reqid")}
   474  	)
   475  	enc, _, err := net.nodeA.c.Encode(net.nodeB.id(), "", message, challenge)
   476  	if err != nil {
   477  		b.Fatal("can't encode handshake packet")
   478  	}
   479  	challenge.Node = nil // force ENR signature verification in decoder
   480  	b.ResetTimer()
   481  
   482  	input := make([]byte, len(enc))
   483  	for i := 0; i < b.N; i++ {
   484  		copy(input, enc)
   485  		net.nodeB.c.sc.storeSentHandshake(idA, "", challenge)
   486  		_, _, _, err := net.nodeB.c.Decode(input, "")
   487  		if err != nil {
   488  			b.Fatal(err)
   489  		}
   490  	}
   491  }
   492  
   493  // This benchmark checks how long it takes to decode an encrypted ping packet.
   494  func BenchmarkV5_DecodePing(b *testing.B) {
   495  	net := newHandshakeTest()
   496  	defer net.close()
   497  
   498  	session := &session{
   499  		readKey:  []byte{233, 203, 93, 195, 86, 47, 177, 186, 227, 43, 2, 141, 244, 230, 120, 17},
   500  		writeKey: []byte{79, 145, 252, 171, 167, 216, 252, 161, 208, 190, 176, 106, 214, 39, 178, 134},
   501  	}
   502  	net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), session, net.nodeB.n())
   503  	net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), session.keysFlipped(), net.nodeA.n())
   504  	addrB := net.nodeA.addr()
   505  	ping := &Ping{ReqID: []byte("reqid"), ENRSeq: 5}
   506  	enc, _, err := net.nodeA.c.Encode(net.nodeB.id(), addrB, ping, nil)
   507  	if err != nil {
   508  		b.Fatalf("can't encode: %v", err)
   509  	}
   510  	b.ResetTimer()
   511  
   512  	input := make([]byte, len(enc))
   513  	for i := 0; i < b.N; i++ {
   514  		copy(input, enc)
   515  		_, _, packet, _ := net.nodeB.c.Decode(input, addrB)
   516  		if _, ok := packet.(*Ping); !ok {
   517  			b.Fatalf("wrong packet type %T", packet)
   518  		}
   519  	}
   520  }
   521  
   522  var pp = spew.NewDefaultConfig()
   523  
   524  type handshakeTest struct {
   525  	nodeA, nodeB handshakeTestNode
   526  	clock        mclock.Simulated
   527  }
   528  
   529  type handshakeTestNode struct {
   530  	ln *enode.LocalNode
   531  	c  *Codec
   532  }
   533  
   534  func newHandshakeTest() *handshakeTest {
   535  	t := new(handshakeTest)
   536  	t.nodeA.init(testKeyA, net.IP{127, 0, 0, 1}, &t.clock, DefaultProtocolID)
   537  	t.nodeB.init(testKeyB, net.IP{127, 0, 0, 1}, &t.clock, DefaultProtocolID)
   538  	return t
   539  }
   540  
   541  func (t *handshakeTest) close() {
   542  	t.nodeA.ln.Database().Close()
   543  	t.nodeB.ln.Database().Close()
   544  }
   545  
   546  func (n *handshakeTestNode) init(key *ecdsa.PrivateKey, ip net.IP, clock mclock.Clock, protocolID [6]byte) {
   547  	db, _ := enode.OpenDB("")
   548  	n.ln = enode.NewLocalNode(db, key)
   549  	n.ln.SetStaticIP(ip)
   550  	n.c = NewCodec(n.ln, key, clock, nil)
   551  }
   552  
   553  func (n *handshakeTestNode) encode(t testing.TB, to handshakeTestNode, p Packet) ([]byte, Nonce) {
   554  	t.Helper()
   555  	return n.encodeWithChallenge(t, to, nil, p)
   556  }
   557  
   558  func (n *handshakeTestNode) encodeWithChallenge(t testing.TB, to handshakeTestNode, c *Whoareyou, p Packet) ([]byte, Nonce) {
   559  	t.Helper()
   560  
   561  	// Copy challenge and add destination node. This avoids sharing 'c' among the two codecs.
   562  	var challenge *Whoareyou
   563  	if c != nil {
   564  		challengeCopy := *c
   565  		challenge = &challengeCopy
   566  		challenge.Node = to.n()
   567  	}
   568  	// Encode to destination.
   569  	enc, nonce, err := n.c.Encode(to.id(), to.addr(), p, challenge)
   570  	if err != nil {
   571  		t.Fatal(fmt.Errorf("(%s) %v", n.ln.ID().TerminalString(), err))
   572  	}
   573  	t.Logf("(%s) -> (%s)   %s\n%s", n.ln.ID().TerminalString(), to.id().TerminalString(), p.Name(), hex.Dump(enc))
   574  	return enc, nonce
   575  }
   576  
   577  func (n *handshakeTestNode) expectDecode(t *testing.T, ptype byte, p []byte) Packet {
   578  	t.Helper()
   579  
   580  	dec, err := n.decode(p)
   581  	if err != nil {
   582  		t.Fatal(fmt.Errorf("(%s) %v", n.ln.ID().TerminalString(), err))
   583  	}
   584  	t.Logf("(%s) %#v", n.ln.ID().TerminalString(), pp.NewFormatter(dec))
   585  	if dec.Kind() != ptype {
   586  		t.Fatalf("expected packet type %d, got %d", ptype, dec.Kind())
   587  	}
   588  	return dec
   589  }
   590  
   591  func (n *handshakeTestNode) expectDecodeErr(t *testing.T, wantErr error, p []byte) {
   592  	t.Helper()
   593  	if _, err := n.decode(p); !errors.Is(err, wantErr) {
   594  		t.Fatal(fmt.Errorf("(%s) got err %q, want %q", n.ln.ID().TerminalString(), err, wantErr))
   595  	}
   596  }
   597  
   598  func (n *handshakeTestNode) decode(input []byte) (Packet, error) {
   599  	_, _, p, err := n.c.Decode(input, "127.0.0.1")
   600  	return p, err
   601  }
   602  
   603  func (n *handshakeTestNode) n() *enode.Node {
   604  	return n.ln.Node()
   605  }
   606  
   607  func (n *handshakeTestNode) addr() string {
   608  	return n.ln.Node().IPAddr().String()
   609  }
   610  
   611  func (n *handshakeTestNode) id() enode.ID {
   612  	return n.ln.ID()
   613  }
   614  
   615  // hexFile reads the given file and decodes the hex data contained in it.
   616  // Whitespace and any lines beginning with the # character are ignored.
   617  func hexFile(file string) []byte {
   618  	fileContent, err := os.ReadFile(file)
   619  	if err != nil {
   620  		panic(err)
   621  	}
   622  
   623  	// Gather hex data, ignore comments.
   624  	var text []byte
   625  	for _, line := range bytes.Split(fileContent, []byte("\n")) {
   626  		line = bytes.TrimSpace(line)
   627  		if len(line) > 0 && line[0] == '#' {
   628  			continue
   629  		}
   630  		text = append(text, line...)
   631  	}
   632  
   633  	// Parse the hex.
   634  	if bytes.HasPrefix(text, []byte("0x")) {
   635  		text = text[2:]
   636  	}
   637  	data := make([]byte, hex.DecodedLen(len(text)))
   638  	if _, err := hex.Decode(data, text); err != nil {
   639  		panic("invalid hex in " + file)
   640  	}
   641  	return data
   642  }
   643  
   644  // writeTestVector writes a test vector file with the given commentary and binary data.
   645  func writeTestVector(file, comment string, data []byte) {
   646  	fd, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   647  	if err != nil {
   648  		panic(err)
   649  	}
   650  	defer fd.Close()
   651  
   652  	if len(comment) > 0 {
   653  		for _, line := range strings.Split(strings.TrimSpace(comment), "\n") {
   654  			fmt.Fprintf(fd, "# %s\n", line)
   655  		}
   656  		fmt.Fprintln(fd)
   657  	}
   658  	for len(data) > 0 {
   659  		var chunk []byte
   660  		if len(data) < 32 {
   661  			chunk = data
   662  		} else {
   663  			chunk = data[:32]
   664  		}
   665  		data = data[len(chunk):]
   666  		fmt.Fprintf(fd, "%x\n", chunk)
   667  	}
   668  }