Skip to content

Commit 693c5b8

Browse files
authored
Feature: RIPEMD-160 (#23)
This branch reimplements the RIPEMD-160 algorithm based on the pointycastle library. This hash function produces a 160-bit output, crucial for encoding addresses in Bitcoin and Cosmos based networks. List of changes: - created a_md4_digest.dart to handle message digestion, including byte processing, block management, and state updates. This class can be extended in the future into: RIPEMD-128, RIPEMD-256, RIPEMD-320, SM3Digest and SHA-1, SHA-224, SHA-256 algorithms. - created ripemd160.dart to implement the RIPEMD-160 hash function - created new methods in register64.dart to support 32 lanes of Register64
1 parent 3c31865 commit 693c5b8

14 files changed

Lines changed: 882 additions & 55 deletions

File tree

lib/cryptography_utils.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export 'src/cdsa/cdsa.dart';
55
export 'src/hash/hmac.dart';
66
export 'src/hash/keccak/keccak.dart';
77
export 'src/hash/pbkdf2.dart';
8-
export 'src/hash/ripemd160.dart';
8+
export 'src/hash/ripemd/ripemd160.dart';
99
export 'src/hash/sha/hash/digest.dart';
1010
export 'src/hash/sha/sha256/sha256.dart';
1111
export 'src/hash/sha/sha512/sha512.dart';

lib/src/bip/bip32/keys/a_bip32_public_key.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ abstract class ABip32PublicKey extends Equatable {
3232

3333
static BigInt calcFingerprint(Uint8List publicKeyBytes) {
3434
Uint8List sha256Fingerprint = Sha256().convert(publicKeyBytes).byteList;
35-
Uint8List ripemd160Fingerprint = Uint8List.fromList(Ripemd160().process(sha256Fingerprint));
35+
Uint8List ripemd160Fingerprint = Ripemd160().process(sha256Fingerprint);
3636
return BigIntUtils.decode(ripemd160Fingerprint.sublist(0, 4));
3737
}
3838

lib/src/hash/keccak/keccakf1600.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121
import 'dart:typed_data';
2222

23-
import 'package:cryptography_utils/src/hash/keccak/register64/register64.dart';
24-
import 'package:cryptography_utils/src/hash/keccak/register64/register64_list.dart';
23+
import 'package:cryptography_utils/src/utils/register64/register64.dart';
24+
import 'package:cryptography_utils/src/utils/register64/register64_list.dart';
2525

2626
///[KeccakF1600] This class is essential for processing input data through the sponge construction, ensuring proper diffusion and security
2727
/// in hash computations. It operates on a 1600-bit state, repeatedly applying a series of transformations to mix the input data and produce
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
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

Comments
 (0)