/* * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You may not use * this file except in compliance with the License. A copy of the License is * located at * * http://aws.amazon.com/apache2.0/ * * or in the "license" file accompanying this file. This file is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "testing.h" /* * Warning: this file uses aws_hash_table_init instead of aws_cryptosdk_enc_ctx_init * in many places because it does a few tricky things, including disabling some of the * destructors to avoid double frees and setting up a very large table to avoid * reallocations. */ AWS_STATIC_STRING_FROM_LITERAL(empty, ""); AWS_STATIC_STRING_FROM_LITERAL(foo, "foo"); AWS_STATIC_STRING_FROM_LITERAL(bar, "bar"); AWS_STATIC_STRING_FROM_LITERAL(foobar, "foobar"); AWS_STATIC_STRING_FROM_LITERAL(foobaz, "foobaz"); AWS_STATIC_STRING_FROM_LITERAL(bar_food, "bar food"); AWS_STATIC_STRING_FROM_LITERAL(bar_null_food, "bar\0food"); AWS_STATIC_STRING_FROM_LITERAL(bar_null_back, "bar\0back"); static int serialize_init(struct aws_allocator *alloc, struct aws_byte_buf *buf, const struct aws_hash_table *enc_ctx) { size_t len; if (aws_cryptosdk_enc_ctx_size(&len, enc_ctx)) return AWS_OP_ERR; if (aws_byte_buf_init(buf, alloc, len)) return AWS_OP_ERR; return aws_cryptosdk_enc_ctx_serialize(alloc, buf, enc_ctx); } int get_sorted_elems_array_test() { const struct aws_string *keys[] = { foo, bar, foobar, empty, bar_null_food }; const struct aws_string *vals[] = { bar, foo, foobaz, bar_food, bar_null_back }; int num_elems = sizeof(keys) / sizeof(const struct aws_string *); const struct aws_string *sorted_keys[] = { empty, bar, bar_null_food, foo, foobar }; const struct aws_string *sorted_vals[] = { bar_food, foo, bar_null_back, bar, foobaz }; struct aws_allocator *alloc = aws_default_allocator(); struct aws_hash_table map; TEST_ASSERT_INT_EQ( aws_hash_table_init(&map, alloc, 10, aws_hash_string, aws_hash_callback_string_eq, NULL, NULL), AWS_OP_SUCCESS); for (int idx = 0; idx < num_elems; ++idx) { struct aws_hash_element *elem; TEST_ASSERT_INT_EQ(aws_hash_table_create(&map, (void *)keys[idx], &elem, NULL), AWS_OP_SUCCESS); elem->value = (void *)vals[idx]; } struct aws_array_list elems; TEST_ASSERT_INT_EQ(aws_cryptosdk_hash_elems_array_init(alloc, &elems, &map), AWS_OP_SUCCESS); aws_array_list_sort(&elems, aws_cryptosdk_compare_hash_elems_by_key_string); TEST_ASSERT_INT_EQ(elems.length, num_elems); for (int idx = 0; idx < num_elems; ++idx) { struct aws_hash_element elem; TEST_ASSERT_INT_EQ(aws_array_list_get_at(&elems, (void *)&elem, idx), AWS_OP_SUCCESS); TEST_ASSERT(aws_string_eq((const struct aws_string *)elem.key, sorted_keys[idx])); TEST_ASSERT(aws_string_eq((const struct aws_string *)elem.value, sorted_vals[idx])); } aws_array_list_clean_up(&elems); aws_cryptosdk_enc_ctx_clean_up(&map); return 0; } int serialize_empty_enc_ctx() { struct aws_allocator *alloc = aws_default_allocator(); struct aws_hash_table enc_ctx; TEST_ASSERT_INT_EQ( aws_hash_table_init(&enc_ctx, alloc, 10, aws_hash_string, aws_hash_callback_string_eq, NULL, NULL), AWS_OP_SUCCESS); struct aws_byte_buf output = { 0 }; size_t len; TEST_ASSERT_SUCCESS(aws_cryptosdk_enc_ctx_size(&len, &enc_ctx)); TEST_ASSERT_INT_EQ(len, 0); TEST_ASSERT_SUCCESS(aws_cryptosdk_enc_ctx_serialize(alloc, &output, &enc_ctx)); TEST_ASSERT_INT_EQ(output.len, 0); aws_byte_buf_clean_up(&output); aws_cryptosdk_enc_ctx_clean_up(&enc_ctx); return 0; } int serialize_valid_enc_ctx() { const uint8_t serialized_ctx[] = "\x00\x04" "\x00\x15" "aws-crypto-public-key\x00\x44" "AmZvwV/dN6o9p/usAnJdRcdnE12UbaDHuEFPeyVkw5FC1ULGlSznzDdD3FP8SW1UMg==" "\x00\x05key_a\x00\x07value_a" "\x00\x05key_b\x00\x07value_b" "\x00\x05key_c\x00\x07value_c"; AWS_STATIC_STRING_FROM_LITERAL(key_a, "key_a"); AWS_STATIC_STRING_FROM_LITERAL(value_a, "value_a"); AWS_STATIC_STRING_FROM_LITERAL(key_b, "key_b"); AWS_STATIC_STRING_FROM_LITERAL(value_b, "value_b"); AWS_STATIC_STRING_FROM_LITERAL(key_c, "key_c"); AWS_STATIC_STRING_FROM_LITERAL(value_c, "value_c"); AWS_STATIC_STRING_FROM_LITERAL(aws_crypto_public_key, "aws-crypto-public-key"); AWS_STATIC_STRING_FROM_LITERAL( public_key_val, "AmZvwV/dN6o9p/usAnJdRcdnE12UbaDHuEFPeyVkw5FC1ULGlSznzDdD3FP8SW1UMg=="); const struct aws_string *keys[] = { key_b, key_a, aws_crypto_public_key, key_c }; const struct aws_string *vals[] = { value_b, value_a, public_key_val, value_c }; int num_elems = sizeof(keys) / sizeof(const struct aws_string *); struct aws_allocator *alloc = aws_default_allocator(); struct aws_hash_table enc_ctx; TEST_ASSERT_INT_EQ( aws_hash_table_init(&enc_ctx, alloc, 10, aws_hash_string, aws_hash_callback_string_eq, NULL, NULL), AWS_OP_SUCCESS); for (int idx = 0; idx < num_elems; ++idx) { struct aws_hash_element *elem; TEST_ASSERT_INT_EQ(aws_hash_table_create(&enc_ctx, (void *)keys[idx], &elem, NULL), AWS_OP_SUCCESS); elem->value = (void *)vals[idx]; } struct aws_byte_buf output; TEST_ASSERT_INT_EQ(serialize_init(alloc, &output, &enc_ctx), AWS_OP_SUCCESS); TEST_ASSERT_INT_EQ(output.len, sizeof(serialized_ctx) - 1); TEST_ASSERT_INT_EQ(0, memcmp(output.buffer, serialized_ctx, output.len)); aws_byte_buf_clean_up(&output); aws_cryptosdk_enc_ctx_clean_up(&enc_ctx); return 0; } int serialize_valid_enc_ctx_unsigned_comparison() { const uint8_t serialized_ctx[] = "\x00\x02" "\x00\x09" "aaaaaaaa\x7f" "\x00\x08" "BBBBBBBB" "\x00\x09" "aaaaaaaa\x80" "\x00\x08" "AAAAAAAA"; AWS_STATIC_STRING_FROM_LITERAL(key_a, "aaaaaaaa\x80"); AWS_STATIC_STRING_FROM_LITERAL(value_a, "AAAAAAAA"); AWS_STATIC_STRING_FROM_LITERAL(key_b, "aaaaaaaa\x7f"); AWS_STATIC_STRING_FROM_LITERAL(value_b, "BBBBBBBB"); const struct aws_string *keys[] = { key_a, key_b }; const struct aws_string *vals[] = { value_a, value_b }; int num_elems = sizeof(keys) / sizeof(const struct aws_string *); struct aws_allocator *alloc = aws_default_allocator(); struct aws_hash_table enc_ctx; TEST_ASSERT_INT_EQ( aws_hash_table_init(&enc_ctx, alloc, 10, aws_hash_string, aws_hash_callback_string_eq, NULL, NULL), AWS_OP_SUCCESS); for (int idx = 0; idx < num_elems; ++idx) { struct aws_hash_element *elem; TEST_ASSERT_INT_EQ(aws_hash_table_create(&enc_ctx, (void *)keys[idx], &elem, NULL), AWS_OP_SUCCESS); elem->value = (void *)vals[idx]; } struct aws_byte_buf output; TEST_ASSERT_INT_EQ(serialize_init(alloc, &output, &enc_ctx), AWS_OP_SUCCESS); TEST_ASSERT_INT_EQ(output.len, sizeof(serialized_ctx) - 1); TEST_ASSERT_INT_EQ(0, memcmp(output.buffer, serialized_ctx, output.len)); aws_byte_buf_clean_up(&output); aws_cryptosdk_enc_ctx_clean_up(&enc_ctx); return 0; } int serialize_error_when_element_too_long() { struct aws_allocator *alloc = aws_default_allocator(); uint8_t bytes[UINT16_MAX + 1] = { 0 }; const struct aws_string *str = aws_string_new_from_array(alloc, bytes, UINT16_MAX + 1); TEST_ASSERT_ADDR_NOT_NULL(str); struct aws_hash_table enc_ctx; TEST_ASSERT_INT_EQ( aws_hash_table_init( &enc_ctx, alloc, 10, aws_hash_string, aws_hash_callback_string_eq, NULL, aws_hash_callback_string_destroy), AWS_OP_SUCCESS); struct aws_hash_element *elem; TEST_ASSERT_INT_EQ(aws_hash_table_create(&enc_ctx, (void *)empty, &elem, NULL), AWS_OP_SUCCESS); elem->value = (void *)str; struct aws_byte_buf output; TEST_ASSERT_ERROR(AWS_CRYPTOSDK_ERR_LIMIT_EXCEEDED, serialize_init(alloc, &output, &enc_ctx)); aws_cryptosdk_enc_ctx_clean_up(&enc_ctx); return 0; } int serialize_error_when_serialized_len_too_long() { struct aws_allocator *alloc = aws_default_allocator(); #define TWO_TO_THE_FIFTEENTH (1 << 15) uint8_t bytes[TWO_TO_THE_FIFTEENTH] = { 0 }; const struct aws_string *str = aws_string_new_from_array(alloc, bytes, TWO_TO_THE_FIFTEENTH); TEST_ASSERT_ADDR_NOT_NULL(str); struct aws_hash_table enc_ctx; // only setting destroy function on value so it doesn't try to destroy same string twice TEST_ASSERT_INT_EQ( aws_hash_table_init( &enc_ctx, alloc, 10, aws_hash_string, aws_hash_callback_string_eq, NULL, aws_hash_callback_string_destroy), AWS_OP_SUCCESS); struct aws_hash_element *elem; TEST_ASSERT_INT_EQ(aws_hash_table_create(&enc_ctx, (void *)str, &elem, NULL), AWS_OP_SUCCESS); elem->value = (void *)str; struct aws_byte_buf output; TEST_ASSERT_ERROR(AWS_CRYPTOSDK_ERR_LIMIT_EXCEEDED, serialize_init(alloc, &output, &enc_ctx)); aws_cryptosdk_enc_ctx_clean_up(&enc_ctx); return 0; } int serialize_valid_enc_ctx_max_length() { struct aws_allocator *alloc = aws_default_allocator(); struct aws_hash_table enc_ctx; TEST_ASSERT_SUCCESS(aws_cryptosdk_enc_ctx_init(alloc, &enc_ctx)); /* 2 bytes: key-value count 2 bytes: key length (=UINT16_MAX - 6) UINT16_MAX - 6 bytes: key field 2 bytes: value length (=0) 0 bytes: value field (empty string) */ #define LONG_ARR_LEN (UINT16_MAX - 6) uint8_t arr[LONG_ARR_LEN] = { 0 }; const struct aws_string *key = aws_string_new_from_array(alloc, arr, LONG_ARR_LEN); TEST_ASSERT_ADDR_NOT_NULL(key); int was_created = 0; struct aws_hash_element *elem; TEST_ASSERT_INT_EQ(aws_hash_table_create(&enc_ctx, (void *)key, &elem, &was_created), AWS_OP_SUCCESS); TEST_ASSERT_INT_EQ(was_created, 1); elem->value = (void *)empty; struct aws_byte_buf output; TEST_ASSERT_INT_EQ(serialize_init(alloc, &output, &enc_ctx), AWS_OP_SUCCESS); TEST_ASSERT_INT_EQ(output.len, UINT16_MAX); aws_byte_buf_clean_up(&output); aws_cryptosdk_enc_ctx_clean_up(&enc_ctx); return 0; } int serialize_error_when_too_many_elements() { struct aws_allocator *alloc = aws_default_allocator(); struct aws_hash_table enc_ctx; TEST_ASSERT_INT_EQ( aws_hash_table_init( &enc_ctx, alloc, (size_t)UINT16_MAX + 10, aws_hash_string, aws_hash_callback_string_eq, aws_hash_callback_string_destroy, NULL), AWS_OP_SUCCESS); char buf[6] = { 0 }; for (size_t idx = 0; idx < (1 << 16); ++idx) { int was_created = 0; struct aws_hash_element *elem; snprintf(buf, sizeof(buf), "%zu", idx); const struct aws_string *str = aws_string_new_from_c_str(alloc, buf); TEST_ASSERT_INT_EQ(aws_hash_table_create(&enc_ctx, (void *)str, &elem, &was_created), AWS_OP_SUCCESS); TEST_ASSERT_INT_EQ(was_created, 1); elem->value = (void *)str; } struct aws_byte_buf output; TEST_ASSERT_ERROR(AWS_CRYPTOSDK_ERR_LIMIT_EXCEEDED, serialize_init(alloc, &output, &enc_ctx)); aws_cryptosdk_enc_ctx_clean_up(&enc_ctx); return 0; } static void *checked_aws_str_dup(struct aws_allocator *alloc, const char *str) { void *val = aws_string_new_from_c_str(alloc, str); if (!val) { abort(); } return val; } int enc_ctx_clone_test() { struct aws_allocator *alloc = aws_default_allocator(); struct aws_hash_table context_src, context_dst; TEST_ASSERT_SUCCESS(aws_cryptosdk_enc_ctx_init(alloc, &context_src)); TEST_ASSERT_SUCCESS(aws_cryptosdk_enc_ctx_init(alloc, &context_dst)); AWS_STATIC_STRING_FROM_LITERAL(static_val, "static value"); AWS_STATIC_STRING_FROM_LITERAL(static_key, "static key"); TEST_ASSERT_SUCCESS( aws_hash_table_put(&context_src, static_key, checked_aws_str_dup(alloc, "value for static key"), NULL)); TEST_ASSERT_SUCCESS( aws_hash_table_put(&context_src, checked_aws_str_dup(alloc, "key for static value"), (void *)static_val, NULL)); TEST_ASSERT_SUCCESS(aws_hash_table_put( &context_src, checked_aws_str_dup(alloc, "already present key"), checked_aws_str_dup(alloc, "already matching value"), NULL)); struct aws_string *already_present_key, *already_matching_value; TEST_ASSERT_SUCCESS(aws_hash_table_put( &context_dst, already_present_key = checked_aws_str_dup(alloc, "already present key"), already_matching_value = checked_aws_str_dup(alloc, "already matching value"), NULL)); TEST_ASSERT_SUCCESS(aws_hash_table_put( &context_src, checked_aws_str_dup(alloc, "value differs"), checked_aws_str_dup(alloc, "value1"), NULL)); struct aws_string *value_differs_key; TEST_ASSERT_SUCCESS(aws_hash_table_put( &context_dst, value_differs_key = checked_aws_str_dup(alloc, "value differs"), checked_aws_str_dup(alloc, "value2"), NULL)); TEST_ASSERT_SUCCESS(aws_hash_table_put( &context_dst, checked_aws_str_dup(alloc, "excess key"), checked_aws_str_dup(alloc, "excess value"), NULL)); TEST_ASSERT_SUCCESS(aws_cryptosdk_enc_ctx_clone(alloc, &context_dst, &context_src)); TEST_ASSERT(aws_hash_table_eq(&context_src, &context_dst, aws_hash_callback_string_eq)); /* Verify that things were/weren't allocated as appropriate */ struct aws_hash_element *src_element, *dst_element; #define LOOKUP(key) \ do { \ AWS_STATIC_STRING_FROM_LITERAL(lookup_key, key); \ TEST_ASSERT_SUCCESS(aws_hash_table_find(&context_src, lookup_key, &src_element)); \ TEST_ASSERT_ADDR_NOT_NULL(src_element); \ TEST_ASSERT_SUCCESS(aws_hash_table_find(&context_dst, lookup_key, &dst_element)); \ TEST_ASSERT_ADDR_NOT_NULL(dst_element); \ } while (0) LOOKUP("static key"); TEST_ASSERT_ADDR_EQ(src_element->key, dst_element->key); TEST_ASSERT_ADDR_NE(src_element->value, dst_element->value); LOOKUP("key for static value"); TEST_ASSERT_ADDR_NE(src_element->key, dst_element->key); TEST_ASSERT_ADDR_EQ(src_element->value, dst_element->value); LOOKUP("already present key"); TEST_ASSERT_ADDR_NE(src_element->key, dst_element->key); TEST_ASSERT_ADDR_NE(src_element->value, dst_element->value); TEST_ASSERT_ADDR_EQ(already_present_key, dst_element->key); TEST_ASSERT_ADDR_EQ(already_matching_value, dst_element->value); LOOKUP("value differs"); TEST_ASSERT_ADDR_NE(src_element->key, dst_element->key); TEST_ASSERT_ADDR_NE(src_element->value, dst_element->value); TEST_ASSERT_ADDR_EQ(value_differs_key, dst_element->key); aws_hash_table_clean_up(&context_dst); aws_hash_table_clean_up(&context_src); return 0; } int deserialize_error_when_duplicate_key_in_context() { uint8_t serialized_enc_ctx[] = { 0x00, 0x04, 0x00, 0x0b, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0c, 0xc2, 0xbd, 0x20, 0x2b, 0x20, 0xc2, 0xbc, 0x20, 0x3d, 0x20, 0xc2, 0xbe, 0x00, 0x0b, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0c, 0xc2, 0xbd, 0x20, 0x2b, 0x20, 0xc2, 0xbc, 0x20, 0x3d, 0x20, 0xc2, 0xbe, 0x00, 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x00, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x00, 0x04, 0x73, 0x6f, 0x6d, 0x65, 0x00, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63 }; struct aws_allocator *alloc = aws_default_allocator(); struct aws_hash_table enc_ctx; TEST_ASSERT_SUCCESS(aws_cryptosdk_enc_ctx_init(alloc, &enc_ctx)); struct aws_byte_cursor cursor = aws_byte_cursor_from_array(serialized_enc_ctx, sizeof(serialized_enc_ctx)); TEST_ASSERT_ERROR(AWS_CRYPTOSDK_ERR_BAD_CIPHERTEXT, aws_cryptosdk_enc_ctx_deserialize(alloc, &enc_ctx, &cursor)); aws_cryptosdk_enc_ctx_clean_up(&enc_ctx); return 0; } struct test_case enc_ctx_test_cases[] = { { "enc_ctx", "get_sorted_elems_array_test", get_sorted_elems_array_test }, { "enc_ctx", "serialize_empty_enc_ctx", serialize_empty_enc_ctx }, { "enc_ctx", "serialize_valid_enc_ctx", serialize_valid_enc_ctx }, { "enc_ctx", "serialize_valid_enc_ctx_unsigned_comparison", serialize_valid_enc_ctx_unsigned_comparison }, { "enc_ctx", "serialize_error_when_element_too_long", serialize_error_when_element_too_long }, { "enc_ctx", "serialize_error_when_serialized_len_too_long", serialize_error_when_serialized_len_too_long }, { "enc_ctx", "serialize_valid_enc_ctx_max_length", serialize_valid_enc_ctx_max_length }, { "enc_ctx", "serialize_error_when_too_many_elements", serialize_error_when_too_many_elements }, { "enc_ctx", "clone_test", enc_ctx_clone_test }, { "enc_ctx", "deserialize_error_when_duplicate_key_in_context", deserialize_error_when_duplicate_key_in_context }, { NULL } };