Add missing SHA224, SHA384, and SHA512 (#63)

* Add SHA384 and SHA512

Sha384 and Sha512 share the same implementation and differ only in the
initailization vectors and the digest concatentation. The algorithm from
from rfc6234 calls for 64bit words and operations and to remain dart2js
friendly, BigInt was used.

INPUT REQUESTED:
  I'm not sure where the test vectors came from; but a simple comparison
  to sha384 and sha512 command line methods shows it matching. If you
  can point me to them, then I can write a better test.

* Add Sha224 + Refactor

Do something less stupid; use _Sha32BitSink and _Sha64BitSink to have
class heirarchies make better sense.

* Support 32bit and 64bit operations for SHA384/512

Two modes of operation: if you're in a browser, you get the slower 32bit
algorithm because you only have 2^53 bits. If you are on the VM /
Flutter, you'll have 64bit operations wich is *much* faster:

32BIT NUMBERS: ~20MB/s hashing
Removing BigInt has some good results:
  Instance of 'Sha224' warmup: 0:00:00.015599
  Instance of 'Sha256' warmup: 0:00:00.002325
  Instance of 'Sha384' warmup: 0:00:00.019082
  Instance of 'Sha512' warmup: 0:00:00.010288
  Instance of 'Sha224' real: 0:00:00.092928
  Instance of 'Sha256' real: 0:00:00.093426
  Instance of 'Sha384' real: 0:00:00.823335
  Instance of 'Sha512' real: 0:00:00.807871

64BIT NUMBERS: ~236MB/s hashing
On the VM, this is much faster with 64bit operations.
  Instance of 'Sha224' warmup: 0:00:00.013285
  Instance of 'Sha256' warmup: 0:00:00.002443
  Instance of 'Sha384' warmup: 0:00:00.020954
  Instance of 'Sha512' warmup: 0:00:00.005616
  Instance of 'Sha224' real: 0:00:00.097196
  Instance of 'Sha256' real: 0:00:00.094167
  Instance of 'Sha384' real: 0:00:00.067605
  Instance of 'Sha512' real: 0:00:00.067564

NOTE:
Compiles with dart2js - cannot reference 64bit hex as the compiler just
vomits... but you can left shift by 32.

* Fix comment

* Add conditional imports

* De-listify 32bit allocations

Speed is still meh in dart2js:

384/512 numbers:
Instance of 'minified:a2' real: 0:00:02.203820
Instance of 'minified:aO' real: 0:00:02.192515

* Add sha monte tests for 224,256,384, and 512

RSP values came from http://csrc.nist.gov/groups/STM/cavp/documents/shs/shabytetestvectors.zip

* Fix formattting of lib/crypto.dart

* Bump version (adding new features) + min sdk

* Bump travis (hidden file)

* Rework monte test to function-only.
11 files changed
tree: edb2c5d0a105dfd72364c9abf31c5fe7b801176e
  1. example/
  2. lib/
  3. test/
  4. .gitignore
  5. .test_config
  6. .travis.yml
  7. analysis_options.yaml
  8. AUTHORS
  9. CHANGELOG.md
  10. CONTRIBUTING.md
  11. LICENSE
  12. pubspec.yaml
  13. README.md
README.md

Cryptographic hashing functions for Dart

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
  • MD5
  • HMAC (i.e. HMAC-MD5, HMAC-SHA1, HMAC-SHA256)

Usage

Digest on a single input

To hash a list of bytes, invoke the convert method on the sha1, sha256 or md5 objects.

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

void main() {
  var bytes = utf8.encode("foobar"); // data being hashed

  var digest = sha1.convert(bytes);

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

Digest on chunked input

If the input data is not available as a single list of bytes, use the chunked conversion approach.

Invoke the startChunkedConversion method to create a sink for the input data. On the sink, invoke the add method for each chunk of input data, and invoke the close method when all the chunks have been added. The digest can then be retrieved from the Sink<Digest> used to create the input data sink.

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 = new AccumulatorSink<Digest>();
  var input = sha1.startChunkedConversion(output);
  input.add(firstChunk);
  input.add(secondChunk); // call `add` for every chunk of input data
  input.close();
  var digest = output.events.single;

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

The above example uses the AccumulatorSink class that comes with the convert package. It is capable of accumulating multiple events, but in this usage only a single Digest is added to it when the data sink's close method is invoked.

HMAC

Create an instance of the Hmac class with the hash function and secret key being used. The object can then be used like the other hash calculating objects.

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

void main() {
  var key = utf8.encode('p@ssw0rd');
  var bytes = utf8.encode("foobar");

  var hmacSha256 = new Hmac(sha256, key); // HMAC-SHA256
  var digest = hmacSha256.convert(bytes);
  
  print("HMAC digest as bytes: ${digest.bytes}");
  print("HMAC digest as hex string: $digest");
}

Disclaimer

Support for this library is given as best effort.

This library has not been reviewed or vetted by security professionals.

Features and bugs

Please file feature requests and bugs at the issue tracker.