Skip to content

Commit

Permalink
Signable trailer (#144)
Browse files Browse the repository at this point in the history
add signable trailing headers.
  • Loading branch information
ilevyor committed Nov 10, 2021
1 parent 3169e2d commit 56af421
Show file tree
Hide file tree
Showing 10 changed files with 764 additions and 234 deletions.
17 changes: 17 additions & 0 deletions include/aws/auth/signable.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define AWS_AUTH_SIGNABLE_H

#include <aws/auth/auth.h>
#include <aws/http/request_response.h>

struct aws_http_message;
struct aws_input_stream;
Expand Down Expand Up @@ -200,6 +201,22 @@ struct aws_signable *aws_signable_new_chunk(
struct aws_input_stream *chunk_data,
struct aws_byte_cursor previous_signature);

/**
* Creates a signable wrapper around a set of headers.
*
* @param allocator memory allocator use to create the signable
* @param trailing_headers http headers to create a signable for
* @param previous_signature the signature computed in the most recent signing that preceded this one. It can be
* found by copying the "signature" property from the signing_result of that most recent signing.
*
* @return the new signable object, or NULL if failure
*/
AWS_AUTH_API
struct aws_signable *aws_signable_new_trailing_headers(
struct aws_allocator *allocator,
struct aws_http_headers *trailing_headers,
struct aws_byte_cursor previous_signature);

/**
* Creates a signable that represents a pre-computed canonical request from an http request
* @param allocator memory allocator use to create the signable
Expand Down
20 changes: 20 additions & 0 deletions include/aws/auth/signing_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ enum aws_signature_type {
* signature query param is added to the signing result.
*/
AWS_ST_CANONICAL_REQUEST_QUERY_PARAMS,

/**
* Compute a signature for the trailing headers.
* the signable should contain the most recent signature value (either the original http request or the most recent
* chunk) in the "previous-signature" property.
*/
AWS_ST_HTTP_REQUEST_TRAILING_HEADERS
};

/**
Expand All @@ -102,12 +109,25 @@ AWS_AUTH_API extern const struct aws_byte_cursor g_aws_signed_body_value_unsigne
*/
AWS_AUTH_API extern const struct aws_byte_cursor g_aws_signed_body_value_streaming_aws4_hmac_sha256_payload;

/**
* 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER'
* For use with `aws_signing_config_aws.signed_body_value`.
*/
AWS_AUTH_API extern const struct aws_byte_cursor g_aws_signed_body_value_streaming_aws4_hmac_sha256_payload_trailer;

/**
* 'STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD'
* For use with `aws_signing_config_aws.signed_body_value`.
*/
AWS_AUTH_API extern const struct aws_byte_cursor g_aws_signed_body_value_streaming_aws4_ecdsa_p256_sha256_payload;

/**
* 'STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER'
* For use with `aws_signing_config_aws.signed_body_value`.
*/
AWS_AUTH_API extern const struct aws_byte_cursor
g_aws_signed_body_value_streaming_aws4_ecdsa_p256_sha256_payload_trailer;

/**
* 'STREAMING-AWS4-HMAC-SHA256-EVENTS'
* For use with `aws_signing_config_aws.signed_body_value`.
Expand Down
54 changes: 54 additions & 0 deletions source/aws_signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ AWS_STATIC_STRING_FROM_LITERAL(s_signature_type_sigv4_http_request, "AWS4-HMAC-S
AWS_STATIC_STRING_FROM_LITERAL(s_signature_type_sigv4_s3_chunked_payload, "AWS4-HMAC-SHA256-PAYLOAD");
AWS_STATIC_STRING_FROM_LITERAL(s_signature_type_sigv4a_s3_chunked_payload, "AWS4-ECDSA-P256-SHA256-PAYLOAD");

AWS_STATIC_STRING_FROM_LITERAL(s_signature_type_sigv4_s3_chunked_trailer_payload, "AWS4-HMAC-SHA256-TRAILER");
AWS_STATIC_STRING_FROM_LITERAL(s_signature_type_sigv4a_s3_chunked_trailer_payload, "AWS4-ECDSA-P256-SHA256-TRAILER");

/* aws-related query param and header tables */
static struct aws_hash_table s_forbidden_headers;
static struct aws_hash_table s_forbidden_params;
Expand Down Expand Up @@ -285,6 +288,13 @@ static int s_get_signature_type_cursor(struct aws_signing_state_aws *state, stru
*cursor = aws_byte_cursor_from_string(s_signature_type_sigv4a_s3_chunked_payload);
}
break;
case AWS_ST_HTTP_REQUEST_TRAILING_HEADERS:
if (state->config.algorithm == AWS_SIGNING_ALGORITHM_V4) {
*cursor = aws_byte_cursor_from_string(s_signature_type_sigv4_s3_chunked_trailer_payload);
} else {
*cursor = aws_byte_cursor_from_string(s_signature_type_sigv4a_s3_chunked_trailer_payload);
}
break;

