c - 使用 mbedlts 在内存受限的系统上使用 SHA 散列文件

标签 c sha mbedtls

我想计算一个大小超过1M的文件的SHA256值。为了使用 mbedtls 库获取此哈希值,我需要将整个文件复制到内存中。但是我的内存大小只有100K。所以我想知道有没有什么方法可以分段计算文件哈希值。

最佳答案

In order to get this hash value with mbedtls library, I need to copy the whole file to the memory.

这是不准确的。 mbedtls 库支持哈希值的增量计算。

要使用 mbedtls 计算 SHA-256 哈希值,您必须执行以下步骤 (reference):

  • 创建 mbedtls_sha256_context 结构的实例。
  • 使用 mbedtls_sha256_init 初始化上下文,然后使用 mbedtls_sha256_starts_ret
  • 使用 mbedtls_sha256_update_ret 将数据输入哈希函数。
  • 使用 mbedtls_sha256_finish_ret 计算最终的哈希和。
  • 使用 mbedtls_sha256_free 释放上下文

请注意,这并不意味着 mbedtls_sha256_context 结构会保留整个数据,直到 mbedtls_sha256_finish_ret 被调用。相反,mbedtls_sha256_context 只保存散列计算的中间结果。当使用 mbedtls_sha256_update_ret 将额外数据输入哈希函数时,计算状态会更新,新的中间结果将存储在 mbedtls_sha256_context 中。

mbedtls_sha256_context 的总大小由 sizeof(mbedtls_sha256_context) 确定,在我的系统上为 108 字节。我们还可以从 mbedtls 源代码 ( reference ) 中看到这一点:

typedef struct mbedtls_sha256_context
{
    uint32_t total[2];          /*!< The number of Bytes processed.  */
    uint32_t state[8];          /*!< The intermediate digest state.  */
    unsigned char buffer[64];   /*!< The data block being processed. */
    int is224;                  /*!< Determines which function to use:
                                     0: Use SHA-256, or 1: Use SHA-224. */
}
mbedtls_sha256_context;

我们可以看到该结构包含一个大小为 2*32 位 = 8 字节 的计数器,用于跟踪到目前为止已处理的字节总数。 8*32 bit = 32 byte用于跟踪散列计算的中间结果。 64 byte用于跟踪当前正在处理的数据 block 。如您所见,这是一个固定大小的缓冲区,不会随着散列数据量的增加而增长。最后使用一个 int 来区分 SHA-224 和 SHA-256。在我的系统上 sizeof(int) == 4。所以总的来说,我们得到了 8+32+64+4 = 108 字节

考虑以下示例程序,它逐步将文件读取到大小为 4096 的缓冲区中,并在每一步中将缓冲区提供给哈希函数:

#include <mbedtls/sha256.h>

#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 4096
#define HASH_SIZE 32

int main(void) {
  int ret;

  // Initialize hash
  mbedtls_sha256_context ctx;
  mbedtls_sha256_init(&ctx);
  mbedtls_sha256_starts_ret(&ctx, /*is224=*/0);

  // Open file
  FILE *fp = fopen("large_file", "r");
  if (fp == NULL) {
    ret = EXIT_FAILURE;
    goto exit;
  }

  // Read file in chunks of size BUFFER_SIZE
  uint8_t buffer[BUFFER_SIZE];
  size_t read;
  while ((read = fread(buffer, 1, BUFFER_SIZE, fp)) > 0) {
    mbedtls_sha256_update_ret(&ctx, buffer, read);
  }

  // Calculate final hash sum
  uint8_t hash[HASH_SIZE];
  mbedtls_sha256_finish_ret(&ctx, hash);

  // Simple debug printing. Use MBEDTLS_SSL_DEBUG_BUF in a real program.
  for (size_t i = 0; i < HASH_SIZE; i++) {
    printf("%02x", hash[i]);
  }
  printf("\n");

  // Cleanup
  fclose(fp);
  ret = EXIT_SUCCESS;

exit:
  mbedtls_sha256_free(&ctx);
  return ret;
}

在大型示例文件上运行程序时,可以观察到以下行为:

$ dd if=/dev/random of=large_file bs=1024 count=1000000
1000000+0 records in
1000000+0 records out
1024000000 bytes (1.0 GB, 977 MiB) copied, 5.78353 s, 177 MB/s
$ sha256sum large_file 
ae2d3b46eec018e006533da47a80e933a741a8b1320cfce7392a5472faae0216  large_file
$ gcc -O3 -static test.c /usr/lib/libmbedcrypto.a
$ ./a.out 
ae2d3b46eec018e006533da47a80e933a741a8b1320cfce7392a5472faae0216

我们可以看到程序计算出了正确的 SHA-256 哈希值。我们还可以检查程序使用的内存:

$ command time -v ./a.out
...
Maximum resident set size (kbytes): 824
...

我们可以看到该程序最多消耗了 824 KB 的内存。因此,我们计算了一个 1 GB 文件的哈希值,内存小于 1 MB。这表明我们不必立即将整个文件加载到内存中以使用 mbedtls 计算其哈希值。

请记住,此测量是在 64 位台式计算机而非嵌入式平台上完成的。此外,除了 -O3 和静态链接(后者大约将程序的内存使用量减半)之外,没有执行进一步的优化。我希望在具有更小地址大小和执行进一步优化的工具链的嵌入式设备上,内存占用空间会更小。

关于c - 使用 mbedlts 在内存受限的系统上使用 SHA 散列文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63051416/

相关文章:

arm - 如何为arm gcc构建mbedtls

c - libtomcrypt 的 RSA 签名验证失败

关于本地主机 repo 中私钥的安全相关问题

c - 我们什么时候需要使用 posix_memalign 而不是 malloc?

c++ - 从 haskell 调用 C opencv 函数

c - 如何从 unix 中的当前时间获取小时值(使用 C)

c++ - 如何在 C/C++ 中使用 SSHA1?

java - SecretKeyFactory.getInstance ("PBKDF2WithHmacSHA512") 抛出 NoSuchAlgorithmException

Java 的 SHA-256 有时会返回 255 位

c - c 中的猜数字游戏,相同的猜测不会计算两次