lsquic_tokgen.c revision 9fc12041
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[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 LSQ_NOTICE("using NULL tokgen"); 141 memset(&srst_ikm, 0, sizeof(srst_ikm)); 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, ©, &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