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.

399 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. if err := nodeConn.PipeReader.Close(); err != nil {
  212. t.Error(err)
  213. return nil, err, true
  214. }
  215. return nil, nil, false
  216. } else if err != nil {
  217. t.Errorf("Failed to read from nodeSecretConn: %v", err)
  218. return nil, err, true
  219. }
  220. *nodeReads = append(*nodeReads, string(readBuffer[:n]))
  221. }
  222. },
  223. )
  224. assert.True(t, ok, "Unexpected task abortion")
  225. // If error:
  226. if trs.FirstError() != nil {
  227. return nil, trs.FirstError(), true
  228. }
  229. // Otherwise:
  230. return nil, nil, false
  231. }
  232. }
  233. // Run foo & bar in parallel
  234. var trs, ok = cmn.Parallel(
  235. genNodeRunner("foo", fooConn, fooWrites, &fooReads),
  236. genNodeRunner("bar", barConn, barWrites, &barReads),
  237. )
  238. require.Nil(t, trs.FirstError())
  239. require.True(t, ok, "unexpected task abortion")
  240. // A helper to ensure that the writes and reads match.
  241. // Additionally, small writes (<= dataMaxSize) must be atomically read.
  242. compareWritesReads := func(writes []string, reads []string) {
  243. for {
  244. // Pop next write & corresponding reads
  245. var read, write string = "", writes[0]
  246. var readCount = 0
  247. for _, readChunk := range reads {
  248. read += readChunk
  249. readCount++
  250. if len(write) <= len(read) {
  251. break
  252. }
  253. if len(write) <= dataMaxSize {
  254. break // atomicity of small writes
  255. }
  256. }
  257. // Compare
  258. if write != read {
  259. t.Errorf("Expected to read %X, got %X", write, read)
  260. }
  261. // Iterate
  262. writes = writes[1:]
  263. reads = reads[readCount:]
  264. if len(writes) == 0 {
  265. break
  266. }
  267. }
  268. }
  269. compareWritesReads(fooWrites, barReads)
  270. compareWritesReads(barWrites, fooReads)
  271. }
  272. // Run go test -update from within this module
  273. // to update the golden test vector file
  274. var update = flag.Bool("update", false, "update .golden files")
  275. func TestDeriveSecretsAndChallengeGolden(t *testing.T) {
  276. goldenFilepath := filepath.Join("testdata", t.Name()+".golden")
  277. if *update {
  278. t.Logf("Updating golden test vector file %s", goldenFilepath)
  279. data := createGoldenTestVectors(t)
  280. cmn.WriteFile(goldenFilepath, []byte(data), 0644)
  281. }
  282. f, err := os.Open(goldenFilepath)
  283. if err != nil {
  284. log.Fatal(err)
  285. }
  286. defer f.Close()
  287. scanner := bufio.NewScanner(f)
  288. for scanner.Scan() {
  289. line := scanner.Text()
  290. params := strings.Split(line, ",")
  291. randSecretVector, err := hex.DecodeString(params[0])
  292. require.Nil(t, err)
  293. randSecret := new([32]byte)
  294. copy((*randSecret)[:], randSecretVector)
  295. locIsLeast, err := strconv.ParseBool(params[1])
  296. require.Nil(t, err)
  297. expectedRecvSecret, err := hex.DecodeString(params[2])
  298. require.Nil(t, err)
  299. expectedSendSecret, err := hex.DecodeString(params[3])
  300. require.Nil(t, err)
  301. expectedChallenge, err := hex.DecodeString(params[4])
  302. require.Nil(t, err)
  303. recvSecret, sendSecret, challenge := deriveSecretAndChallenge(randSecret, locIsLeast)
  304. require.Equal(t, expectedRecvSecret, (*recvSecret)[:], "Recv Secrets aren't equal")
  305. require.Equal(t, expectedSendSecret, (*sendSecret)[:], "Send Secrets aren't equal")
  306. require.Equal(t, expectedChallenge, (*challenge)[:], "challenges aren't equal")
  307. }
  308. }
  309. // Creates the data for a test vector file.
  310. // The file format is:
  311. // Hex(diffie_hellman_secret), loc_is_least, Hex(recvSecret), Hex(sendSecret), Hex(challenge)
  312. func createGoldenTestVectors(t *testing.T) string {
  313. data := ""
  314. for i := 0; i < 32; i++ {
  315. randSecretVector := cmn.RandBytes(32)
  316. randSecret := new([32]byte)
  317. copy((*randSecret)[:], randSecretVector)
  318. data += hex.EncodeToString((*randSecret)[:]) + ","
  319. locIsLeast := cmn.RandBool()
  320. data += strconv.FormatBool(locIsLeast) + ","
  321. recvSecret, sendSecret, challenge := deriveSecretAndChallenge(randSecret, locIsLeast)
  322. data += hex.EncodeToString((*recvSecret)[:]) + ","
  323. data += hex.EncodeToString((*sendSecret)[:]) + ","
  324. data += hex.EncodeToString((*challenge)[:]) + "\n"
  325. }
  326. return data
  327. }
  328. func BenchmarkSecretConnection(b *testing.B) {
  329. b.StopTimer()
  330. fooSecConn, barSecConn := makeSecretConnPair(b)
  331. fooWriteText := cmn.RandStr(dataMaxSize)
  332. // Consume reads from bar's reader
  333. go func() {
  334. readBuffer := make([]byte, dataMaxSize)
  335. for {
  336. _, err := barSecConn.Read(readBuffer)
  337. if err == io.EOF {
  338. return
  339. } else if err != nil {
  340. b.Fatalf("Failed to read from barSecConn: %v", err)
  341. }
  342. }
  343. }()
  344. b.StartTimer()
  345. for i := 0; i < b.N; i++ {
  346. _, err := fooSecConn.Write([]byte(fooWriteText))
  347. if err != nil {
  348. b.Fatalf("Failed to write to fooSecConn: %v", err)
  349. }
  350. }
  351. b.StopTimer()
  352. if err := fooSecConn.Close(); err != nil {
  353. b.Error(err)
  354. }
  355. //barSecConn.Close() race condition
  356. }