1 1.1 christos /* 2 1.1 christos * Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * 4 1.1 christos * Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos * this file except in compliance with the License. You can obtain a copy 6 1.1 christos * in the file LICENSE in the source distribution or at 7 1.1 christos * https://www.openssl.org/source/license.html 8 1.1 christos */ 9 1.1 christos 10 1.1 christos #include "internal/quic_srtm.h" 11 1.1 christos #include "internal/common.h" 12 1.1 christos #include <openssl/lhash.h> 13 1.1 christos #include <openssl/core_names.h> 14 1.1 christos #include <openssl/rand.h> 15 1.1 christos 16 1.1 christos /* 17 1.1 christos * QUIC Stateless Reset Token Manager 18 1.1 christos * ================================== 19 1.1 christos */ 20 1.1 christos typedef struct srtm_item_st SRTM_ITEM; 21 1.1 christos 22 1.1.1.2 christos #define BLINDED_SRT_LEN 16 23 1.1 christos 24 1.1 christos DEFINE_LHASH_OF_EX(SRTM_ITEM); 25 1.1 christos 26 1.1 christos /* 27 1.1 christos * The SRTM is implemented using two LHASH instances, one matching opaque pointers to 28 1.1 christos * an item structure, and another matching a SRT-derived value to an item 29 1.1 christos * structure. Multiple items with different seq_num values under a given opaque, 30 1.1 christos * and duplicate SRTs, are handled using sorted singly-linked lists. 31 1.1 christos * 32 1.1 christos * The O(n) insert and lookup performance is tolerated on the basis that the 33 1.1 christos * total number of entries for a given opaque (total number of extant CIDs for a 34 1.1 christos * connection) should be quite small, and the QUIC protocol allows us to place a 35 1.1 christos * hard limit on this via the active_connection_id_limit TPARAM. Thus there is 36 1.1 christos * no risk of a large number of SRTs needing to be registered under a given 37 1.1 christos * opaque. 38 1.1 christos * 39 1.1 christos * It is expected one SRTM will exist per QUIC_PORT and track all SRTs across 40 1.1 christos * all connections for that QUIC_PORT. 41 1.1 christos */ 42 1.1 christos struct srtm_item_st { 43 1.1.1.2 christos SRTM_ITEM *next_by_srt_blinded; /* SORT BY opaque DESC */ 44 1.1.1.2 christos SRTM_ITEM *next_by_seq_num; /* SORT BY seq_num DESC */ 45 1.1.1.2 christos void *opaque; /* \__ unique identity for item */ 46 1.1.1.2 christos uint64_t seq_num; /* / */ 47 1.1.1.2 christos QUIC_STATELESS_RESET_TOKEN srt; 48 1.1.1.2 christos unsigned char srt_blinded[BLINDED_SRT_LEN]; /* H(srt) */ 49 1.1 christos 50 1.1 christos #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 51 1.1.1.2 christos uint32_t debug_token; 52 1.1 christos #endif 53 1.1 christos }; 54 1.1 christos 55 1.1 christos struct quic_srtm_st { 56 1.1 christos /* Crypto context used to calculate blinded SRTs H(srt). */ 57 1.1.1.2 christos EVP_CIPHER_CTX *blind_ctx; /* kept with key */ 58 1.1 christos 59 1.1.1.2 christos LHASH_OF(SRTM_ITEM) *items_fwd; /* (opaque) -> SRTM_ITEM */ 60 1.1.1.2 christos LHASH_OF(SRTM_ITEM) *items_rev; /* (H(srt)) -> SRTM_ITEM */ 61 1.1 christos 62 1.1 christos /* 63 1.1 christos * Monotonically transitions to 1 in event of allocation failure. The only 64 1.1 christos * valid operation on such an object is to free it. 65 1.1 christos */ 66 1.1.1.2 christos unsigned int alloc_failed : 1; 67 1.1 christos }; 68 1.1 christos 69 1.1 christos static unsigned long items_fwd_hash(const SRTM_ITEM *item) 70 1.1 christos { 71 1.1 christos return (unsigned long)(uintptr_t)item->opaque; 72 1.1 christos } 73 1.1 christos 74 1.1 christos static int items_fwd_cmp(const SRTM_ITEM *a, const SRTM_ITEM *b) 75 1.1 christos { 76 1.1 christos return a->opaque != b->opaque; 77 1.1 christos } 78 1.1 christos 79 1.1 christos static unsigned long items_rev_hash(const SRTM_ITEM *item) 80 1.1 christos { 81 1.1 christos /* 82 1.1 christos * srt_blinded has already been through a crypto-grade hash function, so we 83 1.1 christos * can just use bits from that. 84 1.1 christos */ 85 1.1 christos unsigned long l; 86 1.1 christos 87 1.1 christos memcpy(&l, item->srt_blinded, sizeof(l)); 88 1.1 christos return l; 89 1.1 christos } 90 1.1 christos 91 1.1 christos static int items_rev_cmp(const SRTM_ITEM *a, const SRTM_ITEM *b) 92 1.1 christos { 93 1.1 christos /* 94 1.1 christos * We don't need to use CRYPTO_memcmp here as the relationship of 95 1.1 christos * srt_blinded to srt is already cryptographically obfuscated. 96 1.1 christos */ 97 1.1 christos return memcmp(a->srt_blinded, b->srt_blinded, sizeof(a->srt_blinded)); 98 1.1 christos } 99 1.1 christos 100 1.1 christos static int srtm_check_lh(QUIC_SRTM *srtm, LHASH_OF(SRTM_ITEM) *lh) 101 1.1 christos { 102 1.1 christos if (lh_SRTM_ITEM_error(lh)) { 103 1.1 christos srtm->alloc_failed = 1; 104 1.1 christos return 0; 105 1.1 christos } 106 1.1 christos 107 1.1 christos return 1; 108 1.1 christos } 109 1.1 christos 110 1.1 christos QUIC_SRTM *ossl_quic_srtm_new(OSSL_LIB_CTX *libctx, const char *propq) 111 1.1 christos { 112 1.1 christos QUIC_SRTM *srtm = NULL; 113 1.1 christos unsigned char key[16]; 114 1.1 christos EVP_CIPHER *ecb = NULL; 115 1.1 christos 116 1.1 christos if (RAND_priv_bytes_ex(libctx, key, sizeof(key), sizeof(key) * 8) != 1) 117 1.1 christos goto err; 118 1.1 christos 119 1.1 christos if ((srtm = OPENSSL_zalloc(sizeof(*srtm))) == NULL) 120 1.1 christos return NULL; 121 1.1 christos 122 1.1 christos /* Use AES-128-ECB as a permutation over 128-bit SRTs. */ 123 1.1 christos if ((ecb = EVP_CIPHER_fetch(libctx, "AES-128-ECB", propq)) == NULL) 124 1.1 christos goto err; 125 1.1 christos 126 1.1 christos if ((srtm->blind_ctx = EVP_CIPHER_CTX_new()) == NULL) 127 1.1 christos goto err; 128 1.1 christos 129 1.1 christos if (!EVP_EncryptInit_ex2(srtm->blind_ctx, ecb, key, NULL, NULL)) 130 1.1 christos goto err; 131 1.1 christos 132 1.1 christos EVP_CIPHER_free(ecb); 133 1.1 christos ecb = NULL; 134 1.1 christos 135 1.1 christos /* Create mappings. */ 136 1.1 christos if ((srtm->items_fwd = lh_SRTM_ITEM_new(items_fwd_hash, items_fwd_cmp)) == NULL 137 1.1 christos || (srtm->items_rev = lh_SRTM_ITEM_new(items_rev_hash, items_rev_cmp)) == NULL) 138 1.1 christos goto err; 139 1.1 christos 140 1.1 christos return srtm; 141 1.1 christos 142 1.1 christos err: 143 1.1 christos /* 144 1.1 christos * No cleansing of key needed as blinding exists only for side channel 145 1.1 christos * mitigation. 146 1.1 christos */ 147 1.1 christos ossl_quic_srtm_free(srtm); 148 1.1 christos EVP_CIPHER_free(ecb); 149 1.1 christos return NULL; 150 1.1 christos } 151 1.1 christos 152 1.1 christos static void srtm_free_each(SRTM_ITEM *ihead) 153 1.1 christos { 154 1.1 christos SRTM_ITEM *inext, *item = ihead; 155 1.1 christos 156 1.1 christos for (item = item->next_by_seq_num; item != NULL; item = inext) { 157 1.1 christos inext = item->next_by_seq_num; 158 1.1 christos OPENSSL_free(item); 159 1.1 christos } 160 1.1 christos 161 1.1 christos OPENSSL_free(ihead); 162 1.1 christos } 163 1.1 christos 164 1.1 christos void ossl_quic_srtm_free(QUIC_SRTM *srtm) 165 1.1 christos { 166 1.1 christos if (srtm == NULL) 167 1.1 christos return; 168 1.1 christos 169 1.1 christos lh_SRTM_ITEM_free(srtm->items_rev); 170 1.1 christos if (srtm->items_fwd != NULL) { 171 1.1 christos lh_SRTM_ITEM_doall(srtm->items_fwd, srtm_free_each); 172 1.1 christos lh_SRTM_ITEM_free(srtm->items_fwd); 173 1.1 christos } 174 1.1 christos 175 1.1 christos EVP_CIPHER_CTX_free(srtm->blind_ctx); 176 1.1 christos OPENSSL_free(srtm); 177 1.1 christos } 178 1.1 christos 179 1.1 christos /* 180 1.1 christos * Find a SRTM_ITEM by (opaque, seq_num). Returns NULL if no match. 181 1.1 christos * If head is non-NULL, writes the head of the relevant opaque list to *head if 182 1.1 christos * there is one. 183 1.1 christos * If prev is non-NULL, writes the previous node to *prev or NULL if it is 184 1.1 christos * the first item. 185 1.1 christos */ 186 1.1 christos static SRTM_ITEM *srtm_find(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, 187 1.1.1.2 christos SRTM_ITEM **head_p, SRTM_ITEM **prev_p) 188 1.1 christos { 189 1.1 christos SRTM_ITEM key, *item = NULL, *prev = NULL; 190 1.1 christos 191 1.1.1.2 christos key.opaque = opaque; 192 1.1 christos 193 1.1 christos item = lh_SRTM_ITEM_retrieve(srtm->items_fwd, &key); 194 1.1 christos if (head_p != NULL) 195 1.1 christos *head_p = item; 196 1.1 christos 197 1.1 christos for (; item != NULL; prev = item, item = item->next_by_seq_num) 198 1.1 christos if (item->seq_num == seq_num) { 199 1.1 christos break; 200 1.1 christos } else if (item->seq_num < seq_num) { 201 1.1 christos /* 202 1.1 christos * List is sorted in descending order so there can't be any match 203 1.1 christos * after this. 204 1.1 christos */ 205 1.1 christos item = NULL; 206 1.1 christos break; 207 1.1 christos } 208 1.1 christos 209 1.1 christos if (prev_p != NULL) 210 1.1 christos *prev_p = prev; 211 1.1 christos 212 1.1 christos return item; 213 1.1 christos } 214 1.1 christos 215 1.1 christos /* 216 1.1 christos * Inserts a SRTM_ITEM into the singly-linked by-sequence-number linked list. 217 1.1 christos * The new head pointer is written to *new_head (which may or may not be 218 1.1 christos * unchanged). 219 1.1 christos */ 220 1.1 christos static void sorted_insert_seq_num(SRTM_ITEM *head, SRTM_ITEM *item, SRTM_ITEM **new_head) 221 1.1 christos { 222 1.1 christos uint64_t seq_num = item->seq_num; 223 1.1 christos SRTM_ITEM *cur = head, **fixup = new_head; 224 1.1 christos 225 1.1 christos *new_head = head; 226 1.1 christos 227 1.1 christos while (cur != NULL && cur->seq_num > seq_num) { 228 1.1 christos fixup = &cur->next_by_seq_num; 229 1.1 christos cur = cur->next_by_seq_num; 230 1.1 christos } 231 1.1 christos 232 1.1 christos item->next_by_seq_num = *fixup; 233 1.1 christos *fixup = item; 234 1.1 christos } 235 1.1 christos 236 1.1 christos /* 237 1.1 christos * Inserts a SRTM_ITEM into the singly-linked by-SRT list. 238 1.1 christos * The new head pointer is written to *new_head (which may or may not be 239 1.1 christos * unchanged). 240 1.1 christos */ 241 1.1 christos static void sorted_insert_srt(SRTM_ITEM *head, SRTM_ITEM *item, SRTM_ITEM **new_head) 242 1.1 christos { 243 1.1 christos uintptr_t opaque = (uintptr_t)item->opaque; 244 1.1 christos SRTM_ITEM *cur = head, **fixup = new_head; 245 1.1 christos 246 1.1 christos *new_head = head; 247 1.1 christos 248 1.1 christos while (cur != NULL && (uintptr_t)cur->opaque > opaque) { 249 1.1 christos fixup = &cur->next_by_srt_blinded; 250 1.1 christos cur = cur->next_by_srt_blinded; 251 1.1 christos } 252 1.1 christos 253 1.1 christos item->next_by_srt_blinded = *fixup; 254 1.1 christos *fixup = item; 255 1.1 christos } 256 1.1 christos 257 1.1 christos /* 258 1.1 christos * Computes the blinded SRT value used for internal lookup for side channel 259 1.1 christos * mitigation purposes. We compute this once as a cached value when an SRTM_ITEM 260 1.1 christos * is formed. 261 1.1 christos */ 262 1.1 christos static int srtm_compute_blinded(QUIC_SRTM *srtm, SRTM_ITEM *item, 263 1.1.1.2 christos const QUIC_STATELESS_RESET_TOKEN *token) 264 1.1 christos { 265 1.1 christos int outl = 0; 266 1.1 christos 267 1.1 christos /* 268 1.1 christos * We use AES-128-ECB as a permutation using a random key to facilitate 269 1.1 christos * blinding for side-channel purposes. Encrypt the token as a single AES 270 1.1 christos * block. 271 1.1 christos */ 272 1.1 christos if (!EVP_EncryptUpdate(srtm->blind_ctx, item->srt_blinded, &outl, 273 1.1.1.2 christos (const unsigned char *)token, sizeof(*token))) 274 1.1 christos return 0; 275 1.1 christos 276 1.1 christos if (!ossl_assert(outl == sizeof(*token))) 277 1.1 christos return 0; 278 1.1 christos 279 1.1 christos return 1; 280 1.1 christos } 281 1.1 christos 282 1.1 christos int ossl_quic_srtm_add(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, 283 1.1.1.2 christos const QUIC_STATELESS_RESET_TOKEN *token) 284 1.1 christos { 285 1.1 christos SRTM_ITEM *item = NULL, *head = NULL, *new_head, *r_item; 286 1.1 christos 287 1.1 christos if (srtm->alloc_failed) 288 1.1 christos return 0; 289 1.1 christos 290 1.1 christos /* (opaque, seq_num) duplicates not allowed */ 291 1.1 christos if ((item = srtm_find(srtm, opaque, seq_num, &head, NULL)) != NULL) 292 1.1 christos return 0; 293 1.1 christos 294 1.1 christos if ((item = OPENSSL_zalloc(sizeof(*item))) == NULL) 295 1.1 christos return 0; 296 1.1 christos 297 1.1.1.2 christos item->opaque = opaque; 298 1.1.1.2 christos item->seq_num = seq_num; 299 1.1.1.2 christos item->srt = *token; 300 1.1 christos if (!srtm_compute_blinded(srtm, item, &item->srt)) { 301 1.1 christos OPENSSL_free(item); 302 1.1 christos return 0; 303 1.1 christos } 304 1.1 christos 305 1.1 christos /* Add to forward mapping. */ 306 1.1 christos if (head == NULL) { 307 1.1 christos /* First item under this opaque */ 308 1.1 christos lh_SRTM_ITEM_insert(srtm->items_fwd, item); 309 1.1 christos if (!srtm_check_lh(srtm, srtm->items_fwd)) { 310 1.1 christos OPENSSL_free(item); 311 1.1 christos return 0; 312 1.1 christos } 313 1.1 christos } else { 314 1.1 christos sorted_insert_seq_num(head, item, &new_head); 315 1.1 christos if (new_head != head) { /* head changed, update in lhash */ 316 1.1 christos lh_SRTM_ITEM_insert(srtm->items_fwd, new_head); 317 1.1 christos if (!srtm_check_lh(srtm, srtm->items_fwd)) { 318 1.1 christos OPENSSL_free(item); 319 1.1 christos return 0; 320 1.1 christos } 321 1.1 christos } 322 1.1 christos } 323 1.1 christos 324 1.1 christos /* Add to reverse mapping. */ 325 1.1 christos r_item = lh_SRTM_ITEM_retrieve(srtm->items_rev, item); 326 1.1 christos if (r_item == NULL) { 327 1.1 christos /* First item under this blinded SRT */ 328 1.1 christos lh_SRTM_ITEM_insert(srtm->items_rev, item); 329 1.1 christos if (!srtm_check_lh(srtm, srtm->items_rev)) 330 1.1 christos /* 331 1.1 christos * Can't free the item now as we would have to undo the insertion 332 1.1 christos * into the forward mapping which would require an insert operation 333 1.1 christos * to restore the previous value. which might also fail. However, 334 1.1 christos * the item will be freed OK when we free the entire SRTM. 335 1.1 christos */ 336 1.1 christos return 0; 337 1.1 christos } else { 338 1.1 christos sorted_insert_srt(r_item, item, &new_head); 339 1.1 christos if (new_head != r_item) { /* head changed, update in lhash */ 340 1.1 christos lh_SRTM_ITEM_insert(srtm->items_rev, new_head); 341 1.1 christos if (!srtm_check_lh(srtm, srtm->items_rev)) 342 1.1 christos /* As above. */ 343 1.1 christos return 0; 344 1.1 christos } 345 1.1 christos } 346 1.1 christos 347 1.1 christos return 1; 348 1.1 christos } 349 1.1 christos 350 1.1 christos /* Remove item from reverse mapping. */ 351 1.1 christos static int srtm_remove_from_rev(QUIC_SRTM *srtm, SRTM_ITEM *item) 352 1.1 christos { 353 1.1 christos SRTM_ITEM *rh_item; 354 1.1 christos 355 1.1 christos rh_item = lh_SRTM_ITEM_retrieve(srtm->items_rev, item); 356 1.1 christos assert(rh_item != NULL); 357 1.1 christos if (rh_item == item) { 358 1.1 christos /* 359 1.1 christos * Change lhash to point to item after this one, or remove the entry if 360 1.1 christos * this is the last one. 361 1.1 christos */ 362 1.1 christos if (item->next_by_srt_blinded != NULL) { 363 1.1 christos lh_SRTM_ITEM_insert(srtm->items_rev, item->next_by_srt_blinded); 364 1.1 christos if (!srtm_check_lh(srtm, srtm->items_rev)) 365 1.1 christos return 0; 366 1.1 christos } else { 367 1.1 christos lh_SRTM_ITEM_delete(srtm->items_rev, item); 368 1.1 christos } 369 1.1 christos } else { 370 1.1 christos /* Find our entry in the SRT list */ 371 1.1 christos for (; rh_item->next_by_srt_blinded != item; 372 1.1.1.2 christos rh_item = rh_item->next_by_srt_blinded) 373 1.1.1.2 christos ; 374 1.1 christos rh_item->next_by_srt_blinded = item->next_by_srt_blinded; 375 1.1 christos } 376 1.1 christos 377 1.1 christos return 1; 378 1.1 christos } 379 1.1 christos 380 1.1 christos int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num) 381 1.1 christos { 382 1.1 christos SRTM_ITEM *item, *prev = NULL; 383 1.1 christos 384 1.1 christos if (srtm->alloc_failed) 385 1.1 christos return 0; 386 1.1 christos 387 1.1 christos if ((item = srtm_find(srtm, opaque, seq_num, NULL, &prev)) == NULL) 388 1.1 christos /* No match */ 389 1.1 christos return 0; 390 1.1 christos 391 1.1 christos /* Remove from forward mapping. */ 392 1.1 christos if (prev == NULL) { 393 1.1 christos /* 394 1.1 christos * Change lhash to point to item after this one, or remove the entry if 395 1.1 christos * this is the last one. 396 1.1 christos */ 397 1.1 christos if (item->next_by_seq_num != NULL) { 398 1.1 christos lh_SRTM_ITEM_insert(srtm->items_fwd, item->next_by_seq_num); 399 1.1 christos if (!srtm_check_lh(srtm, srtm->items_fwd)) 400 1.1 christos return 0; 401 1.1 christos } else { 402 1.1 christos lh_SRTM_ITEM_delete(srtm->items_fwd, item); 403 1.1 christos } 404 1.1 christos } else { 405 1.1 christos prev->next_by_seq_num = item->next_by_seq_num; 406 1.1 christos } 407 1.1 christos 408 1.1 christos /* Remove from reverse mapping. */ 409 1.1 christos if (!srtm_remove_from_rev(srtm, item)) 410 1.1 christos return 0; 411 1.1 christos 412 1.1 christos OPENSSL_free(item); 413 1.1 christos return 1; 414 1.1 christos } 415 1.1 christos 416 1.1 christos int ossl_quic_srtm_cull(QUIC_SRTM *srtm, void *opaque) 417 1.1 christos { 418 1.1 christos SRTM_ITEM key, *item = NULL, *inext, *ihead; 419 1.1 christos 420 1.1 christos key.opaque = opaque; 421 1.1 christos 422 1.1 christos if (srtm->alloc_failed) 423 1.1 christos return 0; 424 1.1 christos 425 1.1 christos if ((ihead = lh_SRTM_ITEM_retrieve(srtm->items_fwd, &key)) == NULL) 426 1.1 christos return 1; /* nothing removed is a success condition */ 427 1.1 christos 428 1.1 christos for (item = ihead; item != NULL; item = inext) { 429 1.1 christos inext = item->next_by_seq_num; 430 1.1 christos if (item != ihead) { 431 1.1 christos srtm_remove_from_rev(srtm, item); 432 1.1 christos OPENSSL_free(item); 433 1.1 christos } 434 1.1 christos } 435 1.1 christos 436 1.1 christos lh_SRTM_ITEM_delete(srtm->items_fwd, ihead); 437 1.1 christos srtm_remove_from_rev(srtm, ihead); 438 1.1 christos OPENSSL_free(ihead); 439 1.1 christos return 1; 440 1.1 christos } 441 1.1 christos 442 1.1 christos int ossl_quic_srtm_lookup(QUIC_SRTM *srtm, 443 1.1.1.2 christos const QUIC_STATELESS_RESET_TOKEN *token, 444 1.1.1.2 christos size_t idx, 445 1.1.1.2 christos void **opaque, uint64_t *seq_num) 446 1.1 christos { 447 1.1 christos SRTM_ITEM key, *item; 448 1.1 christos 449 1.1 christos if (srtm->alloc_failed) 450 1.1 christos return 0; 451 1.1 christos 452 1.1 christos if (!srtm_compute_blinded(srtm, &key, token)) 453 1.1 christos return 0; 454 1.1 christos 455 1.1 christos item = lh_SRTM_ITEM_retrieve(srtm->items_rev, &key); 456 1.1.1.2 christos for (; idx > 0 && item != NULL; --idx, item = item->next_by_srt_blinded) 457 1.1.1.2 christos ; 458 1.1 christos if (item == NULL) 459 1.1 christos return 0; 460 1.1 christos 461 1.1 christos if (opaque != NULL) 462 1.1.1.2 christos *opaque = item->opaque; 463 1.1 christos if (seq_num != NULL) 464 1.1.1.2 christos *seq_num = item->seq_num; 465 1.1 christos 466 1.1 christos return 1; 467 1.1 christos } 468 1.1 christos 469 1.1 christos #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 470 1.1 christos 471 1.1 christos static uint32_t token_next = 0x5eadbeef; 472 1.1 christos static size_t tokens_seen; 473 1.1 christos 474 1.1 christos struct check_args { 475 1.1 christos uint32_t token; 476 1.1.1.2 christos int mode; 477 1.1 christos }; 478 1.1 christos 479 1.1 christos static void check_mark(SRTM_ITEM *item, void *arg) 480 1.1 christos { 481 1.1 christos struct check_args *arg_ = arg; 482 1.1 christos uint32_t token = arg_->token; 483 1.1 christos uint64_t prev_seq_num = 0; 484 1.1 christos void *prev_opaque = NULL; 485 1.1 christos int have_prev = 0; 486 1.1 christos 487 1.1 christos assert(item != NULL); 488 1.1 christos 489 1.1 christos while (item != NULL) { 490 1.1 christos if (have_prev) { 491 1.1 christos assert(!(item->opaque == prev_opaque && item->seq_num == prev_seq_num)); 492 1.1 christos if (!arg_->mode) 493 1.1 christos assert(item->opaque != prev_opaque || item->seq_num < prev_seq_num); 494 1.1 christos } 495 1.1 christos 496 1.1 christos ++tokens_seen; 497 1.1 christos item->debug_token = token; 498 1.1.1.2 christos prev_opaque = item->opaque; 499 1.1 christos prev_seq_num = item->seq_num; 500 1.1 christos have_prev = 1; 501 1.1 christos 502 1.1 christos if (arg_->mode) 503 1.1 christos item = item->next_by_srt_blinded; 504 1.1 christos else 505 1.1 christos item = item->next_by_seq_num; 506 1.1 christos } 507 1.1 christos } 508 1.1 christos 509 1.1 christos static void check_count(SRTM_ITEM *item, void *arg) 510 1.1 christos { 511 1.1 christos struct check_args *arg_ = arg; 512 1.1 christos uint32_t token = arg_->token; 513 1.1 christos 514 1.1 christos assert(item != NULL); 515 1.1 christos 516 1.1 christos while (item != NULL) { 517 1.1 christos ++tokens_seen; 518 1.1 christos assert(item->debug_token == token); 519 1.1 christos 520 1.1 christos if (arg_->mode) 521 1.1 christos item = item->next_by_seq_num; 522 1.1 christos else 523 1.1 christos item = item->next_by_srt_blinded; 524 1.1 christos } 525 1.1 christos } 526 1.1 christos 527 1.1 christos #endif 528 1.1 christos 529 1.1 christos void ossl_quic_srtm_check(const QUIC_SRTM *srtm) 530 1.1 christos { 531 1.1 christos #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 532 1.1.1.2 christos struct check_args args = { 0 }; 533 1.1 christos size_t tokens_expected, tokens_expected_old; 534 1.1 christos 535 1.1 christos args.token = token_next; 536 1.1 christos ++token_next; 537 1.1 christos 538 1.1 christos assert(srtm != NULL); 539 1.1 christos assert(srtm->blind_ctx != NULL); 540 1.1 christos assert(srtm->items_fwd != NULL); 541 1.1 christos assert(srtm->items_rev != NULL); 542 1.1 christos 543 1.1 christos tokens_seen = 0; 544 1.1 christos lh_SRTM_ITEM_doall_arg(srtm->items_fwd, check_mark, &args); 545 1.1 christos 546 1.1 christos tokens_expected = tokens_seen; 547 1.1 christos tokens_seen = 0; 548 1.1 christos lh_SRTM_ITEM_doall_arg(srtm->items_rev, check_count, &args); 549 1.1 christos 550 1.1 christos assert(tokens_seen == tokens_expected); 551 1.1 christos tokens_expected_old = tokens_expected; 552 1.1 christos 553 1.1 christos args.token = token_next; 554 1.1 christos ++token_next; 555 1.1 christos 556 1.1 christos args.mode = 1; 557 1.1 christos tokens_seen = 0; 558 1.1 christos lh_SRTM_ITEM_doall_arg(srtm->items_rev, check_mark, &args); 559 1.1 christos 560 1.1 christos tokens_expected = tokens_seen; 561 1.1 christos tokens_seen = 0; 562 1.1 christos lh_SRTM_ITEM_doall_arg(srtm->items_fwd, check_count, &args); 563 1.1 christos 564 1.1 christos assert(tokens_seen == tokens_expected); 565 1.1 christos assert(tokens_seen == tokens_expected_old); 566 1.1 christos #endif 567 1.1 christos } 568