Alternative TLS implementation in Go
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

227 lignes
5.7 KiB

  1. // Copyright 2013 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package th5
  5. import (
  6. "bufio"
  7. "encoding/hex"
  8. "errors"
  9. "flag"
  10. "fmt"
  11. "io"
  12. "io/ioutil"
  13. "net"
  14. "os/exec"
  15. "strconv"
  16. "strings"
  17. "sync"
  18. "testing"
  19. )
  20. // TLS reference tests run a connection against a reference implementation
  21. // (OpenSSL) of TLS and record the bytes of the resulting connection. The Go
  22. // code, during a test, is configured with deterministic randomness and so the
  23. // reference test can be reproduced exactly in the future.
  24. //
  25. // In order to save everyone who wishes to run the tests from needing the
  26. // reference implementation installed, the reference connections are saved in
  27. // files in the testdata directory. Thus running the tests involves nothing
  28. // external, but creating and updating them requires the reference
  29. // implementation.
  30. //
  31. // Tests can be updated by running them with the -update flag. This will cause
  32. // the test files to be regenerated. Generally one should combine the -update
  33. // flag with -test.run to updated a specific test. Since the reference
  34. // implementation will always generate fresh random numbers, large parts of
  35. // the reference connection will always change.
  36. var (
  37. update = flag.Bool("update", false, "update golden files on disk")
  38. opensslVersionTestOnce sync.Once
  39. opensslVersionTestErr error
  40. )
  41. func checkOpenSSLVersion(t *testing.T) {
  42. opensslVersionTestOnce.Do(testOpenSSLVersion)
  43. if opensslVersionTestErr != nil {
  44. t.Fatal(opensslVersionTestErr)
  45. }
  46. }
  47. func testOpenSSLVersion() {
  48. // This test ensures that the version of OpenSSL looks reasonable
  49. // before updating the test data.
  50. if !*update {
  51. return
  52. }
  53. openssl := exec.Command("openssl", "version")
  54. output, err := openssl.CombinedOutput()
  55. if err != nil {
  56. opensslVersionTestErr = err
  57. return
  58. }
  59. version := string(output)
  60. if strings.HasPrefix(version, "OpenSSL 1.1.0") {
  61. return
  62. }
  63. println("***********************************************")
  64. println("")
  65. println("You need to build OpenSSL 1.1.0 from source in order")
  66. println("to update the test data.")
  67. println("")
  68. println("Configure it with:")
  69. println("./Configure enable-weak-ssl-ciphers enable-ssl3 enable-ssl3-method -static linux-x86_64")
  70. println("and then add the apps/ directory at the front of your PATH.")
  71. println("***********************************************")
  72. opensslVersionTestErr = errors.New("version of OpenSSL does not appear to be suitable for updating test data")
  73. }
  74. // recordingConn is a net.Conn that records the traffic that passes through it.
  75. // WriteTo can be used to produce output that can be later be loaded with
  76. // ParseTestData.
  77. type recordingConn struct {
  78. net.Conn
  79. sync.Mutex
  80. flows [][]byte
  81. reading bool
  82. }
  83. func (r *recordingConn) Read(b []byte) (n int, err error) {
  84. if n, err = r.Conn.Read(b); n == 0 {
  85. return
  86. }
  87. b = b[:n]
  88. r.Lock()
  89. defer r.Unlock()
  90. if l := len(r.flows); l == 0 || !r.reading {
  91. buf := make([]byte, len(b))
  92. copy(buf, b)
  93. r.flows = append(r.flows, buf)
  94. } else {
  95. r.flows[l-1] = append(r.flows[l-1], b[:n]...)
  96. }
  97. r.reading = true
  98. return
  99. }
  100. func (r *recordingConn) Write(b []byte) (n int, err error) {
  101. if n, err = r.Conn.Write(b); n == 0 {
  102. return
  103. }
  104. b = b[:n]
  105. r.Lock()
  106. defer r.Unlock()
  107. if l := len(r.flows); l == 0 || r.reading {
  108. buf := make([]byte, len(b))
  109. copy(buf, b)
  110. r.flows = append(r.flows, buf)
  111. } else {
  112. r.flows[l-1] = append(r.flows[l-1], b[:n]...)
  113. }
  114. r.reading = false
  115. return
  116. }
  117. // WriteTo writes Go source code to w that contains the recorded traffic.
  118. func (r *recordingConn) WriteTo(w io.Writer) (int64, error) {
  119. // TLS always starts with a client to server flow.
  120. clientToServer := true
  121. var written int64
  122. for i, flow := range r.flows {
  123. source, dest := "client", "server"
  124. if !clientToServer {
  125. source, dest = dest, source
  126. }
  127. n, err := fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest)
  128. written += int64(n)
  129. if err != nil {
  130. return written, err
  131. }
  132. dumper := hex.Dumper(w)
  133. n, err = dumper.Write(flow)
  134. written += int64(n)
  135. if err != nil {
  136. return written, err
  137. }
  138. err = dumper.Close()
  139. if err != nil {
  140. return written, err
  141. }
  142. clientToServer = !clientToServer
  143. }
  144. return written, nil
  145. }
  146. func parseTestData(r io.Reader) (flows [][]byte, err error) {
  147. var currentFlow []byte
  148. scanner := bufio.NewScanner(r)
  149. for scanner.Scan() {
  150. line := scanner.Text()
  151. // If the line starts with ">>> " then it marks the beginning
  152. // of a new flow.
  153. if strings.HasPrefix(line, ">>> ") {
  154. if len(currentFlow) > 0 || len(flows) > 0 {
  155. flows = append(flows, currentFlow)
  156. currentFlow = nil
  157. }
  158. continue
  159. }
  160. // Otherwise the line is a line of hex dump that looks like:
  161. // 00000170 fc f5 06 bf (...) |.....X{&?......!|
  162. // (Some bytes have been omitted from the middle section.)
  163. if i := strings.IndexByte(line, ' '); i >= 0 {
  164. line = line[i:]
  165. } else {
  166. return nil, errors.New("invalid test data")
  167. }
  168. if i := strings.IndexByte(line, '|'); i >= 0 {
  169. line = line[:i]
  170. } else {
  171. return nil, errors.New("invalid test data")
  172. }
  173. hexBytes := strings.Fields(line)
  174. for _, hexByte := range hexBytes {
  175. val, err := strconv.ParseUint(hexByte, 16, 8)
  176. if err != nil {
  177. return nil, errors.New("invalid hex byte in test data: " + err.Error())
  178. }
  179. currentFlow = append(currentFlow, byte(val))
  180. }
  181. }
  182. if len(currentFlow) > 0 {
  183. flows = append(flows, currentFlow)
  184. }
  185. return flows, nil
  186. }
  187. // tempFile creates a temp file containing contents and returns its path.
  188. func tempFile(contents string) string {
  189. file, err := ioutil.TempFile("", "go-tls-test")
  190. if err != nil {
  191. panic("failed to create temp file: " + err.Error())
  192. }
  193. path := file.Name()
  194. file.WriteString(contents)
  195. file.Close()
  196. return path
  197. }