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() 中相同.
子章节
- 8.1. 头文件
- 8.1.1. sha.h
- 8.1.2. stdint-example.h
- 8.1.3. sha-private.h
- 8.2. SHA 代码
- 8.2.1. sha1.c
- 8.2.2. sha224-256.c
- 8.2.3. sha384-512.c
- 8.2.4. usha.c
- 8.3. HMAC 代码
- 8.4. HKDF 代码
- 8.5. 测试驱动