Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/nss/lib/ssl/tls13replay.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * Anti-replay measures for TLS 1.3.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8
9
#include "nss.h"      /* for NSS_RegisterShutdown */
10
#include "nssilock.h" /* for PZMonitor */
11
#include "pk11pub.h"
12
#include "prinit.h" /* for PR_CallOnce */
13
#include "prmon.h"
14
#include "prtime.h"
15
#include "secerr.h"
16
#include "ssl.h"
17
#include "sslbloom.h"
18
#include "sslimpl.h"
19
#include "tls13hkdf.h"
20
21
static struct {
22
    /* Used to ensure that we only initialize the cleanup function once. */
23
    PRCallOnceType init;
24
    /* Used to serialize access to the filters. */
25
    PZMonitor *lock;
26
    /* The filters, use of which alternates. */
27
    sslBloomFilter filters[2];
28
    /* Which of the two filters is active (0 or 1). */
29
    PRUint8 current;
30
    /* The time that we will next update. */
31
    PRTime nextUpdate;
32
    /* The width of the window; i.e., the period of updates. */
33
    PRTime window;
34
    /* This key ensures that the bloom filter index is unpredictable. */
35
    PK11SymKey *key;
36
} ssl_anti_replay;
37
38
/* Clear the current state and free any resources we allocated. The signature
39
 * here is odd to allow this to be called during shutdown. */
40
static SECStatus
41
tls13_AntiReplayReset(void *appData, void *nssData)
42
0
{
43
0
    if (ssl_anti_replay.key) {
44
0
        PK11_FreeSymKey(ssl_anti_replay.key);
45
0
        ssl_anti_replay.key = NULL;
46
0
    }
47
0
    if (ssl_anti_replay.lock) {
48
0
        PZ_DestroyMonitor(ssl_anti_replay.lock);
49
0
        ssl_anti_replay.lock = NULL;
50
0
    }
51
0
    sslBloom_Destroy(&ssl_anti_replay.filters[0]);
52
0
    sslBloom_Destroy(&ssl_anti_replay.filters[1]);
53
0
    return SECSuccess;
54
0
}
55
56
static PRStatus
57
tls13_AntiReplayInit(void)
58
0
{
59
0
    SECStatus rv = NSS_RegisterShutdown(tls13_AntiReplayReset, NULL);
60
0
    if (rv != SECSuccess) {
61
0
        return PR_FAILURE;
62
0
    }
63
0
    return PR_SUCCESS;
64
0
}
65
66
static SECStatus
67
tls13_AntiReplayKeyGen()
68
0
{
69
0
    PRUint8 buf[32];
70
0
    SECItem keyItem = { siBuffer, buf, sizeof(buf) };
71
0
    PK11SlotInfo *slot;
72
0
    SECStatus rv;
73
0
74
0
    slot = PK11_GetInternalSlot();
75
0
    if (!slot) {
76
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
77
0
        return SECFailure;
78
0
    }
79
0
    rv = PK11_GenerateRandomOnSlot(slot, buf, sizeof(buf));
80
0
    if (rv != SECSuccess) {
81
0
        goto loser;
82
0
    }
83
0
84
0
    ssl_anti_replay.key = PK11_ImportSymKey(slot, CKM_NSS_HKDF_SHA256,
85
0
                                            PK11_OriginUnwrap, CKA_DERIVE,
86
0
                                            &keyItem, NULL);
87
0
    if (!ssl_anti_replay.key) {
88
0
        goto loser;
89
0
    }
90
0
91
0
    PK11_FreeSlot(slot);
92
0
    return SECSuccess;
93
0
94
0
loser:
95
0
    PK11_FreeSlot(slot);
96
0
    return SECFailure;
97
0
}
98
99
/* Set a limit on the combination of number of hashes and bits in each hash. */
100
0
#define SSL_MAX_BLOOM_FILTER_SIZE 64
101
102
/*
103
 * The structures created by this function can be called concurrently on
104
 * multiple threads if the server is multi-threaded.  A monitor is used to
105
 * ensure that only one thread can access the structures that change over time,
106
 * but no such guarantee is provided for configuration data.
107
 *
108
 * Functions that read from static configuration data depend on there being a
109
 * memory barrier between the setup and use of this function.
110
 */
