SHA256 (RFC 4634) 中 "Length_Low" 和 "Length_High" 的含义?

Meaning of "Length_Low" and "Length_High" in SHA256 (RFC 4634)?

今天在学习SHA2,到了代码中看不懂的地方

RFC 4634(定义SHA2的地方)在sha.h文件中定义了两个变量,Length_LowLength_High

uint32_t Length_Low;                /* Message length in bits */
uint32_t Length_High;               /* Message length in bits */
例如,

sha224-256.c 文件中,变量 Length_Low 在两个地方被主动更改。这里:

/*
 * add "length" to the length
 */
static uint32_t addTemp;
#define SHA1AddLength(context, length)                     \
    (addTemp = (context)->Length_Low,                      \
     (context)->Corrupted =                                \
        (((context)->Length_Low += (length)) < addTemp) && \
        (++(context)->Length_High == 0) ? 1 : 0)

这里:

/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (uint8_t) (context->Length_High >> 24);
context->Message_Block[57] = (uint8_t) (context->Length_High >> 16);
context->Message_Block[58] = (uint8_t) (context->Length_High >> 8);
context->Message_Block[59] = (uint8_t) (context->Length_High);
context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24);
context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16);
context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8);
context->Message_Block[63] = (uint8_t) (context->Length_Low);

我了解 RFC 4634 和 SHA2 的原理。然而,作为 C 语言的初学者,我只能理解论文中 99% 的代码。我附上的代码片段属于这剩下的1%。

能否请您解释一下,Length_LowLength_High 变量在 SHA2 的实现中起什么作用?它们在代码层面的含义是什么?

其次,代码片段中发生了什么?我可以识别复合运算符和移位,但代码的难度让我不知所措,尤其是在第二个片段中,取消引用、递增、定义等发生在同一行代码中。

Meta:stackexchange,尤其是 Whosebug,政策不是 post 'code' 的图像,被广泛定义为包括配置文件和日志或错误消息之类的东西,因为它们很难在移动设备上阅读,视障人士无法阅读,不可剪切和粘贴,不可搜索。再加上你的,根据你的 IDE 重新格式化和着色,在我看来非常丑陋。幸运的是,所有 RFC 都是以文本形式发布的,我可以很容易地替换它。

此外,SHA-256 和 SHA-224(以及之前的 SHA-1 和 MD5)的输入块大小为 64 octets 或 512 位,而不是64位,反正和长度字段无关。长度字段确实是 64 位,在代码中实现为两个 32 位变量,用于高半部分和低半部分。

static uint32_t addTemp;
#define SHA224_256AddLength(context, length)               \
  (addTemp = (context)->Length_Low, (context)->Corrupted = \
    (((context)->Length_Low += (length)) < addTemp) &&     \
    (++(context)->Length_High == 0) ? 1 : 0)

是相当棘手的代码,用于将一段输入数据的长度(实际上始终使用 8 表示完整的八位字节或 1-7 表示剩余位)到 2x32 位长度字段。首先它把传入的Length_Low存入addTemp,然后,从里到外:

(context)->Length_Low += (length) // call this CODE1

length的值添加到结构中的Length_Low字段;因为这在 C 中使用无符号算术,如果结果(数学上)溢出,它会被环绕(取模 232)。因此,当且仅当 overflow/wraparound 发生时,总和(Length_low 中的新值)小于 addTemp 中的原始值。这是经过测试的,在这种情况下 Length_High 字段会增加:

( CODE1 < addTemp) && (++(context)->Length_High == 0) // call this CODE2

如果增加高半部分后为零,这意味着也overflowed/wrapped-around,这意味着真正的消息长度由于规范要求,太大而无法放入 64 位字段,因此这被认为是错误并存储在 Corrupted 字段中,稍后将对其进行测试以报告哈希操作失败:

(addTemp=..., (context)->Corrupted = CODE2 ? 1 : 0)

应该注意 ? 1 : 0 在技术上是不必要的; C 中的 &&|| 运算符(以及像 <== 这样的 comparison/equality 运算符)被定义为 return 一个表示真和已经为 false 为零(尽管 testsif(x)while(x) accept 任何非零值作为 true)。然而,有些人觉得把它写出来更清晰,并且在 RFC 中这种动机特别强烈,它发布给包括那些(比如你!)对 C 知之甚少的受众。

事实上,将它写成一个(非常小的)函数而不是一个宏可能会更好,这将允许使用更明显的语句而不是复杂的嵌套表达式,并且 2006 年任何像样的编译器(现在少得多)会内联并折叠以生成与宏相同的代码。但世界并不完美。

相比之下,您的第三个块非常简单。它只是取 2x32 位长度字段并将其存储为当前 Message_Block.

的最后 8 个元素中的 8 个 8 位单元的大端序列