12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669 |
- /**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
- #include <aws/auth/private/aws_signing.h>
- #include <aws/auth/credentials.h>
- #include <aws/auth/private/key_derivation.h>
- #include <aws/auth/signable.h>
- #include <aws/auth/signing.h>
- #include <aws/cal/ecc.h>
- #include <aws/cal/hash.h>
- #include <aws/cal/hmac.h>
- #include <aws/common/date_time.h>
- #include <aws/common/encoding.h>
- #include <aws/common/string.h>
- #include <aws/io/stream.h>
- #include <aws/io/uri.h>
- #include <ctype.h>
- #include <inttypes.h>
- #if defined(_MSC_VER)
- # pragma warning(disable : 4204)
- #endif /* _MSC_VER */
- /*
- * A bunch of initial size values for various buffers used throughout the signing process
- *
- * We want them to be sufficient-but-not-wasting-significant-amounts-of-memory for "most"
- * requests. The body read buffer is an exception since it will just be holding windows rather than
- * the entire thing.
- */
- #define BODY_READ_BUFFER_SIZE 4096
- #define CANONICAL_REQUEST_STARTING_SIZE 1024
- #define STRING_TO_SIGN_STARTING_SIZE 256
- #define SIGNED_HEADERS_STARTING_SIZE 256
- #define CANONICAL_HEADER_BLOCK_STARTING_SIZE 1024
- #define AUTHORIZATION_VALUE_STARTING_SIZE 512
- #define PAYLOAD_HASH_STARTING_SIZE (AWS_SHA256_LEN * 2)
- #define CREDENTIAL_SCOPE_STARTING_SIZE 128
- #define ACCESS_CREDENTIAL_SCOPE_STARTING_SIZE 149
- #define SCRATCH_BUF_STARTING_SIZE 256
- #define MAX_AUTHORIZATION_HEADER_COUNT 4
- #define MAX_AUTHORIZATION_QUERY_PARAM_COUNT 6
- #define DEFAULT_PATH_COMPONENT_COUNT 10
- #define CANONICAL_REQUEST_SPLIT_OVER_ESTIMATE 20
- #define HEX_ENCODED_SIGNATURE_OVER_ESTIMATE 256
- #define MAX_ECDSA_P256_SIGNATURE_AS_BINARY_LENGTH 72
- #define MAX_ECDSA_P256_SIGNATURE_AS_HEX_LENGTH (MAX_ECDSA_P256_SIGNATURE_AS_BINARY_LENGTH * 2)
- #define AWS_SIGV4A_SIGNATURE_PADDING_BYTE ('*')
- AWS_STRING_FROM_LITERAL(g_aws_signing_content_header_name, "x-amz-content-sha256");
- AWS_STRING_FROM_LITERAL(g_aws_signing_authorization_header_name, "Authorization");
- AWS_STRING_FROM_LITERAL(g_aws_signing_authorization_query_param_name, "X-Amz-Signature");
- AWS_STRING_FROM_LITERAL(g_aws_signing_algorithm_query_param_name, "X-Amz-Algorithm");
- AWS_STRING_FROM_LITERAL(g_aws_signing_credential_query_param_name, "X-Amz-Credential");
- AWS_STRING_FROM_LITERAL(g_aws_signing_date_name, "X-Amz-Date");
- AWS_STRING_FROM_LITERAL(g_aws_signing_signed_headers_query_param_name, "X-Amz-SignedHeaders");
- AWS_STRING_FROM_LITERAL(g_aws_signing_security_token_name, "X-Amz-Security-Token");
- AWS_STRING_FROM_LITERAL(g_aws_signing_expires_query_param_name, "X-Amz-Expires");
- AWS_STRING_FROM_LITERAL(g_aws_signing_region_set_name, "X-Amz-Region-Set");
- AWS_STATIC_STRING_FROM_LITERAL(s_signature_type_sigv4_http_request, "AWS4-HMAC-SHA256");
- 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;
- static struct aws_hash_table s_skipped_headers;
- static struct aws_byte_cursor s_amzn_trace_id_header_name;
- static struct aws_byte_cursor s_user_agent_header_name;
- static struct aws_byte_cursor s_connection_header_name;
- static struct aws_byte_cursor s_sec_websocket_key_header_name;
- static struct aws_byte_cursor s_sec_websocket_protocol_header_name;
- static struct aws_byte_cursor s_sec_websocket_version_header_name;
- static struct aws_byte_cursor s_upgrade_header_name;
- static struct aws_byte_cursor s_amz_content_sha256_header_name;
- static struct aws_byte_cursor s_amz_date_header_name;
- static struct aws_byte_cursor s_authorization_header_name;
- static struct aws_byte_cursor s_region_set_header_name;
- static struct aws_byte_cursor s_amz_security_token_header_name;
- static struct aws_byte_cursor s_amz_signature_param_name;
- static struct aws_byte_cursor s_amz_date_param_name;
- static struct aws_byte_cursor s_amz_credential_param_name;
- static struct aws_byte_cursor s_amz_algorithm_param_name;
- static struct aws_byte_cursor s_amz_signed_headers_param_name;
- static struct aws_byte_cursor s_amz_security_token_param_name;
- static struct aws_byte_cursor s_amz_expires_param_name;
- static struct aws_byte_cursor s_amz_region_set_param_name;
- /*
- * Build a set of library-static tables for quick lookup.
- *
- * Construction errors are considered fatal.
- */
- int aws_signing_init_signing_tables(struct aws_allocator *allocator) {
- if (aws_hash_table_init(
- &s_skipped_headers,
- allocator,
- 10,
- aws_hash_byte_cursor_ptr_ignore_case,
- (aws_hash_callback_eq_fn *)aws_byte_cursor_eq_ignore_case,
- NULL,
- NULL)) {
- return AWS_OP_ERR;
- }
- s_amzn_trace_id_header_name = aws_byte_cursor_from_c_str("x-amzn-trace-id");
- if (aws_hash_table_put(&s_skipped_headers, &s_amzn_trace_id_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_user_agent_header_name = aws_byte_cursor_from_c_str("User-Agent");
- if (aws_hash_table_put(&s_skipped_headers, &s_user_agent_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_connection_header_name = aws_byte_cursor_from_c_str("connection");
- if (aws_hash_table_put(&s_skipped_headers, &s_connection_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_connection_header_name = aws_byte_cursor_from_c_str("expect");
- if (aws_hash_table_put(&s_skipped_headers, &s_connection_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_sec_websocket_key_header_name = aws_byte_cursor_from_c_str("sec-websocket-key");
- if (aws_hash_table_put(&s_skipped_headers, &s_sec_websocket_key_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_sec_websocket_protocol_header_name = aws_byte_cursor_from_c_str("sec-websocket-protocol");
- if (aws_hash_table_put(&s_skipped_headers, &s_sec_websocket_protocol_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_sec_websocket_version_header_name = aws_byte_cursor_from_c_str("sec-websocket-version");
- if (aws_hash_table_put(&s_skipped_headers, &s_sec_websocket_version_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_upgrade_header_name = aws_byte_cursor_from_c_str("upgrade");
- if (aws_hash_table_put(&s_skipped_headers, &s_upgrade_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- if (aws_hash_table_init(
- &s_forbidden_headers,
- allocator,
- 10,
- aws_hash_byte_cursor_ptr_ignore_case,
- (aws_hash_callback_eq_fn *)aws_byte_cursor_eq_ignore_case,
- NULL,
- NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_content_sha256_header_name = aws_byte_cursor_from_string(g_aws_signing_content_header_name);
- if (aws_hash_table_put(&s_forbidden_headers, &s_amz_content_sha256_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_date_header_name = aws_byte_cursor_from_string(g_aws_signing_date_name);
- if (aws_hash_table_put(&s_forbidden_headers, &s_amz_date_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_authorization_header_name = aws_byte_cursor_from_string(g_aws_signing_authorization_header_name);
- if (aws_hash_table_put(&s_forbidden_headers, &s_authorization_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_region_set_header_name = aws_byte_cursor_from_string(g_aws_signing_region_set_name);
- if (aws_hash_table_put(&s_forbidden_headers, &s_region_set_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_security_token_header_name = aws_byte_cursor_from_string(g_aws_signing_security_token_name);
- if (aws_hash_table_put(&s_forbidden_headers, &s_amz_security_token_header_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- if (aws_hash_table_init(
- &s_forbidden_params,
- allocator,
- 10,
- aws_hash_byte_cursor_ptr_ignore_case,
- (aws_hash_callback_eq_fn *)aws_byte_cursor_eq_ignore_case,
- NULL,
- NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_signature_param_name = aws_byte_cursor_from_string(g_aws_signing_authorization_query_param_name);
- if (aws_hash_table_put(&s_forbidden_params, &s_amz_signature_param_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_date_param_name = aws_byte_cursor_from_string(g_aws_signing_date_name);
- if (aws_hash_table_put(&s_forbidden_params, &s_amz_date_param_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_credential_param_name = aws_byte_cursor_from_string(g_aws_signing_credential_query_param_name);
- if (aws_hash_table_put(&s_forbidden_params, &s_amz_credential_param_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_algorithm_param_name = aws_byte_cursor_from_string(g_aws_signing_algorithm_query_param_name);
- if (aws_hash_table_put(&s_forbidden_params, &s_amz_algorithm_param_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_signed_headers_param_name = aws_byte_cursor_from_string(g_aws_signing_signed_headers_query_param_name);
- if (aws_hash_table_put(&s_forbidden_params, &s_amz_signed_headers_param_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_security_token_param_name = aws_byte_cursor_from_string(g_aws_signing_security_token_name);
- if (aws_hash_table_put(&s_forbidden_params, &s_amz_security_token_param_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_expires_param_name = aws_byte_cursor_from_string(g_aws_signing_expires_query_param_name);
- if (aws_hash_table_put(&s_forbidden_params, &s_amz_expires_param_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- s_amz_region_set_param_name = aws_byte_cursor_from_string(g_aws_signing_region_set_name);
- if (aws_hash_table_put(&s_forbidden_params, &s_amz_region_set_param_name, NULL, NULL)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- void aws_signing_clean_up_signing_tables(void) {
- aws_hash_table_clean_up(&s_skipped_headers);
- aws_hash_table_clean_up(&s_forbidden_headers);
- aws_hash_table_clean_up(&s_forbidden_params);
- }
- static bool s_is_header_based_signature_value(enum aws_signature_type signature_type) {
- switch (signature_type) {
- case AWS_ST_HTTP_REQUEST_HEADERS:
- case AWS_ST_CANONICAL_REQUEST_HEADERS:
- return true;
- default:
- return false;
- }
- }
- static bool s_is_query_param_based_signature_value(enum aws_signature_type signature_type) {
- switch (signature_type) {
- case AWS_ST_HTTP_REQUEST_QUERY_PARAMS:
- case AWS_ST_CANONICAL_REQUEST_QUERY_PARAMS:
- return true;
- default:
- return false;
- }
- }
- static int s_get_signature_type_cursor(struct aws_signing_state_aws *state, struct aws_byte_cursor *cursor) {
- switch (state->config.signature_type) {
- case AWS_ST_HTTP_REQUEST_HEADERS:
- case AWS_ST_HTTP_REQUEST_QUERY_PARAMS:
- case AWS_ST_CANONICAL_REQUEST_HEADERS:
- case AWS_ST_CANONICAL_REQUEST_QUERY_PARAMS:
- if (state->config.algorithm == AWS_SIGNING_ALGORITHM_V4) {
- *cursor = aws_byte_cursor_from_string(s_signature_type_sigv4_http_request);
- } else {
- *cursor = aws_byte_cursor_from_string(g_signature_type_sigv4a_http_request);
- }
- break;
- case AWS_ST_HTTP_REQUEST_CHUNK:
- case AWS_ST_HTTP_REQUEST_EVENT:
- if (state->config.algorithm == AWS_SIGNING_ALGORITHM_V4) {
- *cursor = aws_byte_cursor_from_string(s_signature_type_sigv4_s3_chunked_payload);
- } else {
- *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);
- }
- return AWS_OP_SUCCESS;
- }
- static int s_append_sts_signature_type(struct aws_signing_state_aws *state, struct aws_byte_buf *dest) {
- struct aws_byte_cursor algorithm_cursor;
- if (s_get_signature_type_cursor(state, &algorithm_cursor)) {
- return AWS_OP_ERR;
- }
- return aws_byte_buf_append_dynamic(dest, &algorithm_cursor);
- }
- /*
- * signing state management
- */
- struct aws_signing_state_aws *aws_signing_state_new(
- struct aws_allocator *allocator,
- const struct aws_signing_config_aws *config,
- const struct aws_signable *signable,
- aws_signing_complete_fn *on_complete,
- void *userdata) {
- if (aws_validate_aws_signing_config_aws(config)) {
- return NULL;
- }
- struct aws_signing_state_aws *state = aws_mem_calloc(allocator, 1, sizeof(struct aws_signing_state_aws));
- if (!state) {
- return NULL;
- }
- state->allocator = allocator;
- /* Make our own copy of the signing config */
- state->config = *config;
- if (state->config.credentials_provider != NULL) {
- aws_credentials_provider_acquire(state->config.credentials_provider);
- }
- if (state->config.credentials != NULL) {
- aws_credentials_acquire(state->config.credentials);
- }
- if (aws_byte_buf_init_cache_and_update_cursors(
- &state->config_string_buffer,
- allocator,
- &state->config.region,
- &state->config.service,
- &state->config.signed_body_value,
- NULL /*end*/)) {
- goto on_error;
- }
- state->signable = signable;
- state->on_complete = on_complete;
- state->userdata = userdata;
- if (aws_signing_result_init(&state->result, allocator)) {
- goto on_error;
- }
- if (aws_byte_buf_init(&state->canonical_request, allocator, CANONICAL_REQUEST_STARTING_SIZE) ||
- aws_byte_buf_init(&state->string_to_sign, allocator, STRING_TO_SIGN_STARTING_SIZE) ||
- aws_byte_buf_init(&state->signed_headers, allocator, SIGNED_HEADERS_STARTING_SIZE) ||
- aws_byte_buf_init(&state->canonical_header_block, allocator, CANONICAL_HEADER_BLOCK_STARTING_SIZE) ||
- aws_byte_buf_init(&state->payload_hash, allocator, PAYLOAD_HASH_STARTING_SIZE) ||
- aws_byte_buf_init(&state->credential_scope, allocator, CREDENTIAL_SCOPE_STARTING_SIZE) ||
- aws_byte_buf_init(&state->access_credential_scope, allocator, ACCESS_CREDENTIAL_SCOPE_STARTING_SIZE) ||
- aws_byte_buf_init(&state->date, allocator, AWS_DATE_TIME_STR_MAX_LEN) ||
- aws_byte_buf_init(&state->signature, allocator, PAYLOAD_HASH_STARTING_SIZE) ||
- aws_byte_buf_init(&state->string_to_sign_payload, allocator, PAYLOAD_HASH_STARTING_SIZE) ||
- aws_byte_buf_init(&state->scratch_buf, allocator, SCRATCH_BUF_STARTING_SIZE)) {
- goto on_error;
- }
- snprintf(
- state->expiration_array, AWS_ARRAY_SIZE(state->expiration_array), "%" PRIu64 "", config->expiration_in_seconds);
- return state;
- on_error:
- aws_signing_state_destroy(state);
- return NULL;
- }
- void aws_signing_state_destroy(struct aws_signing_state_aws *state) {
- aws_signing_result_clean_up(&state->result);
- aws_credentials_provider_release(state->config.credentials_provider);
- aws_credentials_release(state->config.credentials);
- aws_byte_buf_clean_up(&state->config_string_buffer);
- aws_byte_buf_clean_up(&state->canonical_request);
- aws_byte_buf_clean_up(&state->string_to_sign);
- aws_byte_buf_clean_up(&state->signed_headers);
- aws_byte_buf_clean_up(&state->canonical_header_block);
- aws_byte_buf_clean_up(&state->payload_hash);
- aws_byte_buf_clean_up(&state->credential_scope);
- aws_byte_buf_clean_up(&state->access_credential_scope);
- aws_byte_buf_clean_up(&state->date);
- aws_byte_buf_clean_up(&state->signature);
- aws_byte_buf_clean_up(&state->string_to_sign_payload);
- aws_byte_buf_clean_up(&state->scratch_buf);
- aws_mem_release(state->allocator, state);
- }
- /*
- * canonical request utility functions:
- *
- * various appends, conversion/encoding, etc...
- *
- */
- static int s_append_canonical_method(struct aws_signing_state_aws *state) {
- const struct aws_signable *signable = state->signable;
- struct aws_byte_buf *buffer = &state->canonical_request;
- struct aws_byte_cursor method_cursor;
- aws_signable_get_property(signable, g_aws_http_method_property_name, &method_cursor);
- if (aws_byte_buf_append_dynamic(buffer, &method_cursor)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(buffer, '\n')) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- static int s_append_with_lookup(
- struct aws_byte_buf *dst,
- const struct aws_byte_cursor *src,
- const uint8_t *lookup_table) {
- if (aws_byte_buf_reserve_relative(dst, src->len)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_with_lookup(dst, src, lookup_table)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /*
- * A function that builds a normalized path (removes redundant '/' characters, '.' components, and properly pops off
- * components in response '..' components)
- *
- * We use a simple algorithm to do this:
- *
- * First split the path into components
- * Then, using a secondary stack of components, build the final path by pushing and popping (on '..') components
- * on the stack. The final path is then the concatenation of the secondary stack.
- */
- static int s_append_normalized_path(
- const struct aws_byte_cursor *raw_path,
- struct aws_allocator *allocator,
- struct aws_byte_buf *dest) {
- struct aws_array_list raw_split;
- AWS_ZERO_STRUCT(raw_split);
- struct aws_array_list normalized_split;
- AWS_ZERO_STRUCT(normalized_split);
- int result = AWS_OP_ERR;
- if (aws_array_list_init_dynamic(
- &raw_split, allocator, DEFAULT_PATH_COMPONENT_COUNT, sizeof(struct aws_byte_cursor))) {
- goto cleanup;
- }
- if (aws_byte_cursor_split_on_char(raw_path, '/', &raw_split)) {
- goto cleanup;
- }
- const size_t raw_split_count = aws_array_list_length(&raw_split);
- if (aws_array_list_init_dynamic(&normalized_split, allocator, raw_split_count, sizeof(struct aws_byte_cursor))) {
- goto cleanup;
- }
- /*
- * Iterate the raw split to build a list of path components that make up the
- * normalized path
- */
- for (size_t i = 0; i < raw_split_count; ++i) {
- struct aws_byte_cursor path_component;
- AWS_ZERO_STRUCT(path_component);
- if (aws_array_list_get_at(&raw_split, &path_component, i)) {
- goto cleanup;
- }
- if (path_component.len == 0 || (path_component.len == 1 && *path_component.ptr == '.')) {
- /* '.' and '' contribute nothing to a normalized path */
- continue;
- }
- if (path_component.len == 2 && *path_component.ptr == '.' && *(path_component.ptr + 1) == '.') {
- /* '..' causes us to remove the last valid path component */
- aws_array_list_pop_back(&normalized_split);
- } else {
- aws_array_list_push_back(&normalized_split, &path_component);
- }
- }
- /*
- * Special case preserve whether or not the path ended with a '/'
- */
- bool ends_with_slash = raw_path->len > 0 && raw_path->ptr[raw_path->len - 1] == '/';
- /*
- * Paths always start with a single '/'
- */
- if (aws_byte_buf_append_byte_dynamic(dest, '/')) {
- goto cleanup;
- }
- /*
- * build the final normalized path from the normalized split by joining
- * the components together with '/'
- */
- const size_t normalized_split_count = aws_array_list_length(&normalized_split);
- for (size_t i = 0; i < normalized_split_count; ++i) {
- struct aws_byte_cursor normalized_path_component;
- AWS_ZERO_STRUCT(normalized_path_component);
- if (aws_array_list_get_at(&normalized_split, &normalized_path_component, i)) {
- goto cleanup;
- }
- if (aws_byte_buf_append_dynamic(dest, &normalized_path_component)) {
- goto cleanup;
- }
- if (i + 1 < normalized_split_count || ends_with_slash) {
- if (aws_byte_buf_append_byte_dynamic(dest, '/')) {
- goto cleanup;
- }
- }
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_array_list_clean_up(&raw_split);
- aws_array_list_clean_up(&normalized_split);
- return result;
- }
- static int s_append_canonical_path(const struct aws_uri *uri, struct aws_signing_state_aws *state) {
- const struct aws_signing_config_aws *config = &state->config;
- struct aws_byte_buf *canonical_request_buffer = &state->canonical_request;
- struct aws_allocator *allocator = state->allocator;
- int result = AWS_OP_ERR;
- /*
- * Put this at function global scope so that it gets cleaned up even though it's only used inside
- * a single branch. Allows error handling and cleanup to follow the pattern established
- * throughout this file.
- */
- struct aws_byte_buf normalized_path;
- AWS_ZERO_STRUCT(normalized_path);
- /*
- * We assume the request's uri path has already been encoded once (in order to go out on the wire).
- * Some services do not decode the path before performing the sig v4 calculation, resulting in the
- * service actually performing sigv4 on a double-encoding of the path. In order to match those
- * services, we must double encode in our calculation as well.
- */
- if (config->flags.use_double_uri_encode) {
- struct aws_byte_cursor path_cursor;
- /*
- * We need to transform the the normalized path, so we can't just append it into the canonical
- * request. Instead we append it into a temporary buffer and perform the transformation from
- * it.
- *
- * All this does is skip the temporary normalized path in the case where we don't need to
- * double encode.
- */
- if (config->flags.should_normalize_uri_path) {
- if (aws_byte_buf_init(&normalized_path, state->allocator, uri->path.len)) {
- goto cleanup;
- }
- if (s_append_normalized_path(&uri->path, allocator, &normalized_path)) {
- goto cleanup;
- }
- path_cursor = aws_byte_cursor_from_buf(&normalized_path);
- } else {
- path_cursor = uri->path;
- }
- if (aws_byte_buf_append_encoding_uri_path(canonical_request_buffer, &path_cursor)) {
- goto cleanup;
- }
- } else {
- /*
- * If we don't need to perform any kind of transformation on the normalized path, just append it directly
- * into the canonical request buffer
- */
- if (config->flags.should_normalize_uri_path) {
- if (s_append_normalized_path(&uri->path, allocator, canonical_request_buffer)) {
- goto cleanup;
- }
- } else {
- if (aws_byte_buf_append_dynamic(canonical_request_buffer, &uri->path)) {
- goto cleanup;
- }
- }
- }
- if (aws_byte_buf_append_byte_dynamic(canonical_request_buffer, '\n')) {
- goto cleanup;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_byte_buf_clean_up(&normalized_path);
- return result;
- }
- /*
- * URI-encoded query params are compared first by key, then by value
- */
- int s_canonical_query_param_comparator(const void *lhs, const void *rhs) {
- const struct aws_uri_param *left_param = lhs;
- const struct aws_uri_param *right_param = rhs;
- int key_compare = aws_byte_cursor_compare_lexical(&left_param->key, &right_param->key);
- if (key_compare != 0) {
- return key_compare;
- }
- return aws_byte_cursor_compare_lexical(&left_param->value, &right_param->value);
- }
- /*
- * We need to sort the headers in a stable fashion, but the default sorting methods available in the c library are not
- * guaranteed to be stable. We can make the sort stable by instead sorting a wrapper object that includes the original
- * index of the wrapped object and using that index to break lexical ties.
- *
- * We sort a copy of the header (rather than pointers) so that we can easily inject secondary headers into
- * the canonical request.
- */
- struct stable_header {
- struct aws_signable_property_list_pair header;
- size_t original_index;
- };
- int s_canonical_header_comparator(const void *lhs, const void *rhs) {
- const struct stable_header *left_header = lhs;
- const struct stable_header *right_header = rhs;
- int result = aws_byte_cursor_compare_lookup(
- &left_header->header.name, &right_header->header.name, aws_lookup_table_to_lower_get());
- if (result != 0) {
- return result;
- }
- /* they're the same header, use the original index to keep the sort stable */
- if (left_header->original_index < right_header->original_index) {
- return -1;
- }
- /* equality should never happen */
- AWS_ASSERT(left_header->original_index > right_header->original_index);
- return 1;
- }
- /**
- * Given URI-encoded query param, write it to canonical buffer.
- */
- static int s_append_canonical_query_param(struct aws_uri_param *encoded_param, struct aws_byte_buf *buffer) {
- if (aws_byte_buf_append_dynamic(buffer, &encoded_param->key)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(buffer, '=')) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_dynamic(buffer, &encoded_param->value)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /**
- * Given unencoded authorization query param:
- * Add it, URI-encoded to final signing result (to be added to signable later).
- */
- static int s_add_query_param_to_signing_result(
- struct aws_signing_state_aws *state,
- const struct aws_uri_param *unencoded_param) {
- /* URI-Encode, and add to final signing result */
- state->scratch_buf.len = 0;
- if (aws_byte_buf_append_encoding_uri_param(&state->scratch_buf, &unencoded_param->key)) {
- return AWS_OP_ERR;
- }
- size_t key_len = state->scratch_buf.len;
- if (aws_byte_buf_append_encoding_uri_param(&state->scratch_buf, &unencoded_param->value)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor encoded_val = aws_byte_cursor_from_buf(&state->scratch_buf);
- struct aws_byte_cursor encoded_key = aws_byte_cursor_advance(&encoded_val, key_len);
- if (aws_signing_result_append_property_list(
- &state->result, g_aws_http_query_params_property_list_name, &encoded_key, &encoded_val)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /**
- * Given unencoded authorization query param:
- * 1) Add it to list of all unencoded query params (to be canonicalized later).
- * 2) Add it, URI-encoded to final signing result (to be added to signable later).
- */
- static int s_add_authorization_query_param(
- struct aws_signing_state_aws *state,
- struct aws_array_list *unencoded_auth_params,
- const struct aws_uri_param *unencoded_auth_param) {
- /* Add to unencoded list */
- if (aws_array_list_push_back(unencoded_auth_params, unencoded_auth_param)) {
- return AWS_OP_ERR;
- }
- return s_add_query_param_to_signing_result(state, unencoded_auth_param);
- }
- /*
- * Checks the header against both an internal skip list as well as an optional user-supplied filter
- * function. Only sign the header if both functions allow it.
- */
- static bool s_should_sign_header(struct aws_signing_state_aws *state, struct aws_byte_cursor *name) {
- if (state->config.should_sign_header) {
- if (!state->config.should_sign_header(name, state->config.should_sign_header_ud)) {
- return false;
- }
- }
- struct aws_hash_element *element = NULL;
- if (aws_hash_table_find(&s_skipped_headers, name, &element) == AWS_OP_ERR || element != NULL) {
- return false;
- }
- return true;
- }
- /*
- * If the auth type was query param then this function adds all the required query params and values with the
- * exception of X-Amz-Signature (because we're still computing its value) Parameters are added to both the
- * canonical request and the final signing result.
- */
- static int s_add_authorization_query_params(
- struct aws_signing_state_aws *state,
- struct aws_array_list *unencoded_query_params) {
- if (state->config.signature_type != AWS_ST_HTTP_REQUEST_QUERY_PARAMS) {
- return AWS_OP_SUCCESS;
- }
- int result = AWS_OP_ERR;
- /* X-Amz-Algorithm */
- struct aws_uri_param algorithm_param = {
- .key = aws_byte_cursor_from_string(g_aws_signing_algorithm_query_param_name),
- };
- if (s_get_signature_type_cursor(state, &algorithm_param.value)) {
- goto done;
- }
- if (s_add_authorization_query_param(state, unencoded_query_params, &algorithm_param)) {
- goto done;
- }
- /* X-Amz-Credential */
- struct aws_uri_param credential_param = {
- .key = aws_byte_cursor_from_string(g_aws_signing_credential_query_param_name),
- .value = aws_byte_cursor_from_buf(&state->access_credential_scope),
- };
- if (s_add_authorization_query_param(state, unencoded_query_params, &credential_param)) {
- goto done;
- }
- /* X-Amz-Date */
- struct aws_uri_param date_param = {
- .key = aws_byte_cursor_from_string(g_aws_signing_date_name),
- .value = aws_byte_cursor_from_buf(&state->date),
- };
- if (s_add_authorization_query_param(state, unencoded_query_params, &date_param)) {
- goto done;
- }
- /* X-Amz-SignedHeaders */
- struct aws_uri_param signed_headers_param = {
- .key = aws_byte_cursor_from_string(g_aws_signing_signed_headers_query_param_name),
- .value = aws_byte_cursor_from_buf(&state->signed_headers),
- };
- if (s_add_authorization_query_param(state, unencoded_query_params, &signed_headers_param)) {
- goto done;
- }
- /* X-Amz-Expires */
- uint64_t expiration_in_seconds = state->config.expiration_in_seconds;
- if (expiration_in_seconds > 0) {
- struct aws_uri_param expires_param = {
- .key = aws_byte_cursor_from_string(g_aws_signing_expires_query_param_name),
- .value = aws_byte_cursor_from_c_str(state->expiration_array),
- };
- if (s_add_authorization_query_param(state, unencoded_query_params, &expires_param)) {
- goto done;
- }
- }
- /* X-Amz-Security-token */
- struct aws_byte_cursor security_token_name_cur = aws_byte_cursor_from_string(g_aws_signing_security_token_name);
- struct aws_byte_cursor session_token_cursor = aws_credentials_get_session_token(state->config.credentials);
- if (session_token_cursor.len > 0) {
- struct aws_uri_param security_token_param = {
- .key = security_token_name_cur,
- .value = session_token_cursor,
- };
- /* If omit_session_token is true, then security token is added to the
- * final signing result, but is treated as "unsigned" and does not
- * contribute to the authorization signature */
- if (state->config.flags.omit_session_token) {
- if (s_add_query_param_to_signing_result(state, &security_token_param)) {
- goto done;
- }
- } else {
- if (s_add_authorization_query_param(state, unencoded_query_params, &security_token_param)) {
- goto done;
- }
- }
- }
- /* X-Amz-Region-Set */
- if (state->config.algorithm == AWS_SIGNING_ALGORITHM_V4_ASYMMETRIC) {
- struct aws_uri_param region_set_param = {
- .key = aws_byte_cursor_from_string(g_aws_signing_region_set_name),
- .value = state->config.region,
- };
- if (s_add_authorization_query_param(state, unencoded_query_params, ®ion_set_param)) {
- goto done;
- }
- }
- /* NOTE: Update MAX_AUTHORIZATION_QUERY_PARAM_COUNT if more params added */
- result = AWS_OP_SUCCESS;
- done:
- return result;
- }
- static int s_validate_query_params(struct aws_array_list *unencoded_query_params) {
- const size_t param_count = aws_array_list_length(unencoded_query_params);
- for (size_t i = 0; i < param_count; ++i) {
- struct aws_uri_param param;
- AWS_ZERO_STRUCT(param);
- aws_array_list_get_at(unencoded_query_params, ¶m, i);
- struct aws_hash_element *forbidden_element = NULL;
- aws_hash_table_find(&s_forbidden_params, ¶m.key, &forbidden_element);
- if (forbidden_element != NULL) {
- AWS_LOGF_ERROR(
- AWS_LS_AUTH_SIGNING,
- "AWS authorization query param \"" PRInSTR "\" found in request while signing",
- AWS_BYTE_CURSOR_PRI(param.key));
- return aws_raise_error(AWS_AUTH_SIGNING_ILLEGAL_REQUEST_QUERY_PARAM);
- }
- }
- return AWS_OP_SUCCESS;
- }
- /**
- * Apply or remove URI-encoding to each aws_uri_param in a list.
- * (new strings are added to temp_strings)
- * Append function must grow buffer if necessary.
- */
- static int s_transform_query_params(
- struct aws_signing_state_aws *state,
- struct aws_array_list *param_list,
- struct aws_array_list *temp_strings,
- int (*byte_buf_append_dynamic_param_fn)(struct aws_byte_buf *, const struct aws_byte_cursor *)) {
- const size_t param_count = aws_array_list_length(param_list);
- struct aws_uri_param *param = NULL;
- for (size_t i = 0; i < param_count; ++i) {
- aws_array_list_get_at_ptr(param_list, (void **)¶m, i);
- /* encode/decode key and save string */
- state->scratch_buf.len = 0;
- if (byte_buf_append_dynamic_param_fn(&state->scratch_buf, ¶m->key)) {
- return AWS_OP_ERR;
- }
- struct aws_string *key_str = aws_string_new_from_buf(state->allocator, &state->scratch_buf);
- if (!key_str) {
- return AWS_OP_ERR;
- }
- if (aws_array_list_push_back(temp_strings, &key_str)) {
- aws_string_destroy(key_str);
- return AWS_OP_ERR;
- }
- /* encode/decode value and save string */
- state->scratch_buf.len = 0;
- if (byte_buf_append_dynamic_param_fn(&state->scratch_buf, ¶m->value)) {
- return AWS_OP_ERR;
- }
- struct aws_string *value_str = aws_string_new_from_buf(state->allocator, &state->scratch_buf);
- if (!value_str) {
- return AWS_OP_ERR;
- }
- if (aws_array_list_push_back(temp_strings, &value_str)) {
- aws_string_destroy(value_str);
- return AWS_OP_ERR;
- }
- /* save encoded/decoded param */
- param->key = aws_byte_cursor_from_string(key_str);
- param->value = aws_byte_cursor_from_string(value_str);
- }
- return AWS_OP_SUCCESS;
- }
- /*
- * Adds the full canonical query string to the canonical request.
- * Note that aws-c-auth takes query params from the URI, so they should already be URI-encoded.
- * To ensure that the signature uses "canonical" URI-encoding, we decode and then re-encode the params.
- */
- static int s_append_canonical_query_string(struct aws_uri *uri, struct aws_signing_state_aws *state) {
- struct aws_allocator *allocator = state->allocator;
- struct aws_byte_buf *canonical_request_buffer = &state->canonical_request;
- int result = AWS_OP_ERR;
- struct aws_array_list query_params;
- AWS_ZERO_STRUCT(query_params);
- struct aws_array_list temp_strings;
- AWS_ZERO_STRUCT(temp_strings);
- /* Determine max number of query parameters.
- * If none, skip to end of function */
- size_t max_param_count = 0;
- struct aws_uri_param param_i;
- AWS_ZERO_STRUCT(param_i);
- while (aws_uri_query_string_next_param(uri, ¶m_i)) {
- ++max_param_count;
- }
- if (state->config.signature_type == AWS_ST_HTTP_REQUEST_QUERY_PARAMS) {
- max_param_count += MAX_AUTHORIZATION_QUERY_PARAM_COUNT;
- }
- if (max_param_count == 0) {
- goto finish;
- }
- /* Allocate storage for mutable list of query params */
- if (aws_array_list_init_dynamic(&query_params, allocator, max_param_count, sizeof(struct aws_uri_param))) {
- goto cleanup;
- }
- /* Allocate storage for both the decoded, and re-encoded, key and value strings */
- if (aws_array_list_init_dynamic(
- &temp_strings, state->allocator, max_param_count * 4, sizeof(struct aws_string *))) {
- goto cleanup;
- }
- /* Get existing query params */
- if (aws_uri_query_string_params(uri, &query_params)) {
- goto cleanup;
- }
- /* Remove URI-encoding */
- if (s_transform_query_params(state, &query_params, &temp_strings, aws_byte_buf_append_decoding_uri)) {
- goto cleanup;
- }
- /* Validate existing query params */
- if (s_validate_query_params(&query_params)) {
- goto cleanup;
- }
- /* Add authorization query params */
- if (s_add_authorization_query_params(state, &query_params)) {
- goto cleanup;
- }
- /* Apply canonical URI-encoding to the query params */
- if (s_transform_query_params(state, &query_params, &temp_strings, aws_byte_buf_append_encoding_uri_param)) {
- goto cleanup;
- }
- const size_t param_count = aws_array_list_length(&query_params);
- /* Sort the encoded params and append to canonical request */
- qsort(query_params.data, param_count, sizeof(struct aws_uri_param), s_canonical_query_param_comparator);
- for (size_t i = 0; i < param_count; ++i) {
- struct aws_uri_param param;
- AWS_ZERO_STRUCT(param);
- if (aws_array_list_get_at(&query_params, ¶m, i)) {
- goto cleanup;
- }
- if (s_append_canonical_query_param(¶m, canonical_request_buffer)) {
- goto cleanup;
- }
- if (i + 1 < param_count) {
- if (aws_byte_buf_append_byte_dynamic(canonical_request_buffer, '&')) {
- goto cleanup;
- }
- }
- }
- finish:
- if (aws_byte_buf_append_byte_dynamic(canonical_request_buffer, '\n')) {
- goto cleanup;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_array_list_clean_up(&query_params);
- if (aws_array_list_is_valid(&temp_strings)) {
- const size_t string_count = aws_array_list_length(&temp_strings);
- for (size_t i = 0; i < string_count; ++i) {
- struct aws_string *string = NULL;
- aws_array_list_get_at(&temp_strings, &string, i);
- aws_string_destroy(string);
- }
- aws_array_list_clean_up(&temp_strings);
- }
- return result;
- }
- /*
- * It is unclear from the spec (and not resolved by the tests) whether other forms of whitespace (\t \v) should be
- * included in the trimming done to headers
- */
- static bool s_is_space(uint8_t value) {
- return aws_isspace(value);
- }
- /*
- * Appends a single header key-value pair to the canonical request. Multi-line and repeat headers make this more
- * complicated than you'd expect.
- *
- * We call this function on a sorted collection, so header repeats are guaranteed to be consecutive.
- *
- * In particular, there are two cases:
- * (1) This is a header whose name hasn't been seen before, in which case we start a new line and append both name and
- * value. (2) This is a header we've previously seen, just append the value.
- *
- * The fact that we can't '\n' until we've moved to a new header name also complicates the logic.
- *
- * This function appends to a state buffer rather than the canonical request. This allows us to calculate the signed
- * headers (so that it can go into the query param if needed) before the query params are put into the canonical
- * request.
- */
- static int s_append_canonical_header(
- struct aws_signing_state_aws *state,
- struct aws_signable_property_list_pair *header,
- const struct aws_byte_cursor *last_seen_header_name) {
- struct aws_byte_buf *canonical_header_buffer = &state->canonical_header_block;
- struct aws_byte_buf *signed_headers_buffer = &state->signed_headers;
- const uint8_t *to_lower_table = aws_lookup_table_to_lower_get();
- /*
- * Write to the signed_headers shared state for later use, copy
- * to canonical header buffer as well
- */
- if (last_seen_header_name == NULL ||
- aws_byte_cursor_compare_lookup(last_seen_header_name, &header->name, aws_lookup_table_to_lower_get()) != 0) {
- /*
- * The headers arrive in sorted order, so we know we've never seen this header before
- */
- if (last_seen_header_name) {
- /*
- * there's a previous header, add appropriate separator in both canonical header buffer
- * and signed headers buffer
- */
- if (aws_byte_buf_append_byte_dynamic(canonical_header_buffer, '\n')) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(signed_headers_buffer, ';')) {
- return AWS_OP_ERR;
- }
- }
- /* add it to the signed headers buffer */
- if (s_append_with_lookup(signed_headers_buffer, &header->name, to_lower_table)) {
- return AWS_OP_ERR;
- }
- /* add it to the canonical header buffer */
- if (s_append_with_lookup(canonical_header_buffer, &header->name, to_lower_table)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(canonical_header_buffer, ':')) {
- return AWS_OP_ERR;
- }
- } else {
- /* we've seen this header before, add a comma before appending the value */
- if (aws_byte_buf_append_byte_dynamic(canonical_header_buffer, ',')) {
- return AWS_OP_ERR;
- }
- }
- /*
- * This is the unsafe, non-append write of the header value where consecutive whitespace
- * is squashed into a single space. Since this can only shrink the value length and we've
- * already reserved enough to hold the value, we can do raw buffer writes safely without
- * worrying about capacity.
- */
- struct aws_byte_cursor trimmed_value = aws_byte_cursor_trim_pred(&header->value, s_is_space);
- /* raw, unsafe write loop */
- bool in_space = false;
- uint8_t *start_ptr = trimmed_value.ptr;
- uint8_t *end_ptr = trimmed_value.ptr + trimmed_value.len;
- uint8_t *dest_ptr = canonical_header_buffer->buffer + canonical_header_buffer->len;
- while (start_ptr < end_ptr) {
- uint8_t value = *start_ptr;
- bool is_space = s_is_space(value);
- if (is_space) {
- value = ' ';
- }
- if (!is_space || !in_space) {
- *dest_ptr++ = value;
- ++canonical_header_buffer->len;
- }
- in_space = is_space;
- ++start_ptr;
- }
- return AWS_OP_SUCCESS;
- }
- /* Add header to stable_header_list to be canonicalized, and also to final signing result */
- static int s_add_authorization_header(
- struct aws_signing_state_aws *state,
- struct aws_array_list *stable_header_list,
- size_t *out_required_capacity,
- struct aws_byte_cursor name,
- struct aws_byte_cursor value) {
- /* Add to stable_header_list to be canonicalized */
- struct stable_header stable_header = {
- .original_index = aws_array_list_length(stable_header_list),
- .header =
- {
- .name = name,
- .value = value,
- },
- };
- if (aws_array_list_push_back(stable_header_list, &stable_header)) {
- return AWS_OP_ERR;
- }
- /* Add to signing result */
- if (aws_signing_result_append_property_list(&state->result, g_aws_http_headers_property_list_name, &name, &value)) {
- return AWS_OP_ERR;
- }
- *out_required_capacity += name.len + value.len;
- return AWS_OP_SUCCESS;
- }
- /*
- * Builds the list of header name-value pairs to be added to the canonical request. The list members are
- * actually the header wrapper structs that allow for stable sorting.
- *
- * Takes the original request headers, adds X-Amz-Date, and optionally, x-amz-content-sha256
- *
- * If we add filtering/exclusion support, this is where it would go
- */
- static int s_build_canonical_stable_header_list(
- struct aws_signing_state_aws *state,
- struct aws_array_list *stable_header_list,
- size_t *out_required_capacity) {
- AWS_ASSERT(aws_array_list_length(stable_header_list) == 0);
- *out_required_capacity = 0;
- const struct aws_signable *signable = state->signable;
- /*
- * request headers
- */
- struct aws_array_list *signable_header_list = NULL;
- if (aws_signable_get_property_list(signable, g_aws_http_headers_property_list_name, &signable_header_list)) {
- return AWS_OP_ERR;
- }
- const size_t signable_header_count = aws_array_list_length(signable_header_list);
- for (size_t i = 0; i < signable_header_count; ++i) {
- struct stable_header header_wrapper;
- AWS_ZERO_STRUCT(header_wrapper);
- header_wrapper.original_index = i;
- if (aws_array_list_get_at(signable_header_list, &header_wrapper.header, i)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor *header_name_cursor = &header_wrapper.header.name;
- if (!s_should_sign_header(state, header_name_cursor)) {
- continue;
- }
- *out_required_capacity += header_wrapper.header.name.len + header_wrapper.header.value.len;
- if (aws_array_list_push_back(stable_header_list, &header_wrapper)) {
- return AWS_OP_ERR;
- }
- }
- /* If doing HEADERS signature type, add required X-Amz-*** headers.
- * NOTE: For QUERY_PARAMS signature type, X-Amz-*** params are added to query string instead. */
- if (state->config.signature_type == AWS_ST_HTTP_REQUEST_HEADERS) {
- /*
- * X-Amz-Security-Token
- */
- struct aws_byte_cursor session_token_cursor = aws_credentials_get_session_token(state->config.credentials);
- if (session_token_cursor.len > 0) {
- /* Note that if omit_session_token is true, it is added to final
- * signing result but NOT included in canonicalized headers. */
- if (state->config.flags.omit_session_token) {
- if (aws_signing_result_append_property_list(
- &state->result,
- g_aws_http_headers_property_list_name,
- &s_amz_security_token_header_name,
- &session_token_cursor)) {
- return AWS_OP_ERR;
- }
- } else {
- if (s_add_authorization_header(
- state,
- stable_header_list,
- out_required_capacity,
- s_amz_security_token_header_name,
- session_token_cursor)) {
- return AWS_OP_ERR;
- }
- }
- }
- /*
- * X-Amz-Date
- */
- if (s_add_authorization_header(
- state,
- stable_header_list,
- out_required_capacity,
- s_amz_date_header_name,
- aws_byte_cursor_from_buf(&state->date))) {
- return AWS_OP_ERR;
- }
- *out_required_capacity += g_aws_signing_date_name->len + state->date.len;
- /*
- * x-amz-region-set
- */
- if (state->config.algorithm == AWS_SIGNING_ALGORITHM_V4_ASYMMETRIC) {
- if (s_add_authorization_header(
- state,
- stable_header_list,
- out_required_capacity,
- aws_byte_cursor_from_string(g_aws_signing_region_set_name),
- state->config.region)) {
- return AWS_OP_ERR;
- }
- }
- /*
- * x-amz-content-sha256 (optional)
- */
- if (state->config.signed_body_header == AWS_SBHT_X_AMZ_CONTENT_SHA256) {
- if (s_add_authorization_header(
- state,
- stable_header_list,
- out_required_capacity,
- s_amz_content_sha256_header_name,
- aws_byte_cursor_from_buf(&state->payload_hash))) {
- return AWS_OP_ERR;
- }
- }
- /* NOTE: Update MAX_AUTHORIZATION_HEADER_COUNT if more headers added */
- }
- *out_required_capacity += aws_array_list_length(stable_header_list) * 2; /* ':' + '\n' per header */
- return AWS_OP_SUCCESS;
- }
- static int s_validate_signable_header_list(struct aws_array_list *header_list) {
- const size_t header_count = aws_array_list_length(header_list);
- for (size_t i = 0; i < header_count; ++i) {
- struct aws_signable_property_list_pair header;
- AWS_ZERO_STRUCT(header);
- aws_array_list_get_at(header_list, &header, i);
- struct aws_hash_element *forbidden_element = NULL;
- aws_hash_table_find(&s_forbidden_headers, &header.name, &forbidden_element);
- if (forbidden_element != NULL) {
- AWS_LOGF_ERROR(
- AWS_LS_AUTH_SIGNING,
- "AWS authorization header \"" PRInSTR "\" found in request while signing",
- AWS_BYTE_CURSOR_PRI(header.name));
- return aws_raise_error(AWS_AUTH_SIGNING_ILLEGAL_REQUEST_HEADER);
- }
- }
- return AWS_OP_SUCCESS;
- }
- static int s_canonicalize_headers(struct aws_signing_state_aws *state) {
- const struct aws_signable *signable = state->signable;
- struct aws_allocator *allocator = state->allocator;
- struct aws_byte_buf *header_buffer = &state->canonical_header_block;
- AWS_ASSERT(header_buffer->len == 0);
- int result = AWS_OP_ERR;
- struct aws_array_list *signable_header_list = NULL;
- if (aws_signable_get_property_list(signable, g_aws_http_headers_property_list_name, &signable_header_list)) {
- return AWS_OP_ERR;
- }
- if (s_validate_signable_header_list(signable_header_list)) {
- return AWS_OP_ERR;
- }
- const size_t signable_header_count = aws_array_list_length(signable_header_list);
- /* Overestimate capacity to avoid re-allocation */
- size_t headers_reserve_count = signable_header_count + MAX_AUTHORIZATION_HEADER_COUNT;
- struct aws_array_list headers;
- if (aws_array_list_init_dynamic(&headers, allocator, headers_reserve_count, sizeof(struct stable_header))) {
- return AWS_OP_ERR;
- }
- size_t header_buffer_reserve_size = 0;
- if (s_build_canonical_stable_header_list(state, &headers, &header_buffer_reserve_size)) {
- goto on_cleanup;
- }
- /*
- * Make sure there's enough room in the request buffer to hold a conservative overestimate of the room
- * needed for canonical headers. There are places we'll be using an append function that does not resize.
- */
- if (aws_byte_buf_reserve(header_buffer, header_buffer_reserve_size)) {
- return AWS_OP_ERR;
- }
- const size_t header_count = aws_array_list_length(&headers);
- /* Sort the arraylist via lowercase header name and original position */
- qsort(headers.data, header_count, sizeof(struct stable_header), s_canonical_header_comparator);
- /* Iterate the sorted list, writing the canonical representation into the request */
- struct aws_byte_cursor *last_seen_header_name = NULL;
- for (size_t i = 0; i < header_count; ++i) {
- struct stable_header *wrapper = NULL;
- if (aws_array_list_get_at_ptr(&headers, (void **)&wrapper, i)) {
- goto on_cleanup;
- }
- if (s_append_canonical_header(state, &wrapper->header, last_seen_header_name)) {
- goto on_cleanup;
- }
- last_seen_header_name = &wrapper->header.name;
- }
- /* check for count greater than zero in case someone attempts to canonicalize an empty list of trailing headers */
- /* There's always at least one header entry (X-Amz-Date), end the last one */
- if (header_count > 0) {
- if (aws_byte_buf_append_byte_dynamic(header_buffer, '\n')) {
- return AWS_OP_ERR;
- }
- }
- result = AWS_OP_SUCCESS;
- on_cleanup:
- aws_array_list_clean_up(&headers);
- return result;
- }
- static int s_append_signed_headers(struct aws_signing_state_aws *state) {
- struct aws_byte_buf *header_buffer = &state->canonical_header_block;
- struct aws_byte_buf *signed_headers_buffer = &state->signed_headers;
- if (aws_byte_buf_append_byte_dynamic(header_buffer, '\n')) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor signed_headers_cursor = aws_byte_cursor_from_buf(signed_headers_buffer);
- if (aws_byte_buf_append_dynamic(header_buffer, &signed_headers_cursor)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(header_buffer, '\n')) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /*
- * Top-level-ish function to write the canonical header set into a buffer as well as the signed header names
- * into a separate buffer. We do this very early in the canonical request construction process so that the
- * query params processing has the signed header names available to it.
- */
- static int s_build_canonical_headers(struct aws_signing_state_aws *state) {
- if (s_canonicalize_headers(state)) {
- return AWS_OP_ERR;
- }
- if (s_append_signed_headers(state)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /*
- * Computes the canonical request payload value.
- */
- static int s_build_canonical_payload(struct aws_signing_state_aws *state) {
- const struct aws_signable *signable = state->signable;
- struct aws_allocator *allocator = state->allocator;
- struct aws_byte_buf *payload_hash_buffer = &state->payload_hash;
- AWS_ASSERT(payload_hash_buffer->len == 0);
- struct aws_byte_buf body_buffer;
- AWS_ZERO_STRUCT(body_buffer);
- struct aws_byte_buf digest_buffer;
- AWS_ZERO_STRUCT(digest_buffer);
- struct aws_hash *hash = NULL;
- int result = AWS_OP_ERR;
- if (state->config.signed_body_value.len == 0) {
- /* No value provided by user, so we must calculate it */
- hash = aws_sha256_new(allocator);
- if (hash == NULL) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_init(&body_buffer, allocator, BODY_READ_BUFFER_SIZE) ||
- aws_byte_buf_init(&digest_buffer, allocator, AWS_SHA256_LEN)) {
- goto on_cleanup;
- }
- struct aws_input_stream *payload_stream = NULL;
- if (aws_signable_get_payload_stream(signable, &payload_stream)) {
- goto on_cleanup;
- }
- if (payload_stream != NULL) {
- if (aws_input_stream_seek(payload_stream, 0, AWS_SSB_BEGIN)) {
- goto on_cleanup;
- }
- struct aws_stream_status payload_status;
- AWS_ZERO_STRUCT(payload_status);
- while (!payload_status.is_end_of_stream) {
- /* reset the temporary body buffer; we can calculate the hash in window chunks */
- body_buffer.len = 0;
- if (aws_input_stream_read(payload_stream, &body_buffer)) {
- goto on_cleanup;
- }
- if (body_buffer.len > 0) {
- struct aws_byte_cursor body_cursor = aws_byte_cursor_from_buf(&body_buffer);
- aws_hash_update(hash, &body_cursor);
- }
- if (aws_input_stream_get_status(payload_stream, &payload_status)) {
- goto on_cleanup;
- }
- }
- /* reset the input stream for sending */
- if (aws_input_stream_seek(payload_stream, 0, AWS_SSB_BEGIN)) {
- goto on_cleanup;
- }
- }
- if (aws_hash_finalize(hash, &digest_buffer, 0)) {
- goto on_cleanup;
- }
- struct aws_byte_cursor digest_cursor = aws_byte_cursor_from_buf(&digest_buffer);
- if (aws_hex_encode_append_dynamic(&digest_cursor, payload_hash_buffer)) {
- goto on_cleanup;
- }
- } else {
- /* Use value provided in config */
- if (aws_byte_buf_append_dynamic(payload_hash_buffer, &state->config.signed_body_value)) {
- goto on_cleanup;
- }
- }
- result = AWS_OP_SUCCESS;
- on_cleanup:
- aws_byte_buf_clean_up(&digest_buffer);
- aws_byte_buf_clean_up(&body_buffer);
- if (hash) {
- aws_hash_destroy(hash);
- }
- return result;
- }
- /*
- * Copies the previously-computed payload hash into the canonical request buffer
- */
- static int s_append_canonical_payload_hash(struct aws_signing_state_aws *state) {
- struct aws_byte_buf *canonical_request_buffer = &state->canonical_request;
- struct aws_byte_buf *payload_hash_buffer = &state->payload_hash;
- /*
- * Copy the hex-encoded payload hash into the canonical request
- */
- struct aws_byte_cursor payload_hash_cursor = aws_byte_cursor_from_buf(payload_hash_buffer);
- if (aws_byte_buf_append_dynamic(canonical_request_buffer, &payload_hash_cursor)) {
- return AWS_OP_ERR;
- }
- /* Sigv4 spec claims a newline should be included after the payload, but the implementation doesn't do this */
- return AWS_OP_SUCCESS;
- }
- AWS_STATIC_STRING_FROM_LITERAL(s_credential_scope_sigv4_terminator, "aws4_request");
- static int s_append_credential_scope_terminator(enum aws_signing_algorithm algorithm, struct aws_byte_buf *dest) {
- struct aws_byte_cursor terminator_cursor;
- switch (algorithm) {
- case AWS_SIGNING_ALGORITHM_V4:
- case AWS_SIGNING_ALGORITHM_V4_ASYMMETRIC:
- terminator_cursor = aws_byte_cursor_from_string(s_credential_scope_sigv4_terminator);
- break;
- default:
- return aws_raise_error(AWS_AUTH_SIGNING_UNSUPPORTED_ALGORITHM);
- }
- return aws_byte_buf_append_dynamic(dest, &terminator_cursor);
- }
- /*
- * Builds the credential scope string by appending a bunch of things together:
- * Date, region, service, algorithm terminator
- */
- static int s_build_credential_scope(struct aws_signing_state_aws *state) {
- AWS_ASSERT(state->credential_scope.len == 0);
- const struct aws_signing_config_aws *config = &state->config;
- struct aws_byte_buf *dest = &state->credential_scope;
- /*
- * date output uses the non-dynamic append, so make sure there's enough room first
- */
- if (aws_byte_buf_reserve_relative(dest, AWS_DATE_TIME_STR_MAX_LEN)) {
- return AWS_OP_ERR;
- }
- if (aws_date_time_to_utc_time_short_str(&config->date, AWS_DATE_FORMAT_ISO_8601_BASIC, dest)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '/')) {
- return AWS_OP_ERR;
- }
- if (config->algorithm != AWS_SIGNING_ALGORITHM_V4_ASYMMETRIC) {
- if (aws_byte_buf_append_dynamic(dest, &config->region)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '/')) {
- return AWS_OP_ERR;
- }
- }
- if (aws_byte_buf_append_dynamic(dest, &config->service)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '/')) {
- return AWS_OP_ERR;
- }
- if (s_append_credential_scope_terminator(state->config.algorithm, dest)) {
- return AWS_OP_ERR;
- }
- /* While we're at it, build the accesskey/credential scope string which is used during query param signing*/
- struct aws_byte_cursor access_key_cursor = aws_credentials_get_access_key_id(state->config.credentials);
- if (aws_byte_buf_append_dynamic(&state->access_credential_scope, &access_key_cursor)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(&state->access_credential_scope, '/')) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor credential_scope_cursor = aws_byte_cursor_from_buf(&state->credential_scope);
- if (aws_byte_buf_append_dynamic(&state->access_credential_scope, &credential_scope_cursor)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /*
- * Hashes the canonical request and stores its hex representation
- */
- static int s_build_canonical_request_hash(struct aws_signing_state_aws *state) {
- struct aws_allocator *allocator = state->allocator;
- struct aws_byte_buf *dest = &state->string_to_sign_payload;
- int result = AWS_OP_ERR;
- struct aws_byte_buf digest_buffer;
- AWS_ZERO_STRUCT(digest_buffer);
- if (aws_byte_buf_init(&digest_buffer, allocator, AWS_SHA256_LEN)) {
- goto cleanup;
- }
- struct aws_byte_cursor canonical_request_cursor = aws_byte_cursor_from_buf(&state->canonical_request);
- if (aws_sha256_compute(allocator, &canonical_request_cursor, &digest_buffer, 0)) {
- goto cleanup;
- }
- struct aws_byte_cursor digest_cursor = aws_byte_cursor_from_buf(&digest_buffer);
- if (aws_hex_encode_append_dynamic(&digest_cursor, dest)) {
- goto cleanup;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_byte_buf_clean_up(&digest_buffer);
- return result;
- }
- /**
- * Note that there is no canonical request for event signing.
- * The string to sign for events is detailed here:
- * https://docs.aws.amazon.com/transcribe/latest/dg/streaming-http2.html
- *
- * String stringToSign =
- * "AWS4-HMAC-SHA256" +
- * "\n" +
- * DateTime +
- * "\n" +
- * Keypath +
- * "\n" +
- * Hex(priorSignature) +
- * "\n" +
- * HexHash(nonSignatureHeaders) +
- * "\n" +
- * HexHash(payload);
- *
- * This function will build the string_to_sign_payload,
- * aka "everything after the Keypath line in the string to sign".
- */
- static int s_build_string_to_sign_payload_for_event(struct aws_signing_state_aws *state) {
- int result = AWS_OP_ERR;
- struct aws_byte_buf *dest = &state->string_to_sign_payload;
- /*
- * Hex(priorSignature) + "\n"
- *
- * Fortunately, the prior signature is already hex.
- */
- 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) Event 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;
- }
- /*
- * HexHash(nonSignatureHeaders) + "\n"
- *
- * nonSignatureHeaders is just the ":date" header.
- * We need to encode these headers in event-stream format, as described here:
- * https://docs.aws.amazon.com/transcribe/latest/dg/streaming-setting-up.html
- *
- * | Header Name Length | Header Name | Header Value Type | Header Value Length | Header Value |
- * | 1 byte | N bytes | 1 byte | 2 bytes | N bytes |
- */
- struct aws_byte_buf date_buffer;
- AWS_ZERO_STRUCT(date_buffer);
- struct aws_byte_buf digest_buffer;
- AWS_ZERO_STRUCT(digest_buffer);
- if (aws_byte_buf_init(&date_buffer, state->allocator, 15)) {
- goto cleanup;
- }
- struct aws_byte_cursor header_name = aws_byte_cursor_from_c_str(":date");
- AWS_FATAL_ASSERT(aws_byte_buf_write_u8(&date_buffer, (uint8_t)header_name.len));
- if (aws_byte_buf_append_dynamic(&date_buffer, &header_name)) {
- goto cleanup;
- }
- /* Type of timestamp header */
- AWS_FATAL_ASSERT(aws_byte_buf_write_u8(&date_buffer, 8 /*AWS_EVENT_STREAM_HEADER_TIMESTAMP*/));
- AWS_FATAL_ASSERT(aws_byte_buf_write_be64(&date_buffer, (int64_t)aws_date_time_as_millis(&state->config.date)));
- /* calculate sha 256 of encoded buffer */
- if (aws_byte_buf_init(&digest_buffer, state->allocator, AWS_SHA256_LEN)) {
- goto cleanup;
- }
- struct aws_byte_cursor date_cursor = aws_byte_cursor_from_buf(&date_buffer);
- if (aws_sha256_compute(state->allocator, &date_cursor, &digest_buffer, 0)) {
- goto cleanup;
- }
- struct aws_byte_cursor digest_cursor = aws_byte_cursor_from_buf(&digest_buffer);
- if (aws_hex_encode_append_dynamic(&digest_cursor, dest)) {
- goto cleanup;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '\n')) {
- goto cleanup;
- }
- /*
- * HexHash(payload);
- *
- * The payload was already hashed in an earlier stage
- */
- struct aws_byte_cursor current_chunk_hash_cursor = aws_byte_cursor_from_buf(&state->payload_hash);
- if (aws_byte_buf_append_dynamic(dest, ¤t_chunk_hash_cursor)) {
- goto cleanup;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_byte_buf_clean_up(&date_buffer);
- aws_byte_buf_clean_up(&digest_buffer);
- return result;
- }
- static int s_build_canonical_request_body_chunk(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) Chunk 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;
- }
- /* empty hash + \n */
- if (aws_byte_buf_append_dynamic(dest, &g_aws_signed_body_value_empty_sha256)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '\n')) {
- return AWS_OP_ERR;
- }
- /* current hash */
- struct aws_byte_cursor current_chunk_hash_cursor = aws_byte_cursor_from_buf(&state->payload_hash);
- if (aws_byte_buf_append_dynamic(dest, ¤t_chunk_hash_cursor)) {
- return AWS_OP_ERR;
- }
- 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_canonicalize_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
- */
- static int s_build_canonical_request_sigv4(struct aws_signing_state_aws *state) {
- AWS_ASSERT(state->canonical_request.len == 0);
- AWS_ASSERT(state->payload_hash.len > 0);
- int result = AWS_OP_ERR;
- struct aws_uri uri;
- AWS_ZERO_STRUCT(uri);
- struct aws_byte_cursor uri_cursor;
- if (aws_signable_get_property(state->signable, g_aws_http_uri_property_name, &uri_cursor)) {
- return AWS_OP_ERR;
- }
- if (aws_uri_init_parse(&uri, state->allocator, &uri_cursor)) {
- goto cleanup;
- }
- if (s_build_canonical_headers(state)) {
- goto cleanup;
- }
- if (s_append_canonical_method(state)) {
- goto cleanup;
- }
- if (s_append_canonical_path(&uri, state)) {
- goto cleanup;
- }
- if (s_append_canonical_query_string(&uri, state)) {
- goto cleanup;
- }
- 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)) {
- goto cleanup;
- }
- if (s_append_canonical_payload_hash(state)) {
- goto cleanup;
- }
- if (s_build_canonical_request_hash(state)) {
- goto cleanup;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_uri_clean_up(&uri);
- return result;
- }
- /*
- * The canonical header list is the next-to-the-last line on the canonical request, so split by lines and take
- * the penultimate value.
- */
- static struct aws_byte_cursor s_get_signed_headers_from_canonical_request(
- struct aws_allocator *allocator,
- struct aws_byte_cursor canonical_request) {
- struct aws_byte_cursor header_cursor;
- AWS_ZERO_STRUCT(header_cursor);
- struct aws_array_list splits;
- AWS_ZERO_STRUCT(splits);
- if (aws_array_list_init_dynamic(
- &splits, allocator, CANONICAL_REQUEST_SPLIT_OVER_ESTIMATE, sizeof(struct aws_byte_cursor))) {
- return header_cursor;
- }
- if (aws_byte_cursor_split_on_char(&canonical_request, '\n', &splits)) {
- goto done;
- }
- size_t split_count = aws_array_list_length(&splits);
- if (split_count > 1) {
- aws_array_list_get_at(&splits, &header_cursor, split_count - 2);
- }
- done:
- aws_array_list_clean_up(&splits);
- return header_cursor;
- }
- /*
- * Fill in the signing state values needed by later stages that computing the canonical request would have done.
- */
- static int s_apply_existing_canonical_request(struct aws_signing_state_aws *state) {
- struct aws_byte_cursor canonical_request_cursor;
- AWS_ZERO_STRUCT(canonical_request_cursor);
- if (aws_signable_get_property(state->signable, g_aws_canonical_request_property_name, &canonical_request_cursor)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_dynamic(&state->canonical_request, &canonical_request_cursor)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor signed_headers_cursor =
- s_get_signed_headers_from_canonical_request(state->allocator, canonical_request_cursor);
- if (aws_byte_buf_append_dynamic(&state->signed_headers, &signed_headers_cursor)) {
- return AWS_OP_ERR;
- }
- if (s_build_canonical_request_hash(state)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /*
- * Top-level canonical request construction function.
- * For signature types not associated directly with an http request (chunks, events), this calculates the
- * string-to-sign payload that replaces the hashed canonical request in those signing procedures.
- */
- int aws_signing_build_canonical_request(struct aws_signing_state_aws *state) {
- if (aws_date_time_to_utc_time_str(&state->config.date, AWS_DATE_FORMAT_ISO_8601_BASIC, &state->date)) {
- return AWS_OP_ERR;
- }
- if (s_build_canonical_payload(state)) {
- return AWS_OP_ERR;
- }
- if (s_build_credential_scope(state)) {
- return AWS_OP_ERR;
- }
- switch (state->config.signature_type) {
- case AWS_ST_HTTP_REQUEST_HEADERS:
- case AWS_ST_HTTP_REQUEST_QUERY_PARAMS:
- return s_build_canonical_request_sigv4(state);
- case AWS_ST_HTTP_REQUEST_CHUNK:
- return s_build_canonical_request_body_chunk(state);
- case AWS_ST_HTTP_REQUEST_EVENT:
- return s_build_string_to_sign_payload_for_event(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);
- default:
- return AWS_OP_ERR;
- }
- }
- /*
- * Top-level function for computing the string-to-sign in an AWS signing process.
- */
- int aws_signing_build_string_to_sign(struct aws_signing_state_aws *state) {
- /* We must have a canonical request and the credential scope. We must not have the string to sign */
- AWS_ASSERT(state->string_to_sign_payload.len > 0);
- AWS_ASSERT(state->credential_scope.len > 0);
- AWS_ASSERT(state->string_to_sign.len == 0);
- struct aws_byte_buf *dest = &state->string_to_sign;
- if (s_append_sts_signature_type(state, dest)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '\n')) {
- return AWS_OP_ERR;
- }
- /* date_time output uses raw array writes, so ensure there's enough room beforehand */
- if (aws_byte_buf_reserve_relative(dest, AWS_DATE_TIME_STR_MAX_LEN)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor date_cursor = aws_byte_cursor_from_buf(&state->date);
- if (aws_byte_buf_append_dynamic(dest, &date_cursor)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '\n')) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor credential_scope_cursor = aws_byte_cursor_from_buf(&state->credential_scope);
- if (aws_byte_buf_append_dynamic(dest, &credential_scope_cursor)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '\n')) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor sts_payload_cursor = aws_byte_cursor_from_buf(&state->string_to_sign_payload);
- if (aws_byte_buf_append_dynamic(dest, &sts_payload_cursor)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /*
- * Signature calculation utility functions
- */
- AWS_STATIC_STRING_FROM_LITERAL(s_secret_key_prefix, "AWS4");
- /*
- * Computes the key to sign with as a function of the secret access key in the credentials and
- * the components of the credential scope: date, region, service, algorithm terminator
- */
- static int s_compute_sigv4_signing_key(struct aws_signing_state_aws *state, struct aws_byte_buf *dest) {
- /* dest should be empty */
- AWS_ASSERT(dest->len == 0);
- const struct aws_signing_config_aws *config = &state->config;
- struct aws_allocator *allocator = state->allocator;
- int result = AWS_OP_ERR;
- struct aws_byte_buf secret_key;
- AWS_ZERO_STRUCT(secret_key);
- struct aws_byte_buf output;
- AWS_ZERO_STRUCT(output);
- struct aws_byte_buf date_buf;
- AWS_ZERO_STRUCT(date_buf);
- struct aws_byte_cursor secret_access_key_cursor = aws_credentials_get_secret_access_key(state->config.credentials);
- if (aws_byte_buf_init(&secret_key, allocator, s_secret_key_prefix->len + secret_access_key_cursor.len) ||
- aws_byte_buf_init(&output, allocator, AWS_SHA256_LEN) ||
- aws_byte_buf_init(&date_buf, allocator, AWS_DATE_TIME_STR_MAX_LEN)) {
- goto cleanup;
- }
- /*
- * Prep Key
- */
- struct aws_byte_cursor prefix_cursor = aws_byte_cursor_from_string(s_secret_key_prefix);
- if (aws_byte_buf_append_dynamic(&secret_key, &prefix_cursor) ||
- aws_byte_buf_append_dynamic(&secret_key, &secret_access_key_cursor)) {
- goto cleanup;
- }
- /*
- * Prep date
- */
- if (aws_date_time_to_utc_time_short_str(&config->date, AWS_DATE_FORMAT_ISO_8601_BASIC, &date_buf)) {
- goto cleanup;
- }
- struct aws_byte_cursor date_cursor = aws_byte_cursor_from_buf(&date_buf);
- struct aws_byte_cursor secret_key_cursor = aws_byte_cursor_from_buf(&secret_key);
- if (aws_sha256_hmac_compute(allocator, &secret_key_cursor, &date_cursor, &output, 0)) {
- goto cleanup;
- }
- struct aws_byte_cursor chained_key_cursor = aws_byte_cursor_from_buf(&output);
- output.len = 0; /* necessary evil part 1*/
- if (aws_sha256_hmac_compute(allocator, &chained_key_cursor, &config->region, &output, 0)) {
- goto cleanup;
- }
- chained_key_cursor = aws_byte_cursor_from_buf(&output);
- output.len = 0; /* necessary evil part 2 */
- if (aws_sha256_hmac_compute(allocator, &chained_key_cursor, &config->service, &output, 0)) {
- goto cleanup;
- }
- chained_key_cursor = aws_byte_cursor_from_buf(&output);
- struct aws_byte_cursor scope_terminator_cursor = aws_byte_cursor_from_string(s_credential_scope_sigv4_terminator);
- if (aws_sha256_hmac_compute(allocator, &chained_key_cursor, &scope_terminator_cursor, dest, 0)) {
- goto cleanup;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_byte_buf_clean_up_secure(&secret_key);
- aws_byte_buf_clean_up(&output);
- aws_byte_buf_clean_up(&date_buf);
- return result;
- }
- /*
- * Calculates the hex-encoding of the final signature value from the sigv4 signing process
- */
- static int s_calculate_sigv4_signature_value(struct aws_signing_state_aws *state) {
- struct aws_allocator *allocator = state->allocator;
- int result = AWS_OP_ERR;
- struct aws_byte_buf key;
- AWS_ZERO_STRUCT(key);
- struct aws_byte_buf digest;
- AWS_ZERO_STRUCT(digest);
- if (aws_byte_buf_init(&key, allocator, AWS_SHA256_LEN) || aws_byte_buf_init(&digest, allocator, AWS_SHA256_LEN)) {
- goto cleanup;
- }
- if (s_compute_sigv4_signing_key(state, &key)) {
- goto cleanup;
- }
- struct aws_byte_cursor key_cursor = aws_byte_cursor_from_buf(&key);
- struct aws_byte_cursor string_to_sign_cursor = aws_byte_cursor_from_buf(&state->string_to_sign);
- if (aws_sha256_hmac_compute(allocator, &key_cursor, &string_to_sign_cursor, &digest, 0)) {
- goto cleanup;
- }
- struct aws_byte_cursor digest_cursor = aws_byte_cursor_from_buf(&digest);
- if (aws_hex_encode_append_dynamic(&digest_cursor, &state->signature)) {
- goto cleanup;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_byte_buf_clean_up(&key);
- aws_byte_buf_clean_up(&digest);
- return result;
- }
- /*
- * Calculates the hex-encoding of the final signature value from the sigv4a signing process
- */
- static int s_calculate_sigv4a_signature_value(struct aws_signing_state_aws *state) {
- struct aws_allocator *allocator = state->allocator;
- int result = AWS_OP_ERR;
- struct aws_byte_buf ecdsa_digest;
- AWS_ZERO_STRUCT(ecdsa_digest);
- struct aws_byte_buf sha256_digest;
- AWS_ZERO_STRUCT(sha256_digest);
- struct aws_ecc_key_pair *ecc_key = aws_credentials_get_ecc_key_pair(state->config.credentials);
- if (ecc_key == NULL) {
- return aws_raise_error(AWS_AUTH_SIGNING_INVALID_CREDENTIALS);
- }
- if (aws_byte_buf_init(&ecdsa_digest, allocator, aws_ecc_key_pair_signature_length(ecc_key)) ||
- aws_byte_buf_init(&sha256_digest, allocator, AWS_SHA256_LEN)) {
- goto cleanup;
- }
- struct aws_byte_cursor string_to_sign_cursor = aws_byte_cursor_from_buf(&state->string_to_sign);
- if (aws_sha256_compute(allocator, &string_to_sign_cursor, &sha256_digest, 0)) {
- goto cleanup;
- }
- struct aws_byte_cursor sha256_digest_cursor = aws_byte_cursor_from_buf(&sha256_digest);
- if (aws_ecc_key_pair_sign_message(ecc_key, &sha256_digest_cursor, &ecdsa_digest)) {
- goto cleanup;
- }
- struct aws_byte_cursor ecdsa_digest_cursor = aws_byte_cursor_from_buf(&ecdsa_digest);
- if (aws_hex_encode_append_dynamic(&ecdsa_digest_cursor, &state->signature)) {
- goto cleanup;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_byte_buf_clean_up(&ecdsa_digest);
- aws_byte_buf_clean_up(&sha256_digest);
- return result;
- }
- /*
- * Appends a final signature value to a buffer based on the requested signing algorithm
- */
- int s_calculate_signature_value(struct aws_signing_state_aws *state) {
- switch (state->config.algorithm) {
- case AWS_SIGNING_ALGORITHM_V4:
- return s_calculate_sigv4_signature_value(state);
- case AWS_SIGNING_ALGORITHM_V4_ASYMMETRIC:
- return s_calculate_sigv4a_signature_value(state);
- default:
- return aws_raise_error(AWS_AUTH_SIGNING_UNSUPPORTED_ALGORITHM);
- }
- }
- static int s_add_signature_property_to_result_set(struct aws_signing_state_aws *state) {
- int result = AWS_OP_ERR;
- struct aws_byte_buf final_signature_buffer;
- AWS_ZERO_STRUCT(final_signature_buffer);
- if (aws_byte_buf_init(&final_signature_buffer, state->allocator, HEX_ENCODED_SIGNATURE_OVER_ESTIMATE)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor signature_value = aws_byte_cursor_from_buf(&state->signature);
- if (aws_byte_buf_append_dynamic(&final_signature_buffer, &signature_value)) {
- goto cleanup;
- }
- if (state->config.algorithm == AWS_SIGNING_ALGORITHM_V4_ASYMMETRIC &&
- (state->config.signature_type == AWS_ST_HTTP_REQUEST_CHUNK ||
- state->config.signature_type == AWS_ST_HTTP_REQUEST_TRAILING_HEADERS)) {
- if (aws_byte_buf_reserve(&final_signature_buffer, MAX_ECDSA_P256_SIGNATURE_AS_HEX_LENGTH)) {
- goto cleanup;
- }
- if (signature_value.len < MAX_ECDSA_P256_SIGNATURE_AS_HEX_LENGTH) {
- size_t padding_byte_count = MAX_ECDSA_P256_SIGNATURE_AS_HEX_LENGTH - signature_value.len;
- if (!aws_byte_buf_write_u8_n(
- &final_signature_buffer, AWS_SIGV4A_SIGNATURE_PADDING_BYTE, padding_byte_count)) {
- goto cleanup;
- }
- }
- }
- signature_value = aws_byte_cursor_from_buf(&final_signature_buffer);
- if (aws_signing_result_set_property(&state->result, g_aws_signature_property_name, &signature_value)) {
- return AWS_OP_ERR;
- }
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_byte_buf_clean_up(&final_signature_buffer);
- return result;
- }
- /*
- * Adds the appropriate authorization header or query param to the signing result
- */
- static int s_add_authorization_to_result(
- struct aws_signing_state_aws *state,
- struct aws_byte_buf *authorization_value) {
- struct aws_byte_cursor name;
- struct aws_byte_cursor value = aws_byte_cursor_from_buf(authorization_value);
- if (s_is_header_based_signature_value(state->config.signature_type)) {
- name = aws_byte_cursor_from_string(g_aws_signing_authorization_header_name);
- if (aws_signing_result_append_property_list(
- &state->result, g_aws_http_headers_property_list_name, &name, &value)) {
- return AWS_OP_ERR;
- }
- }
- if (s_is_query_param_based_signature_value(state->config.signature_type)) {
- name = aws_byte_cursor_from_string(g_aws_signing_authorization_query_param_name);
- if (aws_signing_result_append_property_list(
- &state->result, g_aws_http_query_params_property_list_name, &name, &value)) {
- return AWS_OP_ERR;
- }
- }
- /*
- * Unconditionally add the signature value as a top-level property.
- */
- if (s_add_signature_property_to_result_set(state)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- AWS_STATIC_STRING_FROM_LITERAL(s_credential_prefix, " Credential=");
- AWS_STATIC_STRING_FROM_LITERAL(s_signed_headers_prefix, ", SignedHeaders=");
- AWS_STATIC_STRING_FROM_LITERAL(s_signature_prefix, ", Signature=");
- /*
- * The Authorization has a lot more than just the final signature value in it. This function appends all those
- * other values together ala:
- *
- * "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date,
- * Signature="
- *
- * The final header value is this with the signature value appended to the end.
- */
- static int s_append_authorization_header_preamble(struct aws_signing_state_aws *state, struct aws_byte_buf *dest) {
- if (s_append_sts_signature_type(state, dest)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor credential_cursor = aws_byte_cursor_from_string(s_credential_prefix);
- if (aws_byte_buf_append_dynamic(dest, &credential_cursor)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor access_key_cursor = aws_credentials_get_access_key_id(state->config.credentials);
- if (aws_byte_buf_append_dynamic(dest, &access_key_cursor)) {
- return AWS_OP_ERR;
- }
- if (aws_byte_buf_append_byte_dynamic(dest, '/')) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor credential_scope_cursor = aws_byte_cursor_from_buf(&state->credential_scope);
- if (aws_byte_buf_append_dynamic(dest, &credential_scope_cursor)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor signed_headers_prefix_cursor = aws_byte_cursor_from_string(s_signed_headers_prefix);
- if (aws_byte_buf_append_dynamic(dest, &signed_headers_prefix_cursor)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor signed_headers_cursor = aws_byte_cursor_from_buf(&state->signed_headers);
- if (aws_byte_buf_append_dynamic(dest, &signed_headers_cursor)) {
- return AWS_OP_ERR;
- }
- struct aws_byte_cursor signature_prefix_cursor = aws_byte_cursor_from_string(s_signature_prefix);
- if (aws_byte_buf_append_dynamic(dest, &signature_prefix_cursor)) {
- return AWS_OP_ERR;
- }
- return AWS_OP_SUCCESS;
- }
- /*
- * Top-level function for constructing the final authorization header/query-param and adding it to the
- * signing result.
- */
- int aws_signing_build_authorization_value(struct aws_signing_state_aws *state) {
- AWS_ASSERT(state->string_to_sign.len > 0);
- AWS_ASSERT(state->credential_scope.len > 0);
- int result = AWS_OP_ERR;
- struct aws_byte_buf authorization_value;
- if (aws_byte_buf_init(&authorization_value, state->allocator, AUTHORIZATION_VALUE_STARTING_SIZE)) {
- goto cleanup;
- }
- if (s_is_header_based_signature_value(state->config.signature_type) &&
- s_append_authorization_header_preamble(state, &authorization_value)) {
- goto cleanup;
- }
- if (s_calculate_signature_value(state)) {
- goto cleanup;
- }
- struct aws_byte_cursor signature_cursor = aws_byte_cursor_from_buf(&state->signature);
- if (aws_byte_buf_append_dynamic(&authorization_value, &signature_cursor)) {
- goto cleanup;
- }
- if (s_add_authorization_to_result(state, &authorization_value)) {
- goto cleanup;
- }
- AWS_LOGF_INFO(
- AWS_LS_AUTH_SIGNING,
- "(id=%p) Http request successfully built final authorization value via algorithm %s, with contents "
- "\n" PRInSTR "\n",
- (void *)state->signable,
- aws_signing_algorithm_to_string(state->config.algorithm),
- AWS_BYTE_BUF_PRI(authorization_value));
- result = AWS_OP_SUCCESS;
- cleanup:
- aws_byte_buf_clean_up(&authorization_value);
- return result;
- }
- int aws_validate_v4a_authorization_value(
- struct aws_allocator *allocator,
- struct aws_ecc_key_pair *ecc_key,
- struct aws_byte_cursor string_to_sign_cursor,
- struct aws_byte_cursor signature_value_cursor) {
- AWS_LOGF_DEBUG(
- AWS_LS_AUTH_SIGNING,
- "(id=%p) Verifying v4a auth value: \n" PRInSTR "\n\nusing string-to-sign: \n" PRInSTR "\n\n",
- (void *)ecc_key,
- AWS_BYTE_CURSOR_PRI(signature_value_cursor),
- AWS_BYTE_CURSOR_PRI(string_to_sign_cursor));
- signature_value_cursor = aws_trim_padded_sigv4a_signature(signature_value_cursor);
- size_t binary_length = 0;
- if (aws_hex_compute_decoded_len(signature_value_cursor.len, &binary_length)) {
- return AWS_OP_ERR;
- }
- int result = AWS_OP_ERR;
- struct aws_byte_buf binary_signature;
- AWS_ZERO_STRUCT(binary_signature);
- struct aws_byte_buf sha256_digest;
- AWS_ZERO_STRUCT(sha256_digest);
- if (aws_byte_buf_init(&binary_signature, allocator, binary_length) ||
- aws_byte_buf_init(&sha256_digest, allocator, AWS_SHA256_LEN)) {
- goto done;
- }
- if (aws_hex_decode(&signature_value_cursor, &binary_signature)) {
- goto done;
- }
- if (aws_sha256_compute(allocator, &string_to_sign_cursor, &sha256_digest, 0)) {
- goto done;
- }
- struct aws_byte_cursor binary_signature_cursor =
- aws_byte_cursor_from_array(binary_signature.buffer, binary_signature.len);
- struct aws_byte_cursor digest_cursor = aws_byte_cursor_from_buf(&sha256_digest);
- if (aws_ecc_key_pair_verify_signature(ecc_key, &digest_cursor, &binary_signature_cursor)) {
- goto done;
- }
- result = AWS_OP_SUCCESS;
- done:
- aws_byte_buf_clean_up(&binary_signature);
- aws_byte_buf_clean_up(&sha256_digest);
- return result;
- }
- int aws_verify_sigv4a_signing(
- struct aws_allocator *allocator,
- const struct aws_signable *signable,
- const struct aws_signing_config_base *base_config,
- struct aws_byte_cursor expected_canonical_request_cursor,
- struct aws_byte_cursor signature_cursor,
- struct aws_byte_cursor ecc_key_pub_x,
- struct aws_byte_cursor ecc_key_pub_y) {
- int result = AWS_OP_ERR;
- if (base_config->config_type != AWS_SIGNING_CONFIG_AWS) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Signing config is not an AWS signing config");
- return aws_raise_error(AWS_AUTH_SIGNING_MISMATCHED_CONFIGURATION);
- }
- if (aws_validate_aws_signing_config_aws((void *)base_config)) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Signing config failed validation");
- return aws_raise_error(AWS_AUTH_SIGNING_INVALID_CONFIGURATION);
- }
- const struct aws_signing_config_aws *config = (void *)base_config;
- if (config->algorithm != AWS_SIGNING_ALGORITHM_V4_ASYMMETRIC) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Signing algorithm is not V4_ASYMMETRIC");
- return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
- }
- if (config->credentials == NULL) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "AWS credentials were not provided/null");
- return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
- }
- struct aws_signing_state_aws *signing_state = aws_signing_state_new(allocator, config, signable, NULL, NULL);
- if (!signing_state) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Unable to create new signing state");
- return AWS_OP_ERR;
- }
- AWS_LOGF_DEBUG(
- AWS_LS_AUTH_SIGNING,
- "(id=%p) Verifying v4a signature: \n" PRInSTR "\n\nagainst expected canonical request: \n" PRInSTR
- "\n\nusing ecc key:\n X:" PRInSTR "\n Y:" PRInSTR "\n\n",
- (void *)signable,
- AWS_BYTE_CURSOR_PRI(signature_cursor),
- AWS_BYTE_CURSOR_PRI(expected_canonical_request_cursor),
- AWS_BYTE_CURSOR_PRI(ecc_key_pub_x),
- AWS_BYTE_CURSOR_PRI(ecc_key_pub_y));
- struct aws_ecc_key_pair *verification_key =
- aws_ecc_key_new_from_hex_coordinates(allocator, AWS_CAL_ECDSA_P256, ecc_key_pub_x, ecc_key_pub_y);
- if (verification_key == NULL) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Unable to create an ECC key from provided coordinates");
- goto done;
- }
- if (aws_credentials_get_ecc_key_pair(signing_state->config.credentials) == NULL) {
- struct aws_credentials *ecc_credentials =
- aws_credentials_new_ecc_from_aws_credentials(allocator, signing_state->config.credentials);
- aws_credentials_release(signing_state->config.credentials);
- signing_state->config.credentials = ecc_credentials;
- if (signing_state->config.credentials == NULL) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Unable to create ECC from provided credentials");
- goto done;
- }
- }
- if (aws_signing_build_canonical_request(signing_state)) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Unable to canonicalize request for signing");
- goto done;
- }
- struct aws_byte_cursor canonical_request_cursor = aws_byte_cursor_from_buf(&signing_state->canonical_request);
- if (aws_byte_cursor_compare_lexical(&expected_canonical_request_cursor, &canonical_request_cursor) != 0) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Canonicalized request and expected canonical request do not match");
- aws_raise_error(AWS_AUTH_CANONICAL_REQUEST_MISMATCH);
- goto done;
- }
- if (aws_signing_build_string_to_sign(signing_state)) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Unable to build string to sign from canonical request");
- goto done;
- }
- if (aws_validate_v4a_authorization_value(
- allocator, verification_key, aws_byte_cursor_from_buf(&signing_state->string_to_sign), signature_cursor)) {
- AWS_LOGF_ERROR(AWS_LS_AUTH_SIGNING, "Signature does not validate");
- aws_raise_error(AWS_AUTH_SIGV4A_SIGNATURE_VALIDATION_FAILURE);
- goto done;
- }
- result = AWS_OP_SUCCESS;
- done:
- if (verification_key) {
- aws_ecc_key_pair_release(verification_key);
- }
- aws_signing_state_destroy(signing_state);
- return result;
- }
- static bool s_is_padding_byte(uint8_t byte) {
- return byte == AWS_SIGV4A_SIGNATURE_PADDING_BYTE;
- }
- struct aws_byte_cursor aws_trim_padded_sigv4a_signature(struct aws_byte_cursor signature) {
- return aws_byte_cursor_trim_pred(&signature, s_is_padding_byte);
- }
|