111
SECStatus
112
SSLExp_SetupAntiReplay(PRTime window, unsigned int k, unsigned int bits)
113
0
{
114
0
    SECStatus rv;
115
0
116
0
    if (k == 0 || bits == 0) {
117
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
118
0
        return SECFailure;
119
0
    }
120
0
    if ((k * (bits + 7) / 8) > SSL_MAX_BLOOM_FILTER_SIZE) {
121
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
122
0
        return SECFailure;
123
0
    }
124
0
125
0
    if (PR_SUCCESS != PR_CallOnce(&ssl_anti_replay.init,
126
0
                                  tls13_AntiReplayInit)) {
127
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
128
0
        return SECFailure;
129
0
    }
130
0
131
0
    (void)tls13_AntiReplayReset(NULL, NULL);
132
0
133
0
    ssl_anti_replay.lock = PZ_NewMonitor(nssILockSSL);
134
0
    if (!ssl_anti_replay.lock) {
135
0
        goto loser; /* Code already set. */
136
0
    }
137
0
138
0
    rv = tls13_AntiReplayKeyGen();
139
0
    if (rv != SECSuccess) {
140
0
        goto loser; /* Code already set. */
141
0
    }
142
0
143
0
    rv = sslBloom_Init(&ssl_anti_replay.filters[0], k, bits);
144
0
    if (rv != SECSuccess) {
145
0
        goto loser; /* Code already set. */
146
0
    }
147
0
    rv = sslBloom_Init(&ssl_anti_replay.filters[1], k, bits);
148
0
    if (rv != SECSuccess) {
149
0
        goto loser; /* Code already set. */
150
0
    }
151
0
    /* When starting out, ensure that 0-RTT is not accepted until the window is
152
0
     * updated.  A ClientHello might have been accepted prior to a restart. */
153
0
    sslBloom_Fill(&ssl_anti_replay.filters[1]);
154
0
155
0
    ssl_anti_replay.current = 0;
156
0
    ssl_anti_replay.nextUpdate = ssl_TimeUsec() + window;
157
0
    ssl_anti_replay.window = window;
158
0
    return SECSuccess;
159
0
160
0
loser:
161
0
    (void)tls13_AntiReplayReset(NULL, NULL);
162
0
    return SECFailure;
163
0
}
164
165
/* This is exposed to tests.  Though it could, this doesn't take the lock on the
166
 * basis that those tests use thread confinement. */
167
void
168
tls13_AntiReplayRollover(PRTime now)
169
0
{
170
0
    ssl_anti_replay.current ^= 1;
171
0
    ssl_anti_replay.nextUpdate = now + ssl_anti_replay.window;
172
0
    sslBloom_Zero(ssl_anti_replay.filters + ssl_anti_replay.current);
173
0
}
174
175
static void
176
tls13_AntiReplayUpdate()
177
0
{
178
0
    PRTime now;
179
0
180
0
    PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ssl_anti_replay.lock);
181
0
182
0
    now = ssl_TimeUsec();
183
0
    if (now < ssl_anti_replay.nextUpdate) {
184
0
        return;
185
0
    }
186
0
187
0
    tls13_AntiReplayRollover(now);
