RSS

Calculate file crypto hash (sha1, sha256, sha384 and sha512) in Flutter

How to calculate to generate file sha1, sha256, sha384 and sha512 crypto hash in Flutter.

Introduce crypto and ChunkedStreamReader

crypto

Flutter crypto package provides a set of cryptographic hashing functions implemented in pure Dart.

The following hashing algorithms are supported:

  • SHA-1
  • SHA-224
  • SHA-256
  • SHA-384
  • SHA-512
  • SHA-512/224
  • SHA-512/256
  • MD5
  • HMAC (i.e. HMAC-MD5, HMAC-SHA1, HMAC-SHA256)

crypto package already provide sample code to calculate hash through from bytes.

Flutter ChunkedStreamReader

To calculate file hash, we need read file to bytes. To support large file, we should read file by chunk and calculate its hash incrementally.

Flutter async package contains utility classes in the style of dart:async to work with asynchronous computations. Flutter async package provides ChunkedStreamReader utility class for reading chunks from a file stream. A chunked stream is a stream where each event is a chunk of elements. This utility class makes it easy to read a chunked stream using custom chunk sizes and sub-stream sizes, without managing partially read chunks.

Sample code to calculate file sha256 hash

Put crypto and async together, an example of calculate file sha256 hash as below. This code can support large file because it calculate hash chunk by chunk, each chunk is 4KB.

sha1, sha384 and sha512 is similar, just replace sha256 with sha1, sha384 or sha512.

import 'package:async/async.dart';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:convert/convert.dart';

Future<Digest> getFileSha256(String path) async {
  final reader = ChunkedStreamReader(File(path).openRead());
  const chunkSize = 4096;
  var output = AccumulatorSink<Digest>();
  var input = sha256.startChunkedConversion(output);

  try {
    while (true) {
      final chunk = await reader.readChunk(chunkSize);
      if (chunk.isEmpty) {
        // indicate end of file
        break;
      }
      input.add(chunk);
    }
  } finally {
    // We always cancel the ChunkedStreamReader,
    // this ensures the underlying stream is cancelled.
    reader.cancel();
  }

  input.close();

  return output.events.single;
}

Notes:

  • The read-operations readChunk must not be invoked until the future from a previous call has completed.
  • Return Digest so we can easily convert to bytes, hex or base64 encoded string.

Example of getFileSha256

Get file hash in hex string

Just use result of Digest.toString() to get hexadecimal digits.

final hash = await getFileSha256(path);
print("sha256 hash hex in lowercase: ${hash.toString()}");

Get file hash in base64 url safe encode

Hash result Digest.bytes can be encode with base64Url to get base64 url safe encoding:

final hash = await getFileSha256(path);
final hashBase64UrlSafe = base64Url.encode(hash.bytes);
print("sha256 hash base64 url safe: $hashBase64UrlSafe");

References