/** * @file hmac.c * @brief HMAC (Keyed-Hashing for Message Authentication) * * @section License * * SPDX-License-Identifier: GPL-2.0-or-later * * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved. * * This file is part of CycloneCRYPTO Open. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @section Description * * HMAC is a mechanism for message authentication using cryptographic hash * functions. HMAC can be used with any iterative cryptographic hash * function (MD5, SHA-1 or SHA-256) in combination with a secret shared * key. Refer to RFC 2104 for more details * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 2.4.4 **/ //Switch to the appropriate trace level #define TRACE_LEVEL CRYPTO_TRACE_LEVEL //Dependencies #include "core/crypto.h" #include "mac/hmac.h" //Check crypto library configuration #if (HMAC_SUPPORT == ENABLED) //HMAC with MD5 OID (1.3.6.1.5.5.8.1.1) const uint8_t HMAC_WITH_MD5_OID[8] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x08, 0x01, 0x01}; //HMAC with Tiger OID (1.3.6.1.5.5.8.1.3) const uint8_t HMAC_WITH_TIGER_OID[8] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x08, 0x01, 0x03}; //HMAC with RIPEMD-160 OID (1.3.6.1.5.5.8.1.4) const uint8_t HMAC_WITH_RIPEMD160_OID[8] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x08, 0x01, 0x04}; //HMAC with SHA-1 OID (1.2.840.113549.2.7) const uint8_t HMAC_WITH_SHA1_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x07}; //HMAC with SHA-224 OID (1.2.840.113549.2.8) const uint8_t HMAC_WITH_SHA224_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x08}; //HMAC with SHA-256 OID (1.2.840.113549.2.9) const uint8_t HMAC_WITH_SHA256_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x09}; //HMAC with SHA-384 OID (1.2.840.113549.2.10) const uint8_t HMAC_WITH_SHA384_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0A}; //HMAC with SHA-512 OID (1.2.840.113549.2.11) const uint8_t HMAC_WITH_SHA512_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0B}; //HMAC with SHA-512/224 OID (1.2.840.113549.2.12) const uint8_t HMAC_WITH_SHA512_224_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0C}; //HMAC with SHA-512/256 OID (1.2.840.113549.2.13) const uint8_t HMAC_WITH_SHA512_256_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0D}; //HMAC with SHA-3-224 OID (2.16.840.1.101.3.4.2.13) const uint8_t HMAC_WITH_SHA3_224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0D}; //HMAC with SHA-3-256 OID (2.16.840.1.101.3.4.2.14) const uint8_t HMAC_WITH_SHA3_256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0E}; //HMAC with SHA-3-384 OID (2.16.840.1.101.3.4.2.15) const uint8_t HMAC_WITH_SHA3_384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0F}; //HMAC with SHA-3-512 OID (2.16.840.1.101.3.4.2.16) const uint8_t HMAC_WITH_SHA3_512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x10}; //HMAC with SM3 OID (1.2.156.10197.1.401.3.1) const uint8_t HMAC_WITH_SM3_OID[10] = {0x2A, 0x81, 0x1C, 0xCF, 0x55, 0x01, 0x82, 0x91, 0x03, 0x01}; /** * @brief Compute HMAC using the specified hash function * @param[in] hash Hash algorithm used to compute HMAC * @param[in] key Key to use in the hash algorithm * @param[in] keyLen Length of the key * @param[in] data The input data for which to compute the hash code * @param[in] dataLen Length of the input data * @param[out] digest The computed HMAC value * @return Error code **/ __weak_func error_t hmacCompute(const HashAlgo *hash, const void *key, size_t keyLen, const void *data, size_t dataLen, uint8_t *digest) { error_t error; #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED) HmacContext *context; #else HmacContext context[1]; #endif #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED) //Allocate a memory buffer to hold the HMAC context context = cryptoAllocMem(sizeof(HmacContext)); //Failed to allocate memory? if(context == NULL) return ERROR_OUT_OF_MEMORY; #endif //Initialize the HMAC context error = hmacInit(context, hash, key, keyLen); //Check status code if(!error) { //Digest the message hmacUpdate(context, data, dataLen); //Finalize the HMAC computation hmacFinal(context, digest); } #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED) //Free previously allocated memory cryptoFreeMem(context); #endif //Return status code return error; } /** * @brief Initialize HMAC calculation * @param[in] context Pointer to the HMAC context to initialize * @param[in] hash Hash algorithm used to compute HMAC * @param[in] key Key to use in the hash algorithm * @param[in] keyLen Length of the key * @return Error code **/ __weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen) { uint_t i; //Check parameters if(context == NULL || hash == NULL) return ERROR_INVALID_PARAMETER; //Make sure the supplied key is valid if(key == NULL && keyLen != 0) return ERROR_INVALID_PARAMETER; //Hash algorithm used to compute HMAC context->hash = hash; //The key is longer than the block size? if(keyLen > hash->blockSize) { //Initialize the hash function context hash->init(&context->hashContext); //Digest the original key hash->update(&context->hashContext, key, keyLen); //Finalize the message digest computation hash->final(&context->hashContext, context->key); //Key is padded to the right with extra zeros osMemset(context->key + hash->digestSize, 0, hash->blockSize - hash->digestSize); } else { //Copy the key osMemcpy(context->key, key, keyLen); //Key is padded to the right with extra zeros osMemset(context->key + keyLen, 0, hash->blockSize - keyLen); } //XOR the resulting key with ipad for(i = 0; i < hash->blockSize; i++) { context->key[i] ^= HMAC_IPAD; } //Initialize context for the first pass hash->init(&context->hashContext); //Start with the inner pad hash->update(&context->hashContext, context->key, hash->blockSize); //Successful initialization return NO_ERROR; } /** * @brief Update the HMAC context with a portion of the message being hashed * @param[in] context Pointer to the HMAC context * @param[in] data Pointer to the buffer being hashed * @param[in] length Length of the buffer **/ __weak_func void hmacUpdate(HmacContext *context, const void *data, size_t length) { const HashAlgo *hash; //Hash algorithm used to compute HMAC hash = context->hash; //Digest the message (first pass) hash->update(&context->hashContext, data, length); } /** * @brief Finish the HMAC calculation * @param[in] context Pointer to the HMAC context * @param[out] digest Calculated HMAC value (optional parameter) **/ __weak_func void hmacFinal(HmacContext *context, uint8_t *digest) { uint_t i; const HashAlgo *hash; //Hash algorithm used to compute HMAC hash = context->hash; //Finish the first pass hash->final(&context->hashContext, context->digest); //XOR the original key with opad for(i = 0; i < hash->blockSize; i++) { context->key[i] ^= HMAC_IPAD ^ HMAC_OPAD; } //Initialize context for the second pass hash->init(&context->hashContext); //Start with outer pad hash->update(&context->hashContext, context->key, hash->blockSize); //Then digest the result of the first hash hash->update(&context->hashContext, context->digest, hash->digestSize); //Finish the second pass hash->final(&context->hashContext, context->digest); //Copy the resulting HMAC value if(digest != NULL) { osMemcpy(digest, context->digest, hash->digestSize); } } /** * @brief Release HMAC context * @param[in] context Pointer to the HMAC context **/ void hmacDeinit(HmacContext *context) { //Make sure the HMAC context is valid if(context != NULL) { //Clear HMAC context osMemset(context, 0, sizeof(HmacContext)); } } /** * @brief Finish the HMAC calculation (no padding added) * @param[in] context Pointer to the HMAC context * @param[out] digest Calculated HMAC value (optional parameter) **/ void hmacFinalRaw(HmacContext *context, uint8_t *digest) { uint_t i; const HashAlgo *hash; //Hash algorithm used to compute HMAC hash = context->hash; //XOR the original key with opad for(i = 0; i < hash->blockSize; i++) { context->key[i] ^= HMAC_IPAD ^ HMAC_OPAD; } //Initialize context for the second pass hash->init(&context->hashContext); //Start with outer pad hash->update(&context->hashContext, context->key, hash->blockSize); //Then digest the result of the first hash hash->update(&context->hashContext, context->digest, hash->digestSize); //Finish the second pass hash->final(&context->hashContext, context->digest); //Copy the resulting HMAC value if(digest != NULL) { osMemcpy(digest, context->digest, hash->digestSize); } } #endif