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(); |
+ } |
+} |