Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format Date Header for ISO8601 Compliance #4

Merged
merged 15 commits into from
Apr 23, 2021
7 changes: 6 additions & 1 deletion source/include/sigv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@

/**< Length of the date header in ISO 8601 format. */
#define SIGV4_ISO_STRING_LEN 16U
/**< Length of the date header found in AWS IoT's HTTP response. */
#define SIGV4_EXPECTED_AWS_IOT_DATE_LEN 20U
/** @}*/

/**
Expand Down Expand Up @@ -379,9 +381,12 @@ SigV4Status_t SigV4_GenerateHTTPAuthorization( const SigV4Parameters_t * pParams

/**
* @brief Parse the date header value from the AWS IoT response.
sukhmanm marked this conversation as resolved.
Show resolved Hide resolved
*
* The AWS IoT response date is of the form: 2018-01-18T09:18:06Z.
sukhmanm marked this conversation as resolved.
Show resolved Hide resolved
* The ISO8601-formatted date is: 20180118T091806Z.
*
* @param[in] pDate The date header value.
* @param[in] pDate The RFC3339-formatted date header value, found in the HTTP
sukhmanm marked this conversation as resolved.
Show resolved Hide resolved
* response returned by AWS IoT (ex. "2018-01-18T09:18:06Z").
* @param[in] dateLen length of the pDate header value.
sukhmanm marked this conversation as resolved.
Show resolved Hide resolved
sukhmanm marked this conversation as resolved.
Show resolved Hide resolved
* @param[out] pDateISO8601 The ISO8601 format compliant date. This buffer must
* be large enough to hold both the ISO8601-formatted date (16 characters) and
Expand Down
114 changes: 114 additions & 0 deletions source/sigv4.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* SigV4 Utility Library v1.0.0
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/**
* @file sigv4.c
* @brief Implements the user-facing functions in sigv4.h
*/

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>

#include "sigv4.h"

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

SigV4Status_t SigV4_AwsIotDateToIso8601( const char * pDate,
size_t dateLen,
char * pDateISO8601,
size_t dateISO8601Len )
{
SigV4Status_t returnStatus = SigV4InvalidParameter;

/* Check for NULL parameters. */
if( pDate == NULL )
{
LogError( ( "Parameter check failed: pDate is NULL." ) );
}
else if( pDateISO8601 == NULL )
{
LogError( ( "Parameter check failed: pDateISO8601 is NULL." ) );
}

/* Check that the date buffer provided is not shorter than the expected
* input format. */
else if( dateLen < SIGV4_EXPECTED_AWS_IOT_DATE_LEN )
sukhmanm marked this conversation as resolved.
Show resolved Hide resolved
{
LogError( ( "Parameter check failed: dateLen must be at least %u.",
SIGV4_EXPECTED_AWS_IOT_DATE_LEN ) );
}

/* Check that the output buffer provided is large enough for the formatted
* string. */
else if( dateISO8601Len < SIGV4_ISO_STRING_LEN + 1 )
{
LogError( ( "Parameter check failed: dateISO8601Len must be at least %u.",
SIGV4_ISO_STRING_LEN + 1 ) );
}
else
{
struct tm dateInfo = { 0 };

/* Parse pDate according to the input's expected string format, and
* populate the date struct with its components. */
if( sscanf( pDate, "%4d-%2d-%2dT%2d:%2d:%2dZ",
sukhmanm marked this conversation as resolved.
Show resolved Hide resolved
&dateInfo.tm_year,
&dateInfo.tm_mon,
&dateInfo.tm_mday,
&dateInfo.tm_hour,
&dateInfo.tm_min,
&dateInfo.tm_sec ) != 6 )
{
LogError( ( "sscanf() failed to parse the date string using the format expected." ) );
returnStatus = SigV4ISOFormattingError;
}
else
{
size_t lenFormatted = 0U;

/* Standardize month and year values for struct tm's specifications:
* - tm_mon = "months from January" (0-11)
* - tm_year = "years since 1900" */
dateInfo.tm_mon--;
gkwicker marked this conversation as resolved.
Show resolved Hide resolved
dateInfo.tm_year -= 1900;

/* Construct ISO 8601 string using members of populated date struct. */
lenFormatted = strftime( pDateISO8601, SIGV4_ISO_STRING_LEN + 1, "%Y%m%dT%H%M%SZ", &dateInfo );

if( lenFormatted != SIGV4_ISO_STRING_LEN )
{
LogError( ( "Failed to generate ISO 8601 date: Call to strftime() for string formatting failed: "
"ExpectedReturnValue=%u, ActualReturnValue=%lu.",
SIGV4_ISO_STRING_LEN, lenFormatted ) );
returnStatus = SigV4ISOFormattingError;
}
else
{
returnStatus = SigV4Success;
}
}
}

return returnStatus;
}