// 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 "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, (SHAversion)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); }