Skip to content

Commit

Permalink
PR comments: write directly to hash
Browse files Browse the repository at this point in the history
  • Loading branch information
lrstewart committed Jun 9, 2024
1 parent 1bd7190 commit e7b3737
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 119 deletions.
123 changes: 48 additions & 75 deletions tests/unit/s2n_fingerprint_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,15 @@
#include "testlib/s2n_testlib.h"

#define S2N_TEST_HASH S2N_HASH_SHA256
#define TEST_COUNT 10

static S2N_RESULT s2n_test_fingerprint_hash(struct s2n_fingerprint_hash *hash,
struct s2n_stuffer *hash_output, struct s2n_hash_state *hash_state,
size_t hash_buffer_size)
static S2N_RESULT s2n_test_hash_new(struct s2n_fingerprint_hash *hash,
struct s2n_hash_state *hash_state)
{
hash->buffer = hash_output;
hash->hash = hash_state;
hash->do_digest = true;
EXPECT_SUCCESS(s2n_hash_new(hash_state));
EXPECT_SUCCESS(s2n_hash_init(hash_state, S2N_TEST_HASH));
EXPECT_SUCCESS(s2n_stuffer_alloc(hash_output, hash_buffer_size));
return S2N_RESULT_OK;
}

Expand All @@ -49,19 +47,32 @@ int main(int argc, char **argv)
EXPECT_ERROR_WITH_ERRNO(s2n_fingerprint_hash_add_char(NULL, test_char),
S2N_ERR_NULL);

/* Successfully added */
/* Add to stuffer */
{
DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free);
EXPECT_SUCCESS(s2n_stuffer_alloc(&output, 1));
EXPECT_SUCCESS(s2n_stuffer_alloc(&output, TEST_COUNT));
struct s2n_fingerprint_hash hash = { .buffer = &output };

EXPECT_OK(s2n_fingerprint_hash_add_char(&hash, test_char));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), 1);
EXPECT_EQUAL(s2n_stuffer_space_remaining(&output), 0);
for (size_t i = 1; i <= TEST_COUNT; i++) {
EXPECT_OK(s2n_fingerprint_hash_add_char(&hash, test_char));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), 1);

char actual_value = 0;
EXPECT_SUCCESS(s2n_stuffer_read_char(&output, &actual_value));
EXPECT_EQUAL(actual_value, test_char);
char actual_value = 0;
EXPECT_SUCCESS(s2n_stuffer_read_char(&output, &actual_value));
EXPECT_EQUAL(actual_value, test_char);
}
}

/* Add to hash */
{
DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free);
struct s2n_fingerprint_hash hash = { 0 };
EXPECT_OK(s2n_test_hash_new(&hash, &hash_state));

for (size_t i = 1; i <= TEST_COUNT; i++) {
EXPECT_OK(s2n_fingerprint_hash_add_char(&hash, test_char));
EXPECT_EQUAL(hash.hash->currently_in_hash, i);
}
}

/* Error due to insufficient space */
Expand All @@ -76,28 +87,6 @@ int main(int argc, char **argv)
EXPECT_ERROR_WITH_ERRNO(s2n_fingerprint_hash_add_char(&hash, test_char),
S2N_ERR_INSUFFICIENT_MEM_SIZE);
}

/* Flushed due to insufficient space */
{
DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free);
DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free);
struct s2n_fingerprint_hash hash = { 0 };
EXPECT_OK(s2n_test_fingerprint_hash(&hash, &output, &hash_state, 1));

EXPECT_OK(s2n_fingerprint_hash_add_char(&hash, test_str[0]));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), 1);
EXPECT_EQUAL(s2n_stuffer_space_remaining(&output), 0);
EXPECT_EQUAL(hash.hash->currently_in_hash, 0);

EXPECT_OK(s2n_fingerprint_hash_add_char(&hash, test_char));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), 1);
EXPECT_EQUAL(s2n_stuffer_space_remaining(&output), 0);
EXPECT_EQUAL(hash.hash->currently_in_hash, 1);

char actual_value = 0;
EXPECT_SUCCESS(s2n_stuffer_read_char(&output, &actual_value));
EXPECT_EQUAL(actual_value, test_char);
}
}

/* Test s2n_fingerprint_hash_add_str */
Expand All @@ -106,19 +95,32 @@ int main(int argc, char **argv)
EXPECT_ERROR_WITH_ERRNO(s2n_fingerprint_hash_add_str(NULL, test_str),
S2N_ERR_NULL);

