in source/sigv4.c [57:724]
static SigV4Status_t generateCanonicalURI( const char * pUri,
size_t uriLen,
bool encodeTwice,
CanonicalContext_t * pCanonicalRequest );
/**
* @brief Canonicalize the query string HTTP URL, beginning (but not
* including) at the "?" character. Does not include "/".
*
* @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 );
/**
* @brief Determine if a character can be written without needing URI encoding when generating Canonical Request.
*
* @param[in] c The character to evaluate.
* @param[in] encodeSlash Whether slashes may be encoded.
*
* @return `true` if the character does not need encoding, `false` if it does.
*/
static bool isAllowedChar( char c,
bool encodeSlash );
/**
* @brief Compare two SigV4 data structures lexicographically, without case-sensitivity.
*
* @param[in] pFirstVal SigV4 key value data structure to sort.
* @param[in] pSecondVal SigV4 key value data structure to sort.
*
* @return Returns a value less than 0 if @pFirstVal < @pSecondVal, or
* a value greater than 0 if @pSecondVal < @pFirstVal. 0 is never returned in
* order to provide stability to quickSort() calls.
*/
static int32_t cmpHeaderField( const void * pFirstVal,
const void * pSecondVal );
#endif /* #if (SIGV4_USE_CANONICAL_SUPPORT == 1) */
/**
* @brief Converts an integer value to its ASCII representation, and stores the
* result in the provided buffer.
*
* @param[in] value The value to convert to ASCII.
* @param[in, out] pBuffer The starting location of the buffer on input, and the
* ending location on output.
* @param[in] bufferLen Width of value to write (padded with leading 0s if
* necessary).
*/
static void intToAscii( int32_t value,
char ** pBuffer,
size_t bufferLen );
/**
* @brief Extract all header key-value pairs from the passed headers data and add them
* to the canonical request.
*
* @param[in] pHeaders HTTP headers to canonicalize.
* @param[in] headersLen Length of HTTP headers to canonicalize.
* @param[in] flags Flag to indicate if headers are already
* in the canonical form.
* @param[out] canonicalRequest Struct to maintain intermediary buffer
* and state of canonicalization.
* @param[out] pSignedHeaders The starting location of the signed headers.
* @param[out] pSignedHeadersLen The length of the signed headers.
*
* @return Following statuses will be returned by the function:
* #SigV4Success if headers are successfully added to the canonical request.
* #SigV4InsufficientMemory if canonical request buffer cannot accommodate the header.
* #SigV4InvalidParameter if HTTP headers are invalid.
* #SigV4MaxHeaderPairCountExceeded if number of headers that needs to be canonicalized
* exceed the SIGV4_MAX_HTTP_HEADER_COUNT macro defined in the config file.
*/
static SigV4Status_t generateCanonicalAndSignedHeaders( const char * pHeaders,
size_t headersLen,
uint32_t flags,
CanonicalContext_t * canonicalRequest,
char ** pSignedHeaders,
size_t * pSignedHeadersLen );
/**
* @brief Append Signed Headers to the Canonical Request buffer.
*
* @param[in] headerCount Number of headers which needs to be appended.
* @param[in] flags Flag to indicate if headers are already
* in the canonical form.
* @param[in,out] pCanonicalRequest Struct to maintain intermediary buffer
* and state of canonicalization.
* @param[out] pSignedHeaders The starting location of the signed headers.
* @param[out] pSignedHeadersLen The length of the signed headers.
*/
static SigV4Status_t appendSignedHeaders( size_t headerCount,
uint32_t flags,
CanonicalContext_t * pCanonicalRequest,
char ** pSignedHeaders,
size_t * pSignedHeadersLen );
/**
* @brief Canonicalize headers and append it to the Canonical Request buffer.
*
* @param[in] headerCount Number of headers which needs to be appended.
* @param[in] flags Flag to indicate if headers are already
* in the canonical form.
* @param[in,out] pCanonicalRequest Struct to maintain intermediary buffer
* and state of canonicalization.
*
* @return Following statuses will be returned by the function:
* #SigV4Success if headers are successfully added to the canonical request.
* #SigV4InsufficientMemory if canonical request buffer cannot accommodate the header.
*/
static SigV4Status_t appendCanonicalizedHeaders( size_t headerCount,
uint32_t flags,
CanonicalContext_t * pCanonicalRequest );
/**
* @brief Store the location of HTTP request hashed payload in the HTTP request.
*
* @param[in] headerIndex Index of request Header in the list of parsed headers.
* @param[in] pAmzSHA256Header Literal for x-amz-content-sha256 header in HTTP request.
* @param[in] amzSHA256HeaderLen Length of @p pAmzSHA256Header.
* @param[in,out] pCanonicalRequest Struct to maintain intermediary buffer
* and state of canonicalization.
*/
static void storeHashedPayloadLocation( size_t headerIndex,
const char * pAmzSHA256Header,
size_t amzSHA256HeaderLen,
CanonicalContext_t * pCanonicalRequest );
/**
* @brief Parse each header key and value pair from HTTP headers.
*
* @param[in] pHeaders HTTP headers to parse.
* @param[in] headersDataLen Length of HTTP headers to parse.
* @param[in] flags Flag to indicate if headers are already
* in the canonical form.
* @param[out] headerCount Count of key-value pairs parsed from pData.
* @param[out] pCanonicalRequest Struct to maintain intermediary buffer
* and state of canonicalization.
*
* @return Following statuses will be returned by the function:
* #SigV4Success if header key or value is successfully added to the canonical request.
* #SigV4InsufficientMemory if canonical request buffer cannot accommodate the header.
* #SigV4MaxHeaderPairCountExceeded if number of key-value entries in the headers data
* exceeds the SIGV4_MAX_HTTP_HEADER_COUNT macro defined in the config file.
*/
static SigV4Status_t parseHeaderKeyValueEntries( const char * pHeaders,
size_t headersDataLen,
uint32_t flags,
size_t * headerCount,
CanonicalContext_t * pCanonicalRequest );
/**
* @brief Copy header key or header value to the Canonical Request buffer.
*
* @param[in] pData Header Key or value to be copied to the canonical request.
* @param[in] dataLen Length of Header Key or value.
* @param[in] flags Flag to indicate if headers are already
* in the canonical form.
* @param[in] separator Character separating the multiple key-value pairs or key and values.
* @param[in,out] pCanonicalRequest Struct to maintain intermediary buffer
* and state of canonicalization.
*
* @return Following statuses will be returned by the function:
* #SigV4Success if the headers are successfully added to the canonical request.
* #SigV4InsufficientMemory if canonical request buffer cannot accommodate the header.
*/
static SigV4Status_t copyHeaderStringToCanonicalBuffer( const char * pData,
size_t dataLen,
uint32_t flags,
char separator,
CanonicalContext_t * pCanonicalRequest );
/**
* @brief Helper function to determine whether a header string character represents a space
* that can be trimmed when creating "Canonical Headers".
* All leading and trailing spaces in the header strings need to be trimmed. Also, sequential spaces
* in the header value need to be trimmed to a single space.
*
* Example of modifying header field for Canonical Headers:
* Actual header pair: | Modifier header pair
* My-Header2: "a b c" \n | my-header2:"a b c"\n
*
* @param[in] value Header value or key string to be trimmed.
* @param[in] index Index of current character.
* @param[in] valLen Length of the string.
* @param[in] trimmedLength Current length of trimmed string.
*
* @return `true` if the character needs to be trimmed, else `false`.
*/
static bool isTrimmableSpace( const char * value,
size_t index,
size_t valLen,
size_t trimmedLength );
/**
* @brief Generate the canonical request but excluding the canonical headers
* and anything that goes after it. Write it onto @p pSignedHeaders and update
* it to point to the next location to write the rest of the canonical request.
*
* @param[in] pParams The application-defined parameters used to
* generate the canonical request.
* @param[in] pCanonicalContext The context of the canonical request.
* @param[in,out] pSignedHeaders The location to start writing the canonical request and
* becomes the location to write the rest of it when this function returns.
* @param[in,out] pSignedHeadersLen The amount of buffer available and becomes the number
* of bytes actually written when this function returns.
* @return SigV4InsufficientMemory if the length of the canonical request output
* buffer cannot fit the actual request before the headers, #SigV4Success otherwise.
*/
static SigV4Status_t generateCanonicalRequestUntilHeaders( const SigV4Parameters_t * pParams,
CanonicalContext_t * pCanonicalContext,
char ** pSignedHeaders,
size_t * pSignedHeadersLen );
/**
* @brief Generates the prefix of the Authorization header of the format:
* "<algorithm> Credential=<access key ID>/<credential scope>, SignedHeaders=<SignedHeaders>, Signature="
*
* @param[in] pParams The application-defined parameters used to
* generate the canonical request.
* @param[in] pAlgorithm The signing algorithm used for SigV4 authentication.
* @param[in] algorithmLen The length of @p pAlgorithm.
* @param[in] pSignedHeaders The signed headers of the SigV4 request.
* @param[in] signedHeadersLen The length of @p pSignedHeaders.
* @param[in,out] pAuthBuf The authorization buffer where to write the prefix.
* Pointer is updated with the next location to write the value of the signature.
* @param[in, out] pAuthPrefixLen On input, it should contain the total length of @p pAuthBuf.
* On output, this will be filled with the length of the Authorization header, if
* operation is successful.
*
* @return #SigV4InsufficientMemory if the length of the authorization buffer, @p pAuthBuf
* is insufficient to store the entire authorization header value (i.e. Prefix + HexEncoded Signature);
* otherwise #SigV4Success.
*/
static SigV4Status_t generateAuthorizationValuePrefix( const SigV4Parameters_t * pParams,
const char * pAlgorithm,
size_t algorithmLen,
const char * pSignedHeaders,
size_t signedHeadersLen,
char * pAuthBuf,
size_t * pAuthPrefixLen );
/**
* @brief Write a line in the canonical request.
* @note Used whenever there are components of the request that
* are already canonicalized.
*
* @param[in] pLine The line to write to the canonical request.
* @param[in] lineLen The length of @p pLine
* @param[in,out] pCanonicalContext The canonical context where
* the line should be written.
* @return SigV4InsufficientMemory if the length of the canonical request
* buffer cannot write the desired line, #SigV4Success otherwise.
*/
static SigV4Status_t writeLineToCanonicalRequest( const char * pLine,
size_t lineLen,
CanonicalContext_t * pCanonicalContext );
/**
* @brief Set a query parameter key in the canonical request.
*
* @param[in] currentParameter The index of the query key to set
* @param[in] pKey The pointer to the query key
* @param[in] keyLen The length of @p pKey
* @param[in,out] pCanonicalRequest The canonical request containing the
* query parameter array of keys and values
*/
static void setQueryParameterKey( size_t currentParameter,
const char * pKey,
size_t keyLen,
CanonicalContext_t * pCanonicalRequest );
/**
* @brief Set a query parameter value in the canonical request.
*
* @param[in] currentParameter The index of the query value to set
* @param[in] pValue The pointer to the query value
* @param[in] valueLen The length of @p pValue
* @param[in,out] pCanonicalRequest The canonical request containing the
* query parameter array of keys and values
*/
static void setQueryParameterValue( size_t currentParameter,
const char * pValue,
size_t valueLen,
CanonicalContext_t * pCanonicalRequest );
/**
* @brief Convert the character to lowercase.
*
* @param[in] inputChar character to be lowercased.
*
* @return Input character converted to lowercase.
*/
static char lowercaseCharacter( char inputChar );
/**
* @brief Write the HTTP request payload hash to the canonical request.
*
* @param[in] pParams The application-defined parameters used for writing
* the payload hash.
* @param[out] pCanonicalContext The canonical context where the payload
* hash should be written.
*
* @return SigV4InsufficientMemory if the length of the canonical request
* buffer cannot write the desired line, #SigV4Success otherwise.
*/
static SigV4Status_t writePayloadHashToCanonicalRequest( const SigV4Parameters_t * pParams,
CanonicalContext_t * pCanonicalContext );
/**
* @brief Generates the key for the HMAC operation.
*
* @note This function can be called multiple times before calling
* #hmacIntermediate. Appending multiple substrings, then calling #hmacAddKey
* on the appended string is also equivalent to calling #hmacAddKey on
* each individual substring.
* @note This function accepts a const char * so that string literals
* can be passed in.
*
* @param[in] pHmacContext The context used for HMAC calculation.
* @param[in] pKey The key used as input for HMAC calculation.
* @param[in] keyLen The length of @p pKey.
* @param[in] isKeyPrefix Flag to indicate whether the passed key is
* prefix of a complete key for an HMAC operation. If this is a prefix,
* then it will be stored in cache for use with remaining part of the
* key that will be provided in a subsequent call to @ref hmacAddKey.
*
* @return Zero on success, all other return values are failures.
*/
static int32_t hmacAddKey( HmacContext_t * pHmacContext,
const char * pKey,
size_t keyLen,
bool isKeyPrefix );
/**
* @brief Generates the intermediate hash output in the HMAC signing process.
* This represents the H( K' ^ i_pad || message ) part of the HMAC algorithm.
*
* @note This MUST be ONLY called after #hmacAddKey; otherwise results in
* undefined behavior. Likewise, one SHOULD NOT call #hmacAddKey after
* calling #hmacIntermediate. One must call #hmacFinal first before calling
* #hmacAddKey again.
*
* @param[in] pHmacContext The context used for HMAC calculation.
* @param[in] pData The data used as input for HMAC calculation.
* @param[in] dataLen The length of @p pData.
*
* @return Zero on success, all other return values are failures.
*/
static int32_t hmacIntermediate( HmacContext_t * pHmacContext,
const char * pData,
size_t dataLen );
/**
* @brief Generates the end output of the HMAC algorithm.
*
* This represents the second hash operation in the HMAC algorithm:
* H( K' ^ o_pad || Intermediate Hash Output )
* where the Intermediate Hash Output is generated from the call
* to @ref hmacIntermediate.
*
* @param[in] pHmacContext The context used for HMAC calculation.
* @param[out] pMac The buffer onto which to write the HMAC digest.
* @param[in] macLen The length of @p pMac.
*
* @return Zero on success, all other return values are failures.
*/
static int32_t hmacFinal( HmacContext_t * pHmacContext,
char * pMac,
size_t macLen );
/**
* @brief Generates the complete HMAC digest given a key and value, then write
* the digest in the provided output buffer.
*
* @param[in] pHmacContext The context used for the current HMAC calculation.
* @param[in] pKey The key passed as input to the HMAC function.
* @param[in] keyLen The length of @p pKey.
* @param[in] pData The data passed as input to the HMAC function.
* @param[in] dataLen The length of @p pData.
* @param[out] pOutput The buffer onto which to write the HMAC digest.
* @param[out] outputLen The length of @p pOutput and must be greater
* than pCryptoInterface->hashDigestLen for this function to succeed.
* @return Zero on success, all other return values are failures.
*/
static int32_t completeHmac( HmacContext_t * pHmacContext,
const char * pKey,
size_t keyLen,
const char * pData,
size_t dataLen,
char * pOutput,
size_t outputLen );
/**
* @brief Generates the complete hash of an input string, then write
* the digest in the provided output buffer.
* @note Unlike #completeHashAndHexEncode, this function will not
* encode the hash and will simply output the bytes written by the
* hash function.
*
* @param[in] pInput The data passed as input to the hash function.
* @param[in] inputLen The length of @p pInput.
* @param[out] pOutput The buffer onto which to write the hash.
* @param[out] outputLen The length of @p pOutput and must be greater
* than pCryptoInterface->hashDigestLen for this function to succeed.
* @param[in] pCryptoInterface The interface used to call hash functions.
* @return Zero on success, all other return values are failures.
*/
static int32_t completeHash( const uint8_t * pInput,
size_t inputLen,
uint8_t * pOutput,
size_t outputLen,
const SigV4CryptoInterface_t * pCryptoInterface );
/**
* @brief Generate the complete hash of an input string, then write
* the digest in an intermediary buffer before hex encoding and
* writing it onto @p pOutput.
*
* @param[in] pInput The data passed as input to the hash function.
* @param[in] inputLen The length of @p pInput.
* @param[out] pOutput The buffer onto which to write the hex-encoded hash.
* @param[out] pOutputLen The length of @p pOutput and must be greater
* than pCryptoInterface->hashDigestLen * 2 for this function to succeed.
* @param[in] pCryptoInterface The interface used to call hash functions.
* @return Zero on success, all other return values are failures.
*/
static SigV4Status_t completeHashAndHexEncode( const char * pInput,
size_t inputLen,
char * pOutput,
size_t * pOutputLen,
const SigV4CryptoInterface_t * pCryptoInterface );
/**
* @brief Generate the prefix of the string to sign containing the
* algorithm and date then write it onto @p pBufStart.
* @note This function assumes that enough bytes remain in @p pBufStart in
* order to write the algorithm and date.
*
* @param[in] pBufStart The starting location of the buffer to write the string
* to sign.
* @param[in] pAlgorithm The algorithm used for generating the SigV4 signature.
* @param[in] algorithmLen The length of @p pAlgorithm.
* @param[in] pDateIso8601 The date used as part of the string to sign.
* @return The number of bytes written to @p pBufStart.
*/
static size_t writeStringToSignPrefix( char * pBufStart,
const char * pAlgorithm,
size_t algorithmLen,
const char * pDateIso8601 );
/**
* @brief Generate the string to sign and write it onto a #SigV4String_t.
*
* @param[in] pParams The application-defined parameters used to
* generate the string to sign.
* @param[in] pAlgorithm The algorithm used for generating the SigV4 signature.
* @param[in] algorithmLen The length of @p pAlgorithm.
* @param[in,out] pCanonicalContext The context of the canonical request.
* @return SigV4InsufficientMemory if the length of the canonical request output
* buffer cannot fit the string to sign, #SigV4Success otherwise.
*/
static SigV4Status_t writeStringToSign( const SigV4Parameters_t * pParams,
const char * pAlgorithm,
size_t algorithmLen,
CanonicalContext_t * pCanonicalContext );
/**
* @brief Generate the signing key and write it onto a #SigV4String_t.
*
* @param[in] pSigV4Params The application-defined parameters used to
* generate the signing key.
* @param[in] pHmacContext The context used for the current HMAC calculation.
* @param[out] pSigningKey The #SigV4String_t onto which the signing key will be written.
* @param[in,out] pBytesRemaining The number of bytes remaining in the canonical buffer.
* @return SigV4InsufficientMemory if the length of @p pSigningKey was insufficient to
* fit the actual signing key, #SigV4Success otherwise.
*/
static SigV4Status_t generateSigningKey( const SigV4Parameters_t * pSigV4Params,
HmacContext_t * pHmacContext,
SigV4String_t * pSigningKey,
size_t * pBytesRemaining );
/**
* @brief Format the credential scope for the authorization header.
* Credential scope includes the access key ID, date, region, and service parameters, and
* ends with "aws4_request" terminator.
*
* @param[in] pSigV4Params The application parameters defining the credential's scope.
* @param[in, out] pCredScope The credential scope in the SigV4 format.
*/
static void generateCredentialScope( const SigV4Parameters_t * pSigV4Params,
SigV4String_t * pCredScope );
/**
* @brief Check if the date represents a valid leap year day.
*
* @param[in] pDateElements The date representation to be verified.
*
* @return #SigV4Success if the date corresponds to a valid leap year,
* #SigV4ISOFormattingError otherwise.
*/
static SigV4Status_t checkLeap( const SigV4DateTime_t * pDateElements );
/**
* @brief Verify the date stored in a SigV4DateTime_t date representation.
*
* @param[in] pDateElements The date representation to be verified.
*
* @return #SigV4Success if the date is valid, and #SigV4ISOFormattingError if
* any member of SigV4DateTime_t is invalid or represents an out-of-range date.
*/
static SigV4Status_t validateDateTime( const SigV4DateTime_t * pDateElements );
/**
* @brief Append the value of a date element to the internal date representation
* structure.
*
* @param[in] formatChar The specifier identifying the struct member to fill.
* @param[in] result The value to assign to the specified struct member.
* @param[out] pDateElements The date representation structure to modify.
*/
static void addToDate( const char formatChar,
int32_t result,
SigV4DateTime_t * pDateElements );
/**
* @brief Interpret the value of the specified characters in date, based on the
* format specifier, and append to the internal date representation.
*
* @param[in] pDate The date to be parsed.
* @param[in] formatChar The format specifier used to interpret characters.
* @param[in] readLoc The index of pDate to read from.
* @param[in] lenToRead The number of characters to read.
* @param[out] pDateElements The date representation to modify.
*
* @return #SigV4Success if parsing succeeded, #SigV4ISOFormattingError if the
* characters read did not match the format specifier.
*/
static SigV4Status_t scanValue( const char * pDate,
const char formatChar,
size_t readLoc,
size_t lenToRead,
SigV4DateTime_t * pDateElements );
/**
* @brief Parses date according to format string parameter, and populates date
* representation struct SigV4DateTime_t with its elements.
*
* @param[in] pDate The date to be parsed.
* @param[in] dateLen Length of pDate, the date to be formatted.
* @param[in] pFormat The format string used to extract date pDateElements from
* pDate. This string, among other characters, may contain specifiers of the
* form "%LV", where L is the number of characters to be read, and V is one of
* {Y, M, D, h, m, s, *}, representing a year, month, day, hour, minute, second,
* or skipped (un-parsed) value, respectively.
* @param[in] formatLen Length of the format string pFormat.
* @param[out] pDateElements The deconstructed date representation of pDate.
*
* @return #SigV4Success if all format specifiers were matched successfully,
* #SigV4ISOFormattingError otherwise.
*/
static SigV4Status_t parseDate( const char * pDate,
size_t dateLen,
const char * pFormat,
size_t formatLen,
SigV4DateTime_t * pDateElements );
/**
* @brief Verify input parameters to the SigV4_GenerateHTTPAuthorization API.
*
* @param[in] pParams Complete SigV4 configurations passed by application.
* @param[in] pAuthBuf The user-supplied buffer for filling Authorization Header.
* @param[in] authBufLen The user-supplied size value of @p pAuthBuf buffer.
* @param[in] pSignature The user-supplied pointer memory to store starting location of
* Signature in Authorization Buffer.
* @param[in] signatureLen The user-supplied pointer to store length of Signature.
*
* @return #SigV4Success if successful, #SigV4InvalidParameter otherwise.
*/
static SigV4Status_t verifyParamsToGenerateAuthHeaderApi( const SigV4Parameters_t * pParams,
const char * pAuthBuf,
const size_t * authBufLen,
char * const * pSignature,
const size_t * signatureLen );
/**
* @brief Assign default arguments based on parameters set in @p pParams.
*
* @param[in] pParams Complete SigV4 configurations passed by application.
* @param[out] pAlgorithm The algorithm used for SigV4 authentication.
* @param[out] algorithmLen The length of @p pAlgorithm.
*/
static void assignDefaultArguments( const SigV4Parameters_t * pParams,
const char ** pAlgorithm,
size_t * algorithmLen );
/**
* @brief Hex digest of provided string parameter.
*
* @param[in] pInputStr String to encode.
* @param[out] pHexOutput Hex representation of @p pInputStr.
*
* @return #SigV4Success if successful, #SigV4InsufficientMemory otherwise.
*/
static SigV4Status_t lowercaseHexEncode( const SigV4String_t * pInputStr,
SigV4String_t * pHexOutput );
/**
* @brief Calculate number of bytes needed for the credential scope.
* @note This does not include the linefeed character.
*
* @param[in] pSigV4Params SigV4 configurations passed by application.
*
* @return Number of bytes needed for credential scope.
*/
static size_t sizeNeededForCredentialScope( const SigV4Parameters_t * pSigV4Params );
/**
* @brief Copy a string into a char * buffer.
* @note This function can be used to copy a string literal without
* MISRA warnings.
*
* @note This function assumes the destination buffer is large enough to hold
* the string to copy, so will always write @p length bytes.
*
* @param[in] destination The buffer to write.
* @param[in] source String to copy.
* @param[in] length Number of characters to copy.
*
* @return @p length The number of characters written from @p source into
* @p destination.
*/
static size_t copyString( char * destination,
const char * source,
size_t length );
/*-----------------------------------------------------------*/
static void intToAscii( int32_t value,
char ** pBuffer,
size_t bufferLen )
{
int32_t currentVal = value;
size_t lenRemaining = bufferLen;
assert( pBuffer != NULL );
assert( bufferLen > 0U );
/* Write base-10 remainder in its ASCII representation, and fill any
* remaining width with '0' characters. */
while( lenRemaining > 0U )
{
lenRemaining--;
( *pBuffer )[ lenRemaining ] = ( char ) ( ( currentVal % 10 ) + '0' );
currentVal /= 10;
}
/* Move pointer to follow last written character. */
*pBuffer = &( ( *pBuffer )[ bufferLen ] );
}