跳到主要内容

8. SHAs, HMAC 和 HKDF 的 C 代码

下面给出这些安全哈希函数的 C 语言演示实现. 第 8.1 节包含头文件 sha.h, 该文件声明 SHA 和 HMAC 函数使用的所有常量, 结构和函数. 它包含基于 USE_32BIT_ONLY 定义状态的条件逻辑; 如果该符号在编译时被定义, 则避免 64 位操作. 它还包含 sha-private.h, 后者提供所有 SHA 函数共有的一些声明. 第 8.2 节包含 sha1.c, sha224-256.c, sha384-512.c 和 usha.c 的 C 代码. 第 8.3 节包含 HMAC 函数的 C 代码, 第 8.4 节包含 HKDF 的 C 代码. 第 8.5 节包含用于运行这些代码的测试驱动.

API 概览

对于每个摘要长度 $$$, 都有以下常量集合, 一个结构和若干函数:

常量:

  • SHA$$$HashSize - 哈希中的八位组数量
  • SHA$$$HashSizeBits - 哈希中的位数
  • SHA$$$_Message_Block_Size - 中间消息块中使用的八位组数量

大多数函数返回以下枚举值之一:

  • shaSuccess(0) - 成功时
  • shaNull(1) - 传入空指针参数时
  • shaInputTooLong(2) - 输入数据过长时
  • shaStateError(3) - 在 SHA$$$FinalBits 或 SHA$$$Result 之后调用 SHA$$$Input 时

结构:

typedef SHA$$$Context

保存生成哈希所需完整状态的不透明结构.

函数:

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

重置哈希上下文状态.

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

将 bytecount 个八位组纳入哈希.

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

将 bitcount 位纳入哈希. 这些位位于该八位组的高位部分. 之后不能再调用 SHA$$$Input().

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

对哈希执行最终计算, 并将值复制到 Message_Digest.

USHA 接口

此外, 还提供带 USHA 前缀的函数, 它们接受 SHAversion 值 (SHA$$$) 以选择 SHA 函数组. 它们新增以下常量, 结构和函数:

常量:

  • shaBadParam(4) - 当传入错误的 SHAversion (SHA$$$) 参数或其他非法参数值时, USHA 函数返回的常量
  • USAMaxHashSize - SHA 哈希大小的最大值
  • SHA$$$ - SHAversion 枚举值, 由 USHA, HMAC 和 HKDF 函数用于选择 SHA 函数组

结构:

typedef USHAContext

保存生成哈希所需完整状态的不透明结构.

函数:

int USHAReset(USHAContext *context, SHAversion whichSha);

重置哈希上下文状态.

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

将 bytecount 个八位组纳入哈希.

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

将 bitcount 位纳入哈希.

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

对哈希执行最终计算, 并将值复制到 Message_Digest. Message_Digest 中超出 USHAHashSize(whichSha) 的八位组保持不变.

int USHAHashSize(enum SHAversion whichSha);

给定哈希中的八位组数量.

int USHAHashSizeBits(enum SHAversion whichSha);

给定哈希中的位数.

int USHABlockSize(enum SHAversion whichSha);

给定哈希的内部块大小.

const char *USHAHashName(enum SHAversion whichSha);

此函数以字符串形式返回给定 SHA 算法的名称.

HMAC 接口

HMAC 函数遵循相同模式, 以允许使用任意长度的文本输入.

结构:

typedef HMACContext

保存生成带密钥消息摘要 (MAC) 所需完整状态的不透明结构.

函数:

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

重置 MAC 上下文状态.

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

将 text_len 个八位组纳入 MAC.

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

将 bitcount 位纳入 MAC.

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

对 MAC 执行最终计算, 并将值复制到 Message_Digest.

此外, 还提供类似 [RFC2104] 所示的组合接口, 允许使用固定长度的文本输入:

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

该组合接口执行完整的 HMAC 计算. 变量与上面的分离调用相同.

HKDF 接口

HKDF 函数的模式类似于 HMAC 函数.

结构:

typedef HKDFContext

保存执行 HKDF 提取并扩展密钥派生所需完整状态的不透明结构.

函数:

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

重置 HKDF 上下文状态.

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

将 ikm_len 个八位组纳入熵提取器.

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

将 ikm_bit_count 位纳入熵提取器.

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

完成 HKDF 提取并执行最终 HKDF 扩展, 将 okm_len 个八位组存入输出密钥材料 (okm). 可选地存储内部生成的伪随机密钥 (prk).

此外, 还提供类似 [RFC5869] 所示的组合接口, 允许使用固定长度的文本输入:

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

执行 HKDF 提取, 将可选 salt 的 salt_len 个八位组与输入密钥材料 (ikm) 的 ikm_len 个八位组合并, 形成伪随机密钥 prk. 输出 prk 必须足够大, 能容纳给定哈希类型所需的八位组.

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

执行 HKDF 扩展, 将伪随机密钥 prk 的 prk_len 个八位组与 info 的 info_len 个八位组合并, 形成存储在 okm 中的 okm_len 个八位组.

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)

该组合接口同时执行 HKDF 提取和扩展. 变量与 hkdfExtract() 和 hkdfExpand() 中相同.

子章节