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.

477 lines
13 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 fooSecConn, barSecConn
  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. // Test that shareEphPubKey rejects lower order public keys based on an
  92. // (incomplete) blacklist.
  93. func TestShareLowOrderPubkey(t *testing.T) {
  94. var fooConn, barConn = makeKVStoreConnPair()
  95. defer fooConn.Close()
  96. defer barConn.Close()
  97. locEphPub, _ := genEphKeys()
  98. // all blacklisted low order points:
  99. for _, remLowOrderPubKey := range blacklist {
  100. remLowOrderPubKey := remLowOrderPubKey
  101. _, _ = cmn.Parallel(
  102. func(_ int) (val interface{}, err error, abort bool) {
  103. _, err = shareEphPubKey(fooConn, locEphPub)
  104. require.Error(t, err)
  105. require.Equal(t, err, ErrSmallOrderRemotePubKey)
  106. return nil, nil, false
  107. },
  108. func(_ int) (val interface{}, err error, abort bool) {
  109. readRemKey, err := shareEphPubKey(barConn, &remLowOrderPubKey)
  110. require.NoError(t, err)
  111. require.Equal(t, locEphPub, readRemKey)
  112. return nil, nil, false
  113. })
  114. }
  115. }
  116. // Test that additionally that the Diffie-Hellman shared secret is non-zero.
  117. // The shared secret would be zero for lower order pub-keys (but tested against the blacklist only).
  118. func TestComputeDHFailsOnLowOrder(t *testing.T) {
  119. _, locPrivKey := genEphKeys()
  120. for _, remLowOrderPubKey := range blacklist {
  121. remLowOrderPubKey := remLowOrderPubKey
  122. shared, err := computeDHSecret(&remLowOrderPubKey, locPrivKey)
  123. assert.Error(t, err)
  124. assert.Equal(t, err, ErrSharedSecretIsZero)
  125. assert.Empty(t, shared)
  126. }
  127. }
  128. func TestConcurrentWrite(t *testing.T) {
  129. fooSecConn, barSecConn := makeSecretConnPair(t)
  130. fooWriteText := cmn.RandStr(dataMaxSize)
  131. // write from two routines.
  132. // should be safe from race according to net.Conn:
  133. // https://golang.org/pkg/net/#Conn
  134. n := 100
  135. wg := new(sync.WaitGroup)
  136. wg.Add(3)
  137. go writeLots(t, wg, fooSecConn, fooWriteText, n)
  138. go writeLots(t, wg, fooSecConn, fooWriteText, n)
  139. // Consume reads from bar's reader
  140. readLots(t, wg, barSecConn, n*2)
  141. wg.Wait()
  142. if err := fooSecConn.Close(); err != nil {
  143. t.Error(err)
  144. }
  145. }
  146. func TestConcurrentRead(t *testing.T) {
  147. fooSecConn, barSecConn := makeSecretConnPair(t)
  148. fooWriteText := cmn.RandStr(dataMaxSize)
  149. n := 100
  150. // read from two routines.
  151. // should be safe from race according to net.Conn:
  152. // https://golang.org/pkg/net/#Conn
  153. wg := new(sync.WaitGroup)
  154. wg.Add(3)
  155. go readLots(t, wg, fooSecConn, n/2)
  156. go readLots(t, wg, fooSecConn, n/2)
  157. // write to bar
  158. writeLots(t, wg, barSecConn, fooWriteText, n)
  159. wg.Wait()
  160. if err := fooSecConn.Close(); err != nil {
  161. t.Error(err)
  162. }
  163. }
  164. func writeLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, txt string, n int) {
  165. defer wg.Done()
  166. for i := 0; i < n; i++ {
  167. _, err := conn.Write([]byte(txt))
  168. if err != nil {
  169. t.Errorf("Failed to write to fooSecConn: %v", err)
  170. return
  171. }
  172. }
  173. }
  174. func readLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, n int) {
  175. readBuffer := make([]byte, dataMaxSize)
  176. for i := 0; i < n; i++ {
  177. _, err := conn.Read(readBuffer)
  178. assert.NoError(t, err)
  179. }
  180. wg.Done()
  181. }
  182. func TestSecretConnectionReadWrite(t *testing.T) {
  183. fooConn, barConn := makeKVStoreConnPair()
  184. fooWrites, barWrites := []string{}, []string{}
  185. fooReads, barReads := []string{}, []string{}
  186. // Pre-generate the things to write (for foo & bar)
  187. for i := 0; i < 100; i++ {
  188. fooWrites = append(fooWrites, cmn.RandStr((cmn.RandInt()%(dataMaxSize*5))+1))
  189. barWrites = append(barWrites, cmn.RandStr((cmn.RandInt()%(dataMaxSize*5))+1))
  190. }
  191. // A helper that will run with (fooConn, fooWrites, fooReads) and vice versa
  192. genNodeRunner := func(id string, nodeConn kvstoreConn, nodeWrites []string, nodeReads *[]string) cmn.Task {
  193. return func(_ int) (interface{}, error, bool) {
  194. // Initiate cryptographic private key and secret connection trhough nodeConn.
  195. nodePrvKey := ed25519.GenPrivKey()
  196. nodeSecretConn, err := MakeSecretConnection(nodeConn, nodePrvKey)
  197. if err != nil {
  198. t.Errorf("Failed to establish SecretConnection for node: %v", err)
  199. return nil, err, true
  200. }
  201. // In parallel, handle some reads and writes.
  202. var trs, ok = cmn.Parallel(
  203. func(_ int) (interface{}, error, bool) {
  204. // Node writes:
  205. for _, nodeWrite := range nodeWrites {
  206. n, err := nodeSecretConn.Write([]byte(nodeWrite))
  207. if err != nil {
  208. t.Errorf("Failed to write to nodeSecretConn: %v", err)
  209. return nil, err, true
  210. }
  211. if n != len(nodeWrite) {
  212. err = fmt.Errorf("Failed to write all bytes. Expected %v, wrote %v", len(nodeWrite), n)
  213. t.Error(err)
  214. return nil, err, true
  215. }
  216. }
  217. if err := nodeConn.PipeWriter.Close(); err != nil {
  218. t.Error(err)
  219. return nil, err, true
  220. }
  221. return nil, nil, false
  222. },
  223. func(_ int) (interface{}, error, bool) {
  224. // Node reads:
  225. readBuffer := make([]byte, dataMaxSize)
  226. for {
  227. n, err := nodeSecretConn.Read(readBuffer)
  228. if err == io.EOF {
  229. if err := nodeConn.PipeReader.Close(); err != nil {
  230. t.Error(err)
  231. return nil, err, true
  232. }
  233. return nil, nil, false
  234. } else if err != nil {
  235. t.Errorf("Failed to read from nodeSecretConn: %v", err)
  236. return nil, err, true
  237. }
  238. *nodeReads = append(*nodeReads, string(readBuffer[:n]))
  239. }
  240. },
  241. )
  242. assert.True(t, ok, "Unexpected task abortion")
  243. // If error:
  244. if trs.FirstError() != nil {
  245. return nil, trs.FirstError(), true
  246. }
  247. // Otherwise:
  248. return nil, nil, false
  249. }
  250. }
  251. // Run foo & bar in parallel
  252. var trs, ok = cmn.Parallel(
  253. genNodeRunner("foo", fooConn, fooWrites, &fooReads),
  254. genNodeRunner("bar", barConn, barWrites, &barReads),
  255. )
  256. require.Nil(t, trs.FirstError())
  257. require.True(t, ok, "unexpected task abortion")
  258. // A helper to ensure that the writes and reads match.
  259. // Additionally, small writes (<= dataMaxSize) must be atomically read.
  260. compareWritesReads := func(writes []string, reads []string) {
  261. for {
  262. // Pop next write & corresponding reads
  263. var read, write string = "", writes[0]
  264. var readCount = 0
  265. for _, readChunk := range reads {
  266. read += readChunk
  267. readCount++
  268. if len(write) <= len(read) {
  269. break
  270. }
  271. if len(write) <= dataMaxSize {
  272. break // atomicity of small writes
  273. }
  274. }
  275. // Compare
  276. if write != read {
  277. t.Errorf("Expected to read %X, got %X", write, read)
  278. }
  279. // Iterate
  280. writes = writes[1:]
  281. reads = reads[readCount:]
  282. if len(writes) == 0 {
  283. break
  284. }
  285. }
  286. }
  287. compareWritesReads(fooWrites, barReads)
  288. compareWritesReads(barWrites, fooReads)
  289. }
  290. // Run go test -update from within this module
  291. // to update the golden test vector file
  292. var update = flag.Bool("update", false, "update .golden files")
  293. func TestDeriveSecretsAndChallengeGolden(t *testing.T) {
  294. goldenFilepath := filepath.Join("testdata", t.Name()+".golden")
  295. if *update {
  296. t.Logf("Updating golden test vector file %s", goldenFilepath)
  297. data := createGoldenTestVectors(t)
  298. cmn.WriteFile(goldenFilepath, []byte(data), 0644)
  299. }
  300. f, err := os.Open(goldenFilepath)
  301. if err != nil {
  302. log.Fatal(err)
  303. }
  304. defer f.Close()
  305. scanner := bufio.NewScanner(f)
  306. for scanner.Scan() {
  307. line := scanner.Text()
  308. params := strings.Split(line, ",")
  309. randSecretVector, err := hex.DecodeString(params[0])
  310. require.Nil(t, err)
  311. randSecret := new([32]byte)
  312. copy((*randSecret)[:], randSecretVector)
  313. locIsLeast, err := strconv.ParseBool(params[1])
  314. require.Nil(t, err)
  315. expectedRecvSecret, err := hex.DecodeString(params[2])
  316. require.Nil(t, err)
  317. expectedSendSecret, err := hex.DecodeString(params[3])
  318. require.Nil(t, err)
  319. expectedChallenge, err := hex.DecodeString(params[4])
  320. require.Nil(t, err)
  321. recvSecret, sendSecret, challenge := deriveSecretAndChallenge(randSecret, locIsLeast)
  322. require.Equal(t, expectedRecvSecret, (*recvSecret)[:], "Recv Secrets aren't equal")
  323. require.Equal(t, expectedSendSecret, (*sendSecret)[:], "Send Secrets aren't equal")
  324. require.Equal(t, expectedChallenge, (*challenge)[:], "challenges aren't equal")
  325. }
  326. }
  327. // Creates the data for a test vector file.
  328. // The file format is:
  329. // Hex(diffie_hellman_secret), loc_is_least, Hex(recvSecret), Hex(sendSecret), Hex(challenge)
  330. func createGoldenTestVectors(t *testing.T) string {
  331. data := ""
  332. for i := 0; i < 32; i++ {
  333. randSecretVector := cmn.RandBytes(32)
  334. randSecret := new([32]byte)
  335. copy((*randSecret)[:], randSecretVector)
  336. data += hex.EncodeToString((*randSecret)[:]) + ","
  337. locIsLeast := cmn.RandBool()
  338. data += strconv.FormatBool(locIsLeast) + ","
  339. recvSecret, sendSecret, challenge := deriveSecretAndChallenge(randSecret, locIsLeast)
  340. data += hex.EncodeToString((*recvSecret)[:]) + ","
  341. data += hex.EncodeToString((*sendSecret)[:]) + ","
  342. data += hex.EncodeToString((*challenge)[:]) + "\n"
  343. }
  344. return data
  345. }
  346. func BenchmarkWriteSecretConnection(b *testing.B) {
  347. b.StopTimer()
  348. b.ReportAllocs()
  349. fooSecConn, barSecConn := makeSecretConnPair(b)
  350. randomMsgSizes := []int{
  351. dataMaxSize / 10,
  352. dataMaxSize / 3,
  353. dataMaxSize / 2,
  354. dataMaxSize,
  355. dataMaxSize * 3 / 2,
  356. dataMaxSize * 2,
  357. dataMaxSize * 7 / 2,
  358. }
  359. fooWriteBytes := make([][]byte, 0, len(randomMsgSizes))
  360. for _, size := range randomMsgSizes {
  361. fooWriteBytes = append(fooWriteBytes, cmn.RandBytes(size))
  362. }
  363. // Consume reads from bar's reader
  364. go func() {
  365. readBuffer := make([]byte, dataMaxSize)
  366. for {
  367. _, err := barSecConn.Read(readBuffer)
  368. if err == io.EOF {
  369. return
  370. } else if err != nil {
  371. b.Errorf("Failed to read from barSecConn: %v", err)
  372. return
  373. }
  374. }
  375. }()
  376. b.StartTimer()
  377. for i := 0; i < b.N; i++ {
  378. idx := cmn.RandIntn(len(fooWriteBytes))
  379. _, err := fooSecConn.Write(fooWriteBytes[idx])
  380. if err != nil {
  381. b.Errorf("Failed to write to fooSecConn: %v", err)
  382. return
  383. }
  384. }
  385. b.StopTimer()
  386. if err := fooSecConn.Close(); err != nil {
  387. b.Error(err)
  388. }
  389. //barSecConn.Close() race condition
  390. }
  391. func BenchmarkReadSecretConnection(b *testing.B) {
  392. b.StopTimer()
  393. b.ReportAllocs()
  394. fooSecConn, barSecConn := makeSecretConnPair(b)
  395. randomMsgSizes := []int{
  396. dataMaxSize / 10,
  397. dataMaxSize / 3,
  398. dataMaxSize / 2,
  399. dataMaxSize,
  400. dataMaxSize * 3 / 2,
  401. dataMaxSize * 2,
  402. dataMaxSize * 7 / 2,
  403. }
  404. fooWriteBytes := make([][]byte, 0, len(randomMsgSizes))
  405. for _, size := range randomMsgSizes {
  406. fooWriteBytes = append(fooWriteBytes, cmn.RandBytes(size))
  407. }
  408. go func() {
  409. for i := 0; i < b.N; i++ {
  410. idx := cmn.RandIntn(len(fooWriteBytes))
  411. _, err := fooSecConn.Write(fooWriteBytes[idx])
  412. if err != nil {
  413. b.Errorf("Failed to write to fooSecConn: %v, %v,%v", err, i, b.N)
  414. return
  415. }
  416. }
  417. }()
  418. b.StartTimer()
  419. for i := 0; i < b.N; i++ {
  420. readBuffer := make([]byte, dataMaxSize)
  421. _, err := barSecConn.Read(readBuffer)
  422. if err == io.EOF {
  423. return
  424. } else if err != nil {
  425. b.Fatalf("Failed to read from barSecConn: %v", err)
  426. }
  427. }
  428. b.StopTimer()
  429. }