blob: 1b86a959e142a923feb19b305dd707b25c77001e [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001package main
2
3import (
4 "bytes"
5 "flag"
6 "fmt"
7 "io"
8 "net"
9 "os"
10 "os/exec"
11 "strings"
12 "sync"
13 "syscall"
14)
15
16var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
17
18var rsaCertificate, ecdsaCertificate Certificate
19
20func initCertificates() {
21 var err error
22 rsaCertificate, err = LoadX509KeyPair("cert.pem", "key.pem")
23 if err != nil {
24 panic(err)
25 }
26
27 ecdsaCertificate, err = LoadX509KeyPair("ecdsa_cert.pem", "ecdsa_key.pem")
28 if err != nil {
29 panic(err)
30 }
31}
32
33var certificateOnce sync.Once
34
35func getRSACertificate() Certificate {
36 certificateOnce.Do(initCertificates)
37 return rsaCertificate
38}
39
40func getECDSACertificate() Certificate {
41 certificateOnce.Do(initCertificates)
42 return ecdsaCertificate
43}
44
45type testCase struct {
46 name string
47 config Config
48 shouldFail bool
49 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -070050 // expectedLocalError, if not empty, contains a substring that must be
51 // found in the local error.
52 expectedLocalError string
Adam Langley80842bd2014-06-20 12:00:00 -070053 // messageLen is the length, in bytes, of the test message that will be
54 // sent.
55 messageLen int
David Benjamin325b5c32014-07-01 19:40:31 -040056 // flags, if not empty, contains a list of command-line flags that will
57 // be passed to the shim program.
58 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -070059}
60
61var clientTests = []testCase{
62 {
63 name: "BadRSASignature",
64 config: Config{
65 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
66 Bugs: ProtocolBugs{
67 InvalidSKXSignature: true,
68 },
69 },
70 shouldFail: true,
71 expectedError: ":BAD_SIGNATURE:",
72 },
73 {
74 name: "BadECDSASignature",
75 config: Config{
76 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
77 Bugs: ProtocolBugs{
78 InvalidSKXSignature: true,
79 },
80 Certificates: []Certificate{getECDSACertificate()},
81 },
82 shouldFail: true,
83 expectedError: ":BAD_SIGNATURE:",
84 },
85 {
86 name: "BadECDSACurve",
87 config: Config{
88 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
89 Bugs: ProtocolBugs{
90 InvalidSKXCurve: true,
91 },
92 Certificates: []Certificate{getECDSACertificate()},
93 },
94 shouldFail: true,
95 expectedError: ":WRONG_CURVE:",
96 },
Adam Langleyac61fa32014-06-23 12:03:11 -070097 {
David Benjamin325b5c32014-07-01 19:40:31 -040098 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -070099 config: Config{
100 Bugs: ProtocolBugs{
101 FailIfNotFallbackSCSV: true,
102 },
103 },
104 shouldFail: true,
105 expectedLocalError: "no fallback SCSV found",
106 },
David Benjamin325b5c32014-07-01 19:40:31 -0400107 {
108 name: "FallbackSCSV",
109 config: Config{
110 Bugs: ProtocolBugs{
111 FailIfNotFallbackSCSV: true,
112 },
113 },
114 flags: []string{"-fallback-scsv"},
115 },
Adam Langley95c29f32014-06-20 12:00:00 -0700116}
117
Adam Langley80842bd2014-06-20 12:00:00 -0700118func doExchange(tlsConn *Conn, messageLen int) error {
Adam Langley95c29f32014-06-20 12:00:00 -0700119 if err := tlsConn.Handshake(); err != nil {
120 return err
121 }
Adam Langley80842bd2014-06-20 12:00:00 -0700122 if messageLen == 0 {
123 messageLen = 32
124 }
125 testMessage := make([]byte, messageLen)
126 for i := range testMessage {
127 testMessage[i] = 0x42
128 }
Adam Langley95c29f32014-06-20 12:00:00 -0700129 tlsConn.Write(testMessage)
130
131 buf := make([]byte, len(testMessage))
132 _, err := io.ReadFull(tlsConn, buf)
133 if err != nil {
134 return err
135 }
136
137 for i, v := range buf {
138 if v != testMessage[i]^0xff {
139 return fmt.Errorf("bad reply contents at byte %d", i)
140 }
141 }
142
143 return nil
144}
145
David Benjamin325b5c32014-07-01 19:40:31 -0400146func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
147 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700148 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400149 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700150 }
David Benjamin325b5c32014-07-01 19:40:31 -0400151 valgrindArgs = append(valgrindArgs, path)
152 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700153
David Benjamin325b5c32014-07-01 19:40:31 -0400154 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700155}
156
David Benjamin325b5c32014-07-01 19:40:31 -0400157func gdbOf(path string, args ...string) *exec.Cmd {
158 xtermArgs := []string{"-e", "gdb", "--args"}
159 xtermArgs = append(xtermArgs, path)
160 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700161
David Benjamin325b5c32014-07-01 19:40:31 -0400162 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700163}
164
165func runTest(test *testCase) error {
166 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
167 if err != nil {
168 panic(err)
169 }
170
171 syscall.CloseOnExec(socks[0])
172 syscall.CloseOnExec(socks[1])
173 clientEnd := os.NewFile(uintptr(socks[0]), "client end")
174 connFile := os.NewFile(uintptr(socks[1]), "our end")
175 conn, err := net.FileConn(connFile)
176 connFile.Close()
177 if err != nil {
178 panic(err)
179 }
180
181 const shim_path = "../../../build/ssl/test/client_shim"
182 var client *exec.Cmd
183 if *useValgrind {
David Benjamin325b5c32014-07-01 19:40:31 -0400184 client = valgrindOf(false, shim_path, test.flags...)
Adam Langley95c29f32014-06-20 12:00:00 -0700185 } else {
David Benjamin325b5c32014-07-01 19:40:31 -0400186 client = exec.Command(shim_path, test.flags...)
Adam Langley95c29f32014-06-20 12:00:00 -0700187 }
188 //client := gdbOf(shim_path)
189 client.ExtraFiles = []*os.File{clientEnd}
190 client.Stdin = os.Stdin
191 var stdoutBuf, stderrBuf bytes.Buffer
192 client.Stdout = &stdoutBuf
193 client.Stderr = &stderrBuf
194
195 if err := client.Start(); err != nil {
196 panic(err)
197 }
198 clientEnd.Close()
199
200 config := test.config
201 if len(config.Certificates) == 0 {
202 config.Certificates = []Certificate{getRSACertificate()}
203 }
204
205 tlsConn := Server(conn, &config)
Adam Langley80842bd2014-06-20 12:00:00 -0700206 err = doExchange(tlsConn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700207
208 conn.Close()
209 childErr := client.Wait()
210
211 stdout := string(stdoutBuf.Bytes())
212 stderr := string(stderrBuf.Bytes())
213 failed := err != nil || childErr != nil
214 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700215 localError := "none"
216 if err != nil {
217 localError = err.Error()
218 }
219 if len(test.expectedLocalError) != 0 {
220 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
221 }
Adam Langley95c29f32014-06-20 12:00:00 -0700222
223 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700224 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700225 if childErr != nil {
226 childError = childErr.Error()
227 }
228
229 var msg string
230 switch {
231 case failed && !test.shouldFail:
232 msg = "unexpected failure"
233 case !failed && test.shouldFail:
234 msg = "unexpected success"
235 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700236 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700237 default:
238 panic("internal error")
239 }
240
241 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
242 }
243
244 if !*useValgrind && len(stderr) > 0 {
245 println(stderr)
246 }
247
248 return nil
249}
250
251var tlsVersions = []struct {
252 name string
253 version uint16
254}{
255 {"SSL3", VersionSSL30},
256 {"TLS1", VersionTLS10},
257 {"TLS11", VersionTLS11},
258 {"TLS12", VersionTLS12},
259}
260
261var testCipherSuites = []struct {
262 name string
263 id uint16
264}{
265 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
266 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
267 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
268 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
269 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
270 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
271 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
272 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
273 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
274 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
275 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
276 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
277 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
278 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
279 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
280}
281
282func addCipherSuiteTests() {
283 for _, suite := range testCipherSuites {
284 var cert Certificate
285 if strings.Contains(suite.name, "ECDSA") {
286 cert = getECDSACertificate()
287 } else {
288 cert = getRSACertificate()
289 }
290
291 for _, ver := range tlsVersions {
292 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
293 continue
294 }
295
296 clientTests = append(clientTests, testCase{
297 name: ver.name + "-" + suite.name,
298 config: Config{
299 MinVersion: ver.version,
300 MaxVersion: ver.version,
301 CipherSuites: []uint16{suite.id},
302 Certificates: []Certificate{cert},
303 },
304 })
305 }
306 }
307}
308
309func addBadECDSASignatureTests() {
310 for badR := BadValue(1); badR < NumBadValues; badR++ {
311 for badS := BadValue(1); badS < NumBadValues; badS++ {
312 clientTests = append(clientTests, testCase{
313 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
314 config: Config{
315 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
316 Certificates: []Certificate{getECDSACertificate()},
317 Bugs: ProtocolBugs{
318 BadECDSAR: badR,
319 BadECDSAS: badS,
320 },
321 },
322 shouldFail: true,
323 expectedError: "SIGNATURE",
324 })
325 }
326 }
327}
328
Adam Langley80842bd2014-06-20 12:00:00 -0700329func addCBCPaddingTests() {
330 clientTests = append(clientTests, testCase{
331 name: "MaxCBCPadding",
332 config: Config{
333 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
334 Bugs: ProtocolBugs{
335 MaxPadding: true,
336 },
337 },
338 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
339 })
340 clientTests = append(clientTests, testCase{
341 name: "BadCBCPadding",
342 config: Config{
343 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
344 Bugs: ProtocolBugs{
345 PaddingFirstByteBad: true,
346 },
347 },
348 shouldFail: true,
349 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
350 })
351 // OpenSSL previously had an issue where the first byte of padding in
352 // 255 bytes of padding wasn't checked.
353 clientTests = append(clientTests, testCase{
354 name: "BadCBCPadding255",
355 config: Config{
356 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
357 Bugs: ProtocolBugs{
358 MaxPadding: true,
359 PaddingFirstByteBadIf255: true,
360 },
361 },
362 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
363 shouldFail: true,
364 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
365 })
366}
367
Adam Langley95c29f32014-06-20 12:00:00 -0700368func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
369 defer wg.Done()
370
371 for test := range c {
372 statusChan <- statusMsg{test: test, started: true}
373 err := runTest(test)
374 statusChan <- statusMsg{test: test, err: err}
375 }
376}
377
378type statusMsg struct {
379 test *testCase
380 started bool
381 err error
382}
383
384func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
385 var started, done, failed, lineLen int
386 defer close(doneChan)
387
388 for msg := range statusChan {
389 if msg.started {
390 started++
391 } else {
392 done++
393 }
394
395 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
396
397 if msg.err != nil {
398 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
399 failed++
400 }
401 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
402 lineLen = len(line)
403 os.Stdout.WriteString(line)
404 }
405}
406
407func main() {
408 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
409
410 flag.Parse()
411
412 addCipherSuiteTests()
413 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -0700414 addCBCPaddingTests()
Adam Langley95c29f32014-06-20 12:00:00 -0700415
416 var wg sync.WaitGroup
417
418 const numWorkers = 64
419
420 statusChan := make(chan statusMsg, numWorkers)
421 testChan := make(chan *testCase, numWorkers)
422 doneChan := make(chan struct{})
423
424 go statusPrinter(doneChan, statusChan, len(clientTests))
425
426 for i := 0; i < numWorkers; i++ {
427 wg.Add(1)
428 go worker(statusChan, testChan, &wg)
429 }
430
431 for i := range clientTests {
432 if len(*flagTest) == 0 || *flagTest == clientTests[i].name {
433 testChan <- &clientTests[i]
434 }
435 }
436
437 close(testChan)
438 wg.Wait()
439 close(statusChan)
440 <-doneChan
441
442 fmt.Printf("\n")
443}