diff options
author | tb <> | 2019-01-23 04:50:24 +0000 |
---|---|---|
committer | tb <> | 2019-01-23 04:50:24 +0000 |
commit | 2b8dd79762b20a8565d77c839ed6f79cf915afd0 (patch) | |
tree | 7c26b5f55f46ef730adb12c099e69285dc178e2b /src | |
parent | aeae4e6f2149f2428c5104f1437661cb4a9fa613 (diff) | |
download | openbsd-2b8dd79762b20a8565d77c839ed6f79cf915afd0.tar.gz openbsd-2b8dd79762b20a8565d77c839ed6f79cf915afd0.tar.bz2 openbsd-2b8dd79762b20a8565d77c839ed6f79cf915afd0.zip |
Add a regression test that builds up the handshake state table
from graph information and cross-checks it against the state
table in tls13_handshake.c.
with help from jsing
Diffstat (limited to 'src')
-rw-r--r-- | src/regress/lib/libssl/handshake/Makefile | 6 | ||||
-rw-r--r-- | src/regress/lib/libssl/handshake/handshake_table.c | 394 |
2 files changed, 399 insertions, 1 deletions
diff --git a/src/regress/lib/libssl/handshake/Makefile b/src/regress/lib/libssl/handshake/Makefile index 151496fcbc..8d42038a27 100644 --- a/src/regress/lib/libssl/handshake/Makefile +++ b/src/regress/lib/libssl/handshake/Makefile | |||
@@ -1,5 +1,6 @@ | |||
1 | # $OpenBSD: Makefile,v 1.1 2019/01/20 06:47:38 tb Exp $ | 1 | # $OpenBSD: Makefile,v 1.2 2019/01/23 04:50:24 tb Exp $ |
2 | 2 | ||
3 | PROGS += handshake_table | ||
3 | PROGS += valid_handshakes_terminate | 4 | PROGS += valid_handshakes_terminate |
4 | 5 | ||
5 | .for p in ${PROGS} | 6 | .for p in ${PROGS} |
@@ -11,6 +12,9 @@ DPADD = ${LIBCRYPTO} ${LIBSSL} | |||
11 | WARNINGS = Yes | 12 | WARNINGS = Yes |
12 | CFLAGS += -DLIBRESSL_INTERNAL -Wundef -Werror -I$(BSDSRCDIR)/lib/libssl | 13 | CFLAGS += -DLIBRESSL_INTERNAL -Wundef -Werror -I$(BSDSRCDIR)/lib/libssl |
13 | 14 | ||
15 | print: handshake_table | ||
16 | ./handshake_table -C | ||
17 | |||
14 | .for p in ${PROGS} | 18 | .for p in ${PROGS} |
15 | run-$p: $p | 19 | run-$p: $p |
16 | @echo '\n======== $@ ========' | 20 | @echo '\n======== $@ ========' |
diff --git a/src/regress/lib/libssl/handshake/handshake_table.c b/src/regress/lib/libssl/handshake/handshake_table.c new file mode 100644 index 0000000000..5a94d5806a --- /dev/null +++ b/src/regress/lib/libssl/handshake/handshake_table.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* $OpenBSD: handshake_table.c,v 1.1 2019/01/23 04:50:24 tb Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2019 Theo Buehler <tb@openbsd.org> | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | #include <err.h> | ||
19 | #include <stdint.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <unistd.h> | ||
23 | |||
24 | #include "tls13_handshake.h" | ||
25 | |||
26 | /* | ||
27 | * From RFC 8446: | ||
28 | * | ||
29 | * Appendix A. State Machine | ||
30 | * | ||
31 | * This appendix provides a summary of the legal state transitions for | ||
32 | * the client and server handshakes. State names (in all capitals, | ||
33 | * e.g., START) have no formal meaning but are provided for ease of | ||
34 | * comprehension. Actions which are taken only in certain circumstances | ||
35 | * are indicated in []. The notation "K_{send,recv} = foo" means "set | ||
36 | * the send/recv key to the given key". | ||
37 | * | ||
38 | * A.1. Client | ||
39 | * | ||
40 | * START <----+ | ||
41 | * Send ClientHello | | Recv HelloRetryRequest | ||
42 | * [K_send = early data] | | | ||
43 | * v | | ||
44 | * / WAIT_SH ----+ | ||
45 | * | | Recv ServerHello | ||
46 | * | | K_recv = handshake | ||
47 | * Can | V | ||
48 | * send | WAIT_EE | ||
49 | * early | | Recv EncryptedExtensions | ||
50 | * data | +--------+--------+ | ||
51 | * | Using | | Using certificate | ||
52 | * | PSK | v | ||
53 | * | | WAIT_CERT_CR | ||
54 | * | | Recv | | Recv CertificateRequest | ||
55 | * | | Certificate | v | ||
56 | * | | | WAIT_CERT | ||
57 | * | | | | Recv Certificate | ||
58 | * | | v v | ||
59 | * | | WAIT_CV | ||
60 | * | | | Recv CertificateVerify | ||
61 | * | +> WAIT_FINISHED <+ | ||
62 | * | | Recv Finished | ||
63 | * \ | [Send EndOfEarlyData] | ||
64 | * | K_send = handshake | ||
65 | * | [Send Certificate [+ CertificateVerify]] | ||
66 | * Can send | Send Finished | ||
67 | * app data --> | K_send = K_recv = application | ||
68 | * after here v | ||
69 | * CONNECTED | ||
70 | * | ||
71 | * Note that with the transitions as shown above, clients may send | ||
72 | * alerts that derive from post-ServerHello messages in the clear or | ||
73 | * with the early data keys. If clients need to send such alerts, they | ||
74 | * SHOULD first rekey to the handshake keys if possible. | ||
75 | * | ||
76 | */ | ||
77 | |||
78 | extern enum tls13_message_type handshakes[][TLS13_NUM_MESSAGE_TYPES]; | ||
79 | |||
80 | struct child { | ||
81 | enum tls13_message_type mt; | ||
82 | uint8_t flag; | ||
83 | uint8_t forced; | ||
84 | uint8_t illegal; | ||
85 | }; | ||
86 | |||
87 | #define DEFAULT 0x00 | ||
88 | |||
89 | static struct child stateinfo[][TLS13_NUM_MESSAGE_TYPES] = { | ||
90 | [CLIENT_HELLO] = { | ||
91 | { SERVER_HELLO, NEGOTIATED, 0, 0 }, | ||
92 | }, | ||
93 | [SERVER_HELLO] = { | ||
94 | { SERVER_ENCRYPTED_EXTENSIONS, DEFAULT, 0, 0 }, | ||
95 | { CLIENT_HELLO_RETRY, WITH_HRR, 0, 0 }, | ||
96 | }, | ||
97 | [CLIENT_HELLO_RETRY] = { | ||
98 | { SERVER_ENCRYPTED_EXTENSIONS, DEFAULT, 0, 0}, | ||
99 | }, | ||
100 | [SERVER_ENCRYPTED_EXTENSIONS] = { | ||
101 | { SERVER_CERTIFICATE_REQUEST, DEFAULT, 0, 0}, | ||
102 | { SERVER_CERTIFICATE, WITHOUT_CR, 0, 0}, | ||
103 | { SERVER_FINISHED, WITH_PSK, 0, 0}, | ||
104 | }, | ||
105 | [SERVER_CERTIFICATE_REQUEST] = { | ||
106 | { SERVER_CERTIFICATE, DEFAULT, 0, 0}, | ||
107 | }, | ||
108 | [SERVER_CERTIFICATE] = { | ||
109 | { SERVER_CERTIFICATE_VERIFY, DEFAULT, 0, 0}, | ||
110 | }, | ||
111 | [SERVER_CERTIFICATE_VERIFY] = { | ||
112 | { SERVER_FINISHED, DEFAULT, 0, 0}, | ||
113 | }, | ||
114 | [SERVER_FINISHED] = { | ||
115 | { CLIENT_FINISHED, DEFAULT, WITHOUT_CR | WITH_PSK, 0 }, | ||
116 | { CLIENT_CERTIFICATE, DEFAULT, 0, WITHOUT_CR | WITH_PSK }, | ||
117 | /* { CLIENT_END_OF_EARLY_DATA, WITH_0RTT, 0, 0}, */ | ||
118 | }, | ||
119 | [CLIENT_CERTIFICATE] = { | ||
120 | { CLIENT_FINISHED, DEFAULT, 0, 0}, | ||
121 | { CLIENT_CERTIFICATE_VERIFY, WITH_CCV, 0, 0}, | ||
122 | }, | ||
123 | [CLIENT_CERTIFICATE_VERIFY] = { | ||
124 | { CLIENT_FINISHED, DEFAULT, 0, 0}, | ||
125 | }, | ||
126 | [CLIENT_FINISHED] = { | ||
127 | { APPLICATION_DATA, DEFAULT, 0, 0}, | ||
128 | }, | ||
129 | [APPLICATION_DATA] = { | ||
130 | { 0, DEFAULT, 0, 0}, | ||
131 | }, | ||
132 | }; | ||
133 | |||
134 | const char *flag2str(uint8_t flag); | ||
135 | void print_flags(uint8_t flags); | ||
136 | const char *mt2str(enum tls13_message_type mt); | ||
137 | void build_table(enum tls13_message_type | ||
138 | table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], struct child current, | ||
139 | struct child end, struct child path[], uint8_t flags, unsigned int depth); | ||
140 | void print_entry(enum tls13_message_type path[TLS13_NUM_MESSAGE_TYPES], | ||
141 | uint8_t flags); | ||
142 | __dead void usage(void); | ||
143 | int verify_table(enum tls13_message_type | ||
144 | table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], int print); | ||
145 | |||
146 | const char * | ||
147 | flag2str(uint8_t flag) | ||
148 | { | ||
149 | const char *ret; | ||
150 | |||
151 | if (flag & (flag - 1)) | ||
152 | errx(1, "more than one bit is set"); | ||
153 | |||
154 | switch (flag) { | ||
155 | case DEFAULT: | ||
156 | ret = ""; | ||
157 | break; | ||
158 | case NEGOTIATED: | ||
159 | ret = "NEGOTIATED"; | ||
160 | break; | ||
161 | case WITHOUT_CR: | ||
162 | ret = "WITHOUT_CR"; | ||
163 | break; | ||
164 | case WITH_HRR: | ||
165 | ret = "WITH_HRR"; | ||
166 | break; | ||
167 | case WITH_PSK: | ||
168 | ret = "WITH_PSK"; | ||
169 | break; | ||
170 | case WITH_CCV: | ||
171 | ret = "WITH_CCV"; | ||
172 | break; | ||
173 | case WITH_0RTT: | ||
174 | ret = "WITH_0RTT"; | ||
175 | break; | ||
176 | default: | ||
177 | ret = "UNKNOWN"; | ||
178 | } | ||
179 | |||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | const char * | ||
184 | mt2str(enum tls13_message_type mt) | ||
185 | { | ||
186 | const char *ret; | ||
187 | |||
188 | switch (mt) { | ||
189 | case INVALID: | ||
190 | ret = "INVALID"; | ||
191 | break; | ||
192 | case CLIENT_HELLO: | ||
193 | ret = "CLIENT_HELLO"; | ||
194 | break; | ||
195 | case CLIENT_HELLO_RETRY: | ||
196 | ret = "CLIENT_HELLO_RETRY"; | ||
197 | break; | ||
198 | case CLIENT_END_OF_EARLY_DATA: | ||
199 | ret = "CLIENT_END_OF_EARLY_DATA"; | ||
200 | break; | ||
201 | case CLIENT_CERTIFICATE: | ||
202 | ret = "CLIENT_CERTIFICATE"; | ||
203 | break; | ||
204 | case CLIENT_CERTIFICATE_VERIFY: | ||
205 | ret = "CLIENT_CERTIFICATE_VERIFY"; | ||
206 | break; | ||
207 | case CLIENT_FINISHED: | ||
208 | ret = "CLIENT_FINISHED"; | ||
209 | break; | ||
210 | case CLIENT_KEY_UPDATE: | ||
211 | ret = "CLIENT_KEY_UPDATE"; | ||
212 | break; | ||
213 | case SERVER_HELLO: | ||
214 | ret = "SERVER_HELLO"; | ||
215 | break; | ||
216 | case SERVER_NEW_SESSION_TICKET: | ||
217 | ret = "SERVER_NEW_SESSION_TICKET"; | ||
218 | break; | ||
219 | case SERVER_ENCRYPTED_EXTENSIONS: | ||
220 | ret = "SERVER_ENCRYPTED_EXTENSIONS"; | ||
221 | break; | ||
222 | case SERVER_CERTIFICATE: | ||
223 | ret = "SERVER_CERTIFICATE"; | ||
224 | break; | ||
225 | case SERVER_CERTIFICATE_VERIFY: | ||
226 | ret = "SERVER_CERTIFICATE_VERIFY"; | ||
227 | break; | ||
228 | case SERVER_CERTIFICATE_REQUEST: | ||
229 | ret = "SERVER_CERTIFICATE_REQUEST"; | ||
230 | break; | ||
231 | case SERVER_FINISHED: | ||
232 | ret = "SERVER_FINISHED"; | ||
233 | break; | ||
234 | case APPLICATION_DATA: | ||
235 | ret = "APPLICATION_DATA"; | ||
236 | break; | ||
237 | case TLS13_NUM_MESSAGE_TYPES: | ||
238 | ret = "TLS13_NUM_MESSAGE_TYPES"; | ||
239 | break; | ||
240 | default: | ||
241 | ret = "UNKNOWN"; | ||
242 | break; | ||
243 | } | ||
244 | |||
245 | return ret; | ||
246 | } | ||
247 | |||
248 | void | ||
249 | print_flags(uint8_t flags) | ||
250 | { | ||
251 | int first = 1, i; | ||
252 | |||
253 | for (i = 0; i < 8; i++) { | ||
254 | uint8_t set = flags & (1U << i); | ||
255 | |||
256 | if (set) { | ||
257 | printf("%s%s", first ? "" : " | ", flag2str(set)); | ||
258 | first = 0; | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | |||
263 | void | ||
264 | print_entry(enum tls13_message_type path[TLS13_NUM_MESSAGE_TYPES], | ||
265 | uint8_t flags) | ||
266 | { | ||
267 | int i; | ||
268 | |||
269 | printf("\t["); | ||
270 | print_flags(flags); | ||
271 | printf("] = {\n"); | ||
272 | |||
273 | for (i = 0; i < TLS13_NUM_MESSAGE_TYPES; i++) { | ||
274 | if (path[i] == 0) | ||
275 | break; | ||
276 | printf("\t\t%s,\n", mt2str(path[i])); | ||
277 | } | ||
278 | printf("\t},\n"); | ||
279 | } | ||
280 | |||
281 | void | ||
282 | build_table(enum tls13_message_type table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], | ||
283 | struct child current, struct child end, struct child path[], uint8_t flags, | ||
284 | unsigned int depth) | ||
285 | { | ||
286 | unsigned int i; | ||
287 | |||
288 | if (depth >= TLS13_NUM_MESSAGE_TYPES - 1) | ||
289 | errx(1, "recursed too deeply"); | ||
290 | |||
291 | /* Record current node. */ | ||
292 | path[depth++] = current; | ||
293 | flags |= current.flag; | ||
294 | |||
295 | /* If we haven't reached the end, recurse over the children. */ | ||
296 | if (current.mt != end.mt) { | ||
297 | for (i = 0; stateinfo[current.mt][i].mt != 0; i++) { | ||
298 | struct child child = stateinfo[current.mt][i]; | ||
299 | int forced = stateinfo[current.mt][i].forced; | ||
300 | int illegal = stateinfo[current.mt][i].illegal; | ||
301 | |||
302 | if ((forced == 0 || (forced & flags)) && | ||
303 | (illegal == 0 || !(illegal & flags))) | ||
304 | build_table(table, child, end, path, flags, | ||
305 | depth); | ||
306 | } | ||
307 | return; | ||
308 | } | ||
309 | |||
310 | if (flags == 0) | ||
311 | errx(1, "path does not set flags"); | ||
312 | |||
313 | if (table[flags][0] != 0) | ||
314 | errx(1, "path traversed twice"); | ||
315 | |||
316 | for (i = 0; i < depth; i++) | ||
317 | table[flags][i] = path[i].mt; | ||
318 | } | ||
319 | |||
320 | int | ||
321 | verify_table(enum tls13_message_type table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], | ||
322 | int print) | ||
323 | { | ||
324 | int success = 1, i; | ||
325 | uint8_t flags = 0; | ||
326 | |||
327 | do { | ||
328 | flags++; | ||
329 | if (table[flags][0] == 0) | ||
330 | continue; | ||
331 | |||
332 | for (i = 0; i < TLS13_NUM_MESSAGE_TYPES; i++) { | ||
333 | if (table[flags][i] != handshakes[flags][i]) { | ||
334 | printf("incorrrect entry %d of handshake ", i); | ||
335 | print_flags(flags); | ||
336 | printf("\n"); | ||
337 | success = 0; | ||
338 | } | ||
339 | } | ||
340 | |||
341 | if (print) | ||
342 | print_entry(table[flags], flags); | ||
343 | } while(flags != UINT8_MAX); | ||
344 | |||
345 | return success; | ||
346 | } | ||
347 | |||
348 | __dead void | ||
349 | usage(void) | ||
350 | { | ||
351 | fprintf(stderr, "usage: handshake_table [-C]\n"); | ||
352 | exit(1); | ||
353 | } | ||
354 | |||
355 | int | ||
356 | main(int argc, char *argv[]) | ||
357 | { | ||
358 | static enum tls13_message_type | ||
359 | hs_table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES]; | ||
360 | struct child start = { | ||
361 | CLIENT_HELLO, NEGOTIATED, 0, 0, | ||
362 | }; | ||
363 | struct child end = { | ||
364 | APPLICATION_DATA, DEFAULT, 0, 0, | ||
365 | }; | ||
366 | struct child path[TLS13_NUM_MESSAGE_TYPES] = {{ 0 }}; | ||
367 | uint8_t flags = 0; | ||
368 | unsigned int depth = 0; | ||
369 | int ch, print = 0; | ||
370 | |||
371 | while ((ch = getopt(argc, argv, "C")) != -1) { | ||
372 | switch (ch) { | ||
373 | case 'C': | ||
374 | print = 1; | ||
375 | break; | ||
376 | default: | ||
377 | usage(); | ||
378 | } | ||
379 | } | ||
380 | argc -= optind; | ||
381 | argv += optind; | ||
382 | |||
383 | if (argc != 0) | ||
384 | usage(); | ||
385 | |||
386 | build_table(hs_table, start, end, path, flags, depth); | ||
387 | if (!verify_table(hs_table, print)) | ||
388 | return 1; | ||
389 | |||
390 | if (!print) | ||
391 | printf("SUCCESS\n"); | ||
392 | |||
393 | return 0; | ||
394 | } | ||