Skip to main content

8. C Code for SHAs, HMAC, and HKDF

Below is a demonstration implementation of these secure hash functions in C. Section 8.1 contains the header file sha.h that declares all constants, structures, and functions used by the SHA and HMAC functions. It includes conditionals based on the state of definition of USE_32BIT_ONLY that, if that symbol is defined at compile time, avoids 64-bit operations. It also contains sha-private.h that provides some declarations common to all the SHA functions. Section 8.2 contains the C code for sha1.c, sha224-256.c, sha384-512.c, and usha.c. Section 8.3 contains the C code for the HMAC functions, and Section 8.4 contains the C code for HKDF. Section 8.5 contains a test driver to exercise the code.

API Overview

For each of the digest lengths $$$, there is the following set of constants, a structure, and functions:

Constants:

  • SHA$$$HashSize - number of octets in the hash
  • SHA$$$HashSizeBits - number of bits in the hash
  • SHA$$$_Message_Block_Size - number of octets used in the intermediate message blocks

Most functions return an enum value that is one of:

  • shaSuccess(0) - on success
  • shaNull(1) - when presented with a null pointer parameter
  • shaInputTooLong(2) - when the input data is too long
  • shaStateError(3) - when SHA$$$Input is called after SHA$$$FinalBits or SHA$$$Result

Structure:

typedef SHA$$$Context

An opaque structure holding the complete state for producing the hash

Functions:

int SHA$$$Reset(SHA$$$Context *context);

Reset the hash context state.

int SHA$$$Input(SHA$$$Context *context, const uint8_t *octets,
unsigned int bytecount);

Incorporate bytecount octets into the hash.

int SHA$$$FinalBits(SHA$$$Context *, const uint8_t octet,
unsigned int bitcount);

Incorporate bitcount bits into the hash. The bits are in the upper portion of the octet. SHA$$$Input() cannot be called after this.

int SHA$$$Result(SHA$$$Context *,
uint8_t Message_Digest[SHA$$$HashSize]);

Do the final calculations on the hash and copy the value into Message_Digest.

USHA Interface

In addition, functions with the prefix USHA are provided that take a SHAversion value (SHA$$$) to select the SHA function suite. They add the following constants, structure, and functions:

Constants:

  • shaBadParam(4) - constant returned by USHA functions when presented with a bad SHAversion (SHA$$$) parameter or other illegal parameter values
  • USAMaxHashSize - maximum of the SHA hash sizes
  • SHA$$$ - SHAversion enumeration values, used by USHA, HMAC, and HKDF functions to select the SHA function suite

Structure:

typedef USHAContext

An opaque structure holding the complete state for producing the hash

Functions:

int USHAReset(USHAContext *context, SHAversion whichSha);

Reset the hash context state.

int USHAInput(USHAContext context*,
const uint8_t *bytes, unsigned int bytecount);

Incorporate bytecount octets into the hash.

int USHAFinalBits(USHAContext *context,
const uint8_t bits, unsigned int bitcount);

Incorporate bitcount bits into the hash.

int USHAResult(USHAContext *context,
uint8_t Message_Digest[USHAMaxHashSize]);

Do the final calculations on the hash and copy the value into Message_Digest. Octets in Message_Digest beyond USHAHashSize(whichSha) are left untouched.

int USHAHashSize(enum SHAversion whichSha);

The number of octets in the given hash.

int USHAHashSizeBits(enum SHAversion whichSha);

The number of bits in the given hash.

int USHABlockSize(enum SHAversion whichSha);

The internal block size for the given hash.

const char *USHAHashName(enum SHAversion whichSha);

This function will return the name of the given SHA algorithm as a string.

HMAC Interface

The HMAC functions follow the same pattern to allow any length of text input to be used.

Structure:

typedef HMACContext

An opaque structure holding the complete state for producing the keyed message digest (MAC)

Functions:

int hmacReset(HMACContext *ctx, enum SHAversion whichSha,
const unsigned char *key, int key_len);

Reset the MAC context state.

int hmacInput(HMACContext *ctx, const unsigned char *text,
int text_len);

Incorporate text_len octets into the MAC.

int hmacFinalBits(HMACContext *ctx, const uint8_t bits,
unsigned int bitcount);

Incorporate bitcount bits into the MAC.

int hmacResult(HMACContext *ctx,
uint8_t Message_Digest[USHAMaxHashSize]);

Do the final calculations on the MAC and copy the value into Message_Digest.

In addition, combined interfaces are provided, similar to that shown in [RFC2104], that allows a fixed-length text input to be used:

int hmac(SHAversion whichSha,
const unsigned char *text, int text_len,
const unsigned char *key, int key_len,
uint8_t digest[USHAMaxHashSize])

This combined interface performs the complete HMAC computation. The variables are the same as in the separate calls above.

HKDF Interface

The HKDF functions are patterned similar to the HMAC functions.

Structure:

typedef HKDFContext

An opaque structure holding the complete state for performing HKDF extract-and-expand key derivation

Functions:

int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
const unsigned char *salt, int salt_len);

Reset the HKDF context state.

int hkdfInput(HKDFContext *context, const unsigned char *ikm,
int ikm_len);

Incorporate ikm_len octets into the entropy extractor.

int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
unsigned int ikm_bit_count)

Incorporate ikm_bit_count bits into the entropy extractor.

int hkdfResult(HKDFContext *context,
uint8_t prk[USHAMaxHashSize],
const unsigned char *info, int info_len,
uint8_t okm[ ], int okm_len)

Finish the HKDF extraction and perform the final HKDF expansion, storing the okm_len octets into output keying material (okm). Optionally store the pseudo-random key (prk) that is generated internally.

In addition, combined interfaces are provided, similar to that shown in [RFC5869], that allows a fixed-length text input to be used:

int hkdfExtract(SHAversion whichSha,
const unsigned char *salt, int salt_len,
const unsigned char *ikm, int ikm_len,
uint8_t prk[USHAMaxHashSize])

Perform HKDF extraction, combining the salt_len octets of the optional salt with the ikm_len octets of the input keying material (ikm) to form the pseudo-random key prk. The output prk must be large enough to hold the octets appropriate for the given hash type.

int hkdfExpand(SHAversion whichSha,
const uint8_t prk[ ], int prk_len,
const unsigned char *info, int info_len,
uint8_t okm[ ], int okm_len)

Perform HKDF expansion, combining the prk_len octets of the pseudo-random key prk with the info_len octets of info to form the okm_len octets stored in okm.

int hkdf(SHAversion whichSha,
const unsigned char *salt, int salt_len,
const unsigned char *ikm, int ikm_len,
const unsigned char *info, int info_len,
uint8_t okm[ ], int okm_len)

This combined interface performs both HKDF extraction and expansion. The variables are the same as in hkdfExtract() and hkdfExpand().

Subsections