comparison src/stream/ngx_stream_quic_module.c @ 8694:cef042935003 quic

QUIC: the "quic_host_key" directive. The token generation in QUIC is reworked. Single host key is used to generate all required keys of needed sizes using HKDF. The "quic_stateless_reset_token_key" directive is removed. Instead, the "quic_host_key" directive is used, which reads key from file, or sets it to random bytes if not specified.
author Vladimir Homutov <vl@nginx.com>
date Mon, 08 Feb 2021 16:49:33 +0300
parents dffb66fb783b
children b4e6b7049984
comparison
equal deleted inserted replaced
8693:3956bbf91002 8694:cef042935003
6 6
7 7
8 #include <ngx_config.h> 8 #include <ngx_config.h>
9 #include <ngx_core.h> 9 #include <ngx_core.h>
10 #include <ngx_stream.h> 10 #include <ngx_stream.h>
11
12 #include <ngx_event_quic_protection.h>
11 13
12 14
13 static ngx_int_t ngx_stream_variable_quic(ngx_stream_session_t *s, 15 static ngx_int_t ngx_stream_variable_quic(ngx_stream_session_t *s,
14 ngx_stream_variable_value_t *v, uintptr_t data); 16 ngx_stream_variable_value_t *v, uintptr_t data);
15 static ngx_int_t ngx_stream_quic_add_variables(ngx_conf_t *cf); 17 static ngx_int_t ngx_stream_quic_add_variables(ngx_conf_t *cf);
18 void *child); 20 void *child);
19 static char *ngx_stream_quic_max_ack_delay(ngx_conf_t *cf, void *post, 21 static char *ngx_stream_quic_max_ack_delay(ngx_conf_t *cf, void *post,
20 void *data); 22 void *data);
21 static char *ngx_stream_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, 23 static char *ngx_stream_quic_max_udp_payload_size(ngx_conf_t *cf, void *post,
22 void *data); 24 void *data);
25 static char *ngx_stream_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd,
26 void *conf);
23 27
24 28
25 static ngx_conf_post_t ngx_stream_quic_max_ack_delay_post = 29 static ngx_conf_post_t ngx_stream_quic_max_ack_delay_post =
26 { ngx_stream_quic_max_ack_delay }; 30 { ngx_stream_quic_max_ack_delay };
27 static ngx_conf_post_t ngx_stream_quic_max_udp_payload_size_post = 31 static ngx_conf_post_t ngx_stream_quic_max_udp_payload_size_post =
124 ngx_conf_set_flag_slot, 128 ngx_conf_set_flag_slot,
125 NGX_STREAM_SRV_CONF_OFFSET, 129 NGX_STREAM_SRV_CONF_OFFSET,
126 offsetof(ngx_quic_conf_t, retry), 130 offsetof(ngx_quic_conf_t, retry),
127 NULL }, 131 NULL },
128 132
129 { ngx_string("quic_stateless_reset_token_key"), 133 { ngx_string("quic_host_key"),
130 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, 134 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
131 ngx_conf_set_str_slot, 135 ngx_stream_quic_host_key,
132 NGX_STREAM_SRV_CONF_OFFSET, 136 NGX_STREAM_SRV_CONF_OFFSET,
133 offsetof(ngx_quic_conf_t, sr_token_key), 137 0,
134 NULL }, 138 NULL },
135 139
136 ngx_null_command 140 ngx_null_command
137 }; 141 };
138 142
170 { ngx_string("quic"), NULL, ngx_stream_variable_quic, 0, 0, 0 }, 174 { ngx_string("quic"), NULL, ngx_stream_variable_quic, 0, 0, 0 },
171 175
172 ngx_stream_null_variable 176 ngx_stream_null_variable
173 }; 177 };
174 178
179 static ngx_str_t ngx_stream_quic_salt = ngx_string("ngx_quic");
180
175 181
176 static ngx_int_t 182 static ngx_int_t
177 ngx_stream_variable_quic(ngx_stream_session_t *s, 183 ngx_stream_variable_quic(ngx_stream_session_t *s,
178 ngx_stream_variable_value_t *v, uintptr_t data) 184 ngx_stream_variable_value_t *v, uintptr_t data)
179 { 185 {
227 * 233 *
228 * conf->tp.original_dcid = { 0, NULL }; 234 * conf->tp.original_dcid = { 0, NULL };
229 * conf->tp.initial_scid = { 0, NULL }; 235 * conf->tp.initial_scid = { 0, NULL };
230 * conf->tp.retry_scid = { 0, NULL }; 236 * conf->tp.retry_scid = { 0, NULL };
231 * conf->tp.preferred_address = NULL 237 * conf->tp.preferred_address = NULL
232 * conf->sr_token_key = { 0, NULL } 238 * conf->host_key = { 0, NULL }
233 * conf->require_alpn = 0; 239 * conf->require_alpn = 0;
234 */ 240 */
235 241
236 conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC; 242 conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC;
237 conf->tp.max_ack_delay = NGX_CONF_UNSET_MSEC; 243 conf->tp.max_ack_delay = NGX_CONF_UNSET_MSEC;
303 ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit, 309 ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit,
304 prev->tp.active_connection_id_limit, 2); 310 prev->tp.active_connection_id_limit, 2);
305 311
306 ngx_conf_merge_value(conf->retry, prev->retry, 0); 312 ngx_conf_merge_value(conf->retry, prev->retry, 0);
307 313
308 if (RAND_bytes(conf->token_key, sizeof(conf->token_key)) <= 0) { 314 ngx_conf_merge_str_value(conf->host_key, prev->host_key, "");
309 return NGX_CONF_ERROR; 315
310 } 316 if (conf->host_key.len == 0) {
311 317
312 ngx_conf_merge_str_value(conf->sr_token_key, prev->sr_token_key, ""); 318 conf->host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN;
313 319 conf->host_key.data = ngx_palloc(cf->pool, conf->host_key.len);
314 if (conf->sr_token_key.len == 0) { 320 if (conf->host_key.data == NULL) {
315 conf->sr_token_key.len = NGX_QUIC_DEFAULT_SRT_KEY_LEN;
316
317 conf->sr_token_key.data = ngx_pnalloc(cf->pool, conf->sr_token_key.len);
318 if (conf->sr_token_key.data == NULL) {
319 return NGX_CONF_ERROR; 321 return NGX_CONF_ERROR;
320 } 322 }
321 323
322 if (RAND_bytes(conf->sr_token_key.data, conf->sr_token_key.len) <= 0) { 324 if (RAND_bytes(conf->host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN)
325 <= 0)
326 {
323 return NGX_CONF_ERROR; 327 return NGX_CONF_ERROR;
324 } 328 }
329 }
330
331 if (ngx_quic_derive_key(cf->log, "av_token_key",
332 &conf->host_key, &ngx_stream_quic_salt,
333 conf->av_token_key, NGX_QUIC_AV_KEY_LEN)
334 != NGX_OK)
335 {
336 return NGX_CONF_ERROR;
337 }
338
339 if (ngx_quic_derive_key(cf->log, "sr_token_key",
340 &conf->host_key, &ngx_stream_quic_salt,
341 conf->sr_token_key, NGX_QUIC_SR_KEY_LEN)
342 != NGX_OK)
343 {
344 return NGX_CONF_ERROR;
325 } 345 }
326 346
327 scf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_ssl_module); 347 scf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_ssl_module);
328 conf->ssl = &scf->ssl; 348 conf->ssl = &scf->ssl;
329 349
364 return NGX_CONF_ERROR; 384 return NGX_CONF_ERROR;
365 } 385 }
366 386
367 return NGX_CONF_OK; 387 return NGX_CONF_OK;
368 } 388 }
389
390
391 static char *
392 ngx_stream_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
393 {
394 ngx_quic_conf_t *qcf = conf;
395
396 u_char *buf;
397 size_t size;
398 ssize_t n;
399 ngx_str_t *value;
400 ngx_file_t file;
401 ngx_file_info_t fi;
402
403 if (qcf->host_key.len) {
404 return "is duplicate";
405 }
406
407 buf = NULL;
408 #if (NGX_SUPPRESS_WARN)
409 size = 0;
410 #endif
411
412 value = cf->args->elts;
413
414 if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) {
415 return NGX_CONF_ERROR;
416 }
417
418 ngx_memzero(&file, sizeof(ngx_file_t));
419 file.name = value[1];
420 file.log = cf->log;
421
422 file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
423
424 if (file.fd == NGX_INVALID_FILE) {
425 ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
426 ngx_open_file_n " \"%V\" failed", &file.name);
427 return NGX_CONF_ERROR;
428 }
429
430 if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
431 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
432 ngx_fd_info_n " \"%V\" failed", &file.name);
433 goto failed;
434 }
435
436 size = ngx_file_size(&fi);
437
438 if (size == 0) {
439 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
440 "\"%V\" zero key size", &file.name);
441 goto failed;
442 }
443
444 buf = ngx_pnalloc(cf->pool, size);
445 if (buf == NULL) {
446 goto failed;
447 }
448
449 n = ngx_read_file(&file, buf, size, 0);
450
451 if (n == NGX_ERROR) {
452 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
453 ngx_read_file_n " \"%V\" failed", &file.name);
454 goto failed;
455 }
456
457 if ((size_t) n != size) {
458 ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
459 ngx_read_file_n " \"%V\" returned only "
460 "%z bytes instead of %uz", &file.name, n, size);
461 goto failed;
462 }
463
464 qcf->host_key.data = buf;
465 qcf->host_key.len = n;
466
467 if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
468 ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
469 ngx_close_file_n " \"%V\" failed", &file.name);
470 }
471
472 return NGX_CONF_OK;
473
474 failed:
475
476 if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
477 ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
478 ngx_close_file_n " \"%V\" failed", &file.name);
479 }
480
481 if (buf) {
482 ngx_explicit_memzero(buf, size);
483 }
484
485 return NGX_CONF_ERROR;
486 }