[PATCH 1 of 4] QUIC: fixed-length buffers for secrets
Vladimir Homutov
vl at nginx.com
Tue Feb 22 10:23:27 UTC 2022
On Mon, Feb 21, 2022 at 05:51:42PM +0300, Sergey Kandaurov wrote:
> On Mon, Feb 21, 2022 at 02:10:31PM +0300, Vladimir Homutov wrote:
> > Patch subject is complete summary.
> >
> >
> > src/event/quic/ngx_event_quic_protection.c | 202 +++++++++++++++-------------
> > 1 files changed, 105 insertions(+), 97 deletions(-)
> >
> >
>
> > # HG changeset patch
> > # User Vladimir Homutov <vl at nginx.com>
> > # Date 1645440604 -10800
> > # Mon Feb 21 13:50:04 2022 +0300
> > # Branch quic
> > # Node ID 1a0a12bef7f00b5422d449b2d4642fff39e0a47e
> > # Parent 55b38514729b8f848709b31295e72d6886a7a433
> > QUIC: fixed-length buffers for secrets.
> >
> > diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c
> > --- a/src/event/quic/ngx_event_quic_protection.c
> > +++ b/src/event/quic/ngx_event_quic_protection.c
> > @@ -17,6 +17,8 @@
> >
> > #define NGX_QUIC_AES_128_KEY_LEN 16
> >
> > +#define NGX_QUIC_KEY_LEN 32
> > +
> > #define NGX_AES_128_GCM_SHA256 0x1301
> > #define NGX_AES_256_GCM_SHA384 0x1302
> > #define NGX_CHACHA20_POLY1305_SHA256 0x1303
> > @@ -30,6 +32,27 @@
> >
> >
> > typedef struct {
> > + size_t len;
> > + u_char data[SHA256_DIGEST_LENGTH];
> > +} ngx_quic_okm_t;
> > +
> > +typedef struct {
> > + size_t len;
> > + u_char data[NGX_QUIC_KEY_LEN];
> > +} ngx_quic_key_t;
> > +
> > +typedef struct {
> > + size_t len;
> > + u_char data[NGX_QUIC_KEY_LEN];
> > +} ngx_quic_hp_t;
> > +
> > +typedef struct {
> > + size_t len;
> > + u_char data[NGX_QUIC_IV_LEN];
> > +} ngx_quic_iv_t;
>
> Style: two empty lines between struct declarations.
thanks, fixed this
>
> > +
> > +
> > +typedef struct {
> > const ngx_quic_cipher_t *c;
> > const EVP_CIPHER *hp;
> > const EVP_MD *d;
> > @@ -37,10 +60,10 @@ typedef struct {
> >
> >
> > typedef struct ngx_quic_secret_s {
> > - ngx_str_t secret;
> > - ngx_str_t key;
> > - ngx_str_t iv;
> > - ngx_str_t hp;
> > + ngx_quic_okm_t secret;
> > + ngx_quic_key_t key;
> > + ngx_quic_iv_t iv;
> > + ngx_quic_hp_t hp;
> > } ngx_quic_secret_t;
> >
> >
> > @@ -57,6 +80,29 @@ struct ngx_quic_keys_s {
> > };
> >
> >
> > +typedef struct {
> > + size_t out_len;
> > + u_char *out;
> > +
> > + size_t prk_len;
> > + const uint8_t *prk;
> > +
> > + size_t label_len;
> > + const u_char *label;
> > +
> > + size_t info_len;
> > + uint8_t info[20];
> > +} ngx_quic_hkdf_t;
> > +
> > +#define ngx_quic_hkdf_set(label, out, prk) \
> > + { \
> > + (out)->len, (out)->data, \
> > + (prk)->len, (prk)->data, \
> > + (sizeof(label) - 1), (u_char *)(label), \
> > + 0, { 0 } \
> > + }
> > +
> > +
> > static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len,
> > const EVP_MD *digest, const u_char *prk, size_t prk_len,
> > const u_char *info, size_t info_len);
> > @@ -78,8 +124,8 @@ static ngx_int_t ngx_quic_tls_seal(const
> > ngx_str_t *ad, ngx_log_t *log);
> > static ngx_int_t ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher,
> > ngx_quic_secret_t *s, u_char *out, u_char *in);
> > -static ngx_int_t ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest,
> > - ngx_str_t *out, ngx_str_t *label, const uint8_t *prk, size_t prk_len);
> > +static ngx_int_t ngx_quic_hkdf_expand(ngx_quic_hkdf_t *hkdf,
> > + const EVP_MD *digest, ngx_pool_t *pool);
> >
> > static ngx_int_t ngx_quic_create_packet(ngx_quic_header_t *pkt,
> > ngx_str_t *res);
> > @@ -204,28 +250,20 @@ ngx_quic_keys_set_initial_secret(ngx_poo
> > client->iv.len = NGX_QUIC_IV_LEN;
> > server->iv.len = NGX_QUIC_IV_LEN;
> >
> > - struct {
> > - ngx_str_t label;
> > - ngx_str_t *key;
> > - ngx_str_t *prk;
> > - } seq[] = {
> > + ngx_quic_hkdf_t seq[] = {
> > /* labels per RFC 9001, 5.1. Packet Protection Keys */
> > - { ngx_string("tls13 client in"), &client->secret, &iss },
> > - { ngx_string("tls13 quic key"), &client->key, &client->secret },
> > - { ngx_string("tls13 quic iv"), &client->iv, &client->secret },
> > - { ngx_string("tls13 quic hp"), &client->hp, &client->secret },
> > - { ngx_string("tls13 server in"), &server->secret, &iss },
> > - { ngx_string("tls13 quic key"), &server->key, &server->secret },
> > - { ngx_string("tls13 quic iv"), &server->iv, &server->secret },
> > - { ngx_string("tls13 quic hp"), &server->hp, &server->secret },
> > + ngx_quic_hkdf_set("tls13 client in", &client->secret, &iss),
> > + ngx_quic_hkdf_set("tls13 quic key", &client->key, &client->secret),
> > + ngx_quic_hkdf_set("tls13 quic iv", &client->iv, &client->secret),
> > + ngx_quic_hkdf_set("tls13 quic hp", &client->hp, &client->secret),
> > + ngx_quic_hkdf_set("tls13 server in", &server->secret, &iss),
> > + ngx_quic_hkdf_set("tls13 quic key", &server->key, &server->secret),
> > + ngx_quic_hkdf_set("tls13 quic iv", &server->iv, &server->secret),
> > + ngx_quic_hkdf_set("tls13 quic hp", &server->hp, &server->secret),
> > };
> >
> > for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
> > -
> > - if (ngx_quic_hkdf_expand(pool, digest, seq[i].key, &seq[i].label,
> > - seq[i].prk->data, seq[i].prk->len)
> > - != NGX_OK)
> > - {
> > + if (ngx_quic_hkdf_expand(&seq[i], digest, pool) != NGX_OK) {
> > return NGX_ERROR;
> > }
> > }
> > @@ -235,40 +273,39 @@ ngx_quic_keys_set_initial_secret(ngx_poo
> >
> >
> > static ngx_int_t
> > -ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest, ngx_str_t *out,
> > - ngx_str_t *label, const uint8_t *prk, size_t prk_len)
> > +ngx_quic_hkdf_expand(ngx_quic_hkdf_t *h, const EVP_MD *digest, ngx_pool_t *pool)
> > {
> > - size_t info_len;
> > uint8_t *p;
> > - uint8_t info[20];
> >
> > - if (out->data == NULL) {
> > - out->data = ngx_pnalloc(pool, out->len);
> > - if (out->data == NULL) {
> > + if (h->out == NULL) {
> > + h->out = ngx_pnalloc(pool, h->out_len);
> > + if (h->out == NULL) {
> > return NGX_ERROR;
> > }
> > }
> >
> > - info_len = 2 + 1 + label->len + 1;
> > + h->info_len = 2 + 1 + h->label_len + 1;
> >
> > - info[0] = 0;
> > - info[1] = out->len;
> > - info[2] = label->len;
> > - p = ngx_cpymem(&info[3], label->data, label->len);
> > + h->info[0] = 0;
> > + h->info[1] = h->out_len;
> > + h->info[2] = h->label_len;
>
> Why?
> info/info_len aren't used/useful outside of ngx_quic_hkdf_expand(),
> they are barely one-time local storages to produce traffic secrets.
sorry, this is a leftover. Indeed, there is no meaning in keeping this.
I had some ideas how to initialize it on declaration and merge 'info'
with 'label', but the result was too clumsy and unsafe to use.
removed info from structure and restored local variable..
>
> > +
> > + p = ngx_cpymem(&h->info[3], h->label, h->label_len);
> > *p = '\0';
> >
> > - if (ngx_hkdf_expand(out->data, out->len, digest,
> > - prk, prk_len, info, info_len)
> > + if (ngx_hkdf_expand(h->out, h->out_len, digest,
> > + h->prk, h->prk_len, h->info, h->info_len)
> > != NGX_OK)
> > {
> > ngx_ssl_error(NGX_LOG_INFO, pool->log, 0,
> > - "ngx_hkdf_expand(%V) failed", label);
> > + "ngx_hkdf_expand(%*s) failed", h->label_len, h->label);
> > return NGX_ERROR;
> > }
> >
> > #ifdef NGX_QUIC_DEBUG_CRYPTO
> > - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pool->log, 0,
> > - "quic expand %V key len:%uz %xV", label, out->len, out);
> > + ngx_log_debug5(NGX_LOG_DEBUG_EVENT, pool->log, 0,
> > + "quic expand \"%*s\" key len:%uz %*xs",
> > + h->label_len, h->label, h->out_len, h->out_len, h->out);
> > #endif
> >
> > return NGX_OK;
> > @@ -652,6 +689,7 @@ ngx_quic_keys_set_encryption_secret(ngx_
> > const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
> > {
> > ngx_int_t key_len;
> > + ngx_str_t secret_str;
> > ngx_uint_t i;
> > ngx_quic_secret_t *peer_secret;
> > ngx_quic_ciphers_t ciphers;
> > @@ -668,8 +706,9 @@ ngx_quic_keys_set_encryption_secret(ngx_
> > return NGX_ERROR;
> > }
> >
> > - peer_secret->secret.data = ngx_pnalloc(pool, secret_len);
> > - if (peer_secret->secret.data == NULL) {
> > + if (sizeof(peer_secret->secret.data) < secret_len) {
> > + ngx_log_error(NGX_LOG_ERR, pool->log, 0,
> > + "unexpected secret len: %uz", secret_len);
> > return NGX_ERROR;
> > }
>
> This won't work with cipher suite hash algorithms used to produce
> HKDF Hash.length (read: secret length) above SHA256_DIGEST_LENGTH,
> such as TLS_AES_256_GCM_SHA384 or any future TLSv1.3 cipher suites
> with SHA384 or above. The same for hardcoding NGX_QUIC_KEY_LEN.
yes, exactly. currently boringssl just defines max size 48 to keep
SHA384, and I think we can safely follow it. If anything will appear
in future, we can always adjust size.
I've renamed NGX_QUIC_KEY_LEN to NGX_QUIC_MAX_MD_SIZE and renamed
structure holding buffer to ngx_quic_md_t, and use it for secret,
key and hp, as it makes sens to use them uniformly.
> The error, if ever leave it there, deserves rasing logging level
> to "alert" as clearly a programmatic error.
>
agreed. This place is the worst in patch - we get size from the library
and hope that our bufer is big enough, otherwise fail with a runtime error.
On the other side, it is pretty clear that size here is just digest
length, and we can expect reasonable numbers here.
-------------- next part --------------
# HG changeset patch
# User Vladimir Homutov <vl at nginx.com>
# Date 1645524401 -10800
# Tue Feb 22 13:06:41 2022 +0300
# Branch quic
# Node ID bb1717365759760bc8175b8d8084819a6ec35c26
# Parent 55b38514729b8f848709b31295e72d6886a7a433
QUIC: fixed-length buffers for secrets.
diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c
--- a/src/event/quic/ngx_event_quic_protection.c
+++ b/src/event/quic/ngx_event_quic_protection.c
@@ -17,6 +17,9 @@
#define NGX_QUIC_AES_128_KEY_LEN 16
+/* largest hash used in TLS is SHA-384 */
+#define NGX_QUIC_MAX_MD_SIZE 48
+
#define NGX_AES_128_GCM_SHA256 0x1301
#define NGX_AES_256_GCM_SHA384 0x1302
#define NGX_CHACHA20_POLY1305_SHA256 0x1303
@@ -30,6 +33,18 @@
typedef struct {
+ size_t len;
+ u_char data[NGX_QUIC_MAX_MD_SIZE];
+} ngx_quic_md_t;
+
+
+typedef struct {
+ size_t len;
+ u_char data[NGX_QUIC_IV_LEN];
+} ngx_quic_iv_t;
+
+
+typedef struct {
const ngx_quic_cipher_t *c;
const EVP_CIPHER *hp;
const EVP_MD *d;
@@ -37,10 +52,10 @@ typedef struct {
typedef struct ngx_quic_secret_s {
- ngx_str_t secret;
- ngx_str_t key;
- ngx_str_t iv;
- ngx_str_t hp;
+ ngx_quic_md_t secret;
+ ngx_quic_md_t key;
+ ngx_quic_iv_t iv;
+ ngx_quic_md_t hp;
} ngx_quic_secret_t;
@@ -57,6 +72,25 @@ struct ngx_quic_keys_s {
};
+typedef struct {
+ size_t out_len;
+ u_char *out;
+
+ size_t prk_len;
+ const uint8_t *prk;
+
+ size_t label_len;
+ const u_char *label;
+} ngx_quic_hkdf_t;
+
+#define ngx_quic_hkdf_set(label, out, prk) \
+ { \
+ (out)->len, (out)->data, \
+ (prk)->len, (prk)->data, \
+ (sizeof(label) - 1), (u_char *)(label), \
+ }
+
+
static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len,
const EVP_MD *digest, const u_char *prk, size_t prk_len,
const u_char *info, size_t info_len);
@@ -78,8 +112,8 @@ static ngx_int_t ngx_quic_tls_seal(const
ngx_str_t *ad, ngx_log_t *log);
static ngx_int_t ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher,
ngx_quic_secret_t *s, u_char *out, u_char *in);
-static ngx_int_t ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest,
- ngx_str_t *out, ngx_str_t *label, const uint8_t *prk, size_t prk_len);
+static ngx_int_t ngx_quic_hkdf_expand(ngx_quic_hkdf_t *hkdf,
+ const EVP_MD *digest, ngx_pool_t *pool);
static ngx_int_t ngx_quic_create_packet(ngx_quic_header_t *pkt,
ngx_str_t *res);
@@ -204,28 +238,20 @@ ngx_quic_keys_set_initial_secret(ngx_poo
client->iv.len = NGX_QUIC_IV_LEN;
server->iv.len = NGX_QUIC_IV_LEN;
- struct {
- ngx_str_t label;
- ngx_str_t *key;
- ngx_str_t *prk;
- } seq[] = {
+ ngx_quic_hkdf_t seq[] = {
/* labels per RFC 9001, 5.1. Packet Protection Keys */
- { ngx_string("tls13 client in"), &client->secret, &iss },
- { ngx_string("tls13 quic key"), &client->key, &client->secret },
- { ngx_string("tls13 quic iv"), &client->iv, &client->secret },
- { ngx_string("tls13 quic hp"), &client->hp, &client->secret },
- { ngx_string("tls13 server in"), &server->secret, &iss },
- { ngx_string("tls13 quic key"), &server->key, &server->secret },
- { ngx_string("tls13 quic iv"), &server->iv, &server->secret },
- { ngx_string("tls13 quic hp"), &server->hp, &server->secret },
+ ngx_quic_hkdf_set("tls13 client in", &client->secret, &iss),
+ ngx_quic_hkdf_set("tls13 quic key", &client->key, &client->secret),
+ ngx_quic_hkdf_set("tls13 quic iv", &client->iv, &client->secret),
+ ngx_quic_hkdf_set("tls13 quic hp", &client->hp, &client->secret),
+ ngx_quic_hkdf_set("tls13 server in", &server->secret, &iss),
+ ngx_quic_hkdf_set("tls13 quic key", &server->key, &server->secret),
+ ngx_quic_hkdf_set("tls13 quic iv", &server->iv, &server->secret),
+ ngx_quic_hkdf_set("tls13 quic hp", &server->hp, &server->secret),
};
for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
-
- if (ngx_quic_hkdf_expand(pool, digest, seq[i].key, &seq[i].label,
- seq[i].prk->data, seq[i].prk->len)
- != NGX_OK)
- {
+ if (ngx_quic_hkdf_expand(&seq[i], digest, pool) != NGX_OK) {
return NGX_ERROR;
}
}
@@ -235,40 +261,41 @@ ngx_quic_keys_set_initial_secret(ngx_poo
static ngx_int_t
-ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest, ngx_str_t *out,
- ngx_str_t *label, const uint8_t *prk, size_t prk_len)
+ngx_quic_hkdf_expand(ngx_quic_hkdf_t *h, const EVP_MD *digest, ngx_pool_t *pool)
{
size_t info_len;
uint8_t *p;
uint8_t info[20];
- if (out->data == NULL) {
- out->data = ngx_pnalloc(pool, out->len);
- if (out->data == NULL) {
+ if (h->out == NULL) {
+ h->out = ngx_pnalloc(pool, h->out_len);
+ if (h->out == NULL) {
return NGX_ERROR;
}
}
- info_len = 2 + 1 + label->len + 1;
+ info_len = 2 + 1 + h->label_len + 1;
info[0] = 0;
- info[1] = out->len;
- info[2] = label->len;
- p = ngx_cpymem(&info[3], label->data, label->len);
+ info[1] = h->out_len;
+ info[2] = h->label_len;
+
+ p = ngx_cpymem(&info[3], h->label, h->label_len);
*p = '\0';
- if (ngx_hkdf_expand(out->data, out->len, digest,
- prk, prk_len, info, info_len)
+ if (ngx_hkdf_expand(h->out, h->out_len, digest,
+ h->prk, h->prk_len, info, info_len)
!= NGX_OK)
{
ngx_ssl_error(NGX_LOG_INFO, pool->log, 0,
- "ngx_hkdf_expand(%V) failed", label);
+ "ngx_hkdf_expand(%*s) failed", h->label_len, h->label);
return NGX_ERROR;
}
#ifdef NGX_QUIC_DEBUG_CRYPTO
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pool->log, 0,
- "quic expand %V key len:%uz %xV", label, out->len, out);
+ ngx_log_debug5(NGX_LOG_DEBUG_EVENT, pool->log, 0,
+ "quic expand \"%*s\" key len:%uz %*xs",
+ h->label_len, h->label, h->out_len, h->out_len, h->out);
#endif
return NGX_OK;
@@ -652,6 +679,7 @@ ngx_quic_keys_set_encryption_secret(ngx_
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
{
ngx_int_t key_len;
+ ngx_str_t secret_str;
ngx_uint_t i;
ngx_quic_secret_t *peer_secret;
ngx_quic_ciphers_t ciphers;
@@ -668,8 +696,9 @@ ngx_quic_keys_set_encryption_secret(ngx_
return NGX_ERROR;
}
- peer_secret->secret.data = ngx_pnalloc(pool, secret_len);
- if (peer_secret->secret.data == NULL) {
+ if (sizeof(peer_secret->secret.data) < secret_len) {
+ ngx_log_error(NGX_LOG_ALERT, pool->log, 0,
+ "unexpected secret len: %uz", secret_len);
return NGX_ERROR;
}
@@ -680,22 +709,17 @@ ngx_quic_keys_set_encryption_secret(ngx_
peer_secret->iv.len = NGX_QUIC_IV_LEN;
peer_secret->hp.len = key_len;
- struct {
- ngx_str_t label;
- ngx_str_t *key;
- const uint8_t *secret;
- } seq[] = {
- { ngx_string("tls13 quic key"), &peer_secret->key, secret },
- { ngx_string("tls13 quic iv"), &peer_secret->iv, secret },
- { ngx_string("tls13 quic hp"), &peer_secret->hp, secret },
+ secret_str.len = secret_len;
+ secret_str.data = (u_char *) secret;
+
+ ngx_quic_hkdf_t seq[] = {
+ ngx_quic_hkdf_set("tls13 quic key", &peer_secret->key, &secret_str),
+ ngx_quic_hkdf_set("tls13 quic iv", &peer_secret->iv, &secret_str),
+ ngx_quic_hkdf_set("tls13 quic hp", &peer_secret->hp, &secret_str),
};
for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
-
- if (ngx_quic_hkdf_expand(pool, ciphers.d, seq[i].key, &seq[i].label,
- seq[i].secret, secret_len)
- != NGX_OK)
- {
+ if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, pool) != NGX_OK) {
return NGX_ERROR;
}
}
@@ -769,49 +793,23 @@ ngx_quic_keys_update(ngx_connection_t *c
next->server.iv.len = NGX_QUIC_IV_LEN;
next->server.hp = current->server.hp;
- struct {
- ngx_str_t label;
- ngx_str_t *key;
- ngx_str_t *secret;
- } seq[] = {
- {
- ngx_string("tls13 quic ku"),
- &next->client.secret,
- ¤t->client.secret,
- },
- {
- ngx_string("tls13 quic key"),
- &next->client.key,
- &next->client.secret,
- },
- {
- ngx_string("tls13 quic iv"),
- &next->client.iv,
- &next->client.secret,
- },
- {
- ngx_string("tls13 quic ku"),
- &next->server.secret,
- ¤t->server.secret,
- },
- {
- ngx_string("tls13 quic key"),
- &next->server.key,
- &next->server.secret,
- },
- {
- ngx_string("tls13 quic iv"),
- &next->server.iv,
- &next->server.secret,
- },
+ ngx_quic_hkdf_t seq[] = {
+ ngx_quic_hkdf_set("tls13 quic ku",
+ &next->client.secret, ¤t->client.secret),
+ ngx_quic_hkdf_set("tls13 quic key",
+ &next->client.key, &next->client.secret),
+ ngx_quic_hkdf_set("tls13 quic iv",
+ &next->client.iv, &next->client.secret),
+ ngx_quic_hkdf_set("tls13 quic ku",
+ &next->server.secret, ¤t->server.secret),
+ ngx_quic_hkdf_set("tls13 quic key",
+ &next->server.key, &next->server.secret),
+ ngx_quic_hkdf_set("tls13 quic iv",
+ &next->server.iv, &next->server.secret),
};
for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
-
- if (ngx_quic_hkdf_expand(c->pool, ciphers.d, seq[i].key, &seq[i].label,
- seq[i].secret->data, seq[i].secret->len)
- != NGX_OK)
- {
+ if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, c->pool) != NGX_OK) {
return NGX_ERROR;
}
}
@@ -909,7 +907,7 @@ ngx_quic_create_retry_packet(ngx_quic_he
}
secret.key.len = sizeof(key);
- secret.key.data = key;
+ ngx_memcpy(secret.key.data, key, sizeof(key));
secret.iv.len = NGX_QUIC_IV_LEN;
if (ngx_quic_tls_seal(ciphers.c, &secret, &itag, nonce, &in, &ad, pkt->log)
More information about the nginx-devel
mailing list