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.
23 // TLS reference tests run a connection against a reference implementation
24 // (OpenSSL) of TLS and record the bytes of the resulting connection. The Go
25 // code, during a test, is configured with deterministic randomness and so the
26 // reference test can be reproduced exactly in the future.
28 // In order to save everyone who wishes to run the tests from needing the
29 // reference implementation installed, the reference connections are saved in
30 // files in the testdata directory. Thus running the tests involves nothing
31 // external, but creating and updating them requires the reference
34 // Tests can be updated by running them with the -update flag. This will cause
35 // the test files to be regenerated. Generally one should combine the -update
36 // flag with -test.run to updated a specific test. Since the reference
37 // implementation will always generate fresh random numbers, large parts of
38 // the reference connection will always change.
41 update
= flag
.Bool("update", false, "update golden files on disk")
43 opensslVersionTestOnce sync
.Once
44 opensslVersionTestErr error
47 func checkOpenSSLVersion(t
*testing
.T
) {
48 opensslVersionTestOnce
.Do(testOpenSSLVersion
)
49 if opensslVersionTestErr
!= nil {
50 t
.Fatal(opensslVersionTestErr
)
54 func testOpenSSLVersion() {
55 // This test ensures that the version of OpenSSL looks reasonable
56 // before updating the test data.
62 openssl
:= exec
.Command("openssl", "version")
63 output
, err
:= openssl
.CombinedOutput()
65 opensslVersionTestErr
= err
69 version
:= string(output
)
70 if strings
.HasPrefix(version
, "OpenSSL 1.1.0") {
74 println("***********************************************")
76 println("You need to build OpenSSL 1.1.0 from source in order")
77 println("to update the test data.")
79 println("Configure it with:")
80 println("./Configure enable-weak-ssl-ciphers enable-ssl3 enable-ssl3-method -static linux-x86_64")
81 println("and then add the apps/ directory at the front of your PATH.")
82 println("***********************************************")
84 opensslVersionTestErr
= errors
.New("version of OpenSSL does not appear to be suitable for updating test data")
87 // recordingConn is a net.Conn that records the traffic that passes through it.
88 // WriteTo can be used to produce output that can be later be loaded with
90 type recordingConn
struct {
97 func (r
*recordingConn
) Read(b
[]byte) (n
int, err error
) {
98 if n
, err
= r
.Conn
.Read(b
); n
== 0 {
106 if l
:= len(r
.flows
); l
== 0 ||
!r
.reading
{
107 buf
:= make([]byte, len(b
))
109 r
.flows
= append(r
.flows
, buf
)
111 r
.flows
[l
-1] = append(r
.flows
[l
-1], b
[:n
]...)
117 func (r
*recordingConn
) Write(b
[]byte) (n
int, err error
) {
118 if n
, err
= r
.Conn
.Write(b
); n
== 0 {
126 if l
:= len(r
.flows
); l
== 0 || r
.reading
{
127 buf
:= make([]byte, len(b
))
129 r
.flows
= append(r
.flows
, buf
)
131 r
.flows
[l
-1] = append(r
.flows
[l
-1], b
[:n
]...)
137 // WriteTo writes Go source code to w that contains the recorded traffic.
138 func (r
*recordingConn
) WriteTo(w io
.Writer
) (int64, error
) {
139 // TLS always starts with a client to server flow.
140 clientToServer
:= true
142 for i
, flow
:= range r
.flows
{
143 source
, dest
:= "client", "server"
145 source
, dest
= dest
, source
147 n
, err
:= fmt
.Fprintf(w
, ">>> Flow %d (%s to %s)\n", i
+1, source
, dest
)
152 dumper
:= hex
.Dumper(w
)
153 n
, err
= dumper
.Write(flow
)
162 clientToServer
= !clientToServer
167 func parseTestData(r io
.Reader
) (flows
[][]byte, err error
) {
168 var currentFlow
[]byte
170 scanner
:= bufio
.NewScanner(r
)
172 line
:= scanner
.Text()
173 // If the line starts with ">>> " then it marks the beginning
175 if strings
.HasPrefix(line
, ">>> ") {
176 if len(currentFlow
) > 0 ||
len(flows
) > 0 {
177 flows
= append(flows
, currentFlow
)
183 // Otherwise the line is a line of hex dump that looks like:
184 // 00000170 fc f5 06 bf (...) |.....X{&?......!|
185 // (Some bytes have been omitted from the middle section.)
187 if i
:= strings
.IndexByte(line
, ' '); i
>= 0 {
190 return nil, errors
.New("invalid test data")
193 if i
:= strings
.IndexByte(line
, '|'); i
>= 0 {
196 return nil, errors
.New("invalid test data")
199 hexBytes
:= strings
.Fields(line
)
200 for _
, hexByte
:= range hexBytes
{
201 val
, err
:= strconv
.ParseUint(hexByte
, 16, 8)
203 return nil, errors
.New("invalid hex byte in test data: " + err
.Error())
205 currentFlow
= append(currentFlow
, byte(val
))
209 if len(currentFlow
) > 0 {
210 flows
= append(flows
, currentFlow
)
216 // tempFile creates a temp file containing contents and returns its path.
217 func tempFile(contents
string) string {
218 file
, err
:= ioutil
.TempFile("", "go-tls-test")
220 panic("failed to create temp file: " + err
.Error())
223 file
.WriteString(contents
)