1 /* $OpenBSD: handshake_table.c,v 1.4 2019/01/23 23:38:44 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 struct child { 79 enum tls13_message_type mt; 80 uint8_t flag; 81 uint8_t forced; 82 uint8_t illegal; 83 }; 84 85 #define DEFAULT 0x00 86 87 static struct child stateinfo[][TLS13_NUM_MESSAGE_TYPES] = { 88 [CLIENT_HELLO] = { 89 {SERVER_HELLO, NEGOTIATED, 0, 0}, 90 }, 91 [SERVER_HELLO] = { 92 {SERVER_ENCRYPTED_EXTENSIONS, DEFAULT, 0, 0}, 93 {CLIENT_HELLO_RETRY, WITH_HRR, 0, 0}, 94 }, 95 [CLIENT_HELLO_RETRY] = { 96 {SERVER_ENCRYPTED_EXTENSIONS, DEFAULT, 0, 0}, 97 }, 98 [SERVER_ENCRYPTED_EXTENSIONS] = { 99 {SERVER_CERTIFICATE_REQUEST, DEFAULT, 0, 0}, 100 {SERVER_CERTIFICATE, WITHOUT_CR, 0, 0}, 101 {SERVER_FINISHED, WITH_PSK, 0, 0}, 102 }, 103 [SERVER_CERTIFICATE_REQUEST] = { 104 {SERVER_CERTIFICATE, DEFAULT, 0, 0}, 105 }, 106 [SERVER_CERTIFICATE] = { 107 {SERVER_CERTIFICATE_VERIFY, DEFAULT, 0, 0}, 108 }, 109 [SERVER_CERTIFICATE_VERIFY] = { 110 {SERVER_FINISHED, DEFAULT, 0, 0}, 111 }, 112 [SERVER_FINISHED] = { 113 {CLIENT_FINISHED, DEFAULT, WITHOUT_CR | WITH_PSK, 0}, 114 {CLIENT_CERTIFICATE, DEFAULT, 0, WITHOUT_CR | WITH_PSK}, 115 /* {CLIENT_END_OF_EARLY_DATA, WITH_0RTT, 0, 0}, */ 116 }, 117 [CLIENT_CERTIFICATE] = { 118 {CLIENT_FINISHED, DEFAULT, 0, 0}, 119 {CLIENT_CERTIFICATE_VERIFY, WITH_CCV, 0, 0}, 120 }, 121 [CLIENT_CERTIFICATE_VERIFY] = { 122 {CLIENT_FINISHED, DEFAULT, 0, 0}, 123 }, 124 [CLIENT_FINISHED] = { 125 {APPLICATION_DATA, DEFAULT, 0, 0}, 126 }, 127 [APPLICATION_DATA] = { 128 {0, DEFAULT, 0, 0}, 129 }, 130 }; 131 132 void build_table(enum tls13_message_type 133 table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], struct child current, 134 struct child end, struct child path[], uint8_t flags, unsigned int depth); 135 size_t count_handshakes(void); 136 const char *flag2str(uint8_t flag); 137 const char *mt2str(enum tls13_message_type mt); 138 void print_entry(enum tls13_message_type path[TLS13_NUM_MESSAGE_TYPES], 139 uint8_t flags); 140 void print_flags(uint8_t flags); 141 __dead void usage(void); 142 int verify_table(enum tls13_message_type 143 table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], int print); 144 145 const char * 146 flag2str(uint8_t flag) 147 { 148 const char *ret; 149 150 if (flag & (flag - 1)) 151 errx(1, "more than one bit is set"); 152 153 switch (flag) { 154 case INITIAL: 155 ret = "INITIAL"; 156 break; 157 case NEGOTIATED: 158 ret = "NEGOTIATED"; 159 break; 160 case WITHOUT_CR: 161 ret = "WITHOUT_CR"; 162 break; 163 case WITH_HRR: 164 ret = "WITH_HRR"; 165 break; 166 case WITH_PSK: 167 ret = "WITH_PSK"; 168 break; 169 case WITH_CCV: 170 ret = "WITH_CCV"; 171 break; 172 case WITH_0RTT: 173 ret = "WITH_0RTT"; 174 break; 175 default: 176 ret = "UNKNOWN"; 177 } 178 179 return ret; 180 } 181 182 const char * 183 mt2str(enum tls13_message_type mt) 184 { 185 const char *ret; 186 187 switch (mt) { 188 case INVALID: 189 ret = "INVALID"; 190 break; 191 case CLIENT_HELLO: 192 ret = "CLIENT_HELLO"; 193 break; 194 case CLIENT_HELLO_RETRY: 195 ret = "CLIENT_HELLO_RETRY"; 196 break; 197 case CLIENT_END_OF_EARLY_DATA: 198 ret = "CLIENT_END_OF_EARLY_DATA"; 199 break; 200 case CLIENT_CERTIFICATE: 201 ret = "CLIENT_CERTIFICATE"; 202 break; 203 case CLIENT_CERTIFICATE_VERIFY: 204 ret = "CLIENT_CERTIFICATE_VERIFY"; 205 break; 206 case CLIENT_FINISHED: 207 ret = "CLIENT_FINISHED"; 208 break; 209 case CLIENT_KEY_UPDATE: 210 ret = "CLIENT_KEY_UPDATE"; 211 break; 212 case SERVER_HELLO: 213 ret = "SERVER_HELLO"; 214 break; 215 case SERVER_NEW_SESSION_TICKET: 216 ret = "SERVER_NEW_SESSION_TICKET"; 217 break; 218 case SERVER_ENCRYPTED_EXTENSIONS: 219 ret = "SERVER_ENCRYPTED_EXTENSIONS"; 220 break; 221 case SERVER_CERTIFICATE: 222 ret = "SERVER_CERTIFICATE"; 223 break; 224 case SERVER_CERTIFICATE_VERIFY: 225 ret = "SERVER_CERTIFICATE_VERIFY"; 226 break; 227 case SERVER_CERTIFICATE_REQUEST: 228 ret = "SERVER_CERTIFICATE_REQUEST"; 229 break; 230 case SERVER_FINISHED: 231 ret = "SERVER_FINISHED"; 232 break; 233 case APPLICATION_DATA: 234 ret = "APPLICATION_DATA"; 235 break; 236 case TLS13_NUM_MESSAGE_TYPES: 237 ret = "TLS13_NUM_MESSAGE_TYPES"; 238 break; 239 default: 240 ret = "UNKNOWN"; 241 break; 242 } 243 244 return ret; 245 } 246 247 void 248 print_flags(uint8_t flags) 249 { 250 int first = 1, i; 251 252 if (flags == 0) { 253 printf("%s", flag2str(flags)); 254 return; 255 } 256 257 for (i = 0; i < 8; i++) { 258 uint8_t set = flags & (1U << i); 259 260 if (set) { 261 printf("%s%s", first ? "" : " | ", flag2str(set)); 262 first = 0; 263 } 264 } 265 } 266 267 void 268 print_entry(enum tls13_message_type path[TLS13_NUM_MESSAGE_TYPES], 269 uint8_t flags) 270 { 271 int i; 272 273 printf("\t["); 274 print_flags(flags); 275 printf("] = {\n"); 276 277 for (i = 0; i < TLS13_NUM_MESSAGE_TYPES; i++) { 278 if (path[i] == 0) 279 break; 280 printf("\t\t%s,\n", mt2str(path[i])); 281 } 282 printf("\t},\n"); 283 } 284 285 extern enum tls13_message_type handshakes[][TLS13_NUM_MESSAGE_TYPES]; 286 extern size_t handshake_count; 287 288 size_t 289 count_handshakes(void) 290 { 291 size_t ret = 0, i; 292 293 for (i = 0; i < handshake_count; i++) { 294 if (handshakes[i][0] != INVALID) 295 ret++; 296 } 297 298 return ret; 299 } 300 301 void 302 build_table(enum tls13_message_type table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], 303 struct child current, struct child end, struct child path[], uint8_t flags, 304 unsigned int depth) 305 { 306 unsigned int i; 307 308 if (depth >= TLS13_NUM_MESSAGE_TYPES - 1) 309 errx(1, "recursed too deeply"); 310 311 /* Record current node. */ 312 path[depth++] = current; 313 flags |= current.flag; 314 315 /* If we haven't reached the end, recurse over the children. */ 316 if (current.mt != end.mt) { 317 for (i = 0; stateinfo[current.mt][i].mt != 0; i++) { 318 struct child child = stateinfo[current.mt][i]; 319 int forced = stateinfo[current.mt][i].forced; 320 int illegal = stateinfo[current.mt][i].illegal; 321 322 if ((forced == 0 || (forced & flags)) && 323 (illegal == 0 || !(illegal & flags))) 324 build_table(table, child, end, path, flags, 325 depth); 326 } 327 return; 328 } 329 330 if (flags == 0) 331 errx(1, "path does not set flags"); 332 333 if (table[flags][0] != 0) 334 errx(1, "path traversed twice"); 335 336 for (i = 0; i < depth; i++) 337 table[flags][i] = path[i].mt; 338 } 339 340 int 341 verify_table(enum tls13_message_type table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES], 342 int print) 343 { 344 int success = 1, i; 345 size_t num_valid, num_found = 0; 346 uint8_t flags = 0; 347 348 do { 349 if (table[flags][0] == 0) 350 continue; 351 352 num_found++; 353 354 for (i = 0; i < TLS13_NUM_MESSAGE_TYPES; i++) { 355 if (table[flags][i] != handshakes[flags][i]) { 356 printf("incorrect entry %d of handshake ", i); 357 print_flags(flags); 358 printf("\n"); 359 success = 0; 360 } 361 } 362 363 if (print) 364 print_entry(table[flags], flags); 365 } while(++flags != 0); 366 367 num_valid = count_handshakes(); 368 if (num_valid != num_found) { 369 printf("incorrect number of handshakes: want %zu, got %zu.\n", 370 num_valid, num_found); 371 success = 0; 372 } 373 374 return success; 375 } 376 377 __dead void 378 usage(void) 379 { 380 fprintf(stderr, "usage: handshake_table [-C]\n"); 381 exit(1); 382 } 383 384 int 385 main(int argc, char *argv[]) 386 { 387 static enum tls13_message_type 388 hs_table[UINT8_MAX][TLS13_NUM_MESSAGE_TYPES] = { 389 [INITIAL] = { 390 CLIENT_HELLO, 391 SERVER_HELLO, 392 }, 393 }; 394 struct child start = { 395 CLIENT_HELLO, NEGOTIATED, 0, 0, 396 }; 397 struct child end = { 398 APPLICATION_DATA, DEFAULT, 0, 0, 399 }; 400 struct child path[TLS13_NUM_MESSAGE_TYPES] = {{0}}; 401 uint8_t flags = 0; 402 unsigned int depth = 0; 403 int ch, print = 0; 404 405 while ((ch = getopt(argc, argv, "C")) != -1) { 406 switch (ch) { 407 case 'C': 408 print = 1; 409 break; 410 default: 411 usage(); 412 } 413 } 414 argc -= optind; 415 argv += optind; 416 417 if (argc != 0) 418 usage(); 419 420 build_table(hs_table, start, end, path, flags, depth); 421 if (!verify_table(hs_table, print)) 422 return 1; 423 424 if (!print) 425 printf("SUCCESS\n"); 426 427 return 0; 428 } 429