PR c++/85515
[official-gcc.git] / libgo / go / crypto / tls / handshake_test.go
blob4b3fa238f4ae0d59ebf1895491495c99dea98517
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.
5 package tls
7 import (
8 "bufio"
9 "encoding/hex"
10 "errors"
11 "flag"
12 "fmt"
13 "io"
14 "io/ioutil"
15 "net"
16 "os/exec"
17 "strconv"
18 "strings"
19 "sync"
20 "testing"
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
32 // implementation.
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.
40 var (
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.
58 if !*update {
59 return
62 openssl := exec.Command("openssl", "version")
63 output, err := openssl.CombinedOutput()
64 if err != nil {
65 opensslVersionTestErr = err
66 return
69 version := string(output)
70 if strings.HasPrefix(version, "OpenSSL 1.1.0") {
71 return
74 println("***********************************************")
75 println("")
76 println("You need to build OpenSSL 1.1.0 from source in order")
77 println("to update the test data.")
78 println("")
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
89 // ParseTestData.
90 type recordingConn struct {
91 net.Conn
92 sync.Mutex
93 flows [][]byte
94 reading bool
97 func (r *recordingConn) Read(b []byte) (n int, err error) {
98 if n, err = r.Conn.Read(b); n == 0 {
99 return
101 b = b[:n]
103 r.Lock()
104 defer r.Unlock()
106 if l := len(r.flows); l == 0 || !r.reading {
107 buf := make([]byte, len(b))
108 copy(buf, b)
109 r.flows = append(r.flows, buf)
110 } else {
111 r.flows[l-1] = append(r.flows[l-1], b[:n]...)
113 r.reading = true
114 return
117 func (r *recordingConn) Write(b []byte) (n int, err error) {
118 if n, err = r.Conn.Write(b); n == 0 {
119 return
121 b = b[:n]
123 r.Lock()
124 defer r.Unlock()
126 if l := len(r.flows); l == 0 || r.reading {
127 buf := make([]byte, len(b))
128 copy(buf, b)
129 r.flows = append(r.flows, buf)
130 } else {
131 r.flows[l-1] = append(r.flows[l-1], b[:n]...)
133 r.reading = false
134 return
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
141 var written int64
142 for i, flow := range r.flows {
143 source, dest := "client", "server"
144 if !clientToServer {
145 source, dest = dest, source
147 n, err := fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest)
148 written += int64(n)
149 if err != nil {
150 return written, err
152 dumper := hex.Dumper(w)
153 n, err = dumper.Write(flow)
154 written += int64(n)
155 if err != nil {
156 return written, err
158 err = dumper.Close()
159 if err != nil {
160 return written, err
162 clientToServer = !clientToServer
164 return written, nil
167 func parseTestData(r io.Reader) (flows [][]byte, err error) {
168 var currentFlow []byte
170 scanner := bufio.NewScanner(r)
171 for scanner.Scan() {
172 line := scanner.Text()
173 // If the line starts with ">>> " then it marks the beginning
174 // of a new flow.
175 if strings.HasPrefix(line, ">>> ") {
176 if len(currentFlow) > 0 || len(flows) > 0 {
177 flows = append(flows, currentFlow)
178 currentFlow = nil
180 continue
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 {
188 line = line[i:]
189 } else {
190 return nil, errors.New("invalid test data")
193 if i := strings.IndexByte(line, '|'); i >= 0 {
194 line = line[:i]
195 } else {
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)
202 if err != nil {
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)
213 return flows, nil
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")
219 if err != nil {
220 panic("failed to create temp file: " + err.Error())
222 path := file.Name()
223 file.WriteString(contents)
224 file.Close()
225 return path