lsquic_tokgen.c revision fb3e20e0
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_crypter_key[N_TOKEN_TYPES][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    /* We encrypt different token types using different keys. */
76    struct crypter  tg_crypters[N_TOKEN_TYPES];
77
78    /* Stateless reset token is generated using HKDF with CID as the
79     * `info' parameter to HKDF-Expand.
80     */
81    size_t          tg_srst_prk_sz;
82    uint8_t         tg_srst_prk_buf[SRST_MAX_PRK_SIZE];
83};
84
85
86
87
88static int
89get_or_generate_state (struct lsquic_engine_public *enpub, time_t now,
90                                        struct tokgen_shm_state *shm_state)
91{
92    const struct lsquic_shared_hash_if *const shi = enpub->enp_shi;
93    void *const ctx = enpub->enp_shi_ctx;
94    void *data, *copy, *key_copy;
95    int s;
96    unsigned sz;
97    size_t bufsz;
98    struct {
99        time_t        now;
100        unsigned char buf[24];
101    }
102#if __GNUC__
103    /* This is more of a documentation note: this struct should already
104     * have a multiple-of-eight size.
105     */
106    __attribute__((packed))
107#endif
108    srst_ikm;
109
110    data = shm_state;
111    sz = sizeof(*shm_state);
112    s = shi->shi_lookup(ctx, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE, &data, &sz);
113
114    if (s == 1)
115    {
116        if (sz != sizeof(*shm_state))
117        {
118            LSQ_WARN("found SHM data has non-matching size %u", sz);
119            return -1;
120        }
121        if (data != (void *) shm_state)
122            memcpy(shm_state, data, sizeof(*shm_state));
123        if (shm_state->tgss_version != TOKGEN_VERSION)
124        {
125            LSQ_DEBUG("found SHM data has non-matching version %u",
126                                                        shm_state->tgss_version);
127            return -1;
128        }
129        LSQ_DEBUG("found SHM data: size %u; version %u", sz,
130                                                        shm_state->tgss_version);
131        return 0;
132    }
133
134    if (s != 0)
135    {
136        if (s != -1)
137            LSQ_WARN("SHM lookup returned unexpected value %d", s);
138        LSQ_DEBUG("SHM lookup returned an error: generate");
139        goto generate;
140    }
141
142    assert(s == 0);
143    LSQ_DEBUG("%s does not exist: generate", TOKGEN_SHM_KEY);
144  generate:
145    now = time(NULL);
146    memset(shm_state, 0, sizeof(*shm_state));
147    shm_state->tgss_version = TOKGEN_VERSION;
148    memcpy(shm_state->tgss_magic_top, TOKGEN_SHM_MAGIC_TOP,
149                                        sizeof(TOKGEN_SHM_MAGIC_TOP) - 1);
150    if (getenv("LSQUIC_NULL_TOKGEN"))
151    {
152        LSQ_NOTICE("using NULL tokgen");
153        memset(&srst_ikm, 0, sizeof(srst_ikm));
154    }
155    else
156    {
157        srst_ikm.now = now;
158        RAND_bytes(srst_ikm.buf, sizeof(srst_ikm.buf));
159    }
160    if (!HKDF_extract(shm_state->tgss_srst_prk, &bufsz,
161                     EVP_sha256(), (uint8_t *) &srst_ikm, sizeof(srst_ikm),
162                     srst_salt, sizeof(srst_salt)))
163    {
164        LSQ_ERROR("HKDF_extract failed");
165        return -1;
166    }
167    shm_state->tgss_srst_prk_size = (uint8_t) bufsz;
168    memcpy(shm_state->tgss_magic_bottom, TOKGEN_SHM_MAGIC_BOTTOM,
169                                        sizeof(TOKGEN_SHM_MAGIC_BOTTOM) - 1);
170
171    data = malloc(sizeof(*shm_state));
172    if (!data)
173    {
174        LSQ_ERROR("%s: malloc", __func__);
175        return -1;
176    }
177    memcpy(data, shm_state, sizeof(*shm_state));
178    key_copy = malloc(TOKGEN_SHM_KEY_SIZE);
179    if (!key_copy)
180    {
181        LSQ_ERROR("%s: malloc", __func__);
182        free(data);
183        return -1;
184    }
185    memcpy(key_copy, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE);
186    s = shi->shi_insert(ctx, key_copy, TOKGEN_SHM_KEY_SIZE, data,
187                                                    sizeof(*shm_state), 0);
188    if (s != 0)
189    {
190        LSQ_ERROR("cannot insert into SHM");
191        free(data);
192        free(key_copy);
193        return -1;
194    }
195    sz = sizeof(*shm_state);
196    s = shi->shi_lookup(ctx, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE, &copy, &sz);
197    if (s != 1 || sz != sizeof(*shm_state))
198    {
199        LSQ_ERROR("cannot lookup after insert: s=%d; sz=%u", s, sz);
200        return -1;
201    }
202    if (copy != data)
203        memcpy(shm_state, copy, sizeof(*shm_state));
204    LSQ_INFO("inserted %s of size %u", TOKGEN_SHM_KEY, sz);
205    return 0;
206}
207
208
209struct token_generator *
210lsquic_tg_new (struct lsquic_engine_public *enpub)
211{
212    struct token_generator *tokgen;
213    time_t now;
214    struct tokgen_shm_state shm_state;
215
216    tokgen = calloc(1, sizeof(*tokgen));
217    if (!tokgen)
218        goto err;
219
220    now = time(NULL);
221    if (0 != get_or_generate_state(enpub, now, &shm_state))
222        goto err;
223
224
225    tokgen->tg_srst_prk_sz = shm_state.tgss_srst_prk_size;
226    if (tokgen->tg_srst_prk_sz > sizeof(tokgen->tg_srst_prk_buf))
227    {
228        LSQ_WARN("bad stateless reset key size");
229        goto err;
230    }
231    memcpy(tokgen->tg_srst_prk_buf, shm_state.tgss_srst_prk,
232                                                    tokgen->tg_srst_prk_sz);
233
234    LSQ_DEBUG("initialized");
235    return tokgen;
236
237  err:
238    LSQ_ERROR("error initializing");
239    free(tokgen);
240    return NULL;
241}
242
243
244void
245lsquic_tg_destroy (struct token_generator *tokgen)
246{
247    free(tokgen);
248    LSQ_DEBUG("destroyed");
249}
250
251
252void
253lsquic_tg_generate_sreset (struct token_generator *tokgen,
254        const struct lsquic_cid *cid, unsigned char *reset_token)
255{
256    char str[IQUIC_SRESET_TOKEN_SZ * 2 + 1];
257
258    (void) HKDF_expand(reset_token, IQUIC_SRESET_TOKEN_SZ, EVP_sha256(),
259        tokgen->tg_srst_prk_buf, tokgen->tg_srst_prk_sz, cid->idbuf, cid->len);
260    LSQ_DEBUGC("generated stateless reset token %s for CID %"CID_FMT,
261        HEXSTR(reset_token, IQUIC_SRESET_TOKEN_SZ, str), CID_BITS(cid));
262}
263