[修改] 增加freeRTOS

1. 版本FreeRTOSv202212.01,命名为kernel;
This commit is contained in:
2023-05-06 16:43:01 +00:00
commit a345df017b
20944 changed files with 11094377 additions and 0 deletions

View File

@ -0,0 +1,574 @@
/*
* SigV4 Library v1.2.0
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.h
* @brief Interface for the SigV4 Library.
*/
#ifndef SIGV4_H_
#define SIGV4_H_
/* Standard includes. */
#include <stdint.h>
#include <stddef.h>
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
/* SIGV4_DO_NOT_USE_CUSTOM_CONFIG allows building of the SigV4 library without a
* config file. If a config file is provided, the SIGV4_DO_NOT_USE_CUSTOM_CONFIG
* macro must not be defined.
*/
#ifndef SIGV4_DO_NOT_USE_CUSTOM_CONFIG
#include "sigv4_config.h"
#endif
/* Include config defaults header to get default values of configurations not
* defined in sigv4_config.h file. */
#include "sigv4_config_defaults.h"
/* Convenience macros for library optimization */
/** @addtogroup sigv4_constants
* @{
*/
#define SIGV4_AWS4_HMAC_SHA256 "AWS4-HMAC-SHA256" /**< AWS identifier for SHA256 signing algorithm. */
#define SIGV4_AWS4_HMAC_SHA256_LENGTH ( sizeof( SIGV4_AWS4_HMAC_SHA256 ) - 1U ) /**< Length of AWS identifier for SHA256 signing algorithm. */
#define SIGV4_HTTP_X_AMZ_DATE_HEADER "x-amz-date" /**< AWS identifier for HTTP date header. */
#define SIGV4_HTTP_X_AMZ_SECURITY_TOKEN_HEADER "x-amz-security-token" /**< AWS identifier for security token. */
#define SIGV4_STREAMING_AWS4_HMAC_SHA256_PAYLOAD "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" /**< S3 identifier for chunked payloads. */
/* MISRA Ref 5.4.1 [Macro identifiers] */
/* More details at: https://github.com/aws/SigV4-for-AWS-IoT-embedded-sdk/blob/main/MISRA.md#rule-54 */
/* coverity[other_declaration] */
#define SIGV4_HTTP_X_AMZ_CONTENT_SHA256_HEADER "x-amz-content-sha256" /**< S3 identifier for streaming requests. */
#define SIGV4_HTTP_X_AMZ_CONTENT_SHA256_HEADER_LENGTH ( sizeof( SIGV4_HTTP_X_AMZ_CONTENT_SHA256_HEADER ) - 1U ) /**< Length of S3 identifier for streaming requests. */
#define SIGV4_HTTP_X_AMZ_STORAGE_CLASS_HEADER "x-amz-storage-class" /**< S3 identifier for reduced streaming redundancy. */
#define SIGV4_ACCESS_KEY_ID_LENGTH 20U /**< Length of access key ID. */
#define SIGV4_SECRET_ACCESS_KEY_LENGTH 40U /**< Length of secret access key. */
#define SIGV4_ISO_STRING_LEN 16U /**< Length of ISO 8601 date string. */
#define SIGV4_EXPECTED_LEN_RFC_3339 20U /**< Length of RFC 3339 date input. */
#define SIGV4_EXPECTED_LEN_RFC_5322 29U
/**< Length of RFC 5322 date input. */
/** @}*/
/**
* @defgroup sigv4_canonical_flags SigV4HttpParameters_t Flags
* @brief Flags for SigV4HttpParameters_t.flags. These flags inform the library
* of parameters already in canonical form.
*
* Flags should be bitwise-ORed with each other to change the behavior of
* #SigV4_GenerateHTTPAuthorization.
*/
/**
* @ingroup sigv4_canonical_flags
* @brief Set this flag to indicate that the HTTP request path input is already
* canonicalized.
*
* This flag is valid only for #SigV4HttpParameters_t.flags.
*/
#define SIGV4_HTTP_PATH_IS_CANONICAL_FLAG 0x1U
/**
* @ingroup sigv4_canonical_flags
* @brief Set this flag to indicate that the HTTP request query input is already
* canonicalized.
*
* This flag is valid only for #SigV4HttpParameters_t.flags.
*/
#define SIGV4_HTTP_QUERY_IS_CANONICAL_FLAG 0x2U
/**
* @ingroup sigv4_canonical_flags
* @brief Set this flag to indicate that the HTTP request headers input is
* already canonicalized.
*
* This flag is valid only for #SigV4HttpParameters_t.flags.
*/
#define SIGV4_HTTP_HEADERS_ARE_CANONICAL_FLAG 0x4U
/**
* @ingroup sigv4_canonical_flags
* @brief Set this flag to indicate that the HTTP request payload is
* already hashed.
*
* This flag is valid only for #SigV4HttpParameters_t.flags.
*/
#define SIGV4_HTTP_PAYLOAD_IS_HASH 0x8U
/**
* @ingroup sigv4_canonical_flags
* @brief Set this flag to indicate that the HTTP request path, query, and
* headers are all already canonicalized.
*
* This flag is valid only for #SigV4HttpParameters_t.flags.
*/
#define SIGV4_HTTP_ALL_ARE_CANONICAL_FLAG 0x7U
/**
* @ingroup sigv4_enum_types
* @brief Return status of the SigV4 Library.
*/
typedef enum SigV4Status
{
/**
* @brief The SigV4 library function completed successfully.
*
* Functions that may return this value:
* - #SigV4_GenerateHTTPAuthorization
* - #SigV4_AwsIotDateToIso8601
*/
SigV4Success,
/**
* @brief The SigV4 library function received an invalid input
* parameter.
*
* Functions that may return this value:
* - #SigV4_GenerateHTTPAuthorization
* - #SigV4_AwsIotDateToIso8601
*/
SigV4InvalidParameter,
/**
* @brief The application buffer was not large enough for the specified hash
* function.
*
* Functions that may return this value:
* - #SigV4_GenerateHTTPAuthorization
*/
SigV4InsufficientMemory,
/**
* @brief An error occurred while formatting the provided date header.
*
* Functions that may return this value:
* - #SigV4_AwsIotDateToIso8601
*/
SigV4ISOFormattingError,
/**
* @brief The maximum number of header parameters was exceeded while parsing
* the http header string passed to the library.
* The maximum number of supported HTTP headers can be configured
* with the SIGV4_MAX_HTTP_HEADER_COUNT macro in the library config file
* passed by the application.
*
* Functions that may return this value:
* - #SigV4_GenerateHTTPAuthorization
*/
SigV4MaxHeaderPairCountExceeded,
/**
* @brief The maximum number of query parameters was exceeded while parsing
* the query string passed to the library.
* The maximum number of supported query parameters can be configured
* with the SIGV4_MAX_QUERY_PAIR_COUNT macro in the library config file
* passed by the application.
*
* Functions that may return this value:
* - #SigV4_GenerateHTTPAuthorization
*/
SigV4MaxQueryPairCountExceeded,
/**
* @brief An error occurred while performing a hash operation.
*
* Functions that may return this value:
* - #SigV4_GenerateHTTPAuthorization
*/
SigV4HashError,
/**
* @brief HTTP headers parsed to the library are invalid.
*
* Functions that may return this value:
* - #SigV4_GenerateHTTPAuthorization
*/
SigV4InvalidHttpHeaders
} SigV4Status_t;
/**
* @ingroup sigv4_struct_types
* @brief The cryptography interface used to supply the user-defined hash
* implementation.
*/
typedef struct SigV4CryptoInterface
{
/**
* @brief Initializes the @p pHashContext.
*
* @param[in] pHashContext Context used to maintain the hash's current state
* during incremental updates.
*
* @return Zero on success, all other return values are failures.
*/
int32_t ( * hashInit )( void * pHashContext );
/**
* @brief Calculates an ongoing hash update (SHA-256, for example).
*
* @param[in] pHashContext Context used to maintain the hash's current state
* during incremental updates.
* @param[in] pInput Buffer holding the data to hash.
* @param[in] inputLen length of the input buffer data.
*
* @return Zero on success, all other return values are failures.
*/
int32_t ( * hashUpdate )( void * pHashContext,
const uint8_t * pInput,
size_t inputLen );
/**
* @brief Calculates the final binary digest of the hash from the context.
*
* @param[in] pHashContext Context used to maintain the hash's current state
* during incremental updates.
* @param[out] pOutput The buffer used to place final hash binary digest
* output.
* @param[in] outputLen The length of the pOutput buffer, which must be
* larger than the hash digest length specified in
* #SIGV4_HASH_MAX_DIGEST_LENGTH.
*
* @return Zero on success, all other return values are failures.
*/
int32_t ( * hashFinal )( void * pHashContext,
uint8_t * pOutput,
size_t outputLen );
/**
* @brief Context for the hashInit, hashUpdate, and hashFinal interfaces.
*/
void * pHashContext;
/**
* @brief The block length of the hash function.
*/
size_t hashBlockLen;
/**
* @brief The digest length of the hash function.
*/
size_t hashDigestLen;
} SigV4CryptoInterface_t;
/**
* @ingroup sigv4_struct_types
* @brief Configurations of the HTTP request used to create the Canonical
* Request.
*/
typedef struct SigV4HttpParameters
{
const char * pHttpMethod; /**< @brief The HTTP method: GET, POST, PUT, etc. */
size_t httpMethodLen; /**< @brief Length of pHttpMethod. */
/**
* @brief These flags are used to indicate if the path, query, or headers are already
* in the canonical form. This is to bypass the internal sorting, white space
* trimming, and encoding done by the library. This is a performance optimization
* option. Please see https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
* for information on generating a canonical path, query, and headers string.
* - #SIGV4_HTTP_PATH_IS_CANONICAL_FLAG 0x1
* - #SIGV4_HTTP_QUERY_IS_CANONICAL_FLAG 0x2
* - #SIGV4_HTTP_HEADERS_ARE_CANONICAL_FLAG 0x4
* - #SIGV4_HTTP_ALL_ARE_CANONICAL_FLAG 0x7
*/
uint32_t flags;
/**
* @brief The path in the HTTP request. This is the absolute request URI,
* which contains everything in the URI following the HTTP host until the
* question mark character ("?") that begins any query string parameters
* (e.g. "/path/to/item.txt"). If SIGV4_HTTP_PATH_IS_CANONICAL_FLAG is set,
* then this input must already be in canonical form.
*
* @note If there exists no path for the HTTP request, then this can be
* NULL.
*/
const char * pPath;
size_t pathLen; /**< @brief Length of pPath. */
/**
* @brief The HTTP request query from the URL, if it exists. This contains all
* characters following the question mark character ("?") that denotes the start
* of the query. If SIGV4_HTTP_QUERY_IS_CANONICAL_FLAG is set, then this input
* must already be in canonical form.
*
* @note If the HTTP request does not contain query string, this can
* be NULL.
*/
const char * pQuery;
size_t queryLen; /**< @brief Length of pQuery. */
/**
* @brief The headers from the HTTP request that we want to sign. This
* should be the raw headers in HTTP request format. If
* SIGV4_HTTP_HEADERS_IS_CANONICAL_FLAG is set, then this input must
* already be in canonical form.
*
* @note The headers data MUST NOT be empty. For HTTP/1.1 requests, it is
* required that the "host" header MUST be part of the SigV4 signature.
*/
const char * pHeaders;
size_t headersLen; /**< @brief Length of pHeaders. */
/**
* @brief The HTTP response body, if one exists (ex. PUT request). If this
* body is chunked, then this field should be set with
* STREAMING-AWS4-HMAC-SHA256-PAYLOAD.
*/
const char * pPayload;
size_t payloadLen; /**< @brief Length of pPayload. */
} SigV4HttpParameters_t;
/**
* @ingroup sigv4_struct_types
* @brief Configurations for the AWS credentials used to generate the Signing
* Key.
*/
typedef struct SigV4Credentials
{
/**
* @brief The pAccessKeyId MUST be at least 16 characters long
* but not more than 128 characters long.
*/
const char * pAccessKeyId;
size_t accessKeyIdLen; /**< @brief Length of pAccessKeyId. */
/**
* @brief The pSecretAccessKey MUST be at least 40 characters long.
*/
const char * pSecretAccessKey;
size_t secretAccessKeyLen; /**< @brief Length of pSecretAccessKey. */
} SigV4Credentials_t;
/**
* @ingroup sigv4_struct_types
* @brief Complete configurations required for generating "String to Sign" and
* "Signing Key" values.
*
* Consists of parameter structures #SigV4Credentials_t,
* #SigV4CryptoInterface_t, and #SigV4HttpParameters_t, along with date, region,
* and service specifications.
*/
typedef struct SigV4Parameters
{
/**
* @brief The AccessKeyId, SecretAccessKey, and SecurityToken used to
* generate the Authorization header.
*/
SigV4Credentials_t * pCredentials;
/**
* @brief The date in ISO 8601 format, e.g. "20150830T123600Z". This is
* always 16 characters long.
*/
const char * pDateIso8601;
/**
* @brief The algorithm used for SigV4 authentication. If set to NULL,
* this will automatically be set to "AWS4-HMAC-SHA256" by default.
*/
const char * pAlgorithm;
size_t algorithmLen; /**< @brief Length of pAlgorithm. */
/**
* @brief The target AWS region for the request. Please see
* https://docs.aws.amazon.com/general/latest/gr/rande.html for a list of
* region names and codes.
*/
const char * pRegion;
size_t regionLen; /**< @brief Length of pRegion. */
/**
* @brief The target AWS service for the request. The service name can be
* found as the first segment of the service endpoint. Please see
* https://docs.aws.amazon.com/general/latest/gr/aws-service-information.html
* (https://docs.aws.amazon.com/general/latest/gr/aws-service-information.html)
* for your service of interest.
*/
const char * pService;
size_t serviceLen; /**< @brief Length of pService. */
/**
* @brief The cryptography interface.
*/
SigV4CryptoInterface_t * pCryptoInterface;
/**
* @brief HTTP specific SigV4 parameters for canonical request calculation.
*/
SigV4HttpParameters_t * pHttpParameters;
} SigV4Parameters_t;
/**
* @brief Generates the HTTP Authorization header value.
* @note The API does not support HTTP headers containing empty HTTP header keys or values.
*
* @param[in] pParams Parameters for generating the SigV4 signature.
* @param[out] pAuthBuf Buffer to hold the generated Authorization header value.
* @param[in, out] authBufLen Input: the length of @p pAuthBuf, output: the length
* of the authorization value written to the buffer.
* @param[out] pSignature Location of the signature in the authorization string.
* @param[out] signatureLen The length of @p pSignature.
*
* @return #SigV4Success if successful, error code otherwise.
*
* <b>Example</b>
* @code{c}
* // The following example shows how to use the SigV4_GenerateHTTPAuthorization
* // function to generate the HTTP Authorization header value for HTTP requests
* // to AWS services requiring SigV4 authentication.
*
* SigV4Status_t status = SigV4Success;
*
* // Buffer to hold the authorization header.
* char pSigv4Auth[ 2048U ];
* size_t sigv4AuthLen = sizeof( pSigv4Auth );
*
* // Pointer to signature in the Authorization header that will be populated in
* // pSigv4Auth by the SigV4_GenerateHTTPAuthorization API function.
* char * signature = NULL;
* size_t signatureLen = 0;
*
* SigV4Parameters_t sigv4Params =
* {
* // Parsed temporary credentials obtained from AWS IoT Credential Provider.
* .pCredentials = &sigv4Creds,
* // Date in ISO8601 format.
* .pDateIso8601 = pDateISO8601,
* // The AWS region for the request.
* .pRegion = AWS_REGION,
* .regionLen = strlen( AWS_REGION ),
* // The AWS service for the request.
* .pService = AWS_SERVICE_NAME,
* .serviceLen = strlen( AWS_SERVICE_NAME ),
* // SigV4 crypto interface. See SigV4CryptoInterface_t interface documentation.
* .pCryptoInterface = &cryptoInterface,
* // HTTP parameters for the HTTP request to generate a SigV4 authorization header for.
* .pHttpParameters = &sigv4HttpParams
* };
*
* status = SigV4_GenerateHTTPAuthorization( &sigv4Params, pSigv4Auth, &sigv4AuthLen, &signature, &signatureLen );
*
* if( status != SigV4Success )
* {
* // Failed to generate authorization header.
* }
* @endcode
*/
/* @[declare_sigV4_generateHTTPAuthorization_function] */
SigV4Status_t SigV4_GenerateHTTPAuthorization( const SigV4Parameters_t * pParams,
char * pAuthBuf,
size_t * authBufLen,
char ** pSignature,
size_t * signatureLen );
/* @[declare_sigV4_generateHTTPAuthorization_function] */
/**
* @brief Parse the date header value from the AWS IoT response, and generate
* the formatted ISO 8601 date required for authentication.
*
* This is an optional utility function available to the application, to assist
* with formatting of the date header obtained from AWS IoT (when requesting a
* temporary token or sending a POST request).
*
* AWS SigV4 authentication requires an ISO 8601 date to be present in the
* "x-amz-date" request header, as well as in the credential scope (must be
* identical). For additional information on date handling, please see
* https://docs.aws.amazon.com/general/latest/gr/sigv4-date-handling.html.
*
* Acceptable Input Formats:
* - RFC 5322 (ex. "Thu, 18 Jan 2018 09:18:06 GMT"), the preferred format in
* HTTP 'Date' response headers. If using this format, the date parameter
* should match "***, DD 'MMM' YYYY hh:mm:ss GMT" exactly.
* - RFC 3339 (ex. "2018-01-18T09:18:06Z"), found occasionally in 'Date' and
* expiration headers. If using this format, the date parameter should match
* "YYYY-MM-DD'T'hh:mm:ss'Z'" exactly.
*
* Formatted Output:
* - The ISO8601-formatted date will be returned in the form
* "YYYYMMDD'T'HHMMSS'Z'" (ex. "20180118T091806Z").
*
* @param[in] pDate The date header (in
* [RFC 3339](https://tools.ietf.org/html/rfc3339) or
* [RFC 5322](https://tools.ietf.org/html/rfc5322) formats). An acceptable date
* header can be found in the HTTP response returned by AWS IoT. This value
* should use UTC (with no time-zone offset), and be exactly 20 or 29 characters
* in length (excluding the null character), to comply with RFC 3339 and RFC
* 5322 formats, respectively.
* @param[in] dateLen The length of the pDate header value. Must be either
* SIGV4_EXPECTED_LEN_RFC_3339 or SIGV4_EXPECTED_LEN_RFC_5322, for valid input
* parameters.
* @param[out] pDateISO8601 The formatted ISO8601-compliant date. The date value
* written to this buffer will be exactly 16 characters in length, to comply
* with the ISO8601 standard required for SigV4 authentication.
* @param[in] dateISO8601Len The length of buffer pDateISO8601. Must be at least
* SIGV4_ISO_STRING_LEN bytes, for valid input parameters.
*
* @return #SigV4Success code if successful, error code otherwise.
*
*
* <b>Example</b>
* @code{c}
* // The following example shows how to use the SigV4_AwsIotDateToIso8601
* // function to convert an AWS IoT date header value to a ISO 8601 date.
*
* SigV4Status_t status = SigV4Success;
* char pDateISO8601[SIGV4_ISO_STRING_LEN] = {0};
* size_t pDateISO8601Len = SIGV4_ISO_STRING_LEN;
*
* // pDate and dateLen are the date header and length which are parsed from
* // an AWS IoT Credential Provider HTTP response, using an HTTP library.
* status = SigV4_AwsIotDateToIso8601( pDate, dateLen, pDateISO8601, pDateISO8601Len );
*
* if( status != SigV4Success )
* {
* // Failed to parse date
* }
* @endcode
*/
/* @[declare_sigV4_awsIotDateToIso8601_function] */
SigV4Status_t SigV4_AwsIotDateToIso8601( const char * pDate,
size_t dateLen,
char * pDateISO8601,
size_t dateISO8601Len );
/* @[declare_sigV4_awsIotDateToIso8601_function] */
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* SIGV4_H_ */

