blob: b5743c51c5e97f2bfbf81875e116cbce037d7bb8 [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 Langley80842bd2014-06-20 12:00:00 -070050 // messageLen is the length, in bytes, of the test message that will be
51 // sent.
52 messageLen int
Adam Langley95c29f32014-06-20 12:00:00 -070053}
54
55var clientTests = []testCase{
56 {
57 name: "BadRSASignature",
58 config: Config{
59 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
60 Bugs: ProtocolBugs{
61 InvalidSKXSignature: true,
62 },
63 },
64 shouldFail: true,
65 expectedError: ":BAD_SIGNATURE:",
66 },
67 {
68 name: "BadECDSASignature",
69 config: Config{
70 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
71 Bugs: ProtocolBugs{
72 InvalidSKXSignature: true,
73 },
74 Certificates: []Certificate{getECDSACertificate()},
75 },
76 shouldFail: true,
77 expectedError: ":BAD_SIGNATURE:",
78 },
79 {
80 name: "BadECDSACurve",
81 config: Config{
82 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
83 Bugs: ProtocolBugs{
84 InvalidSKXCurve: true,
85 },
86 Certificates: []Certificate{getECDSACertificate()},
87 },
88 shouldFail: true,
89 expectedError: ":WRONG_CURVE:",
90 },
91}
92
Adam Langley80842bd2014-06-20 12:00:00 -070093func doExchange(tlsConn *Conn, messageLen int) error {
Adam Langley95c29f32014-06-20 12:00:00 -070094 if err := tlsConn.Handshake(); err != nil {
95 return err
96 }
Adam Langley80842bd2014-06-20 12:00:00 -070097 if messageLen == 0 {
98 messageLen = 32
99 }
100 testMessage := make([]byte, messageLen)
101 for i := range testMessage {
102 testMessage[i] = 0x42
103 }
Adam Langley95c29f32014-06-20 12:00:00 -0700104 tlsConn.Write(testMessage)
105
106 buf := make([]byte, len(testMessage))
107 _, err := io.ReadFull(tlsConn, buf)
108 if err != nil {
109 return err
110 }
111
112 for i, v := range buf {
113 if v != testMessage[i]^0xff {
114 return fmt.Errorf("bad reply contents at byte %d", i)
115 }
116 }
117
118 return nil
119}
120
121func valgrindOf(dbAttach bool, baseArgs ...string) *exec.Cmd {
122 args := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
123 if dbAttach {
124 args = append(args, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
125 }
126 args = append(args, baseArgs...)
127
128 return exec.Command("valgrind", args...)
129}
130
131func gdbOf(baseArgs ...string) *exec.Cmd {
132 args := []string{"-e", "gdb", "--args"}
133 args = append(args, baseArgs...)
134
135 return exec.Command("xterm", args...)
136}
137
138func runTest(test *testCase) error {
139 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
140 if err != nil {
141 panic(err)
142 }
143
144 syscall.CloseOnExec(socks[0])
145 syscall.CloseOnExec(socks[1])
146 clientEnd := os.NewFile(uintptr(socks[0]), "client end")
147 connFile := os.NewFile(uintptr(socks[1]), "our end")
148 conn, err := net.FileConn(connFile)
149 connFile.Close()
150 if err != nil {
151 panic(err)
152 }
153
154 const shim_path = "../../../build/ssl/test/client_shim"
155 var client *exec.Cmd
156 if *useValgrind {
157 client = valgrindOf(false, shim_path)
158 } else {
159 client = exec.Command(shim_path)
160 }
161 //client := gdbOf(shim_path)
162 client.ExtraFiles = []*os.File{clientEnd}
163 client.Stdin = os.Stdin
164 var stdoutBuf, stderrBuf bytes.Buffer
165 client.Stdout = &stdoutBuf
166 client.Stderr = &stderrBuf
167
168 if err := client.Start(); err != nil {
169 panic(err)
170 }
171 clientEnd.Close()
172
173 config := test.config
174 if len(config.Certificates) == 0 {
175 config.Certificates = []Certificate{getRSACertificate()}
176 }
177
178 tlsConn := Server(conn, &config)
Adam Langley80842bd2014-06-20 12:00:00 -0700179 err = doExchange(tlsConn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700180
181 conn.Close()
182 childErr := client.Wait()
183
184 stdout := string(stdoutBuf.Bytes())
185 stderr := string(stderrBuf.Bytes())
186 failed := err != nil || childErr != nil
187 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
188
189 if failed != test.shouldFail || failed && !correctFailure {
190 localError := "none"
191 childError := "none"
192 if err != nil {
193 localError = err.Error()
194 }
195 if childErr != nil {
196 childError = childErr.Error()
197 }
198
199 var msg string
200 switch {
201 case failed && !test.shouldFail:
202 msg = "unexpected failure"
203 case !failed && test.shouldFail:
204 msg = "unexpected success"
205 case failed && !correctFailure:
206 msg = "bad error (wanted '" + test.expectedError + "')"
207 default:
208 panic("internal error")
209 }
210
211 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
212 }
213
214 if !*useValgrind && len(stderr) > 0 {
215 println(stderr)
216 }
217
218 return nil
219}
220
221var tlsVersions = []struct {
222 name string
223 version uint16
224}{
225 {"SSL3", VersionSSL30},
226 {"TLS1", VersionTLS10},
227 {"TLS11", VersionTLS11},
228 {"TLS12", VersionTLS12},
229}
230
231var testCipherSuites = []struct {
232 name string
233 id uint16
234}{
235 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
236 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
237 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
238 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
239 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
240 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
241 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
242 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
243 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
244 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
245 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
246 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
247 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
248 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
249 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
250}
251
252func addCipherSuiteTests() {
253 for _, suite := range testCipherSuites {
254 var cert Certificate
255 if strings.Contains(suite.name, "ECDSA") {
256 cert = getECDSACertificate()
257 } else {
258 cert = getRSACertificate()
259 }
260
261 for _, ver := range tlsVersions {
262 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
263 continue
264 }
265
266 clientTests = append(clientTests, testCase{
267 name: ver.name + "-" + suite.name,
268 config: Config{
269 MinVersion: ver.version,
270 MaxVersion: ver.version,
271 CipherSuites: []uint16{suite.id},
272 Certificates: []Certificate{cert},
273 },
274 })
275 }
276 }
277}
278
279func addBadECDSASignatureTests() {
280 for badR := BadValue(1); badR < NumBadValues; badR++ {
281 for badS := BadValue(1); badS < NumBadValues; badS++ {
282 clientTests = append(clientTests, testCase{
283 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
284 config: Config{
285 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
286 Certificates: []Certificate{getECDSACertificate()},
287 Bugs: ProtocolBugs{
288 BadECDSAR: badR,
289 BadECDSAS: badS,
290 },
291 },
292 shouldFail: true,
293 expectedError: "SIGNATURE",
294 })
295 }
296 }
297}
298
Adam Langley80842bd2014-06-20 12:00:00 -0700299func addCBCPaddingTests() {
300 clientTests = append(clientTests, testCase{
301 name: "MaxCBCPadding",
302 config: Config{
303 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
304 Bugs: ProtocolBugs{
305 MaxPadding: true,
306 },
307 },
308 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
309 })
310 clientTests = append(clientTests, testCase{
311 name: "BadCBCPadding",
312 config: Config{
313 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
314 Bugs: ProtocolBugs{
315 PaddingFirstByteBad: true,
316 },
317 },
318 shouldFail: true,
319 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
320 })
321 // OpenSSL previously had an issue where the first byte of padding in
322 // 255 bytes of padding wasn't checked.
323 clientTests = append(clientTests, testCase{
324 name: "BadCBCPadding255",
325 config: Config{
326 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
327 Bugs: ProtocolBugs{
328 MaxPadding: true,
329 PaddingFirstByteBadIf255: true,
330 },
331 },
332 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
333 shouldFail: true,
334 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
335 })
336}
337
Adam Langley95c29f32014-06-20 12:00:00 -0700338func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
339 defer wg.Done()
340
341 for test := range c {
342 statusChan <- statusMsg{test: test, started: true}
343 err := runTest(test)
344 statusChan <- statusMsg{test: test, err: err}
345 }
346}
347
348type statusMsg struct {
349 test *testCase
350 started bool
351 err error
352}
353
354func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
355 var started, done, failed, lineLen int
356 defer close(doneChan)
357
358 for msg := range statusChan {
359 if msg.started {
360 started++
361 } else {
362 done++
363 }
364
365 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
366
367 if msg.err != nil {
368 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
369 failed++
370 }
371 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
372 lineLen = len(line)
373 os.Stdout.WriteString(line)
374 }
375}
376
377func main() {
378 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
379
380 flag.Parse()
381
382 addCipherSuiteTests()
383 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -0700384 addCBCPaddingTests()
Adam Langley95c29f32014-06-20 12:00:00 -0700385
386 var wg sync.WaitGroup
387
388 const numWorkers = 64
389
390 statusChan := make(chan statusMsg, numWorkers)
391 testChan := make(chan *testCase, numWorkers)
392 doneChan := make(chan struct{})
393
394 go statusPrinter(doneChan, statusChan, len(clientTests))
395
396 for i := 0; i < numWorkers; i++ {
397 wg.Add(1)
398 go worker(statusChan, testChan, &wg)
399 }
400
401 for i := range clientTests {
402 if len(*flagTest) == 0 || *flagTest == clientTests[i].name {
403 testChan <- &clientTests[i]
404 }
405 }
406
407 close(testChan)
408 wg.Wait()
409 close(statusChan)
410 <-doneChan
411
412 fmt.Printf("\n")
413}