lsquic_tokgen.c revision 5392f7a3
1/* Copyright (c) 2017 - 2019 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[20];
96    } srst_ikm;
97
98    data = shm_state;
99    sz = sizeof(shm_state);
100    s = shi->shi_lookup(ctx, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE, &data, &sz);
101
102    if (s == 1)
103    {
104        if (sz != sizeof(*shm_state))
105        {
106            LSQ_WARN("found SHM data has non-matching size %u", sz);
107            return -1;
108        }
109        if (data != (void *) shm_state)
110            memcpy(shm_state, data, sizeof(*shm_state));
111        if (shm_state->tgss_version != TOKGEN_VERSION)
112        {
113            LSQ_DEBUG("found SHM data has non-matching version %u",
114                                                        shm_state->tgss_version);
115            return -1;
116        }
117        LSQ_DEBUG("found SHM data: size %u; version %u", sz,
118                                                        shm_state->tgss_version);
119        return 0;
120    }
121
122    if (s != 0)
123    {
124        if (s != -1)
125            LSQ_WARN("SHM lookup returned unexpected value %d", s);
126        LSQ_DEBUG("SHM lookup returned an error: generate");
127        goto generate;
128    }
129
130    assert(s == 0);
131    LSQ_DEBUG("%s does not exist: generate", TOKGEN_SHM_KEY);
132  generate:
133    now = time(NULL);
134    memset(shm_state, 0, sizeof(*shm_state));
135    shm_state->tgss_version = TOKGEN_VERSION;
136    memcpy(shm_state->tgss_magic_top, TOKGEN_SHM_MAGIC_TOP,
137                                        sizeof(TOKGEN_SHM_MAGIC_TOP) - 1);
138    if (getenv("LSQUIC_NULL_TOKGEN"))
139    {
140        memset(&srst_ikm, 0, sizeof(srst_ikm));
141        LSQ_NOTICE("using NULL tokgen");
142    }
143    else
144    {
145        srst_ikm.now = now;
146        RAND_bytes(srst_ikm.buf, sizeof(srst_ikm.buf));
147    }
148    if (!HKDF_extract(shm_state->tgss_srst_prk, &bufsz,
149                     EVP_sha256(), (uint8_t *) &srst_ikm, sizeof(srst_ikm),
150                     srst_salt, sizeof(srst_salt)))
151    {
152        LSQ_ERROR("HKDF_extract failed");
153        return -1;
154    }
155    shm_state->tgss_srst_prk_size = (uint8_t) bufsz;
156    memcpy(shm_state->tgss_magic_bottom, TOKGEN_SHM_MAGIC_BOTTOM,
157                                        sizeof(TOKGEN_SHM_MAGIC_BOTTOM) - 1);
158
159    data = malloc(sizeof(*shm_state));
160    if (!data)
161    {
162        LSQ_ERROR("%s: malloc", __func__);
163        return -1;
164    }
165    memcpy(data, shm_state, sizeof(*shm_state));
166    key_copy = malloc(TOKGEN_SHM_KEY_SIZE);
167    if (!key_copy)
168    {
169        LSQ_ERROR("%s: malloc", __func__);
170        free(data);
171        return -1;
172    }
173    memcpy(key_copy, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE);
174    s = shi->shi_insert(ctx, key_copy, TOKGEN_SHM_KEY_SIZE, data,
175                                                    sizeof(*shm_state), 0);
176    if (s != 0)
177    {
178        LSQ_ERROR("cannot insert into SHM");
179        free(data);
180        free(key_copy);
181        return -1;
182    }
183    sz = sizeof(*shm_state);
184    s = shi->shi_lookup(ctx, TOKGEN_SHM_KEY, TOKGEN_SHM_KEY_SIZE, &copy, &sz);
185    if (s != 1 || sz != sizeof(*shm_state))
186    {
187        LSQ_ERROR("cannot lookup after insert: s=%d; sz=%u", s, sz);
188        return -1;
189    }
190    if (copy != data)
191        memcpy(shm_state, copy, sizeof(*shm_state));
192    LSQ_INFO("inserted %s of size %u", TOKGEN_SHM_KEY, sz);
193    return 0;
194}
195
196
197struct token_generator *
198lsquic_tg_new (struct lsquic_engine_public *enpub)
199{
200    struct token_generator *tokgen;
201    time_t now;
202    struct tokgen_shm_state shm_state;
203
204    tokgen = calloc(1, sizeof(*tokgen));
205    if (!tokgen)
206        goto err;
207
208    now = time(NULL);
209    if (0 != get_or_generate_state(enpub, now, &shm_state))
210        goto err;
211
212
213    tokgen->tg_srst_prk_sz = shm_state.tgss_srst_prk_size;
214    if (tokgen->tg_srst_prk_sz > sizeof(tokgen->tg_srst_prk_buf))
215    {
216        LSQ_WARN("bad stateless reset key size");
217        goto err;
218    }
219    memcpy(tokgen->tg_srst_prk_buf, shm_state.tgss_srst_prk,
220                                                    tokgen->tg_srst_prk_sz);
221
222    LSQ_DEBUG("initialized");
223    return tokgen;
224
225  err:
226    LSQ_ERROR("error initializing");
227    free(tokgen);
228    return NULL;
229}
230
231
232void
233lsquic_tg_destroy (struct token_generator *tokgen)
234{
235    free(tokgen);
236    LSQ_DEBUG("destroyed");
237}
238
239
240void
241lsquic_tg_generate_sreset (struct token_generator *tokgen,
242        const struct lsquic_cid *cid, unsigned char *reset_token)
243{
244    char str[IQUIC_SRESET_TOKEN_SZ * 2 + 1];
245
246    (void) HKDF_expand(reset_token, IQUIC_SRESET_TOKEN_SZ, EVP_sha256(),
247        tokgen->tg_srst_prk_buf, tokgen->tg_srst_prk_sz, cid->idbuf, cid->len);
248    LSQ_DEBUGC("generated stateless reset token %s for CID %"CID_FMT,
249        HEXSTR(reset_token, IQUIC_SRESET_TOKEN_SZ, str), CID_BITS(cid));
250}
251