Skip to content

Commit

Permalink
Auth API revamp (#156)
Browse files Browse the repository at this point in the history
Updates the python CRT against the AuthRevamp branch of aws-c-auth

Co-authored-by: Bret Ambrose <[email protected]>
  • Loading branch information
graebm and bretambrose authored Jun 15, 2020
1 parent e18f041 commit 9e60439
Show file tree
Hide file tree
Showing 23 changed files with 11,469 additions and 286 deletions.
2 changes: 1 addition & 1 deletion aws-common-runtime/aws-c-auth
Submodule aws-c-auth updated 47 files
+67 −0 README.md
+12 −0 include/aws/auth/auth.h
+255 −0 include/aws/auth/aws_imds_client.h
+155 −64 include/aws/auth/credentials.h
+7 −2 include/aws/auth/private/aws_signing.h
+54 −3 include/aws/auth/private/credentials_utils.h
+23 −2 include/aws/auth/signable.h
+28 −4 include/aws/auth/signing.h
+153 −40 include/aws/auth/signing_config.h
+37 −0 source/auth.c
+1,612 −0 source/aws_imds_client.c
+3 −2 source/aws_profile.c
+326 −202 source/aws_signing.c
+134 −64 source/credentials.c
+41 −27 source/credentials_provider_cached.c
+16 −6 source/credentials_provider_chain.c
+53 −128 source/credentials_provider_ecs.c
+23 −26 source/credentials_provider_environment.c
+104 −852 source/credentials_provider_imds.c
+268 −0 source/credentials_provider_process.c
+10 −2 source/credentials_provider_profile.c
+4 −11 source/credentials_provider_static.c
+242 −69 source/credentials_provider_sts.c
+1,159 −0 source/credentials_provider_sts_web_identity.c
+645 −0 source/credentials_provider_x509.c
+158 −1 source/credentials_utils.c
+3 −1 source/external/cJSON.c
+3 −5 source/signable.c
+95 −58 source/signing.c
+50 −10 source/signing_config.c
+125 −17 source/sigv4_http_request.c
+1 −1 source/xml_parser.c
+47 −2 tests/CMakeLists.txt
+1,391 −0 tests/aws_imds_client_test.c
+23 −42 tests/credentials_provider_ecs_tests.c
+87 −114 tests/credentials_provider_imds_tests.c
+372 −0 tests/credentials_provider_process_tests.c
+113 −28 tests/credentials_provider_sts_tests.c
+1,203 −0 tests/credentials_provider_sts_web_identity_tests.c
+9 −8 tests/credentials_provider_utils.c
+2 −1 tests/credentials_provider_utils.h
+662 −0 tests/credentials_provider_x509_tests.c
+90 −85 tests/credentials_tests.c
+6 −0 tests/shared_credentials_test_definitions.h
+42 −65 tests/sigv4_tests.c
+332 −0 tests/test_chunked_signing.c
+10 −14 tests/test_signable.c
2 changes: 1 addition & 1 deletion aws-common-runtime/aws-c-common
Submodule aws-c-common updated 71 files
+3 −0 .cbmc-batch/jobs/Makefile.common
+2 −4 .cbmc-batch/jobs/aws_hash_table_clean_up/Makefile
+12 −3 .cbmc-batch/jobs/aws_hash_table_clean_up/aws_hash_table_clean_up_harness.c
+3 −3 .cbmc-batch/jobs/aws_hash_table_clean_up/cbmc-batch.yaml
+12 −4 .cbmc-batch/jobs/aws_string_destroy_secure/aws_string_destroy_secure_harness.c
+2 −2 .cbmc-batch/jobs/aws_string_destroy_secure/cbmc-batch.yaml
+20 −5 .github/workflows/ci.yml
+27 −11 CMakeLists.txt
+7 −9 README.md
+9 −0 cmake/AwsFeatureTests.cmake
+0 −30 cmake/AwsSIMD.cmake
+14 −0 cmake/AwsTestHarness.cmake
+15 −0 include/aws/common/allocator.h
+68 −53 include/aws/common/bigint.h
+46 −0 include/aws/common/byte_buf.h
+94 −0 include/aws/common/cache.h
+37 −0 include/aws/common/cpuid.h
+18 −0 include/aws/common/encoding.h
+38 −0 include/aws/common/encoding.inl
+3 −3 include/aws/common/exports.h
+39 −0 include/aws/common/fifo_cache.h
+39 −0 include/aws/common/lifo_cache.h
+131 −0 include/aws/common/linked_hash_table.h
+8 −66 include/aws/common/lru_cache.h
+108 −0 include/aws/common/math.fallback.inl
+88 −0 include/aws/common/math.gcc_builtin.inl
+18 −0 include/aws/common/math.h
+4 −0 include/aws/common/math.inl
+70 −0 include/aws/common/math.msvc.inl
+65 −0 include/aws/common/process.h
+6 −0 include/aws/common/string.h
+10 −1 include/aws/common/string.inl
+1 −1 include/aws/common/system_info.h
+3 −0 sanitizer-blacklist.txt
+430 −0 source/allocator_sba.c
+0 −91 source/arch/cpuid.c
+25 −0 source/arch/generic/cpuid.c
+39 −0 source/arch/intel/asm/cpuid.c
+111 −0 source/arch/intel/cpuid.c
+0 −0 source/arch/intel/encoding_avx2.c
+22 −0 source/arch/intel/msvc/cpuid.c
+478 −415 source/bigint.c
+40 −0 source/byte_buf.c
+70 −0 source/cache.c
+31 −31 source/date_time.c
+69 −0 source/fifo_cache.c
+72 −0 source/lifo_cache.c
+147 −0 source/linked_hash_table.c
+3 −3 source/log_formatter.c
+71 −113 source/lru_cache.c
+41 −1 source/posix/process.c
+5 −5 source/posix/system_info.c
+92 −0 source/process_common.c
+21 −1 source/windows/process.c
+47 −16 tests/CMakeLists.txt
+201 −0 tests/alloc_test.c
+480 −450 tests/bigint_test.c
+135 −0 tests/byte_buf_test.c
+369 −0 tests/cache_test.c
+7 −11 tests/cursor_test.c
+117 −0 tests/encoding_test.c
+178 −0 tests/linked_hash_table_test.c
+4 −4 tests/logging/log_writer_test.c
+1 −1 tests/logging/logging_test_utilities.c
+2 −2 tests/logging/test_logger.c
+0 −266 tests/lru_cache_test.c
+65 −2 tests/process_test.c
+1 −0 tests/resources/ascii.txt
+ tests/resources/utf16be.txt
+ tests/resources/utf16le.txt
+1 −0 tests/resources/utf8.txt
2 changes: 1 addition & 1 deletion aws-common-runtime/aws-c-http
Submodule aws-c-http updated 41 files
+4 −5 bin/elasticurl/main.c
+192 −0 include/aws/http/connection.h
+6 −0 include/aws/http/connection_manager.h
+19 −0 include/aws/http/http.h
+14 −0 include/aws/http/private/connection_impl.h
+6 −0 include/aws/http/private/connection_manager_system_vtable.h
+50 −1 include/aws/http/private/h1_encoder.h
+10 −2 include/aws/http/private/h1_stream.h
+59 −16 include/aws/http/private/h2_connection.h
+3 −3 include/aws/http/private/h2_decoder.h
+10 −46 include/aws/http/private/h2_frames.h
+17 −1 include/aws/http/private/h2_stream.h
+1 −0 include/aws/http/private/request_response_impl.h
+67 −0 include/aws/http/request_response.h
+71 −10 source/connection.c
+377 −85 source/connection_manager.c
+25 −5 source/h1_connection.c
+466 −91 source/h1_encoder.c
+96 −3 source/h1_stream.c
+570 −171 source/h2_connection.c
+30 −30 source/h2_decoder.c
+45 −45 source/h2_frames.c
+166 −24 source/h2_stream.c
+1 −1 source/hpack.c
+22 −12 source/http.c
+10 −0 source/request_response.c
+2 −2 source/websocket.c
+69 −23 tests/CMakeLists.txt
+5 −5 tests/fuzz/fuzz_h2_decoder_correct.c
+6 −7 tests/h2_test_helper.c
+11 −11 tests/h2_test_helper.h
+1 −1 tests/proxy_test_helper.c
+288 −58 tests/test_connection_manager.c
+5 −2 tests/test_connection_monitor.c
+970 −9 tests/test_h1_client.c
+413 −0 tests/test_h1_encoder.c
+2,603 −871 tests/test_h2_client.c
+44 −44 tests/test_h2_decoder.c
+5 −5 tests/test_h2_encoder.c
+1 −1 tests/test_message.c
+2 −2 tests/test_tls.c
2 changes: 1 addition & 1 deletion aws-common-runtime/s2n
Submodule s2n updated from bffd9b to f08b70
216 changes: 159 additions & 57 deletions awscrt/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ def __init__(self, access_key_id, secret_access_key, session_token=None):
super(AwsCredentials, self).__init__()
self._binding = _awscrt.credentials_new(access_key_id, secret_access_key, session_token)

@classmethod
def _from_binding(cls, binding):
"""Construct from a pre-existing native object"""
credentials = cls.__new__(cls) # avoid class's default constructor
super(cls, credentials).__init__() # just invoke parent class's __init__()
credentials._binding = binding
return credentials

@property
def access_key_id(self):
return _awscrt.credentials_access_key_id(self._binding)
Expand Down Expand Up @@ -168,12 +176,12 @@ def new_static(cls, access_key_id, secret_access_key, session_token=None):
def get_credentials(self):
future = Future()

def _on_complete(error_code, access_key_id, secret_access_key, session_token):
def _on_complete(error_code, binding):
try:
if error_code:
future.set_exception(awscrt.exceptions.from_code(error_code))
else:
credentials = AwsCredentials(access_key_id, secret_access_key, session_token)
credentials = AwsCredentials._from_binding(binding)
future.set_result(credentials)

except Exception as e:
Expand All @@ -190,34 +198,52 @@ def _on_complete(error_code, access_key_id, secret_access_key, session_token):
class AwsSigningAlgorithm(IntEnum):
"""AWS signing algorithm enumeration."""

SigV4Header = 0
"""Use Signature Version 4 to sign headers."""

SigV4QueryParam = 1
"""Use Signature Version 4 to sign query parameters."""
V4 = 0
"""Use Signature Version 4"""


class AwsBodySigningConfigType(IntEnum):
"""Body signing config enumeration"""
class AwsSignatureType(IntEnum):
"""Which sort of signature should be computed from the signable."""

BodySigningOff = 0
HTTP_REQUEST_HEADERS = 0
"""
No attempts will be made to sign the payload, and no "x-amz-content-sha256"
header will be added to the request.
A signature for a full HTTP request should be computed,
with header updates applied to the signing result.
"""

BodySigningOn = 1
HTTP_REQUEST_QUERY_PARAMS = 1
"""
The body will be signed and "x-amz-content-sha256" will contain
the value of the signature.
A signature for a full HTTP request should be computed,
with query param updates applied to the signing result.
"""

UnsignedPayload = 2

class AwsSignedBodyValueType(IntEnum):
"""Controls what goes in the canonical request's body value."""

EMPTY = 0
"""Use the SHA-256 of the empty string."""

PAYLOAD = 1
"""Use the SHA-256 of the actual payload."""

UNSIGNED_PAYLOAD = 2
"""Use the literal string "UNSIGNED-PAYLOAD"."""


class AwsSignedBodyHeaderType(IntEnum):
"""
The body will not be signed, but "x-amz-content-sha256" will contain
the value "UNSIGNED-PAYLOAD". This value is currently only used for Amazon S3.
Controls if signing adds a header containing the canonical request's signed body value.
See :class:`AwsSignedBodyValueType`.
"""

NONE = 0
"""Do not add a header."""

X_AMZ_CONTENT_SHA_256 = 1
"""Add the "x-amz-content-sha-256" header with the canonical request's signed body value"""


class AwsSigningConfig(NativeResource):
"""
Expand All @@ -228,7 +254,10 @@ class AwsSigningConfig(NativeResource):
It is good practice to use a new config for each signature, or the date might get too old.
Args:
algorithm (AwsSigningAlgorithm): Which signing process to invoke.
algorithm (AwsSigningAlgorithm): Which signing algorithm to use.
signature_type (AwsSignatureType): Which sort of signature should be
computed from the signable.
credentials_provider (AwsCredentialsProviderBase): Credentials provider
to fetch signing credentials with.
Expand All @@ -242,53 +271,84 @@ class AwsSigningConfig(NativeResource):
`datetime.datetime.now(datetime.timezone.utc)` is used.
Naive dates (lacking timezone info) are assumed to be in local time.
should_sign_param (Optional[Callable[[str], bool]]):
Optional function to control which parameters (header or query) are
should_sign_header (Optional[Callable[[str], bool]]):
Optional function to control which headers are
a part of the canonical request.
Skipping auth-required params will result in an unusable signature.
Skipping auth-required headers will result in an unusable signature.
Headers injected by the signing process are not skippable.
This function does not override the internal check function
(x-amzn-trace-id, user-agent), but rather supplements it.
In particular, a header will get signed if and only if it returns
true to both the internal check (skips x-amzn-trace-id, user-agent)
and this function (if defined).
use_double_uri_encode (bool): Set True to double-encode the resource
path when constructing the canonical request. By default, all
services except S3 use double encoding.
use_double_uri_encode (bool): Whether to double-encode the resource path
when constructing the canonical request (assuming the path is already
encoded). Default is True. All services except S3 use double encoding.
should_normalize_uri_path (bool): Whether the resource paths are
normalized when building the canonical request.
body_signing_type (AwsBodySigningConfigType): Controls how the payload
will be signed.
signed_body_value_type (AwsSignedBodyValueType): Controls what goes in
the canonical request's body value. Default is to use the SHA-256
of the actual payload.
signed_body_header_type (AwsSignedBodyHeaderType): Controls if signing
adds a header containing the canonical request's signed body value.
Default is to not add a header.
expiration_in_seconds (Optional[int]): If set, and signature_type is
`HTTP_REQUEST_QUERY_PARAMS`, then signing will add "X-Amz-Expires"
to the query string, equal to the value specified here.
omit_session_token (bool): If set True, the "X-Amz-Security-Token"
query param is omitted from the canonical request.
The default False should be used for most services.
"""
__slots__ = ('_priv_should_sign_cb')

_attributes = ('algorithm', 'credentials_provider', 'region', 'service', 'date', 'should_sign_param',
'use_double_uri_encode', 'should_normalize_uri_path', 'body_signing_type')
_attributes = (
'algorithm',
'signature_type',
'credentials_provider',
'region',
'service',
'date',
'should_sign_header',
'use_double_uri_encode',
'should_normalize_uri_path',
'signed_body_value_type',
'signed_body_header_type',
'expiration_in_seconds',
'omit_session_token',
)

def __init__(self,
algorithm, # type: AwsSigningAlgorithm
credentials_provider, # type: AwsCredentialsProviderBase
region, # type: str
service, # type: str
date=None, # type: Optional[datetime.datetime]
should_sign_param=None, # type: Optional[Callable[[str], bool]]
use_double_uri_encode=False, # type: bool
should_normalize_uri_path=True, # type: bool
body_signing_type=AwsBodySigningConfigType.BodySigningOn # type: AwsBodySigningConfigType
algorithm,
signature_type,
credentials_provider,
region,
service,
date=None,
should_sign_header=None,
use_double_uri_encode=True,
should_normalize_uri_path=True,
signed_body_value_type=AwsSignedBodyValueType.PAYLOAD,
signed_body_header_type=AwsSignedBodyHeaderType.NONE,
expiration_in_seconds=None,
omit_session_token=False,
):
# type: (...) -> None

assert isinstance(algorithm, AwsSigningAlgorithm)
assert isinstance(signature_type, AwsSignatureType)
assert isinstance(credentials_provider, AwsCredentialsProviderBase)
assert isinstance_str(region)
assert isinstance_str(service)
assert isinstance(date, datetime.datetime) or date is None
assert callable(should_sign_param) or should_sign_param is None
assert isinstance(body_signing_type, AwsBodySigningConfigType)
assert callable(should_sign_header) or should_sign_header is None
assert isinstance(signed_body_value_type, AwsSignedBodyValueType)
assert isinstance(signed_body_header_type, AwsSignedBodyHeaderType)
assert expiration_in_seconds is None or expiration_in_seconds > 0

super(AwsSigningConfig, self).__init__()

Expand All @@ -306,25 +366,33 @@ def __init__(self,
epoch = datetime.datetime(1970, 1, 1, tzinfo=_utc)
timestamp = (date - epoch).total_seconds()

self._priv_should_sign_cb = should_sign_param
self._priv_should_sign_cb = should_sign_header

if should_sign_param is not None:
def should_sign_param_wrapper(name):
return should_sign_param(name=name)
if should_sign_header is not None:
def should_sign_header_wrapper(name):
return should_sign_header(name=name)
else:
should_sign_param_wrapper = None
should_sign_header_wrapper = None

if expiration_in_seconds is None:
# C layer uses 0 to indicate None
expiration_in_seconds = 0

self._binding = _awscrt.signing_config_new(
algorithm,
signature_type,
credentials_provider,
region,
service,
date,
timestamp,
should_sign_param_wrapper,
should_sign_header_wrapper,
use_double_uri_encode,
should_normalize_uri_path,
body_signing_type)
signed_body_value_type,
signed_body_header_type,
expiration_in_seconds,
omit_session_token)

def replace(self, **kwargs):
"""
Expand All @@ -336,9 +404,14 @@ def replace(self, **kwargs):

@property
def algorithm(self):
"""AwsSigningAlgorithm: Which signing process to invoke"""
"""AwsSigningAlgorithm: Which signing algorithm to use"""
return AwsSigningAlgorithm(_awscrt.signing_config_get_algorithm(self._binding))

@property
def signature_type(self):
"""AwsSignatureType: Which sort of signature should be computed from the signable."""
return AwsSignatureType(_awscrt.signing_config_get_signature_type(self._binding))

@property
def credentials_provider(self):
"""AwsCredentialsProviderBase: Credentials provider to fetch signing credentials with"""
Expand Down Expand Up @@ -367,12 +440,12 @@ def date(self):
return _awscrt.signing_config_get_date(self._binding)

@property
def should_sign_param(self):
def should_sign_header(self):
"""
Optional[Callable[[str], bool]]: Optional function to control which
parameters (header or query) are a part of the canonical request.
headers are a part of the canonical request.
Skipping auth-required params will result in an unusable signature.
Skipping auth-required headers will result in an unusable signature.
Headers injected by the signing process are not skippable.
This function does not override the internal check function
(x-amzn-trace-id, user-agent), but rather supplements it. In particular,
Expand All @@ -385,7 +458,7 @@ def should_sign_param(self):
def use_double_uri_encode(self):
"""
bool: Whether to double-encode the resource path when constructing
the canonical request.
the canonical request (assuming the path is already encoded).
By default, all services except S3 use double encoding.
"""
Expand All @@ -400,9 +473,38 @@ def should_normalize_uri_path(self):
return _awscrt.signing_config_get_should_normalize_uri_path(self._binding)

@property
def body_signing_type(self):
"""AwsBodySigningConfigType: Controls how the payload will be signed."""
return AwsBodySigningConfigType(_awscrt.signing_config_get_body_signing_type(self._binding))
def signed_body_value_type(self):
"""
AwsSignedBodyValueType: Controls what goes in the canonical request's body value.
"""
return AwsSignedBodyValueType(_awscrt.signing_config_get_signed_body_value_type(self._binding))

@property
def signed_body_header_type(self):
"""
AwsSignedBodyHeaderType: Controls if signing adds a header containing
the canonical request's signed body value.
"""
return AwsSignedBodyHeaderType(_awscrt.signing_config_get_signed_body_header_type(self._binding))

@property
def expiration_in_seconds(self):
"""
Optional[int]: If set, and signature_type is `HTTP_REQUEST_QUERY_PARAMS`,
then signing will add "X-Amz-Expires" to the query string, equal to the
value specified here. Otherwise, this is None has no effect.
"""
expiration = _awscrt.signing_config_get_expiration_in_seconds(self._binding)
# C layer uses 0 to indicate None
return None if expiration == 0 else expiration

@property
def omit_session_token(self):
"""
bool: Whether the "X-Amz-Security-Token" query param is omitted
from the canonical request. This should be False for most services.
"""
return _awscrt.signing_config_get_omit_session_token(self._binding)


def aws_sign_request(http_request, signing_config):
Expand Down
11 changes: 4 additions & 7 deletions awscrt/awsiot_mqtt_connection_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,20 +244,17 @@ def websockets_with_default_aws_signing(region, credentials_provider, websocket_
"""
_check_required_kwargs(**kwargs)

def _should_sign_param(name, **kwargs):
blacklist = ['x-amz-date', 'x-amz-security-token']
return not (name.lower() in blacklist)

def _sign_websocket_handshake_request(transform_args, **kwargs):
# transform_args need to know when transform is done
try:
signing_config = awscrt.auth.AwsSigningConfig(
algorithm=awscrt.auth.AwsSigningAlgorithm.SigV4QueryParam,
algorithm=awscrt.auth.AwsSigningAlgorithm.V4,
signature_type=awscrt.auth.AwsSignatureType.HTTP_REQUEST_QUERY_PARAMS,
credentials_provider=credentials_provider,
region=region,
service='iotdevicegateway',
should_sign_param=_should_sign_param,
body_signing_type=awscrt.auth.AwsBodySigningConfigType.BodySigningOff)
omit_session_token=True, # IoT is weird and does not sign X-Amz-Security-Token
)

signing_future = awscrt.auth.aws_sign_request(transform_args.http_request, signing_config)
signing_future.add_done_callback(lambda x: transform_args.set_done(x.exception()))
Expand Down
Loading

0 comments on commit 9e60439

Please sign in to comment.