|
| 1 | +// This class was primarily influenced by: |
| 2 | +// "pointycastle" - Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) |
| 3 | +// |
| 4 | +// Permission is hereby granted, free of charge, to any person obtaining a copy of |
| 5 | +// this software and associated documentation files (the "Software"), to deal in |
| 6 | +// the Software without restriction, including without limitation the rights to |
| 7 | +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
| 8 | +// of the Software, and to permit persons to whom the Software is furnished to do |
| 9 | +// so, subject to the following conditions: |
| 10 | +// |
| 11 | +// The above copyright notice and this permission notice shall be included in all |
| 12 | +// copies or substantial portions of the Software. |
| 13 | +// |
| 14 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 15 | +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
| 16 | +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
| 17 | +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| 18 | +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 19 | +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 20 | + |
| 21 | +import 'dart:typed_data'; |
| 22 | + |
| 23 | +import 'package:cryptography_utils/src/utils/register64/register64.dart'; |
| 24 | + |
| 25 | +/// [AMD4Digest] provides message digestion, including byte processing, block management, and state updates. |
| 26 | +/// This class can serve as a base for RIPEMD-128, RIPEMD-160, RIPEMD-256, RIPEMD-320, SM3Digest, and SHA-1, SHA-224, and SHA-256 algorithms. |
| 27 | +
|
| 28 | +/// Algorithms already implemented using AMD4Digest: RIPEMD-160. Algorithms not yet implemented but potentially possible with this algorithm: RIPEMD-128, RIPEMD-256, RIPEMD-320, SM3Digest, SHA-1, SHA-224 and SHA-256. |
| 29 | +abstract class AMD4Digest { |
| 30 | + final Register64 _byteCountRegister64 = Register64(0); |
| 31 | + final Uint8List _wordBufferUint8List = Uint8List(4); |
| 32 | + final List<int> _bufferList; |
| 33 | + final List<int> _stateList; |
| 34 | + final Endian _endian; |
| 35 | + final int _packedStateSize; |
| 36 | + |
| 37 | + final int _digestSize; |
| 38 | + late int _wordBufferOffset; |
| 39 | + late int _bufferOffset; |
| 40 | + |
| 41 | + AMD4Digest({ |
| 42 | + required Endian endian, |
| 43 | + required int digestSize, |
| 44 | + required int stateSize, |
| 45 | + required int bufferSize, |
| 46 | + required int packedStateSize, |
| 47 | + }) : _endian = endian, |
| 48 | + _digestSize = digestSize, |
| 49 | + _stateList = List<int>.filled(stateSize, 0, growable: false), |
| 50 | + _bufferList = List<int>.filled(bufferSize, 0, growable: false), |
| 51 | + _packedStateSize = packedStateSize { |
| 52 | + _reset(); |
| 53 | + } |
| 54 | + |
| 55 | + List<int> get stateList => _stateList; |
| 56 | + |
| 57 | + List<int> get bufferList => _bufferList; |
| 58 | + |
| 59 | + Uint8List process(Uint8List inputUint8List) { |
| 60 | + _update(inputUint8List, 0, inputUint8List.length); |
| 61 | + |
| 62 | + Uint8List outputUint8List = Uint8List(_digestSize); |
| 63 | + int length = _doFinal(outputUint8List, 0); |
| 64 | + |
| 65 | + return outputUint8List.sublist(0, length); |
| 66 | + } |
| 67 | + |
| 68 | + void resetState(); |
| 69 | + |
| 70 | + void processBlock(); |
| 71 | + |
| 72 | + void _update(Uint8List inputUint8List, int inputOffset, int length) { |
| 73 | + int nbytes; |
| 74 | + int inputOffsetValue = inputOffset; |
| 75 | + int lengthValue = length; |
| 76 | + |
| 77 | + nbytes = _processUntilNextWord(inputUint8List, inputOffsetValue, lengthValue); |
| 78 | + inputOffsetValue += nbytes; |
| 79 | + lengthValue -= nbytes; |
| 80 | + |
| 81 | + nbytes = _processWholeWords(inputUint8List, inputOffsetValue, lengthValue); |
| 82 | + inputOffsetValue += nbytes; |
| 83 | + lengthValue -= nbytes; |
| 84 | + |
| 85 | + _processByte(inputUint8List, inputOffsetValue, lengthValue); |
| 86 | + } |
| 87 | + |
| 88 | + int _doFinal(Uint8List outputUint8List, int outputOffset) { |
| 89 | + Register64 bitLengthRegister64 = Register64() |
| 90 | + ..setRegister64(_byteCountRegister64) |
| 91 | + ..shiftLeft(3); |
| 92 | + |
| 93 | + _processPadding(); |
| 94 | + _processLength(bitLengthRegister64); |
| 95 | + _finalizeBlockProcessing(); |
| 96 | + |
| 97 | + _packState(outputUint8List, outputOffset); |
| 98 | + |
| 99 | + _reset(); |
| 100 | + |
| 101 | + return _digestSize; |
| 102 | + } |
| 103 | + |
| 104 | + void _reset() { |
| 105 | + _byteCountRegister64.setInt(0); |
| 106 | + |
| 107 | + _wordBufferOffset = 0; |
| 108 | + _wordBufferUint8List.fillRange(0, _wordBufferUint8List.length, 0); |
| 109 | + |
| 110 | + _bufferOffset = 0; |
| 111 | + _bufferList.fillRange(0, _bufferList.length, 0); |
| 112 | + |
| 113 | + resetState(); |
| 114 | + } |
| 115 | + |
| 116 | + int _processUntilNextWord(Uint8List inputUint8List, int inputOffset, int length) { |
| 117 | + int processed = 0; |
| 118 | + int inputOffsetValue = inputOffset; |
| 119 | + int lengthValue = length; |
| 120 | + |
| 121 | + while ((_wordBufferOffset != 0) && lengthValue > 0) { |
| 122 | + _updateByte(inputUint8List[inputOffsetValue]); |
| 123 | + |
| 124 | + inputOffsetValue++; |
| 125 | + lengthValue--; |
| 126 | + processed++; |
| 127 | + } |
| 128 | + return processed; |
| 129 | + } |
| 130 | + |
| 131 | + int _processWholeWords(Uint8List inputUint8List, int inputOffset, int length) { |
| 132 | + int processed = 0; |
| 133 | + int inputOffsetValue = inputOffset; |
| 134 | + int lengthValue = length; |
| 135 | + |
| 136 | + while (lengthValue > _wordBufferUint8List.length) { |
| 137 | + _processWord(inputUint8List, inputOffsetValue); |
| 138 | + |
| 139 | + inputOffsetValue += _wordBufferUint8List.length; |
| 140 | + lengthValue -= _wordBufferUint8List.length; |
| 141 | + _byteCountRegister64.sumInt(_wordBufferUint8List.length); |
| 142 | + processed += 4; |
| 143 | + } |
| 144 | + return processed; |
| 145 | + } |
| 146 | + |
| 147 | + void _processByte(Uint8List inputUint8List, int inputOffset, int length) { |
| 148 | + int inputOffsetValue = inputOffset; |
| 149 | + int lengthValue = length; |
| 150 | + |
| 151 | + while (lengthValue > 0) { |
| 152 | + _updateByte(inputUint8List[inputOffsetValue]); |
| 153 | + |
| 154 | + inputOffsetValue++; |
| 155 | + lengthValue--; |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | + void _processPadding() { |
| 160 | + _updateByte(128); |
| 161 | + while (_wordBufferOffset != 0) { |
| 162 | + _updateByte(0); |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + void _updateByte(int input) { |
| 167 | + _wordBufferUint8List[_wordBufferOffset++] = _mask8Bits(input); |
| 168 | + _flushWordBuffer(); |
| 169 | + _byteCountRegister64.sumInt(1); |
| 170 | + } |
| 171 | + |
| 172 | + void _processLength(Register64 bitLengthRegister64) { |
| 173 | + if (_bufferOffset > 14) { |
| 174 | + _finalizeBlockProcessing(); |
| 175 | + } |
| 176 | + |
| 177 | + _bufferList[14] = bitLengthRegister64.lowerHalf; |
| 178 | + _bufferList[15] = bitLengthRegister64.upperHalf; |
| 179 | + } |
| 180 | + |
| 181 | + void _packState(Uint8List outputUint8List, int outputOffset) { |
| 182 | + for (int i = 0; i < _packedStateSize; i++) { |
| 183 | + _packInput32(_stateList[i], outputUint8List, outputOffset + i * 4, _endian); |
| 184 | + } |
| 185 | + } |
| 186 | + |
| 187 | + void _flushWordBuffer() { |
| 188 | + if (_wordBufferOffset == _wordBufferUint8List.length) { |
| 189 | + _processWord(_wordBufferUint8List, 0); |
| 190 | + _wordBufferOffset = 0; |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + void _processWord(Uint8List inputUint8List, int inputOffset) { |
| 195 | + _bufferList[_bufferOffset++] = _unpackInput32Bits(inputUint8List, inputOffset, _endian); |
| 196 | + |
| 197 | + if (_bufferOffset == 16) { |
| 198 | + _finalizeBlockProcessing(); |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + void _finalizeBlockProcessing() { |
| 203 | + processBlock(); |
| 204 | + |
| 205 | + _bufferOffset = 0; |
| 206 | + _bufferList.fillRange(0, 16, 0); |
| 207 | + } |
| 208 | + |
| 209 | + int _mask8Bits(int input) { |
| 210 | + return input & 0xFF; |
| 211 | + } |
| 212 | + |
| 213 | + int _unpackInput32Bits(Uint8List inputUint8List, int offset, Endian endian) { |
| 214 | + ByteData byteData = ByteData.view(inputUint8List.buffer, inputUint8List.offsetInBytes, inputUint8List.length); |
| 215 | + return byteData.getUint32(offset, endian); |
| 216 | + } |
| 217 | + |
| 218 | + void _packInput32(int x, Uint8List outputUint8List, int offset, Endian endian) { |
| 219 | + ByteData.view(outputUint8List.buffer, outputUint8List.offsetInBytes, outputUint8List.length).setUint32(offset, x, endian); |
| 220 | + } |
| 221 | +} |
0 commit comments