You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
11 KiB

  1. package conn
  2. import (
  3. "bufio"
  4. "encoding/hex"
  5. "flag"
  6. "fmt"
  7. "io"
  8. "log"
  9. "net"
  10. "os"
  11. "path/filepath"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "testing"
  16. "github.com/stretchr/testify/assert"
  17. "github.com/stretchr/testify/require"
  18. "github.com/tendermint/tendermint/crypto/ed25519"
  19. cmn "github.com/tendermint/tendermint/libs/common"
  20. )
  21. type kvstoreConn struct {
  22. *io.PipeReader
  23. *io.PipeWriter
  24. }
  25. func (drw kvstoreConn) Close() (err error) {
  26. err2 := drw.PipeWriter.CloseWithError(io.EOF)
  27. err1 := drw.PipeReader.Close()
  28. if err2 != nil {
  29. return err
  30. }
  31. return err1
  32. }
  33. // Each returned ReadWriteCloser is akin to a net.Connection
  34. func makeKVStoreConnPair() (fooConn, barConn kvstoreConn) {
  35. barReader, fooWriter := io.Pipe()
  36. fooReader, barWriter := io.Pipe()
  37. return kvstoreConn{fooReader, fooWriter}, kvstoreConn{barReader, barWriter}
  38. }
  39. func makeSecretConnPair(tb testing.TB) (fooSecConn, barSecConn *SecretConnection) {
  40. var fooConn, barConn = makeKVStoreConnPair()
  41. var fooPrvKey = ed25519.GenPrivKey()
  42. var fooPubKey = fooPrvKey.PubKey()
  43. var barPrvKey = ed25519.GenPrivKey()
  44. var barPubKey = barPrvKey.PubKey()
  45. // Make connections from both sides in parallel.
  46. var trs, ok = cmn.Parallel(
  47. func(_ int) (val interface{}, err error, abort bool) {
  48. fooSecConn, err = MakeSecretConnection(fooConn, fooPrvKey)
  49. if err != nil {
  50. tb.Errorf("Failed to establish SecretConnection for foo: %v", err)
  51. return nil, err, true
  52. }
  53. remotePubBytes := fooSecConn.RemotePubKey()
  54. if !remotePubBytes.Equals(barPubKey) {
  55. err = fmt.Errorf("Unexpected fooSecConn.RemotePubKey. Expected %v, got %v",
  56. barPubKey, fooSecConn.RemotePubKey())
  57. tb.Error(err)
  58. return nil, err, false
  59. }
  60. return nil, nil, false
  61. },
  62. func(_ int) (val interface{}, err error, abort bool) {
  63. barSecConn, err = MakeSecretConnection(barConn, barPrvKey)
  64. if barSecConn == nil {
  65. tb.Errorf("Failed to establish SecretConnection for bar: %v", err)
  66. return nil, err, true
  67. }
  68. remotePubBytes := barSecConn.RemotePubKey()
  69. if !remotePubBytes.Equals(fooPubKey) {
  70. err = fmt.Errorf("Unexpected barSecConn.RemotePubKey. Expected %v, got %v",
  71. fooPubKey, barSecConn.RemotePubKey())
  72. tb.Error(err)
  73. return nil, nil, false
  74. }
  75. return nil, nil, false
  76. },
  77. )
  78. require.Nil(tb, trs.FirstError())
  79. require.True(tb, ok, "Unexpected task abortion")
  80. return
  81. }
  82. func TestSecretConnectionHandshake(t *testing.T) {
  83. fooSecConn, barSecConn := makeSecretConnPair(t)
  84. if err := fooSecConn.Close(); err != nil {
  85. t.Error(err)
  86. }
  87. if err := barSecConn.Close(); err != nil {
  88. t.Error(err)
  89. }
  90. }
  91. func TestShareLowOrderPubkey(t *testing.T) {
  92. var fooConn, barConn = makeKVStoreConnPair()
  93. locEphPub, _ := genEphKeys()
  94. // all blacklisted low order points:
  95. for _, remLowOrderPubKey := range blacklist {
  96. _, _ = cmn.Parallel(
  97. func(_ int) (val interface{}, err error, abort bool) {
  98. _, err = shareEphPubKey(fooConn, locEphPub)
  99. require.Error(t, err)
  100. require.Equal(t, err, ErrSmallOrderRemotePubKey)
  101. return nil, nil, false
  102. },
  103. func(_ int) (val interface{}, err error, abort bool) {
  104. readRemKey, err := shareEphPubKey(barConn, &remLowOrderPubKey)
  105. require.NoError(t, err)
  106. require.Equal(t, locEphPub, readRemKey)
  107. return nil, nil, false
  108. })
  109. }
  110. }
  111. func TestConcurrentWrite(t *testing.T) {
  112. fooSecConn, barSecConn := makeSecretConnPair(t)
  113. fooWriteText := cmn.RandStr(dataMaxSize)
  114. // write from two routines.
  115. // should be safe from race according to net.Conn:
  116. // https://golang.org/pkg/net/#Conn
  117. n := 100
  118. wg := new(sync.WaitGroup)
  119. wg.Add(3)
  120. go writeLots(t, wg, fooSecConn, fooWriteText, n)
  121. go writeLots(t, wg, fooSecConn, fooWriteText, n)
  122. // Consume reads from bar's reader
  123. readLots(t, wg, barSecConn, n*2)
  124. wg.Wait()
  125. if err := fooSecConn.Close(); err != nil {
  126. t.Error(err)
  127. }
  128. }
  129. func TestConcurrentRead(t *testing.T) {
  130. fooSecConn, barSecConn := makeSecretConnPair(t)
  131. fooWriteText := cmn.RandStr(dataMaxSize)
  132. n := 100
  133. // read from two routines.
  134. // should be safe from race according to net.Conn:
  135. // https://golang.org/pkg/net/#Conn
  136. wg := new(sync.WaitGroup)
  137. wg.Add(3)
  138. go readLots(t, wg, fooSecConn, n/2)
  139. go readLots(t, wg, fooSecConn, n/2)
  140. // write to bar
  141. writeLots(t, wg, barSecConn, fooWriteText, n)
  142. wg.Wait()
  143. if err := fooSecConn.Close(); err != nil {
  144. t.Error(err)
  145. }
  146. }
  147. func writeLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, txt string, n int) {
  148. defer wg.Done()
  149. for i := 0; i < n; i++ {
  150. _, err := conn.Write([]byte(txt))
  151. if err != nil {
  152. t.Fatalf("Failed to write to fooSecConn: %v", err)
  153. }
  154. }
  155. }
  156. func readLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, n int) {
  157. readBuffer := make([]byte, dataMaxSize)
  158. for i := 0; i < n; i++ {
  159. _, err := conn.Read(readBuffer)
  160. assert.NoError(t, err)
  161. }
  162. wg.Done()
  163. }
  164. func TestSecretConnectionReadWrite(t *testing.T) {
  165. fooConn, barConn := makeKVStoreConnPair()
  166. fooWrites, barWrites := []string{}, []string{}
  167. fooReads, barReads := []string{}, []string{}
  168. // Pre-generate the things to write (for foo & bar)
  169. for i := 0; i < 100; i++ {
  170. fooWrites = append(fooWrites, cmn.RandStr((cmn.RandInt()%(dataMaxSize*5))+1))
  171. barWrites = append(barWrites, cmn.RandStr((cmn.RandInt()%(dataMaxSize*5))+1))
  172. }
  173. // A helper that will run with (fooConn, fooWrites, fooReads) and vice versa
  174. genNodeRunner := func(id string, nodeConn kvstoreConn, nodeWrites []string, nodeReads *[]string) cmn.Task {
  175. return func(_ int) (interface{}, error, bool) {
  176. // Initiate cryptographic private key and secret connection trhough nodeConn.
  177. nodePrvKey := ed25519.GenPrivKey()
  178. nodeSecretConn, err := MakeSecretConnection(nodeConn, nodePrvKey)
  179. if err != nil {
  180. t.Errorf("Failed to establish SecretConnection for node: %v", err)
  181. return nil, err, true
  182. }
  183. // In parallel, handle some reads and writes.
  184. var trs, ok = cmn.Parallel(
  185. func(_ int) (interface{}, error, bool) {
  186. // Node writes:
  187. for _, nodeWrite := range nodeWrites {
  188. n, err := nodeSecretConn.Write([]byte(nodeWrite))
  189. if err != nil {
  190. t.Errorf("Failed to write to nodeSecretConn: %v", err)
  191. return nil, err, true
  192. }
  193. if n != len(nodeWrite) {
  194. err = fmt.Errorf("Failed to write all bytes. Expected %v, wrote %v", len(nodeWrite), n)
  195. t.Error(err)
  196. return nil, err, true
  197. }
  198. }
  199. if err := nodeConn.PipeWriter.Close(); err != nil {
  200. t.Error(err)
  201. return nil, err, true
  202. }
  203. return nil, nil, false
  204. },
  205. func(_ int) (interface{}, error, bool) {
  206. // Node reads:
  207. readBuffer := make([]byte, dataMaxSize)
  208. for {
  209. n, err := nodeSecretConn.Read(readBuffer)
  210. if err == io.EOF {
  211. return nil, nil, false
  212. } else if err != nil {
  213. t.Errorf("Failed to read from nodeSecretConn: %v", err)
  214. return nil, err, true
  215. }
  216. *nodeReads = append(*nodeReads, string(readBuffer[:n]))
  217. }
  218. if err := nodeConn.PipeReader.Close(); err != nil {
  219. t.Error(err)
  220. return nil, err, true
  221. }
  222. return nil, nil, false
  223. },
  224. )
  225. assert.True(t, ok, "Unexpected task abortion")
  226. // If error:
  227. if trs.FirstError() != nil {
  228. return nil, trs.FirstError(), true
  229. }
  230. // Otherwise:
  231. return nil, nil, false
  232. }
  233. }
  234. // Run foo & bar in parallel
  235. var trs, ok = cmn.Parallel(
  236. genNodeRunner("foo", fooConn, fooWrites, &fooReads),
  237. genNodeRunner("bar", barConn, barWrites, &barReads),
  238. )
  239. require.Nil(t, trs.FirstError())
  240. require.True(t, ok, "unexpected task abortion")
  241. // A helper to ensure that the writes and reads match.
  242. // Additionally, small writes (<= dataMaxSize) must be atomically read.
  243. compareWritesReads := func(writes []string, reads []string) {
  244. for {
  245. // Pop next write & corresponding reads
  246. var read, write string = "", writes[0]
  247. var readCount = 0
  248. for _, readChunk := range reads {
  249. read += readChunk
  250. readCount++
  251. if len(write) <= len(read) {
  252. break
  253. }
  254. if len(write) <= dataMaxSize {
  255. break // atomicity of small writes
  256. }
  257. }
  258. // Compare
  259. if write != read {
  260. t.Errorf("Expected to read %X, got %X", write, read)
  261. }
  262. // Iterate
  263. writes = writes[1:]
  264. reads = reads[readCount:]
  265. if len(writes) == 0 {
  266. break
  267. }
  268. }
  269. }
  270. compareWritesReads(fooWrites, barReads)
  271. compareWritesReads(barWrites, fooReads)
  272. }
  273. // Run go test -update from within this module
  274. // to update the golden test vector file
  275. var update = flag.Bool("update", false, "update .golden files")
  276. func TestDeriveSecretsAndChallengeGolden(t *testing.T) {
  277. goldenFilepath := filepath.Join("testdata", t.Name()+".golden")
  278. if *update {
  279. t.Logf("Updating golden test vector file %s", goldenFilepath)
  280. data := createGoldenTestVectors(t)
  281. cmn.WriteFile(goldenFilepath, []byte(data), 0644)
  282. }
  283. f, err := os.Open(goldenFilepath)
  284. if err != nil {
  285. log.Fatal(err)
  286. }
  287. defer f.Close()
  288. scanner := bufio.NewScanner(f)
  289. for scanner.Scan() {
  290. line := scanner.Text()
  291. params := strings.Split(line, ",")
  292. randSecretVector, err := hex.DecodeString(params[0])
  293. require.Nil(t, err)
  294. randSecret := new([32]byte)
  295. copy((*randSecret)[:], randSecretVector)
  296. locIsLeast, err := strconv.ParseBool(params[1])
  297. require.Nil(t, err)
  298. expectedRecvSecret, err := hex.DecodeString(params[2])
  299. require.Nil(t, err)
  300. expectedSendSecret, err := hex.DecodeString(params[3])
  301. require.Nil(t, err)
  302. expectedChallenge, err := hex.DecodeString(params[4])
  303. require.Nil(t, err)
  304. recvSecret, sendSecret, challenge := deriveSecretAndChallenge(randSecret, locIsLeast)
  305. require.Equal(t, expectedRecvSecret, (*recvSecret)[:], "Recv Secrets aren't equal")
  306. require.Equal(t, expectedSendSecret, (*sendSecret)[:], "Send Secrets aren't equal")
  307. require.Equal(t, expectedChallenge, (*challenge)[:], "challenges aren't equal")
  308. }
  309. }
  310. // Creates the data for a test vector file.
  311. // The file format is:
  312. // Hex(diffie_hellman_secret), loc_is_least, Hex(recvSecret), Hex(sendSecret), Hex(challenge)
  313. func createGoldenTestVectors(t *testing.T) string {
  314. data := ""
  315. for i := 0; i < 32; i++ {
  316. randSecretVector := cmn.RandBytes(32)
  317. randSecret := new([32]byte)
  318. copy((*randSecret)[:], randSecretVector)
  319. data += hex.EncodeToString((*randSecret)[:]) + ","
  320. locIsLeast := cmn.RandBool()
  321. data += strconv.FormatBool(locIsLeast) + ","
  322. recvSecret, sendSecret, challenge := deriveSecretAndChallenge(randSecret, locIsLeast)
  323. data += hex.EncodeToString((*recvSecret)[:]) + ","
  324. data += hex.EncodeToString((*sendSecret)[:]) + ","
  325. data += hex.EncodeToString((*challenge)[:]) + "\n"
  326. }
  327. return data
  328. }
  329. func BenchmarkSecretConnection(b *testing.B) {
  330. b.StopTimer()
  331. fooSecConn, barSecConn := makeSecretConnPair(b)
  332. fooWriteText := cmn.RandStr(dataMaxSize)
  333. // Consume reads from bar's reader
  334. go func() {
  335. readBuffer := make([]byte, dataMaxSize)
  336. for {
  337. _, err := barSecConn.Read(readBuffer)
  338. if err == io.EOF {
  339. return
  340. } else if err != nil {
  341. b.Fatalf("Failed to read from barSecConn: %v", err)
  342. }
  343. }
  344. }()
  345. b.StartTimer()
  346. for i := 0; i < b.N; i++ {
  347. _, err := fooSecConn.Write([]byte(fooWriteText))
  348. if err != nil {
  349. b.Fatalf("Failed to write to fooSecConn: %v", err)
  350. }
  351. }
  352. b.StopTimer()
  353. if err := fooSecConn.Close(); err != nil {
  354. b.Error(err)
  355. }
  356. //barSecConn.Close() race condition
  357. }