Skip to content
This repository has been archived by the owner on Dec 23, 2020. It is now read-only.

Latest commit

 

History

History
177 lines (125 loc) · 5.15 KB

digest.md

File metadata and controls

177 lines (125 loc) · 5.15 KB

Digests with Pointy Castle

This article describes how to calculate digests using the Pointy Castle package, which is an implementation of cryptographic algorithms for use with the Dart programming language.

Overview

A digest is a value derived from a message. In all/most cases, the digest is a small fixed size, regardless of the size of the message. The digest value is produced by running an algorithm over the message. Pointy Castle has implementations of a number of different cryptographic digest algorithms (cryptographic hash algorithms or cryptographic hash functions). The term "hash" and "digest" are often used interchangably. And in Pointy Castle, the Digest class represents the algorithm rather than the value that is produced.

To calculate a digest value:

  1. Instantiate a class that implements the Digest abstract class.

  2. Provide it the bytes to calculate the digest over. Either as a single Uint8List, or as fragments of Uint8List and bytes.

This program calculates the SHA-265 digest of text strings:

import 'dart:convert';
import 'dart:typed_data';

import "package:pointycastle/export.dart";

Uint8List sha256Digest(Uint8List dataToDigest) {

  final d = new SHA256Digest();

  return d.process(dataToDigest);
}

void main(List<String> args) {
  for (final data in args) {
    print('Data: "$data"');
    final hashValue = sha256Digest(utf8.encode(data));
	print('SHA-256: $hashValue');
  }
}

Note: these overview examples do not use the registry. For information on how to use the registry, see the following details.

Details

Implementation

Using the registry

If using the registry, invoke the Digest factory with the name of the digest algorithm.

final d = new Digest("SHA-256");

Possible names include: "MD2", "MD4", "MD5", "RIPEMD-128", "RIPEMD-160", "RIPEMD-256", "RIPEMD-320", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "Tiger" and "Whirlpool".

Note: these examples store the digest object in "d", since they could be for any digest algorithm. But it is better to give the variable a more meaningful name, such as "sha256".

Some digest implementations should not be instantiated using the registry, because additional parameters need to be passed to their constructors. These include: Blake2bDigest, SHA3Digest and SHA512tDigest.

Without the registry

If the registery is not used, invoke the digest implementation's constructor.

final d = new SHA256Digest(); // SHA-256

All of the available digest classes of are listed as the implementers of the Digest abstract class.

Providing the data to digest

Complete data

If all the data is available as a single sequence of bytes, pass it to the process method to obtain the digest. The input data must be a single Uint8List, and the calculated digest is returned in a new Uint8List.

final Uint8List dataToDigest = ...

final hash = d.process(dataToDigest);

Progressive data

The data can also be provided as a sequence of individual bytes or fragments of bytes.

To provide a single byte, use the updateByte method. It takes a single int.

To provide a fragment of bytes, use the update method. This method takes a Uint8List, an offset to where the bytes start and the length. Therefore, a sublist of the Uint8List can be provided, instead of the entire Uint8List.

After all the data has been provided, use the doFinal method to obtain the digest. The doFinal method takes two arguments: a Uint8List where it will store the digest and an offset to where it will start.

The destination, after the offset position, must be large enough to hold the digest. The number of bytes required depends on the digest algorithm being used, and can be found using the digestSize getter.

final chunk1 = utf8.encode('cellophane');
final chunk2 = utf8.encode('world');

d.updateByte(0x48); // 'H'
d.updateByte(0x65); // 'e'
d.update(chunk1, 1, 4);
d.updateByte(0x20); // ' '
d.update(chunk2, 0, chunk2.length);
d.updateByte(0x21); // '!'

final hash = Uint8List(d.digestSize); // create a destination for storing the hash

d.doFinal(hash, 0); // hash of "Hello world!"

Discarding provided data

When providing the data progressively, previously provided data can be discarded by invoking the reset method.

Normally, reset does not need to be explicitly done because it is done automatically by the process and doFinal methods. This is only required if previously provided data is abandoned.

final part1 = utf8.encode('Hello ');
final part2 = utf8.encode('world!');

final d = new SHA256Digest();
final hash = Uint8List(d.digestSize);

// Without rest

d.update(part1, 0, part1.length);
d.update(part2, 0, part2.length);
d.doFinal(hash, 0); // hash of "Hello world!"

// With reset

d.update(part1, 0, part1.length);
d.reset();
d.update(part2, 0, part2.length);
d.doFinal(hash, 0); // hash of "world!"

Note: do not use the resetState method that is available in the SHA-family of implementations. The reset method will internally invoke the resetState method, as well as perform other operations to reset the digester.