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