/* Successfully added */
/* Add to stuffer */
{
DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free);
EXPECT_SUCCESS(s2n_stuffer_alloc(&output, test_str_len));
EXPECT_SUCCESS(s2n_stuffer_alloc(&output, test_str_len * TEST_COUNT));
struct s2n_fingerprint_hash hash = { .buffer = &output };

EXPECT_OK(s2n_fingerprint_hash_add_str(&hash, test_str));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), test_str_len);
EXPECT_EQUAL(s2n_stuffer_space_remaining(&output), 0);
for (size_t i = 1; i <= TEST_COUNT; i++) {
EXPECT_OK(s2n_fingerprint_hash_add_str(&hash, test_str));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), test_str_len);

uint8_t actual_value[sizeof(test_str)] = { 0 };
EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, actual_value, test_str_len));
EXPECT_BYTEARRAY_EQUAL(actual_value, test_str, test_str_len);
}
}

/* Add to hash */
{
DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free);
struct s2n_fingerprint_hash hash = { 0 };
EXPECT_OK(s2n_test_hash_new(&hash, &hash_state));

uint8_t actual_value[sizeof(test_str)] = { 0 };
EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, actual_value, test_str_len));
EXPECT_BYTEARRAY_EQUAL(actual_value, test_str, test_str_len);
for (size_t i = 1; i <= TEST_COUNT; i++) {
EXPECT_OK(s2n_fingerprint_hash_add_str(&hash, test_str));
EXPECT_EQUAL(hash.hash->currently_in_hash, test_str_len * i);
}
}

/* Error due to insufficient space */
Expand All @@ -144,28 +146,6 @@ int main(int argc, char **argv)
S2N_ERR_INSUFFICIENT_MEM_SIZE);
EXPECT_SUCCESS(s2n_stuffer_free(&output));
}

/* Flushed due to insufficient space */
{
DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free);
DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free);
struct s2n_fingerprint_hash hash = { 0 };
EXPECT_OK(s2n_test_fingerprint_hash(&hash, &output, &hash_state, test_str_len));

EXPECT_OK(s2n_fingerprint_hash_add_str(&hash, test_str));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), test_str_len);
EXPECT_EQUAL(s2n_stuffer_space_remaining(&output), 0);
EXPECT_EQUAL(hash.hash->currently_in_hash, 0);

EXPECT_OK(s2n_fingerprint_hash_add_char(&hash, test_char));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), 1);
EXPECT_EQUAL(s2n_stuffer_space_remaining(&output), test_str_len - 1);
EXPECT_EQUAL(hash.hash->currently_in_hash, test_str_len);

char actual_value = 0;
EXPECT_SUCCESS(s2n_stuffer_read_char(&output, &actual_value));
EXPECT_EQUAL(actual_value, test_char);
}
}

/* Test s2n_fingerprint_hash_digest */
Expand All @@ -187,31 +167,24 @@ int main(int argc, char **argv)

/* Digest successfully calculated */
{
DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free);
DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free);
struct s2n_fingerprint_hash hash = { 0 };
EXPECT_OK(s2n_test_fingerprint_hash(&hash, &output, &hash_state, test_str_len));
EXPECT_OK(s2n_test_hash_new(&hash, &hash_state));

EXPECT_OK(s2n_fingerprint_hash_add_str(&hash, test_value));
EXPECT_EQUAL(s2n_stuffer_data_available(&output), test_str_len);
EXPECT_EQUAL(s2n_stuffer_space_remaining(&output), 0);
EXPECT_EQUAL(hash.hash->currently_in_hash, 0);
EXPECT_EQUAL(hash.hash->currently_in_hash, test_str_len);

uint8_t actual_digest[sizeof(digest_value)] = { 0 };
EXPECT_OK(s2n_fingerprint_hash_digest(&hash, actual_digest, sizeof(actual_digest)));
EXPECT_BYTEARRAY_EQUAL(actual_digest, digest_value, sizeof(digest_value));

EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0);
EXPECT_EQUAL(s2n_stuffer_space_remaining(&output), test_str_len);
EXPECT_EQUAL(hash.bytes_digested, test_str_len);
}

/* Hash can be reused after digest */
{
DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free);
DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free);
struct s2n_fingerprint_hash hash = { 0 };
EXPECT_OK(s2n_test_fingerprint_hash(&hash, &output, &hash_state, test_str_len));
EXPECT_OK(s2n_test_hash_new(&hash, &hash_state));

const size_t count = 10;
for (size_t i = 0; i < count; i++) {
Expand Down
59 changes: 15 additions & 44 deletions tls/s2n_fingerprint.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,47 +39,34 @@ bool s2n_is_grease_value(uint16_t val)
return s2n_result_is_ok(s2n_assert_grease_value(val));
}

