quic_srtm.c 16 KB

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