1*0957b409SSimon J. Gerraty /*
2*0957b409SSimon J. Gerraty * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
3*0957b409SSimon J. Gerraty *
4*0957b409SSimon J. Gerraty * Permission is hereby granted, free of charge, to any person obtaining
5*0957b409SSimon J. Gerraty * a copy of this software and associated documentation files (the
6*0957b409SSimon J. Gerraty * "Software"), to deal in the Software without restriction, including
7*0957b409SSimon J. Gerraty * without limitation the rights to use, copy, modify, merge, publish,
8*0957b409SSimon J. Gerraty * distribute, sublicense, and/or sell copies of the Software, and to
9*0957b409SSimon J. Gerraty * permit persons to whom the Software is furnished to do so, subject to
10*0957b409SSimon J. Gerraty * the following conditions:
11*0957b409SSimon J. Gerraty *
12*0957b409SSimon J. Gerraty * The above copyright notice and this permission notice shall be
13*0957b409SSimon J. Gerraty * included in all copies or substantial portions of the Software.
14*0957b409SSimon J. Gerraty *
15*0957b409SSimon J. Gerraty * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16*0957b409SSimon J. Gerraty * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17*0957b409SSimon J. Gerraty * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18*0957b409SSimon J. Gerraty * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19*0957b409SSimon J. Gerraty * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20*0957b409SSimon J. Gerraty * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21*0957b409SSimon J. Gerraty * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22*0957b409SSimon J. Gerraty * SOFTWARE.
23*0957b409SSimon J. Gerraty */
24*0957b409SSimon J. Gerraty
25*0957b409SSimon J. Gerraty #include "inner.h"
26*0957b409SSimon J. Gerraty
27*0957b409SSimon J. Gerraty /*
28*0957b409SSimon J. Gerraty * Each entry consists in a fixed number of bytes. Entries are concatenated
29*0957b409SSimon J. Gerraty * in the store block. "Addresses" are really offsets in the block,
30*0957b409SSimon J. Gerraty * expressed over 32 bits (so the cache may have size at most 4 GB, which
31*0957b409SSimon J. Gerraty * "ought to be enough for everyone"). The "null address" is 0xFFFFFFFF.
32*0957b409SSimon J. Gerraty * Note that since the storage block alignment is in no way guaranteed, we
33*0957b409SSimon J. Gerraty * perform only accesses that can handle unaligned data.
34*0957b409SSimon J. Gerraty *
35*0957b409SSimon J. Gerraty * Two concurrent data structures are maintained:
36*0957b409SSimon J. Gerraty *
37*0957b409SSimon J. Gerraty * -- Entries are organised in a doubly-linked list; saved entries are added
38*0957b409SSimon J. Gerraty * at the head, and loaded entries are moved to the head. Eviction uses
39*0957b409SSimon J. Gerraty * the list tail (this is the LRU algorithm).
40*0957b409SSimon J. Gerraty *
41*0957b409SSimon J. Gerraty * -- Entries are indexed with a binary tree: all left descendants of a
42*0957b409SSimon J. Gerraty * node have a lower session ID (in lexicographic order), while all
43*0957b409SSimon J. Gerraty * right descendants have a higher session ID. The tree is heuristically
44*0957b409SSimon J. Gerraty * balanced.
45*0957b409SSimon J. Gerraty *
46*0957b409SSimon J. Gerraty * Entry format:
47*0957b409SSimon J. Gerraty *
48*0957b409SSimon J. Gerraty * session ID 32 bytes
49*0957b409SSimon J. Gerraty * master secret 48 bytes
50*0957b409SSimon J. Gerraty * protocol version 2 bytes (big endian)
51*0957b409SSimon J. Gerraty * cipher suite 2 bytes (big endian)
52*0957b409SSimon J. Gerraty * list prev 4 bytes (big endian)
53*0957b409SSimon J. Gerraty * list next 4 bytes (big endian)
54*0957b409SSimon J. Gerraty * tree left child 4 bytes (big endian)
55*0957b409SSimon J. Gerraty * tree right child 4 bytes (big endian)
56*0957b409SSimon J. Gerraty *
57*0957b409SSimon J. Gerraty * If an entry has a protocol version set to 0, then it is "disabled":
58*0957b409SSimon J. Gerraty * it was a session pushed to the cache at some point, but it has
59*0957b409SSimon J. Gerraty * been explicitly removed.
60*0957b409SSimon J. Gerraty *
61*0957b409SSimon J. Gerraty * We need to keep the tree balanced because an attacker could make
62*0957b409SSimon J. Gerraty * handshakes, selecting some specific sessions (by reusing them) to
63*0957b409SSimon J. Gerraty * try to make us make an imbalanced tree that makes lookups expensive
64*0957b409SSimon J. Gerraty * (a denial-of-service attack that would persist as long as the cache
65*0957b409SSimon J. Gerraty * remains, i.e. even after the attacker made all his connections).
66*0957b409SSimon J. Gerraty * To do that, we replace the session ID (or the start of the session ID)
67*0957b409SSimon J. Gerraty * with a HMAC value computed over the replaced part; the hash function
68*0957b409SSimon J. Gerraty * implementation and the key are obtained from the server context upon
69*0957b409SSimon J. Gerraty * first save() call.
70*0957b409SSimon J. Gerraty *
71*0957b409SSimon J. Gerraty * Theoretically, an attacker could use the exact timing of the lookup
72*0957b409SSimon J. Gerraty * to infer the current tree topology, and try to revive entries to make
73*0957b409SSimon J. Gerraty * it as unbalanced as possible. However, since the session ID are
74*0957b409SSimon J. Gerraty * chosen randomly by the server, and the attacker cannot see the
75*0957b409SSimon J. Gerraty * indexing values and must thus rely on blind selection, it should be
76*0957b409SSimon J. Gerraty * exponentially difficult for the attacker to maintain a large
77*0957b409SSimon J. Gerraty * imbalance.
78*0957b409SSimon J. Gerraty */
79*0957b409SSimon J. Gerraty #define SESSION_ID_LEN 32
80*0957b409SSimon J. Gerraty #define MASTER_SECRET_LEN 48
81*0957b409SSimon J. Gerraty
82*0957b409SSimon J. Gerraty #define SESSION_ID_OFF 0
83*0957b409SSimon J. Gerraty #define MASTER_SECRET_OFF 32
84*0957b409SSimon J. Gerraty #define VERSION_OFF 80
85*0957b409SSimon J. Gerraty #define CIPHER_SUITE_OFF 82
86*0957b409SSimon J. Gerraty #define LIST_PREV_OFF 84
87*0957b409SSimon J. Gerraty #define LIST_NEXT_OFF 88
88*0957b409SSimon J. Gerraty #define TREE_LEFT_OFF 92
89*0957b409SSimon J. Gerraty #define TREE_RIGHT_OFF 96
90*0957b409SSimon J. Gerraty
91*0957b409SSimon J. Gerraty #define LRU_ENTRY_LEN 100
92*0957b409SSimon J. Gerraty
93*0957b409SSimon J. Gerraty #define ADDR_NULL ((uint32_t)-1)
94*0957b409SSimon J. Gerraty
95*0957b409SSimon J. Gerraty #define GETSET(name, off) \
96*0957b409SSimon J. Gerraty static inline uint32_t get_ ## name(br_ssl_session_cache_lru *cc, uint32_t x) \
97*0957b409SSimon J. Gerraty { \
98*0957b409SSimon J. Gerraty return br_dec32be(cc->store + x + (off)); \
99*0957b409SSimon J. Gerraty } \
100*0957b409SSimon J. Gerraty static inline void set_ ## name(br_ssl_session_cache_lru *cc, \
101*0957b409SSimon J. Gerraty uint32_t x, uint32_t val) \
102*0957b409SSimon J. Gerraty { \
103*0957b409SSimon J. Gerraty br_enc32be(cc->store + x + (off), val); \
104*0957b409SSimon J. Gerraty }
105*0957b409SSimon J. Gerraty
GETSET(prev,LIST_PREV_OFF)106*0957b409SSimon J. Gerraty GETSET(prev, LIST_PREV_OFF)
107*0957b409SSimon J. Gerraty GETSET(next, LIST_NEXT_OFF)
108*0957b409SSimon J. Gerraty GETSET(left, TREE_LEFT_OFF)
109*0957b409SSimon J. Gerraty GETSET(right, TREE_RIGHT_OFF)
110*0957b409SSimon J. Gerraty
111*0957b409SSimon J. Gerraty /*
112*0957b409SSimon J. Gerraty * Transform the session ID by replacing the first N bytes with a HMAC
113*0957b409SSimon J. Gerraty * value computed over these bytes, using the random key K (the HMAC
114*0957b409SSimon J. Gerraty * value is truncated if needed). HMAC will use the same hash function
115*0957b409SSimon J. Gerraty * as the DRBG in the SSL server context, so with SHA-256, SHA-384,
116*0957b409SSimon J. Gerraty * or SHA-1, depending on what is available.
117*0957b409SSimon J. Gerraty *
118*0957b409SSimon J. Gerraty * The risk of collision is considered too small to be a concern; and
119*0957b409SSimon J. Gerraty * the impact of a collision is low (the handshake won't succeed). This
120*0957b409SSimon J. Gerraty * risk is much lower than any transmission error, which would lead to
121*0957b409SSimon J. Gerraty * the same consequences.
122*0957b409SSimon J. Gerraty *
123*0957b409SSimon J. Gerraty * Source and destination arrays msut be disjoint.
124*0957b409SSimon J. Gerraty */
125*0957b409SSimon J. Gerraty static void
126*0957b409SSimon J. Gerraty mask_id(br_ssl_session_cache_lru *cc,
127*0957b409SSimon J. Gerraty const unsigned char *src, unsigned char *dst)
128*0957b409SSimon J. Gerraty {
129*0957b409SSimon J. Gerraty br_hmac_key_context hkc;
130*0957b409SSimon J. Gerraty br_hmac_context hc;
131*0957b409SSimon J. Gerraty
132*0957b409SSimon J. Gerraty memcpy(dst, src, SESSION_ID_LEN);
133*0957b409SSimon J. Gerraty br_hmac_key_init(&hkc, cc->hash, cc->index_key, sizeof cc->index_key);
134*0957b409SSimon J. Gerraty br_hmac_init(&hc, &hkc, SESSION_ID_LEN);
135*0957b409SSimon J. Gerraty br_hmac_update(&hc, src, SESSION_ID_LEN);
136*0957b409SSimon J. Gerraty br_hmac_out(&hc, dst);
137*0957b409SSimon J. Gerraty }
138*0957b409SSimon J. Gerraty
139*0957b409SSimon J. Gerraty /*
140*0957b409SSimon J. Gerraty * Find a node by ID. Returned value is the node address, or ADDR_NULL if
141*0957b409SSimon J. Gerraty * the node is not found.
142*0957b409SSimon J. Gerraty *
143*0957b409SSimon J. Gerraty * If addr_link is not NULL, then '*addr_link' is set to the address of the
144*0957b409SSimon J. Gerraty * last followed link. If the found node is the root, or if the tree is
145*0957b409SSimon J. Gerraty * empty, then '*addr_link' is set to ADDR_NULL.
146*0957b409SSimon J. Gerraty */
147*0957b409SSimon J. Gerraty static uint32_t
find_node(br_ssl_session_cache_lru * cc,const unsigned char * id,uint32_t * addr_link)148*0957b409SSimon J. Gerraty find_node(br_ssl_session_cache_lru *cc, const unsigned char *id,
149*0957b409SSimon J. Gerraty uint32_t *addr_link)
150*0957b409SSimon J. Gerraty {
151*0957b409SSimon J. Gerraty uint32_t x, y;
152*0957b409SSimon J. Gerraty
153*0957b409SSimon J. Gerraty x = cc->root;
154*0957b409SSimon J. Gerraty y = ADDR_NULL;
155*0957b409SSimon J. Gerraty while (x != ADDR_NULL) {
156*0957b409SSimon J. Gerraty int r;
157*0957b409SSimon J. Gerraty
158*0957b409SSimon J. Gerraty r = memcmp(id, cc->store + x + SESSION_ID_OFF, SESSION_ID_LEN);
159*0957b409SSimon J. Gerraty if (r < 0) {
160*0957b409SSimon J. Gerraty y = x + TREE_LEFT_OFF;
161*0957b409SSimon J. Gerraty x = get_left(cc, x);
162*0957b409SSimon J. Gerraty } else if (r == 0) {
163*0957b409SSimon J. Gerraty if (addr_link != NULL) {
164*0957b409SSimon J. Gerraty *addr_link = y;
165*0957b409SSimon J. Gerraty }
166*0957b409SSimon J. Gerraty return x;
167*0957b409SSimon J. Gerraty } else {
168*0957b409SSimon J. Gerraty y = x + TREE_RIGHT_OFF;
169*0957b409SSimon J. Gerraty x = get_right(cc, x);
170*0957b409SSimon J. Gerraty }
171*0957b409SSimon J. Gerraty }
172*0957b409SSimon J. Gerraty if (addr_link != NULL) {
173*0957b409SSimon J. Gerraty *addr_link = y;
174*0957b409SSimon J. Gerraty }
175*0957b409SSimon J. Gerraty return ADDR_NULL;
176*0957b409SSimon J. Gerraty }
177*0957b409SSimon J. Gerraty
178*0957b409SSimon J. Gerraty /*
179*0957b409SSimon J. Gerraty * For node x, find its replacement upon removal.
180*0957b409SSimon J. Gerraty *
181*0957b409SSimon J. Gerraty * -- If node x has no child, then this returns ADDR_NULL.
182*0957b409SSimon J. Gerraty * -- Otherwise, if node x has a left child, then the replacement is the
183*0957b409SSimon J. Gerraty * rightmost left-descendent.
184*0957b409SSimon J. Gerraty * -- Otherwise, the replacement is the leftmost right-descendent.
185*0957b409SSimon J. Gerraty *
186*0957b409SSimon J. Gerraty * If a node is returned, then '*al' is set to the address of the field
187*0957b409SSimon J. Gerraty * that points to that node. Otherwise (node x has no child), '*al' is
188*0957b409SSimon J. Gerraty * set to ADDR_NULL.
189*0957b409SSimon J. Gerraty *
190*0957b409SSimon J. Gerraty * Note that the replacement node, when found, is always a descendent
191*0957b409SSimon J. Gerraty * of node 'x', so it cannot be the tree root. Thus, '*al' can be set
192*0957b409SSimon J. Gerraty * to ADDR_NULL only when no node is found and ADDR_NULL is returned.
193*0957b409SSimon J. Gerraty */
194*0957b409SSimon J. Gerraty static uint32_t
find_replacement_node(br_ssl_session_cache_lru * cc,uint32_t x,uint32_t * al)195*0957b409SSimon J. Gerraty find_replacement_node(br_ssl_session_cache_lru *cc, uint32_t x, uint32_t *al)
196*0957b409SSimon J. Gerraty {
197*0957b409SSimon J. Gerraty uint32_t y1, y2;
198*0957b409SSimon J. Gerraty
199*0957b409SSimon J. Gerraty y1 = get_left(cc, x);
200*0957b409SSimon J. Gerraty if (y1 != ADDR_NULL) {
201*0957b409SSimon J. Gerraty y2 = x + TREE_LEFT_OFF;
202*0957b409SSimon J. Gerraty for (;;) {
203*0957b409SSimon J. Gerraty uint32_t z;
204*0957b409SSimon J. Gerraty
205*0957b409SSimon J. Gerraty z = get_right(cc, y1);
206*0957b409SSimon J. Gerraty if (z == ADDR_NULL) {
207*0957b409SSimon J. Gerraty *al = y2;
208*0957b409SSimon J. Gerraty return y1;
209*0957b409SSimon J. Gerraty }
210*0957b409SSimon J. Gerraty y2 = y1 + TREE_RIGHT_OFF;
211*0957b409SSimon J. Gerraty y1 = z;
212*0957b409SSimon J. Gerraty }
213*0957b409SSimon J. Gerraty }
214*0957b409SSimon J. Gerraty y1 = get_right(cc, x);
215*0957b409SSimon J. Gerraty if (y1 != ADDR_NULL) {
216*0957b409SSimon J. Gerraty y2 = x + TREE_RIGHT_OFF;
217*0957b409SSimon J. Gerraty for (;;) {
218*0957b409SSimon J. Gerraty uint32_t z;
219*0957b409SSimon J. Gerraty
220*0957b409SSimon J. Gerraty z = get_left(cc, y1);
221*0957b409SSimon J. Gerraty if (z == ADDR_NULL) {
222*0957b409SSimon J. Gerraty *al = y2;
223*0957b409SSimon J. Gerraty return y1;
224*0957b409SSimon J. Gerraty }
225*0957b409SSimon J. Gerraty y2 = y1 + TREE_LEFT_OFF;
226*0957b409SSimon J. Gerraty y1 = z;
227*0957b409SSimon J. Gerraty }
228*0957b409SSimon J. Gerraty }
229*0957b409SSimon J. Gerraty *al = ADDR_NULL;
230*0957b409SSimon J. Gerraty return ADDR_NULL;
231*0957b409SSimon J. Gerraty }
232*0957b409SSimon J. Gerraty
233*0957b409SSimon J. Gerraty /*
234*0957b409SSimon J. Gerraty * Set the link at address 'alx' to point to node 'x'. If 'alx' is
235*0957b409SSimon J. Gerraty * ADDR_NULL, then this sets the tree root to 'x'.
236*0957b409SSimon J. Gerraty */
237*0957b409SSimon J. Gerraty static inline void
set_link(br_ssl_session_cache_lru * cc,uint32_t alx,uint32_t x)238*0957b409SSimon J. Gerraty set_link(br_ssl_session_cache_lru *cc, uint32_t alx, uint32_t x)
239*0957b409SSimon J. Gerraty {
240*0957b409SSimon J. Gerraty if (alx == ADDR_NULL) {
241*0957b409SSimon J. Gerraty cc->root = x;
242*0957b409SSimon J. Gerraty } else {
243*0957b409SSimon J. Gerraty br_enc32be(cc->store + alx, x);
244*0957b409SSimon J. Gerraty }
245*0957b409SSimon J. Gerraty }
246*0957b409SSimon J. Gerraty
247*0957b409SSimon J. Gerraty /*
248*0957b409SSimon J. Gerraty * Remove node 'x' from the tree. This function shall not be called if
249*0957b409SSimon J. Gerraty * node 'x' is not part of the tree.
250*0957b409SSimon J. Gerraty */
251*0957b409SSimon J. Gerraty static void
remove_node(br_ssl_session_cache_lru * cc,uint32_t x)252*0957b409SSimon J. Gerraty remove_node(br_ssl_session_cache_lru *cc, uint32_t x)
253*0957b409SSimon J. Gerraty {
254*0957b409SSimon J. Gerraty uint32_t alx, y, aly;
255*0957b409SSimon J. Gerraty
256*0957b409SSimon J. Gerraty /*
257*0957b409SSimon J. Gerraty * Removal algorithm:
258*0957b409SSimon J. Gerraty * ------------------
259*0957b409SSimon J. Gerraty *
260*0957b409SSimon J. Gerraty * - If we remove the root, then the tree becomes empty.
261*0957b409SSimon J. Gerraty *
262*0957b409SSimon J. Gerraty * - If the removed node has no child, then we can simply remove
263*0957b409SSimon J. Gerraty * it, with nothing else to do.
264*0957b409SSimon J. Gerraty *
265*0957b409SSimon J. Gerraty * - Otherwise, the removed node must be replaced by either its
266*0957b409SSimon J. Gerraty * rightmost left-descendent, or its leftmost right-descendent.
267*0957b409SSimon J. Gerraty * The replacement node itself must be removed from its current
268*0957b409SSimon J. Gerraty * place. By definition, that replacement node has either no
269*0957b409SSimon J. Gerraty * child, or at most a single child that will replace it in the
270*0957b409SSimon J. Gerraty * tree.
271*0957b409SSimon J. Gerraty */
272*0957b409SSimon J. Gerraty
273*0957b409SSimon J. Gerraty /*
274*0957b409SSimon J. Gerraty * Find node back and its ancestor link. If the node was the
275*0957b409SSimon J. Gerraty * root, then alx is set to ADDR_NULL.
276*0957b409SSimon J. Gerraty */
277*0957b409SSimon J. Gerraty find_node(cc, cc->store + x + SESSION_ID_OFF, &alx);
278*0957b409SSimon J. Gerraty
279*0957b409SSimon J. Gerraty /*
280*0957b409SSimon J. Gerraty * Find replacement node 'y', and 'aly' is set to the address of
281*0957b409SSimon J. Gerraty * the link to that replacement node. If the removed node has no
282*0957b409SSimon J. Gerraty * child, then both 'y' and 'aly' are set to ADDR_NULL.
283*0957b409SSimon J. Gerraty */
284*0957b409SSimon J. Gerraty y = find_replacement_node(cc, x, &aly);
285*0957b409SSimon J. Gerraty
286*0957b409SSimon J. Gerraty if (y != ADDR_NULL) {
287*0957b409SSimon J. Gerraty uint32_t z;
288*0957b409SSimon J. Gerraty
289*0957b409SSimon J. Gerraty /*
290*0957b409SSimon J. Gerraty * The unlinked replacement node may have one child (but
291*0957b409SSimon J. Gerraty * not two) that takes its place.
292*0957b409SSimon J. Gerraty */
293*0957b409SSimon J. Gerraty z = get_left(cc, y);
294*0957b409SSimon J. Gerraty if (z == ADDR_NULL) {
295*0957b409SSimon J. Gerraty z = get_right(cc, y);
296*0957b409SSimon J. Gerraty }
297*0957b409SSimon J. Gerraty set_link(cc, aly, z);
298*0957b409SSimon J. Gerraty
299*0957b409SSimon J. Gerraty /*
300*0957b409SSimon J. Gerraty * Link the replacement node in its new place, overwriting
301*0957b409SSimon J. Gerraty * the current link to the node 'x' (which removes 'x').
302*0957b409SSimon J. Gerraty */
303*0957b409SSimon J. Gerraty set_link(cc, alx, y);
304*0957b409SSimon J. Gerraty
305*0957b409SSimon J. Gerraty /*
306*0957b409SSimon J. Gerraty * The replacement node adopts the left and right children
307*0957b409SSimon J. Gerraty * of the removed node. Note that this also works even if
308*0957b409SSimon J. Gerraty * the replacement node was a direct descendent of the
309*0957b409SSimon J. Gerraty * removed node, since we unlinked it previously.
310*0957b409SSimon J. Gerraty */
311*0957b409SSimon J. Gerraty set_left(cc, y, get_left(cc, x));
312*0957b409SSimon J. Gerraty set_right(cc, y, get_right(cc, x));
313*0957b409SSimon J. Gerraty } else {
314*0957b409SSimon J. Gerraty /*
315*0957b409SSimon J. Gerraty * No replacement, we simply unlink the node 'x'.
316*0957b409SSimon J. Gerraty */
317*0957b409SSimon J. Gerraty set_link(cc, alx, ADDR_NULL);
318*0957b409SSimon J. Gerraty }
319*0957b409SSimon J. Gerraty }
320*0957b409SSimon J. Gerraty
321*0957b409SSimon J. Gerraty static void
lru_save(const br_ssl_session_cache_class ** ctx,br_ssl_server_context * server_ctx,const br_ssl_session_parameters * params)322*0957b409SSimon J. Gerraty lru_save(const br_ssl_session_cache_class **ctx,
323*0957b409SSimon J. Gerraty br_ssl_server_context *server_ctx,
324*0957b409SSimon J. Gerraty const br_ssl_session_parameters *params)
325*0957b409SSimon J. Gerraty {
326*0957b409SSimon J. Gerraty br_ssl_session_cache_lru *cc;
327*0957b409SSimon J. Gerraty unsigned char id[SESSION_ID_LEN];
328*0957b409SSimon J. Gerraty uint32_t x, alx;
329*0957b409SSimon J. Gerraty
330*0957b409SSimon J. Gerraty cc = (br_ssl_session_cache_lru *)ctx;
331*0957b409SSimon J. Gerraty
332*0957b409SSimon J. Gerraty /*
333*0957b409SSimon J. Gerraty * If the buffer is too small, we don't record anything. This
334*0957b409SSimon J. Gerraty * test avoids problems in subsequent code.
335*0957b409SSimon J. Gerraty */
336*0957b409SSimon J. Gerraty if (cc->store_len < LRU_ENTRY_LEN) {
337*0957b409SSimon J. Gerraty return;
338*0957b409SSimon J. Gerraty }
339*0957b409SSimon J. Gerraty
340*0957b409SSimon J. Gerraty /*
341*0957b409SSimon J. Gerraty * Upon the first save in a session cache instance, we obtain
342*0957b409SSimon J. Gerraty * a random key for our indexing.
343*0957b409SSimon J. Gerraty */
344*0957b409SSimon J. Gerraty if (!cc->init_done) {
345*0957b409SSimon J. Gerraty br_hmac_drbg_generate(&server_ctx->eng.rng,
346*0957b409SSimon J. Gerraty cc->index_key, sizeof cc->index_key);
347*0957b409SSimon J. Gerraty cc->hash = br_hmac_drbg_get_hash(&server_ctx->eng.rng);
348*0957b409SSimon J. Gerraty cc->init_done = 1;
349*0957b409SSimon J. Gerraty }
350*0957b409SSimon J. Gerraty mask_id(cc, params->session_id, id);
351*0957b409SSimon J. Gerraty
352*0957b409SSimon J. Gerraty /*
353*0957b409SSimon J. Gerraty * Look for the node in the tree. If the same ID is already used,
354*0957b409SSimon J. Gerraty * then reject it. This is a collision event, which should be
355*0957b409SSimon J. Gerraty * exceedingly rare.
356*0957b409SSimon J. Gerraty * Note: we do NOT record the emplacement here, because the
357*0957b409SSimon J. Gerraty * removal of an entry may change the tree topology.
358*0957b409SSimon J. Gerraty */
359*0957b409SSimon J. Gerraty if (find_node(cc, id, NULL) != ADDR_NULL) {
360*0957b409SSimon J. Gerraty return;
361*0957b409SSimon J. Gerraty }
362*0957b409SSimon J. Gerraty
363*0957b409SSimon J. Gerraty /*
364*0957b409SSimon J. Gerraty * Find some room for the new parameters. If the cache is not
365*0957b409SSimon J. Gerraty * full yet, add it to the end of the area and bump the pointer up.
366*0957b409SSimon J. Gerraty * Otherwise, evict the list tail entry. Note that we already
367*0957b409SSimon J. Gerraty * filtered out the case of a ridiculously small buffer that
368*0957b409SSimon J. Gerraty * cannot hold any entry at all; thus, if there is no room for an
369*0957b409SSimon J. Gerraty * extra entry, then the cache cannot be empty.
370*0957b409SSimon J. Gerraty */
371*0957b409SSimon J. Gerraty if (cc->store_ptr > (cc->store_len - LRU_ENTRY_LEN)) {
372*0957b409SSimon J. Gerraty /*
373*0957b409SSimon J. Gerraty * Evict tail. If the buffer has room for a single entry,
374*0957b409SSimon J. Gerraty * then this may also be the head.
375*0957b409SSimon J. Gerraty */
376*0957b409SSimon J. Gerraty x = cc->tail;
377*0957b409SSimon J. Gerraty cc->tail = get_prev(cc, x);
378*0957b409SSimon J. Gerraty if (cc->tail == ADDR_NULL) {
379*0957b409SSimon J. Gerraty cc->head = ADDR_NULL;
380*0957b409SSimon J. Gerraty } else {
381*0957b409SSimon J. Gerraty set_next(cc, cc->tail, ADDR_NULL);
382*0957b409SSimon J. Gerraty }
383*0957b409SSimon J. Gerraty
384*0957b409SSimon J. Gerraty /*
385*0957b409SSimon J. Gerraty * Remove the node from the tree.
386*0957b409SSimon J. Gerraty */
387*0957b409SSimon J. Gerraty remove_node(cc, x);
388*0957b409SSimon J. Gerraty } else {
389*0957b409SSimon J. Gerraty /*
390*0957b409SSimon J. Gerraty * Allocate room for new node.
391*0957b409SSimon J. Gerraty */
392*0957b409SSimon J. Gerraty x = cc->store_ptr;
393*0957b409SSimon J. Gerraty cc->store_ptr += LRU_ENTRY_LEN;
394*0957b409SSimon J. Gerraty }
395*0957b409SSimon J. Gerraty
396*0957b409SSimon J. Gerraty /*
397*0957b409SSimon J. Gerraty * Find the emplacement for the new node, and link it.
398*0957b409SSimon J. Gerraty */
399*0957b409SSimon J. Gerraty find_node(cc, id, &alx);
400*0957b409SSimon J. Gerraty set_link(cc, alx, x);
401*0957b409SSimon J. Gerraty set_left(cc, x, ADDR_NULL);
402*0957b409SSimon J. Gerraty set_right(cc, x, ADDR_NULL);
403*0957b409SSimon J. Gerraty
404*0957b409SSimon J. Gerraty /*
405*0957b409SSimon J. Gerraty * New entry becomes new list head. It may also become the list
406*0957b409SSimon J. Gerraty * tail if the cache was empty at that point.
407*0957b409SSimon J. Gerraty */
408*0957b409SSimon J. Gerraty if (cc->head == ADDR_NULL) {
409*0957b409SSimon J. Gerraty cc->tail = x;
410*0957b409SSimon J. Gerraty } else {
411*0957b409SSimon J. Gerraty set_prev(cc, cc->head, x);
412*0957b409SSimon J. Gerraty }
413*0957b409SSimon J. Gerraty set_prev(cc, x, ADDR_NULL);
414*0957b409SSimon J. Gerraty set_next(cc, x, cc->head);
415*0957b409SSimon J. Gerraty cc->head = x;
416*0957b409SSimon J. Gerraty
417*0957b409SSimon J. Gerraty /*
418*0957b409SSimon J. Gerraty * Fill data in the entry.
419*0957b409SSimon J. Gerraty */
420*0957b409SSimon J. Gerraty memcpy(cc->store + x + SESSION_ID_OFF, id, SESSION_ID_LEN);
421*0957b409SSimon J. Gerraty memcpy(cc->store + x + MASTER_SECRET_OFF,
422*0957b409SSimon J. Gerraty params->master_secret, MASTER_SECRET_LEN);
423*0957b409SSimon J. Gerraty br_enc16be(cc->store + x + VERSION_OFF, params->version);
424*0957b409SSimon J. Gerraty br_enc16be(cc->store + x + CIPHER_SUITE_OFF, params->cipher_suite);
425*0957b409SSimon J. Gerraty }
426*0957b409SSimon J. Gerraty
427*0957b409SSimon J. Gerraty static int
lru_load(const br_ssl_session_cache_class ** ctx,br_ssl_server_context * server_ctx,br_ssl_session_parameters * params)428*0957b409SSimon J. Gerraty lru_load(const br_ssl_session_cache_class **ctx,
429*0957b409SSimon J. Gerraty br_ssl_server_context *server_ctx,
430*0957b409SSimon J. Gerraty br_ssl_session_parameters *params)
431*0957b409SSimon J. Gerraty {
432*0957b409SSimon J. Gerraty br_ssl_session_cache_lru *cc;
433*0957b409SSimon J. Gerraty unsigned char id[SESSION_ID_LEN];
434*0957b409SSimon J. Gerraty uint32_t x;
435*0957b409SSimon J. Gerraty
436*0957b409SSimon J. Gerraty (void)server_ctx;
437*0957b409SSimon J. Gerraty cc = (br_ssl_session_cache_lru *)ctx;
438*0957b409SSimon J. Gerraty if (!cc->init_done) {
439*0957b409SSimon J. Gerraty return 0;
440*0957b409SSimon J. Gerraty }
441*0957b409SSimon J. Gerraty mask_id(cc, params->session_id, id);
442*0957b409SSimon J. Gerraty x = find_node(cc, id, NULL);
443*0957b409SSimon J. Gerraty if (x != ADDR_NULL) {
444*0957b409SSimon J. Gerraty unsigned version;
445*0957b409SSimon J. Gerraty
446*0957b409SSimon J. Gerraty version = br_dec16be(cc->store + x + VERSION_OFF);
447*0957b409SSimon J. Gerraty if (version == 0) {
448*0957b409SSimon J. Gerraty /*
449*0957b409SSimon J. Gerraty * Entry is disabled, we pretend we did not find it.
450*0957b409SSimon J. Gerraty * Notably, we don't move it to the front of the
451*0957b409SSimon J. Gerraty * LRU list.
452*0957b409SSimon J. Gerraty */
453*0957b409SSimon J. Gerraty return 0;
454*0957b409SSimon J. Gerraty }
455*0957b409SSimon J. Gerraty params->version = version;
456*0957b409SSimon J. Gerraty params->cipher_suite = br_dec16be(
457*0957b409SSimon J. Gerraty cc->store + x + CIPHER_SUITE_OFF);
458*0957b409SSimon J. Gerraty memcpy(params->master_secret,
459*0957b409SSimon J. Gerraty cc->store + x + MASTER_SECRET_OFF,
460*0957b409SSimon J. Gerraty MASTER_SECRET_LEN);
461*0957b409SSimon J. Gerraty if (x != cc->head) {
462*0957b409SSimon J. Gerraty /*
463*0957b409SSimon J. Gerraty * Found node is not at list head, so move
464*0957b409SSimon J. Gerraty * it to the head.
465*0957b409SSimon J. Gerraty */
466*0957b409SSimon J. Gerraty uint32_t p, n;
467*0957b409SSimon J. Gerraty
468*0957b409SSimon J. Gerraty p = get_prev(cc, x);
469*0957b409SSimon J. Gerraty n = get_next(cc, x);
470*0957b409SSimon J. Gerraty set_next(cc, p, n);
471*0957b409SSimon J. Gerraty if (n == ADDR_NULL) {
472*0957b409SSimon J. Gerraty cc->tail = p;
473*0957b409SSimon J. Gerraty } else {
474*0957b409SSimon J. Gerraty set_prev(cc, n, p);
475*0957b409SSimon J. Gerraty }
476*0957b409SSimon J. Gerraty set_prev(cc, cc->head, x);
477*0957b409SSimon J. Gerraty set_next(cc, x, cc->head);
478*0957b409SSimon J. Gerraty set_prev(cc, x, ADDR_NULL);
479*0957b409SSimon J. Gerraty cc->head = x;
480*0957b409SSimon J. Gerraty }
481*0957b409SSimon J. Gerraty return 1;
482*0957b409SSimon J. Gerraty }
483*0957b409SSimon J. Gerraty return 0;
484*0957b409SSimon J. Gerraty }
485*0957b409SSimon J. Gerraty
486*0957b409SSimon J. Gerraty static const br_ssl_session_cache_class lru_class = {
487*0957b409SSimon J. Gerraty sizeof(br_ssl_session_cache_lru),
488*0957b409SSimon J. Gerraty &lru_save,
489*0957b409SSimon J. Gerraty &lru_load
490*0957b409SSimon J. Gerraty };
491*0957b409SSimon J. Gerraty
492*0957b409SSimon J. Gerraty /* see inner.h */
493*0957b409SSimon J. Gerraty void
br_ssl_session_cache_lru_init(br_ssl_session_cache_lru * cc,unsigned char * store,size_t store_len)494*0957b409SSimon J. Gerraty br_ssl_session_cache_lru_init(br_ssl_session_cache_lru *cc,
495*0957b409SSimon J. Gerraty unsigned char *store, size_t store_len)
496*0957b409SSimon J. Gerraty {
497*0957b409SSimon J. Gerraty cc->vtable = &lru_class;
498*0957b409SSimon J. Gerraty cc->store = store;
499*0957b409SSimon J. Gerraty cc->store_len = store_len;
500*0957b409SSimon J. Gerraty cc->store_ptr = 0;
501*0957b409SSimon J. Gerraty cc->init_done = 0;
502*0957b409SSimon J. Gerraty cc->head = ADDR_NULL;
503*0957b409SSimon J. Gerraty cc->tail = ADDR_NULL;
504*0957b409SSimon J. Gerraty cc->root = ADDR_NULL;
505*0957b409SSimon J. Gerraty }
506*0957b409SSimon J. Gerraty
507*0957b409SSimon J. Gerraty /* see bearssl_ssl.h */
br_ssl_session_cache_lru_forget(br_ssl_session_cache_lru * cc,const unsigned char * id)508*0957b409SSimon J. Gerraty void br_ssl_session_cache_lru_forget(
509*0957b409SSimon J. Gerraty br_ssl_session_cache_lru *cc, const unsigned char *id)
510*0957b409SSimon J. Gerraty {
511*0957b409SSimon J. Gerraty unsigned char mid[SESSION_ID_LEN];
512*0957b409SSimon J. Gerraty uint32_t addr;
513*0957b409SSimon J. Gerraty
514*0957b409SSimon J. Gerraty /*
515*0957b409SSimon J. Gerraty * If the cache is not initialised yet, then it is empty, and
516*0957b409SSimon J. Gerraty * there is nothing to forget.
517*0957b409SSimon J. Gerraty */
518*0957b409SSimon J. Gerraty if (!cc->init_done) {
519*0957b409SSimon J. Gerraty return;
520*0957b409SSimon J. Gerraty }
521*0957b409SSimon J. Gerraty
522*0957b409SSimon J. Gerraty /*
523*0957b409SSimon J. Gerraty * Look for the node in the tree. If found, the entry is marked
524*0957b409SSimon J. Gerraty * as "disabled"; it will be reused in due course, as it ages
525*0957b409SSimon J. Gerraty * through the list.
526*0957b409SSimon J. Gerraty *
527*0957b409SSimon J. Gerraty * We do not go through the complex moves of actually releasing
528*0957b409SSimon J. Gerraty * the entry right away because explicitly forgetting sessions
529*0957b409SSimon J. Gerraty * should be a rare event, meant mostly for testing purposes,
530*0957b409SSimon J. Gerraty * so this is not worth the extra code size.
531*0957b409SSimon J. Gerraty */
532*0957b409SSimon J. Gerraty mask_id(cc, id, mid);
533*0957b409SSimon J. Gerraty addr = find_node(cc, mid, NULL);
534*0957b409SSimon J. Gerraty if (addr != ADDR_NULL) {
535*0957b409SSimon J. Gerraty br_enc16be(cc->store + addr + VERSION_OFF, 0);
536*0957b409SSimon J. Gerraty }
537*0957b409SSimon J. Gerraty }
538