@@ -44,12 +44,23 @@ abstract class HashSink implements Sink<List<int>> {
4444 /// This should be updated each time [updateHash] is called.
4545 Uint32List get digest;
4646
47+ /// The number of signature bytes emitted at the end of the message.
48+ ///
49+ /// An encrypted message is followed by a signature which depends
50+ /// on the encryption algorithm used. This value specifies the
51+ /// number of bytes used by this signature. It must always be
52+ /// a power of 2 and no less than 8.
53+ final int _signatureBytes;
54+
4755 /// Creates a new hash.
4856 ///
4957 /// [chunkSizeInWords] represents the size of the input chunks processed by
5058 /// the algorithm, in terms of 32-bit words.
51- HashSink (this ._sink, int chunkSizeInWords, {Endian endian = Endian .big})
59+ HashSink (this ._sink, int chunkSizeInWords,
60+ {Endian endian = Endian .big, int signatureBytes = 8 })
5261 : _endian = endian,
62+ assert (signatureBytes >= 8 ),
63+ _signatureBytes = signatureBytes,
5364 _currentChunk = Uint32List (chunkSizeInWords);
5465
5566 /// Runs a single iteration of the hash computation, updating [digest] with
@@ -82,10 +93,12 @@ abstract class HashSink implements Sink<List<int>> {
8293 Uint8List _byteDigest () {
8394 if (_endian == Endian .host) return digest.buffer.asUint8List ();
8495
85- var byteDigest = Uint8List (digest.lengthInBytes);
86- var byteData = byteDigest.buffer.asByteData ();
87- for (var i = 0 ; i < digest.length; i++ ) {
88- byteData.setUint32 (i * bytesPerWord, digest[i]);
96+ // Cache the digest locally as `get` could be expensive.
97+ final cachedDigest = digest;
98+ final byteDigest = Uint8List (cachedDigest.lengthInBytes);
99+ final byteData = byteDigest.buffer.asByteData ();
100+ for (var i = 0 ; i < cachedDigest.length; i++ ) {
101+ byteData.setUint32 (i * bytesPerWord, cachedDigest[i]);
89102 }
90103 return byteDigest;
91104 }
@@ -116,11 +129,14 @@ abstract class HashSink implements Sink<List<int>> {
116129 /// This adds a 1 bit to the end of the message, and expands it with 0 bits to
117130 /// pad it out.
118131 void _finalizeData () {
119- // Pad out the data with 0x80, eight 0s, and as many more 0s as we need to
120- // land cleanly on a chunk boundary.
132+ // Pad out the data with 0x80, eight or sixteen 0s, and as many more 0s
133+ // as we need to land cleanly on a chunk boundary.
121134 _pendingData.add (0x80 );
122- var contentsLength = _lengthInBytes + 9 ;
123- var finalizedLength = _roundUp (contentsLength, _currentChunk.lengthInBytes);
135+
136+ final contentsLength = _lengthInBytes + 1 /* 0x80 */ + _signatureBytes;
137+ final finalizedLength =
138+ _roundUp (contentsLength, _currentChunk.lengthInBytes);
139+
124140 for (var i = 0 ; i < finalizedLength - contentsLength; i++ ) {
125141 _pendingData.add (0 );
126142 }
@@ -133,9 +149,11 @@ abstract class HashSink implements Sink<List<int>> {
133149 var lengthInBits = _lengthInBytes * bitsPerByte;
134150
135151 // Add the full length of the input data as a 64-bit value at the end of the
136- // hash.
137- var offset = _pendingData.length;
138- _pendingData.addAll (Uint8List (8 ));
152+ // hash. Note: we're only writing out 64 bits, so skip ahead 8 if the
153+ // signature is 128-bit.
154+ final offset = _pendingData.length + (_signatureBytes - 8 );
155+
156+ _pendingData.addAll (Uint8List (_signatureBytes));
139157 var byteData = _pendingData.buffer.asByteData ();
140158
141159 // We're essentially doing byteData.setUint64(offset, lengthInBits, _endian)
0 commit comments