Skip to content

Commit

Permalink
Support presigned URL signature "UNSIGNED-PAYLOAD" and expose EncodeU…
Browse files Browse the repository at this point in the history
…RI (#89)

* Support for Presigned URLs

* encodeURI exposed to outside world

* Uncrustify: triggered by comment.

* moved #include <stdbool.h> to header to support SigV4_EncodeURI

* writeCanonicalQueryParameters() added argument doubleEncodeEqualsInParmsValues

* added unit test

* updated size_table.md

* updated test cbmc stubs

* updated test cbmc stubs - SigV4_EncodeURI()

* rework to minimise changes to not upset CI

* rework to minimise changes to not upset CI

* fix for CBMC proof

---------

Co-authored-by: GitHub Action <[email protected]>
Co-authored-by: Giuseppe Penone <[email protected]>
  • Loading branch information
3 people authored Feb 23, 2024
1 parent 9d9f95a commit e828353
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 11 deletions.
4 changes: 2 additions & 2 deletions docs/doxygen/include/size_table.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</tr>
<tr>
<td>sigv4.c</td>
<td><center>5.2K</center></td>
<td><center>5.3K</center></td>
<td><center>4.4K</center></td>
</tr>
<tr>
Expand All @@ -19,7 +19,7 @@
</tr>
<tr>
<td><b>Total estimates</b></td>
<td><b><center>5.6K</center></b></td>
<td><b><center>5.7K</center></b></td>
<td><b><center>4.7K</center></b></td>
</tr>
</table>
35 changes: 35 additions & 0 deletions source/include/sigv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

/* Standard includes. */
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

/* *INDENT-OFF* */
Expand Down Expand Up @@ -125,6 +126,15 @@
*/
#define SIGV4_HTTP_PAYLOAD_IS_HASH 0x8U

/**
* @ingroup sigv4_canonical_flags
* @brief Set this flag to indicate that the HTTP request is
* a presigned URL.
*
* This flag is valid only for #SigV4HttpParameters_t.flags.
*/
#define SIGV4_HTTP_IS_PRESIGNED_URL 0x10U

/**
* @ingroup sigv4_canonical_flags
* @brief Set this flag to indicate that the HTTP request path, query, and
Expand Down Expand Up @@ -565,6 +575,31 @@ SigV4Status_t SigV4_AwsIotDateToIso8601( const char * pDate,
size_t dateISO8601Len );
/* @[declare_sigV4_awsIotDateToIso8601_function] */

#if ( SIGV4_USE_CANONICAL_SUPPORT == 1 )

/**
* @brief Normalize a URI string according to RFC 3986 and fill destination
* buffer with the formatted string.
*
* @param[in] pUri The URI string to encode.
* @param[in] uriLen Length of pUri.
* @param[out] pCanonicalURI The resulting canonicalized URI.
* @param[in, out] canonicalURILen input: the length of pCanonicalURI,
* output: the length of the generated canonical URI.
* @param[in] encodeSlash Option to indicate if slashes should be encoded.
* @param[in] doubleEncodeEquals Option to indicate if equals should be double-encoded.
*/
/* @[declare_sigV4_EncodeURI_function] */
SigV4Status_t SigV4_EncodeURI( const char * pUri,
size_t uriLen,
char * pCanonicalURI,
size_t * canonicalURILen,
bool encodeSlash,
bool doubleEncodeEquals );
/* @[declare_sigV4_encodeURI_function] */

#endif /* #if (SIGV4_USE_CANONICAL_SUPPORT == 1) */

/* *INDENT-OFF* */
#ifdef __cplusplus
}
Expand Down
49 changes: 40 additions & 9 deletions source/sigv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@

#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

Expand Down Expand Up @@ -85,11 +84,13 @@
*
* @param[in] pQuery HTTP request query.
* @param[in] queryLen Length of pQuery.
* @param[in] doubleEncodeEqualsInParmsValues whether to double-encode any equals ( = ) characters in parameter values.
* @param[in, out] pCanonicalContext Struct to maintain intermediary buffer
* and state of canonicalization.
*/
static SigV4Status_t generateCanonicalQuery( const char * pQuery,
size_t queryLen,
const bool doubleEncodeEqualsInParmsValues,
CanonicalContext_t * pCanonicalContext );

/**
Expand Down Expand Up @@ -1314,6 +1315,18 @@ static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,
return ret;
}

/*-----------------------------------------------------------*/

