summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortb <>2019-01-23 04:50:24 +0000
committertb <>2019-01-23 04:50:24 +0000
commit2b8dd79762b20a8565d77c839ed6f79cf915afd0 (patch)
tree7c26b5f55f46ef730adb12c099e69285dc178e2b /src
parentaeae4e6f2149f2428c5104f1437661cb4a9fa613 (diff)
downloadopenbsd-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/Makefile6
-rw-r--r--src/regress/lib/libssl/handshake/handshake_table.c394
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
3PROGS += handshake_table
3PROGS += valid_handshakes_terminate 4PROGS += valid_handshakes_terminate
4 5
5.for p in ${PROGS} 6.for p in ${PROGS}
@@ -11,6 +12,9 @@ DPADD = ${LIBCRYPTO} ${LIBSSL}
11WARNINGS = Yes 12WARNINGS = Yes
12CFLAGS += -DLIBRESSL_INTERNAL -Wundef -Werror -I$(BSDSRCDIR)/lib/libssl 13CFLAGS += -DLIBRESSL_INTERNAL -Wundef -Werror -I$(BSDSRCDIR)/lib/libssl
13 14
15print: handshake_table
16 ./handshake_table -C
17
14.for p in ${PROGS} 18.for p in ${PROGS}
15run-$p: $p 19run-$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
78extern enum tls13_message_type handshakes[][TLS13_NUM_MESSAGE_TYPES];
79
80struct 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
89static 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
134const char *flag2str(uint8_t flag);
135void print_flags(uint8_t flags);
136const char *mt2str(enum tls13_message_type mt);
137void 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);
140void print_entry(enum tls13_message_type path[TLS13_NUM_MESSAGE_TYPES],
141 uint8_t flags);
142__dead void usage(void);
143int verify_table(enum tls13_message_type
144 table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], int print);
145
146const char *
147flag2str(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
183const char *
184mt2str(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
248void
249print_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
263void
264print_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
281void
282build_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
320int
321verify_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
349usage(void)
350{
351 fprintf(stderr, "usage: handshake_table [-C]\n");
352 exit(1);
353}
354
355int
356main(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}