我试图想出一种方法来验证我从服务器下载到嵌入式系统的文件的真实性。我正在考虑使用哈希(最好是SHA256)。我主要担心的是文件大小可能太大而无法将其加载到缓冲区中并将其传递给散列函数。
在线搜索时,我遇到了这个答案(https://crypto.stackexchange.com/a/19805),它讨论了计算哈希树而不是整个文件的哈希值。这是一种有效的方法吗?有没有人有任何建议以不同的方式做到这一点?
我试图想出一种方法来验证我从服务器下载到嵌入式系统的文件的真实性。我正在考虑使用哈希(最好是SHA256)。我主要担心的是文件大小可能太大而无法将其加载到缓冲区中并将其传递给散列函数。
在线搜索时,我遇到了这个答案(https://crypto.stackexchange.com/a/19805),它讨论了计算哈希树而不是整个文件的哈希值。这是一种有效的方法吗?有没有人有任何建议以不同的方式做到这一点?
只要按照从头到尾的顺序接收文件,就可以增量计算哈希。任何适用于嵌入式系统(以及大多数不适用的系统)的密码学 API 都允许您通过传递连续的输入块来计算哈希。每次收到一个块时,将输入提供给哈希计算并将其附加到文件中。传输结束后,完成哈希计算,如果哈希不正确,则删除文件。
请注意,哈希只能验证文件的完整性,而不是真实性。这意味着设备需要提前知道哈希值,因此需要通过另一个通道传输哈希。这通常通过使用缓慢但受信任的通道来传输散列(例如端到端 TLS 到受信任的服务器)和具有更多带宽但文件本身安全性较低的通道(例如广播机制)来完成。
仅当您需要能够独立验证文件的某些部分而不必检索整个文件时,哈希树才有用。如果您按顺序完整地获取文件,则它没有用。
SHA-2 的内存要求与消息的大小无关。
大多数加密哈希的工作方式并不完全直观。它只是一个以初始值开始并接受其块大小的单个输入的函数,该块大小对摘要进行置换。哈希没有内部状态,每个后续块只是对摘要进行更多排列。给定的摘要加上给定的输入块将始终产生相同的新摘要。这是MD5 和 SHA-2 使用的Merkle-Damgård 构造背后的想法之一。每个输入块上的散列操作是单独的和原子的,与整个消息无关(尽管最终填充块编码被散列的消息的总长度)。对文件进行散列处理只是重复执行此操作,从标准指定的初始值开始,到没有更多要散列的块时结束。你只需要存储三个东西来计算一个 SHA-2 摘要:
从FIPS-180-4 § 5.2 开始,通过将消息分解为单独的块来解析消息:
5.2 解析消息 消息及其填充必须被解析为N m位块。 5.2.1 SHA-1、SHA-224 和 SHA-256 对于 SHA-1、SHA-224 和 SHA-256,消息及其填充被解析为N 个 512 位块,M (1) , M (2) ,..., M ( N )。由于输入块的 512 位可能是 表示为 16 个 32 位字,消息块i的前 32 位是 表示为M 0 ( i ),接下来的 32 位是M 1 ( i ),依此类推,直到M 15 ( i )。 5.2.2 SHA-334、SHA-512、SHA-512/224 和 SHA-512/256 对于 SHA-384、SHA-512、SHA-512/224 和 SHA-512/256,消息及其填充是 解析为N个 1024 位块,M (1) , M (2) ,..., M ( N )。由于输入的 1024 位 块可以表示为十六个 64 位字,消息块的前 64 位 i表示为M 0 ( i ),接下来的 64 位是M 1 ( i ),依此类推,直到M 15 ( i )。
对于 SHA-1、SHA-256 和 SHA-512,内部状态大小与摘要一样大(SHA-224 和 SHA-512/224 等其他哈希只是较大版本的截断版本,具有不同的起始值)。这意味着,对于您使用 SHA-256 的用例,您只需要能够存储 256 位(32 字节)的数据来计算任何大小的数据摘要,从 0 位到 2 64 - 1 位(此限制是由于使用了填充,它将消息的大小编码为 64 位块)。各种优化可能会增加内存需求,但不足以成为嵌入式系统的关注点。
一些较新的散列函数具有大于其输出摘要大小的内部状态,以避免长度扩展攻击,其中,在不知道m并且只有其摘要的情况下,您可以计算 m' 的摘要,其中m'以米。例如,SHA-3(无论摘要大小)具有 1600 位的更大内部状态来防止这种情况。它确保任何单个散列操作都依赖于内部状态,最终在发布最终摘要时不会显示出来。
几乎每个散列函数,当然还有每个值得使用的散列函数(是的,包括 SHA-256),都可以以流方式使用。您无需将整个文件都提供给它以在单个块中进行散列;相反,您可以将文件分成小块提供给它,并在完成后获得最终的哈希值。