| Index: lib/src/hmac.dart
|
| diff --git a/lib/src/hmac.dart b/lib/src/hmac.dart
|
| index d9eae8caedb623d5d3004209de64128bb973353e..a11c4b67af10847b3c1b10f2d3e9b83209b9e5ac 100644
|
| --- a/lib/src/hmac.dart
|
| +++ b/lib/src/hmac.dart
|
| @@ -4,10 +4,12 @@
|
|
|
| library crypto.hmac;
|
|
|
| +import 'dart:convert';
|
| import 'dart:typed_data';
|
|
|
| import 'package:typed_data/typed_data.dart';
|
|
|
| +import 'digest.dart';
|
| import 'digest_sink.dart';
|
| import 'hash.dart';
|
|
|
| @@ -17,26 +19,22 @@ import 'hash.dart';
|
| ///
|
| /// HMAC allows messages to be cryptographically authenticated using any
|
| /// iterated cryptographic hash function.
|
| -///
|
| -/// The message's data is added using [add]. Once it's been fully added, the
|
| -/// [digest] and [close] methods can be used to extract the message
|
| -/// authentication digest.
|
| -///
|
| -/// If an expected authentication digest is available, the [verify] method may
|
| -/// also be used to ensure that the message actually corresponds to that digest.
|
| -// TODO(floitsch): make HMAC implement Sink, EventSink or similar.
|
| -class HMAC {
|
| - /// The bytes from the message so far.
|
| - final _message = new Uint8Buffer();
|
| -
|
| +class HMAC extends Converter<List<int>, Digest> {
|
| /// The hash function used to compute the authentication digest.
|
| final Hash _hash;
|
|
|
| /// The secret key shared by the sender and the receiver.
|
| final Uint8List _key;
|
|
|
| - /// Whether [close] has been called.
|
| - bool _isClosed = false;
|
| + /// The bytes from the message so far.
|
| + final _message = new Uint8Buffer();
|
| +
|
| + /// The sink for implementing the deprecated APIs that involved adding data
|
| + /// directly to the [HMAC] instance.
|
| + _HmacSink _sink;
|
| +
|
| + /// The sink that [_sink] sends the [Digest] to once it finishes hashing.
|
| + DigestSink _innerSink;
|
|
|
| /// Create an [HMAC] object from a [Hash] and a binary key.
|
| ///
|
| @@ -51,52 +49,60 @@ class HMAC {
|
| // If [key] is shorter than the block size, the rest of [_key] will be
|
| // 0-padded.
|
| _key.setRange(0, key.length, key);
|
| +
|
| + _innerSink = new DigestSink();
|
| + _sink = startChunkedConversion(_innerSink);
|
| + }
|
| +
|
| + Digest convert(List<int> data) {
|
| + var innerSink = new DigestSink();
|
| + var outerSink = startChunkedConversion(innerSink);
|
| + outerSink.add(data);
|
| + outerSink.close();
|
| + return innerSink.value;
|
| }
|
|
|
| + ByteConversionSink startChunkedConversion(Sink<Digest> sink) =>
|
| + new _HmacSink(sink, _hash, _key);
|
| +
|
| /// Adds a list of bytes to the message.
|
| ///
|
| /// If [this] has already been closed, throws a [StateError].
|
| + @Deprecated("Expires in 1.0.0. Use HMAC.convert() or "
|
| + "HMAC.startChunkedConversion() instead.")
|
| void add(List<int> data) {
|
| - if (_isClosed) throw new StateError("HMAC is closed");
|
| _message.addAll(data);
|
| + _sink.add(data);
|
| + }
|
| +
|
| + /// Closes [this] and returns the digest of the message as a list of bytes.
|
| + ///
|
| + /// Once closed, [add] may no longer be called.
|
| + @Deprecated("Expires in 1.0.0. Use HMAC.convert() or "
|
| + "HMAC.startChunkedConversion() instead.")
|
| + List<int> close() {
|
| + _sink.close();
|
| + return _innerSink.value.bytes;
|
| }
|
|
|
| /// Returns the digest of the message so far, as a list of bytes.
|
| + @Deprecated("Expires in 1.0.0. Use HMAC.convert() or "
|
| + "HMAC.startChunkedConversion() instead.")
|
| List<int> get digest {
|
| - // Compute inner padding.
|
| - var padding = new Uint8List(_key.length);
|
| - for (var i = 0; i < padding.length; i++) {
|
| - padding[i] = 0x36 ^ _key[i];
|
| - }
|
| + if (_sink._isClosed) return _innerSink.value.bytes;
|
|
|
| - // Inner hash computation.
|
| - var innerDigest = _hashWithPadding(padding, _message);
|
| + // This may be called at any point while the message is being hashed, but
|
| + // the [_HmacSink] only supports getting the value once. To make this work,
|
| + // we just re-hash everything after we get the digest. It's redundant, but
|
| + // this API is deprecated anyway.
|
| + _sink.close();
|
| + var bytes = _innerSink.value.bytes;
|
|
|
| - // Compute outer padding.
|
| - for (var i = 0; i < padding.length; i++) {
|
| - padding[i] = 0x5c ^ _key[i];
|
| - }
|
| -
|
| - // Outer hash computation which is the result.
|
| - return _hashWithPadding(padding, innerDigest);
|
| - }
|
| + _innerSink = new DigestSink();
|
| + _sink = _hash.startChunkedConversion(_innerSink);
|
| + _sink.add(_message);
|
|
|
| - /// Returns the digest of [padding] followed by [data].
|
| - List<int> _hashWithPadding(List<int> padding, List<int> data) {
|
| - var innerSink = new DigestSink();
|
| - _hash.startChunkedConversion(innerSink)
|
| - ..add(padding)
|
| - ..add(data)
|
| - ..close();
|
| - return innerSink.value.bytes;
|
| - }
|
| -
|
| - /// Closes [this] and returns the digest of the message as a list of bytes.
|
| - ///
|
| - /// Once closed, [add] may no longer be called.
|
| - List<int> close() {
|
| - _isClosed = true;
|
| - return digest;
|
| + return bytes;
|
| }
|
|
|
| /// Returns whether the digest computed for the data so far matches the given
|
| @@ -107,6 +113,7 @@ class HMAC {
|
| ///
|
| /// Throws an [ArgumentError] if the given digest does not have the same size
|
| /// as the digest computed by [this].
|
| + @Deprecated("Expires in 1.0.0. Use Digest.==() instead.")
|
| bool verify(List<int> digest) {
|
| var computedDigest = this.digest;
|
| if (digest.length != computedDigest.length) {
|
| @@ -122,3 +129,55 @@ class HMAC {
|
| return result == 0;
|
| }
|
| }
|
| +
|
| +/// The concrete implementation of the HMAC algorithm.
|
| +class _HmacSink extends ByteConversionSink {
|
| + /// The sink for the outer hash computation.
|
| + final ByteConversionSink _outerSink;
|
| +
|
| + /// The sink that [_innerSink]'s result will be added to when it's available.
|
| + final _innerResultSink = new DigestSink();
|
| +
|
| + /// The sink for the inner hash computation.
|
| + ByteConversionSink _innerSink;
|
| +
|
| + /// Whether [close] has been called.
|
| + bool _isClosed = false;
|
| +
|
| + _HmacSink(Sink<Digest> sink, Hash hash, List<int> key)
|
| + : _outerSink = hash.startChunkedConversion(sink) {
|
| + _innerSink = hash.startChunkedConversion(_innerResultSink);
|
| +
|
| + // Compute outer padding.
|
| + var padding = new Uint8List(key.length);
|
| + for (var i = 0; i < padding.length; i++) {
|
| + padding[i] = 0x5c ^ key[i];
|
| + }
|
| + _outerSink.add(padding);
|
| +
|
| + // Compute inner padding.
|
| + for (var i = 0; i < padding.length; i++) {
|
| + padding[i] = 0x36 ^ key[i];
|
| + }
|
| + _innerSink.add(padding);
|
| + }
|
| +
|
| + void add(List<int> data) {
|
| + if (_isClosed) throw new StateError("HMAC is closed");
|
| + _innerSink.add(data);
|
| + }
|
| +
|
| + void addSlice(List<int> data, int start, int end, bool isLast) {
|
| + if (_isClosed) throw new StateError("HMAC is closed");
|
| + _innerSink.addSlice(data, start, end, isLast);
|
| + }
|
| +
|
| + void close() {
|
| + if (_isClosed) return;
|
| + _isClosed = true;
|
| +
|
| + _innerSink.close();
|
| + _outerSink.add(_innerResultSink.value.bytes);
|
| + _outerSink.close();
|
| + }
|
| +}
|
|
|