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.

SHA-1 SHA-256 SHA-512 on bytes

Sample code to calculate SHA-256 hash for a short string. The idea is first convert string to bytes, then invoke the convert() method on the sha1, sha256 or sha512 objects:

import 'package:crypto/crypto.dart';
import 'dart:convert'; // for the utf8.encode method

void main() {
  final bytes = utf8.encode("Hello, world."); // data being hashed

  final digest = sha256.convert(bytes);

  print("Digest as bytes: ${digest.bytes}");
  print("Digest as hex string: $digest");
}

If the input string is very long, may calculate hash incrementally through chunked input:

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

void main() {
  var firstChunk = utf8.encode("foo");
  var secondChunk = utf8.encode("bar");

  var output = AccumulatorSink<Digest>();
  var input = sha1.startChunkedConversion(output);
  input.add(firstChunk);
  input.add(secondChunk); // call `add` for every chunk of input data
  input.close();
  final digest = output.events.single;

  print("Digest as bytes: ${digest.bytes}");
  print("Digest as hex string: $digest");
}

HMAC

Flutter also support calculate HMAC (Hash-based Message Authentication Codes).

HMAC is a specific type of message authentication code (MAC) involving a cryptographic hash function and a secret cryptographic key. As with any MAC, it may be used to simultaneously verify both the data integrity and authenticity of a message.

There is an example calculate HMAC in Flutter:

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

void main() {
  final key = utf8.encode('hash password');
  final bytes = utf8.encode("some text here...");

  final hmacSha256 = Hmac(sha256, key); // HMAC-SHA256
  final digest = hmacSha256.convert(bytes);

  print("HMAC digest as bytes: ${digest.bytes}");
  print("HMAC digest as hex string: $digest");
}

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

OmniLock - Block / Hide App on iOS

Block distractive apps from appearing on the Home Screen and App Library, enhance your focus and reduce screen time.

DNS Firewall for iOS and Mac OS

Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps

Ad