| Index: packages/crypto/lib/src/hmac.dart
|
| diff --git a/packages/crypto/lib/src/hmac.dart b/packages/crypto/lib/src/hmac.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5caed7521dbe6f58979956c4bdca3bcc60a1a20f
|
| --- /dev/null
|
| +++ b/packages/crypto/lib/src/hmac.dart
|
| @@ -0,0 +1,107 @@
|
| +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +import 'dart:convert';
|
| +import 'dart:typed_data';
|
| +
|
| +import 'digest.dart';
|
| +import 'digest_sink.dart';
|
| +import 'hash.dart';
|
| +
|
| +/// An implementation of [keyed-hash method authentication codes][rfc].
|
| +///
|
| +/// [rfc]: https://tools.ietf.org/html/rfc2104
|
| +///
|
| +/// HMAC allows messages to be cryptographically authenticated using any
|
| +/// iterated cryptographic hash function.
|
| +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;
|
| +
|
| + /// Create an [Hmac] object from a [Hash] and a binary key.
|
| + ///
|
| + /// The key should be a secret shared between the sender and receiver of the
|
| + /// message.
|
| + Hmac(Hash hash, List<int> key)
|
| + : _hash = hash,
|
| + _key = new Uint8List(hash.blockSize) {
|
| + // Hash the key if it's longer than the block size of the hash.
|
| + if (key.length > _hash.blockSize) key = _hash.convert(key).bytes;
|
| +
|
| + // If [key] is shorter than the block size, the rest of [_key] will be
|
| + // 0-padded.
|
| + _key.setRange(0, key.length, key);
|
| + }
|
| +
|
| + @override
|
| + Digest convert(List<int> data) {
|
| + var innerSink = new DigestSink();
|
| + var outerSink = startChunkedConversion(innerSink);
|
| + outerSink.add(data);
|
| + outerSink.close();
|
| + return innerSink.value;
|
| + }
|
| +
|
| + @override
|
| + ByteConversionSink startChunkedConversion(Sink<Digest> sink) =>
|
| + new _HmacSink(sink, _hash, _key);
|
| +}
|
| +
|
| +/// 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);
|
| + }
|
| +
|
| + @override
|
| + void add(List<int> data) {
|
| + if (_isClosed) throw new StateError("HMAC is closed");
|
| + _innerSink.add(data);
|
| + }
|
| +
|
| + @override
|
| + 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);
|
| + }
|
| +
|
| + @override
|
| + void close() {
|
| + if (_isClosed) return;
|
| + _isClosed = true;
|
| +
|
| + _innerSink.close();
|
| + _outerSink.add(_innerResultSink.value.bytes);
|
| + _outerSink.close();
|
| + }
|
| +}
|
|
|