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.

478 lines
13 KiB

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