static S2N_RESULT s2n_fingerprint_hash_flush(struct s2n_fingerprint_hash *hash)
{
RESULT_ENSURE_REF(hash);

uint32_t hash_data_len = s2n_stuffer_data_available(hash->buffer);
uint8_t *hash_data = s2n_stuffer_raw_read(hash->buffer, hash_data_len);
RESULT_ENSURE_REF(hash_data);

RESULT_GUARD_POSIX(s2n_hash_update(hash->hash, hash_data, hash_data_len));
RESULT_GUARD_POSIX(s2n_stuffer_wipe(hash->buffer));
return S2N_RESULT_OK;
}

static S2N_RESULT s2n_fingerprint_hash_reserve_space(struct s2n_fingerprint_hash *hash, size_t size)
S2N_RESULT s2n_fingerprint_hash_add_char(struct s2n_fingerprint_hash *hash, char c)
{
RESULT_ENSURE_REF(hash);
if (s2n_stuffer_space_remaining(hash->buffer) < size) {
RESULT_ENSURE(hash->hash, S2N_ERR_INSUFFICIENT_MEM_SIZE);
RESULT_GUARD(s2n_fingerprint_hash_flush(hash));
if (hash->hash) {
RESULT_GUARD_POSIX(s2n_hash_update(hash->hash, &c, 1));
} else {
RESULT_ENSURE(s2n_stuffer_space_remaining(hash->buffer) >= 1,
S2N_ERR_INSUFFICIENT_MEM_SIZE);
RESULT_GUARD_POSIX(s2n_stuffer_write_char(hash->buffer, c));
}
return S2N_RESULT_OK;
}

S2N_RESULT s2n_fingerprint_hash_add_char(struct s2n_fingerprint_hash *hash, char c)
{
RESULT_GUARD(s2n_fingerprint_hash_reserve_space(hash, 1));
RESULT_GUARD_POSIX(s2n_stuffer_write_char(hash->buffer, c));
return S2N_RESULT_OK;
}

S2N_RESULT s2n_fingerprint_hash_add_str(struct s2n_fingerprint_hash *hash, const char *str)
{
RESULT_GUARD(s2n_fingerprint_hash_reserve_space(hash, strlen(str)));
RESULT_GUARD_POSIX(s2n_stuffer_write_str(hash->buffer, str));
RESULT_ENSURE_REF(hash);
if (hash->hash) {
RESULT_GUARD_POSIX(s2n_hash_update(hash->hash, str, strlen(str)));
} else {
RESULT_ENSURE(s2n_stuffer_space_remaining(hash->buffer) >= strlen(str),
S2N_ERR_INSUFFICIENT_MEM_SIZE);
RESULT_GUARD_POSIX(s2n_stuffer_write_str(hash->buffer, str));
}
return S2N_RESULT_OK;
}

S2N_RESULT s2n_fingerprint_hash_digest(struct s2n_fingerprint_hash *hash, uint8_t *out, size_t out_size)
{
RESULT_GUARD(s2n_fingerprint_hash_flush(hash));

RESULT_ENSURE_REF(hash);
RESULT_ENSURE_REF(hash->hash);
hash->bytes_digested += hash->hash->currently_in_hash;
Expand Down Expand Up @@ -110,28 +97,12 @@ int s2n_client_hello_get_fingerprint_hash(struct s2n_client_hello *ch, s2n_finge
struct s2n_stuffer output_stuffer = { 0 };
POSIX_GUARD(s2n_blob_init(&output_stuffer.blob, output, max_output_size));

/* The maximum size of the hash input string is variable and could theoretically
* be extremely large. However, we don't need enough memory to hold the full string
* when calculating a hash. We can calculate and add the string to the hash in chunks,
* similarly to how the TLS transcript hash is calculated by adding handshake
* messages to the hash as they become available. After a chunk is added to the hash,
* the buffer can be wiped and reused for the next chunk.
*
* This ensures that our calculation requires a constant amount of memory.
*
* The size of the buffer is chosen to be the block size of most hashes.
*/
uint8_t hash_mem[64] = { 0 };
struct s2n_stuffer hash_stuffer = { 0 };
POSIX_GUARD(s2n_blob_init(&hash_stuffer.blob, hash_mem, sizeof(hash_mem)));

DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free);
POSIX_GUARD(s2n_hash_new(&hash_state));
s2n_hash_allow_md5_for_fips(&hash_state);
POSIX_GUARD(s2n_hash_init(&hash_state, method->hash));

struct s2n_fingerprint_hash hash = {
.buffer = &hash_stuffer,
.hash = &hash_state,
.do_digest = true,
};
Expand Down

0 comments on commit e7b3737

Please sign in to comment.