让我回答关于 sha 256 和 sha 512 的问题。简而言之:算法本身与字节序无关。字节序敏感部分是数据从字节缓冲区导入算法工作变量以及将其导出回摘要结果时 - 也是字节缓冲区。如果导入/导出包括强制转换,那么字节序很重要。
可能在哪里进行转换:在 sha 512 中有一个 128 字节的工作缓冲区。在我的代码中,它的定义如下:
union
{
U64 w [80]; (see U64 example below)
byte buffer [128];
};
输入数据被复制到这个字节缓冲区,然后在 W 上完成工作。这意味着数据被转换为某种 64 位类型。必须交换此数据。在我的情况下,它换成了小端机器。
更好的方法是准备一个获取每个字节并将其放置在 u64 类型中正确位置的宏。
当算法完成时,摘要结果从工作变量输出到某个字节缓冲区,如果这是由 memcpy 完成的,它也必须被交换。
在 32 位机器上实现 sha 512(专为 64 位机器设计)时,可能会发生另一种转换。就我而言,我定义了一个 64 位类型:
typedef struct {
uint high;
uint low;
} U64;
假设我也为 little endian 定义它,如下所示:
typedef struct {
uint low;
uint high;
} U64;
然后 k 算法 init 是这样完成的:
static const SHA_U64 k[80] =
{
{0xD728AE22, 0x428A2F98}, {0x23EF65CD, 0x71374491}, ...
...
...
}
但我需要 k[0].high 的逻辑值在任何机器上都相同。所以在这个例子中,我将需要另一个交换高低值的 k 数组。
在数据存储在工作参数中之后,任何按位操作在两个大/小端机器上都会产生相同的结果。
好的方法是避免任何强制转换:使用宏将字节从输入缓冲区导入您的工作参数。使用逻辑值而不考虑内存映射。使用宏将输出导出到摘要结果。
用于从字节缓冲区获取 32 位到 int32 的宏(BE = 大端):
#define GET_BE_BYTES_FROM32(a)
((((NQ_UINT32) (a)[0]) << 24) |
(((NQ_UINT32) (a)[1]) << 16) |
(((NQ_UINT32) (a)[2]) << 8) |
((NQ_UINT32) (a)[3]))
#define GET_LE_BYTES_FROM32(a)
((((NQ_UINT32) (a)[3]) << 24) |
(((NQ_UINT32) (a)[2]) << 16) |
(((NQ_UINT32) (a)[1]) << 8) |
((NQ_UINT32) (a)[0]))