default:
return aws_raise_error(AWS_AUTH_SIGNING_UNSUPPORTED_SIGNATURE_TYPE);
Expand Down Expand Up @@ -1715,6 +1725,47 @@ static int s_build_canonical_request_body_chunk(struct aws_signing_state_aws *st
return AWS_OP_SUCCESS;
}

static int s_build_canonical_request_trailing_headers(struct aws_signing_state_aws *state) {

struct aws_byte_buf *dest = &state->string_to_sign_payload;

/* previous signature + \n */
struct aws_byte_cursor prev_signature_cursor;
AWS_ZERO_STRUCT(prev_signature_cursor);
if (aws_signable_get_property(state->signable, g_aws_previous_signature_property_name, &prev_signature_cursor)) {
AWS_LOGF_ERROR(
AWS_LS_AUTH_SIGNING,
"(id=%p) trailing_headers signable missing previous signature property",
(void *)state->signable);
return aws_raise_error(AWS_AUTH_SIGNING_MISSING_PREVIOUS_SIGNATURE);
}

/* strip any padding (AWS_SIGV4A_SIGNATURE_PADDING_BYTE) from the previous signature */
prev_signature_cursor = aws_trim_padded_sigv4a_signature(prev_signature_cursor);

if (aws_byte_buf_append_dynamic(dest, &prev_signature_cursor)) {
return AWS_OP_ERR;
}

if (aws_byte_buf_append_byte_dynamic(dest, '\n')) {
return AWS_OP_ERR;
}

/* current hash */

if (s_build_canonical_headers(state)) {
return AWS_OP_ERR;
}
struct aws_byte_cursor header_block_cursor = aws_byte_cursor_from_buf(&state->canonical_header_block);
if (aws_byte_buf_append_dynamic(&state->canonical_request, &header_block_cursor)) {
return AWS_OP_ERR;
}
if (s_build_canonical_request_hash(state)) {
return AWS_OP_ERR;
}
return AWS_OP_SUCCESS;
}

