libgo: Merge from revision 18783:00cce3a34d7e of master library.
[official-gcc.git] / libgo / go / crypto / tls / handshake_client_test.go
blob766cb1320e777b923772db14d16139290257ce7f
1 // Copyright 2010 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 "bytes"
9 "crypto/ecdsa"
10 "crypto/rsa"
11 "crypto/x509"
12 "encoding/pem"
13 "fmt"
14 "io"
15 "net"
16 "os"
17 "os/exec"
18 "path/filepath"
19 "strconv"
20 "testing"
21 "time"
24 // Note: see comment in handshake_test.go for details of how the reference
25 // tests work.
27 // blockingSource is an io.Reader that blocks a Read call until it's closed.
28 type blockingSource chan bool
30 func (b blockingSource) Read([]byte) (n int, err error) {
31 <-b
32 return 0, io.EOF
35 // clientTest represents a test of the TLS client handshake against a reference
36 // implementation.
37 type clientTest struct {
38 // name is a freeform string identifying the test and the file in which
39 // the expected results will be stored.
40 name string
41 // command, if not empty, contains a series of arguments for the
42 // command to run for the reference server.
43 command []string
44 // config, if not nil, contains a custom Config to use for this test.
45 config *Config
46 // cert, if not empty, contains a DER-encoded certificate for the
47 // reference server.
48 cert []byte
49 // key, if not nil, contains either a *rsa.PrivateKey or
50 // *ecdsa.PrivateKey which is the private key for the reference server.
51 key interface{}
54 var defaultServerCommand = []string{"openssl", "s_server"}
56 // connFromCommand starts the reference server process, connects to it and
57 // returns a recordingConn for the connection. The stdin return value is a
58 // blockingSource for the stdin of the child process. It must be closed before
59 // Waiting for child.
60 func (test *clientTest) connFromCommand() (conn *recordingConn, child *exec.Cmd, stdin blockingSource, err error) {
61 cert := testRSACertificate
62 if len(test.cert) > 0 {
63 cert = test.cert
65 certPath := tempFile(string(cert))
66 defer os.Remove(certPath)
68 var key interface{} = testRSAPrivateKey
69 if test.key != nil {
70 key = test.key
72 var pemType string
73 var derBytes []byte
74 switch key := key.(type) {
75 case *rsa.PrivateKey:
76 pemType = "RSA"
77 derBytes = x509.MarshalPKCS1PrivateKey(key)
78 case *ecdsa.PrivateKey:
79 pemType = "EC"
80 var err error
81 derBytes, err = x509.MarshalECPrivateKey(key)
82 if err != nil {
83 panic(err)
85 default:
86 panic("unknown key type")
89 var pemOut bytes.Buffer
90 pem.Encode(&pemOut, &pem.Block{Type: pemType + " PRIVATE KEY", Bytes: derBytes})
92 keyPath := tempFile(string(pemOut.Bytes()))
93 defer os.Remove(keyPath)
95 var command []string
96 if len(test.command) > 0 {
97 command = append(command, test.command...)
98 } else {
99 command = append(command, defaultServerCommand...)
101 command = append(command, "-cert", certPath, "-certform", "DER", "-key", keyPath)
102 // serverPort contains the port that OpenSSL will listen on. OpenSSL
103 // can't take "0" as an argument here so we have to pick a number and
104 // hope that it's not in use on the machine. Since this only occurs
105 // when -update is given and thus when there's a human watching the
106 // test, this isn't too bad.
107 const serverPort = 24323
108 command = append(command, "-accept", strconv.Itoa(serverPort))
110 cmd := exec.Command(command[0], command[1:]...)
111 stdin = blockingSource(make(chan bool))
112 cmd.Stdin = stdin
113 var out bytes.Buffer
114 cmd.Stdout = &out
115 cmd.Stderr = &out
116 if err := cmd.Start(); err != nil {
117 return nil, nil, nil, err
120 // OpenSSL does print an "ACCEPT" banner, but it does so *before*
121 // opening the listening socket, so we can't use that to wait until it
122 // has started listening. Thus we are forced to poll until we get a
123 // connection.
124 var tcpConn net.Conn
125 for i := uint(0); i < 5; i++ {
126 var err error
127 tcpConn, err = net.DialTCP("tcp", nil, &net.TCPAddr{
128 IP: net.IPv4(127, 0, 0, 1),
129 Port: serverPort,
131 if err == nil {
132 break
134 time.Sleep((1 << i) * 5 * time.Millisecond)
136 if tcpConn == nil {
137 close(stdin)
138 out.WriteTo(os.Stdout)
139 cmd.Process.Kill()
140 return nil, nil, nil, cmd.Wait()
143 record := &recordingConn{
144 Conn: tcpConn,
147 return record, cmd, stdin, nil
150 func (test *clientTest) dataPath() string {
151 return filepath.Join("testdata", "Client-"+test.name)
154 func (test *clientTest) loadData() (flows [][]byte, err error) {
155 in, err := os.Open(test.dataPath())
156 if err != nil {
157 return nil, err
159 defer in.Close()
160 return parseTestData(in)
163 func (test *clientTest) run(t *testing.T, write bool) {
164 var clientConn, serverConn net.Conn
165 var recordingConn *recordingConn
166 var childProcess *exec.Cmd
167 var stdin blockingSource
169 if write {
170 var err error
171 recordingConn, childProcess, stdin, err = test.connFromCommand()
172 if err != nil {
173 t.Fatalf("Failed to start subcommand: %s", err)
175 clientConn = recordingConn
176 } else {
177 clientConn, serverConn = net.Pipe()
180 config := test.config
181 if config == nil {
182 config = testConfig
184 client := Client(clientConn, config)
186 doneChan := make(chan bool)
187 go func() {
188 if _, err := client.Write([]byte("hello\n")); err != nil {
189 t.Logf("Client.Write failed: %s", err)
191 client.Close()
192 clientConn.Close()
193 doneChan <- true
196 if !write {
197 flows, err := test.loadData()
198 if err != nil {
199 t.Fatalf("%s: failed to load data from %s", test.name, test.dataPath())
201 for i, b := range flows {
202 if i%2 == 1 {
203 serverConn.Write(b)
204 continue
206 bb := make([]byte, len(b))
207 _, err := io.ReadFull(serverConn, bb)
208 if err != nil {
209 t.Fatalf("%s #%d: %s", test.name, i, err)
211 if !bytes.Equal(b, bb) {
212 t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", test.name, i, bb, b)
215 serverConn.Close()
218 <-doneChan
220 if write {
221 path := test.dataPath()
222 out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
223 if err != nil {
224 t.Fatalf("Failed to create output file: %s", err)
226 defer out.Close()
227 recordingConn.Close()
228 close(stdin)
229 childProcess.Process.Kill()
230 childProcess.Wait()
231 if len(recordingConn.flows) < 3 {
232 childProcess.Stdout.(*bytes.Buffer).WriteTo(os.Stdout)
233 t.Fatalf("Client connection didn't work")
235 recordingConn.WriteTo(out)
236 fmt.Printf("Wrote %s\n", path)
240 func runClientTestForVersion(t *testing.T, template *clientTest, prefix, option string) {
241 test := *template
242 test.name = prefix + test.name
243 if len(test.command) == 0 {
244 test.command = defaultClientCommand
246 test.command = append([]string(nil), test.command...)
247 test.command = append(test.command, option)
248 test.run(t, *update)
251 func runClientTestTLS10(t *testing.T, template *clientTest) {
252 runClientTestForVersion(t, template, "TLSv10-", "-tls1")
255 func runClientTestTLS11(t *testing.T, template *clientTest) {
256 runClientTestForVersion(t, template, "TLSv11-", "-tls1_1")
259 func runClientTestTLS12(t *testing.T, template *clientTest) {
260 runClientTestForVersion(t, template, "TLSv12-", "-tls1_2")
263 func TestHandshakeClientRSARC4(t *testing.T) {
264 test := &clientTest{
265 name: "RSA-RC4",
266 command: []string{"openssl", "s_server", "-cipher", "RC4-SHA"},
268 runClientTestTLS10(t, test)
269 runClientTestTLS11(t, test)
270 runClientTestTLS12(t, test)
273 func TestHandshakeClientECDHERSAAES(t *testing.T) {
274 test := &clientTest{
275 name: "ECDHE-RSA-AES",
276 command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-AES128-SHA"},
278 runClientTestTLS10(t, test)
279 runClientTestTLS11(t, test)
280 runClientTestTLS12(t, test)
283 func TestHandshakeClientECDHEECDSAAES(t *testing.T) {
284 test := &clientTest{
285 name: "ECDHE-ECDSA-AES",
286 command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA"},
287 cert: testECDSACertificate,
288 key: testECDSAPrivateKey,
290 runClientTestTLS10(t, test)
291 runClientTestTLS11(t, test)
292 runClientTestTLS12(t, test)
295 func TestHandshakeClientECDHEECDSAAESGCM(t *testing.T) {
296 test := &clientTest{
297 name: "ECDHE-ECDSA-AES-GCM",
298 command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-GCM-SHA256"},
299 cert: testECDSACertificate,
300 key: testECDSAPrivateKey,
302 runClientTestTLS12(t, test)
305 func TestHandshakeClientCertRSA(t *testing.T) {
306 config := *testConfig
307 cert, _ := X509KeyPair([]byte(clientCertificatePEM), []byte(clientKeyPEM))
308 config.Certificates = []Certificate{cert}
310 test := &clientTest{
311 name: "ClientCert-RSA-RSA",
312 command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"},
313 config: &config,
316 runClientTestTLS10(t, test)
317 runClientTestTLS12(t, test)
319 test = &clientTest{
320 name: "ClientCert-RSA-ECDSA",
321 command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"},
322 config: &config,
323 cert: testECDSACertificate,
324 key: testECDSAPrivateKey,
327 runClientTestTLS10(t, test)
328 runClientTestTLS12(t, test)
331 func TestHandshakeClientCertECDSA(t *testing.T) {
332 config := *testConfig
333 cert, _ := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM))
334 config.Certificates = []Certificate{cert}
336 test := &clientTest{
337 name: "ClientCert-ECDSA-RSA",
338 command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"},
339 config: &config,
342 runClientTestTLS10(t, test)
343 runClientTestTLS12(t, test)
345 test = &clientTest{
346 name: "ClientCert-ECDSA-ECDSA",
347 command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"},
348 config: &config,
349 cert: testECDSACertificate,
350 key: testECDSAPrivateKey,
353 runClientTestTLS10(t, test)
354 runClientTestTLS12(t, test)