lsquic_tokgen.c revision b1a7c3f9
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2#include <assert.h>
3#include <stddef.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sys/queue.h>
7#include <time.h>
8
9#ifndef WIN32
10#include <netinet/in.h>
11#include <sys/socket.h>
12#endif
13
14#include <openssl/aead.h>
15#include <openssl/hkdf.h>
16#include <openssl/rand.h>
17#include <openssl/ssl.h>
18
19#include "lsquic.h"
20#include "lsquic_int_types.h"
21#include "lsquic_sizes.h"
22#include "lsquic_types.h"
23#include "lsquic_packet_common.h"
24#include "lsquic_packet_in.h"
25#include "lsquic_tokgen.h"
26#include "lsquic_trans_params.h"
27#include "lsquic_util.h"
28#include "lsquic_mm.h"
29#include "lsquic_engine_public.h"
30
31#define LSQUIC_LOGGER_MODULE LSQLM_TOKGEN
32#include "lsquic_logger.h"
33
34#define STRINGIFY(x) #x
35#define TOSTRING(x) STRINGIFY(x)
36
37#define TOKGEN_VERSION 2
38
39#define CRYPTER_KEY_SIZE        16
40#define SRST_MAX_PRK_SIZE       EVP_MAX_MD_SIZE
41
42#define TOKGEN_SHM_KEY "TOKGEN" TOSTRING(TOKGEN_VERSION)
43#define TOKGEN_SHM_KEY_SIZE (sizeof(TOKGEN_SHM_KEY) - 1)
44
45#define TOKGEN_SHM_MAGIC_TOP "Feliz"
46#define TOKGEN_SHM_MAGIC_BOTTOM "Navidad"
47
48struct tokgen_shm_state
49{
50    uint8_t     tgss_version;
51    uint8_t     tgss_magic_top[sizeof(TOKGEN_SHM_MAGIC_TOP) - 1];
52    uint8_t     tgss_padding[2 * CRYPTER_KEY_SIZE];
53    uint8_t     tgss_srst_prk_size;
54    uint8_t     tgss_srst_prk[SRST_MAX_PRK_SIZE];
55    uint8_t     tgss_magic_bottom[sizeof(TOKGEN_SHM_MAGIC_BOTTOM) - 1];
56};
57
58
59
60static const uint8_t srst_salt[8] = "\x28\x6e\x81\x02\x40\x5b\x2c\x2b";
61
62struct crypter
63{
64    EVP_AEAD_CTX    ctx;
65    unsigned long   nonce_counter;
66    size_t          nonce_prk_sz;
67    uint8_t         nonce_prk_buf[EVP_MAX_MD_SIZE];
68};
69
70
71
72
73struct token_generator
74{
75
76    /* Stateless reset token is generated using HKDF with CID as the
77     * `info' parameter to HKDF-Expand.
78     */
79    size_t          tg_srst_prk_sz;
80    uint8_t         tg_srst_prk_buf[SRST_MAX_PRK_SIZE];
81};
82
83
84
85
86static int
87get_or_generate_state (struct lsquic_engine_public *enpub, time_t now,
88                                        struct tokgen_shm_state *shm_state)
89{
90    const struct lsquic_shared_hash_if *const shi = enpub->enp_shi;
91    void *const ctx = enpub->enp_shi_ctx;
92    void *data, *copy, *key_copy;
93    int s;
94    unsigned sz;
95    size_t bufsz;
96    struct {
97        time_t        now;
98        unsigned char buf[24];
99    }
100#if __GNUC__
101    /* This is more of a documentation note: this struct should already
102     * have a multiple-of-eight size.
103     */
104    __attribute__((packed))
105#endif
106    srst_ikm;
107
108    data = shm_state;
109    sz = sizeof(*shm_state);
110    s = shi->shi_lookup(ctx, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE, &data, &sz);
111
112    if (s == 1)
113    {
114        if (sz != sizeof(*shm_state))
115        {
116            LSQ_WARN("found SHM data has non-matching size %u", sz);
117            return -1;
118        }
119        if (data != (void *) shm_state)
120            memcpy(shm_state, data, sizeof(*shm_state));
121        if (shm_state->tgss_version != TOKGEN_VERSION)
122        {
123            LSQ_DEBUG("found SHM data has non-matching version %u",
124                                                        shm_state->tgss_version);
125            return -1;
126        }
127        LSQ_DEBUG("found SHM data: size %u; version %u", sz,
128                                                        shm_state->tgss_version);
129        return 0;
130    }
131
132    if (s != 0)
133    {
134        if (s != -1)
135            LSQ_WARN("SHM lookup returned unexpected value %d", s);
136        LSQ_DEBUG("SHM lookup returned an error: generate");
137        goto generate;
138    }
139
140    assert(s == 0);
141    LSQ_DEBUG("%s does not exist: generate", TOKGEN_SHM_KEY);
142  generate:
143    now = time(NULL);
144    memset(shm_state, 0, sizeof(*shm_state));
145    shm_state->tgss_version = TOKGEN_VERSION;
146    memcpy(shm_state->tgss_magic_top, TOKGEN_SHM_MAGIC_TOP,
147                                        sizeof(TOKGEN_SHM_MAGIC_TOP) - 1);
148    if (getenv("LSQUIC_NULL_TOKGEN"))
149    {
150        LSQ_NOTICE("using NULL tokgen");
151        memset(&srst_ikm, 0, sizeof(srst_ikm));
152    }
153    else
154    {
155        srst_ikm.now = now;
156        RAND_bytes(srst_ikm.buf, sizeof(srst_ikm.buf));
157    }
158    if (!HKDF_extract(shm_state->tgss_srst_prk, &bufsz,
159                     EVP_sha256(), (uint8_t *) &srst_ikm, sizeof(srst_ikm),
160                     srst_salt, sizeof(srst_salt)))
161    {
162        LSQ_ERROR("HKDF_extract failed");
163        return -1;
164    }
165    shm_state->tgss_srst_prk_size = (uint8_t) bufsz;
166    memcpy(shm_state->tgss_magic_bottom, TOKGEN_SHM_MAGIC_BOTTOM,
167                                        sizeof(TOKGEN_SHM_MAGIC_BOTTOM) - 1);
168
169    data = malloc(sizeof(*shm_state));
170    if (!data)
171    {
172        LSQ_ERROR("%s: malloc", __func__);
173        return -1;
174    }
175    memcpy(data, shm_state, sizeof(*shm_state));
176    key_copy = malloc(TOKGEN_SHM_KEY_SIZE);
177    if (!key_copy)
178    {
179        LSQ_ERROR("%s: malloc", __func__);
180        free(data);
181        return -1;
182    }
183    memcpy(key_copy, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE);
184    s = shi->shi_insert(ctx, key_copy, TOKGEN_SHM_KEY_SIZE, data,
185                                                    sizeof(*shm_state), 0);
186    if (s != 0)
187    {
188        LSQ_ERROR("cannot insert into SHM");
189        free(data);
190        free(key_copy);
191        return -1;
192    }
193    sz = sizeof(*shm_state);
194    s = shi->shi_lookup(ctx, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE, &copy, &sz);
195    if (s != 1 || sz != sizeof(*shm_state))
196    {
197        LSQ_ERROR("cannot lookup after insert: s=%d; sz=%u", s, sz);
198        return -1;
199    }
200    if (copy != data)
201        memcpy(shm_state, copy, sizeof(*shm_state));
202    LSQ_INFO("inserted %s of size %u", TOKGEN_SHM_KEY, sz);
203    return 0;
204}
205
206
207struct token_generator *
208lsquic_tg_new (struct lsquic_engine_public *enpub)
209{
210    struct token_generator *tokgen;
211    time_t now;
212    struct tokgen_shm_state shm_state;
213
214    tokgen = calloc(1, sizeof(*tokgen));
215    if (!tokgen)
216        goto err;
217
218    now = time(NULL);
219    if (0 != get_or_generate_state(enpub, now, &shm_state))
220        goto err;
221
222
223    tokgen->tg_srst_prk_sz = shm_state.tgss_srst_prk_size;
224    if (tokgen->tg_srst_prk_sz > sizeof(tokgen->tg_srst_prk_buf))
225    {
226        LSQ_WARN("bad stateless reset key size");
227        goto err;
228    }
229    memcpy(tokgen->tg_srst_prk_buf, shm_state.tgss_srst_prk,
230                                                    tokgen->tg_srst_prk_sz);
231
232    LSQ_DEBUG("initialized");
233    return tokgen;
234
235  err:
236    LSQ_ERROR("error initializing");
237    free(tokgen);
238    return NULL;
239}
240
241
242void
243lsquic_tg_destroy (struct token_generator *tokgen)
244{
245    free(tokgen);
246    LSQ_DEBUG("destroyed");
247}
248
249
250void
251lsquic_tg_generate_sreset (struct token_generator *tokgen,
252        const struct lsquic_cid *cid, unsigned char *reset_token)
253{
254    char str[IQUIC_SRESET_TOKEN_SZ * 2 + 1];
255
256    (void) HKDF_expand(reset_token, IQUIC_SRESET_TOKEN_SZ, EVP_sha256(),
257        tokgen->tg_srst_prk_buf, tokgen->tg_srst_prk_sz, cid->idbuf, cid->len);
258    LSQ_DEBUGC("generated stateless reset token %s for CID %"CID_FMT,
259        HEXSTR(reset_token, IQUIC_SRESET_TOKEN_SZ, str), CID_BITS(cid));
260}
261