哈希与消息认证

密码学哈希会把任意数量的数据转换成一个短小、长度固定的摘要。相同的输入总是产生相同的摘要, 不同的输入(哪怕只改变一个比特)会产生完全不同的摘要,并且没有实际可行的方法能找到一个输入 使其产生某个指定的摘要。消息认证码(MAC)在这一思路上加入了一个密钥,因此摘要还能证明数据 没有被不持有该密钥的任何人篡改。

 

本页所有函数都接受并返回 TBytes, 分别位于 sgcCrypto_SHA2、 sgcCrypto_Keccak、 sgcCrypto_Blake2b、 sgcCrypto_Blake2s、 sgcCrypto_SipHash、 sgcCrypto_HMAC、 sgcCrypto_CMAC 和 sgcCrypto_Poly1305 单元中。

 

SHA-2

 

这是当今生产环境中 TLS 证书、代码签名和大多数协议背后所使用的哈希算法族。除非有特定需求要求 使用其他长度,否则请使用 SHA-256。

 


function sgcSHA1(const aData: TBytes): TBytes;
function sgcSHA224(const aData: TBytes): TBytes;
function sgcSHA256(const aData: TBytes): TBytes;
function sgcSHA384(const aData: TBytes): TBytes;
function sgcSHA512(const aData: TBytes): TBytes;

 

SHA-3、SHAKE、cSHAKE 和 KMAC

 

SHA-3 基于与 SHA-2 不同的构造方式(Keccak),因此其中一个算法族出现的弱点不会波及另一个, 这正是为何需要长期安全保障的协议往往会同时指定这两种算法。SHAKE128 和 SHAKE256 是可扩展输出函数: 与固定摘要长度不同,您可以按需索取任意字节数,并且较短的输出总是较长输出(针对同一输入)的前缀。 ML-KEM、ML-DSA 和 SLH-DSA(参见后量子密码学)内部均基于 SHAKE 构建。KMAC 是 SHA-3 原生的消息认证码,构建于 cSHAKE 之上。

 


function sgcSHA3_224(const aData: TBytes): TBytes;
function sgcSHA3_256(const aData: TBytes): TBytes;
function sgcSHA3_384(const aData: TBytes): TBytes;
function sgcSHA3_512(const aData: TBytes): TBytes;
function sgcSHAKE128(const aData: TBytes; aLen: Integer): TBytes;
function sgcSHAKE256(const aData: TBytes; aLen: Integer): TBytes;
function sgcKMAC128(const aKey, aData: TBytes; aLen: Integer; const aCustom: TBytes): TBytes;
function sgcKMAC256(const aKey, aData: TBytes; aLen: Integer; const aCustom: TBytes): TBytes;

 

BLAKE2b 和 BLAKE2s

 

BLAKE2 在软件实现中比 SHA-2 和 SHA-3 更快,同时提供相同的安全余量,并且可以直接接受一个可选密钥, 而无需借助 HMAC 构造。BLAKE2b 面向 64 位平台,摘要长度最高可达 512 位;BLAKE2s 面向 32 位及嵌入式平台, 摘要长度最高可达 256 位。较短的摘要并不是完整摘要的截断, 长度本身会被混入哈希计算之中,因此程序不能通过切换长度就期望得到匹配的输出。

 


function sgcBlake2b(const aData: TBytes; aDigestLen: Integer = 64): TBytes;
function sgcBlake2b_Keyed(const aKey, aData: TBytes; aDigestLen: Integer = 64): TBytes;
function sgcBlake2s(const aData: TBytes; aDigestLen: Integer = 32): TBytes;
function sgcBlake2s_Keyed(const aKey, aData: TBytes; aDigestLen: Integer = 32): TBytes;

 

SipHash

 

SipHash-2-4 是一种为哈希表查找而设计的快速带密钥哈希。它的作用是阻止攻击者向您的应用程序 输入精心构造的数据,使这些数据全部落入同一个桶中,从而把 O(1) 的查找变成 O(n), 这是一种称为哈希泛洪攻击的拒绝服务手段。它恰好使用 16 字节密钥并生成 8 字节标签; 它不是通用 MAC,不应用于消息认证。

 