View File

@ -0,0 +1,257 @@
/*
* SigV4 Library v1.2.0
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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_config_defaults.h
* @brief The default values for configuration macros used by the SigV4 Library.
*
* @note This file should NOT be modified. If custom values are needed for any
* configuration macros, a sigv4_config.h file should be provided to the SigV4
* Library to override the default values defined in this file. To use the custom
* config file, the preprocessor macro SIGV4_DO_NOT_USE_CUSTOM_CONFIG
* must NOT be set.
*/
#ifndef SIGV4_CONFIG_DEFAULTS_H_
#define SIGV4_CONFIG_DEFAULTS_H_
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
/* The macro definition for SIGV4_DO_NOT_USE_CUSTOM_CONFIG is for Doxygen
* documentation only. */
/**
* @brief Define this macro to build the AWS IoT SigV4 Library without the
* custom config file sigv4_config.h.
*
* Without the custom config, the the AWS IoT SigV4 Library builds with default
* values of config macros defined in the sigv4_config_defaults.h file.
*
* If a custom config file is provided, then
* SIGV4_DO_NOT_USE_CUSTOM_CONFIG must not be defined.
*
* <b>Default value</b>: SIGV4_DO_NOT_USE_CUSTOM_CONFIG is
* <b>not</b> defined by default and the library expects a
* sigv4_config.h file.
*/
#ifdef DOXYGEN
#define SIGV4_DO_NOT_USE_CUSTOM_CONFIG
#endif
/**
* @brief Macro defining the size of the internal buffer used for incremental
* canonicalization and hashing.
*
* A buffer of this size in bytes is declared on the stack. It should be be
* large enough for the digest output of the specified hash function.
*
* <b>Possible values:</b> Any positive 32 bit integer. <br>
* <b>Default value:</b> `1024`
*/
#ifndef SIGV4_PROCESSING_BUFFER_LENGTH
#define SIGV4_PROCESSING_BUFFER_LENGTH 1024U
#endif
/**
* @brief Macro defining the maximum number of headers in the request, used to
* assist the library in sorting header fields during canonicalization.
*
* This macro should be updated if the number of request headers the application
* wishes to sign is higher or lower than the default value (100).
*
* <b>Possible values:</b> Any positive 32 bit integer. <br>
* <b>Default value:</b> `100`
*/
#ifndef SIGV4_MAX_HTTP_HEADER_COUNT
#define SIGV4_MAX_HTTP_HEADER_COUNT 100U
#endif
/**
* @brief Macro defining the maximum number of query key/value pairs, used to
* assist the library in sorting query keys during canonicalization.
*
* This macro should be updated if the number of query key/value pairs the
* application wishes to sign is higher or lower than the default value (100).
*
* <b>Possible values:</b> Any positive 32 bit integer. <br>
* <b>Default value:</b> `100`
*/
#ifndef SIGV4_MAX_QUERY_PAIR_COUNT
#define SIGV4_MAX_QUERY_PAIR_COUNT 100U
#endif
/**
* @brief Macro used to compute the worst-case stack size when sorting elements
* associated with #SIGV4_MAX_QUERY_PAIR_COUNT or #SIGV4_MAX_HTTP_HEADER_COUNT.
* Suppose the max of the two aforementioned macros is X, then the macro
* below must be set to 2 * ceiling(log(X)/log(2)) where ceiling rounds up
* the ones digit if the decimal is greater than 0.
* @note If updating #SIGV4_MAX_QUERY_PAIR_COUNT or #SIGV4_MAX_HTTP_HEADER_COUNT,
* be sure to update this value based on the formula above.
*/
#ifndef SIGV4_WORST_CASE_SORT_STACK_SIZE
#define SIGV4_WORST_CASE_SORT_STACK_SIZE 14U
#endif
/**
* @brief Macro indicating the largest block size of any hashing
* algorithm used for SigV4 authentication i.e. the maximum of all
* values specified for the hashBlockLen in #SigV4CryptoInterface_t.
* For example, using SHA-512 would require this value to be at least 128.
*
* <b>Possible values:</b> Any positive 32 bit integer. <br>
* <b>Default value:</b> `64`
*/
#ifndef SIGV4_HASH_MAX_BLOCK_LENGTH
#define SIGV4_HASH_MAX_BLOCK_LENGTH 64U
#endif
/**
* @brief Macro defining the maximum digest length of the specified hash function,
* used to determine the length of the output buffer.
*
* This macro should be updated if using a hashing algorithm other than SHA256
* (32 byte digest length). For example, using SHA512 would require this
* value to be at least 64.
*
* <b>Possible values:</b> Any positive 32 bit integer. <br>
* <b>Default value:</b> `32`
*/
#ifndef SIGV4_HASH_MAX_DIGEST_LENGTH
#define SIGV4_HASH_MAX_DIGEST_LENGTH 32U
#endif
/**
* @brief Macro to statically enable support for canonicalizing the URI,
* headers, and query in this library.
*
* Set this to one to enable the encoding functions used to create the canonical
* request.
*
* <b>Possible values:</b> 0 or 1 <br>
* <b>Default value:</b> `1`
*/
#ifndef SIGV4_USE_CANONICAL_SUPPORT
#define SIGV4_USE_CANONICAL_SUPPORT 1
#endif
/**
* @brief Macro called by the SigV4 library for logging "Error" level
* messages.
*
* To enable error level logging in the SigV4 library, this macro should
* be mapped to the application-specific logging implementation that supports
* error logging.
*
* @note This logging macro is called in the SigV4 library with
* parameters wrapped in double parentheses to be ISO C89/C90 standard
* compliant. For a reference POSIX implementation of the logging macros, refer
* to sigv4_config.h files, and the logging-stack in demos folder of the [AWS
* IoT Embedded C SDK
* repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
*
* <b>Default value</b>: Error logging is turned off, and no code is generated
* for calls to the macro in the SigV4 library on compilation.
*/
#ifndef LogError
#define LogError( message )
#endif
/**
* @brief Macro called by the the SigV4 library for logging "Warning"
* level messages.
*
* To enable warning level logging in the SigV4 library, this macro
* should be mapped to the application-specific logging implementation that
* supports warning logging.
*
* @note This logging macro is called in the SigV4 library with
* parameters wrapped in double parentheses to be ISO C89/C90 standard
* compliant. For a reference POSIX implementation of the logging macros, refer
* to sigv4_config.h files, and the logging-stack in demos folder of the [AWS
* IoT Embedded C SDK
* repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
*
* <b>Default value</b>: Warning logs are turned off, and no code is generated
* for calls to the macro in the SigV4 library on compilation.
*/
#ifndef LogWarn
#define LogWarn( message )
#endif
/**
* @brief Macro called by the the SigV4 library for logging "Info" level
* messages.
*
* To enable info level logging in the SigV4 library, this macro should
* be mapped to the application-specific logging implementation that supports
* info logging.
*
* @note This logging macro is called in the SigV4 library with
* parameters wrapped in double parentheses to be ISO C89/C90 standard
* compliant. For a reference POSIX implementation of the logging macros, refer
* to sigv4_config.h files, and the logging-stack in demos folder of the [AWS
* IoT Embedded C SDK
* repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
*
* <b>Default value</b>: Info logging is turned off, and no code is generated
* for calls to the macro in the SigV4 library on compilation.
*/
#ifndef LogInfo
#define LogInfo( message )
#endif
/**
* @brief Macro called by the the SigV4 library for logging "Debug"
* level messages.
*
* To enable debug level logging from SigV4 library, this macro should
* be mapped to the application-specific logging implementation that supports
* debug logging.
*
* @note This logging macro is called in the SigV4 library with
* parameters wrapped in double parentheses to be ISO C89/C90 standard
* compliant. For a reference POSIX implementation of the logging macros, refer
* to sigv4_config.h files, and the logging-stack in demos folder of the [AWS
* IoT Embedded C SDK
* repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
*
* <b>Default value</b>: Debug logging is turned off, and no code is generated
* for calls to the macro in the SigV4 library on compilation.
*/
#ifndef LogDebug
#define LogDebug( message )
#endif
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* ifndef SIGV4_CONFIG_DEFAULTS_H_ */