188
0
}
189
190
PRBool
191
tls13_InWindow(const sslSocket *ss, const sslSessionID *sid)
192
0
{
193
0
    PRInt32 timeDelta;
194
0
195
0
    /* Calculate the difference between the client's view of the age of the
196
0
     * ticket (in |ss->xtnData.ticketAge|) and the server's view, which we now
197
0
     * calculate.  The result should be close to zero.  timeDelta is signed to
198
0
     * make the comparisons below easier. */
199
0
    timeDelta = ss->xtnData.ticketAge -
200
0
                ((ssl_TimeUsec() - sid->creationTime) / PR_USEC_PER_MSEC);
201
0
202
0
    /* Only allow the time delta to be at most half of our window.  This is
203
0
     * symmetrical, though it doesn't need to be; this assumes that clock errors
204
0
     * on server and client will tend to cancel each other out.
205
0
     *
206
0
     * There are two anti-replay filters that roll over each window.  In the
207
0
     * worst case, immediately after a rollover of the filters, we only have a
208
0
     * single window worth of recorded 0-RTT attempts.  Thus, the period in
209
0
     * which we can accept 0-RTT is at most one window wide.  This uses PR_ABS()
210
0
     * and half the window so that the first attempt can be up to half a window
211
0
     * early and then replays will be caught until the attempts are half a
212
0
     * window late.
213
0
     *
214
0
     * For example, a 0-RTT attempt arrives early, but near the end of window 1.
215
0
     * The attempt is then recorded in window 1.  Rollover to window 2 could
216
0
     * occur immediately afterwards.  Window 1 is still checked for new 0-RTT
217
0
     * attempts for the remainder of window 2.  Therefore, attempts to replay
218
0
     * are detected because the value is recorded in window 1.  When rollover
219
0
     * occurs again, window 1 is erased and window 3 instated.  If we allowed an
220
0
     * attempt to be late by more than half a window, then this check would not
221
0
     * prevent the same 0-RTT attempt from being accepted during window 1 and
222
0
     * later window 3.
223
0
     */
224
0
    return PR_ABS(timeDelta) < (ssl_anti_replay.window / 2);
225
0
}
226
227
/* Checks for a duplicate in the two filters we have.  Performs maintenance on
228
 * the filters as a side-effect. This only detects a probable replay, it's
229
 * possible that this will return true when the 0-RTT attempt is not genuinely a
230
 * replay.  In that case, we reject 0-RTT unnecessarily, but that's OK because
231
 * no client expects 0-RTT to work every time. */
232
PRBool
233
tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid)
234
0
{
235
0
    PRBool replay;
236
0
    unsigned int size;
237
0
    PRUint8 index;
238
0
    SECStatus rv;
239
0
    static const char *label = "tls13 anti-replay";
240
0
    PRUint8 buf[SSL_MAX_BLOOM_FILTER_SIZE];
241
0
242
0
    /* If SSL_SetupAntiReplay hasn't been called, then treat all attempts at
243
0
     * 0-RTT as a replay. */
244
0
    if (!ssl_anti_replay.init.initialized) {
245
0
        return PR_TRUE;
246
0
    }
247
0
248
0
    if (!tls13_InWindow(ss, sid)) {
249
0
        return PR_TRUE;
250
0
    }
251
0
252
0
    size = ssl_anti_replay.filters[0].k *
253
0
           (ssl_anti_replay.filters[0].bits + 7) / 8;
254
0
    PORT_Assert(size <= SSL_MAX_BLOOM_FILTER_SIZE);
255
0
    rv = tls13_HkdfExpandLabelRaw(ssl_anti_replay.key, ssl_hash_sha256,
256
0
                                  ss->xtnData.pskBinder.data,
257
0
                                  ss->xtnData.pskBinder.len,
258
0
                                  label, strlen(label),
259
0
                                  buf, size);
260
0
    if (rv != SECSuccess) {
261
0
        return PR_TRUE;
262
0
    }
263
0
264
0
    PZ_EnterMonitor(ssl_anti_replay.lock);
265
0
    tls13_AntiReplayUpdate();
266
0
267
0
    index = ssl_anti_replay.current;
268
0
    replay = sslBloom_Add(&ssl_anti_replay.filters[index], buf);
269
0
    if (!replay) {
270
0
        replay = sslBloom_Check(&ssl_anti_replay.filters[index ^ 1],
271
0
                                buf);
272
0
    }
273
0
274
0
    PZ_ExitMonitor(ssl_anti_replay.lock);
275
0
    return replay;
276
0
}