Branch data Line data Source code
1 : : /*
2 : : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License").
5 : : * You may not use this file except in compliance with the License.
6 : : * A copy of the License is located at
7 : : *
8 : : * http://aws.amazon.com/apache2.0
9 : : *
10 : : * or in the "license" file accompanying this file. This file is distributed
11 : : * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 : : * express or implied. See the License for the specific language governing
13 : : * permissions and limitations under the License.
14 : : */
15 : :
16 : : #include "tls/s2n_fingerprint.h"
17 : :
18 : : #include "utils/s2n_blob.h"
19 : : #include "utils/s2n_mem.h"
20 : : #include "utils/s2n_safety.h"
21 : :
22 : : static S2N_RESULT s2n_fingerprint_init(struct s2n_fingerprint *fingerprint,
23 : : s2n_fingerprint_type type)
24 : 143 : {
25 [ - + ][ # # ]: 143 : RESULT_ENSURE_REF(fingerprint);
26 : :
27 : 143 : switch (type) {
28 [ + + ]: 71 : case S2N_FINGERPRINT_JA3:
29 : 71 : fingerprint->method = &ja3_fingerprint;
30 : 71 : break;
31 [ + + ]: 71 : case S2N_FINGERPRINT_JA4:
32 : 71 : fingerprint->method = &ja4_fingerprint;
33 : 71 : break;
34 [ + + ]: 1 : default:
35 [ + - ]: 1 : RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT);
36 : 143 : }
37 : :
38 : 142 : const struct s2n_fingerprint_method *method = fingerprint->method;
39 [ - + ][ # # ]: 142 : RESULT_ENSURE_REF(method);
40 [ - + ]: 142 : RESULT_GUARD_POSIX(s2n_hash_new(&fingerprint->hash));
41 [ - + ]: 142 : RESULT_GUARD_POSIX(s2n_hash_init(&fingerprint->hash, method->hash));
42 : 142 : return S2N_RESULT_OK;
43 : 142 : }
44 : :
45 : : struct s2n_fingerprint *s2n_fingerprint_new(s2n_fingerprint_type type)
46 : 90 : {
47 : 90 : DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free);
48 [ - + ]: 90 : PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_fingerprint)));
49 [ - + ]: 90 : PTR_GUARD_POSIX(s2n_blob_zero(&mem));
50 : 90 : struct s2n_fingerprint *fingerprint = (struct s2n_fingerprint *) (void *) mem.data;
51 [ - + ][ # # ]: 90 : PTR_ENSURE_REF(fingerprint);
52 [ + + ]: 90 : PTR_GUARD_RESULT(s2n_fingerprint_init(fingerprint, type));
53 : 89 : ZERO_TO_DISABLE_DEFER_CLEANUP(mem);
54 : 89 : return fingerprint;
55 : 90 : }
56 : :
57 : : static S2N_CLEANUP_RESULT s2n_fingerprint_free_fields(struct s2n_fingerprint *fingerprint)
58 : 143 : {
59 [ + + ]: 143 : if (!fingerprint) {
60 : 1 : return S2N_RESULT_OK;
61 : 1 : }
62 [ - + ]: 142 : RESULT_GUARD_POSIX(s2n_hash_free(&fingerprint->hash));
63 [ - + ]: 142 : RESULT_GUARD_POSIX(s2n_stuffer_free(&fingerprint->workspace));
64 : 142 : return S2N_RESULT_OK;
65 : 142 : }
66 : :
67 : : int s2n_fingerprint_free(struct s2n_fingerprint **fingerprint_ptr)
68 : 91 : {
69 [ + + ]: 91 : if (!fingerprint_ptr) {
70 : 1 : return S2N_SUCCESS;
71 : 1 : }
72 [ - + ]: 90 : POSIX_GUARD_RESULT(s2n_fingerprint_free_fields(*fingerprint_ptr));
73 [ - + ]: 90 : POSIX_GUARD(s2n_free_object((uint8_t **) (void **) fingerprint_ptr,
74 : 90 : sizeof(struct s2n_fingerprint)));
75 : 90 : return S2N_SUCCESS;
76 : 90 : }
77 : :
78 : : int s2n_fingerprint_wipe(struct s2n_fingerprint *fingerprint)
79 : 152 : {
80 [ + + ][ + - ]: 152 : POSIX_ENSURE(fingerprint, S2N_ERR_INVALID_ARGUMENT);
81 : 151 : fingerprint->client_hello = NULL;
82 : 151 : fingerprint->raw_size = 0;
83 : 151 : return S2N_SUCCESS;
84 : 152 : }
85 : :
86 : : int s2n_fingerprint_set_client_hello(struct s2n_fingerprint *fingerprint, struct s2n_client_hello *ch)
87 : 146 : {
88 [ + + ][ + - ]: 146 : POSIX_ENSURE(fingerprint, S2N_ERR_INVALID_ARGUMENT);
89 [ + + ][ + - ]: 145 : POSIX_ENSURE(ch, S2N_ERR_INVALID_ARGUMENT);
90 [ + - ][ + + ]: 142 : POSIX_ENSURE(!ch->sslv2, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
91 [ - + ]: 140 : POSIX_GUARD(s2n_fingerprint_wipe(fingerprint));
92 : 140 : fingerprint->client_hello = ch;
93 : 140 : return S2N_SUCCESS;
94 : 140 : }
95 : :
96 : : int s2n_fingerprint_get_hash_size(const struct s2n_fingerprint *fingerprint, uint32_t *size)
97 : 4 : {
98 [ + + ][ + - ]: 4 : POSIX_ENSURE(fingerprint, S2N_ERR_INVALID_ARGUMENT);
99 : 3 : const struct s2n_fingerprint_method *method = fingerprint->method;
100 [ # # ][ - + ]: 3 : POSIX_ENSURE_REF(method);
101 [ + - ][ + + ]: 3 : POSIX_ENSURE(size, S2N_ERR_INVALID_ARGUMENT);
102 : 2 : *size = method->hash_str_size;
103 : 2 : return S2N_SUCCESS;
104 : 3 : }
105 : :
106 : : int s2n_fingerprint_get_hash(struct s2n_fingerprint *fingerprint,
107 : : uint32_t max_output_size, uint8_t *output, uint32_t *output_size)
108 : 95 : {
109 [ + + ][ + - ]: 95 : POSIX_ENSURE(fingerprint, S2N_ERR_INVALID_ARGUMENT);
110 : 94 : const struct s2n_fingerprint_method *method = fingerprint->method;
111 [ # # ][ - + ]: 94 : POSIX_ENSURE_REF(method);
112 : :
113 [ + + ][ + - ]: 94 : POSIX_ENSURE(max_output_size >= method->hash_str_size, S2N_ERR_INSUFFICIENT_MEM_SIZE);
114 [ + - ][ + + ]: 92 : POSIX_ENSURE(output, S2N_ERR_INVALID_ARGUMENT);
115 [ + - ][ + + ]: 91 : POSIX_ENSURE(output_size, S2N_ERR_INVALID_ARGUMENT);
116 : 90 : *output_size = 0;
117 : :
118 : 90 : struct s2n_fingerprint_hash hash = {
119 : 90 : .hash = &fingerprint->hash,
120 : 90 : };
121 [ - + ]: 90 : POSIX_GUARD(s2n_hash_reset(&fingerprint->hash));
122 : :
123 : 90 : struct s2n_stuffer output_stuffer = { 0 };
124 [ - + ]: 90 : POSIX_GUARD(s2n_blob_init(&output_stuffer.blob, output, max_output_size));
125 : :
126 [ + + ][ + - ]: 90 : POSIX_ENSURE(fingerprint->client_hello, S2N_ERR_INVALID_STATE);
127 [ - + ]: 89 : POSIX_GUARD_RESULT(method->fingerprint(fingerprint, &hash, &output_stuffer));
128 : :
129 : 89 : *output_size = s2n_stuffer_data_available(&output_stuffer);
130 : 89 : return S2N_SUCCESS;
131 : 89 : }
132 : :
133 : : int s2n_fingerprint_get_raw_size(const struct s2n_fingerprint *fingerprint, uint32_t *size)
134 : 27 : {
135 [ + + ][ + - ]: 27 : POSIX_ENSURE(fingerprint, S2N_ERR_INVALID_ARGUMENT);
136 [ + + ][ + - ]: 26 : POSIX_ENSURE(size, S2N_ERR_INVALID_ARGUMENT);
137 : : /* A zero-length raw string is impossible for all fingerprinting methods
138 : : * currently supported, so raw_size == 0 indicates that raw_size has not been
139 : : * calculated yet.
140 : : */
141 [ + - ][ + + ]: 25 : POSIX_ENSURE(fingerprint->raw_size != 0, S2N_ERR_INVALID_STATE);
142 : 14 : *size = fingerprint->raw_size;
143 : 14 : return S2N_SUCCESS;
144 : 25 : }
145 : :
146 : : int s2n_fingerprint_get_raw(struct s2n_fingerprint *fingerprint,
147 : : uint32_t max_output_size, uint8_t *output, uint32_t *output_size)
148 : 68 : {
149 [ + - ][ + + ]: 68 : POSIX_ENSURE(fingerprint, S2N_ERR_INVALID_ARGUMENT);
150 : 67 : const struct s2n_fingerprint_method *method = fingerprint->method;
151 [ - + ][ # # ]: 67 : POSIX_ENSURE_REF(method);
152 : :
153 [ + + ][ + - ]: 67 : POSIX_ENSURE(max_output_size > 0, S2N_ERR_INSUFFICIENT_MEM_SIZE);
154 [ + - ][ + + ]: 64 : POSIX_ENSURE(output, S2N_ERR_INVALID_ARGUMENT);
155 [ + + ][ + - ]: 62 : POSIX_ENSURE(output_size, S2N_ERR_INVALID_ARGUMENT);
156 : 60 : *output_size = 0;
157 : :
158 : 60 : struct s2n_stuffer output_stuffer = { 0 };
159 [ - + ]: 60 : POSIX_GUARD(s2n_blob_init(&output_stuffer.blob, output, max_output_size));
160 : 60 : struct s2n_fingerprint_hash hash = {
161 : 60 : .buffer = &output_stuffer,
162 : 60 : };
163 : :
164 [ # # ][ - + ]: 60 : POSIX_ENSURE(fingerprint->client_hello, S2N_ERR_INVALID_STATE);
165 [ + + ]: 60 : POSIX_GUARD_RESULT(method->fingerprint(fingerprint, &hash, &output_stuffer));
166 : 49 : *output_size = s2n_stuffer_data_available(&output_stuffer);
167 : 49 : return S2N_SUCCESS;
168 : 60 : }
169 : :
170 : : /* See https://datatracker.ietf.org/doc/html/rfc8701
171 : : * for an explanation of GREASE and lists of the GREASE values.
172 : : */
173 : : static S2N_RESULT s2n_assert_grease_value(uint16_t val)
174 : 2916 : {
175 : 2916 : uint8_t byte1 = val >> 8;
176 : 2916 : uint8_t byte2 = val & 0x00FF;
177 : : /* Both bytes of the GREASE values are identical */
178 [ + - ][ + + ]: 2916 : RESULT_ENSURE_EQ(byte1, byte2);
179 : : /* The GREASE value bytes all follow the format 0x[0-F]A.
180 : : * So 0x0A, 0x1A, 0x2A etc, up to 0xFA. */
181 [ + + ][ + - ]: 728 : RESULT_ENSURE_EQ((byte1 | 0xF0), 0xFA);
182 : 33 : return S2N_RESULT_OK;
183 : 728 : }
184 : :
185 : : /**
186 : : *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#details
187 : : *# The program needs to ignore GREASE values anywhere it sees them
188 : : */
189 : : bool s2n_fingerprint_is_grease_value(uint16_t val)
190 : 2916 : {
191 : 2916 : return s2n_result_is_ok(s2n_assert_grease_value(val));
192 : 2916 : }
193 : :
194 : : S2N_RESULT s2n_fingerprint_parse_extension(struct s2n_stuffer *input, uint16_t *iana)
195 : 870 : {
196 : 870 : uint16_t size = 0;
197 [ - + ]: 870 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(input, iana));
198 [ - + ]: 870 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(input, &size));
199 [ - + ]: 870 : RESULT_GUARD_POSIX(s2n_stuffer_skip_read(input, size));
200 : 870 : return S2N_RESULT_OK;
201 : 870 : }
202 : :
203 : : S2N_RESULT s2n_fingerprint_get_legacy_version(struct s2n_client_hello *ch, uint16_t *version)
204 : 137 : {
205 [ # # ][ - + ]: 137 : RESULT_ENSURE_REF(ch);
206 [ # # ][ - + ]: 137 : RESULT_ENSURE_REF(version);
207 : 137 : uint8_t high_byte = (ch->legacy_version / 10);
208 : 137 : uint8_t low_byte = (ch->legacy_version % 10);
209 : 137 : *version = high_byte << 8 | low_byte;
210 : 137 : return S2N_RESULT_OK;
211 : 137 : }
212 : :
213 : : S2N_RESULT s2n_fingerprint_hash_add_char(struct s2n_fingerprint_hash *hash, char c)
214 : 1625 : {
215 [ + + ][ + - ]: 1625 : RESULT_ENSURE_REF(hash);
216 [ + + ]: 1624 : if (hash->hash) {
217 [ - + ]: 542 : RESULT_GUARD_POSIX(s2n_hash_update(hash->hash, &c, 1));
218 : 1082 : } else {
219 [ # # ][ - + ]: 1082 : RESULT_ENSURE_REF(hash->buffer);
220 [ + + ][ + - ]: 1082 : RESULT_ENSURE(s2n_stuffer_space_remaining(hash->buffer) >= 1,
221 : 1077 : S2N_ERR_INSUFFICIENT_MEM_SIZE);
222 [ - + ]: 1077 : RESULT_GUARD_POSIX(s2n_stuffer_write_char(hash->buffer, c));
223 : 1077 : }
224 : 1619 : return S2N_RESULT_OK;
225 : 1624 : }
226 : :
227 : : S2N_RESULT s2n_fingerprint_hash_add_str(struct s2n_fingerprint_hash *hash,
228 : : const char *str, size_t str_size)
229 : 1594 : {
230 : 1594 : return s2n_fingerprint_hash_add_bytes(hash, (const uint8_t *) str, str_size);
231 : 1594 : }
232 : :
233 : : S2N_RESULT s2n_fingerprint_hash_add_bytes(struct s2n_fingerprint_hash *hash,
234 : : const uint8_t *bytes, size_t size)
235 : 1723 : {
236 [ + + ][ + - ]: 1723 : RESULT_ENSURE_REF(hash);
237 [ + - ][ + + ]: 1722 : RESULT_ENSURE(S2N_MEM_IS_READABLE(bytes, size), S2N_ERR_NULL);
[ + + ]
238 [ + + ]: 1720 : if (hash->hash) {
239 [ - + ]: 628 : RESULT_GUARD_POSIX(s2n_hash_update(hash->hash, bytes, size));
240 : 1092 : } else {
241 [ - + ][ # # ]: 1092 : RESULT_ENSURE_REF(hash->buffer);
242 [ + + ][ + - ]: 1092 : RESULT_ENSURE(s2n_stuffer_space_remaining(hash->buffer) >= size,
243 : 1080 : S2N_ERR_INSUFFICIENT_MEM_SIZE);
244 [ - + ]: 1080 : RESULT_GUARD_POSIX(s2n_stuffer_write_text(hash->buffer, bytes, size));
245 : 1080 : }
246 : 1708 : return S2N_RESULT_OK;
247 : 1720 : }
248 : :
249 : : S2N_RESULT s2n_fingerprint_hash_digest(struct s2n_fingerprint_hash *hash, struct s2n_blob *out)
250 : 121 : {
251 [ + + ][ + - ]: 121 : RESULT_ENSURE_REF(hash);
252 [ + + ][ + - ]: 120 : RESULT_ENSURE_REF(hash->hash);
253 [ - + ][ # # ]: 119 : RESULT_ENSURE_REF(out);
254 : :
255 : 119 : uint64_t bytes = 0;
256 [ - + ]: 119 : RESULT_GUARD_POSIX(s2n_hash_get_currently_in_hash_total(hash->hash, &bytes));
257 : 119 : hash->bytes_digested += bytes;
258 : :
259 [ - + ]: 119 : RESULT_GUARD_POSIX(s2n_hash_digest(hash->hash, out->data, out->size));
260 [ - + ]: 119 : RESULT_GUARD_POSIX(s2n_hash_reset(hash->hash));
261 : 119 : return S2N_RESULT_OK;
262 : 119 : }
263 : :
264 : : bool s2n_fingerprint_hash_do_digest(struct s2n_fingerprint_hash *hash)
265 : 351 : {
266 [ + + ][ + + ]: 351 : return hash && hash->hash;
267 : 351 : }
268 : :
269 : : int s2n_client_hello_get_fingerprint_hash(struct s2n_client_hello *ch, s2n_fingerprint_type type,
270 : : uint32_t max_output_size, uint8_t *output, uint32_t *output_size, uint32_t *str_size)
271 : 33 : {
272 [ + + ][ + - ]: 33 : POSIX_ENSURE(type == S2N_FINGERPRINT_JA3, S2N_ERR_INVALID_ARGUMENT);
273 [ + + ][ + - ]: 32 : POSIX_ENSURE(max_output_size >= MD5_DIGEST_LENGTH, S2N_ERR_INSUFFICIENT_MEM_SIZE);
274 [ + - ][ + + ]: 15 : POSIX_ENSURE(str_size, S2N_ERR_INVALID_ARGUMENT);
275 [ + + ][ + - ]: 14 : POSIX_ENSURE(output_size, S2N_ERR_INVALID_ARGUMENT);
276 [ + + ][ + - ]: 13 : POSIX_ENSURE(output, S2N_ERR_INVALID_ARGUMENT);
277 : :
278 : 12 : DEFER_CLEANUP(struct s2n_fingerprint fingerprint = { 0 }, s2n_fingerprint_free_fields);
279 [ - + ]: 12 : POSIX_GUARD_RESULT(s2n_fingerprint_init(&fingerprint, type));
280 [ + + ]: 12 : POSIX_GUARD(s2n_fingerprint_set_client_hello(&fingerprint, ch));
281 : :
282 : 11 : uint32_t hex_hash_size = 0;
283 : 11 : uint8_t hex_hash[S2N_JA3_HASH_STR_SIZE] = { 0 };
284 [ - + ]: 11 : POSIX_GUARD(s2n_fingerprint_get_hash(&fingerprint, sizeof(hex_hash), hex_hash, &hex_hash_size));
285 : :
286 : : /* s2n_client_hello_get_fingerprint_hash expects the raw bytes of the JA3 hash,
287 : : * but s2n_fingerprint_get_hash returns a hex string instead.
288 : : * We need to translate back to the raw bytes.
289 : : */
290 : 11 : struct s2n_blob bytes_out = { 0 };
291 [ - + ]: 11 : POSIX_GUARD(s2n_blob_init(&bytes_out, output, MD5_DIGEST_LENGTH));
292 : 11 : struct s2n_stuffer hex_in = { 0 };
293 [ - + ]: 11 : POSIX_GUARD(s2n_blob_init(&hex_in.blob, hex_hash, hex_hash_size));
294 [ - + ]: 11 : POSIX_GUARD(s2n_stuffer_skip_write(&hex_in, hex_hash_size));
295 [ - + ]: 11 : POSIX_GUARD_RESULT(s2n_stuffer_read_hex(&hex_in, &bytes_out));
296 : 11 : *output_size = bytes_out.size;
297 : :
298 [ - + ]: 11 : POSIX_GUARD(s2n_fingerprint_get_raw_size(&fingerprint, str_size));
299 : 11 : return S2N_SUCCESS;
300 : 11 : }
301 : :
302 : : int s2n_client_hello_get_fingerprint_string(struct s2n_client_hello *ch, s2n_fingerprint_type type,
303 : : uint32_t max_output_size, uint8_t *output, uint32_t *output_size)
304 : 42 : {
305 [ + - ][ + + ]: 42 : POSIX_ENSURE(type == S2N_FINGERPRINT_JA3, S2N_ERR_INVALID_ARGUMENT);
306 : 41 : DEFER_CLEANUP(struct s2n_fingerprint fingerprint = { 0 },
307 : 41 : s2n_fingerprint_free_fields);
308 [ - + ]: 41 : POSIX_GUARD_RESULT(s2n_fingerprint_init(&fingerprint, type));
309 [ + + ]: 41 : POSIX_GUARD(s2n_fingerprint_set_client_hello(&fingerprint, ch));
310 [ + + ]: 39 : POSIX_GUARD(s2n_fingerprint_get_raw(&fingerprint, max_output_size, output, output_size));
311 : 25 : return S2N_SUCCESS;
312 : 39 : }
|