function sgcSipHash24(const aKey, aData: TBytes): TBytes;

 

HMAC

 

HMAC 可以把上面任意一种哈希算法变成一个消息认证码。当底层哈希本身不直接接受密钥时 (如 SHA-1、SHA-2),HMAC 就是标准做法。

 


function sgcHMAC_SHA1(const aKey, aData: TBytes): TBytes;
function sgcHMAC_SHA256(const aKey, aData: TBytes): TBytes;
function sgcHMAC_SHA384(const aKey, aData: TBytes): TBytes;
function sgcHMAC_SHA512(const aKey, aData: TBytes): TBytes;

var
  oTag: TBytes;
begin
  oTag := sgcHMAC_SHA256(TBytes(oKey), TBytes(oMessage));
end;

 

AES-CMAC

 

CMAC 使用分组密码而不是哈希来构建 MAC。当某个协议按名称明确指定使用 AES-CMAC 时请使用它 (这在智能卡和物联网规范中很常见);其他情况下,更常见的默认选择是 HMAC-SHA256。

 


function sgcAES_CMAC(const aKey, aData: TBytes): TBytes;
function sgcAES_CMAC_Verify(const aKey, aData, aTag: TBytes): Boolean;

 

Poly1305

 

Poly1305 是一次性认证器:绝不能对同一个密钥使用两次。实际使用中它通常不会被直接调用, 而是作为对称加密页面中 ChaCha20-Poly1305 和 XChaCha20-Poly1305 AEAD 密码的认证部分,这些密码会为每条消息派生一个全新的一次性密钥。

 


function sgcPoly1305(const aKey, aData: TBytes): TBytes;
function sgcPoly1305_Verify(const aKey, aData, aTag: TBytes): Boolean;

 

比较 MAC 或密码哈希

 

当其中一个标签或摘要来自不受信任的来源时,切勿使用普通的 = 来比较两个标签或摘要。逐字节比较、一旦发现不匹配就立即返回的实现,其耗时会随着不匹配出现的位置越靠前而明显更短, 这足以成为攻击者的一个信号,使其能够逐字节地伪造出一个有效标签,需要多次尝试才能完成。请改用 sgcCrypto_Encoding 中的恒定时间比较函数, 该函数列在密码学总览页的“编码与实用工具”一节中。

 


function sgcConstantTimeEquals(const aLeft, aRight: TBytes): Boolean;

 

RIPEMD-160

 

来自欧洲 RIPE 项目的 160 位摘要,位于 sgcCrypto_Legacy 中,但并不属于下面那一节所指的“遗留”范畴。它没有像 MD5 和 SHA-1 那样被攻破,但速度比 SHA-256 慢, 安全余量也更小,因此只应在某种格式明确按名称指定它时使用,而不是作为默认选择。比特币及其所有衍生链 都用它来生成地址,叠加在 SHA-256 之上。HMAC-RIPEMD160 即 RFC 2286。

 


function sgcRIPEMD160(const aData: TBytes): TBytes;
function sgcHMAC_RIPEMD160(const aKey, aData: TBytes): TBytes;

var
  oHash160: TBytes;
begin
  { the Bitcoin address digest: RIPEMD-160 over SHA-256 }
  oHash160 := sgcRIPEMD160(sgcSHA256(oPublicKey));
end;

 

遗留哈希

 

sgcCrypto_Legacy 提供 MD4、MD5 和 HMAC-MD5。这三者作为安全原语均已被攻破:针对 MD4 和 MD5 都已发表过可实际利用的碰撞。 它们的存在仅是为了与那些按名称指定它们的旧协议和文件格式互操作(例如 NTLM 身份验证)。 请勿在任何新场景中选用它们;请改用 SHA-256 或 BLAKE2b。

 


function sgcMD4(const aData: TBytes): TBytes;
function sgcMD5(const aData: TBytes): TBytes;
function sgcHMAC_MD5(const aKey, aData: TBytes): TBytes;