View File

@ -0,0 +1,243 @@
/*
* SigV4 Library v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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_internal.h
* @brief Internal definitions for the SigV4 Library.
*/
#ifndef SIGV4_INTERNAL_H_
#define SIGV4_INTERNAL_H_
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
/* SIGV4_DO_NOT_USE_CUSTOM_CONFIG allows building of the SigV4 library without a
* config file. If a config file is provided, the SIGV4_DO_NOT_USE_CUSTOM_CONFIG
* macro must not be defined.
*/
#ifndef SIGV4_DO_NOT_USE_CUSTOM_CONFIG
#include "sigv4_config.h"
#endif
/* Include config defaults header to get default values of configurations not
* defined in sigv4_config.h file. */
#include "sigv4_config_defaults.h"
/* Constants for date verification. */
#define YEAR_MIN 1900L /**< Earliest year accepted. */
#define MONTH_ASCII_LEN 3U /**< Length of month abbreviations. */
/**
* @brief Month name abbreviations for RFC 5322 date parsing.
*/
#define MONTH_NAMES { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }
/**
* @brief Number of days in each respective month.
*/
#define MONTH_DAYS { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
#define FORMAT_RFC_3339 "%4Y-%2M-%2DT%2h:%2m:%2sZ" /**< Format string to parse RFC 3339 date. */
#define FORMAT_RFC_3339_LEN sizeof( FORMAT_RFC_3339 ) - 1U /**< Length of the RFC 3339 format string. */
#define FORMAT_RFC_5322 "%3*, %2D %3M %4Y %2h:%2m:%2s GMT" /**< Format string to parse RFC 5322 date. */
#define FORMAT_RFC_5322_LEN sizeof( FORMAT_RFC_5322 ) - 1U /**< Length of the RFC 3339 format string. */
#define ISO_YEAR_LEN 4U /**< Length of year value in ISO 8601 date. */
#define ISO_NON_YEAR_LEN 2U /**< Length of non-year values in ISO 8601 date. */
#define ISO_DATE_SCOPE_LEN 8U /**< Length of date substring used in credential scope. */
/* SigV4 related string literals and lengths. */
/**
* @brief The separator between each component of the credential scope.
*/
#define CREDENTIAL_SCOPE_SEPARATOR '/'
#define CREDENTIAL_SCOPE_SEPARATOR_LEN 1U /**< The length of #CREDENTIAL_SCOPE_SEPARATOR. */
/**
* @brief The last component that terminates the credential scope.
*/
#define CREDENTIAL_SCOPE_TERMINATOR "aws4_request"
#define CREDENTIAL_SCOPE_TERMINATOR_LEN ( sizeof( CREDENTIAL_SCOPE_TERMINATOR ) - 1U ) /**< The length of #CREDENTIAL_SCOPE_TERMINATOR. */
/**
* @brief Default value when HttpParameters_t.pPath == NULL.
*/
#define HTTP_EMPTY_PATH "/"
#define HTTP_EMPTY_PATH_LEN ( sizeof( HTTP_EMPTY_PATH ) - 1U ) /**< The length of #HTTP_EMPTY_PATH. */
#define URI_ENCODED_SPECIAL_CHAR_SIZE 3U /**< The size of an encoded URI special character. */
#define URI_DOUBLE_ENCODED_EQUALS_CHAR_SIZE 5U /**< The size of the double-encoded "=" character. */
#define LINEFEED_CHAR '\n' /**< A linefeed character used to build the canonical request. */
#define LINEFEED_CHAR_LEN 1U /**< The length of #LINEFEED_CHAR. */
#define HTTP_REQUEST_LINE_ENDING "\r\n" /**< The string used in non-canonicalized HTTP headers to separate header entries in HTTP request. */
#define HTTP_REQUEST_LINE_ENDING_LEN ( sizeof( HTTP_REQUEST_LINE_ENDING ) - 1U ) /**< The length of #HTTP_REQUEST_LINE_ENDING. */
#define SPACE_CHAR ' ' /**< A linefeed character used to build the Authorization header value. */
#define SPACE_CHAR_LEN 1U /**< The length of #SPACE_CHAR. */
#define S3_SERVICE_NAME "s3" /**< S3 is the only service where the URI must only be encoded once. */
#define S3_SERVICE_NAME_LEN ( sizeof( S3_SERVICE_NAME ) - 1U ) /**< The length of #S3_SERVICE_NAME. */
#define SIGV4_HMAC_SIGNING_KEY_PREFIX "AWS4" /**< HMAC signing key prefix. */
#define SIGV4_HMAC_SIGNING_KEY_PREFIX_LEN ( sizeof( SIGV4_HMAC_SIGNING_KEY_PREFIX ) - 1U ) /**< The length of #SIGV4_HMAC_SIGNING_KEY_PREFIX. */
#define AUTH_CREDENTIAL_PREFIX "Credential=" /**< The prefix that goes before the credential value in the Authorization header value. */
#define AUTH_CREDENTIAL_PREFIX_LEN ( sizeof( AUTH_CREDENTIAL_PREFIX ) - 1U ) /**< The length of #AUTH_CREDENTIAL_PREFIX. */
#define AUTH_SEPARATOR ", " /**< The separator between each component in the Authorization header value. */
#define AUTH_SEPARATOR_LEN ( sizeof( AUTH_SEPARATOR ) - 1U ) /**< The length of #AUTH_SEPARATOR. */
#define AUTH_SIGNED_HEADERS_PREFIX "SignedHeaders=" /**< The prefix that goes before the signed headers in the Authorization header value. */
#define AUTH_SIGNED_HEADERS_PREFIX_LEN ( sizeof( AUTH_SIGNED_HEADERS_PREFIX ) - 1U ) /**< The length of #AUTH_SIGNED_HEADERS_PREFIX. */
#define AUTH_SIGNATURE_PREFIX "Signature=" /**< The prefix that goes before the signature in the Authorization header value. */
#define AUTH_SIGNATURE_PREFIX_LEN ( sizeof( AUTH_SIGNATURE_PREFIX ) - 1U ) /**< The length of #AUTH_SIGNATURE_PREFIX. */
#define HMAC_INNER_PAD_BYTE ( 0x36U ) /**< The "ipad" byte used for generating the inner key in the HMAC calculation process. */
#define HMAC_OUTER_PAD_BYTE ( 0x5CU ) /**< The "opad" byte used for generating the outer key in the HMAC calculation process. */
#define HMAX_IPAD_XOR_OPAD_BYTE ( 0x6AU ) /**< The XOR of the "ipad" and "opad" bytes to extract outer key from inner key. */
/**
* @brief A helper macro to print insufficient memory errors.
*/
#define LOG_INSUFFICIENT_MEMORY_ERROR( purposeOfWrite, bytesExceeded ) \
{ \
LogError( ( "Unable to " purposeOfWrite ": Insufficient memory configured in SIGV4_PROCESSING_BUFFER_LENGTH macro. BytesExceeded=%lu", \
( unsigned long ) ( bytesExceeded ) ) ); \
}
/**
* @brief A helper macro to test if a flag is set.
*/
#define FLAG_IS_SET( bits, flag ) ( ( ( bits ) & ( flag ) ) == ( flag ) )
/**
* @brief A helper macro to determine if a character is whitespace.
* @note The ctype function isspace() returns true for the following characters:
* ` `, `\t`, `\n`, `\v`, `\f`, `\r`. However, according to RFC5234:
* https://datatracker.ietf.org/doc/html/rfc5234#appendix-B.1
* the only whitespace characters in an HTTP header are spaces and
* horizontal tabs.
*/
#define isWhitespace( c ) ( ( ( c ) == ' ' ) || ( ( c ) == '\t' ) )
/**
* @brief An aggregator representing the individually parsed elements of the
* user-provided date parameter. This is used to verify the complete date
* representation, and construct the final ISO 8601 string.
*/
typedef struct SigV4DateTime
{
int32_t tm_year; /**< Year (1900 or later) */
int32_t tm_mon; /**< Month (1 to 12) */
int32_t tm_mday; /**< Day of Month (1 to 28/29/30/31) */
int32_t tm_hour; /**< Hour (0 to 23) */
int32_t tm_min; /**< Minutes (0 to 59) */
int32_t tm_sec; /**< Seconds (0 to 60) */
} SigV4DateTime_t;
/**
* @brief A library structure holding the string and length values of parameters to
* be sorted and standardized. This allows for a layer of abstraction during the
* canonicalization step of the V4 signing process.
*/
typedef struct SigV4String
{
char * pData; /**< SigV4 string data */
size_t dataLen; /**< Length of pData */
} SigV4String_t;
/**
* @brief A library structure holding the string and length values of parameters to
* be sorted and standardized. This allows for a layer of abstraction during the
* canonicalization step of the V4 signing process.
*/
typedef struct SigV4ConstString
{
const char * pData; /**< SigV4 string data */
size_t dataLen; /**< Length of pData */
} SigV4ConstString_t;
/**
* @brief A key-value pair data structure that allows for sorting of SigV4
* string values using internal comparison functions, and provides additional
* stability to quickSort(), to comply with Misra rule 21.9.
*/
typedef struct SigV4KeyValuePair
{
SigV4ConstString_t key; /**< SigV4 string identifier */
SigV4ConstString_t value; /**< SigV4 data */
} SigV4KeyValuePair_t;
/**
* @brief An aggregator to maintain the internal state of canonicalization
* during intermediate calculations.
*/
typedef struct CanonicalContext
{
SigV4KeyValuePair_t pQueryLoc[ SIGV4_MAX_QUERY_PAIR_COUNT ]; /**< Query pointers used during sorting. */
SigV4KeyValuePair_t pHeadersLoc[ SIGV4_MAX_HTTP_HEADER_COUNT ]; /**< Header pointers used during sorting. */
uint8_t pBufProcessing[ SIGV4_PROCESSING_BUFFER_LENGTH ]; /**< Internal calculation buffer used during canonicalization. */
char * pBufCur; /**< pBufProcessing cursor. */
size_t bufRemaining; /**< pBufProcessing value used during internal calculation. */
const char * pHashPayloadLoc; /**< Pointer used to store the location of hashed HTTP request payload. */
size_t hashPayloadLen; /**< Length of hashed HTTP request payload. */
} CanonicalContext_t;
/**
* @brief An aggregator to maintain the internal state of HMAC
* calculations.
*/
typedef struct HmacContext
{
/**
* @brief The cryptography interface.
*/
const SigV4CryptoInterface_t * pCryptoInterface;
/**
* @brief All accumulated key data.
*/
uint8_t key[ SIGV4_HASH_MAX_BLOCK_LENGTH ];
/**
* @brief The length of the accumulated key data.
*/
size_t keyLen;
} HmacContext_t;
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* ifndef SIGV4_INTERNAL_H_ */

