Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Side by Side Diff: lib/src/hmac.dart

Issue 1356403003: Make HMAC a Converter. (Closed) Base URL: git@github.com:dart-lang/crypto.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/utils.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library crypto.hmac; 5 library crypto.hmac;
6 6
7 import 'dart:convert';
7 import 'dart:typed_data'; 8 import 'dart:typed_data';
8 9
9 import 'package:typed_data/typed_data.dart'; 10 import 'package:typed_data/typed_data.dart';
10 11
12 import 'digest.dart';
11 import 'digest_sink.dart'; 13 import 'digest_sink.dart';
12 import 'hash.dart'; 14 import 'hash.dart';
13 15
14 /// An implementation of [keyed-hash method authentication codes][rfc]. 16 /// An implementation of [keyed-hash method authentication codes][rfc].
15 /// 17 ///
16 /// [rfc]: https://tools.ietf.org/html/rfc2104 18 /// [rfc]: https://tools.ietf.org/html/rfc2104
17 /// 19 ///
18 /// HMAC allows messages to be cryptographically authenticated using any 20 /// HMAC allows messages to be cryptographically authenticated using any
19 /// iterated cryptographic hash function. 21 /// iterated cryptographic hash function.
20 /// 22 class HMAC extends Converter<List<int>, Digest> {
21 /// The message's data is added using [add]. Once it's been fully added, the
22 /// [digest] and [close] methods can be used to extract the message
23 /// authentication digest.
24 ///
25 /// If an expected authentication digest is available, the [verify] method may
26 /// also be used to ensure that the message actually corresponds to that digest.
27 // TODO(floitsch): make HMAC implement Sink, EventSink or similar.
28 class HMAC {
29 /// The bytes from the message so far.
30 final _message = new Uint8Buffer();
31
32 /// The hash function used to compute the authentication digest. 23 /// The hash function used to compute the authentication digest.
33 final Hash _hash; 24 final Hash _hash;
34 25
35 /// The secret key shared by the sender and the receiver. 26 /// The secret key shared by the sender and the receiver.
36 final Uint8List _key; 27 final Uint8List _key;
37 28
38 /// Whether [close] has been called. 29 /// The bytes from the message so far.
39 bool _isClosed = false; 30 final _message = new Uint8Buffer();
31
32 /// The sink for implementing the deprecated APIs that involved adding data
33 /// directly to the [HMAC] instance.
34 _HmacSink _sink;
35
36 /// The sink that [_sink] sends the [Digest] to once it finishes hashing.
37 DigestSink _innerSink;
40 38
41 /// Create an [HMAC] object from a [Hash] and a binary key. 39 /// Create an [HMAC] object from a [Hash] and a binary key.
42 /// 40 ///
43 /// The key should be a secret shared between the sender and receiver of the 41 /// The key should be a secret shared between the sender and receiver of the
44 /// message. 42 /// message.
45 HMAC(Hash hash, List<int> key) 43 HMAC(Hash hash, List<int> key)
46 : _hash = hash, 44 : _hash = hash,
47 _key = new Uint8List(hash.blockSize) { 45 _key = new Uint8List(hash.blockSize) {
48 // Hash the key if it's longer than the block size of the hash. 46 // Hash the key if it's longer than the block size of the hash.
49 if (key.length > _hash.blockSize) key = _hash.convert(key).bytes; 47 if (key.length > _hash.blockSize) key = _hash.convert(key).bytes;
50 48
51 // If [key] is shorter than the block size, the rest of [_key] will be 49 // If [key] is shorter than the block size, the rest of [_key] will be
52 // 0-padded. 50 // 0-padded.
53 _key.setRange(0, key.length, key); 51 _key.setRange(0, key.length, key);
52
53 _innerSink = new DigestSink();
54 _sink = startChunkedConversion(_innerSink);
54 } 55 }
55 56
57 Digest convert(List<int> data) {
58 var innerSink = new DigestSink();
59 var outerSink = startChunkedConversion(innerSink);
60 outerSink.add(data);
61 outerSink.close();
62 return innerSink.value;
63 }
64
65 ByteConversionSink startChunkedConversion(Sink<Digest> sink) =>
66 new _HmacSink(sink, _hash, _key);
67
56 /// Adds a list of bytes to the message. 68 /// Adds a list of bytes to the message.
57 /// 69 ///
58 /// If [this] has already been closed, throws a [StateError]. 70 /// If [this] has already been closed, throws a [StateError].
71 @Deprecated("Expires in 1.0.0. Use HMAC.convert() or "
72 "HMAC.startChunkedConversion() instead.")
59 void add(List<int> data) { 73 void add(List<int> data) {
60 if (_isClosed) throw new StateError("HMAC is closed");
61 _message.addAll(data); 74 _message.addAll(data);
62 } 75 _sink.add(data);
63
64 /// Returns the digest of the message so far, as a list of bytes.
65 List<int> get digest {
66 // Compute inner padding.
67 var padding = new Uint8List(_key.length);
68 for (var i = 0; i < padding.length; i++) {
69 padding[i] = 0x36 ^ _key[i];
70 }
71
72 // Inner hash computation.
73 var innerDigest = _hashWithPadding(padding, _message);
74
75 // Compute outer padding.
76 for (var i = 0; i < padding.length; i++) {
77 padding[i] = 0x5c ^ _key[i];
78 }
79
80 // Outer hash computation which is the result.
81 return _hashWithPadding(padding, innerDigest);
82 }
83
84 /// Returns the digest of [padding] followed by [data].
85 List<int> _hashWithPadding(List<int> padding, List<int> data) {
86 var innerSink = new DigestSink();
87 _hash.startChunkedConversion(innerSink)
88 ..add(padding)
89 ..add(data)
90 ..close();
91 return innerSink.value.bytes;
92 } 76 }
93 77
94 /// Closes [this] and returns the digest of the message as a list of bytes. 78 /// Closes [this] and returns the digest of the message as a list of bytes.
95 /// 79 ///
96 /// Once closed, [add] may no longer be called. 80 /// Once closed, [add] may no longer be called.
81 @Deprecated("Expires in 1.0.0. Use HMAC.convert() or "
82 "HMAC.startChunkedConversion() instead.")
97 List<int> close() { 83 List<int> close() {
98 _isClosed = true; 84 _sink.close();
99 return digest; 85 return _innerSink.value.bytes;
86 }
87
88 /// Returns the digest of the message so far, as a list of bytes.
89 @Deprecated("Expires in 1.0.0. Use HMAC.convert() or "
90 "HMAC.startChunkedConversion() instead.")
91 List<int> get digest {
92 if (_sink._isClosed) return _innerSink.value.bytes;
93
94 // This may be called at any point while the message is being hashed, but
95 // the [_HmacSink] only supports getting the value once. To make this work,
96 // we just re-hash everything after we get the digest. It's redundant, but
97 // this API is deprecated anyway.
98 _sink.close();
99 var bytes = _innerSink.value.bytes;
100
101 _innerSink = new DigestSink();
102 _sink = _hash.startChunkedConversion(_innerSink);
103 _sink.add(_message);
104
105 return bytes;
100 } 106 }
101 107
102 /// Returns whether the digest computed for the data so far matches the given 108 /// Returns whether the digest computed for the data so far matches the given
103 /// [digest]. 109 /// [digest].
104 /// 110 ///
105 /// This method should be used instead of iterative comparisons to avoid 111 /// This method should be used instead of iterative comparisons to avoid
106 /// leaking information via timing. 112 /// leaking information via timing.
107 /// 113 ///
108 /// Throws an [ArgumentError] if the given digest does not have the same size 114 /// Throws an [ArgumentError] if the given digest does not have the same size
109 /// as the digest computed by [this]. 115 /// as the digest computed by [this].
116 @Deprecated("Expires in 1.0.0. Use Digest.==() instead.")
110 bool verify(List<int> digest) { 117 bool verify(List<int> digest) {
111 var computedDigest = this.digest; 118 var computedDigest = this.digest;
112 if (digest.length != computedDigest.length) { 119 if (digest.length != computedDigest.length) {
113 throw new ArgumentError( 120 throw new ArgumentError(
114 'Invalid digest size: ${digest.length} in HMAC.verify. ' 121 'Invalid digest size: ${digest.length} in HMAC.verify. '
115 'Expected: ${_hash.blockSize}.'); 122 'Expected: ${_hash.blockSize}.');
116 } 123 }
117 124
118 var result = 0; 125 var result = 0;
119 for (var i = 0; i < digest.length; i++) { 126 for (var i = 0; i < digest.length; i++) {
120 result |= digest[i] ^ computedDigest[i]; 127 result |= digest[i] ^ computedDigest[i];
121 } 128 }
122 return result == 0; 129 return result == 0;
123 } 130 }
124 } 131 }
132
133 /// The concrete implementation of the HMAC algorithm.
134 class _HmacSink extends ByteConversionSink {
135 /// The sink for the outer hash computation.
136 final ByteConversionSink _outerSink;
137
138 /// The sink that [_innerSink]'s result will be added to when it's available.
139 final _innerResultSink = new DigestSink();
140
141 /// The sink for the inner hash computation.
142 ByteConversionSink _innerSink;
143
144 /// Whether [close] has been called.
145 bool _isClosed = false;
146
147 _HmacSink(Sink<Digest> sink, Hash hash, List<int> key)
148 : _outerSink = hash.startChunkedConversion(sink) {
149 _innerSink = hash.startChunkedConversion(_innerResultSink);
150
151 // Compute outer padding.
152 var padding = new Uint8List(key.length);
153 for (var i = 0; i < padding.length; i++) {
154 padding[i] = 0x5c ^ key[i];
155 }
156 _outerSink.add(padding);
157
158 // Compute inner padding.
159 for (var i = 0; i < padding.length; i++) {
160 padding[i] = 0x36 ^ key[i];
161 }
162 _innerSink.add(padding);
163 }
164
165 void add(List<int> data) {
166 if (_isClosed) throw new StateError("HMAC is closed");
167 _innerSink.add(data);
168 }
169
170 void addSlice(List<int> data, int start, int end, bool isLast) {
171 if (_isClosed) throw new StateError("HMAC is closed");
172 _innerSink.addSlice(data, start, end, isLast);
173 }
174
175 void close() {
176 if (_isClosed) return;
177 _isClosed = true;
178
179 _innerSink.close();
180 _outerSink.add(_innerResultSink.value.bytes);
181 _outerSink.close();
182 }
183 }
OLDNEW
« no previous file with comments | « no previous file | test/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698