blob: ef5b55e822e6ab84e666dcfcfd3d6b3783bd6bdb [file] [log] [blame] [view]
# 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:
```dart
import 'dart:convert';
import 'dart:typed_data';
import "package:pointycastle/export.dart";
Uint8List sha256Digest(Uint8List dataToDigest) {
final d = SHA256Digest();
return d.process(dataToDigest);
}
void main(List<String> args) {
final valuesToDigest = (args.isNotEmpty) ? args : ['Hello world!'];
for (final data in valuesToDigest) {
print('Data: "$data"');
final hash = sha256Digest(utf8.encode(data) as Uint8List);
print('SHA-256: $hash');
print('SHA-256: ${bin2hex(hash)}'); // output in hexadecimal
}
}
```
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.
```dart
final d = 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", "Whirlpool" and "SM3".
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.
```dart
final d = SHA256Digest(); // SHA-256
```
All of the available digest classes of are listed as the implementers
of the
[Digest](https://pub.dev/documentation/pointycastle/latest/pointycastle.api/Digest-class.html)
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`.
```dart
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.
```dart
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.
```dart
final part1 = utf8.encode('Hello ');
final part2 = utf8.encode('world!');
final d = 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.