SigV4Status_t SigV4_EncodeURI( const char * pUri,
size_t uriLen,
char * pCanonicalURI,
size_t * canonicalURILen,
bool encodeSlash,
bool doubleEncodeEquals )
{
return encodeURI( pUri, uriLen, pCanonicalURI, canonicalURILen, encodeSlash, doubleEncodeEquals );
}

/*-----------------------------------------------------------*/

static SigV4Status_t encodeURI( const char * pUri,
Expand Down Expand Up @@ -2054,7 +2067,8 @@ static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,
size_t bufferLen,
const char * pValue,
size_t valueLen,
size_t * pEncodedLen )
size_t * pEncodedLen,
const bool doubleEncodeEqualsInParmsValues )
{
SigV4Status_t returnStatus = SigV4Success;
size_t valueBytesWritten = 0U;
Expand Down Expand Up @@ -2082,8 +2096,8 @@ static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,
valueLen,
pBufCur + 1U,
&valueBytesWritten,
true,
true );
true /* Encode slash (/) */,
doubleEncodeEqualsInParmsValues );

if( returnStatus == SigV4Success )
{
Expand All @@ -2098,7 +2112,8 @@ static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,
/*-----------------------------------------------------------*/

static SigV4Status_t writeCanonicalQueryParameters( CanonicalContext_t * pCanonicalRequest,
size_t numberOfParameters )
size_t numberOfParameters,
const bool doubleEncodeEqualsInParmsValues )
{
SigV4Status_t returnStatus = SigV4Success;
char * pBufLoc = NULL;
Expand All @@ -2122,7 +2137,7 @@ static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,
pBufLoc,
&encodedLen,
true /* Encode slash (/) */,
false /* Do not encode '='. */ );
false /* Do not double encode '='. */ );

if( returnStatus == SigV4Success )
{
Expand All @@ -2134,7 +2149,8 @@ static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,
remainingLen,
pCanonicalRequest->pQueryLoc[ paramsIndex ].value.pData,
pCanonicalRequest->pQueryLoc[ paramsIndex ].value.dataLen,
&encodedLen );
&encodedLen,
doubleEncodeEqualsInParmsValues );
pBufLoc += encodedLen;
remainingLen -= encodedLen;
}
Expand Down Expand Up @@ -2176,6 +2192,7 @@ static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,

static SigV4Status_t generateCanonicalQuery( const char * pQuery,
size_t queryLen,
const bool doubleEncodeEqualsInParmsValues,
CanonicalContext_t * pCanonicalContext )
{
SigV4Status_t returnStatus = SigV4Success;
Expand All @@ -2199,9 +2216,8 @@ static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,
* - Do not URI-encode any of the unreserved characters that RFC 3986 defines:
* A-Z, a-z, 0-9, hyphen ( - ), underscore ( _ ), period ( . ), and tilde ( ~ ).
* - Percent-encode all other characters with %XY, where X and Y are hexadecimal characters (0-9 and uppercase A-F).
* - Double-encode any equals ( = ) characters in parameter values.
*/
returnStatus = writeCanonicalQueryParameters( pCanonicalContext, numberOfParameters );
returnStatus = writeCanonicalQueryParameters( pCanonicalContext, numberOfParameters, doubleEncodeEqualsInParmsValues );
}

