blob: 72e8cce2c0df5b1f57d7d075e7b7d3130e7ada7a [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001package main
2
3import (
4 "bytes"
David Benjamina08e49d2014-08-24 01:46:07 -04005 "crypto/ecdsa"
6 "crypto/elliptic"
David Benjamin407a10c2014-07-16 12:58:59 -04007 "crypto/x509"
David Benjamin2561dc32014-08-24 01:25:27 -04008 "encoding/base64"
David Benjamina08e49d2014-08-24 01:46:07 -04009 "encoding/pem"
Adam Langley95c29f32014-06-20 12:00:00 -070010 "flag"
11 "fmt"
12 "io"
Kenny Root7fdeaf12014-08-05 15:23:37 -070013 "io/ioutil"
Adam Langley95c29f32014-06-20 12:00:00 -070014 "net"
15 "os"
16 "os/exec"
David Benjamin884fdf12014-08-02 15:28:23 -040017 "path"
David Benjamin2bc8e6f2014-08-02 15:22:37 -040018 "runtime"
Adam Langley95c29f32014-06-20 12:00:00 -070019 "strings"
20 "sync"
21 "syscall"
22)
23
24var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
25
David Benjamin025b3d32014-07-01 19:53:04 -040026const (
27 rsaCertificateFile = "cert.pem"
28 ecdsaCertificateFile = "ecdsa_cert.pem"
29)
30
31const (
David Benjamina08e49d2014-08-24 01:46:07 -040032 rsaKeyFile = "key.pem"
33 ecdsaKeyFile = "ecdsa_key.pem"
34 channelIDKeyFile = "channel_id_key.pem"
David Benjamin025b3d32014-07-01 19:53:04 -040035)
36
Adam Langley95c29f32014-06-20 12:00:00 -070037var rsaCertificate, ecdsaCertificate Certificate
David Benjamina08e49d2014-08-24 01:46:07 -040038var channelIDKey *ecdsa.PrivateKey
39var channelIDBytes []byte
Adam Langley95c29f32014-06-20 12:00:00 -070040
41func initCertificates() {
42 var err error
David Benjamin025b3d32014-07-01 19:53:04 -040043 rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070044 if err != nil {
45 panic(err)
46 }
47
David Benjamin025b3d32014-07-01 19:53:04 -040048 ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070049 if err != nil {
50 panic(err)
51 }
David Benjamina08e49d2014-08-24 01:46:07 -040052
53 channelIDPEMBlock, err := ioutil.ReadFile(channelIDKeyFile)
54 if err != nil {
55 panic(err)
56 }
57 channelIDDERBlock, _ := pem.Decode(channelIDPEMBlock)
58 if channelIDDERBlock.Type != "EC PRIVATE KEY" {
59 panic("bad key type")
60 }
61 channelIDKey, err = x509.ParseECPrivateKey(channelIDDERBlock.Bytes)
62 if err != nil {
63 panic(err)
64 }
65 if channelIDKey.Curve != elliptic.P256() {
66 panic("bad curve")
67 }
68
69 channelIDBytes = make([]byte, 64)
70 writeIntPadded(channelIDBytes[:32], channelIDKey.X)
71 writeIntPadded(channelIDBytes[32:], channelIDKey.Y)
Adam Langley95c29f32014-06-20 12:00:00 -070072}
73
74var certificateOnce sync.Once
75
76func getRSACertificate() Certificate {
77 certificateOnce.Do(initCertificates)
78 return rsaCertificate
79}
80
81func getECDSACertificate() Certificate {
82 certificateOnce.Do(initCertificates)
83 return ecdsaCertificate
84}
85
David Benjamin025b3d32014-07-01 19:53:04 -040086type testType int
87
88const (
89 clientTest testType = iota
90 serverTest
91)
92
David Benjamin6fd297b2014-08-11 18:43:38 -040093type protocol int
94
95const (
96 tls protocol = iota
97 dtls
98)
99
Adam Langley95c29f32014-06-20 12:00:00 -0700100type testCase struct {
David Benjamin025b3d32014-07-01 19:53:04 -0400101 testType testType
David Benjamin6fd297b2014-08-11 18:43:38 -0400102 protocol protocol
Adam Langley95c29f32014-06-20 12:00:00 -0700103 name string
104 config Config
105 shouldFail bool
106 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -0700107 // expectedLocalError, if not empty, contains a substring that must be
108 // found in the local error.
109 expectedLocalError string
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400110 // expectedVersion, if non-zero, specifies the TLS version that must be
111 // negotiated.
112 expectedVersion uint16
David Benjamina08e49d2014-08-24 01:46:07 -0400113 // expectChannelID controls whether the connection should have
114 // negotiated a Channel ID with channelIDKey.
115 expectChannelID bool
Adam Langley80842bd2014-06-20 12:00:00 -0700116 // messageLen is the length, in bytes, of the test message that will be
117 // sent.
118 messageLen int
David Benjamin025b3d32014-07-01 19:53:04 -0400119 // certFile is the path to the certificate to use for the server.
120 certFile string
121 // keyFile is the path to the private key to use for the server.
122 keyFile string
David Benjamin1d5c83e2014-07-22 19:20:02 -0400123 // resumeSession controls whether a second connection should be tested
124 // which resumes the first session.
125 resumeSession bool
David Benjamin98e882e2014-08-08 13:24:34 -0400126 // sendPrefix sends a prefix on the socket before actually performing a
127 // handshake.
128 sendPrefix string
David Benjamin325b5c32014-07-01 19:40:31 -0400129 // flags, if not empty, contains a list of command-line flags that will
130 // be passed to the shim program.
131 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -0700132}
133
David Benjamin025b3d32014-07-01 19:53:04 -0400134var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700135 {
136 name: "BadRSASignature",
137 config: Config{
138 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
139 Bugs: ProtocolBugs{
140 InvalidSKXSignature: true,
141 },
142 },
143 shouldFail: true,
144 expectedError: ":BAD_SIGNATURE:",
145 },
146 {
147 name: "BadECDSASignature",
148 config: Config{
149 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
150 Bugs: ProtocolBugs{
151 InvalidSKXSignature: true,
152 },
153 Certificates: []Certificate{getECDSACertificate()},
154 },
155 shouldFail: true,
156 expectedError: ":BAD_SIGNATURE:",
157 },
158 {
159 name: "BadECDSACurve",
160 config: Config{
161 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
162 Bugs: ProtocolBugs{
163 InvalidSKXCurve: true,
164 },
165 Certificates: []Certificate{getECDSACertificate()},
166 },
167 shouldFail: true,
168 expectedError: ":WRONG_CURVE:",
169 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700170 {
David Benjamina8e3e0e2014-08-06 22:11:10 -0400171 testType: serverTest,
172 name: "BadRSAVersion",
173 config: Config{
174 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
175 Bugs: ProtocolBugs{
176 RsaClientKeyExchangeVersion: VersionTLS11,
177 },
178 },
179 shouldFail: true,
180 expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
181 },
182 {
David Benjamin325b5c32014-07-01 19:40:31 -0400183 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700184 config: Config{
185 Bugs: ProtocolBugs{
186 FailIfNotFallbackSCSV: true,
187 },
188 },
189 shouldFail: true,
190 expectedLocalError: "no fallback SCSV found",
191 },
David Benjamin325b5c32014-07-01 19:40:31 -0400192 {
David Benjamin2a0c4962014-08-22 23:46:35 -0400193 name: "SendFallbackSCSV",
David Benjamin325b5c32014-07-01 19:40:31 -0400194 config: Config{
195 Bugs: ProtocolBugs{
196 FailIfNotFallbackSCSV: true,
197 },
198 },
199 flags: []string{"-fallback-scsv"},
200 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400201 {
202 testType: serverTest,
David Benjamin35a7a442014-07-05 00:23:20 -0400203 name: "ServerNameExtension",
David Benjamin197b3ab2014-07-02 18:37:33 -0400204 config: Config{
205 ServerName: "example.com",
206 },
207 flags: []string{"-expect-server-name", "example.com"},
208 },
David Benjamin35a7a442014-07-05 00:23:20 -0400209 {
210 testType: clientTest,
211 name: "DuplicateExtensionClient",
212 config: Config{
213 Bugs: ProtocolBugs{
214 DuplicateExtension: true,
215 },
216 },
217 shouldFail: true,
218 expectedLocalError: "remote error: error decoding message",
219 },
220 {
221 testType: serverTest,
222 name: "DuplicateExtensionServer",
223 config: Config{
224 Bugs: ProtocolBugs{
225 DuplicateExtension: true,
226 },
227 },
228 shouldFail: true,
229 expectedLocalError: "remote error: error decoding message",
230 },
David Benjamin7b030512014-07-08 17:30:11 -0400231 {
232 name: "ClientCertificateTypes",
233 config: Config{
234 ClientAuth: RequestClientCert,
235 ClientCertificateTypes: []byte{
236 CertTypeDSSSign,
237 CertTypeRSASign,
238 CertTypeECDSASign,
239 },
240 },
David Benjamin2561dc32014-08-24 01:25:27 -0400241 flags: []string{
242 "-expect-certificate-types",
243 base64.StdEncoding.EncodeToString([]byte{
244 CertTypeDSSSign,
245 CertTypeRSASign,
246 CertTypeECDSASign,
247 }),
248 },
David Benjamin7b030512014-07-08 17:30:11 -0400249 },
David Benjamin636293b2014-07-08 17:59:18 -0400250 {
251 name: "NoClientCertificate",
252 config: Config{
253 ClientAuth: RequireAnyClientCert,
254 },
255 shouldFail: true,
256 expectedLocalError: "client didn't provide a certificate",
257 },
David Benjamin1c375dd2014-07-12 00:48:23 -0400258 {
259 name: "UnauthenticatedECDH",
260 config: Config{
261 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
262 Bugs: ProtocolBugs{
263 UnauthenticatedECDH: true,
264 },
265 },
266 shouldFail: true,
David Benjamine8f3d662014-07-12 01:10:19 -0400267 expectedError: ":UNEXPECTED_MESSAGE:",
David Benjamin1c375dd2014-07-12 00:48:23 -0400268 },
David Benjamin9c651c92014-07-12 13:27:45 -0400269 {
270 name: "SkipServerKeyExchange",
271 config: Config{
272 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
273 Bugs: ProtocolBugs{
274 SkipServerKeyExchange: true,
275 },
276 },
277 shouldFail: true,
278 expectedError: ":UNEXPECTED_MESSAGE:",
279 },
David Benjamin1f5f62b2014-07-12 16:18:02 -0400280 {
David Benjamina0e52232014-07-19 17:39:58 -0400281 name: "SkipChangeCipherSpec-Client",
282 config: Config{
283 Bugs: ProtocolBugs{
284 SkipChangeCipherSpec: true,
285 },
286 },
287 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400288 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400289 },
290 {
291 testType: serverTest,
292 name: "SkipChangeCipherSpec-Server",
293 config: Config{
294 Bugs: ProtocolBugs{
295 SkipChangeCipherSpec: true,
296 },
297 },
298 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400299 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400300 },
David Benjamin42be6452014-07-21 14:50:23 -0400301 {
302 testType: serverTest,
303 name: "SkipChangeCipherSpec-Server-NPN",
304 config: Config{
305 NextProtos: []string{"bar"},
306 Bugs: ProtocolBugs{
307 SkipChangeCipherSpec: true,
308 },
309 },
310 flags: []string{
311 "-advertise-npn", "\x03foo\x03bar\x03baz",
312 },
313 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400314 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
315 },
316 {
317 name: "FragmentAcrossChangeCipherSpec-Client",
318 config: Config{
319 Bugs: ProtocolBugs{
320 FragmentAcrossChangeCipherSpec: true,
321 },
322 },
323 shouldFail: true,
324 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
325 },
326 {
327 testType: serverTest,
328 name: "FragmentAcrossChangeCipherSpec-Server",
329 config: Config{
330 Bugs: ProtocolBugs{
331 FragmentAcrossChangeCipherSpec: true,
332 },
333 },
334 shouldFail: true,
335 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
336 },
337 {
338 testType: serverTest,
339 name: "FragmentAcrossChangeCipherSpec-Server-NPN",
340 config: Config{
341 NextProtos: []string{"bar"},
342 Bugs: ProtocolBugs{
343 FragmentAcrossChangeCipherSpec: true,
344 },
345 },
346 flags: []string{
347 "-advertise-npn", "\x03foo\x03bar\x03baz",
348 },
349 shouldFail: true,
350 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamin42be6452014-07-21 14:50:23 -0400351 },
David Benjaminf3ec83d2014-07-21 22:42:34 -0400352 {
353 testType: serverTest,
354 name: "EarlyChangeCipherSpec-server-1",
355 config: Config{
356 Bugs: ProtocolBugs{
357 EarlyChangeCipherSpec: 1,
358 },
359 },
360 shouldFail: true,
361 expectedError: ":CCS_RECEIVED_EARLY:",
362 },
363 {
364 testType: serverTest,
365 name: "EarlyChangeCipherSpec-server-2",
366 config: Config{
367 Bugs: ProtocolBugs{
368 EarlyChangeCipherSpec: 2,
369 },
370 },
371 shouldFail: true,
372 expectedError: ":CCS_RECEIVED_EARLY:",
373 },
David Benjamind23f4122014-07-23 15:09:48 -0400374 {
David Benjamind23f4122014-07-23 15:09:48 -0400375 name: "SkipNewSessionTicket",
376 config: Config{
377 Bugs: ProtocolBugs{
378 SkipNewSessionTicket: true,
379 },
380 },
381 shouldFail: true,
382 expectedError: ":CCS_RECEIVED_EARLY:",
383 },
David Benjamin7e3305e2014-07-28 14:52:32 -0400384 {
David Benjamind86c7672014-08-02 04:07:12 -0400385 testType: serverTest,
David Benjaminbef270a2014-08-02 04:22:02 -0400386 name: "FallbackSCSV",
387 config: Config{
388 MaxVersion: VersionTLS11,
389 Bugs: ProtocolBugs{
390 SendFallbackSCSV: true,
391 },
392 },
393 shouldFail: true,
394 expectedError: ":INAPPROPRIATE_FALLBACK:",
395 },
396 {
397 testType: serverTest,
398 name: "FallbackSCSV-VersionMatch",
399 config: Config{
400 Bugs: ProtocolBugs{
401 SendFallbackSCSV: true,
402 },
403 },
404 },
David Benjamin98214542014-08-07 18:02:39 -0400405 {
406 testType: serverTest,
407 name: "FragmentedClientVersion",
408 config: Config{
409 Bugs: ProtocolBugs{
410 MaxHandshakeRecordLength: 1,
411 FragmentClientVersion: true,
412 },
413 },
414 shouldFail: true,
415 expectedError: ":RECORD_TOO_SMALL:",
416 },
David Benjamin98e882e2014-08-08 13:24:34 -0400417 {
418 testType: serverTest,
419 name: "MinorVersionTolerance",
420 config: Config{
421 Bugs: ProtocolBugs{
422 SendClientVersion: 0x03ff,
423 },
424 },
425 expectedVersion: VersionTLS12,
426 },
427 {
428 testType: serverTest,
429 name: "MajorVersionTolerance",
430 config: Config{
431 Bugs: ProtocolBugs{
432 SendClientVersion: 0x0400,
433 },
434 },
435 expectedVersion: VersionTLS12,
436 },
437 {
438 testType: serverTest,
439 name: "VersionTooLow",
440 config: Config{
441 Bugs: ProtocolBugs{
442 SendClientVersion: 0x0200,
443 },
444 },
445 shouldFail: true,
446 expectedError: ":UNSUPPORTED_PROTOCOL:",
447 },
448 {
449 testType: serverTest,
450 name: "HttpGET",
451 sendPrefix: "GET / HTTP/1.0\n",
452 shouldFail: true,
453 expectedError: ":HTTP_REQUEST:",
454 },
455 {
456 testType: serverTest,
457 name: "HttpPOST",
458 sendPrefix: "POST / HTTP/1.0\n",
459 shouldFail: true,
460 expectedError: ":HTTP_REQUEST:",
461 },
462 {
463 testType: serverTest,
464 name: "HttpHEAD",
465 sendPrefix: "HEAD / HTTP/1.0\n",
466 shouldFail: true,
467 expectedError: ":HTTP_REQUEST:",
468 },
469 {
470 testType: serverTest,
471 name: "HttpPUT",
472 sendPrefix: "PUT / HTTP/1.0\n",
473 shouldFail: true,
474 expectedError: ":HTTP_REQUEST:",
475 },
476 {
477 testType: serverTest,
478 name: "HttpCONNECT",
479 sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n",
480 shouldFail: true,
481 expectedError: ":HTTPS_PROXY_REQUEST:",
482 },
Adam Langley95c29f32014-06-20 12:00:00 -0700483}
484
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400485func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) error {
David Benjamin6fd297b2014-08-11 18:43:38 -0400486 if test.protocol == dtls {
487 conn = newPacketAdaptor(conn)
488 }
489
490 if test.sendPrefix != "" {
491 if _, err := conn.Write([]byte(test.sendPrefix)); err != nil {
492 return err
493 }
David Benjamin98e882e2014-08-08 13:24:34 -0400494 }
495
David Benjamin1d5c83e2014-07-22 19:20:02 -0400496 var tlsConn *Conn
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400497 if test.testType == clientTest {
David Benjamin6fd297b2014-08-11 18:43:38 -0400498 if test.protocol == dtls {
499 tlsConn = DTLSServer(conn, config)
500 } else {
501 tlsConn = Server(conn, config)
502 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400503 } else {
504 config.InsecureSkipVerify = true
David Benjamin6fd297b2014-08-11 18:43:38 -0400505 if test.protocol == dtls {
506 tlsConn = DTLSClient(conn, config)
507 } else {
508 tlsConn = Client(conn, config)
509 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400510 }
511
Adam Langley95c29f32014-06-20 12:00:00 -0700512 if err := tlsConn.Handshake(); err != nil {
513 return err
514 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700515
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400516 if vers := tlsConn.ConnectionState().Version; test.expectedVersion != 0 && vers != test.expectedVersion {
517 return fmt.Errorf("got version %x, expected %x", vers, test.expectedVersion)
518 }
519
David Benjamina08e49d2014-08-24 01:46:07 -0400520 if test.expectChannelID {
521 channelID := tlsConn.ConnectionState().ChannelID
522 if channelID == nil {
523 return fmt.Errorf("no channel ID negotiated")
524 }
525 if channelID.Curve != channelIDKey.Curve ||
526 channelIDKey.X.Cmp(channelIDKey.X) != 0 ||
527 channelIDKey.Y.Cmp(channelIDKey.Y) != 0 {
528 return fmt.Errorf("incorrect channel ID")
529 }
530 }
531
Kenny Root7fdeaf12014-08-05 15:23:37 -0700532 if messageLen < 0 {
David Benjamin6fd297b2014-08-11 18:43:38 -0400533 if test.protocol == dtls {
534 return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
535 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700536 // Read until EOF.
537 _, err := io.Copy(ioutil.Discard, tlsConn)
538 return err
539 }
540
Adam Langley80842bd2014-06-20 12:00:00 -0700541 if messageLen == 0 {
542 messageLen = 32
543 }
544 testMessage := make([]byte, messageLen)
545 for i := range testMessage {
546 testMessage[i] = 0x42
547 }
Adam Langley95c29f32014-06-20 12:00:00 -0700548 tlsConn.Write(testMessage)
549
550 buf := make([]byte, len(testMessage))
David Benjamin6fd297b2014-08-11 18:43:38 -0400551 if test.protocol == dtls {
552 bufTmp := make([]byte, len(buf)+1)
553 n, err := tlsConn.Read(bufTmp)
554 if err != nil {
555 return err
556 }
557 if n != len(buf) {
558 return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
559 }
560 copy(buf, bufTmp)
561 } else {
562 _, err := io.ReadFull(tlsConn, buf)
563 if err != nil {
564 return err
565 }
Adam Langley95c29f32014-06-20 12:00:00 -0700566 }
567
568 for i, v := range buf {
569 if v != testMessage[i]^0xff {
570 return fmt.Errorf("bad reply contents at byte %d", i)
571 }
572 }
573
574 return nil
575}
576
David Benjamin325b5c32014-07-01 19:40:31 -0400577func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
578 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700579 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400580 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700581 }
David Benjamin325b5c32014-07-01 19:40:31 -0400582 valgrindArgs = append(valgrindArgs, path)
583 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700584
David Benjamin325b5c32014-07-01 19:40:31 -0400585 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700586}
587
David Benjamin325b5c32014-07-01 19:40:31 -0400588func gdbOf(path string, args ...string) *exec.Cmd {
589 xtermArgs := []string{"-e", "gdb", "--args"}
590 xtermArgs = append(xtermArgs, path)
591 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700592
David Benjamin325b5c32014-07-01 19:40:31 -0400593 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700594}
595
David Benjamin1d5c83e2014-07-22 19:20:02 -0400596func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700597 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
598 if err != nil {
599 panic(err)
600 }
601
602 syscall.CloseOnExec(socks[0])
603 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400604 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700605 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400606 conn, err = net.FileConn(connFile)
607 if err != nil {
608 panic(err)
609 }
Adam Langley95c29f32014-06-20 12:00:00 -0700610 connFile.Close()
611 if err != nil {
612 panic(err)
613 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400614 return shimEnd, conn
615}
616
David Benjamin884fdf12014-08-02 15:28:23 -0400617func runTest(test *testCase, buildDir string) error {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400618 shimEnd, conn := openSocketPair()
619 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700620
David Benjamin884fdf12014-08-02 15:28:23 -0400621 shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
David Benjamin5a593af2014-08-11 19:51:50 -0400622 var flags []string
David Benjamin1d5c83e2014-07-22 19:20:02 -0400623 if test.testType == serverTest {
David Benjamin5a593af2014-08-11 19:51:50 -0400624 flags = append(flags, "-server")
625
David Benjamin025b3d32014-07-01 19:53:04 -0400626 flags = append(flags, "-key-file")
627 if test.keyFile == "" {
628 flags = append(flags, rsaKeyFile)
629 } else {
630 flags = append(flags, test.keyFile)
631 }
632
633 flags = append(flags, "-cert-file")
634 if test.certFile == "" {
635 flags = append(flags, rsaCertificateFile)
636 } else {
637 flags = append(flags, test.certFile)
638 }
639 }
David Benjamin5a593af2014-08-11 19:51:50 -0400640
David Benjamin6fd297b2014-08-11 18:43:38 -0400641 if test.protocol == dtls {
642 flags = append(flags, "-dtls")
643 }
644
David Benjamin5a593af2014-08-11 19:51:50 -0400645 if test.resumeSession {
646 flags = append(flags, "-resume")
647 }
648
David Benjamin025b3d32014-07-01 19:53:04 -0400649 flags = append(flags, test.flags...)
650
651 var shim *exec.Cmd
652 if *useValgrind {
653 shim = valgrindOf(false, shim_path, flags...)
654 } else {
655 shim = exec.Command(shim_path, flags...)
656 }
657 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400658 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400659 shim.Stdin = os.Stdin
660 var stdoutBuf, stderrBuf bytes.Buffer
661 shim.Stdout = &stdoutBuf
662 shim.Stderr = &stderrBuf
663
664 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700665 panic(err)
666 }
David Benjamin025b3d32014-07-01 19:53:04 -0400667 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400668 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700669
670 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400671 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400672 if test.testType == clientTest {
673 if len(config.Certificates) == 0 {
674 config.Certificates = []Certificate{getRSACertificate()}
675 }
David Benjamin025b3d32014-07-01 19:53:04 -0400676 }
Adam Langley95c29f32014-06-20 12:00:00 -0700677
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400678 err := doExchange(test, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700679 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400680 if err == nil && test.resumeSession {
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400681 err = doExchange(test, &config, connResume, test.messageLen)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400682 connResume.Close()
683 }
684
David Benjamin025b3d32014-07-01 19:53:04 -0400685 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700686
687 stdout := string(stdoutBuf.Bytes())
688 stderr := string(stderrBuf.Bytes())
689 failed := err != nil || childErr != nil
690 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700691 localError := "none"
692 if err != nil {
693 localError = err.Error()
694 }
695 if len(test.expectedLocalError) != 0 {
696 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
697 }
Adam Langley95c29f32014-06-20 12:00:00 -0700698
699 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700700 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700701 if childErr != nil {
702 childError = childErr.Error()
703 }
704
705 var msg string
706 switch {
707 case failed && !test.shouldFail:
708 msg = "unexpected failure"
709 case !failed && test.shouldFail:
710 msg = "unexpected success"
711 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700712 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700713 default:
714 panic("internal error")
715 }
716
717 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
718 }
719
720 if !*useValgrind && len(stderr) > 0 {
721 println(stderr)
722 }
723
724 return nil
725}
726
727var tlsVersions = []struct {
728 name string
729 version uint16
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400730 flag string
Adam Langley95c29f32014-06-20 12:00:00 -0700731}{
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400732 {"SSL3", VersionSSL30, "-no-ssl3"},
733 {"TLS1", VersionTLS10, "-no-tls1"},
734 {"TLS11", VersionTLS11, "-no-tls11"},
735 {"TLS12", VersionTLS12, "-no-tls12"},
Adam Langley95c29f32014-06-20 12:00:00 -0700736}
737
738var testCipherSuites = []struct {
739 name string
740 id uint16
741}{
742 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400743 {"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700744 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400745 {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700746 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400747 {"DHE-RSA-AES128-GCM", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
748 {"DHE-RSA-AES128-SHA", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
749 {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
750 {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700751 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
752 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
753 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
754 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700755 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700756 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400757 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700758 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
759 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700760 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400761 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700762}
763
764func addCipherSuiteTests() {
765 for _, suite := range testCipherSuites {
766 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400767 var certFile string
768 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700769 if strings.Contains(suite.name, "ECDSA") {
770 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400771 certFile = ecdsaCertificateFile
772 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700773 } else {
774 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400775 certFile = rsaCertificateFile
776 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700777 }
778
779 for _, ver := range tlsVersions {
780 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
781 continue
782 }
783
David Benjamin1d5c83e2014-07-22 19:20:02 -0400784 // Go's TLS implementation only implements session
785 // resumption with tickets, so SSLv3 cannot resume
786 // sessions.
787 resumeSession := ver.version != VersionSSL30
788
David Benjamin025b3d32014-07-01 19:53:04 -0400789 testCases = append(testCases, testCase{
790 testType: clientTest,
791 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700792 config: Config{
793 MinVersion: ver.version,
794 MaxVersion: ver.version,
795 CipherSuites: []uint16{suite.id},
796 Certificates: []Certificate{cert},
797 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400798 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700799 })
David Benjamin025b3d32014-07-01 19:53:04 -0400800
David Benjamin76d8abe2014-08-14 16:25:34 -0400801 testCases = append(testCases, testCase{
802 testType: serverTest,
803 name: ver.name + "-" + suite.name + "-server",
804 config: Config{
805 MinVersion: ver.version,
806 MaxVersion: ver.version,
807 CipherSuites: []uint16{suite.id},
808 Certificates: []Certificate{cert},
809 },
810 certFile: certFile,
811 keyFile: keyFile,
812 resumeSession: resumeSession,
813 })
David Benjamin6fd297b2014-08-11 18:43:38 -0400814
815 // TODO(davidben): Fix DTLS 1.2 support and test that.
816 if ver.version == VersionTLS10 && strings.Index(suite.name, "RC4") == -1 {
817 testCases = append(testCases, testCase{
818 testType: clientTest,
819 protocol: dtls,
820 name: "D" + ver.name + "-" + suite.name + "-client",
821 config: Config{
822 MinVersion: ver.version,
823 MaxVersion: ver.version,
824 CipherSuites: []uint16{suite.id},
825 Certificates: []Certificate{cert},
826 },
827 resumeSession: resumeSession,
828 })
829 testCases = append(testCases, testCase{
830 testType: serverTest,
831 protocol: dtls,
832 name: "D" + ver.name + "-" + suite.name + "-server",
833 config: Config{
834 MinVersion: ver.version,
835 MaxVersion: ver.version,
836 CipherSuites: []uint16{suite.id},
837 Certificates: []Certificate{cert},
838 },
839 certFile: certFile,
840 keyFile: keyFile,
841 resumeSession: resumeSession,
842 })
843 }
Adam Langley95c29f32014-06-20 12:00:00 -0700844 }
845 }
846}
847
848func addBadECDSASignatureTests() {
849 for badR := BadValue(1); badR < NumBadValues; badR++ {
850 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400851 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700852 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
853 config: Config{
854 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
855 Certificates: []Certificate{getECDSACertificate()},
856 Bugs: ProtocolBugs{
857 BadECDSAR: badR,
858 BadECDSAS: badS,
859 },
860 },
861 shouldFail: true,
862 expectedError: "SIGNATURE",
863 })
864 }
865 }
866}
867
Adam Langley80842bd2014-06-20 12:00:00 -0700868func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400869 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700870 name: "MaxCBCPadding",
871 config: Config{
872 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
873 Bugs: ProtocolBugs{
874 MaxPadding: true,
875 },
876 },
877 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
878 })
David Benjamin025b3d32014-07-01 19:53:04 -0400879 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700880 name: "BadCBCPadding",
881 config: Config{
882 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
883 Bugs: ProtocolBugs{
884 PaddingFirstByteBad: true,
885 },
886 },
887 shouldFail: true,
888 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
889 })
890 // OpenSSL previously had an issue where the first byte of padding in
891 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400892 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700893 name: "BadCBCPadding255",
894 config: Config{
895 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
896 Bugs: ProtocolBugs{
897 MaxPadding: true,
898 PaddingFirstByteBadIf255: true,
899 },
900 },
901 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
902 shouldFail: true,
903 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
904 })
905}
906
Kenny Root7fdeaf12014-08-05 15:23:37 -0700907func addCBCSplittingTests() {
908 testCases = append(testCases, testCase{
909 name: "CBCRecordSplitting",
910 config: Config{
911 MaxVersion: VersionTLS10,
912 MinVersion: VersionTLS10,
913 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
914 },
915 messageLen: -1, // read until EOF
916 flags: []string{
917 "-async",
918 "-write-different-record-sizes",
919 "-cbc-record-splitting",
920 },
David Benjamina8e3e0e2014-08-06 22:11:10 -0400921 })
922 testCases = append(testCases, testCase{
Kenny Root7fdeaf12014-08-05 15:23:37 -0700923 name: "CBCRecordSplittingPartialWrite",
924 config: Config{
925 MaxVersion: VersionTLS10,
926 MinVersion: VersionTLS10,
927 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
928 },
929 messageLen: -1, // read until EOF
930 flags: []string{
931 "-async",
932 "-write-different-record-sizes",
933 "-cbc-record-splitting",
934 "-partial-write",
935 },
936 })
937}
938
David Benjamin636293b2014-07-08 17:59:18 -0400939func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400940 // Add a dummy cert pool to stress certificate authority parsing.
941 // TODO(davidben): Add tests that those values parse out correctly.
942 certPool := x509.NewCertPool()
943 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
944 if err != nil {
945 panic(err)
946 }
947 certPool.AddCert(cert)
948
David Benjamin636293b2014-07-08 17:59:18 -0400949 for _, ver := range tlsVersions {
950 if ver.version == VersionSSL30 {
951 // TODO(davidben): The Go implementation does not
952 // correctly compute CertificateVerify hashes for SSLv3.
953 continue
954 }
955
956 var cipherSuites []uint16
957 if ver.version >= VersionTLS12 {
958 // Pick a SHA-256 cipher suite. The Go implementation
959 // does not correctly handle client auth with a SHA-384
960 // cipher suite.
961 cipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
962 }
963
964 testCases = append(testCases, testCase{
965 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400966 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400967 config: Config{
968 MinVersion: ver.version,
969 MaxVersion: ver.version,
970 CipherSuites: cipherSuites,
971 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400972 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400973 },
974 flags: []string{
975 "-cert-file", rsaCertificateFile,
976 "-key-file", rsaKeyFile,
977 },
978 })
979 testCases = append(testCases, testCase{
980 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400981 name: ver.name + "-Client-ClientAuth-ECDSA",
David Benjamin636293b2014-07-08 17:59:18 -0400982 config: Config{
983 MinVersion: ver.version,
984 MaxVersion: ver.version,
985 CipherSuites: cipherSuites,
986 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400987 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400988 },
989 flags: []string{
990 "-cert-file", ecdsaCertificateFile,
991 "-key-file", ecdsaKeyFile,
992 },
993 })
David Benjamin67666e72014-07-12 15:47:52 -0400994 testCases = append(testCases, testCase{
995 testType: serverTest,
996 name: ver.name + "-Server-ClientAuth-RSA",
997 config: Config{
998 Certificates: []Certificate{rsaCertificate},
999 },
1000 flags: []string{"-require-any-client-certificate"},
1001 })
1002 testCases = append(testCases, testCase{
1003 testType: serverTest,
1004 name: ver.name + "-Server-ClientAuth-ECDSA",
1005 config: Config{
1006 Certificates: []Certificate{ecdsaCertificate},
1007 },
1008 flags: []string{"-require-any-client-certificate"},
1009 })
David Benjamin636293b2014-07-08 17:59:18 -04001010 }
1011}
1012
David Benjamin43ec06f2014-08-05 02:28:57 -04001013// Adds tests that try to cover the range of the handshake state machine, under
1014// various conditions. Some of these are redundant with other tests, but they
1015// only cover the synchronous case.
David Benjamin6fd297b2014-08-11 18:43:38 -04001016func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) {
David Benjamin43ec06f2014-08-05 02:28:57 -04001017 var suffix string
1018 var flags []string
1019 var maxHandshakeRecordLength int
David Benjamin6fd297b2014-08-11 18:43:38 -04001020 if protocol == dtls {
1021 suffix = "-DTLS"
1022 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001023 if async {
David Benjamin6fd297b2014-08-11 18:43:38 -04001024 suffix += "-Async"
David Benjamin43ec06f2014-08-05 02:28:57 -04001025 flags = append(flags, "-async")
1026 } else {
David Benjamin6fd297b2014-08-11 18:43:38 -04001027 suffix += "-Sync"
David Benjamin43ec06f2014-08-05 02:28:57 -04001028 }
1029 if splitHandshake {
1030 suffix += "-SplitHandshakeRecords"
David Benjamin98214542014-08-07 18:02:39 -04001031 maxHandshakeRecordLength = 1
David Benjamin43ec06f2014-08-05 02:28:57 -04001032 }
1033
1034 // Basic handshake, with resumption. Client and server.
1035 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001036 protocol: protocol,
1037 name: "Basic-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001038 config: Config{
1039 Bugs: ProtocolBugs{
1040 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1041 },
1042 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001043 flags: flags,
1044 resumeSession: true,
1045 })
1046 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001047 protocol: protocol,
1048 name: "Basic-Client-RenewTicket" + suffix,
David Benjaminbed9aae2014-08-07 19:13:38 -04001049 config: Config{
1050 Bugs: ProtocolBugs{
1051 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1052 RenewTicketOnResume: true,
1053 },
1054 },
1055 flags: flags,
1056 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001057 })
1058 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001059 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001060 testType: serverTest,
1061 name: "Basic-Server" + suffix,
1062 config: Config{
1063 Bugs: ProtocolBugs{
1064 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1065 },
1066 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001067 flags: flags,
1068 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001069 })
1070
David Benjamin6fd297b2014-08-11 18:43:38 -04001071 // TLS client auth.
1072 testCases = append(testCases, testCase{
1073 protocol: protocol,
1074 testType: clientTest,
1075 name: "ClientAuth-Client" + suffix,
1076 config: Config{
1077 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
1078 ClientAuth: RequireAnyClientCert,
1079 Bugs: ProtocolBugs{
1080 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1081 },
1082 },
1083 flags: append(flags,
1084 "-cert-file", rsaCertificateFile,
1085 "-key-file", rsaKeyFile),
1086 })
1087 testCases = append(testCases, testCase{
1088 protocol: protocol,
1089 testType: serverTest,
1090 name: "ClientAuth-Server" + suffix,
1091 config: Config{
1092 Certificates: []Certificate{rsaCertificate},
1093 },
1094 flags: append(flags, "-require-any-client-certificate"),
1095 })
1096
David Benjamin43ec06f2014-08-05 02:28:57 -04001097 // No session ticket support; server doesn't send NewSessionTicket.
1098 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001099 protocol: protocol,
1100 name: "SessionTicketsDisabled-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001101 config: Config{
1102 SessionTicketsDisabled: true,
1103 Bugs: ProtocolBugs{
1104 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1105 },
1106 },
1107 flags: flags,
1108 })
1109 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001110 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001111 testType: serverTest,
1112 name: "SessionTicketsDisabled-Server" + suffix,
1113 config: Config{
1114 SessionTicketsDisabled: true,
1115 Bugs: ProtocolBugs{
1116 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1117 },
1118 },
1119 flags: flags,
1120 })
1121
David Benjamin6fd297b2014-08-11 18:43:38 -04001122 if protocol == tls {
1123 // NPN on client and server; results in post-handshake message.
1124 testCases = append(testCases, testCase{
1125 protocol: protocol,
1126 name: "NPN-Client" + suffix,
1127 config: Config{
1128 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1129 NextProtos: []string{"foo"},
1130 Bugs: ProtocolBugs{
1131 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1132 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001133 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001134 flags: append(flags, "-select-next-proto", "foo"),
1135 })
1136 testCases = append(testCases, testCase{
1137 protocol: protocol,
1138 testType: serverTest,
1139 name: "NPN-Server" + suffix,
1140 config: Config{
1141 NextProtos: []string{"bar"},
1142 Bugs: ProtocolBugs{
1143 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1144 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001145 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001146 flags: append(flags,
1147 "-advertise-npn", "\x03foo\x03bar\x03baz",
1148 "-expect-next-proto", "bar"),
1149 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001150
David Benjamin6fd297b2014-08-11 18:43:38 -04001151 // Client does False Start and negotiates NPN.
1152 testCases = append(testCases, testCase{
1153 protocol: protocol,
1154 name: "FalseStart" + suffix,
1155 config: Config{
1156 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1157 NextProtos: []string{"foo"},
1158 Bugs: ProtocolBugs{
1159 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1160 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001161 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001162 flags: append(flags,
1163 "-false-start",
1164 "-select-next-proto", "foo"),
1165 resumeSession: true,
1166 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001167
David Benjamin6fd297b2014-08-11 18:43:38 -04001168 // False Start without session tickets.
1169 testCases = append(testCases, testCase{
1170 name: "FalseStart-SessionTicketsDisabled",
1171 config: Config{
1172 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1173 NextProtos: []string{"foo"},
1174 SessionTicketsDisabled: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001175 Bugs: ProtocolBugs{
1176 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1177 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001178 },
David Benjamin4e99c522014-08-24 01:45:30 -04001179 flags: append(flags,
David Benjamin6fd297b2014-08-11 18:43:38 -04001180 "-false-start",
1181 "-select-next-proto", "foo",
David Benjamin4e99c522014-08-24 01:45:30 -04001182 ),
David Benjamin6fd297b2014-08-11 18:43:38 -04001183 })
David Benjamin1e7f8d72014-08-08 12:27:04 -04001184
David Benjamina08e49d2014-08-24 01:46:07 -04001185 // Server parses a V2ClientHello.
David Benjamin6fd297b2014-08-11 18:43:38 -04001186 testCases = append(testCases, testCase{
1187 protocol: protocol,
1188 testType: serverTest,
1189 name: "SendV2ClientHello" + suffix,
1190 config: Config{
1191 // Choose a cipher suite that does not involve
1192 // elliptic curves, so no extensions are
1193 // involved.
1194 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
1195 Bugs: ProtocolBugs{
1196 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1197 SendV2ClientHello: true,
1198 },
David Benjamin1e7f8d72014-08-08 12:27:04 -04001199 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001200 flags: flags,
1201 })
David Benjamina08e49d2014-08-24 01:46:07 -04001202
1203 // Client sends a Channel ID.
1204 testCases = append(testCases, testCase{
1205 protocol: protocol,
1206 name: "ChannelID-Client" + suffix,
1207 config: Config{
1208 RequestChannelID: true,
1209 Bugs: ProtocolBugs{
1210 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1211 },
1212 },
1213 flags: append(flags,
1214 "-send-channel-id", channelIDKeyFile,
1215 ),
1216 resumeSession: true,
1217 expectChannelID: true,
1218 })
1219
1220 // Server accepts a Channel ID.
1221 testCases = append(testCases, testCase{
1222 protocol: protocol,
1223 testType: serverTest,
1224 name: "ChannelID-Server" + suffix,
1225 config: Config{
1226 ChannelID: channelIDKey,
1227 Bugs: ProtocolBugs{
1228 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1229 },
1230 },
1231 flags: append(flags,
1232 "-expect-channel-id",
1233 base64.StdEncoding.EncodeToString(channelIDBytes),
1234 ),
1235 resumeSession: true,
1236 expectChannelID: true,
1237 })
David Benjamin6fd297b2014-08-11 18:43:38 -04001238 } else {
1239 testCases = append(testCases, testCase{
1240 protocol: protocol,
1241 name: "SkipHelloVerifyRequest" + suffix,
1242 config: Config{
1243 Bugs: ProtocolBugs{
1244 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1245 SkipHelloVerifyRequest: true,
1246 },
1247 },
1248 flags: flags,
1249 })
1250
1251 testCases = append(testCases, testCase{
1252 testType: serverTest,
1253 protocol: protocol,
1254 name: "CookieExchange" + suffix,
1255 config: Config{
1256 Bugs: ProtocolBugs{
1257 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1258 },
1259 },
1260 flags: append(flags, "-cookie-exchange"),
1261 })
1262 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001263}
1264
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001265func addVersionNegotiationTests() {
1266 for i, shimVers := range tlsVersions {
1267 // Assemble flags to disable all newer versions on the shim.
1268 var flags []string
1269 for _, vers := range tlsVersions[i+1:] {
1270 flags = append(flags, vers.flag)
1271 }
1272
1273 for _, runnerVers := range tlsVersions {
1274 expectedVersion := shimVers.version
1275 if runnerVers.version < shimVers.version {
1276 expectedVersion = runnerVers.version
1277 }
1278 suffix := shimVers.name + "-" + runnerVers.name
1279
1280 testCases = append(testCases, testCase{
1281 testType: clientTest,
1282 name: "VersionNegotiation-Client-" + suffix,
1283 config: Config{
1284 MaxVersion: runnerVers.version,
1285 },
1286 flags: flags,
1287 expectedVersion: expectedVersion,
1288 })
1289
David Benjamin76d8abe2014-08-14 16:25:34 -04001290 testCases = append(testCases, testCase{
1291 testType: serverTest,
1292 name: "VersionNegotiation-Server-" + suffix,
1293 config: Config{
1294 MaxVersion: runnerVers.version,
1295 },
1296 flags: flags,
1297 expectedVersion: expectedVersion,
1298 })
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001299 }
1300 }
1301}
1302
David Benjamin884fdf12014-08-02 15:28:23 -04001303func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
Adam Langley95c29f32014-06-20 12:00:00 -07001304 defer wg.Done()
1305
1306 for test := range c {
1307 statusChan <- statusMsg{test: test, started: true}
David Benjamin884fdf12014-08-02 15:28:23 -04001308 err := runTest(test, buildDir)
Adam Langley95c29f32014-06-20 12:00:00 -07001309 statusChan <- statusMsg{test: test, err: err}
1310 }
1311}
1312
1313type statusMsg struct {
1314 test *testCase
1315 started bool
1316 err error
1317}
1318
1319func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
1320 var started, done, failed, lineLen int
1321 defer close(doneChan)
1322
1323 for msg := range statusChan {
1324 if msg.started {
1325 started++
1326 } else {
1327 done++
1328 }
1329
1330 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
1331
1332 if msg.err != nil {
1333 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
1334 failed++
1335 }
1336 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
1337 lineLen = len(line)
1338 os.Stdout.WriteString(line)
1339 }
1340}
1341
1342func main() {
1343 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001344 var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
David Benjamin884fdf12014-08-02 15:28:23 -04001345 var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.")
Adam Langley95c29f32014-06-20 12:00:00 -07001346
1347 flag.Parse()
1348
1349 addCipherSuiteTests()
1350 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -07001351 addCBCPaddingTests()
Kenny Root7fdeaf12014-08-05 15:23:37 -07001352 addCBCSplittingTests()
David Benjamin636293b2014-07-08 17:59:18 -04001353 addClientAuthTests()
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001354 addVersionNegotiationTests()
David Benjamin43ec06f2014-08-05 02:28:57 -04001355 for _, async := range []bool{false, true} {
1356 for _, splitHandshake := range []bool{false, true} {
David Benjamin6fd297b2014-08-11 18:43:38 -04001357 for _, protocol := range []protocol{tls, dtls} {
1358 addStateMachineCoverageTests(async, splitHandshake, protocol)
1359 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001360 }
1361 }
Adam Langley95c29f32014-06-20 12:00:00 -07001362
1363 var wg sync.WaitGroup
1364
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001365 numWorkers := *flagNumWorkers
Adam Langley95c29f32014-06-20 12:00:00 -07001366
1367 statusChan := make(chan statusMsg, numWorkers)
1368 testChan := make(chan *testCase, numWorkers)
1369 doneChan := make(chan struct{})
1370
David Benjamin025b3d32014-07-01 19:53:04 -04001371 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -07001372
1373 for i := 0; i < numWorkers; i++ {
1374 wg.Add(1)
David Benjamin884fdf12014-08-02 15:28:23 -04001375 go worker(statusChan, testChan, *flagBuildDir, &wg)
Adam Langley95c29f32014-06-20 12:00:00 -07001376 }
1377
David Benjamin025b3d32014-07-01 19:53:04 -04001378 for i := range testCases {
1379 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
1380 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -07001381 }
1382 }
1383
1384 close(testChan)
1385 wg.Wait()
1386 close(statusChan)
1387 <-doneChan
1388
1389 fmt.Printf("\n")
1390}