blob: ae7446462eec8371a8066ea6b2a2d407aac64fae [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 Benjamine58c4f52014-08-24 03:47:07 -0400129 // shimWritesFirst controls whether the shim sends an initial "hello"
130 // message before doing a roundtrip with the runner.
131 shimWritesFirst bool
David Benjamin325b5c32014-07-01 19:40:31 -0400132 // flags, if not empty, contains a list of command-line flags that will
133 // be passed to the shim program.
134 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -0700135}
136
David Benjamin025b3d32014-07-01 19:53:04 -0400137var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700138 {
139 name: "BadRSASignature",
140 config: Config{
141 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
142 Bugs: ProtocolBugs{
143 InvalidSKXSignature: true,
144 },
145 },
146 shouldFail: true,
147 expectedError: ":BAD_SIGNATURE:",
148 },
149 {
150 name: "BadECDSASignature",
151 config: Config{
152 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
153 Bugs: ProtocolBugs{
154 InvalidSKXSignature: true,
155 },
156 Certificates: []Certificate{getECDSACertificate()},
157 },
158 shouldFail: true,
159 expectedError: ":BAD_SIGNATURE:",
160 },
161 {
162 name: "BadECDSACurve",
163 config: Config{
164 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
165 Bugs: ProtocolBugs{
166 InvalidSKXCurve: true,
167 },
168 Certificates: []Certificate{getECDSACertificate()},
169 },
170 shouldFail: true,
171 expectedError: ":WRONG_CURVE:",
172 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700173 {
David Benjamina8e3e0e2014-08-06 22:11:10 -0400174 testType: serverTest,
175 name: "BadRSAVersion",
176 config: Config{
177 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
178 Bugs: ProtocolBugs{
179 RsaClientKeyExchangeVersion: VersionTLS11,
180 },
181 },
182 shouldFail: true,
183 expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
184 },
185 {
David Benjamin325b5c32014-07-01 19:40:31 -0400186 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700187 config: Config{
188 Bugs: ProtocolBugs{
189 FailIfNotFallbackSCSV: true,
190 },
191 },
192 shouldFail: true,
193 expectedLocalError: "no fallback SCSV found",
194 },
David Benjamin325b5c32014-07-01 19:40:31 -0400195 {
David Benjamin2a0c4962014-08-22 23:46:35 -0400196 name: "SendFallbackSCSV",
David Benjamin325b5c32014-07-01 19:40:31 -0400197 config: Config{
198 Bugs: ProtocolBugs{
199 FailIfNotFallbackSCSV: true,
200 },
201 },
202 flags: []string{"-fallback-scsv"},
203 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400204 {
David Benjamin7b030512014-07-08 17:30:11 -0400205 name: "ClientCertificateTypes",
206 config: Config{
207 ClientAuth: RequestClientCert,
208 ClientCertificateTypes: []byte{
209 CertTypeDSSSign,
210 CertTypeRSASign,
211 CertTypeECDSASign,
212 },
213 },
David Benjamin2561dc32014-08-24 01:25:27 -0400214 flags: []string{
215 "-expect-certificate-types",
216 base64.StdEncoding.EncodeToString([]byte{
217 CertTypeDSSSign,
218 CertTypeRSASign,
219 CertTypeECDSASign,
220 }),
221 },
David Benjamin7b030512014-07-08 17:30:11 -0400222 },
David Benjamin636293b2014-07-08 17:59:18 -0400223 {
224 name: "NoClientCertificate",
225 config: Config{
226 ClientAuth: RequireAnyClientCert,
227 },
228 shouldFail: true,
229 expectedLocalError: "client didn't provide a certificate",
230 },
David Benjamin1c375dd2014-07-12 00:48:23 -0400231 {
232 name: "UnauthenticatedECDH",
233 config: Config{
234 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
235 Bugs: ProtocolBugs{
236 UnauthenticatedECDH: true,
237 },
238 },
239 shouldFail: true,
David Benjamine8f3d662014-07-12 01:10:19 -0400240 expectedError: ":UNEXPECTED_MESSAGE:",
David Benjamin1c375dd2014-07-12 00:48:23 -0400241 },
David Benjamin9c651c92014-07-12 13:27:45 -0400242 {
243 name: "SkipServerKeyExchange",
244 config: Config{
245 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
246 Bugs: ProtocolBugs{
247 SkipServerKeyExchange: true,
248 },
249 },
250 shouldFail: true,
251 expectedError: ":UNEXPECTED_MESSAGE:",
252 },
David Benjamin1f5f62b2014-07-12 16:18:02 -0400253 {
David Benjamina0e52232014-07-19 17:39:58 -0400254 name: "SkipChangeCipherSpec-Client",
255 config: Config{
256 Bugs: ProtocolBugs{
257 SkipChangeCipherSpec: true,
258 },
259 },
260 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400261 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400262 },
263 {
264 testType: serverTest,
265 name: "SkipChangeCipherSpec-Server",
266 config: Config{
267 Bugs: ProtocolBugs{
268 SkipChangeCipherSpec: true,
269 },
270 },
271 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400272 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400273 },
David Benjamin42be6452014-07-21 14:50:23 -0400274 {
275 testType: serverTest,
276 name: "SkipChangeCipherSpec-Server-NPN",
277 config: Config{
278 NextProtos: []string{"bar"},
279 Bugs: ProtocolBugs{
280 SkipChangeCipherSpec: true,
281 },
282 },
283 flags: []string{
284 "-advertise-npn", "\x03foo\x03bar\x03baz",
285 },
286 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400287 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
288 },
289 {
290 name: "FragmentAcrossChangeCipherSpec-Client",
291 config: Config{
292 Bugs: ProtocolBugs{
293 FragmentAcrossChangeCipherSpec: true,
294 },
295 },
296 shouldFail: true,
297 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
298 },
299 {
300 testType: serverTest,
301 name: "FragmentAcrossChangeCipherSpec-Server",
302 config: Config{
303 Bugs: ProtocolBugs{
304 FragmentAcrossChangeCipherSpec: true,
305 },
306 },
307 shouldFail: true,
308 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
309 },
310 {
311 testType: serverTest,
312 name: "FragmentAcrossChangeCipherSpec-Server-NPN",
313 config: Config{
314 NextProtos: []string{"bar"},
315 Bugs: ProtocolBugs{
316 FragmentAcrossChangeCipherSpec: true,
317 },
318 },
319 flags: []string{
320 "-advertise-npn", "\x03foo\x03bar\x03baz",
321 },
322 shouldFail: true,
323 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamin42be6452014-07-21 14:50:23 -0400324 },
David Benjaminf3ec83d2014-07-21 22:42:34 -0400325 {
326 testType: serverTest,
327 name: "EarlyChangeCipherSpec-server-1",
328 config: Config{
329 Bugs: ProtocolBugs{
330 EarlyChangeCipherSpec: 1,
331 },
332 },
333 shouldFail: true,
334 expectedError: ":CCS_RECEIVED_EARLY:",
335 },
336 {
337 testType: serverTest,
338 name: "EarlyChangeCipherSpec-server-2",
339 config: Config{
340 Bugs: ProtocolBugs{
341 EarlyChangeCipherSpec: 2,
342 },
343 },
344 shouldFail: true,
345 expectedError: ":CCS_RECEIVED_EARLY:",
346 },
David Benjamind23f4122014-07-23 15:09:48 -0400347 {
David Benjamind23f4122014-07-23 15:09:48 -0400348 name: "SkipNewSessionTicket",
349 config: Config{
350 Bugs: ProtocolBugs{
351 SkipNewSessionTicket: true,
352 },
353 },
354 shouldFail: true,
355 expectedError: ":CCS_RECEIVED_EARLY:",
356 },
David Benjamin7e3305e2014-07-28 14:52:32 -0400357 {
David Benjamind86c7672014-08-02 04:07:12 -0400358 testType: serverTest,
David Benjaminbef270a2014-08-02 04:22:02 -0400359 name: "FallbackSCSV",
360 config: Config{
361 MaxVersion: VersionTLS11,
362 Bugs: ProtocolBugs{
363 SendFallbackSCSV: true,
364 },
365 },
366 shouldFail: true,
367 expectedError: ":INAPPROPRIATE_FALLBACK:",
368 },
369 {
370 testType: serverTest,
371 name: "FallbackSCSV-VersionMatch",
372 config: Config{
373 Bugs: ProtocolBugs{
374 SendFallbackSCSV: true,
375 },
376 },
377 },
David Benjamin98214542014-08-07 18:02:39 -0400378 {
379 testType: serverTest,
380 name: "FragmentedClientVersion",
381 config: Config{
382 Bugs: ProtocolBugs{
383 MaxHandshakeRecordLength: 1,
384 FragmentClientVersion: true,
385 },
386 },
387 shouldFail: true,
388 expectedError: ":RECORD_TOO_SMALL:",
389 },
David Benjamin98e882e2014-08-08 13:24:34 -0400390 {
391 testType: serverTest,
392 name: "MinorVersionTolerance",
393 config: Config{
394 Bugs: ProtocolBugs{
395 SendClientVersion: 0x03ff,
396 },
397 },
398 expectedVersion: VersionTLS12,
399 },
400 {
401 testType: serverTest,
402 name: "MajorVersionTolerance",
403 config: Config{
404 Bugs: ProtocolBugs{
405 SendClientVersion: 0x0400,
406 },
407 },
408 expectedVersion: VersionTLS12,
409 },
410 {
411 testType: serverTest,
412 name: "VersionTooLow",
413 config: Config{
414 Bugs: ProtocolBugs{
415 SendClientVersion: 0x0200,
416 },
417 },
418 shouldFail: true,
419 expectedError: ":UNSUPPORTED_PROTOCOL:",
420 },
421 {
422 testType: serverTest,
423 name: "HttpGET",
424 sendPrefix: "GET / HTTP/1.0\n",
425 shouldFail: true,
426 expectedError: ":HTTP_REQUEST:",
427 },
428 {
429 testType: serverTest,
430 name: "HttpPOST",
431 sendPrefix: "POST / HTTP/1.0\n",
432 shouldFail: true,
433 expectedError: ":HTTP_REQUEST:",
434 },
435 {
436 testType: serverTest,
437 name: "HttpHEAD",
438 sendPrefix: "HEAD / HTTP/1.0\n",
439 shouldFail: true,
440 expectedError: ":HTTP_REQUEST:",
441 },
442 {
443 testType: serverTest,
444 name: "HttpPUT",
445 sendPrefix: "PUT / HTTP/1.0\n",
446 shouldFail: true,
447 expectedError: ":HTTP_REQUEST:",
448 },
449 {
450 testType: serverTest,
451 name: "HttpCONNECT",
452 sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n",
453 shouldFail: true,
454 expectedError: ":HTTPS_PROXY_REQUEST:",
455 },
David Benjamin39ebf532014-08-31 02:23:49 -0400456 {
457 name: "SkipCipherVersionCheck",
458 config: Config{
459 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
460 MaxVersion: VersionTLS11,
461 Bugs: ProtocolBugs{
462 SkipCipherVersionCheck: true,
463 },
464 },
465 shouldFail: true,
466 expectedError: ":WRONG_CIPHER_RETURNED:",
467 },
Adam Langley95c29f32014-06-20 12:00:00 -0700468}
469
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400470func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) error {
David Benjamin6fd297b2014-08-11 18:43:38 -0400471 if test.protocol == dtls {
472 conn = newPacketAdaptor(conn)
473 }
474
475 if test.sendPrefix != "" {
476 if _, err := conn.Write([]byte(test.sendPrefix)); err != nil {
477 return err
478 }
David Benjamin98e882e2014-08-08 13:24:34 -0400479 }
480
David Benjamin1d5c83e2014-07-22 19:20:02 -0400481 var tlsConn *Conn
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400482 if test.testType == clientTest {
David Benjamin6fd297b2014-08-11 18:43:38 -0400483 if test.protocol == dtls {
484 tlsConn = DTLSServer(conn, config)
485 } else {
486 tlsConn = Server(conn, config)
487 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400488 } else {
489 config.InsecureSkipVerify = true
David Benjamin6fd297b2014-08-11 18:43:38 -0400490 if test.protocol == dtls {
491 tlsConn = DTLSClient(conn, config)
492 } else {
493 tlsConn = Client(conn, config)
494 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400495 }
496
Adam Langley95c29f32014-06-20 12:00:00 -0700497 if err := tlsConn.Handshake(); err != nil {
498 return err
499 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700500
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400501 if vers := tlsConn.ConnectionState().Version; test.expectedVersion != 0 && vers != test.expectedVersion {
502 return fmt.Errorf("got version %x, expected %x", vers, test.expectedVersion)
503 }
504
David Benjamina08e49d2014-08-24 01:46:07 -0400505 if test.expectChannelID {
506 channelID := tlsConn.ConnectionState().ChannelID
507 if channelID == nil {
508 return fmt.Errorf("no channel ID negotiated")
509 }
510 if channelID.Curve != channelIDKey.Curve ||
511 channelIDKey.X.Cmp(channelIDKey.X) != 0 ||
512 channelIDKey.Y.Cmp(channelIDKey.Y) != 0 {
513 return fmt.Errorf("incorrect channel ID")
514 }
515 }
516
David Benjamine58c4f52014-08-24 03:47:07 -0400517 if test.shimWritesFirst {
518 var buf [5]byte
519 _, err := io.ReadFull(tlsConn, buf[:])
520 if err != nil {
521 return err
522 }
523 if string(buf[:]) != "hello" {
524 return fmt.Errorf("bad initial message")
525 }
526 }
527
Kenny Root7fdeaf12014-08-05 15:23:37 -0700528 if messageLen < 0 {
David Benjamin6fd297b2014-08-11 18:43:38 -0400529 if test.protocol == dtls {
530 return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
531 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700532 // Read until EOF.
533 _, err := io.Copy(ioutil.Discard, tlsConn)
534 return err
535 }
536
Adam Langley80842bd2014-06-20 12:00:00 -0700537 if messageLen == 0 {
538 messageLen = 32
539 }
540 testMessage := make([]byte, messageLen)
541 for i := range testMessage {
542 testMessage[i] = 0x42
543 }
Adam Langley95c29f32014-06-20 12:00:00 -0700544 tlsConn.Write(testMessage)
545
546 buf := make([]byte, len(testMessage))
David Benjamin6fd297b2014-08-11 18:43:38 -0400547 if test.protocol == dtls {
548 bufTmp := make([]byte, len(buf)+1)
549 n, err := tlsConn.Read(bufTmp)
550 if err != nil {
551 return err
552 }
553 if n != len(buf) {
554 return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
555 }
556 copy(buf, bufTmp)
557 } else {
558 _, err := io.ReadFull(tlsConn, buf)
559 if err != nil {
560 return err
561 }
Adam Langley95c29f32014-06-20 12:00:00 -0700562 }
563
564 for i, v := range buf {
565 if v != testMessage[i]^0xff {
566 return fmt.Errorf("bad reply contents at byte %d", i)
567 }
568 }
569
570 return nil
571}
572
David Benjamin325b5c32014-07-01 19:40:31 -0400573func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
574 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700575 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400576 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700577 }
David Benjamin325b5c32014-07-01 19:40:31 -0400578 valgrindArgs = append(valgrindArgs, path)
579 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700580
David Benjamin325b5c32014-07-01 19:40:31 -0400581 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700582}
583
David Benjamin325b5c32014-07-01 19:40:31 -0400584func gdbOf(path string, args ...string) *exec.Cmd {
585 xtermArgs := []string{"-e", "gdb", "--args"}
586 xtermArgs = append(xtermArgs, path)
587 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700588
David Benjamin325b5c32014-07-01 19:40:31 -0400589 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700590}
591
David Benjamin1d5c83e2014-07-22 19:20:02 -0400592func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700593 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
594 if err != nil {
595 panic(err)
596 }
597
598 syscall.CloseOnExec(socks[0])
599 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400600 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700601 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400602 conn, err = net.FileConn(connFile)
603 if err != nil {
604 panic(err)
605 }
Adam Langley95c29f32014-06-20 12:00:00 -0700606 connFile.Close()
607 if err != nil {
608 panic(err)
609 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400610 return shimEnd, conn
611}
612
David Benjamin884fdf12014-08-02 15:28:23 -0400613func runTest(test *testCase, buildDir string) error {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400614 shimEnd, conn := openSocketPair()
615 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700616
David Benjamin884fdf12014-08-02 15:28:23 -0400617 shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
David Benjamin5a593af2014-08-11 19:51:50 -0400618 var flags []string
David Benjamin1d5c83e2014-07-22 19:20:02 -0400619 if test.testType == serverTest {
David Benjamin5a593af2014-08-11 19:51:50 -0400620 flags = append(flags, "-server")
621
David Benjamin025b3d32014-07-01 19:53:04 -0400622 flags = append(flags, "-key-file")
623 if test.keyFile == "" {
624 flags = append(flags, rsaKeyFile)
625 } else {
626 flags = append(flags, test.keyFile)
627 }
628
629 flags = append(flags, "-cert-file")
630 if test.certFile == "" {
631 flags = append(flags, rsaCertificateFile)
632 } else {
633 flags = append(flags, test.certFile)
634 }
635 }
David Benjamin5a593af2014-08-11 19:51:50 -0400636
David Benjamin6fd297b2014-08-11 18:43:38 -0400637 if test.protocol == dtls {
638 flags = append(flags, "-dtls")
639 }
640
David Benjamin5a593af2014-08-11 19:51:50 -0400641 if test.resumeSession {
642 flags = append(flags, "-resume")
643 }
644
David Benjamine58c4f52014-08-24 03:47:07 -0400645 if test.shimWritesFirst {
646 flags = append(flags, "-shim-writes-first")
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 Benjaminf7768e42014-08-31 02:06:47 -0400745 {"AES128-SHA256", TLS_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400746 {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700747 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400748 {"AES256-SHA256", TLS_RSA_WITH_AES_256_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400749 {"DHE-RSA-AES128-GCM", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
750 {"DHE-RSA-AES128-SHA", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400751 {"DHE-RSA-AES128-SHA256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400752 {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
753 {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400754 {"DHE-RSA-AES256-SHA256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700755 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
756 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400757 {"ECDHE-ECDSA-AES128-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
758 {"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700759 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400760 {"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700761 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700762 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700763 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400764 {"ECDHE-RSA-AES128-SHA256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400765 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700766 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400767 {"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700768 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700769 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400770 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700771}
772
David Benjaminf7768e42014-08-31 02:06:47 -0400773func isTLS12Only(suiteName string) bool {
774 return strings.HasSuffix(suiteName, "-GCM") ||
775 strings.HasSuffix(suiteName, "-SHA256") ||
776 strings.HasSuffix(suiteName, "-SHA384")
777}
778
Adam Langley95c29f32014-06-20 12:00:00 -0700779func addCipherSuiteTests() {
780 for _, suite := range testCipherSuites {
781 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400782 var certFile string
783 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700784 if strings.Contains(suite.name, "ECDSA") {
785 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400786 certFile = ecdsaCertificateFile
787 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700788 } else {
789 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400790 certFile = rsaCertificateFile
791 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700792 }
793
794 for _, ver := range tlsVersions {
David Benjaminf7768e42014-08-31 02:06:47 -0400795 if ver.version < VersionTLS12 && isTLS12Only(suite.name) {
Adam Langley95c29f32014-06-20 12:00:00 -0700796 continue
797 }
798
David Benjamin1d5c83e2014-07-22 19:20:02 -0400799 // Go's TLS implementation only implements session
800 // resumption with tickets, so SSLv3 cannot resume
801 // sessions.
802 resumeSession := ver.version != VersionSSL30
803
David Benjamin025b3d32014-07-01 19:53:04 -0400804 testCases = append(testCases, testCase{
805 testType: clientTest,
806 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700807 config: Config{
808 MinVersion: ver.version,
809 MaxVersion: ver.version,
810 CipherSuites: []uint16{suite.id},
811 Certificates: []Certificate{cert},
812 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400813 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700814 })
David Benjamin025b3d32014-07-01 19:53:04 -0400815
David Benjamin76d8abe2014-08-14 16:25:34 -0400816 testCases = append(testCases, testCase{
817 testType: serverTest,
818 name: ver.name + "-" + suite.name + "-server",
819 config: Config{
820 MinVersion: ver.version,
821 MaxVersion: ver.version,
822 CipherSuites: []uint16{suite.id},
823 Certificates: []Certificate{cert},
824 },
825 certFile: certFile,
826 keyFile: keyFile,
827 resumeSession: resumeSession,
828 })
David Benjamin6fd297b2014-08-11 18:43:38 -0400829
830 // TODO(davidben): Fix DTLS 1.2 support and test that.
831 if ver.version == VersionTLS10 && strings.Index(suite.name, "RC4") == -1 {
832 testCases = append(testCases, testCase{
833 testType: clientTest,
834 protocol: dtls,
835 name: "D" + ver.name + "-" + suite.name + "-client",
836 config: Config{
837 MinVersion: ver.version,
838 MaxVersion: ver.version,
839 CipherSuites: []uint16{suite.id},
840 Certificates: []Certificate{cert},
841 },
842 resumeSession: resumeSession,
843 })
844 testCases = append(testCases, testCase{
845 testType: serverTest,
846 protocol: dtls,
847 name: "D" + ver.name + "-" + suite.name + "-server",
848 config: Config{
849 MinVersion: ver.version,
850 MaxVersion: ver.version,
851 CipherSuites: []uint16{suite.id},
852 Certificates: []Certificate{cert},
853 },
854 certFile: certFile,
855 keyFile: keyFile,
856 resumeSession: resumeSession,
857 })
858 }
Adam Langley95c29f32014-06-20 12:00:00 -0700859 }
860 }
861}
862
863func addBadECDSASignatureTests() {
864 for badR := BadValue(1); badR < NumBadValues; badR++ {
865 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400866 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700867 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
868 config: Config{
869 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
870 Certificates: []Certificate{getECDSACertificate()},
871 Bugs: ProtocolBugs{
872 BadECDSAR: badR,
873 BadECDSAS: badS,
874 },
875 },
876 shouldFail: true,
877 expectedError: "SIGNATURE",
878 })
879 }
880 }
881}
882
Adam Langley80842bd2014-06-20 12:00:00 -0700883func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400884 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700885 name: "MaxCBCPadding",
886 config: Config{
887 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
888 Bugs: ProtocolBugs{
889 MaxPadding: true,
890 },
891 },
892 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
893 })
David Benjamin025b3d32014-07-01 19:53:04 -0400894 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700895 name: "BadCBCPadding",
896 config: Config{
897 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
898 Bugs: ProtocolBugs{
899 PaddingFirstByteBad: true,
900 },
901 },
902 shouldFail: true,
903 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
904 })
905 // OpenSSL previously had an issue where the first byte of padding in
906 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400907 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700908 name: "BadCBCPadding255",
909 config: Config{
910 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
911 Bugs: ProtocolBugs{
912 MaxPadding: true,
913 PaddingFirstByteBadIf255: true,
914 },
915 },
916 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
917 shouldFail: true,
918 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
919 })
920}
921
Kenny Root7fdeaf12014-08-05 15:23:37 -0700922func addCBCSplittingTests() {
923 testCases = append(testCases, testCase{
924 name: "CBCRecordSplitting",
925 config: Config{
926 MaxVersion: VersionTLS10,
927 MinVersion: VersionTLS10,
928 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
929 },
930 messageLen: -1, // read until EOF
931 flags: []string{
932 "-async",
933 "-write-different-record-sizes",
934 "-cbc-record-splitting",
935 },
David Benjamina8e3e0e2014-08-06 22:11:10 -0400936 })
937 testCases = append(testCases, testCase{
Kenny Root7fdeaf12014-08-05 15:23:37 -0700938 name: "CBCRecordSplittingPartialWrite",
939 config: Config{
940 MaxVersion: VersionTLS10,
941 MinVersion: VersionTLS10,
942 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
943 },
944 messageLen: -1, // read until EOF
945 flags: []string{
946 "-async",
947 "-write-different-record-sizes",
948 "-cbc-record-splitting",
949 "-partial-write",
950 },
951 })
952}
953
David Benjamin636293b2014-07-08 17:59:18 -0400954func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400955 // Add a dummy cert pool to stress certificate authority parsing.
956 // TODO(davidben): Add tests that those values parse out correctly.
957 certPool := x509.NewCertPool()
958 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
959 if err != nil {
960 panic(err)
961 }
962 certPool.AddCert(cert)
963
David Benjamin636293b2014-07-08 17:59:18 -0400964 for _, ver := range tlsVersions {
David Benjamin636293b2014-07-08 17:59:18 -0400965 testCases = append(testCases, testCase{
966 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400967 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400968 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -0400969 MinVersion: ver.version,
970 MaxVersion: ver.version,
971 ClientAuth: RequireAnyClientCert,
972 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{
David Benjamin67666e72014-07-12 15:47:52 -0400980 testType: serverTest,
981 name: ver.name + "-Server-ClientAuth-RSA",
982 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -0400983 MinVersion: ver.version,
984 MaxVersion: ver.version,
David Benjamin67666e72014-07-12 15:47:52 -0400985 Certificates: []Certificate{rsaCertificate},
986 },
987 flags: []string{"-require-any-client-certificate"},
988 })
David Benjamine098ec22014-08-27 23:13:20 -0400989 if ver.version != VersionSSL30 {
990 testCases = append(testCases, testCase{
991 testType: serverTest,
992 name: ver.name + "-Server-ClientAuth-ECDSA",
993 config: Config{
994 MinVersion: ver.version,
995 MaxVersion: ver.version,
996 Certificates: []Certificate{ecdsaCertificate},
997 },
998 flags: []string{"-require-any-client-certificate"},
999 })
1000 testCases = append(testCases, testCase{
1001 testType: clientTest,
1002 name: ver.name + "-Client-ClientAuth-ECDSA",
1003 config: Config{
1004 MinVersion: ver.version,
1005 MaxVersion: ver.version,
1006 ClientAuth: RequireAnyClientCert,
1007 ClientCAs: certPool,
1008 },
1009 flags: []string{
1010 "-cert-file", ecdsaCertificateFile,
1011 "-key-file", ecdsaKeyFile,
1012 },
1013 })
1014 }
David Benjamin636293b2014-07-08 17:59:18 -04001015 }
1016}
1017
David Benjamin43ec06f2014-08-05 02:28:57 -04001018// Adds tests that try to cover the range of the handshake state machine, under
1019// various conditions. Some of these are redundant with other tests, but they
1020// only cover the synchronous case.
David Benjamin6fd297b2014-08-11 18:43:38 -04001021func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) {
David Benjamin43ec06f2014-08-05 02:28:57 -04001022 var suffix string
1023 var flags []string
1024 var maxHandshakeRecordLength int
David Benjamin6fd297b2014-08-11 18:43:38 -04001025 if protocol == dtls {
1026 suffix = "-DTLS"
1027 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001028 if async {
David Benjamin6fd297b2014-08-11 18:43:38 -04001029 suffix += "-Async"
David Benjamin43ec06f2014-08-05 02:28:57 -04001030 flags = append(flags, "-async")
1031 } else {
David Benjamin6fd297b2014-08-11 18:43:38 -04001032 suffix += "-Sync"
David Benjamin43ec06f2014-08-05 02:28:57 -04001033 }
1034 if splitHandshake {
1035 suffix += "-SplitHandshakeRecords"
David Benjamin98214542014-08-07 18:02:39 -04001036 maxHandshakeRecordLength = 1
David Benjamin43ec06f2014-08-05 02:28:57 -04001037 }
1038
1039 // Basic handshake, with resumption. Client and server.
1040 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001041 protocol: protocol,
1042 name: "Basic-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001043 config: Config{
1044 Bugs: ProtocolBugs{
1045 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1046 },
1047 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001048 flags: flags,
1049 resumeSession: true,
1050 })
1051 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001052 protocol: protocol,
1053 name: "Basic-Client-RenewTicket" + suffix,
David Benjaminbed9aae2014-08-07 19:13:38 -04001054 config: Config{
1055 Bugs: ProtocolBugs{
1056 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1057 RenewTicketOnResume: true,
1058 },
1059 },
1060 flags: flags,
1061 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001062 })
1063 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001064 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001065 testType: serverTest,
1066 name: "Basic-Server" + suffix,
1067 config: Config{
1068 Bugs: ProtocolBugs{
1069 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1070 },
1071 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001072 flags: flags,
1073 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001074 })
1075
David Benjamin6fd297b2014-08-11 18:43:38 -04001076 // TLS client auth.
1077 testCases = append(testCases, testCase{
1078 protocol: protocol,
1079 testType: clientTest,
1080 name: "ClientAuth-Client" + suffix,
1081 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -04001082 ClientAuth: RequireAnyClientCert,
David Benjamin6fd297b2014-08-11 18:43:38 -04001083 Bugs: ProtocolBugs{
1084 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1085 },
1086 },
1087 flags: append(flags,
1088 "-cert-file", rsaCertificateFile,
1089 "-key-file", rsaKeyFile),
1090 })
1091 testCases = append(testCases, testCase{
1092 protocol: protocol,
1093 testType: serverTest,
1094 name: "ClientAuth-Server" + suffix,
1095 config: Config{
1096 Certificates: []Certificate{rsaCertificate},
1097 },
1098 flags: append(flags, "-require-any-client-certificate"),
1099 })
1100
David Benjamin43ec06f2014-08-05 02:28:57 -04001101 // No session ticket support; server doesn't send NewSessionTicket.
1102 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001103 protocol: protocol,
1104 name: "SessionTicketsDisabled-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001105 config: Config{
1106 SessionTicketsDisabled: true,
1107 Bugs: ProtocolBugs{
1108 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1109 },
1110 },
1111 flags: flags,
1112 })
1113 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001114 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001115 testType: serverTest,
1116 name: "SessionTicketsDisabled-Server" + suffix,
1117 config: Config{
1118 SessionTicketsDisabled: true,
1119 Bugs: ProtocolBugs{
1120 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1121 },
1122 },
1123 flags: flags,
1124 })
1125
David Benjamin6fd297b2014-08-11 18:43:38 -04001126 if protocol == tls {
1127 // NPN on client and server; results in post-handshake message.
1128 testCases = append(testCases, testCase{
1129 protocol: protocol,
1130 name: "NPN-Client" + suffix,
1131 config: Config{
1132 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1133 NextProtos: []string{"foo"},
1134 Bugs: ProtocolBugs{
1135 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1136 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001137 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001138 flags: append(flags, "-select-next-proto", "foo"),
1139 })
1140 testCases = append(testCases, testCase{
1141 protocol: protocol,
1142 testType: serverTest,
1143 name: "NPN-Server" + suffix,
1144 config: Config{
1145 NextProtos: []string{"bar"},
1146 Bugs: ProtocolBugs{
1147 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1148 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001149 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001150 flags: append(flags,
1151 "-advertise-npn", "\x03foo\x03bar\x03baz",
1152 "-expect-next-proto", "bar"),
1153 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001154
David Benjamin6fd297b2014-08-11 18:43:38 -04001155 // Client does False Start and negotiates NPN.
1156 testCases = append(testCases, testCase{
1157 protocol: protocol,
1158 name: "FalseStart" + suffix,
1159 config: Config{
1160 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1161 NextProtos: []string{"foo"},
1162 Bugs: ProtocolBugs{
David Benjamine58c4f52014-08-24 03:47:07 -04001163 ExpectFalseStart: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001164 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1165 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001166 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001167 flags: append(flags,
1168 "-false-start",
1169 "-select-next-proto", "foo"),
David Benjamine58c4f52014-08-24 03:47:07 -04001170 shimWritesFirst: true,
1171 resumeSession: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001172 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001173
David Benjamin6fd297b2014-08-11 18:43:38 -04001174 // False Start without session tickets.
1175 testCases = append(testCases, testCase{
1176 name: "FalseStart-SessionTicketsDisabled",
1177 config: Config{
1178 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1179 NextProtos: []string{"foo"},
1180 SessionTicketsDisabled: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001181 Bugs: ProtocolBugs{
David Benjamine58c4f52014-08-24 03:47:07 -04001182 ExpectFalseStart: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001183 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1184 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001185 },
David Benjamin4e99c522014-08-24 01:45:30 -04001186 flags: append(flags,
David Benjamin6fd297b2014-08-11 18:43:38 -04001187 "-false-start",
1188 "-select-next-proto", "foo",
David Benjamin4e99c522014-08-24 01:45:30 -04001189 ),
David Benjamine58c4f52014-08-24 03:47:07 -04001190 shimWritesFirst: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001191 })
David Benjamin1e7f8d72014-08-08 12:27:04 -04001192
David Benjamina08e49d2014-08-24 01:46:07 -04001193 // Server parses a V2ClientHello.
David Benjamin6fd297b2014-08-11 18:43:38 -04001194 testCases = append(testCases, testCase{
1195 protocol: protocol,
1196 testType: serverTest,
1197 name: "SendV2ClientHello" + suffix,
1198 config: Config{
1199 // Choose a cipher suite that does not involve
1200 // elliptic curves, so no extensions are
1201 // involved.
1202 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
1203 Bugs: ProtocolBugs{
1204 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1205 SendV2ClientHello: true,
1206 },
David Benjamin1e7f8d72014-08-08 12:27:04 -04001207 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001208 flags: flags,
1209 })
David Benjamina08e49d2014-08-24 01:46:07 -04001210
1211 // Client sends a Channel ID.
1212 testCases = append(testCases, testCase{
1213 protocol: protocol,
1214 name: "ChannelID-Client" + suffix,
1215 config: Config{
1216 RequestChannelID: true,
1217 Bugs: ProtocolBugs{
1218 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1219 },
1220 },
1221 flags: append(flags,
1222 "-send-channel-id", channelIDKeyFile,
1223 ),
1224 resumeSession: true,
1225 expectChannelID: true,
1226 })
1227
1228 // Server accepts a Channel ID.
1229 testCases = append(testCases, testCase{
1230 protocol: protocol,
1231 testType: serverTest,
1232 name: "ChannelID-Server" + suffix,
1233 config: Config{
1234 ChannelID: channelIDKey,
1235 Bugs: ProtocolBugs{
1236 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1237 },
1238 },
1239 flags: append(flags,
1240 "-expect-channel-id",
1241 base64.StdEncoding.EncodeToString(channelIDBytes),
1242 ),
1243 resumeSession: true,
1244 expectChannelID: true,
1245 })
David Benjamin6fd297b2014-08-11 18:43:38 -04001246 } else {
1247 testCases = append(testCases, testCase{
1248 protocol: protocol,
1249 name: "SkipHelloVerifyRequest" + suffix,
1250 config: Config{
1251 Bugs: ProtocolBugs{
1252 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1253 SkipHelloVerifyRequest: true,
1254 },
1255 },
1256 flags: flags,
1257 })
1258
1259 testCases = append(testCases, testCase{
1260 testType: serverTest,
1261 protocol: protocol,
1262 name: "CookieExchange" + suffix,
1263 config: Config{
1264 Bugs: ProtocolBugs{
1265 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1266 },
1267 },
1268 flags: append(flags, "-cookie-exchange"),
1269 })
1270 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001271}
1272
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001273func addVersionNegotiationTests() {
1274 for i, shimVers := range tlsVersions {
1275 // Assemble flags to disable all newer versions on the shim.
1276 var flags []string
1277 for _, vers := range tlsVersions[i+1:] {
1278 flags = append(flags, vers.flag)
1279 }
1280
1281 for _, runnerVers := range tlsVersions {
1282 expectedVersion := shimVers.version
1283 if runnerVers.version < shimVers.version {
1284 expectedVersion = runnerVers.version
1285 }
1286 suffix := shimVers.name + "-" + runnerVers.name
1287
1288 testCases = append(testCases, testCase{
1289 testType: clientTest,
1290 name: "VersionNegotiation-Client-" + suffix,
1291 config: Config{
1292 MaxVersion: runnerVers.version,
1293 },
1294 flags: flags,
1295 expectedVersion: expectedVersion,
1296 })
1297
David Benjamin76d8abe2014-08-14 16:25:34 -04001298 testCases = append(testCases, testCase{
1299 testType: serverTest,
1300 name: "VersionNegotiation-Server-" + suffix,
1301 config: Config{
1302 MaxVersion: runnerVers.version,
1303 },
1304 flags: flags,
1305 expectedVersion: expectedVersion,
1306 })
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001307 }
1308 }
1309}
1310
David Benjamin5c24a1d2014-08-31 00:59:27 -04001311func addD5BugTests() {
1312 testCases = append(testCases, testCase{
1313 testType: serverTest,
1314 name: "D5Bug-NoQuirk-Reject",
1315 config: Config{
1316 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1317 Bugs: ProtocolBugs{
1318 SSL3RSAKeyExchange: true,
1319 },
1320 },
1321 shouldFail: true,
1322 expectedError: ":TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG:",
1323 })
1324 testCases = append(testCases, testCase{
1325 testType: serverTest,
1326 name: "D5Bug-Quirk-Normal",
1327 config: Config{
1328 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1329 },
1330 flags: []string{"-tls-d5-bug"},
1331 })
1332 testCases = append(testCases, testCase{
1333 testType: serverTest,
1334 name: "D5Bug-Quirk-Bug",
1335 config: Config{
1336 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1337 Bugs: ProtocolBugs{
1338 SSL3RSAKeyExchange: true,
1339 },
1340 },
1341 flags: []string{"-tls-d5-bug"},
1342 })
1343}
1344
David Benjamine78bfde2014-09-06 12:45:15 -04001345func addExtensionTests() {
1346 testCases = append(testCases, testCase{
1347 testType: clientTest,
1348 name: "DuplicateExtensionClient",
1349 config: Config{
1350 Bugs: ProtocolBugs{
1351 DuplicateExtension: true,
1352 },
1353 },
1354 shouldFail: true,
1355 expectedLocalError: "remote error: error decoding message",
1356 })
1357 testCases = append(testCases, testCase{
1358 testType: serverTest,
1359 name: "DuplicateExtensionServer",
1360 config: Config{
1361 Bugs: ProtocolBugs{
1362 DuplicateExtension: true,
1363 },
1364 },
1365 shouldFail: true,
1366 expectedLocalError: "remote error: error decoding message",
1367 })
1368 testCases = append(testCases, testCase{
1369 testType: clientTest,
1370 name: "ServerNameExtensionClient",
1371 config: Config{
1372 Bugs: ProtocolBugs{
1373 ExpectServerName: "example.com",
1374 },
1375 },
1376 flags: []string{"-host-name", "example.com"},
1377 })
1378 testCases = append(testCases, testCase{
1379 testType: clientTest,
1380 name: "ServerNameExtensionClient",
1381 config: Config{
1382 Bugs: ProtocolBugs{
1383 ExpectServerName: "mismatch.com",
1384 },
1385 },
1386 flags: []string{"-host-name", "example.com"},
1387 shouldFail: true,
1388 expectedLocalError: "tls: unexpected server name",
1389 })
1390 testCases = append(testCases, testCase{
1391 testType: clientTest,
1392 name: "ServerNameExtensionClient",
1393 config: Config{
1394 Bugs: ProtocolBugs{
1395 ExpectServerName: "missing.com",
1396 },
1397 },
1398 shouldFail: true,
1399 expectedLocalError: "tls: unexpected server name",
1400 })
1401 testCases = append(testCases, testCase{
1402 testType: serverTest,
1403 name: "ServerNameExtensionServer",
1404 config: Config{
1405 ServerName: "example.com",
1406 },
1407 flags: []string{"-expect-server-name", "example.com"},
1408 resumeSession: true,
1409 })
1410}
1411
David Benjamin884fdf12014-08-02 15:28:23 -04001412func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
Adam Langley95c29f32014-06-20 12:00:00 -07001413 defer wg.Done()
1414
1415 for test := range c {
1416 statusChan <- statusMsg{test: test, started: true}
David Benjamin884fdf12014-08-02 15:28:23 -04001417 err := runTest(test, buildDir)
Adam Langley95c29f32014-06-20 12:00:00 -07001418 statusChan <- statusMsg{test: test, err: err}
1419 }
1420}
1421
1422type statusMsg struct {
1423 test *testCase
1424 started bool
1425 err error
1426}
1427
1428func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
1429 var started, done, failed, lineLen int
1430 defer close(doneChan)
1431
1432 for msg := range statusChan {
1433 if msg.started {
1434 started++
1435 } else {
1436 done++
1437 }
1438
1439 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
1440
1441 if msg.err != nil {
1442 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
1443 failed++
1444 }
1445 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
1446 lineLen = len(line)
1447 os.Stdout.WriteString(line)
1448 }
1449}
1450
1451func main() {
1452 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 -04001453 var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
David Benjamin884fdf12014-08-02 15:28:23 -04001454 var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.")
Adam Langley95c29f32014-06-20 12:00:00 -07001455
1456 flag.Parse()
1457
1458 addCipherSuiteTests()
1459 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -07001460 addCBCPaddingTests()
Kenny Root7fdeaf12014-08-05 15:23:37 -07001461 addCBCSplittingTests()
David Benjamin636293b2014-07-08 17:59:18 -04001462 addClientAuthTests()
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001463 addVersionNegotiationTests()
David Benjamin5c24a1d2014-08-31 00:59:27 -04001464 addD5BugTests()
David Benjamine78bfde2014-09-06 12:45:15 -04001465 addExtensionTests()
David Benjamin43ec06f2014-08-05 02:28:57 -04001466 for _, async := range []bool{false, true} {
1467 for _, splitHandshake := range []bool{false, true} {
David Benjamin6fd297b2014-08-11 18:43:38 -04001468 for _, protocol := range []protocol{tls, dtls} {
1469 addStateMachineCoverageTests(async, splitHandshake, protocol)
1470 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001471 }
1472 }
Adam Langley95c29f32014-06-20 12:00:00 -07001473
1474 var wg sync.WaitGroup
1475
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001476 numWorkers := *flagNumWorkers
Adam Langley95c29f32014-06-20 12:00:00 -07001477
1478 statusChan := make(chan statusMsg, numWorkers)
1479 testChan := make(chan *testCase, numWorkers)
1480 doneChan := make(chan struct{})
1481
David Benjamin025b3d32014-07-01 19:53:04 -04001482 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -07001483
1484 for i := 0; i < numWorkers; i++ {
1485 wg.Add(1)
David Benjamin884fdf12014-08-02 15:28:23 -04001486 go worker(statusChan, testChan, *flagBuildDir, &wg)
Adam Langley95c29f32014-06-20 12:00:00 -07001487 }
1488
David Benjamin025b3d32014-07-01 19:53:04 -04001489 for i := range testCases {
1490 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
1491 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -07001492 }
1493 }
1494
1495 close(testChan)
1496 wg.Wait()
1497 close(statusChan)
1498 <-doneChan
1499
1500 fmt.Printf("\n")
1501}