5
\$\begingroup\$

I am trying out a small piece of code that would generate HMAC-SHA1. I have been asked to code the HMAC implementation myself using the OpenSSL libs for SHA1 calculation.

After 'wiki'ing for the algorithm, here is what I have below. I have used input with RFC 2104 specified test values:

key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
key_len = 16 bytes
data = "Hi There"
data_len = 8 bytes digest = 0x9294727a3638bb1c13f48ef8158bfc9d

But the output I get is:

675b0b3a1b4ddf4e124872da6c2f632bfed957e9*

Is this really not the way to implement HMAC? How else can this be done?

#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/err.h>
#include <openssl/conf.h>
#include <openssl/engine.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>     /* for memset() */
#include <unistd.h>

#define IPAD 0x36
#define OPAD 0x5C

#define SHA1_DIGESTLENGTH 20
#define SHA1_BLOCK_LENGTH 64
#define COUNTER_LENGTH 8

typedef unsigned          char uint8_t;
typedef unsigned short     int uint16_t;
typedef unsigned           int uint32_t;

/**
 * Key
 */
#define SECRET { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b , 0x0b , 0x0b , 0x0b , 0x0b , 0x0b , 0x0b , 0x0b , 0x0b}
#define COUNTER {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
/*
 * hmac sha1.
 */
int main(int argc, char *argv[]){
    unsigned char ipad[SHA1_BLOCK_LENGTH];
    unsigned char opad[SHA1_BLOCK_LENGTH];
    unsigned char algKey[SHA1_BLOCK_LENGTH];
    unsigned char msgBuffer[SHA1_BLOCK_LENGTH + COUNTER_LENGTH];
    unsigned char valBuffer[SHA1_BLOCK_LENGTH + 20];
    unsigned int i;
    uint8_t ctr[] = "Hi There";
    uint8_t hash[20];
    uint8_t key[]= SECRET;
    int len = 16;
    memset(hash, 0, sizeof(hash));
    memset(algKey, 0, sizeof(algKey));
    strncpy((char *)algKey, (char *)key, len);

    memset(ipad, IPAD, sizeof(ipad));
    memset(opad, OPAD, sizeof(opad));

    for (i = 0; i < SHA1_BLOCK_LENGTH; i++){
        ipad[i] ^= algKey[i];
    }

    for (i = 0; i < SHA1_BLOCK_LENGTH; i++){
        opad[i] ^= algKey[i];
    }

    memset(msgBuffer, 0, sizeof(msgBuffer));

    strncpy((char *)msgBuffer, (char *)ipad, SHA1_BLOCK_LENGTH);
    strncpy((char *)msgBuffer + SHA1_BLOCK_LENGTH, (char *)ctr, COUNTER_LENGTH);


    EVP_MD_CTX mdctx, md_ctx;
    const EVP_MD *md;

    unsigned char md_value[EVP_MAX_MD_SIZE];
    unsigned int md_len;

    OpenSSL_add_all_digests();


    md = EVP_get_digestbyname("sha1");

    if(!md) {
        printf("Unknown message digest\n");
        exit(1);
    }

    EVP_MD_CTX_init(&mdctx);
    EVP_DigestInit_ex(&mdctx, md, NULL);
    EVP_DigestUpdate(&mdctx, msgBuffer,(SHA1_BLOCK_LENGTH + COUNTER_LENGTH) );
    EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
    EVP_MD_CTX_cleanup(&mdctx);


    printf("Digest is: ");
    for(i = 0; i < md_len; i++) printf("%02x", md_value[i]);
    printf("\n");

    unsigned char *copy;
    copy = malloc(md_len);
    strncpy((char*)copy, (char *)md_value, md_len);


    memset(valBuffer, 0, sizeof(valBuffer));
    strncpy((char *)valBuffer, (char *)opad, SHA1_BLOCK_LENGTH);
    strncpy((char *)valBuffer + SHA1_BLOCK_LENGTH, (char *)copy, md_len);

    EVP_MD_CTX_init(&md_ctx);
    EVP_DigestInit_ex(&md_ctx, md, NULL);
    EVP_DigestUpdate(&md_ctx, valBuffer, SHA1_BLOCK_LENGTH + 20);
    printf( "updated Digest..\n");
    EVP_DigestFinal_ex(&md_ctx, md_value, &md_len);
    EVP_MD_CTX_cleanup(&md_ctx);

    printf("Digest is: ");
    for(i = 0; i < md_len; i++) printf("%02x", md_value[i]);
    printf("\n");

    return 0;
}
\$\endgroup\$
6
\$\begingroup\$
  • You can make these #defines:

    #define SHA1_DIGESTLENGTH 20
    #define SHA1_BLOCK_LENGTH 64
    #define COUNTER_LENGTH 8
    

    into a more concise enum:

    typedef enum { COUNTER=8, SHA1_DIGEST=20, SHA1_BLOCK=64 } Length;
    

    This could also be done with SECRET and COUNTER, but each hex value would need a name.

    If you use the enum recommendation, you may need to rename the second COUNTER to avoid possible name-clashing. Perhaps rename it based on what the counter is for.

  • You don't need your own typedefs for these:

    typedef unsigned           char uint8_t;
    typedef unsigned short     int uint16_t;
    typedef unsigned           int uint32_t;
    

    Prefer to use the typedefs already defined in <stdint.h>.

  • For unformatted output with a newline, use puts() instead of printf():

    puts("Some text...");
    
  • I'd recommend splitting this program into separate functions. This will make it easier to tell how the it works and make it easier to maintain. A function should have one purpose, andmain() should have minimal implementation (such as acquiring input and calling other functions).

  • There's no need to call exit(1) within main(). Just return 1.

    The return 0 is also not needed at the end. Reaching this point implies successful termination, so the compiler will do this return for you.

\$\endgroup\$
2
\$\begingroup\$

Might I suggest using OpenSSL's built in HMAC_* methods instead of re-rolling?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.