blob: ee4e040347dd8319389aa803067c603da22c5570 [file] [log] [blame]
// See file LICENSE for more information.
library src.impl.digests.md4_family_digest;
import 'dart:typed_data';
import 'package:pointycastle/src/ufixnum.dart';
import 'package:pointycastle/src/impl/base_digest.dart';
/// Base implementation of MD4 family style digest
abstract class MD4FamilyDigest extends BaseDigest {
final _byteCount = Register64(0);
final _wordBuffer = Uint8List(4);
late int _wordBufferOffset;
final Endian _endian;
final int _packedStateSize;
final List<int> state;
final List<int> buffer;
late int bufferOffset;
MD4FamilyDigest(this._endian, int stateSize, int bufferSize,
[int? packedStateSize])
: _packedStateSize =
(packedStateSize == null) ? stateSize : packedStateSize,
state = List<int>.filled(stateSize, 0, growable: false),
buffer = List<int>.filled(bufferSize, 0, growable: false) {
reset();
}
/// Reset state of digest.
void resetState();
/// Process a whole block of data in extender digest.
void processBlock();
@override
void reset() {
_byteCount.set(0);
_wordBufferOffset = 0;
_wordBuffer.fillRange(0, _wordBuffer.length, 0);
bufferOffset = 0;
buffer.fillRange(0, buffer.length, 0);
resetState();
}
@override
void updateByte(int inp) {
_wordBuffer[_wordBufferOffset++] = clip8(inp);
_processWordIfBufferFull();
_byteCount.sum(1);
}
@override
void update(Uint8List inp, int inpOff, int len) {
int nbytes;
nbytes = _processUntilNextWord(inp, inpOff, len);
inpOff += nbytes;
len -= nbytes;
nbytes = _processWholeWords(inp, inpOff, len);
inpOff += nbytes;
len -= nbytes;
_processBytes(inp, inpOff, len);
}
@override
int doFinal(Uint8List out, int outOff) {
var bitLength = Register64(_byteCount)..shiftl(3);
_processPadding();
_processLength(bitLength);
_doProcessBlock();
_packState(out, outOff);
reset();
return digestSize;
}
/// Process a word (4 bytes) of data stored in [inp], starting at [inpOff].
void _processWord(Uint8List inp, int inpOff) {
buffer[bufferOffset++] = unpack32(inp, inpOff, _endian);
if (bufferOffset == 16) {
_doProcessBlock();
}
}
/// Process a block of data and reset the [buffer].
void _doProcessBlock() {
processBlock();
// reset the offset and clean out the word buffer.
bufferOffset = 0;
buffer.fillRange(0, 16, 0);
}
/// Process [len] bytes from [inp] starting at [inpOff]
void _processBytes(Uint8List inp, int inpOff, int len) {
while (len > 0) {
updateByte(inp[inpOff]);
inpOff++;
len--;
}
}
/// Process data word by word until no more words can be extracted from [inp] and return the number of bytes processed.
int _processWholeWords(Uint8List inp, int inpOff, int len) {
var processed = 0;
while (len > _wordBuffer.length) {
_processWord(inp, inpOff);
inpOff += _wordBuffer.length;
len -= _wordBuffer.length;
_byteCount.sum(_wordBuffer.length);
processed += 4;
}
return processed;
}
/// Process bytes from [inp] until the word buffer [_wordBuffer] is full and reset and return the number of bytes processed.
int _processUntilNextWord(Uint8List inp, int inpOff, int len) {
var processed = 0;
while ((_wordBufferOffset != 0) && (len > 0)) {
updateByte(inp[inpOff]);
inpOff++;
len--;
processed++;
}
return processed;
}
// ignore: comment_references
/// Process a word in [_xBuff] if it is already full and then reset it
void _processWordIfBufferFull() {
if (_wordBufferOffset == _wordBuffer.length) {
_processWord(_wordBuffer, 0);
_wordBufferOffset = 0;
}
}
/// Add final padding to the digest
void _processPadding() {
updateByte(128);
while (_wordBufferOffset != 0) {
updateByte(0);
}
}
// ignore: comment_references
/// Called from [finish] so that extender can process the number of bits processed.
void _processLength(Register64 bitLength) {
if (bufferOffset > 14) {
_doProcessBlock();
}
switch (_endian) {
case Endian.little:
buffer[14] = bitLength.lo32;
buffer[15] = bitLength.hi32;
break;
case Endian.big:
buffer[14] = bitLength.hi32;
buffer[15] = bitLength.lo32;
break;
default:
throw StateError('Invalid endianness: $_endian');
}
}
void _packState(Uint8List out, int outOff) {
for (var i = 0; i < _packedStateSize; i++) {
pack32(state[i], out, (outOff + i * 4), _endian);
}
}
}