github.com/supragya/TendermintConnector@v0.0.0-20210619045051-113e32b84fb1/_deprecated_chains/irisnet/conn/secret_connection_test.go (about) 1 package conn 2 3 import ( 4 "bufio" 5 "encoding/hex" 6 "flag" 7 "fmt" 8 "github.com/tendermint/tendermint/crypto" 9 "github.com/tendermint/tendermint/crypto/secp256k1" 10 "io" 11 "log" 12 "net" 13 "os" 14 "path/filepath" 15 "strconv" 16 "strings" 17 "sync" 18 "testing" 19 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 "github.com/tendermint/tendermint/crypto/ed25519" 23 cmn "github.com/tendermint/tendermint/libs/common" 24 ) 25 26 type kvstoreConn struct { 27 *io.PipeReader 28 *io.PipeWriter 29 } 30 31 func (drw kvstoreConn) Close() (err error) { 32 err2 := drw.PipeWriter.CloseWithError(io.EOF) 33 err1 := drw.PipeReader.Close() 34 if err2 != nil { 35 return err 36 } 37 return err1 38 } 39 40 // Each returned ReadWriteCloser is akin to a net.Connection 41 func makeKVStoreConnPair() (fooConn, barConn kvstoreConn) { 42 barReader, fooWriter := io.Pipe() 43 fooReader, barWriter := io.Pipe() 44 return kvstoreConn{fooReader, fooWriter}, kvstoreConn{barReader, barWriter} 45 } 46 47 func makeSecretConnPair(tb testing.TB) (fooSecConn, barSecConn *SecretConnection) { 48 49 var fooConn, barConn = makeKVStoreConnPair() 50 var fooPrvKey = ed25519.GenPrivKey() 51 var fooPubKey = fooPrvKey.PubKey() 52 var barPrvKey = ed25519.GenPrivKey() 53 var barPubKey = barPrvKey.PubKey() 54 55 // Make connections from both sides in parallel. 56 var trs, ok = cmn.Parallel( 57 func(_ int) (val interface{}, err error, abort bool) { 58 fooSecConn, err = MakeSecretConnection(fooConn, fooPrvKey) 59 if err != nil { 60 tb.Errorf("Failed to establish SecretConnection for foo: %v", err) 61 return nil, err, true 62 } 63 remotePubBytes := fooSecConn.RemotePubKey() 64 if !remotePubBytes.Equals(barPubKey) { 65 err = fmt.Errorf("Unexpected fooSecConn.RemotePubKey. Expected %v, got %v", 66 barPubKey, fooSecConn.RemotePubKey()) 67 tb.Error(err) 68 return nil, err, false 69 } 70 return nil, nil, false 71 }, 72 func(_ int) (val interface{}, err error, abort bool) { 73 barSecConn, err = MakeSecretConnection(barConn, barPrvKey) 74 if barSecConn == nil { 75 tb.Errorf("Failed to establish SecretConnection for bar: %v", err) 76 return nil, err, true 77 } 78 remotePubBytes := barSecConn.RemotePubKey() 79 if !remotePubBytes.Equals(fooPubKey) { 80 err = fmt.Errorf("Unexpected barSecConn.RemotePubKey. Expected %v, got %v", 81 fooPubKey, barSecConn.RemotePubKey()) 82 tb.Error(err) 83 return nil, nil, false 84 } 85 return nil, nil, false 86 }, 87 ) 88 89 require.Nil(tb, trs.FirstError()) 90 require.True(tb, ok, "Unexpected task abortion") 91 92 return 93 } 94 95 func TestSecretConnectionHandshake(t *testing.T) { 96 fooSecConn, barSecConn := makeSecretConnPair(t) 97 if err := fooSecConn.Close(); err != nil { 98 t.Error(err) 99 } 100 if err := barSecConn.Close(); err != nil { 101 t.Error(err) 102 } 103 } 104 105 // Test that shareEphPubKey rejects lower order public keys based on an 106 // (incomplete) blacklist. 107 func TestShareLowOrderPubkey(t *testing.T) { 108 var fooConn, barConn = makeKVStoreConnPair() 109 defer fooConn.Close() 110 defer barConn.Close() 111 locEphPub, _ := genEphKeys() 112 113 // all blacklisted low order points: 114 for _, remLowOrderPubKey := range blacklist { 115 _, _ = cmn.Parallel( 116 func(_ int) (val interface{}, err error, abort bool) { 117 _, err = shareEphPubKey(fooConn, locEphPub) 118 119 require.Error(t, err) 120 require.Equal(t, err, ErrSmallOrderRemotePubKey) 121 122 return nil, nil, false 123 }, 124 func(_ int) (val interface{}, err error, abort bool) { 125 readRemKey, err := shareEphPubKey(barConn, &remLowOrderPubKey) 126 127 require.NoError(t, err) 128 require.Equal(t, locEphPub, readRemKey) 129 130 return nil, nil, false 131 }) 132 } 133 } 134 135 // Test that additionally that the Diffie-Hellman shared secret is non-zero. 136 // The shared secret would be zero for lower order pub-keys (but tested against the blacklist only). 137 func TestComputeDHFailsOnLowOrder(t *testing.T) { 138 _, locPrivKey := genEphKeys() 139 for _, remLowOrderPubKey := range blacklist { 140 shared, err := computeDHSecret(&remLowOrderPubKey, locPrivKey) 141 assert.Error(t, err) 142 143 assert.Equal(t, err, ErrSharedSecretIsZero) 144 assert.Empty(t, shared) 145 } 146 } 147 148 func TestConcurrentWrite(t *testing.T) { 149 fooSecConn, barSecConn := makeSecretConnPair(t) 150 fooWriteText := cmn.RandStr(dataMaxSize) 151 152 // write from two routines. 153 // should be safe from race according to net.Conn: 154 // https://golang.org/pkg/net/#Conn 155 n := 100 156 wg := new(sync.WaitGroup) 157 wg.Add(3) 158 go writeLots(t, wg, fooSecConn, fooWriteText, n) 159 go writeLots(t, wg, fooSecConn, fooWriteText, n) 160 161 // Consume reads from bar's reader 162 readLots(t, wg, barSecConn, n*2) 163 wg.Wait() 164 165 if err := fooSecConn.Close(); err != nil { 166 t.Error(err) 167 } 168 } 169 170 func TestConcurrentRead(t *testing.T) { 171 fooSecConn, barSecConn := makeSecretConnPair(t) 172 fooWriteText := cmn.RandStr(dataMaxSize) 173 n := 100 174 175 // read from two routines. 176 // should be safe from race according to net.Conn: 177 // https://golang.org/pkg/net/#Conn 178 wg := new(sync.WaitGroup) 179 wg.Add(3) 180 go readLots(t, wg, fooSecConn, n/2) 181 go readLots(t, wg, fooSecConn, n/2) 182 183 // write to bar 184 writeLots(t, wg, barSecConn, fooWriteText, n) 185 wg.Wait() 186 187 if err := fooSecConn.Close(); err != nil { 188 t.Error(err) 189 } 190 } 191 192 func writeLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, txt string, n int) { 193 defer wg.Done() 194 for i := 0; i < n; i++ { 195 _, err := conn.Write([]byte(txt)) 196 if err != nil { 197 t.Errorf("Failed to write to fooSecConn: %v", err) 198 return 199 } 200 } 201 } 202 203 func readLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, n int) { 204 readBuffer := make([]byte, dataMaxSize) 205 for i := 0; i < n; i++ { 206 _, err := conn.Read(readBuffer) 207 assert.NoError(t, err) 208 } 209 wg.Done() 210 } 211 212 func TestSecretConnectionReadWrite(t *testing.T) { 213 fooConn, barConn := makeKVStoreConnPair() 214 fooWrites, barWrites := []string{}, []string{} 215 fooReads, barReads := []string{}, []string{} 216 217 // Pre-generate the things to write (for foo & bar) 218 for i := 0; i < 100; i++ { 219 fooWrites = append(fooWrites, cmn.RandStr((cmn.RandInt()%(dataMaxSize*5))+1)) 220 barWrites = append(barWrites, cmn.RandStr((cmn.RandInt()%(dataMaxSize*5))+1)) 221 } 222 223 // A helper that will run with (fooConn, fooWrites, fooReads) and vice versa 224 genNodeRunner := func(id string, nodeConn kvstoreConn, nodeWrites []string, nodeReads *[]string) cmn.Task { 225 return func(_ int) (interface{}, error, bool) { 226 // Initiate cryptographic private key and secret connection trhough nodeConn. 227 nodePrvKey := ed25519.GenPrivKey() 228 nodeSecretConn, err := MakeSecretConnection(nodeConn, nodePrvKey) 229 if err != nil { 230 t.Errorf("Failed to establish SecretConnection for node: %v", err) 231 return nil, err, true 232 } 233 // In parallel, handle some reads and writes. 234 var trs, ok = cmn.Parallel( 235 func(_ int) (interface{}, error, bool) { 236 // Node writes: 237 for _, nodeWrite := range nodeWrites { 238 n, err := nodeSecretConn.Write([]byte(nodeWrite)) 239 if err != nil { 240 t.Errorf("Failed to write to nodeSecretConn: %v", err) 241 return nil, err, true 242 } 243 if n != len(nodeWrite) { 244 err = fmt.Errorf("Failed to write all bytes. Expected %v, wrote %v", len(nodeWrite), n) 245 t.Error(err) 246 return nil, err, true 247 } 248 } 249 if err := nodeConn.PipeWriter.Close(); err != nil { 250 t.Error(err) 251 return nil, err, true 252 } 253 return nil, nil, false 254 }, 255 func(_ int) (interface{}, error, bool) { 256 // Node reads: 257 readBuffer := make([]byte, dataMaxSize) 258 for { 259 n, err := nodeSecretConn.Read(readBuffer) 260 if err == io.EOF { 261 if err := nodeConn.PipeReader.Close(); err != nil { 262 t.Error(err) 263 return nil, err, true 264 } 265 return nil, nil, false 266 } else if err != nil { 267 t.Errorf("Failed to read from nodeSecretConn: %v", err) 268 return nil, err, true 269 } 270 *nodeReads = append(*nodeReads, string(readBuffer[:n])) 271 } 272 }, 273 ) 274 assert.True(t, ok, "Unexpected task abortion") 275 276 // If error: 277 if trs.FirstError() != nil { 278 return nil, trs.FirstError(), true 279 } 280 281 // Otherwise: 282 return nil, nil, false 283 } 284 } 285 286 // Run foo & bar in parallel 287 var trs, ok = cmn.Parallel( 288 genNodeRunner("foo", fooConn, fooWrites, &fooReads), 289 genNodeRunner("bar", barConn, barWrites, &barReads), 290 ) 291 require.Nil(t, trs.FirstError()) 292 require.True(t, ok, "unexpected task abortion") 293 294 // A helper to ensure that the writes and reads match. 295 // Additionally, small writes (<= dataMaxSize) must be atomically read. 296 compareWritesReads := func(writes []string, reads []string) { 297 for { 298 // Pop next write & corresponding reads 299 var read, write string = "", writes[0] 300 var readCount = 0 301 for _, readChunk := range reads { 302 read += readChunk 303 readCount++ 304 if len(write) <= len(read) { 305 break 306 } 307 if len(write) <= dataMaxSize { 308 break // atomicity of small writes 309 } 310 } 311 // Compare 312 if write != read { 313 t.Errorf("Expected to read %X, got %X", write, read) 314 } 315 // Iterate 316 writes = writes[1:] 317 reads = reads[readCount:] 318 if len(writes) == 0 { 319 break 320 } 321 } 322 } 323 324 compareWritesReads(fooWrites, barReads) 325 compareWritesReads(barWrites, fooReads) 326 327 } 328 329 // Run go test -update from within this module 330 // to update the golden test vector file 331 var update = flag.Bool("update", false, "update .golden files") 332 333 func TestDeriveSecretsAndChallengeGolden(t *testing.T) { 334 goldenFilepath := filepath.Join("testdata", t.Name()+".golden") 335 if *update { 336 t.Logf("Updating golden test vector file %s", goldenFilepath) 337 data := createGoldenTestVectors(t) 338 cmn.WriteFile(goldenFilepath, []byte(data), 0644) 339 } 340 f, err := os.Open(goldenFilepath) 341 if err != nil { 342 log.Fatal(err) 343 } 344 defer f.Close() 345 scanner := bufio.NewScanner(f) 346 for scanner.Scan() { 347 line := scanner.Text() 348 params := strings.Split(line, ",") 349 randSecretVector, err := hex.DecodeString(params[0]) 350 require.Nil(t, err) 351 randSecret := new([32]byte) 352 copy((*randSecret)[:], randSecretVector) 353 locIsLeast, err := strconv.ParseBool(params[1]) 354 require.Nil(t, err) 355 expectedRecvSecret, err := hex.DecodeString(params[2]) 356 require.Nil(t, err) 357 expectedSendSecret, err := hex.DecodeString(params[3]) 358 require.Nil(t, err) 359 expectedChallenge, err := hex.DecodeString(params[4]) 360 require.Nil(t, err) 361 362 recvSecret, sendSecret, challenge := deriveSecretAndChallenge(randSecret, locIsLeast) 363 require.Equal(t, expectedRecvSecret, (*recvSecret)[:], "Recv Secrets aren't equal") 364 require.Equal(t, expectedSendSecret, (*sendSecret)[:], "Send Secrets aren't equal") 365 require.Equal(t, expectedChallenge, (*challenge)[:], "challenges aren't equal") 366 } 367 } 368 369 type privKeyWithNilPubKey struct { 370 orig crypto.PrivKey 371 } 372 373 func (pk privKeyWithNilPubKey) Bytes() []byte { return pk.orig.Bytes() } 374 func (pk privKeyWithNilPubKey) Sign(msg []byte) ([]byte, error) { return pk.orig.Sign(msg) } 375 func (pk privKeyWithNilPubKey) PubKey() crypto.PubKey { return nil } 376 func (pk privKeyWithNilPubKey) Equals(pk2 crypto.PrivKey) bool { return pk.orig.Equals(pk2) } 377 378 func TestNilPubkey(t *testing.T) { 379 var fooConn, barConn = makeKVStoreConnPair() 380 var fooPrvKey = ed25519.GenPrivKey() 381 var barPrvKey = privKeyWithNilPubKey{ed25519.GenPrivKey()} 382 383 go func() { 384 _, err := MakeSecretConnection(barConn, barPrvKey) 385 assert.NoError(t, err) 386 }() 387 388 assert.NotPanics(t, func() { 389 _, err := MakeSecretConnection(fooConn, fooPrvKey) 390 if assert.Error(t, err) { 391 assert.Equal(t, "expected ed25519 pubkey, got <nil>", err.Error()) 392 } 393 }) 394 } 395 396 func TestNonEd25519Pubkey(t *testing.T) { 397 var fooConn, barConn = makeKVStoreConnPair() 398 var fooPrvKey = ed25519.GenPrivKey() 399 var barPrvKey = secp256k1.GenPrivKey() 400 401 go func() { 402 _, err := MakeSecretConnection(barConn, barPrvKey) 403 assert.NoError(t, err) 404 }() 405 406 assert.NotPanics(t, func() { 407 _, err := MakeSecretConnection(fooConn, fooPrvKey) 408 if assert.Error(t, err) { 409 assert.Equal(t, "expected ed25519 pubkey, got secp256k1.PubKeySecp256k1", err.Error()) 410 } 411 }) 412 } 413 414 // Creates the data for a test vector file. 415 // The file format is: 416 // Hex(diffie_hellman_secret), loc_is_least, Hex(recvSecret), Hex(sendSecret), Hex(challenge) 417 func createGoldenTestVectors(t *testing.T) string { 418 data := "" 419 for i := 0; i < 32; i++ { 420 randSecretVector := cmn.RandBytes(32) 421 randSecret := new([32]byte) 422 copy((*randSecret)[:], randSecretVector) 423 data += hex.EncodeToString((*randSecret)[:]) + "," 424 locIsLeast := cmn.RandBool() 425 data += strconv.FormatBool(locIsLeast) + "," 426 recvSecret, sendSecret, challenge := deriveSecretAndChallenge(randSecret, locIsLeast) 427 data += hex.EncodeToString((*recvSecret)[:]) + "," 428 data += hex.EncodeToString((*sendSecret)[:]) + "," 429 data += hex.EncodeToString((*challenge)[:]) + "\n" 430 } 431 return data 432 } 433 434 func BenchmarkWriteSecretConnection(b *testing.B) { 435 b.StopTimer() 436 b.ReportAllocs() 437 fooSecConn, barSecConn := makeSecretConnPair(b) 438 randomMsgSizes := []int{ 439 dataMaxSize / 10, 440 dataMaxSize / 3, 441 dataMaxSize / 2, 442 dataMaxSize, 443 dataMaxSize * 3 / 2, 444 dataMaxSize * 2, 445 dataMaxSize * 7 / 2, 446 } 447 fooWriteBytes := make([][]byte, 0, len(randomMsgSizes)) 448 for _, size := range randomMsgSizes { 449 fooWriteBytes = append(fooWriteBytes, cmn.RandBytes(size)) 450 } 451 // Consume reads from bar's reader 452 go func() { 453 readBuffer := make([]byte, dataMaxSize) 454 for { 455 _, err := barSecConn.Read(readBuffer) 456 if err == io.EOF { 457 return 458 } else if err != nil { 459 b.Errorf("Failed to read from barSecConn: %v", err) 460 return 461 } 462 } 463 }() 464 465 b.StartTimer() 466 for i := 0; i < b.N; i++ { 467 idx := cmn.RandIntn(len(fooWriteBytes)) 468 _, err := fooSecConn.Write(fooWriteBytes[idx]) 469 if err != nil { 470 b.Errorf("Failed to write to fooSecConn: %v", err) 471 return 472 } 473 } 474 b.StopTimer() 475 476 if err := fooSecConn.Close(); err != nil { 477 b.Error(err) 478 } 479 //barSecConn.Close() race condition 480 } 481 482 func BenchmarkReadSecretConnection(b *testing.B) { 483 b.StopTimer() 484 b.ReportAllocs() 485 fooSecConn, barSecConn := makeSecretConnPair(b) 486 randomMsgSizes := []int{ 487 dataMaxSize / 10, 488 dataMaxSize / 3, 489 dataMaxSize / 2, 490 dataMaxSize, 491 dataMaxSize * 3 / 2, 492 dataMaxSize * 2, 493 dataMaxSize * 7 / 2, 494 } 495 fooWriteBytes := make([][]byte, 0, len(randomMsgSizes)) 496 for _, size := range randomMsgSizes { 497 fooWriteBytes = append(fooWriteBytes, cmn.RandBytes(size)) 498 } 499 go func() { 500 for i := 0; i < b.N; i++ { 501 idx := cmn.RandIntn(len(fooWriteBytes)) 502 _, err := fooSecConn.Write(fooWriteBytes[idx]) 503 if err != nil { 504 b.Errorf("Failed to write to fooSecConn: %v, %v,%v", err, i, b.N) 505 return 506 } 507 } 508 }() 509 510 b.StartTimer() 511 for i := 0; i < b.N; i++ { 512 readBuffer := make([]byte, dataMaxSize) 513 _, err := barSecConn.Read(readBuffer) 514 515 if err == io.EOF { 516 return 517 } else if err != nil { 518 b.Fatalf("Failed to read from barSecConn: %v", err) 519 } 520 } 521 b.StopTimer() 522 }