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.

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