diff --git a/sigv4.c b/sigv4.c index 53442b6..f49ace9 100644 --- a/sigv4.c +++ b/sigv4.c @@ -4,11 +4,19 @@ #define AWS_SIGV4_SIGNING_ALGORITHM "AWS4-HMAC-SHA256" #define SHA256_DIGEST_SIZE 32 #define AWS_SIGV4_HEX_SHA256_LENGTH SHA256_DIGEST_SIZE * 2 -#define AWS_SIGV4_CANONICAL_REQUEST_BUF_LEN 2048 // Increased for large session tokens -#define AWS_SIGV4_STRING_TO_SIGN_BUF_LEN 2048 // Increased for large session tokens +#define AWS_SIGV4_CANONICAL_REQUEST_BUF_LEN 4096 // Increased for large session tokens +#define AWS_SIGV4_STRING_TO_SIGN_BUF_LEN 1024 #define AWS_SIGV4_KEY_BUF_LEN 64 #define AWS_SIGV4_MAX_NUM_QUERY_COMPONENTS 50 #define HMAC_MAX_MD_CBLOCK 128 +/* host, x-amz-date and x-amz-content-sha256 are appended to the caller's headers */ +#define AWS_SIGV4_MAX_NUM_CANONICAL_HEADERS (AWS_SIGV4_MAX_NUM_HEADERS + 3) + +/* writable space left in an output buffer ending at last (exclusive) */ +static unsigned int space_left(unsigned char *str, unsigned char *last) +{ + return (last > str) ? (unsigned int)(last - str) : 0; +} int aws_sigv4_empty_str(aws_sigv4_str_t *str) { @@ -96,21 +104,33 @@ static bool is_s3(aws_sigv4_params_t *sigv4_params) strncmp((char *)sigv4_params->service.data, "s3", 2) == 0; } +/* lexicographic order: on a common prefix the shorter name sorts first, so that + e.g. x-amz-meta sorts before x-amz-meta-foo as the canonical form requires */ +static int aws_sigv4_cmp(unsigned char *d1, size_t l1, unsigned char *d2, size_t l2) +{ + size_t len = l1 <= l2 ? l1 : l2; + int rc = strncmp((char *)d1, (char *)d2, len); + if (rc != 0) + { + return rc; + } + return (l1 < l2) ? -1 : (l1 > l2) ? 1 : 0; +} + static int aws_sigv4_kv_cmp(aws_sigv4_kv_t *p1, aws_sigv4_kv_t *p2) { - size_t len = p1->key.len <= p2->key.len ? p1->key.len : p2->key.len; - return strncmp((char *)p1->key.data, (char *)p2->key.data, len); + return aws_sigv4_cmp(p1->key.data, p1->key.len, p2->key.data, p2->key.len); } static int aws_sigv4_str_cmp(aws_sigv4_str_t *p1, aws_sigv4_str_t *p2) { - size_t len = p1->len <= p2->len ? p1->len : p2->len; - return strncmp((char *)p1->data, (char *)p2->data, len); + return aws_sigv4_cmp(p1->data, p1->len, p2->data, p2->len); } static unsigned char *construct_query_str(unsigned char *dst_cstr, + unsigned char *last, aws_sigv4_kv_t *query_params, size_t query_num) { @@ -118,9 +138,9 @@ static unsigned char *construct_query_str(unsigned char *dst_cstr, for (i = 0; i < query_num; i++) { /* here we assume args are percent-encoded */ - dst_cstr = aws_sigv4_sprintf(dst_cstr, "%V=%V", - &query_params[i].key, &query_params[i].value); - if (i != query_num - 1) + dst_cstr = aws_sigv4_snprintf(dst_cstr, space_left(dst_cstr, last), "%V=%V", + &query_params[i].key, &query_params[i].value); + if (i != query_num - 1 && dst_cstr < last) { *(dst_cstr++) = '&'; } @@ -218,20 +238,23 @@ void get_signing_key(aws_sigv4_params_t *sigv4_params, aws_sigv4_str_t *signing_ } void get_credential_scope(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *credential_scope) + aws_sigv4_str_t *credential_scope, + unsigned char *last) { unsigned char *str = credential_scope->data; /* get date in yyyymmdd format */ - str = aws_sigv4_snprintf(str, 8, "%V", &sigv4_params->x_amz_date); - str = aws_sigv4_sprintf(str, "/%V/%V/aws4_request", - &sigv4_params->region, &sigv4_params->service); + str = aws_sigv4_snprintf(str, space_left(str, last) < 8 ? space_left(str, last) : 8, + "%V", &sigv4_params->x_amz_date); + str = aws_sigv4_snprintf(str, space_left(str, last), "/%V/%V/aws4_request", + &sigv4_params->region, &sigv4_params->service); credential_scope->len = str - credential_scope->data; } void get_signed_headers(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *signed_headers) + aws_sigv4_str_t *signed_headers, + unsigned char *last) { - aws_sigv4_str_t headers[AWS_SIGV4_MAX_NUM_HEADERS]; + aws_sigv4_str_t headers[AWS_SIGV4_MAX_NUM_CANONICAL_HEADERS]; unsigned num_headers = 0; bool has_amz_content_sha256_header = false; @@ -264,18 +287,19 @@ void get_signed_headers(aws_sigv4_params_t *sigv4_params, { if (i > 0) { - str = aws_sigv4_sprintf(str, ";"); + str = aws_sigv4_snprintf(str, space_left(str, last), ";"); } - str = aws_sigv4_sprintf(str, "%V", &headers[i]); + str = aws_sigv4_snprintf(str, space_left(str, last), "%V", &headers[i]); } signed_headers->len = str - signed_headers->data; } void get_canonical_headers(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *canonical_headers) + aws_sigv4_str_t *canonical_headers, + unsigned char *last) { - aws_sigv4_kv_t headers[AWS_SIGV4_MAX_NUM_HEADERS]; + aws_sigv4_kv_t headers[AWS_SIGV4_MAX_NUM_CANONICAL_HEADERS]; unsigned num_headers = 0; aws_sigv4_kv_t *amz_content_sha256_header = NULL; @@ -321,22 +345,23 @@ void get_canonical_headers(aws_sigv4_params_t *sigv4_params, unsigned char *str = canonical_headers->data; for (i = 0; i < num_headers; i++) { - str = aws_sigv4_sprintf(str, "%V:%V\n", &headers[i].key, &headers[i].value); + str = aws_sigv4_snprintf(str, space_left(str, last), "%V:%V\n", &headers[i].key, &headers[i].value); } canonical_headers->len = str - canonical_headers->data; } int get_canonical_request(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *canonical_request) + aws_sigv4_str_t *canonical_request, + unsigned char *last) { unsigned char *str = canonical_request->data; /* TODO: Here we assume the URI and query string have already been encoded. * Add encoding logic in future. */ - str = aws_sigv4_sprintf(str, "%V\n%V\n", - &sigv4_params->method, - &sigv4_params->uri); + str = aws_sigv4_snprintf(str, space_left(str, last), "%V\n%V\n", + &sigv4_params->method, + &sigv4_params->uri); /* query string can be empty */ if (!aws_sigv4_empty_str(&sigv4_params->query_str)) @@ -346,27 +371,41 @@ int get_canonical_request(aws_sigv4_params_t *sigv4_params, parse_query_params(&sigv4_params->query_str, query_params, &query_num); sigv4_params->sort(query_params, query_num, sizeof(aws_sigv4_kv_t), (aws_sigv4_compare_func_t)aws_sigv4_kv_cmp); - str = construct_query_str(str, query_params, query_num); + str = construct_query_str(str, last, query_params, query_num); + } + if (str < last) + { + *(str++) = '\n'; } - *(str++) = '\n'; aws_sigv4_str_t canonical_headers = {.data = str}; - get_canonical_headers(sigv4_params, &canonical_headers); + get_canonical_headers(sigv4_params, &canonical_headers, last); str += canonical_headers.len; - *(str++) = '\n'; + if (str < last) + { + *(str++) = '\n'; + } aws_sigv4_str_t signed_headers = {.data = str}; - get_signed_headers(sigv4_params, &signed_headers); + get_signed_headers(sigv4_params, &signed_headers, last); str += signed_headers.len; - *(str++) = '\n'; + if (str < last) + { + *(str++) = '\n'; + } // Use UNSIGNED-PAYLOAD if specified, otherwise compute payload hash if (sigv4_params->unsigned_payload && is_s3(sigv4_params)) { - str = aws_sigv4_sprintf(str, "UNSIGNED-PAYLOAD"); + str = aws_sigv4_snprintf(str, space_left(str, last), "UNSIGNED-PAYLOAD"); } else { + /* get_hex_sha256 writes a fixed number of bytes, so it needs the room upfront */ + if (space_left(str, last) < AWS_SIGV4_HEX_SHA256_LENGTH) + { + return AWS_SIGV4_BUFFER_OVERFLOW_ERROR; + } aws_sigv4_str_t hex_sha256 = {.data = str}; get_hex_sha256(sigv4_params, &sigv4_params->payload, &hex_sha256); str += hex_sha256.len; @@ -374,29 +413,38 @@ int get_canonical_request(aws_sigv4_params_t *sigv4_params, canonical_request->len = str - canonical_request->data; - // Check for buffer overflow - if (canonical_request->len >= AWS_SIGV4_CANONICAL_REQUEST_BUF_LEN) + // Check for buffer overflow: writes above are clamped to last, so hitting it means + // the canonical request was truncated and the signature would not match + if (str >= last) { return AWS_SIGV4_BUFFER_OVERFLOW_ERROR; } return AWS_SIGV4_OK; } -void get_string_to_sign(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *request_date, - aws_sigv4_str_t *credential_scope, - aws_sigv4_str_t *canonical_request, - aws_sigv4_str_t *string_to_sign) +int get_string_to_sign(aws_sigv4_params_t *sigv4_params, + aws_sigv4_str_t *request_date, + aws_sigv4_str_t *credential_scope, + aws_sigv4_str_t *canonical_request, + aws_sigv4_str_t *string_to_sign, + unsigned char *last) { unsigned char *str = string_to_sign->data; - str = aws_sigv4_sprintf(str, "AWS4-HMAC-SHA256\n%V\n%V\n", - request_date, credential_scope); + str = aws_sigv4_snprintf(str, space_left(str, last), "AWS4-HMAC-SHA256\n%V\n%V\n", + request_date, credential_scope); + + /* get_hex_sha256 writes a fixed number of bytes, so it needs the room upfront */ + if (space_left(str, last) < AWS_SIGV4_HEX_SHA256_LENGTH) + { + return AWS_SIGV4_BUFFER_OVERFLOW_ERROR; + } aws_sigv4_str_t hex_sha256 = {.data = str}; get_hex_sha256(sigv4_params, canonical_request, &hex_sha256); str += hex_sha256.len; string_to_sign->len = str - string_to_sign->data; + return AWS_SIGV4_OK; } int aws_sigv4_sign(aws_sigv4_params_t *sigv4_params, aws_sigv4_header_t *auth_header) @@ -425,32 +473,51 @@ int aws_sigv4_sign(aws_sigv4_params_t *sigv4_params, aws_sigv4_header_t *auth_he auth_header->key.data = (unsigned char *)AWS_SIGV4_AUTH_HEADER_NAME; auth_header->key.len = strlen(AWS_SIGV4_AUTH_HEADER_NAME); + /* the caller's value buffer is AWS_SIGV4_AUTH_HEADER_MAX_LEN long, keep the + hex signature (fixed length, written last) inside it as well */ + unsigned char *auth_last = auth_header->value.data + AWS_SIGV4_AUTH_HEADER_MAX_LEN - 1 - AWS_SIGV4_HEX_SHA256_LENGTH; + /* AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/ */ unsigned char *str = auth_header->value.data; - str = aws_sigv4_sprintf(str, "AWS4-HMAC-SHA256 Credential=%V/", - &sigv4_params->access_key_id); + str = aws_sigv4_snprintf(str, space_left(str, auth_last), "AWS4-HMAC-SHA256 Credential=%V/", + &sigv4_params->access_key_id); aws_sigv4_str_t credential_scope = {.data = str}; - get_credential_scope(sigv4_params, &credential_scope); + get_credential_scope(sigv4_params, &credential_scope, auth_last); str += credential_scope.len; /* SignedHeaders= */ - str = aws_sigv4_sprintf(str, ", SignedHeaders="); + str = aws_sigv4_snprintf(str, space_left(str, auth_last), ", SignedHeaders="); aws_sigv4_str_t signed_headers = {.data = str}; - get_signed_headers(sigv4_params, &signed_headers); + get_signed_headers(sigv4_params, &signed_headers, auth_last); str += signed_headers.len; /* Signature= */ - str = aws_sigv4_sprintf(str, ", Signature="); + str = aws_sigv4_snprintf(str, space_left(str, auth_last), ", Signature="); + if (str >= auth_last) + { + rc = AWS_SIGV4_BUFFER_OVERFLOW_ERROR; + goto err; + } /* Task 1: Create a canonical request */ unsigned char canonical_request_buf[AWS_SIGV4_CANONICAL_REQUEST_BUF_LEN] = {0}; aws_sigv4_str_t canonical_request = {.data = canonical_request_buf}; - get_canonical_request(sigv4_params, &canonical_request); + rc = get_canonical_request(sigv4_params, &canonical_request, + canonical_request_buf + AWS_SIGV4_CANONICAL_REQUEST_BUF_LEN - 1); + if (rc != AWS_SIGV4_OK) + { + goto err; + } /* Task 2: Create a string to sign */ unsigned char string_to_sign_buf[AWS_SIGV4_STRING_TO_SIGN_BUF_LEN] = {0}; aws_sigv4_str_t string_to_sign = {.data = string_to_sign_buf}; - get_string_to_sign(sigv4_params, &sigv4_params->x_amz_date, &credential_scope, - &canonical_request, &string_to_sign); + rc = get_string_to_sign(sigv4_params, &sigv4_params->x_amz_date, &credential_scope, + &canonical_request, &string_to_sign, + string_to_sign_buf + AWS_SIGV4_STRING_TO_SIGN_BUF_LEN - 1); + if (rc != AWS_SIGV4_OK) + { + goto err; + } /* Task 3: Calculate the signature */ /* 3.1: Derive signing key */ unsigned char signing_key_buf[AWS_SIGV4_KEY_BUF_LEN] = {0}; diff --git a/sigv4.h b/sigv4.h index 3975643..fcf87c9 100644 --- a/sigv4.h +++ b/sigv4.h @@ -19,7 +19,7 @@ #define AWS_SIGV4_BUFFER_OVERFLOW_ERROR -2 #define AWS_SIGV4_INVALID_INPUT_ERROR -1 #define AWS_SIGV4_OK 0 -#define AWS_SIGV4_MAX_NUM_HEADERS 10 +#define AWS_SIGV4_MAX_NUM_HEADERS 24 #define AWS_SIGV4_AUTH_HEADER_MAX_LEN 2048 typedef struct aws_sigv4_str_s @@ -118,33 +118,42 @@ void get_signing_key(aws_sigv4_params_t *sigv4_params, * * @param[in] sigv4_params Pointer to a struct of sigv4 parameters * @param[out] credential_scope Struct of buffer to store credential scope string + * @param[in] last End of the writable output buffer (exclusive) */ void get_credential_scope(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *credential_scope); + aws_sigv4_str_t *credential_scope, + unsigned char *last); /** @brief get signed headers string * * @param[in] sigv4_params Pointer to a struct of sigv4 parameters * @param[out] signed_headers Struct of buffer to store signed headers string + * @param[in] last End of the writable output buffer (exclusive) */ void get_signed_headers(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *signed_headers); + aws_sigv4_str_t *signed_headers, + unsigned char *last); /** @brief get canonical headers string * * @param[in] sigv4_params Pointer to a struct of sigv4 parameters * @param[out] canonical_headers Struct of buffer to store canonical headers string + * @param[in] last End of the writable output buffer (exclusive) */ void get_canonical_headers(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *canonical_headers); + aws_sigv4_str_t *canonical_headers, + unsigned char *last); /** @brief get canonical request string * * @param[in] sigv4_params Pointer to a struct of sigv4 parameters * @param[out] canonical_request Struct of buffer to store canonical request string + * @param[in] last End of the writable output buffer (exclusive) + * @return Status code where zero for success and non-zero for failure */ int get_canonical_request(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *canonical_request); + aws_sigv4_str_t *canonical_request, + unsigned char *last); /** @brief get string to sign * @@ -153,12 +162,15 @@ int get_canonical_request(aws_sigv4_params_t *sigv4_params, * @param[in] credential_scope Pointer to a struct of precomputed credential scope * @param[in] canonical_request Pointer to a struct of precomputed canonical request * @param[out] string_to_sign Struct of buffer to store string to sign + * @param[in] last End of the writable output buffer (exclusive) + * @return Status code where zero for success and non-zero for failure */ -void get_string_to_sign(aws_sigv4_params_t *sigv4_params, - aws_sigv4_str_t *request_date, - aws_sigv4_str_t *credential_scope, - aws_sigv4_str_t *canonical_request, - aws_sigv4_str_t *string_to_sign); +int get_string_to_sign(aws_sigv4_params_t *sigv4_params, + aws_sigv4_str_t *request_date, + aws_sigv4_str_t *credential_scope, + aws_sigv4_str_t *canonical_request, + aws_sigv4_str_t *string_to_sign, + unsigned char *last); /** @brief perform sigv4 signing * diff --git a/test.c b/test.c index 727f861..f4504d0 100644 --- a/test.c +++ b/test.c @@ -52,6 +52,141 @@ START_TEST(AwsSigv4Test_AwsSigv4Sign) } END_TEST +/* every additional header must end up in both the canonical and the signed headers, + otherwise S3 rejects the request with "There were headers present in the request + which were not signed" (e.g. x-amz-copy-source on CopyObject) */ +START_TEST(AwsSigv4Test_AdditionalHeadersAreSigned) +{ + aws_sigv4_params_t sigv4_params = { + .access_key_id = aws_sigv4_string((unsigned char *)"AKIDEXAMPLE"), + .secret_access_key = aws_sigv4_string((unsigned char *)"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"), + .method = aws_sigv4_string((unsigned char *)"PUT"), + .uri = aws_sigv4_string((unsigned char *)"/Logo_dark2.png"), + .host = aws_sigv4_string((unsigned char *)"riptides-logos.s3.eu-central-1.amazonaws.com"), + .region = aws_sigv4_string((unsigned char *)"eu-central-1"), + .service = aws_sigv4_string((unsigned char *)"s3"), + .x_amz_date = aws_sigv4_string((unsigned char *)"20260803T120000Z"), + .headers = { + { + .key = aws_sigv4_string((unsigned char *)"x-amz-copy-source"), + .value = aws_sigv4_string((unsigned char *)"/riptides-logos/Logo_dark.png"), + }, + { + .key = aws_sigv4_string((unsigned char *)"x-amz-content-sha256"), + .value = aws_sigv4_string((unsigned char *)"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), + }, + }, + .num_headers = 2, + .hmac_sha256 = HMAC_SHA256, + .sha256 = (void *)SHA256, + .sort = qsort, + }; + + char auth_buf[AWS_SIGV4_AUTH_HEADER_MAX_LEN] = {0}; + aws_sigv4_header_t auth_header = { + .value = aws_sigv4_string((unsigned char *)auth_buf)}; + + int rc = aws_sigv4_sign(&sigv4_params, &auth_header); + const unsigned char *expected_auth_header_value = + "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20260803/eu-central-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-copy-source;x-amz-date, Signature=bd8e0ec929d2846e7ea9be3ac601e143bf6f02a23c476f0bd54d9c5a114eac1a"; + ck_assert_int_eq(rc, AWS_SIGV4_OK); + ck_assert_pstr_eq(auth_header.value.data, expected_auth_header_value); +} +END_TEST + +/* on a common prefix the shorter header name sorts first, so SSE-KMS requests keep + x-amz-server-side-encryption ahead of x-amz-server-side-encryption-aws-kms-key-id */ +START_TEST(AwsSigv4Test_PrefixHeaderNamesAreOrdered) +{ + aws_sigv4_params_t sigv4_params = { + .access_key_id = aws_sigv4_string((unsigned char *)"AKIDEXAMPLE"), + .secret_access_key = aws_sigv4_string((unsigned char *)"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"), + .method = aws_sigv4_string((unsigned char *)"PUT"), + .uri = aws_sigv4_string((unsigned char *)"/obj.txt"), + .host = aws_sigv4_string((unsigned char *)"riptides-logos.s3.eu-central-1.amazonaws.com"), + .region = aws_sigv4_string((unsigned char *)"eu-central-1"), + .service = aws_sigv4_string((unsigned char *)"s3"), + .x_amz_date = aws_sigv4_string((unsigned char *)"20260803T120000Z"), + .payload = aws_sigv4_string((unsigned char *)"abc"), + .unsigned_payload = true, + .headers = { + { + .key = aws_sigv4_string((unsigned char *)"x-amz-server-side-encryption"), + .value = aws_sigv4_string((unsigned char *)"aws:kms"), + }, + { + .key = aws_sigv4_string((unsigned char *)"x-amz-server-side-encryption-aws-kms-key-id"), + .value = aws_sigv4_string((unsigned char *)"arn:aws:kms:eu-central-1:1:key/abc"), + }, + { + .key = aws_sigv4_string((unsigned char *)"x-amz-content-sha256"), + .value = aws_sigv4_string((unsigned char *)"UNSIGNED-PAYLOAD"), + }, + { + .key = aws_sigv4_string((unsigned char *)"content-length"), + .value = aws_sigv4_string((unsigned char *)"3"), + }, + }, + .num_headers = 4, + .hmac_sha256 = HMAC_SHA256, + .sha256 = (void *)SHA256, + .sort = qsort, + }; + + char auth_buf[AWS_SIGV4_AUTH_HEADER_MAX_LEN] = {0}; + aws_sigv4_header_t auth_header = { + .value = aws_sigv4_string((unsigned char *)auth_buf)}; + + int rc = aws_sigv4_sign(&sigv4_params, &auth_header); + const unsigned char *expected_auth_header_value = + "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20260803/eu-central-1/s3/aws4_request, SignedHeaders=content-length;host;x-amz-content-sha256;x-amz-date;x-amz-server-side-encryption;x-amz-server-side-encryption-aws-kms-key-id, Signature=030bc849566322f211383dcab0b9dab63c6e29eca34786d6b303f601cb734f0c"; + ck_assert_int_eq(rc, AWS_SIGV4_OK); + ck_assert_pstr_eq(auth_header.value.data, expected_auth_header_value); +} +END_TEST + +/* headers that do not fit into the internal buffers must fail the signing instead of + writing past them */ +START_TEST(AwsSigv4Test_TooLargeCanonicalRequestFails) +{ + static char keys[AWS_SIGV4_MAX_NUM_HEADERS][64]; + static char values[AWS_SIGV4_MAX_NUM_HEADERS][1024]; + + aws_sigv4_params_t sigv4_params = { + .access_key_id = aws_sigv4_string((unsigned char *)"AKIDEXAMPLE"), + .secret_access_key = aws_sigv4_string((unsigned char *)"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"), + .method = aws_sigv4_string((unsigned char *)"PUT"), + .uri = aws_sigv4_string((unsigned char *)"/obj.txt"), + .host = aws_sigv4_string((unsigned char *)"riptides-logos.s3.eu-central-1.amazonaws.com"), + .region = aws_sigv4_string((unsigned char *)"eu-central-1"), + .service = aws_sigv4_string((unsigned char *)"s3"), + .x_amz_date = aws_sigv4_string((unsigned char *)"20260803T120000Z"), + .unsigned_payload = true, + .hmac_sha256 = HMAC_SHA256, + .sha256 = (void *)SHA256, + .sort = qsort, + }; + + unsigned int i; + for (i = 0; i < AWS_SIGV4_MAX_NUM_HEADERS; i++) + { + snprintf(keys[i], sizeof(keys[i]), "x-amz-meta-header-number-%02u", i); + memset(values[i], 'v', sizeof(values[i]) - 1); + values[i][sizeof(values[i]) - 1] = '\0'; + sigv4_params.headers[i].key = aws_sigv4_string((unsigned char *)keys[i]); + sigv4_params.headers[i].value = aws_sigv4_string((unsigned char *)values[i]); + sigv4_params.num_headers++; + } + + char auth_buf[AWS_SIGV4_AUTH_HEADER_MAX_LEN] = {0}; + aws_sigv4_header_t auth_header = { + .value = aws_sigv4_string((unsigned char *)auth_buf)}; + + int rc = aws_sigv4_sign(&sigv4_params, &auth_header); + ck_assert_int_eq(rc, AWS_SIGV4_BUFFER_OVERFLOW_ERROR); +} +END_TEST + Suite *aws_sigv4_test_suite(void) { Suite *s; @@ -59,6 +194,9 @@ Suite *aws_sigv4_test_suite(void) TCase *tc_aws_sigv4_sign = tcase_create("AwsSigv4Test_AwsSigv4Sign"); tcase_add_test(tc_aws_sigv4_sign, AwsSigv4Test_AwsSigv4Sign); + tcase_add_test(tc_aws_sigv4_sign, AwsSigv4Test_AdditionalHeadersAreSigned); + tcase_add_test(tc_aws_sigv4_sign, AwsSigv4Test_PrefixHeaderNamesAreOrdered); + tcase_add_test(tc_aws_sigv4_sign, AwsSigv4Test_TooLargeCanonicalRequestFails); suite_add_tcase(s, tc_aws_sigv4_sign); return s; }