/*
* Builds a sigv4-signed canonical request and its hashed value
*/
Expand Down Expand Up @@ -1865,6 +1916,9 @@ int aws_signing_build_canonical_request(struct aws_signing_state_aws *state) {
case AWS_ST_HTTP_REQUEST_CHUNK:
return s_build_canonical_request_body_chunk(state);

case AWS_ST_HTTP_REQUEST_TRAILING_HEADERS:
return s_build_canonical_request_trailing_headers(state);

case AWS_ST_CANONICAL_REQUEST_HEADERS:
case AWS_ST_CANONICAL_REQUEST_QUERY_PARAMS:
return s_apply_existing_canonical_request(state);
Expand Down
115 changes: 115 additions & 0 deletions source/signable_chunk.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

#include <aws/auth/signable.h>
#include <aws/common/string.h>

/*
* This is a simple aws_signable wrapper implementation for an s3 chunk
*/
struct aws_signable_chunk_impl {
struct aws_input_stream *chunk_data;
struct aws_string *previous_signature;
};

static int s_aws_signable_chunk_get_property(
const struct aws_signable *signable,
const struct aws_string *name,
struct aws_byte_cursor *out_value) {

struct aws_signable_chunk_impl *impl = signable->impl;

AWS_ZERO_STRUCT(*out_value);

/*
* uri and method can be queried directly from the wrapper request
*/
if (aws_string_eq(name, g_aws_previous_signature_property_name)) {
*out_value = aws_byte_cursor_from_string(impl->previous_signature);
} else {
return AWS_OP_ERR;
}

return AWS_OP_SUCCESS;
}

static int s_aws_signable_chunk_get_property_list(
const struct aws_signable *signable,
const struct aws_string *name,
struct aws_array_list **out_list) {
(void)signable;
(void)name;
(void)out_list;

return AWS_OP_ERR;
}

static int s_aws_signable_chunk_get_payload_stream(
const struct aws_signable *signable,
struct aws_input_stream **out_input_stream) {

struct aws_signable_chunk_impl *impl = signable->impl;
*out_input_stream = impl->chunk_data;

return AWS_OP_SUCCESS;
}

static void s_aws_signable_chunk_destroy(struct aws_signable *signable) {
if (signable == NULL) {
return;
}

struct aws_signable_chunk_impl *impl = signable->impl;
if (impl == NULL) {
return;
}

aws_string_destroy(impl->previous_signature);

aws_mem_release(signable->allocator, signable);
}

static struct aws_signable_vtable s_signable_chunk_vtable = {
.get_property = s_aws_signable_chunk_get_property,
.get_property_list = s_aws_signable_chunk_get_property_list,
.get_payload_stream = s_aws_signable_chunk_get_payload_stream,
.destroy = s_aws_signable_chunk_destroy,
};

struct aws_signable *aws_signable_new_chunk(
struct aws_allocator *allocator,
struct aws_input_stream *chunk_data,
struct aws_byte_cursor previous_signature) {

struct aws_signable *signable = NULL;
struct aws_signable_chunk_impl *impl = NULL;
aws_mem_acquire_many(
allocator, 2, &signable, sizeof(struct aws_signable), &impl, sizeof(struct aws_signable_chunk_impl));

if (signable == NULL || impl == NULL) {
return NULL;
}

AWS_ZERO_STRUCT(*signable);
AWS_ZERO_STRUCT(*impl);

signable->allocator = allocator;
signable->vtable = &s_signable_chunk_vtable;
signable->impl = impl;

impl->chunk_data = chunk_data;
impl->previous_signature = aws_string_new_from_array(allocator, previous_signature.ptr, previous_signature.len);
if (impl->previous_signature == NULL) {
goto on_error;
}

return signable;

on_error:

aws_signable_destroy(signable);

return NULL;
}
130 changes: 130 additions & 0 deletions source/signable_http_request.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

#include <aws/auth/signable.h>
#include <aws/common/string.h>
#include <aws/http/request_response.h>

/*
* This is a simple aws_signable wrapper implementation for the aws_http_message struct
*/
struct aws_signable_http_request_impl {
struct aws_http_message *request;
struct aws_array_list headers;
};

static int s_aws_signable_http_request_get_property(
const struct aws_signable *signable,
const struct aws_string *name,
struct aws_byte_cursor *out_value) {

struct aws_signable_http_request_impl *impl = signable->impl;

AWS_ZERO_STRUCT(*out_value);

/*
* uri and method can be queried directly from the wrapper request
*/
if (aws_string_eq(name, g_aws_http_uri_property_name)) {
aws_http_message_get_request_path(impl->request, out_value);
} else if (aws_string_eq(name, g_aws_http_method_property_name)) {
aws_http_message_get_request_method(impl->request, out_value);
} else {
return AWS_OP_ERR;
}

return AWS_OP_SUCCESS;
}

static int s_aws_signable_http_request_get_property_list(
const struct aws_signable *signable,
const struct aws_string *name,
struct aws_array_list **out_list) {

struct aws_signable_http_request_impl *impl = signable->impl;

*out_list = NULL;

if (aws_string_eq(name, g_aws_http_headers_property_list_name)) {
*out_list = &impl->headers;
} else {
return AWS_OP_ERR;
}

return AWS_OP_SUCCESS;
}

static int s_aws_signable_http_request_get_payload_stream(
const struct aws_signable *signable,
struct aws_input_stream **out_input_stream) {

struct aws_signable_http_request_impl *impl = signable->impl;
*out_input_stream = aws_http_message_get_body_stream(impl->request);

return AWS_OP_SUCCESS;
}

static void s_aws_signable_http_request_destroy(struct aws_signable *signable) {
if (signable == NULL) {
return;
}

struct aws_signable_http_request_impl *impl = signable->impl;
if (impl == NULL) {
return;
}

aws_array_list_clean_up(&impl->headers);
aws_mem_release(signable->allocator, signable);
}

static struct aws_signable_vtable s_signable_http_request_vtable = {
.get_property = s_aws_signable_http_request_get_property,
.get_property_list = s_aws_signable_http_request_get_property_list,
.get_payload_stream = s_aws_signable_http_request_get_payload_stream,
.destroy = s_aws_signable_http_request_destroy,
};

struct aws_signable *aws_signable_new_http_request(struct aws_allocator *allocator, struct aws_http_message *request) {

struct aws_signable *signable = NULL;
struct aws_signable_http_request_impl *impl = NULL;
aws_mem_acquire_many(
allocator, 2, &signable, sizeof(struct aws_signable), &impl, sizeof(struct aws_signable_http_request_impl));

AWS_ZERO_STRUCT(*signable);
AWS_ZERO_STRUCT(*impl);

signable->allocator = allocator;
signable->vtable = &s_signable_http_request_vtable;
signable->impl = impl;

/*
* Copy the headers since they're not different types
*/
size_t header_count = aws_http_message_get_header_count(request);
if (aws_array_list_init_dynamic(
&impl->headers, allocator, header_count, sizeof(struct aws_signable_property_list_pair))) {
goto on_error;
}

for (size_t i = 0; i < header_count; ++i) {
struct aws_http_header header;
aws_http_message_get_header(request, &header, i);

struct aws_signable_property_list_pair property = {.name = header.name, .value = header.value};
aws_array_list_push_back(&impl->headers, &property);
}

impl->request = request;

return signable;

on_error:

aws_signable_destroy(signable);

return NULL;
}
Loading

0 comments on commit 56af421

Please sign in to comment.