Azure IoT common library
Dependents: STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more
Diff: hmac.c
- Revision:
- 0:fa2de1b79154
- Child:
- 19:2e0811512ceb
diff -r 000000000000 -r fa2de1b79154 hmac.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hmac.c Fri Apr 08 12:01:36 2016 -0700 @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/**************************** hmac.c ****************************/ +/******************** See RFC 4634 for details ******************/ +/* +* Description: +* This file implements the HMAC algorithm (Keyed-Hashing for +* Message Authentication, RFC2104), expressed in terms of the +* various SHA algorithms. +*/ + +#include <stdlib.h> +#ifdef _CRTDBG_MAP_ALLOC +#include <crtdbg.h> +#endif +#include "azure_c_shared_utility/gballoc.h" + +#include "azure_c_shared_utility/sha.h" + +/* +* hmac +* +* Description: +* This function will compute an HMAC message digest. +* +* Parameters: +* whichSha: [in] +* One of SHA1, SHA224, SHA256, SHA384, SHA512 +* key: [in] +* The secret shared key. +* key_len: [in] +* The length of the secret shared key. +* message_array: [in] +* An array of characters representing the message. +* length: [in] +* The length of the message in message_array +* digest: [out] +* Where the digest is returned. +* NOTE: The length of the digest is determined by +* the value of whichSha. +* +* Returns: +* sha Error Code. +* +*/ +int hmac(SHAversion whichSha, const unsigned char *text, int text_len, + const unsigned char *key, int key_len, + uint8_t digest[USHAMaxHashSize]) +{ + HMACContext ctx; + return hmacReset(&ctx, whichSha, key, key_len) || + hmacInput(&ctx, text, text_len) || + hmacResult(&ctx, digest); +} + +/* +* hmacReset +* +* Description: +* This function will initialize the hmacContext in preparation +* for computing a new HMAC message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* whichSha: [in] +* One of SHA1, SHA224, SHA256, SHA384, SHA512 +* key: [in] +* The secret shared key. +* key_len: [in] +* The length of the secret shared key. +* +* Returns: +* sha Error Code. +* +*/ +int hmacReset(HMACContext *ctx, enum SHAversion whichSha, + const unsigned char *key, int key_len) +{ + int i, blocksize, hashsize; + + /* inner padding - key XORd with ipad */ + unsigned char k_ipad[USHA_Max_Message_Block_Size]; + + /* temporary buffer when keylen > blocksize */ + unsigned char tempkey[USHAMaxHashSize]; + + if (!ctx) return shaNull; + + blocksize = ctx->blockSize = USHABlockSize(whichSha); + hashsize = ctx->hashSize = USHAHashSize(whichSha); + + ctx->whichSha = whichSha; + + /* + * If key is longer than the hash blocksize, + * reset it to key = HASH(key). + */ + if (key_len > blocksize) { + USHAContext tctx; + int err = USHAReset(&tctx, whichSha) || + USHAInput(&tctx, key, key_len) || + USHAResult(&tctx, tempkey); + if (err != shaSuccess) return err; + + key = tempkey; + key_len = hashsize; + } + + /* + * The HMAC transform looks like: + * + * SHA(K XOR opad, SHA(K XOR ipad, text)) + * + * where K is an n byte key. + * ipad is the byte 0x36 repeated blocksize times + * opad is the byte 0x5c repeated blocksize times + * and text is the data being protected. + */ + + /* store key into the pads, XOR'd with ipad and opad values */ + for (i = 0; i < key_len; i++) { + k_ipad[i] = key[i] ^ 0x36; + ctx->k_opad[i] = key[i] ^ 0x5c; + } + /* remaining pad bytes are '\0' XOR'd with ipad and opad values */ + for (; i < blocksize; i++) { + k_ipad[i] = 0x36; + ctx->k_opad[i] = 0x5c; + } + + /* perform inner hash */ + /* init context for 1st pass */ + return USHAReset(&ctx->shaContext, whichSha) || + /* and start with inner pad */ + USHAInput(&ctx->shaContext, k_ipad, blocksize); +} + +/* +* hmacInput +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The HMAC context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int hmacInput(HMACContext *ctx, const unsigned char *text, + int text_len) +{ + if (!ctx) return shaNull; + /* then text of datagram */ + return USHAInput(&ctx->shaContext, text, text_len); +} + +/* +* HMACFinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The HMAC context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +*/ +int hmacFinalBits(HMACContext *ctx, + const uint8_t bits, + unsigned int bitcount) +{ + if (!ctx) return shaNull; + /* then final bits of datagram */ + return USHAFinalBits(&ctx->shaContext, bits, bitcount); +} + +/* +* HMACResult +* +* Description: +* This function will return the N-byte message digest into the +* Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the Nth element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the HMAC hash. +* digest: [out] +* Where the digest is returned. +* NOTE 2: The length of the hash is determined by the value of +* whichSha that was passed to hmacReset(). +* +* Returns: +* sha Error Code. +* +*/ +int hmacResult(HMACContext *ctx, uint8_t *digest) +{ + if (!ctx) return shaNull; + + /* finish up 1st pass */ + /* (Use digest here as a temporary buffer.) */ + return USHAResult(&ctx->shaContext, digest) || + + /* perform outer SHA */ + /* init context for 2nd pass */ + USHAReset(&ctx->shaContext, ctx->whichSha) || + + /* start with outer pad */ + USHAInput(&ctx->shaContext, ctx->k_opad, ctx->blockSize) || + + /* then results of 1st hash */ + USHAInput(&ctx->shaContext, digest, ctx->hashSize) || + + /* finish up 2nd pass */ + USHAResult(&ctx->shaContext, digest); +} + +