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