Nigel Rantor / azure_c_shared_utility

Fork of azure_c_shared_utility by Azure IoT

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers hmac.c Source File

hmac.c

00001 // Copyright (c) Microsoft. All rights reserved.
00002 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
00003 
00004 /**************************** hmac.c ****************************/
00005 /******************** See RFC 4634 for details ******************/
00006 /*
00007 *  Description:
00008 *      This file implements the HMAC algorithm (Keyed-Hashing for
00009 *      Message Authentication, RFC2104), expressed in terms of the
00010 *      various SHA algorithms.
00011 */
00012 
00013 #include "azure_c_shared_utility/sha.h"
00014 
00015 /*
00016 *  hmac
00017 *
00018 *  Description:
00019 *      This function will compute an HMAC message digest.
00020 *
00021 *  Parameters:
00022 *      whichSha: [in]
00023 *          One of SHA1, SHA224, SHA256, SHA384, SHA512
00024 *      key: [in]
00025 *          The secret shared key.
00026 *      key_len: [in]
00027 *          The length of the secret shared key.
00028 *      message_array: [in]
00029 *          An array of characters representing the message.
00030 *      length: [in]
00031 *          The length of the message in message_array
00032 *      digest: [out]
00033 *          Where the digest is returned.
00034 *          NOTE: The length of the digest is determined by
00035 *              the value of whichSha.
00036 *
00037 *  Returns:
00038 *      sha Error Code.
00039 *
00040 */
00041 int hmac(SHAversion whichSha, const unsigned char *text, int text_len,
00042     const unsigned char *key, int key_len,
00043     uint8_t digest[USHAMaxHashSize])
00044 {
00045     HMACContext ctx;
00046     return hmacReset(&ctx, whichSha, key, key_len) ||
00047         hmacInput(&ctx, text, text_len) ||
00048         hmacResult(&ctx, digest);
00049 }
00050 
00051 /*
00052 *  hmacReset
00053 *
00054 *  Description:
00055 *      This function will initialize the hmacContext in preparation
00056 *      for computing a new HMAC message digest.
00057 *
00058 *  Parameters:
00059 *      context: [in/out]
00060 *          The context to reset.
00061 *      whichSha: [in]
00062 *          One of SHA1, SHA224, SHA256, SHA384, SHA512
00063 *      key: [in]
00064 *          The secret shared key.
00065 *      key_len: [in]
00066 *          The length of the secret shared key.
00067 *
00068 *  Returns:
00069 *      sha Error Code.
00070 *
00071 */
00072 int hmacReset(HMACContext *ctx, enum SHAversion whichSha,
00073     const unsigned char *key, int key_len)
00074 {
00075     int i, blocksize, hashsize;
00076 
00077     /* inner padding - key XORd with ipad */
00078     unsigned char k_ipad[USHA_Max_Message_Block_Size];
00079 
00080     /* temporary buffer when keylen > blocksize */
00081     unsigned char tempkey[USHAMaxHashSize];
00082 
00083     if (!ctx) return shaNull;
00084 
00085     blocksize = ctx->blockSize = USHABlockSize(whichSha);
00086     hashsize = ctx->hashSize = USHAHashSize(whichSha);
00087 
00088     ctx->whichSha = whichSha;
00089 
00090     /*
00091     * If key is longer than the hash blocksize,
00092     * reset it to key = HASH(key).
00093     */
00094     if (key_len > blocksize) {
00095         USHAContext tctx;
00096         int err = USHAReset(&tctx, whichSha) ||
00097             USHAInput(&tctx, key, key_len) ||
00098             USHAResult(&tctx, tempkey);
00099         if (err != shaSuccess) return err;
00100 
00101         key = tempkey;
00102         key_len = hashsize;
00103     }
00104 
00105     /*
00106     * The HMAC transform looks like:
00107     *
00108     * SHA(K XOR opad, SHA(K XOR ipad, text))
00109     *
00110     * where K is an n byte key.
00111     * ipad is the byte 0x36 repeated blocksize times
00112     * opad is the byte 0x5c repeated blocksize times
00113     * and text is the data being protected.
00114     */
00115 
00116     /* store key into the pads, XOR'd with ipad and opad values */
00117     for (i = 0; i < key_len; i++) {
00118         k_ipad[i] = key[i] ^ 0x36;
00119         ctx->k_opad[i] = key[i] ^ 0x5c;
00120     }
00121     /* remaining pad bytes are '\0' XOR'd with ipad and opad values */
00122     for (; i < blocksize; i++) {
00123         k_ipad[i] = 0x36;
00124         ctx->k_opad[i] = 0x5c;
00125     }
00126 
00127     /* perform inner hash */
00128     /* init context for 1st pass */
00129     return USHAReset(&ctx->shaContext, whichSha) ||
00130         /* and start with inner pad */
00131         USHAInput(&ctx->shaContext, k_ipad, blocksize);
00132 }
00133 
00134 /*
00135 *  hmacInput
00136 *
00137 *  Description:
00138 *      This function accepts an array of octets as the next portion
00139 *      of the message.
00140 *
00141 *  Parameters:
00142 *      context: [in/out]
00143 *          The HMAC context to update
00144 *      message_array: [in]
00145 *          An array of characters representing the next portion of
00146 *          the message.
00147 *      length: [in]
00148 *          The length of the message in message_array
00149 *
00150 *  Returns:
00151 *      sha Error Code.
00152 *
00153 */
00154 int hmacInput(HMACContext *ctx, const unsigned char *text,
00155     int text_len)
00156 {
00157     if (!ctx) return shaNull;
00158     /* then text of datagram */
00159     return USHAInput(&ctx->shaContext, text, text_len);
00160 }
00161 
00162 /*
00163 * HMACFinalBits
00164 *
00165 * Description:
00166 *   This function will add in any final bits of the message.
00167 *
00168 * Parameters:
00169 *   context: [in/out]
00170 *     The HMAC context to update
00171 *   message_bits: [in]
00172 *     The final bits of the message, in the upper portion of the
00173 *     byte. (Use 0b###00000 instead of 0b00000### to input the
00174 *     three bits ###.)
00175 *   length: [in]
00176 *     The number of bits in message_bits, between 1 and 7.
00177 *
00178 * Returns:
00179 *   sha Error Code.
00180 */
00181 int hmacFinalBits(HMACContext *ctx,
00182     const uint8_t bits,
00183     unsigned int bitcount)
00184 {
00185     if (!ctx) return shaNull;
00186     /* then final bits of datagram */
00187     return USHAFinalBits(&ctx->shaContext, bits, bitcount);
00188 }
00189 
00190 /*
00191 * HMACResult
00192 *
00193 * Description:
00194 *   This function will return the N-byte message digest into the
00195 *   Message_Digest array provided by the caller.
00196 *   NOTE: The first octet of hash is stored in the 0th element,
00197 *      the last octet of hash in the Nth element.
00198 *
00199 * Parameters:
00200 *   context: [in/out]
00201 *     The context to use to calculate the HMAC hash.
00202 *   digest: [out]
00203 *     Where the digest is returned.
00204 *   NOTE 2: The length of the hash is determined by the value of
00205 *      whichSha that was passed to hmacReset().
00206 *
00207 * Returns:
00208 *   sha Error Code.
00209 *
00210 */
00211 int hmacResult(HMACContext *ctx, uint8_t *digest)
00212 {
00213     if (!ctx) return shaNull;
00214 
00215     /* finish up 1st pass */
00216     /* (Use digest here as a temporary buffer.) */
00217     return USHAResult(&ctx->shaContext, digest) ||
00218 
00219         /* perform outer SHA */
00220         /* init context for 2nd pass */
00221         USHAReset(&ctx->shaContext, (SHAversion)ctx->whichSha) ||
00222 
00223         /* start with outer pad */
00224         USHAInput(&ctx->shaContext, ctx->k_opad, ctx->blockSize) ||
00225 
00226         /* then results of 1st hash */
00227         USHAInput(&ctx->shaContext, digest, ctx->hashSize) ||
00228 
00229         /* finish up 2nd pass */
00230         USHAResult(&ctx->shaContext, digest);
00231 }
00232 
00233