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, ©, &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