]> git.ipfire.org Git - thirdparty/openssl.git/blame - ssl/ssl_cert_comp.c
Remove handling of NULL sig param in ossl_ecdsa_deterministic_sign
[thirdparty/openssl.git] / ssl / ssl_cert_comp.c
CommitLineData
b67cb09f 1/*
da1c088f 2 * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
b67cb09f
TS
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
10#include <stdio.h>
11#include "ssl_local.h"
12#include "internal/e_os.h"
13#include "internal/refcount.h"
14
15size_t ossl_calculate_comp_expansion(int alg, size_t length)
16{
17 size_t ret;
18 /*
19 * Uncompressibility expansion:
20 * ZLIB: N + 11 + 5 * (N >> 14)
21 * Brotli: per RFC7932: N + 5 + 3 * (N >> 16)
22 * ZSTD: N + 4 + 14 + 3 * (N >> 17) + 4
23 */
24
25 switch (alg) {
26 case TLSEXT_comp_cert_zlib:
27 ret = length + 11 + 5 * (length >> 14);
28 break;
29 case TLSEXT_comp_cert_brotli:
30 ret = length + 5 + 3 * (length >> 16);
31 break;
32 case TLSEXT_comp_cert_zstd:
33 ret = length + 22 + 3 * (length >> 17);
34 break;
35 default:
36 return 0;
37 }
38 /* Check for overflow */
39 if (ret < length)
40 return 0;
41 return ret;
42}
43
44int ossl_comp_has_alg(int a)
45{
46#ifndef OPENSSL_NO_COMP_ALG
47 /* 0 means "any" algorithm */
48 if ((a == 0 || a == TLSEXT_comp_cert_brotli) && BIO_f_brotli() != NULL)
49 return 1;
50 if ((a == 0 || a == TLSEXT_comp_cert_zstd) && BIO_f_zstd() != NULL)
51 return 1;
52 if ((a == 0 || a == TLSEXT_comp_cert_zlib) && BIO_f_zlib() != NULL)
53 return 1;
54#endif
55 return 0;
56}
57
58/* New operation Helper routine */
59#ifndef OPENSSL_NO_COMP_ALG
60static OSSL_COMP_CERT *OSSL_COMP_CERT_new(unsigned char *data, size_t len, size_t orig_len, int alg)
61{
62 OSSL_COMP_CERT *ret = NULL;
63
64 if (!ossl_comp_has_alg(alg)
65 || data == NULL
66 || (ret = OPENSSL_zalloc(sizeof(*ret))) == NULL
43a07d6d 67 || !CRYPTO_NEW_REF(&ret->references, 1))
b67cb09f
TS
68 goto err;
69
b67cb09f
TS
70 ret->data = data;
71 ret->len = len;
72 ret->orig_len = orig_len;
73 ret->alg = alg;
74 return ret;
75 err:
76 ERR_raise(ERR_LIB_SSL, ERR_R_MALLOC_FAILURE);
77 OPENSSL_free(data);
78 OPENSSL_free(ret);
79 return NULL;
80}
81
82__owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_compressed_data(unsigned char *data, size_t len,
83 size_t orig_len, int alg)
84{
85 return OSSL_COMP_CERT_new(OPENSSL_memdup(data, len), len, orig_len, alg);
86}
87
88__owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_uncompressed_data(unsigned char *data, size_t len,
89 int alg)
90{
91 OSSL_COMP_CERT *ret = NULL;
92 size_t max_length;
93 int comp_length;
94 COMP_METHOD *method;
95 unsigned char *comp_data = NULL;
96 COMP_CTX *comp_ctx = NULL;
97
98 switch (alg) {
99 case TLSEXT_comp_cert_brotli:
100 method = COMP_brotli_oneshot();
101 break;
102 case TLSEXT_comp_cert_zlib:
3840271e 103 method = COMP_zlib_oneshot();
b67cb09f
TS
104 break;
105 case TLSEXT_comp_cert_zstd:
106 method = COMP_zstd_oneshot();
107 break;
108 default:
109 goto err;
110 }
111
112 if ((max_length = ossl_calculate_comp_expansion(alg, len)) == 0
113 || method == NULL
114 || (comp_ctx = COMP_CTX_new(method)) == NULL
115 || (comp_data = OPENSSL_zalloc(max_length)) == NULL)
116 goto err;
117
118 comp_length = COMP_compress_block(comp_ctx, comp_data, max_length, data, len);
119 if (comp_length <= 0)
120 goto err;
121
122 ret = OSSL_COMP_CERT_new(comp_data, comp_length, len, alg);
123 comp_data = NULL;
124
125 err:
126 OPENSSL_free(comp_data);
127 COMP_CTX_free(comp_ctx);
128 return ret;
129}
130
131void OSSL_COMP_CERT_free(OSSL_COMP_CERT *cc)
132{
133 int i;
134
135 if (cc == NULL)
136 return;
137
43a07d6d 138 CRYPTO_DOWN_REF(&cc->references, &i);
b67cb09f
TS
139 REF_PRINT_COUNT("OSSL_COMP_CERT", cc);
140 if (i > 0)
141 return;
142 REF_ASSERT_ISNT(i < 0);
143
144 OPENSSL_free(cc->data);
43a07d6d 145 CRYPTO_FREE_REF(&cc->references);
b67cb09f
TS
146 OPENSSL_free(cc);
147}
148int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *cc)
149{
150 int i;
151
43a07d6d 152 if (CRYPTO_UP_REF(&cc->references, &i) <= 0)
b67cb09f
TS
153 return 0;
154
155 REF_PRINT_COUNT("OSSL_COMP_CERT", cc);
156 REF_ASSERT_ISNT(i < 2);
157 return ((i > 1) ? 1 : 0);
158}
159
160static int ssl_set_cert_comp_pref(int *prefs, int *algs, size_t len)
161{
162 size_t j = 0;
163 size_t i;
164 int found = 0;
165 int already_set[TLSEXT_comp_cert_limit];
166 int tmp_prefs[TLSEXT_comp_cert_limit];
167
168 /* Note that |len| is the number of |algs| elements */
169 /* clear all algorithms */
170 if (len == 0 || algs == NULL) {
171 memset(prefs, 0, sizeof(tmp_prefs));
172 return 1;
173 }
174
175 /* This will 0-terminate the array */
176 memset(tmp_prefs, 0, sizeof(tmp_prefs));
177 memset(already_set, 0, sizeof(already_set));
178 /* Include only those algorithms we support, ignoring duplicates and unknowns */
179 for (i = 0; i < len; i++) {
180 if (algs[i] != 0 && ossl_comp_has_alg(algs[i])) {
181 /* Check for duplicate */
182 if (already_set[algs[i]])
183 return 0;
184 tmp_prefs[j++] = algs[i];
185 already_set[algs[i]] = 1;
186 found = 1;
187 }
188 }
189 if (found)
190 memcpy(prefs, tmp_prefs, sizeof(tmp_prefs));
191 return found;
192}
193
194static size_t ssl_get_cert_to_compress(SSL *ssl, CERT_PKEY *cpk, unsigned char **data)
195{
196 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
197 WPACKET tmppkt;
198 BUF_MEM buf = { 0 };
199 size_t ret = 0;
b67cb09f
TS
200
201 if (sc == NULL
202 || cpk == NULL
203 || !sc->server
204 || !SSL_in_before(ssl))
205 return 0;
206
207 /* Use the |tmppkt| for the to-be-compressed data */
208 if (!WPACKET_init(&tmppkt, &buf))
209 goto out;
210
211 /* no context present, add 0-length context */
212 if (!WPACKET_put_bytes_u8(&tmppkt, 0))
213 goto out;
214
215 /*
72620ac7
TS
216 * ssl3_output_cert_chain() may generate an SSLfatal() error,
217 * for this case, we want to ignore it, argument for_comp = 1
b67cb09f 218 */
72620ac7 219 if (!ssl3_output_cert_chain(sc, &tmppkt, cpk, 1))
b67cb09f
TS
220 goto out;
221 WPACKET_get_total_written(&tmppkt, &ret);
222
223 out:
b67cb09f
TS
224 WPACKET_cleanup(&tmppkt);
225 if (ret != 0 && data != NULL)
226 *data = (unsigned char *)buf.data;
227 else
228 OPENSSL_free(buf.data);
229 return ret;
230}
231
232static int ssl_compress_one_cert(SSL *ssl, CERT_PKEY *cpk, int alg)
233{
234 unsigned char *cert_data = NULL;
235 OSSL_COMP_CERT *comp_cert = NULL;
236 size_t length;
237
238 if (cpk == NULL
239 || alg == TLSEXT_comp_cert_none
240 || !ossl_comp_has_alg(alg))
241 return 0;
242
243 if ((length = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)
244 return 0;
245 comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, length, alg);
246 OPENSSL_free(cert_data);
247 if (comp_cert == NULL)
248 return 0;
249
250 OSSL_COMP_CERT_free(cpk->comp_cert[alg]);
251 cpk->comp_cert[alg] = comp_cert;
252 return 1;
253}
254
255/* alg_in can be 0, meaning any/all algorithms */
256static int ssl_compress_certs(SSL *ssl, CERT_PKEY *cpks, int alg_in)
257{
258 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
259 int i;
260 int j;
261 int alg;
262 int count = 0;
263
264 if (sc == NULL
265 || cpks == NULL
266 || !ossl_comp_has_alg(alg_in))
267 return 0;
268
269 /* Look through the preferences to see what we have */
270 for (i = 0; i < TLSEXT_comp_cert_limit; i++) {
271 /*
272 * alg = 0 means compress for everything, but only for algorithms enabled
273 * alg != 0 means compress for that algorithm if enabled
274 */
275 alg = sc->cert_comp_prefs[i];
276 if ((alg_in == 0 && alg != TLSEXT_comp_cert_none)
277 || (alg_in != 0 && alg == alg_in)) {
278
279 for (j = 0; j < SSL_PKEY_NUM; j++) {
280 /* No cert, move on */
281 if (cpks[j].x509 == NULL)
282 continue;
283
284 if (!ssl_compress_one_cert(ssl, &cpks[j], alg))
285 return 0;
286
287 /* if the cert expanded, set the value in the CERT_PKEY to NULL */
288 if (cpks[j].comp_cert[alg]->len >= cpks[j].comp_cert[alg]->orig_len) {
289 OSSL_COMP_CERT_free(cpks[j].comp_cert[alg]);
290 cpks[j].comp_cert[alg] = NULL;
291 } else {
292 count++;
293 }
294 }
295 }
296 }
297 return (count > 0);
298}
299
300static size_t ssl_get_compressed_cert(SSL *ssl, CERT_PKEY *cpk, int alg, unsigned char **data,
301 size_t *orig_len)
302{
303 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
304 size_t cert_len = 0;
305 size_t comp_len = 0;
306 unsigned char *cert_data = NULL;
307 OSSL_COMP_CERT *comp_cert = NULL;
308
309 if (sc == NULL
310 || cpk == NULL
311 || data == NULL
312 || orig_len == NULL
313 || !sc->server
314 || !SSL_in_before(ssl)
315 || !ossl_comp_has_alg(alg))
316 return 0;
317
318 if ((cert_len = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)
319 goto err;
320
321 comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, cert_len, alg);
322 OPENSSL_free(cert_data);
323 if (comp_cert == NULL)
324 goto err;
325
326 comp_len = comp_cert->len;
327 *orig_len = comp_cert->orig_len;
328 *data = comp_cert->data;
329 comp_cert->data = NULL;
330 err:
331 OSSL_COMP_CERT_free(comp_cert);
332 return comp_len;
333}
334
335static int ossl_set1_compressed_cert(CERT *cert, int algorithm,
336 unsigned char *comp_data, size_t comp_length,
337 size_t orig_length)
338{
339 OSSL_COMP_CERT *comp_cert;
340
341 /* No explicit cert set */
342 if (cert == NULL || cert->key == NULL)
343 return 0;
344
345 comp_cert = OSSL_COMP_CERT_from_compressed_data(comp_data, comp_length,
346 orig_length, algorithm);
347 if (comp_cert == NULL)
348 return 0;
349
350 OSSL_COMP_CERT_free(cert->key->comp_cert[algorithm]);
351 cert->key->comp_cert[algorithm] = comp_cert;
352
353 return 1;
354}
355#endif
356
357/*-
358 * Public API
359 */
360int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len)
361{
362#ifndef OPENSSL_NO_COMP_ALG
363 return ssl_set_cert_comp_pref(ctx->cert_comp_prefs, algs, len);
364#else
365 return 0;
366#endif
367}
368
369int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len)
370{
371#ifndef OPENSSL_NO_COMP_ALG
372 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
373
374 if (sc == NULL)
375 return 0;
376 return ssl_set_cert_comp_pref(sc->cert_comp_prefs, algs, len);
377#else
378 return 0;
379#endif
380}
381
382int SSL_compress_certs(SSL *ssl, int alg)
383{
384#ifndef OPENSSL_NO_COMP_ALG
385 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
386
387 if (sc == NULL || sc->cert == NULL)
388 return 0;
389
390 return ssl_compress_certs(ssl, sc->cert->pkeys, alg);
391#endif
392 return 0;
393}
394
395int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg)
396{
397 int ret = 0;
398#ifndef OPENSSL_NO_COMP_ALG
399 SSL *new = SSL_new(ctx);
400
401 if (new == NULL)
402 return 0;
403
404 ret = ssl_compress_certs(new, ctx->cert->pkeys, alg);
405 SSL_free(new);
406#endif
407 return ret;
408}
409
410size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data, size_t *orig_len)
411{
412#ifndef OPENSSL_NO_COMP_ALG
413 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
414 CERT_PKEY *cpk = NULL;
415
416 if (sc->cert != NULL)
417 cpk = sc->cert->key;
418 else
419 cpk = ssl->ctx->cert->key;
420
421 return ssl_get_compressed_cert(ssl, cpk, alg, data, orig_len);
422#else
423 return 0;
424#endif
425}
426
427size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data, size_t *orig_len)
428{
429#ifndef OPENSSL_NO_COMP_ALG
430 size_t ret;
431 SSL *new = SSL_new(ctx);
432
433 ret = ssl_get_compressed_cert(new, ctx->cert->key, alg, data, orig_len);
434 SSL_free(new);
435 return ret;
436#else
437 return 0;
438#endif
439}
440
441int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int algorithm, unsigned char *comp_data,
442 size_t comp_length, size_t orig_length)
443{
444#ifndef OPENSSL_NO_COMP_ALG
445 return ossl_set1_compressed_cert(ctx->cert, algorithm, comp_data, comp_length, orig_length);
446#else
447 return 0;
448#endif
449}
450
451int SSL_set1_compressed_cert(SSL *ssl, int algorithm, unsigned char *comp_data,
452 size_t comp_length, size_t orig_length)
453{
454#ifndef OPENSSL_NO_COMP_ALG
455 SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
456
457 /* Cannot set a pre-compressed certificate on a client */
458 if (sc == NULL || !sc->server)
459 return 0;
460
461 return ossl_set1_compressed_cert(sc->cert, algorithm, comp_data, comp_length, orig_length);
462#else
463 return 0;
464#endif
465}