Skip to content

Commit f3362bd

Browse files
authored
AwsSigningConfig.signed_body_value is now a string instead of an enum (#172)
This allows precalculated hashes to be used.
1 parent 5f6be49 commit f3362bd

File tree

11 files changed

+118
-53
lines changed

11 files changed

+118
-53
lines changed

aws-common-runtime/aws-c-common

Submodule aws-c-common updated 44 files

awscrt/auth.py

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -208,24 +208,32 @@ class AwsSignatureType(IntEnum):
208208
"""
209209

210210

211-
class AwsSignedBodyValueType(IntEnum):
212-
"""Controls what goes in the canonical request's body value."""
211+
class AwsSignedBodyValue:
212+
"""
213+
Values for use with :attr:`AwsSigningConfig.signed_body_value`.
214+
215+
Some services use special values (e.g. "UNSIGNED-PAYLOAD") when the body
216+
is not being signed in the usual way.
217+
"""
218+
219+
EMPTY_SHA256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
220+
"""The SHA-256 of the empty string."""
213221

214-
EMPTY = 0
215-
"""Use the SHA-256 of the empty string."""
222+
UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'
223+
"""Unsigned payload option (not accepted by all services)"""
216224

217-
PAYLOAD = 1
218-
"""Use the SHA-256 of the actual payload."""
225+
STREAMING_AWS4_HMAC_SHA256_PAYLOAD = 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD'
226+
"""Each payload chunk will be signed (not accepted by all services)"""
219227

220-
UNSIGNED_PAYLOAD = 2
221-
"""Use the literal string "UNSIGNED-PAYLOAD"."""
228+
STREAMING_AWS4_HMAC_SHA256_EVENTS = 'STREAMING-AWS4-HMAC-SHA256-EVENTS'
229+
"""Each event will be signed (not accepted by all services)"""
222230

223231

224232
class AwsSignedBodyHeaderType(IntEnum):
225233
"""
226234
Controls if signing adds a header containing the canonical request's signed body value.
227235
228-
See :class:`AwsSignedBodyValueType`.
236+
See :attr:`AwsSigningConfig.signed_body_value`.
229237
"""
230238

231239
NONE = 0
@@ -280,9 +288,13 @@ class AwsSigningConfig(NativeResource):
280288
should_normalize_uri_path (bool): Whether the resource paths are
281289
normalized when building the canonical request.
282290
283-
signed_body_value_type (AwsSignedBodyValueType): Controls what goes in
284-
the canonical request's body value. Default is to use the SHA-256
285-
of the actual payload.
291+
signed_body_value (Optional[str]): If set, this value is used as the
292+
canonical request's body value. Typically, this is the SHA-256
293+
of the payload, written as lowercase hex. If this has been
294+
precalculated, it can be set here. Special values used by certain
295+
services can also be set (see :class:`AwsSignedBodyValue`). If `None`
296+
is passed (the default), the typical value will be calculated from
297+
the payload during signing.
286298
287299
signed_body_header_type (AwsSignedBodyHeaderType): Controls if signing
288300
adds a header containing the canonical request's signed body value.
@@ -308,7 +320,7 @@ class AwsSigningConfig(NativeResource):
308320
'should_sign_header',
309321
'use_double_uri_encode',
310322
'should_normalize_uri_path',
311-
'signed_body_value_type',
323+
'signed_body_value',
312324
'signed_body_header_type',
313325
'expiration_in_seconds',
314326
'omit_session_token',
@@ -324,7 +336,7 @@ def __init__(self,
324336
should_sign_header=None,
325337
use_double_uri_encode=True,
326338
should_normalize_uri_path=True,
327-
signed_body_value_type=AwsSignedBodyValueType.PAYLOAD,
339+
signed_body_value=None,
328340
signed_body_header_type=AwsSignedBodyHeaderType.NONE,
329341
expiration_in_seconds=None,
330342
omit_session_token=False,
@@ -336,7 +348,7 @@ def __init__(self,
336348
assert isinstance_str(region)
337349
assert isinstance_str(service)
338350
assert callable(should_sign_header) or should_sign_header is None
339-
assert isinstance(signed_body_value_type, AwsSignedBodyValueType)
351+
assert signed_body_value is None or (isinstance(signed_body_value, str) and len(signed_body_value) > 0)
340352
assert isinstance(signed_body_header_type, AwsSignedBodyHeaderType)
341353
assert expiration_in_seconds is None or expiration_in_seconds > 0
342354

@@ -379,7 +391,7 @@ def should_sign_header_wrapper(name):
379391
should_sign_header_wrapper,
380392
use_double_uri_encode,
381393
should_normalize_uri_path,
382-
signed_body_value_type,
394+
signed_body_value,
383395
signed_body_header_type,
384396
expiration_in_seconds,
385397
omit_session_token)
@@ -463,11 +475,16 @@ def should_normalize_uri_path(self):
463475
return _awscrt.signing_config_get_should_normalize_uri_path(self._binding)
464476

465477
@property
466-
def signed_body_value_type(self):
478+
def signed_body_value(self):
467479
"""
468-
AwsSignedBodyValueType: Controls what goes in the canonical request's body value.
480+
Optional[str]: What to use as the canonical request's body value.
481+
If `None` is set (the default), a value will be calculated from
482+
the payload during signing. Typically, this is the SHA-256 of the
483+
payload, written as lowercase hex. If this has been precalculated,
484+
it can be set here. Special values used by certain services can also
485+
be set (see :class:`AwsSignedBodyValue`).
469486
"""
470-
return AwsSignedBodyValueType(_awscrt.signing_config_get_signed_body_value_type(self._binding))
487+
return _awscrt.signing_config_get_signed_body_value(self._binding)
471488

472489
@property
473490
def signed_body_header_type(self):

docsrc/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Minimal makefile for Sphinx documentation
2+
#
3+
4+
# You can set these variables from the command line, and also
5+
# from the environment for the first two.
6+
SPHINXOPTS ?=
7+
SPHINXBUILD ?= sphinx-build
8+
SOURCEDIR = source
9+
BUILDDIR = build
10+
11+
# Put it first so that "make" without argument is like "make help".
12+
help:
13+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14+
15+
.PHONY: help Makefile
16+
17+
# Catch-all target: route all unknown targets to Sphinx using the new
18+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19+
%: Makefile
20+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

make-docs.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
pushd `dirname $0`/docsrc > /dev/null
4+
make html
5+
cp -a build/html/. ../docs
6+
popd > /dev/null

source/auth.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ PyObject *aws_py_signing_config_get_service(PyObject *self, PyObject *args);
2626
PyObject *aws_py_signing_config_get_date(PyObject *self, PyObject *args);
2727
PyObject *aws_py_signing_config_get_use_double_uri_encode(PyObject *self, PyObject *args);
2828
PyObject *aws_py_signing_config_get_should_normalize_uri_path(PyObject *self, PyObject *args);
29-
PyObject *aws_py_signing_config_get_signed_body_value_type(PyObject *self, PyObject *args);
29+
PyObject *aws_py_signing_config_get_signed_body_value(PyObject *self, PyObject *args);
3030
PyObject *aws_py_signing_config_get_signed_body_header_type(PyObject *self, PyObject *args);
3131
PyObject *aws_py_signing_config_get_expiration_in_seconds(PyObject *self, PyObject *args);
3232
PyObject *aws_py_signing_config_get_omit_session_token(PyObject *self, PyObject *args);

source/auth_signing_config.c

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,13 @@ PyObject *aws_py_signing_config_new(PyObject *self, PyObject *args) {
7878
PyObject *py_should_sign_header_fn;
7979
PyObject *py_use_double_uri_encode;
8080
PyObject *py_should_normalize_uri_path;
81-
int signed_body_value_type;
81+
struct aws_byte_cursor signed_body_value;
8282
int signed_body_header_type;
8383
uint64_t expiration_in_seconds;
8484
PyObject *py_omit_session_token;
8585
if (!PyArg_ParseTuple(
8686
args,
87-
"iiOs#s#OdOOOiiKO",
87+
"iiOs#s#OdOOOz#iKO",
8888
&algorithm,
8989
&signature_type,
9090
&py_credentials_provider,
@@ -97,7 +97,8 @@ PyObject *aws_py_signing_config_new(PyObject *self, PyObject *args) {
9797
&py_should_sign_header_fn,
9898
&py_use_double_uri_encode,
9999
&py_should_normalize_uri_path,
100-
&signed_body_value_type,
100+
&signed_body_value.ptr,
101+
&signed_body_value.len,
101102
&signed_body_header_type,
102103
&expiration_in_seconds,
103104
&py_omit_session_token)) {
@@ -123,9 +124,11 @@ PyObject *aws_py_signing_config_new(PyObject *self, PyObject *args) {
123124
binding->native.config_type = AWS_SIGNING_CONFIG_AWS;
124125
binding->native.algorithm = algorithm;
125126
binding->native.signature_type = signature_type;
127+
binding->native.region = region;
128+
binding->native.service = service;
126129
binding->native.flags.use_double_uri_encode = PyObject_IsTrue(py_use_double_uri_encode);
127130
binding->native.flags.should_normalize_uri_path = PyObject_IsTrue(py_should_normalize_uri_path);
128-
binding->native.signed_body_value = signed_body_value_type;
131+
binding->native.signed_body_value = signed_body_value;
129132
binding->native.signed_body_header = signed_body_header_type;
130133
binding->native.expiration_in_seconds = expiration_in_seconds;
131134
binding->native.flags.omit_session_token = PyObject_IsTrue(py_omit_session_token);
@@ -138,26 +141,17 @@ PyObject *aws_py_signing_config_new(PyObject *self, PyObject *args) {
138141
binding->py_credentials_provider = py_credentials_provider;
139142
Py_INCREF(binding->py_credentials_provider);
140143

141-
/* strings: service, region */
142-
size_t total_string_len;
143-
if (aws_add_size_checked(region.len, service.len, &total_string_len)) {
144-
PyErr_SetAwsLastError();
144+
/* backup strings */
145+
if (aws_byte_buf_init_cache_and_update_cursors(
146+
&binding->string_storage,
147+
aws_py_get_allocator(),
148+
&binding->native.region,
149+
&binding->native.service,
150+
&binding->native.signed_body_value,
151+
NULL)) {
145152
goto error;
146153
}
147154

148-
if (aws_byte_buf_init(&binding->string_storage, aws_py_get_allocator(), total_string_len)) {
149-
PyErr_SetAwsLastError();
150-
goto error;
151-
}
152-
153-
binding->native.region.ptr = binding->string_storage.buffer + binding->string_storage.len;
154-
binding->native.region.len = region.len;
155-
aws_byte_buf_write_from_whole_cursor(&binding->string_storage, region);
156-
157-
binding->native.service.ptr = binding->string_storage.buffer + binding->string_storage.len;
158-
binding->native.service.len = service.len;
159-
aws_byte_buf_write_from_whole_cursor(&binding->string_storage, service);
160-
161155
/* date: store original datetime python object so user doesn't see different timezones after set/get */
162156
aws_date_time_init_epoch_secs(&binding->native.date, timestamp);
163157
binding->py_date = py_date;
@@ -275,13 +269,19 @@ PyObject *aws_py_signing_config_get_should_normalize_uri_path(PyObject *self, Py
275269
return PyBool_FromLong(binding->native.flags.should_normalize_uri_path);
276270
}
277271

278-
PyObject *aws_py_signing_config_get_signed_body_value_type(PyObject *self, PyObject *args) {
272+
PyObject *aws_py_signing_config_get_signed_body_value(PyObject *self, PyObject *args) {
279273
struct config_binding *binding = s_common_get(self, args);
280274
if (!binding) {
281275
return NULL;
282276
}
283277

284-
return PyLong_FromLong(binding->native.signed_body_value);
278+
/* In C layer, 0 length cursor indicates "defaults please".
279+
* In Python layer, None indicates "defaults please". */
280+
if (binding->native.signed_body_value.len == 0) {
281+
Py_RETURN_NONE;
282+
}
283+
284+
return PyString_FromAwsByteCursor(&binding->native.signed_body_value);
285285
}
286286

287287
PyObject *aws_py_signing_config_get_signed_body_header_type(PyObject *self, PyObject *args) {

source/module.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ static PyMethodDef s_module_methods[] = {
554554
AWS_PY_METHOD_DEF(signing_config_get_date, METH_VARARGS),
555555
AWS_PY_METHOD_DEF(signing_config_get_use_double_uri_encode, METH_VARARGS),
556556
AWS_PY_METHOD_DEF(signing_config_get_should_normalize_uri_path, METH_VARARGS),
557-
AWS_PY_METHOD_DEF(signing_config_get_signed_body_value_type, METH_VARARGS),
557+
AWS_PY_METHOD_DEF(signing_config_get_signed_body_value, METH_VARARGS),
558558
AWS_PY_METHOD_DEF(signing_config_get_signed_body_header_type, METH_VARARGS),
559559
AWS_PY_METHOD_DEF(signing_config_get_expiration_in_seconds, METH_VARARGS),
560560
AWS_PY_METHOD_DEF(signing_config_get_omit_session_token, METH_VARARGS),

0 commit comments

Comments
 (0)