LCOV - code coverage report
Current view: top level - tls - s2n_fingerprint.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 209 209 100.0 %
Date: 2025-08-15 07:28:39 Functions: 21 21 100.0 %
Branches: 173 274 63.1 %

           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 : }

Generated by: LCOV version 1.14