简体   繁体   English

在 Dart/Flutter 本地计算 Dropbox 的内容 Hash

[英]Calculate Dropbox's Content Hash locally in Dart/Flutter

I'm trying to calculate the content_hash of a Dropbox file, following their algorithm, Dropbox docs here , however, I cannot get the overall hash (result of Step 4) correct.我正在尝试计算 Dropbox 文件的content_hash ,按照他们的算法, Dropbox 文档在这里,但是,我无法得到整体 hash(步骤 4 的结果)正确。

I can produce the correct hashes on each block, so I suspect the issue starts with the conversion to the concantenated binary string (Step 3).我可以在每个块上生成正确的哈希值,所以我怀疑问题始于转换为连接的二进制字符串(第 3 步)。

Per Dropbox's docs, there are 4 steps involved:根据 Dropbox 的文档,涉及 4 个步骤:

  1. Split the file into blocks of 4 MB (4,194,304 or 4 * 1024 * 1024 bytes).将文件拆分为 4 MB(4,194,304 或 4 * 1024 * 1024 字节)的块。 The last block (if any) may be smaller than 4 MB.最后一个块(如果有)可能小于 4 MB。
  2. Compute the hash of each block using SHA-256.使用 SHA-256 计算每个块的 hash。
  3. Concatenate the hash of all blocks in the binary format to form a single binary string.将二进制格式的所有块的 hash 连接起来,形成一个二进制字符串。
  4. Compute the hash of the concatenated string using SHA-256.使用 SHA-256 计算串联字符串的 hash。 Output the resulting hash in hexadecimal format. Output 十六进制格式的结果 hash。

They provide an example, with the resulting hash values, which I can only replicate up to step 2 (unfortunately, they do not provide an example of the single binary string for Step 3)他们提供了一个示例,结果为 hash 个值,我只能将其复制到第 2 步(不幸的是,他们没有为第 3 步提供单个二进制字符串的示例)

My code is as follows:我的代码如下:

Future<void> getDropContentHash() async {

  String getHash(List<int> bytes) => sha256.convert(bytes).toString();

  String toBinary(String string) {
    /// converting string to binary string, following this SO Answer
    /// https://stackoverflow.com/a/25906926/12132021
    return string.codeUnits.map((x) => x.toRadixString(2).padLeft(8, '0')).join();
  }

  /// Nasa Milky Way Example:
  final response = await http.get(Uri.parse('https://www.dropbox.com/static/images/developers/milky-way-nasa.jpg'));
  final bytes = response.bodyBytes;

  /// Step 1: 
  /// Split the file into blocks of 4 MB (4,194,304 or 4 * 1024 * 1024 bytes). The last block (if any) may be smaller than 4 MB.

  final chunks = bytes.slices(4 * 1024 * 1024);
  print(chunks.length); // 3, as expected in the Nasa example

  /// Step 2: 
  /// Compute the hash of each block using SHA-256.
  final chunkHashes = <String>[];
  for(final chunk in chunks) {
    final blockHash = getHash(chunk);
    chunkHashes.add(blockHash);
  }
  print(chunkHashes);

  /*
  Per the docs, the chunkHashes are correct:
  [
    2a846fa617c3361fc117e1c5c1e1838c336b6a5cef982c1a2d9bdf68f2f1992a,
    c68469027410ea393eba6551b9fa1e26db775f00eae70a0c3c129a0011a39cf9,
    7376192de020925ce6c5ef5a8a0405e931b0a9a8c75517aacd9ca24a8a56818b
  ]
  */

  /// Step 3: 
  /// Concatenate the hash of all blocks in the binary format to form a single binary string.
  /// I suspect the issue with the `toBinary` method, converting to a single binary string:
  final concatenatedString = chunkHashes.map((chunkHash) => toBinary(chunkHash)).join();
  print(concatenatedString);

  /// Step 4: 
  /// Compute the hash of the concatenated string using SHA-256. Output the resulting hash in hexadecimal format.
  final contentHash = getHash(concatenatedString.codeUnits);
  print(contentHash);

  /*
  Does NOT yield the correct value of:
  485291fa0ee50c016982abbfa943957bcd231aae0492ccbaa22c58e3997b35e0

  Instead, it yields:
  0f63574f6c7cf29d1735e8f5ec4ef63abb3bc5c1b8618c6455a2c554bc844396
  */

}

Can anyone help me get the correct overall hash of 485291fa0ee50c016982abbfa943957bcd231aae0492ccbaa22c58e3997b35e0 ?任何人都可以帮我得到 485291fa0ee50c016982abbfa943957bcd231aae0492ccbaa22c58e3997b35e0 的正确整体485291fa0ee50c016982abbfa943957bcd231aae0492ccbaa22c58e3997b35e0吗?

I figured it out.我想到了。 I did not need to convert to binary string.我不需要转换为二进制字符串。 I just needed to continue working with bytes, and only convert to a string after the overall hash (step 4)我只需要继续使用字节,并且只在整体 hash 之后转换为字符串(第 4 步)

The resulting code (simplified & refactored) to calculate the Dropbox content hash locally:用于在本地计算 Dropbox 内容 hash 的结果代码(经过简化和重构):

  Future<void> hashNasaImage() async {
    final response = await http.get(Uri.parse('https://www.dropbox.com/static/images/developers/milky-way-nasa.jpg'));
    final bytes = response.bodyBytes;
    final contentHash = getDropContentHash(bytes);
    print(contentHash); 
    // 485291fa0ee50c016982abbfa943957bcd231aae0492ccbaa22c58e3997b35e0
  }

  String getDropContentHash(List<int> bytes) {
    List<int> getHash(List<int> bytes) => sha256.convert(bytes).bytes;    
    final chunks = bytes.slices(4 * 1024 * 1024);
    final chunkHashes = chunks.fold(<int>[], (buffer, chunk) => buffer..addAll(getHash(chunk)));
    return Digest(getHash(chunkHashes)).toString();
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM