github.com/core-coin/go-core/v2@v2.1.9/p2p/rlpx/rlpx_test.go (about) 1 // Copyright 2020 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rlpx 18 19 import ( 20 "bytes" 21 crand "crypto/rand" 22 "encoding/hex" 23 "fmt" 24 "io" 25 "net" 26 "reflect" 27 "strings" 28 "testing" 29 30 "github.com/davecgh/go-spew/spew" 31 "github.com/stretchr/testify/assert" 32 33 "github.com/core-coin/go-core/v2/crypto" 34 "github.com/core-coin/go-core/v2/rlp" 35 ) 36 37 type message struct { 38 code uint64 39 data []byte 40 err error 41 } 42 43 func TestHandshake(t *testing.T) { 44 p1, p2 := createPeers(t) 45 p1.Close() 46 p2.Close() 47 } 48 49 // This test checks that messages can be sent and received through WriteMsg/ReadMsg. 50 func TestReadWriteMsg(t *testing.T) { 51 peer1, peer2 := createPeers(t) 52 defer peer1.Close() 53 defer peer2.Close() 54 55 testCode := uint64(23) 56 testData := []byte("test") 57 checkMsgReadWrite(t, peer1, peer2, testCode, testData) 58 59 t.Log("enabling snappy") 60 peer1.SetSnappy(true) 61 peer2.SetSnappy(true) 62 checkMsgReadWrite(t, peer1, peer2, testCode, testData) 63 } 64 65 func checkMsgReadWrite(t *testing.T, p1, p2 *Conn, msgCode uint64, msgData []byte) { 66 // Set up the reader. 67 ch := make(chan message, 1) 68 go func() { 69 var msg message 70 msg.code, msg.data, _, msg.err = p1.Read() 71 ch <- msg 72 }() 73 74 // Write the message. 75 _, err := p2.Write(msgCode, msgData) 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 // Check it was received correctly. 81 msg := <-ch 82 assert.Equal(t, msgCode, msg.code, "wrong message code returned from ReadMsg") 83 assert.Equal(t, msgData, msg.data, "wrong message data returned from ReadMsg") 84 } 85 86 func createPeers(t *testing.T) (peer1, peer2 *Conn) { 87 conn1, conn2 := net.Pipe() 88 key1, key2 := newkey(), newkey() 89 peer1 = NewConn(conn1, key2.PublicKey()) // dialer 90 peer2 = NewConn(conn2, nil) // listener 91 doHandshake(t, peer1, peer2, key1, key2) 92 return peer1, peer2 93 } 94 95 func doHandshake(t *testing.T, peer1, peer2 *Conn, key1, key2 *crypto.PrivateKey) { 96 keyChan := make(chan *crypto.PublicKey, 1) 97 go func() { 98 pubKey, err := peer2.Handshake(key2) 99 if err != nil { 100 t.Errorf("peer2 could not do handshake: %v", err) 101 } 102 keyChan <- pubKey 103 }() 104 105 pubKey2, err := peer1.Handshake(key1) 106 if err != nil { 107 t.Errorf("peer1 could not do handshake: %v", err) 108 } 109 pubKey1 := <-keyChan 110 111 // Confirm the handshake was successful. 112 if !reflect.DeepEqual(pubKey1, key1.PublicKey()) || !reflect.DeepEqual(pubKey2, key2.PublicKey()) { 113 t.Fatal("unsuccessful handshake") 114 } 115 } 116 117 // This test checks the frame data of written messages. 118 func TestFrameReadWrite(t *testing.T) { 119 conn := NewConn(nil, nil) 120 hash := fakeHash([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) 121 conn.InitWithSecrets(Secrets{ 122 AES: crypto.SHA3(), 123 MAC: crypto.SHA3(), 124 IngressMAC: hash, 125 EgressMAC: hash, 126 }) 127 h := conn.handshake 128 129 golden := unhex(` 130 ee4bdf598434add487b2845159762028 131 01010101010101010101010101010101 132 2ad9100d3c2ac6652fa799328a635bc2 133 01010101010101010101010101010101 134 `) 135 msgCode := uint64(8) 136 msg := []uint{1, 2, 3, 4} 137 msgEnc, _ := rlp.EncodeToBytes(msg) 138 139 // Check writeFrame. The frame that's written should be equal to the test vector. 140 buf := new(bytes.Buffer) 141 if err := h.writeFrame(buf, msgCode, msgEnc); err != nil { 142 t.Fatalf("WriteMsg error: %v", err) 143 } 144 if !bytes.Equal(buf.Bytes(), golden) { 145 t.Fatalf("output mismatch:\n got: %x\n want: %x", buf.Bytes(), golden) 146 } 147 148 // Check readFrame on the test vector. 149 content, err := h.readFrame(bytes.NewReader(golden)) 150 if err != nil { 151 t.Fatalf("ReadMsg error: %v", err) 152 } 153 wantContent := unhex("08C401020304") 154 if !bytes.Equal(content, wantContent) { 155 t.Errorf("frame content mismatch:\ngot %x\nwant %x", content, wantContent) 156 } 157 } 158 159 type fakeHash []byte 160 161 func (fakeHash) Write(p []byte) (int, error) { return len(p), nil } 162 func (fakeHash) Reset() {} 163 func (fakeHash) BlockSize() int { return 0 } 164 func (h fakeHash) Size() int { return len(h) } 165 func (h fakeHash) Sum(b []byte) []byte { return append(b, h...) } 166 167 type handshakeAuthTest struct { 168 input string 169 isPlain bool 170 wantVersion uint 171 wantRest []rlp.RawValue 172 } 173 174 var cip8HandshakeAuthTests = []handshakeAuthTest{ 175 // (Auth₂) CIP-8 encoding 176 { 177 input: `024ccad299392df90734104b9ba7991f77db1052fa8b3545931b23e604d5b0da47c28e3fee12d3996cc86db94c742a04ef8b0829aea28823494f004f76a498f3ad51bd33b2b39186265910b63dd553f423a0b1efcb83395c4e7c7f0bfad98ba436e7da129a7edd05bb164066dd6d26502cf13d0ebb6c6219960648d4ecc9d2d6750f527a7e6248a1f33e92f99013a02f12cd87092af3875c1a9d9180d120db62686a5e6ca4d59438ba08ab8ba78de725f980156fb787c2aff1071eb85b2d5f17acde2cb145aef636ca4aabd55addc287baf8f380f18a2a14a17fcdabc8999182431097dd036b7ae2a5fda25003affcd7c5794cdf332c3b42390df741823e4e009afa57e04d7054313be6bec8ee2e5dea88fa5e9e0c272598e82d268cd90e4525faa817f4104d56b4f8b0b0f77d22bbb23ad30cd5992980ab1df5c2a0db3fb43f0a6019eeba9f8d4c37539968066b449817451a7d0f7da7a30e6dbad7c9ef4eb3faa02a8ad3c7da0c0e9f50fe997e640a492ee2b4a9999e1df7b6b36d67697ff19de5d63b074acf51bc624c9470237381beafd0ae51d0fc2bed5d09f86f2eaf65a4fbb8b9f7182f5c29f6db7ba2220b5d21bd21ae00cfd4e2296bfed3fc138c276139c0977b0054e8b01dfd8984dce11dd344e55f67b9f5d328eeea0e5832ee3c66491b3b52525c989165c3f2c3bd4308bb7cf86895b3b7f420135886c078fb013455b2007e5c1e4e523d14d0c880eb3f34da3818924a16feb3aaced85cea20900d2a89d107c1572cc5f8098838190a1681f11f8acc88e06a0aae35205756d4b08db01de3f6228cf8a3aae8e4ff21`, 178 wantVersion: 4, 179 wantRest: []rlp.RawValue{}, 180 }, 181 // (Auth₃) RLPx v4 CIP-8 encoding with version 56, additional list elements 182 { 183 input: `025138cbe53a20acc28881ca94d94f875d7457c69f02d3ae004e60b4e08fca7d4e5f5ba4b2d16f563a430c4360bf92681163ad692281eaba19c780fc6fd2ca3a826799eed17bb560433a3b78d2cb74f829d214a3f1cce087f16d4dc9ed5522db59bfbc80d640b490e5b537ad04660f80cf0cc89bf3a08a56df2ec532cfc832175320619038e14b622f4ca0359dee9fecaa5e1597914655feb2630721c766e146842fb5932d2e290ba19dad372751299526babfc4b33f6a024517833a8dd1d6206da34a8bdc0dd9d590e627957d6687c4749c068fa59f9fee57bef1a1ab8f8b8ee2bce13e04971a880de143049c798ea433db213f0ddcfd2548c7677e790dc47649eec27aa5bf64d2459e616cba3485e557e6ddef6ceb686edcb3d05de624625777ee516486c9ee68aaaca100e3d45a9a18dffe4d6a24b782fb50483af9424785e74da583470c76fe89822a5f15a2dc94fc108d30e7e426ade84746044a9a9000554c2b436c75afea49a3a04bff423b5cbe6ec15ce38c0a5328cc93b8cdebf4dc8130fb778a4d42de1c9c7b3d37ad6433d4faf36b48e110087295d4ece5143fecc0784affb4f1530a75af4ae757f3658f88316781ca40b081271e08a95f9a879980edb6e5ed29e0b4829f38a27c75408df324041612645e8509cf611013ae5f3b7ca807dbfe6666617962861feceb316bb169393bcf6e49ba9e2691d680218a87f9b87b8dad962f236fb2fd7085aab681c59621c0b3422fdbe4bbd0f840bfa6a87f12e9e140d84615e35673aa9a9e00c0a4d69e1eef4d1de4092572966d5f6d245be6080d984d8e9b44d55391a84280a04bc5ba`, 184 wantVersion: 56, 185 wantRest: []rlp.RawValue{{0x01}, {0x02}, {0xC2, 0x04, 0x05}}, 186 }, 187 } 188 189 type handshakeAckTest struct { 190 input string 191 wantVersion uint 192 wantRest []rlp.RawValue 193 } 194 195 var cip8HandshakeRespTests = []handshakeAckTest{ 196 // (Ack₂) CIP-8 encoding 197 { 198 input: `01a4dc5bb0d46b3ef7e35124fe30913961ff4b962ac49328a1f17c734ba40a8c0d2b4007a81ab3257da60bb7c5ef1c2b883dd58ea1b51f6e6a318037dac7d00c6b57a658e804a033e3a61921160c00ec11fb2719fd11b2ea8882eb4d1fe15b6ef5c4747e9af92c27d18f99bddc824d576d8650257a421b5ec059aeb7feec0174209c8f057d95a6991ae36ca72547de2e089eee5319b342cc4f8b12cb7628806236af54295d7d21c9e00aa81f063f5dc852b7721f942bf3b12313716297824cb1e635f9934793d92520970fc2071349e3163b2364ff7fd9d2079c2a5bb39d5b312546c2de60815e1152760122e2ae43cff2b0774a9879471e83872f1d86fa5fbab0d27e59722717ddee8881834671d1ae6a5eb09019df64bb5297bb4dc476d860a9f4ce813382c47d434380616de332e25d03a7f993d0576c76b63b95f0157fb2e915c7c0ce189ab854b026afbcd80699acf862dd589c628705c7c310fd1c551d22aac8ffbe3c332c3c46f6df4f887f552c10116dabeb83cca00ced3bb517cf9c5c425e5c9aa77b6f29876632398dff93123485fe3778ee63a85bc41e72eab9e4980ee25b6a69`, 199 wantVersion: 4, 200 wantRest: []rlp.RawValue{}, 201 }, 202 // (Ack₃) CIP-8 encoding with version 57, additional list elements 203 { 204 input: `018298af236a6774f8f6497412c6529aa4d867612c22bd96942bc502319a74d0ea6d07a5802b5997a5abd917153c0e860ceb703a869c9716f85100db6100d8ae296f52abb054e8f9caa5c060f656576e5d9effe4ab40fb49e156c4765ace4d047cfc8b4432fe281d5508f536943fb769b6fa060b6faaba7ac13d8b84317b8dabd4d53883393a539314df3671ae076cd5da05728313cfbd8956c6c973a46fed1b2fd0520b3658b06637d061d84377ff2cebd387e5ef965c76c4810283e496a53c954d2af9b955a73f31fd4bc63ab373d22bf5f0b14955312d7ffb7ad335c90bd8ac563e8d7576ee5ceefbff16f2e4a9c5d4de3a2192985b7aa2522a5bc92b221cb2f036ba427039e83cbba7acfcc60596bd06afc875cf355fe2f8a2d8a35539684341635a348bec7a4445a38adc9999ebaf195fb325dc131665f77fe1b7d9e1f037e3e82cebf90aaf92f7042c6802dded28657a4e5281adedf98d62a14de5b88c518d5337bd1d483d459e8d49c1ff3ec211cd2f09717b0acbadebe7a0266e3c466c356ee0`, 205 wantVersion: 57, 206 wantRest: []rlp.RawValue{{0x06}, {0xC2, 0x07, 0x08}, {0x81, 0xFA}}, 207 }, 208 } 209 210 func TestHandshakeForwardCompatibility(t *testing.T) { 211 var ( 212 keyA, _ = crypto.UnmarshalPrivateKeyHex("f2cdd78003bf733b7badb5001ea1fe9248346f5b7943a87b3c40528a8f10941eb29313c1abf703aaab4a46a94a0b2302ec89bf1998332d7c60") 213 keyB, _ = crypto.UnmarshalPrivateKeyHex("856a9af6b0b651dd2f43b5e12193652ec1701c4da6f1c0d2a366ac4b9dabc9433ef09e41ca129552bd2c029086d9b03604de872a3b3432041f") 214 ephA, _ = crypto.UnmarshalPrivateKeyHex("45516f2d6e60098e547e9b50d386e75f530805fb468c132bead2ce7b205208d895cb086fff390eff73c349a7e5caf1c8c8d8278ae31a6b175a") 215 ephB, _ = crypto.UnmarshalPrivateKeyHex("96b3c4485ef83aae585776685bed5d7d6373befb7b661f43592ac703b94ed543526a23d4de35af35c30690998993f140ed1fd9389bc99506b9") 216 nonceA = unhex("7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6") 217 nonceB = unhex("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd") 218 _, _, _, _ = keyA, keyB, ephA, ephB 219 authSignature = unhex("c180a432dad22041c7c648776681b4b20cb040c58ffe8d58a344cb86e704eb26e68300b8c4f3f0836c7025eeb81f76b4952fa0a0f70effdb008ec4fd100e0e7b7bc30b5a5ac8aea7eb0d2092bbdd5a3763e78e7c1c6d13cc0856a6171640af4e7d1f013a08c4d474ecf8223d1c871a412700f6e2ad06289ee32fb5b673d332629f5aa30e553783812eaf76dc929e9785765e001e93e1fe5ac2c1ebbaca3a15fc814a4321959afd6d97f200") 220 _ = authSignature 221 ) 222 makeAuth := func(test handshakeAuthTest) *authMsgV4 { 223 msg := &authMsgV4{Version: test.wantVersion, Rest: test.wantRest} 224 copy(msg.Signature[:], authSignature) 225 copy(msg.InitiatorPubkey[:], keyA.PublicKey()[:]) 226 copy(msg.Nonce[:], nonceA) 227 return msg 228 } 229 makeAck := func(test handshakeAckTest) *authRespV4 { 230 msg := &authRespV4{Version: test.wantVersion, Rest: test.wantRest} 231 copy(msg.RandomPubkey[:], ephB.PublicKey()[:]) 232 copy(msg.Nonce[:], nonceB) 233 return msg 234 } 235 236 // check auth msg parsing 237 for _, test := range cip8HandshakeAuthTests { 238 r := bytes.NewReader(unhex(test.input)) 239 msg := new(authMsgV4) 240 ciphertext, err := readHandshakeMsg(msg, encAuthMsgLen, keyB, r) 241 if err != nil { 242 t.Errorf("error for input %x:\n %v", unhex(test.input), err) 243 continue 244 } 245 if !bytes.Equal(ciphertext, unhex(test.input)) { 246 t.Errorf("wrong ciphertext for input %x:\n %x", unhex(test.input), ciphertext) 247 } 248 want := makeAuth(test) 249 if !reflect.DeepEqual(msg, want) { 250 t.Errorf("wrong msg for input %x:\ngot %s\nwant %s", unhex(test.input), spew.Sdump(msg), spew.Sdump(want)) 251 } 252 } 253 254 // check auth resp parsing 255 for _, test := range cip8HandshakeRespTests { 256 input := unhex(test.input) 257 r := bytes.NewReader(input) 258 msg := new(authRespV4) 259 ciphertext, err := readHandshakeMsg(msg, encAuthRespLen, keyA, r) 260 if err != nil { 261 t.Errorf("error for input %x:\n %v", input, err) 262 continue 263 } 264 if !bytes.Equal(ciphertext, input) { 265 t.Errorf("wrong ciphertext for input %x:\n %x", input, err) 266 } 267 want := makeAck(test) 268 if !reflect.DeepEqual(msg, want) { 269 t.Errorf("wrong msg for input %x:\ngot %s\nwant %s", input, spew.Sdump(msg), spew.Sdump(want)) 270 } 271 } 272 273 // check derivation for (Auth₂, Ack₂) on recipient side 274 var ( 275 hs = &encHandshake{ 276 initiator: false, 277 respNonce: nonceB, 278 randomPrivKey: ephB, 279 } 280 authCiphertext = unhex(cip8HandshakeAuthTests[1].input) 281 authRespCiphertext = unhex(cip8HandshakeRespTests[1].input) 282 authMsg = makeAuth(cip8HandshakeAuthTests[1]) 283 wantAES = unhex("02a8f660fd5c452851980694e076d64b2168605201638e95c47d3c696000b1d7") 284 wantMAC = unhex("96a446be84d374707892788f7de25dcf9b175f4bc9dd7a6c119af31cc2b30f72") 285 wantFooIngressHash = unhex("457aba35ae6b27710c07589c86fc50f88338139a8b67429c608cfc6d7fab7b87") 286 ) 287 if err := hs.handleAuthMsg(authMsg, keyB); err != nil { 288 t.Fatalf("handleAuthMsg: %v", err) 289 } 290 derived, err := hs.secrets(authCiphertext, authRespCiphertext) 291 if err != nil { 292 t.Fatalf("secrets: %v", err) 293 } 294 if !bytes.Equal(derived.AES, wantAES) { 295 t.Errorf("aes-secret mismatch:\ngot %x\nwant %x", derived.AES, wantAES) 296 } 297 if !bytes.Equal(derived.MAC, wantMAC) { 298 t.Errorf("mac-secret mismatch:\ngot %x\nwant %x", derived.MAC, wantMAC) 299 } 300 io.WriteString(derived.IngressMAC, "foo") 301 fooIngressHash := derived.IngressMAC.Sum(nil) 302 if !bytes.Equal(fooIngressHash, wantFooIngressHash) { 303 t.Errorf("ingress-mac('foo') mismatch:\ngot %x\nwant %x", fooIngressHash, wantFooIngressHash) 304 } 305 } 306 307 func unhex(str string) []byte { 308 r := strings.NewReplacer("\t", "", " ", "", "\n", "") 309 b, err := hex.DecodeString(r.Replace(str)) 310 if err != nil { 311 panic(fmt.Sprintf("invalid hex string: %q", str)) 312 } 313 return b 314 } 315 316 func newkey() *crypto.PrivateKey { 317 key, err := crypto.GenerateKey(crand.Reader) 318 if err != nil { 319 panic("couldn't generate key: " + err.Error()) 320 } 321 return key 322 }