View File

@ -0,0 +1,85 @@
/*
* SigV4 Library v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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_quicksort.h
* @brief Declaration of Quicksort function for the SigV4 Library.
*/
#ifndef SIGV4_QUICKSORT_H_
#define SIGV4_QUICKSORT_H_
/* Standard includes. */
#include <stdint.h>
#include <stddef.h>
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
/* SIGV4_DO_NOT_USE_CUSTOM_CONFIG allows building of the SigV4 library without a
* config file. If a config file is provided, the SIGV4_DO_NOT_USE_CUSTOM_CONFIG
* macro must not be defined.
*/
#ifndef SIGV4_DO_NOT_USE_CUSTOM_CONFIG
#include "sigv4_config.h"
#endif
/* Include config defaults header to get default values of configurations not
* defined in sigv4_config.h file. */
#include "sigv4_config_defaults.h"
/**
* @brief The comparison function used for sorting.
* @param[in] pFirstVal The first value to compare
* @param[in] pSecondVal The second value to compare
*
* @return A value less than 0 if @p pFirstVal is less than
* @p pSecondVal. Otherwise, greater than 0.
*/
typedef int32_t ( * ComparisonFunc_t )( const void * pFirstVal,
const void * pSecondVal );
/**
* @brief Perform quicksort on an array.
*
* @param[in] pArray The array to be sorted.
* @param[in] numItems The number of items in an array.
* @param[in] itemSize The amount of memory per entry in the array.
* @param[out] comparator The comparison function to determine if one item is less than another.
*/
void quickSort( void * pArray,
size_t numItems,
size_t itemSize,
ComparisonFunc_t comparator );
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* ifndef SIGV4_QUICKSORT_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,248 @@
/*
* SigV4 Library v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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_quicksort.c
* @brief Implements an Iterative Quicksort Algorithm for the SigV4 Library.
*/
#include "sigv4_quicksort.h"
#include <string.h>
#include <assert.h>
/**
* @brief Push a value to the stack.
*/
#define PUSH_STACK( valueToPush, stack, index ) \
{ \
( stack )[ ( index ) ] = ( valueToPush ); \
++( index ); \
}
/**
* @brief Pop a value from the stack.
*/
#define POP_STACK( valueToPop, stack, index ) \
{ \
--( index ); \
( valueToPop ) = ( stack )[ ( index ) ]; \
}
/*-----------------------------------------------------------*/
/**
* @brief A helper function to swap the value of two pointers
* given their sizes.
*
* @param[in] pFirstItem The item to swap with @p pSecondItem.
* @param[in] pSecondItem The item to swap with @p pFirstItem.
* @param[in] itemSize The amount of memory per entry in the array.
*/
static void swap( void * pFirstItem,
void * pSecondItem,
size_t itemSize );
/**
* @brief A helper function to perform quicksort on a subarray.
*
* @param[in] pArray The array to be sorted.
* @param[in] low The low index of the array.
* @param[in] high The high index of the array.
* @param[in] itemSize The amount of memory per entry in the array.
* @param[out] comparator The comparison function to determine if one item is less than another.
*/
static void quickSortHelper( void * pArray,
size_t low,
size_t high,
size_t itemSize,
ComparisonFunc_t comparator );
/**
* @brief A helper function to partition a subarray using the last element
* of the array as the pivot. All items smaller than the pivot end up
* at its left while all items greater than end up at its right.
*
* @param[in] pArray The array to be sorted.
* @param[in] low The low index of the array.
* @param[in] high The high index of the array.
* @param[in] itemSize The amount of memory per entry in the array.
* @param[out] comparator The comparison function to determine if one item is less than another.
*
* @return The index of the pivot
*/
static size_t partition( void * pArray,
size_t low,
size_t high,
size_t itemSize,
ComparisonFunc_t comparator );
/*-----------------------------------------------------------*/
static void swap( void * pFirstItem,
void * pSecondItem,
size_t itemSize )
{
uint8_t * pFirstByte = pFirstItem;
uint8_t * pSecondByte = pSecondItem;
size_t dataSize = itemSize;
assert( pFirstItem != NULL );
assert( pSecondItem != NULL );
/* Swap one byte at a time. */
while( dataSize-- > 0U )
{
uint8_t tmp = *pFirstByte;
*pFirstByte = *pSecondByte;
++pFirstByte;
*pSecondByte = tmp;
++pSecondByte;
}
}
static void quickSortHelper( void * pArray,
size_t low,
size_t high,
size_t itemSize,
ComparisonFunc_t comparator )
{
size_t stack[ SIGV4_WORST_CASE_SORT_STACK_SIZE ];
/* Low and high are first two items on the stack. Note
* that we use an intermediary variable for MISRA compliance. */
size_t top = 0U, lo = low, hi = high;
PUSH_STACK( lo, stack, top );
PUSH_STACK( hi, stack, top );
while( top > 0U )
{
size_t partitionIndex;
size_t len1, len2;
POP_STACK( hi, stack, top );
POP_STACK( lo, stack, top );
partitionIndex = partition( pArray, lo, hi, itemSize, comparator );
/* Calculate length of the left partition containing items smaller
* than the pivot element.
* The length is zero if either:
* 1. The pivoted item is the smallest in the the array before partitioning.
* OR
* 2. The left partition is only of single length which can be treated as
* sorted, and thus, of zero length for avoided adding to the stack. */
len1 = ( ( partitionIndex != 0U ) && ( ( partitionIndex - 1U ) > lo ) ) ? ( partitionIndex - lo ) : 0U;
/* Calculate length of the right partition containing items greater than
* or equal to the pivot item.
* The calculated length is zero if either:
* 1. The pivoted item is the greatest in the the array before partitioning.
* OR
* 2. The right partition contains only a single length which can be treated as
* sorted, and thereby, of zero length to avoid adding to the stack. */
len2 = ( ( partitionIndex + 1U ) < hi ) ? ( hi - partitionIndex ) : 0U;
/* Push the information of the left and right partitions to the stack.
* Note: For stack space optimization, the larger of the partitions is pushed
* first and the smaller is pushed later so that the smaller part of the tree
* is completed first without increasing stack space usage before coming back
* to the larger partition. */
if( len1 > len2 )
{
PUSH_STACK( lo, stack, top );
PUSH_STACK( partitionIndex - 1U, stack, top );
if( len2 > 0U )
{
PUSH_STACK( partitionIndex + 1U, stack, top );
PUSH_STACK( hi, stack, top );
}
}
else
{
if( len2 > 0U )
{
PUSH_STACK( partitionIndex + 1U, stack, top );
PUSH_STACK( hi, stack, top );
}
if( len1 > 0U )
{
PUSH_STACK( lo, stack, top );
PUSH_STACK( partitionIndex - 1U, stack, top );
}
}
}
}
static size_t partition( void * pArray,
size_t low,
size_t high,
size_t itemSize,
ComparisonFunc_t comparator )
{
uint8_t * pivot;
uint8_t * pArrayLocal = ( uint8_t * ) pArray;
size_t i = low - 1U, j = low;
assert( pArray != NULL );
assert( comparator != NULL );
/* Choose pivot as the highest indexed item in the current partition. */
pivot = pArrayLocal + ( high * itemSize );
/* Iterate over all elements of the current array to partition it
* in comparison to the chosen pivot with smaller items on the left
* and larger or equal to items on the right. */
for( ; j < high; j++ )
{
/* Use comparator function to check current element is smaller than the pivot */
if( comparator( pArrayLocal + ( j * itemSize ), pivot ) < 0 )
{
++i;
swap( pArrayLocal + ( i * itemSize ), pArrayLocal + ( j * itemSize ), itemSize );
}
}
/* Place the pivot between the smaller and larger item chunks of
* the array. This represents the 2 partitions of the array. */
swap( pArrayLocal + ( ( i + 1U ) * itemSize ), pivot, itemSize );
/* Return the pivot item's index. */
return i + 1U;
}
void quickSort( void * pArray,
size_t numItems,
size_t itemSize,
ComparisonFunc_t comparator )
{
assert( pArray != NULL );
assert( numItems > 0U );
assert( itemSize > 0U );
assert( comparator != NULL );
quickSortHelper( pArray, 0U, numItems - 1U, itemSize, comparator );
}