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  }