Mercurial > hg > nginx-quic
comparison src/event/quic/ngx_event_quic_tokens.c @ 8412:e19723c40d28 quic
QUIC: separate files for tokens related processing.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Tue, 13 Apr 2021 14:41:52 +0300 |
parents | |
children | b4e6b7049984 |
comparison
equal
deleted
inserted
replaced
8411:bc910a5ec737 | 8412:e19723c40d28 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Nginx, Inc. | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_event.h> | |
10 #include <ngx_sha1.h> | |
11 #include <ngx_event_quic_protection.h> | |
12 | |
13 | |
14 #define NGX_QUIC_MAX_TOKEN_SIZE 64 | |
15 /* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */ | |
16 | |
17 | |
18 static void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, | |
19 u_char buf[20]); | |
20 | |
21 | |
22 ngx_int_t | |
23 ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret, | |
24 u_char *token) | |
25 { | |
26 ngx_str_t tmp; | |
27 | |
28 tmp.data = secret; | |
29 tmp.len = NGX_QUIC_SR_KEY_LEN; | |
30 | |
31 if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token, | |
32 NGX_QUIC_SR_TOKEN_LEN) | |
33 != NGX_OK) | |
34 { | |
35 return NGX_ERROR; | |
36 } | |
37 | |
38 #if (NGX_DEBUG) | |
39 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
40 "quic stateless reset token %*xs", | |
41 (size_t) NGX_QUIC_SR_TOKEN_LEN, token); | |
42 #endif | |
43 | |
44 return NGX_OK; | |
45 } | |
46 | |
47 | |
48 ngx_int_t | |
49 ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token, | |
50 ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry) | |
51 { | |
52 int len, iv_len; | |
53 u_char *p, *iv; | |
54 EVP_CIPHER_CTX *ctx; | |
55 const EVP_CIPHER *cipher; | |
56 | |
57 u_char in[NGX_QUIC_MAX_TOKEN_SIZE]; | |
58 | |
59 ngx_quic_address_hash(c, !is_retry, in); | |
60 | |
61 p = in + 20; | |
62 | |
63 p = ngx_cpymem(p, &exp, sizeof(time_t)); | |
64 | |
65 *p++ = is_retry ? 1 : 0; | |
66 | |
67 if (odcid) { | |
68 *p++ = odcid->len; | |
69 p = ngx_cpymem(p, odcid->data, odcid->len); | |
70 | |
71 } else { | |
72 *p++ = 0; | |
73 } | |
74 | |
75 len = p - in; | |
76 | |
77 cipher = EVP_aes_256_cbc(); | |
78 iv_len = EVP_CIPHER_iv_length(cipher); | |
79 | |
80 token->len = iv_len + len + EVP_CIPHER_block_size(cipher); | |
81 token->data = ngx_pnalloc(c->pool, token->len); | |
82 if (token->data == NULL) { | |
83 return NGX_ERROR; | |
84 } | |
85 | |
86 ctx = EVP_CIPHER_CTX_new(); | |
87 if (ctx == NULL) { | |
88 return NGX_ERROR; | |
89 } | |
90 | |
91 iv = token->data; | |
92 | |
93 if (RAND_bytes(iv, iv_len) <= 0 | |
94 || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv)) | |
95 { | |
96 EVP_CIPHER_CTX_free(ctx); | |
97 return NGX_ERROR; | |
98 } | |
99 | |
100 token->len = iv_len; | |
101 | |
102 if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) { | |
103 EVP_CIPHER_CTX_free(ctx); | |
104 return NGX_ERROR; | |
105 } | |
106 | |
107 token->len += len; | |
108 | |
109 if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) { | |
110 EVP_CIPHER_CTX_free(ctx); | |
111 return NGX_ERROR; | |
112 } | |
113 | |
114 token->len += len; | |
115 | |
116 EVP_CIPHER_CTX_free(ctx); | |
117 | |
118 #ifdef NGX_QUIC_DEBUG_PACKETS | |
119 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
120 "quic new token len:%uz %xV", token->len, token); | |
121 #endif | |
122 | |
123 return NGX_OK; | |
124 } | |
125 | |
126 | |
127 static void | |
128 ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, u_char buf[20]) | |
129 { | |
130 size_t len; | |
131 u_char *data; | |
132 ngx_sha1_t sha1; | |
133 struct sockaddr_in *sin; | |
134 #if (NGX_HAVE_INET6) | |
135 struct sockaddr_in6 *sin6; | |
136 #endif | |
137 | |
138 len = (size_t) c->socklen; | |
139 data = (u_char *) c->sockaddr; | |
140 | |
141 if (no_port) { | |
142 switch (c->sockaddr->sa_family) { | |
143 | |
144 #if (NGX_HAVE_INET6) | |
145 case AF_INET6: | |
146 sin6 = (struct sockaddr_in6 *) c->sockaddr; | |
147 | |
148 len = sizeof(struct in6_addr); | |
149 data = sin6->sin6_addr.s6_addr; | |
150 | |
151 break; | |
152 #endif | |
153 | |
154 case AF_INET: | |
155 sin = (struct sockaddr_in *) c->sockaddr; | |
156 | |
157 len = sizeof(in_addr_t); | |
158 data = (u_char *) &sin->sin_addr; | |
159 | |
160 break; | |
161 } | |
162 } | |
163 | |
164 ngx_sha1_init(&sha1); | |
165 ngx_sha1_update(&sha1, data, len); | |
166 ngx_sha1_final(buf, &sha1); | |
167 } | |
168 | |
169 | |
170 ngx_int_t | |
171 ngx_quic_validate_token(ngx_connection_t *c, u_char *key, | |
172 ngx_quic_header_t *pkt) | |
173 { | |
174 int len, tlen, iv_len; | |
175 u_char *iv, *p; | |
176 time_t now, exp; | |
177 size_t total; | |
178 ngx_str_t odcid; | |
179 EVP_CIPHER_CTX *ctx; | |
180 const EVP_CIPHER *cipher; | |
181 | |
182 u_char addr_hash[20]; | |
183 u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE]; | |
184 | |
185 /* Retry token or NEW_TOKEN in a previous connection */ | |
186 | |
187 cipher = EVP_aes_256_cbc(); | |
188 iv = pkt->token.data; | |
189 iv_len = EVP_CIPHER_iv_length(cipher); | |
190 | |
191 /* sanity checks */ | |
192 | |
193 if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) { | |
194 goto garbage; | |
195 } | |
196 | |
197 if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) { | |
198 goto garbage; | |
199 } | |
200 | |
201 ctx = EVP_CIPHER_CTX_new(); | |
202 if (ctx == NULL) { | |
203 return NGX_ERROR; | |
204 } | |
205 | |
206 if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) { | |
207 EVP_CIPHER_CTX_free(ctx); | |
208 return NGX_ERROR; | |
209 } | |
210 | |
211 p = pkt->token.data + iv_len; | |
212 len = pkt->token.len - iv_len; | |
213 | |
214 if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) { | |
215 EVP_CIPHER_CTX_free(ctx); | |
216 goto garbage; | |
217 } | |
218 total = len; | |
219 | |
220 if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) { | |
221 EVP_CIPHER_CTX_free(ctx); | |
222 goto garbage; | |
223 } | |
224 total += tlen; | |
225 | |
226 EVP_CIPHER_CTX_free(ctx); | |
227 | |
228 if (total < (20 + sizeof(time_t) + 2)) { | |
229 goto garbage; | |
230 } | |
231 | |
232 p = tdec + 20; | |
233 | |
234 ngx_memcpy(&exp, p, sizeof(time_t)); | |
235 p += sizeof(time_t); | |
236 | |
237 pkt->retried = (*p++ == 1); | |
238 | |
239 ngx_quic_address_hash(c, !pkt->retried, addr_hash); | |
240 | |
241 if (ngx_memcmp(tdec, addr_hash, 20) != 0) { | |
242 goto bad_token; | |
243 } | |
244 | |
245 odcid.len = *p++; | |
246 if (odcid.len) { | |
247 if (odcid.len > NGX_QUIC_MAX_CID_LEN) { | |
248 goto bad_token; | |
249 } | |
250 | |
251 if ((size_t)(tdec + total - p) < odcid.len) { | |
252 goto bad_token; | |
253 } | |
254 | |
255 odcid.data = p; | |
256 p += odcid.len; | |
257 } | |
258 | |
259 now = ngx_time(); | |
260 | |
261 if (now > exp) { | |
262 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token"); | |
263 return NGX_DECLINED; | |
264 } | |
265 | |
266 if (odcid.len) { | |
267 pkt->odcid.len = odcid.len; | |
268 pkt->odcid.data = ngx_pstrdup(c->pool, &odcid); | |
269 if (pkt->odcid.data == NULL) { | |
270 return NGX_ERROR; | |
271 } | |
272 | |
273 } else { | |
274 pkt->odcid = pkt->dcid; | |
275 } | |
276 | |
277 pkt->validated = 1; | |
278 | |
279 return NGX_OK; | |
280 | |
281 garbage: | |
282 | |
283 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token"); | |
284 | |
285 return NGX_ABORT; | |
286 | |
287 bad_token: | |
288 | |
289 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token"); | |
290 | |
291 return NGX_DECLINED; | |
292 } |