if( returnStatus == SigV4Success )
Expand Down Expand Up @@ -2799,6 +2815,13 @@ static SigV4Status_t generateCanonicalRequestUntilHeaders( const SigV4Parameters
SigV4Status_t returnStatus = SigV4Success;
const char * pPath = NULL;
size_t pathLen = 0U;
bool doubleEncodeEqualsInParmsValues = true;

/* In presigned URL we do not want to double-encode any equals ( = ) characters in parameter values */
if( FLAG_IS_SET( pParams->pHttpParameters->flags, SIGV4_HTTP_IS_PRESIGNED_URL ) )
{
doubleEncodeEqualsInParmsValues = false;
}

/* Set defaults for path and algorithm. */
if( ( pParams->pHttpParameters->pPath == NULL ) ||
Expand Down Expand Up @@ -2863,6 +2886,7 @@ static SigV4Status_t generateCanonicalRequestUntilHeaders( const SigV4Parameters
{
returnStatus = generateCanonicalQuery( pParams->pHttpParameters->pQuery,
pParams->pHttpParameters->queryLen,
doubleEncodeEqualsInParmsValues,
pCanonicalContext );
}
}
Expand Down Expand Up @@ -3090,6 +3114,13 @@ static SigV4Status_t writePayloadHashToCanonicalRequest( const SigV4Parameters_t
/* Remove new line at the end of the payload. */
pCanonicalContext->pBufCur--;
}
else if( FLAG_IS_SET( pParams->pHttpParameters->flags, SIGV4_HTTP_IS_PRESIGNED_URL ) )
{
/* Copy the UNSIGNED-PAYLOAD data in the headers data list. */
returnStatus = copyHeaderStringToCanonicalBuffer( "UNSIGNED-PAYLOAD", strlen( "UNSIGNED-PAYLOAD" ), pParams->pHttpParameters->flags, '\n', pCanonicalContext );
/* Remove new line at the end of the payload. */
pCanonicalContext->pBufCur--;
}
else
{
encodedLen = pCanonicalContext->bufRemaining;
Expand Down
1 change: 1 addition & 0 deletions test/cbmc/include/sigv4_stubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ SigV4Status_t encodeURI( const char * pUri,

SigV4Status_t generateCanonicalQuery( const char * pQuery,
size_t queryLen,
const bool doubleEncodeEqualsInParmsValues,
CanonicalContext_t * pCanonicalContext );

SigV4Status_t generateCanonicalAndSignedHeaders( const char * pHeaders,
Expand Down
2 changes: 2 additions & 0 deletions test/cbmc/proofs/SigV4_GenerateHTTPAuthorization/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ MAX_HEADERS_LEN=6
MAX_URI_LEN=3
MAX_REQUEST_LEN=16
S3_SERVICE_LEN=2
UNSIGNED_PAYLOAD_LEN=17
MAX_REGION_LEN=30
MAX_SERVICE_LEN=30
MAX_ALGORITHM_LEN=30
Expand Down Expand Up @@ -50,6 +51,7 @@ UNWINDSET += lowercaseHexEncode.0:$(MAX_HASH_DIGEST_LEN)
UNWINDSET += hmacIntermediate.0:$(MAX_HASH_BLOCK_LEN)
UNWINDSET += hmacFinal.0:$(MAX_HASH_BLOCK_LEN)
UNWINDSET += strncmp.0:$(S3_SERVICE_LEN)
UNWINDSET += strlen.0:$(UNSIGNED_PAYLOAD_LEN)

PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROOF_SOURCES += $(SRCDIR)/test/cbmc/stubs/hash_stubs.c
Expand Down
1 change: 1 addition & 0 deletions test/cbmc/stubs/sigv4_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ SigV4Status_t encodeURI( const char * pUri,

SigV4Status_t generateCanonicalQuery( const char * pQuery,
size_t queryLen,
const bool doubleEncodeEqualsInParmsValues,
CanonicalContext_t * pCanonicalContext )
{
SigV4Status_t returnStatus = SigV4InsufficientMemory;
Expand Down
11 changes: 11 additions & 0 deletions test/unit-test/sigv4_utest.c
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,17 @@ void test_SigV4_GenerateHTTPAuthorization_Happy_Paths()
TEST_ASSERT_EQUAL( SigV4Success, returnStatus );
TEST_ASSERT_EQUAL( SIGV4_HASH_MAX_DIGEST_LENGTH * 2U, signatureLen );
TEST_ASSERT_EQUAL_MEMORY( pExpectedSignature, signature, signatureLen );

/* Coverage for NON double-encoded equals in query string value for presigned URL. */
resetInputParams();
params.pHttpParameters->pQuery = QUERY_VALUE_HAS_EQUALS;
params.pHttpParameters->queryLen = STR_LIT_LEN( QUERY_VALUE_HAS_EQUALS );
params.pHttpParameters->flags = SIGV4_HTTP_IS_PRESIGNED_URL;
pExpectedSignature = "6759e09cf532c4f9b5190873cb4c43305180f5d4d3418d65b6c0affce827dbc4";
returnStatus = SigV4_GenerateHTTPAuthorization( &params, authBuf, &authBufLen, &signature, &signatureLen );
TEST_ASSERT_EQUAL( SigV4Success, returnStatus );
TEST_ASSERT_EQUAL( SIGV4_HASH_MAX_DIGEST_LENGTH * 2U, signatureLen );
TEST_ASSERT_EQUAL_MEMORY( pExpectedSignature, signature, signatureLen );
}

/* Test the API for handling corner cases of sorting the Query Parameters (when generating Canonical Query) */
Expand Down

0 comments on commit e828353

Please sign in to comment.