/src/h2o/deps/picotls/lib/certificate_compression.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018 Fastly |
3 | | * |
4 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | | * of this software and associated documentation files (the "Software"), to |
6 | | * deal in the Software without restriction, including without limitation the |
7 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
8 | | * sell copies of the Software, and to permit persons to whom the Software is |
9 | | * furnished to do so, subject to the following conditions: |
10 | | * |
11 | | * The above copyright notice and this permission notice shall be included in |
12 | | * all copies or substantial portions of the Software. |
13 | | * |
14 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
19 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
20 | | * IN THE SOFTWARE. |
21 | | */ |
22 | | #include <assert.h> |
23 | | #include <stdlib.h> |
24 | | #include "brotli/decode.h" |
25 | | #include "brotli/encode.h" |
26 | | #include "picotls/certificate_compression.h" |
27 | | |
28 | | static inline int decompress_certificate(ptls_decompress_certificate_t *self, ptls_t *tls, uint16_t algorithm, ptls_iovec_t output, |
29 | | ptls_iovec_t input) |
30 | 0 | { |
31 | 0 | if (algorithm != PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI) |
32 | 0 | goto Fail; |
33 | | |
34 | 0 | size_t decoded_size = output.len; |
35 | 0 | if (BrotliDecoderDecompress(input.len, input.base, &decoded_size, output.base) != BROTLI_DECODER_RESULT_SUCCESS) |
36 | 0 | goto Fail; |
37 | | |
38 | 0 | if (decoded_size != output.len) |
39 | 0 | goto Fail; |
40 | | |
41 | 0 | return 0; |
42 | 0 | Fail: |
43 | 0 | return PTLS_ALERT_BAD_CERTIFICATE; |
44 | 0 | } |
45 | | |
46 | | static const uint16_t algorithms[] = {PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI, UINT16_MAX}; |
47 | | |
48 | | ptls_decompress_certificate_t ptls_decompress_certificate = {algorithms, decompress_certificate}; |
49 | | |
50 | | static int emit_compressed_certificate(ptls_emit_certificate_t *_self, ptls_t *tls, ptls_message_emitter_t *emitter, |
51 | | ptls_key_schedule_t *key_sched, ptls_iovec_t context, int push_status_request, |
52 | | const uint16_t *compress_algos, size_t num_compress_algos) |
53 | 0 | { |
54 | 0 | ptls_emit_compressed_certificate_t *self = (void *)_self; |
55 | 0 | struct st_ptls_compressed_certificate_entry_t *entry; |
56 | 0 | int ret; |
57 | |
|
58 | 0 | assert(context.len == 0 || !"precompressed mode can only be used for server certificates"); |
59 | | |
60 | 0 | for (size_t i = 0; i != num_compress_algos; ++i) { |
61 | 0 | if (compress_algos[i] == PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI) |
62 | 0 | goto FoundBrotli; |
63 | 0 | } |
64 | | /* brotli not found, delegate to the core */ |
65 | 0 | ret = PTLS_ERROR_DELEGATE; |
66 | 0 | goto Exit; |
67 | | |
68 | 0 | FoundBrotli: |
69 | 0 | entry = &self->without_ocsp_status; |
70 | 0 | if (push_status_request && self->with_ocsp_status.uncompressed_length != 0) |
71 | 0 | entry = &self->with_ocsp_status; |
72 | |
|
73 | 0 | ptls_push_message(emitter, key_sched, PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE, { |
74 | 0 | ptls_buffer_push16(emitter->buf, PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI); |
75 | 0 | ptls_buffer_push24(emitter->buf, entry->uncompressed_length); |
76 | 0 | ptls_buffer_push_block(emitter->buf, 3, { ptls_buffer_pushv(emitter->buf, entry->bytes.base, entry->bytes.len); }); |
77 | 0 | }); |
78 | | |
79 | 0 | ret = 0; |
80 | |
|
81 | 0 | Exit: |
82 | 0 | return ret; |
83 | 0 | } |
84 | | |
85 | | static int build_compressed(struct st_ptls_compressed_certificate_entry_t *entry, ptls_iovec_t *certificates, |
86 | | size_t num_certificates, ptls_iovec_t ocsp_status) |
87 | 0 | { |
88 | 0 | ptls_buffer_t uncompressed; |
89 | 0 | int ret; |
90 | |
|
91 | 0 | ptls_buffer_init(&uncompressed, "", 0); |
92 | | |
93 | | /* build uncompressed */ |
94 | 0 | if ((ret = ptls_build_certificate_message(&uncompressed, ptls_iovec_init(NULL, 0), certificates, num_certificates, |
95 | 0 | ocsp_status)) != 0) |
96 | 0 | goto Exit; |
97 | 0 | entry->uncompressed_length = (uint32_t)uncompressed.off; |
98 | | |
99 | | /* compress */ |
100 | 0 | entry->bytes.len = uncompressed.off - 1; |
101 | 0 | if ((entry->bytes.base = malloc(entry->bytes.len)) == NULL) { |
102 | 0 | ret = PTLS_ERROR_NO_MEMORY; |
103 | 0 | goto Exit; |
104 | 0 | } |
105 | 0 | if (BrotliEncoderCompress(BROTLI_MAX_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_MODE_GENERIC, uncompressed.off, uncompressed.base, |
106 | 0 | &entry->bytes.len, entry->bytes.base) != BROTLI_TRUE) { |
107 | 0 | ret = PTLS_ERROR_COMPRESSION_FAILURE; |
108 | 0 | goto Exit; |
109 | 0 | } |
110 | | |
111 | 0 | ret = 0; |
112 | |
|
113 | 0 | Exit: |
114 | 0 | if (ret != 0) { |
115 | 0 | free(entry->bytes.base); |
116 | 0 | *entry = (struct st_ptls_compressed_certificate_entry_t){0}; |
117 | 0 | } |
118 | 0 | ptls_buffer_dispose(&uncompressed); |
119 | 0 | return ret; |
120 | 0 | } |
121 | | |
122 | | int ptls_init_compressed_certificate(ptls_emit_compressed_certificate_t *self, ptls_iovec_t *certificates, size_t num_certificates, |
123 | | ptls_iovec_t ocsp_status) |
124 | 0 | { |
125 | 0 | int ret; |
126 | |
|
127 | 0 | *self = (ptls_emit_compressed_certificate_t){{emit_compressed_certificate}, PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI}; |
128 | | |
129 | | /* build entries */ |
130 | 0 | if ((ret = build_compressed(&self->without_ocsp_status, certificates, num_certificates, ptls_iovec_init(NULL, 0))) != 0) |
131 | 0 | goto Exit; |
132 | 0 | if (ocsp_status.len != 0) { |
133 | 0 | if ((ret = build_compressed(&self->with_ocsp_status, certificates, num_certificates, ocsp_status)) != 0) |
134 | 0 | goto Exit; |
135 | 0 | } |
136 | | |
137 | 0 | ret = 0; |
138 | |
|
139 | 0 | Exit: |
140 | 0 | if (ret != 0) |
141 | 0 | ptls_dispose_compressed_certificate(self); |
142 | 0 | return ret; |
143 | 0 | } |
144 | | |
145 | | void ptls_dispose_compressed_certificate(ptls_emit_compressed_certificate_t *self) |
146 | 0 | { |
147 | 0 | free(self->with_ocsp_status.bytes.base); |
148 | 0 | free(self->without_ocsp_status.bytes.base); |
149 | 0 | } |