diff --git a/lib/src/codecs/base/base58_codec.dart b/lib/src/codecs/base/base58_codec.dart index 0558cd5..f573604 100644 --- a/lib/src/codecs/base/base58_codec.dart +++ b/lib/src/codecs/base/base58_codec.dart @@ -32,7 +32,7 @@ import 'dart:typed_data'; import 'package:codec_utils/src/utils/big_int_utils.dart'; -import 'package:crypto/crypto.dart'; +import 'package:codec_utils/src/utils/sha/sha256/sha256.dart'; /// The [Base58Codec] class is designed for encoding data using the Base58 encoding scheme. class Base58Codec { @@ -98,7 +98,7 @@ class Base58Codec { } static List _computeChecksum(Uint8List dataBytes) { - Uint8List doubleSha256Digest = Uint8List.fromList(sha256.convert(sha256.convert(dataBytes).bytes).bytes); + Uint8List doubleSha256Digest = Sha256().convert(Sha256().convert(dataBytes).byteList).byteList; return doubleSha256Digest.sublist(0, 4); } } diff --git a/lib/src/utils/sha/hash/a_hash.dart b/lib/src/utils/sha/hash/a_hash.dart new file mode 100644 index 0000000..17fbd37 --- /dev/null +++ b/lib/src/utils/sha/hash/a_hash.dart @@ -0,0 +1,50 @@ +//This class was primarily influenced by: +// Copyright 2015, the Dart project authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import 'dart:convert'; + +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; +import 'package:codec_utils/src/utils/sha/hash/digest_sink.dart'; + +/// [AHash] serves as an abstraction for cryptographic hash functions, managing the conversion of byte input into a digest. It ensures a modular and structured approach to hashing, defining a standard interface for processing data in both single-pass and chunked modes. +abstract class AHash extends Converter, Digest> { + @override + Digest convert(List input) { + DigestSink digestSink = DigestSink(); + startChunkedConversion(digestSink) + ..add(input) + ..close(); + return digestSink.valueDigest; + } + + @override + ByteConversionSink startChunkedConversion(Sink sink); + + int get blockSize; +} diff --git a/lib/src/utils/sha/hash/a_hash_sink.dart b/lib/src/utils/sha/hash/a_hash_sink.dart new file mode 100644 index 0000000..5cc3a72 --- /dev/null +++ b/lib/src/utils/sha/hash/a_hash_sink.dart @@ -0,0 +1,140 @@ +//This class was primarily influenced by: +// Copyright 2015, the Dart project authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import 'dart:typed_data'; + +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; +import 'package:typed_data/typed_buffers.dart'; + +/// [AHashSink] is a base class for processing streaming hash input efficiently. It handles incoming data in chunks, manages buffering, and ensures correct endian formatting. +abstract class AHashSink implements Sink> { + static const int bitsPerByte = 8; + static const int bytesPerWord = 4; + static const int mask32 = 0xFFFFFFFF; + + final Sink _sink; + final Endian _endian; + final Uint32List _currentUint8List; + final int _signatureBytes; + final Uint8Buffer _pendingUint8Buffer = Uint8Buffer(); + + int _lengthInBytes = 0; + bool _closedBool = false; + + AHashSink(this._sink, int chunkSize, {Endian endian = Endian.big, int signaturesBytes = 8}) + : _endian = endian, + _signatureBytes = signaturesBytes, + _currentUint8List = Uint32List(chunkSize); + + @override + void add(List dataList) { + if (_closedBool) { + return; + } + _lengthInBytes += dataList.length; + _pendingUint8Buffer.addAll(dataList); + _applyIterate(); + } + + @override + void close() { + if (_closedBool) { + return; + } + _closedBool = true; + _finalizeData(); + _applyIterate(); + _sink + ..add(Digest(_applyByteDigest())) + ..close(); + } + + Uint32List get digestUint32List; + + void updateHash(Uint32List inputUint32List); + + void _applyIterate() { + ByteData pendingByteData = _pendingUint8Buffer.buffer.asByteData(); + int pendingData = _pendingUint8Buffer.length ~/ _currentUint8List.lengthInBytes; + + for (int i = 0; i < pendingData; i++) { + for (int j = 0; j < _currentUint8List.length; j++) { + _currentUint8List[j] = pendingByteData.getUint32(i * _currentUint8List.lengthInBytes + j * bytesPerWord, _endian); + } + updateHash(_currentUint8List); + } + + _pendingUint8Buffer.removeRange(0, pendingData * _currentUint8List.lengthInBytes); + } + + Uint8List _applyByteDigest() { + if (_endian == Endian.host) { + return digestUint32List.buffer.asUint8List(); + } + Uint32List cacheUint32List = digestUint32List; + Uint8List digestUint8List = Uint8List(cacheUint32List.lengthInBytes); + ByteData currentByteData = digestUint8List.buffer.asByteData(); + for (int i = 0; i < cacheUint32List.length; i++) { + currentByteData.setUint32(i * bytesPerWord, cacheUint32List[i]); + } + return digestUint8List; + } + + void _finalizeData() { + _pendingUint8Buffer.add(0x80); + int contentsLength = _lengthInBytes + 1 + _signatureBytes; + int finalizedLength = _applyRoundUp(contentsLength, _currentUint8List.lengthInBytes); + + for (int i = 0; i < finalizedLength - contentsLength; i++) { + _pendingUint8Buffer.add(0); + } + + int lengthInBits = _lengthInBytes * bitsPerByte; + int offsetOutput = _pendingUint8Buffer.length + (_signatureBytes - 8); + + _pendingUint8Buffer.addAll(Uint8List(_signatureBytes)); + + ByteData currentByteData = _pendingUint8Buffer.buffer.asByteData(); + int highBits = lengthInBits ~/ 0x100000000; + int lowBits = lengthInBits & mask32; + + if (_endian == Endian.big) { + currentByteData + ..setUint32(offsetOutput, highBits, _endian) + ..setUint32(offsetOutput + bytesPerWord, lowBits, _endian); + } else { + currentByteData + ..setUint32(offsetOutput, lowBits, _endian) + ..setUint32(offsetOutput + bytesPerWord, highBits, _endian); + } + } + + int _applyRoundUp(int value, int multiple) { + return (value + multiple - 1) & -multiple; + } +} diff --git a/lib/src/utils/sha/hash/digest.dart b/lib/src/utils/sha/hash/digest.dart new file mode 100644 index 0000000..9b0844f --- /dev/null +++ b/lib/src/utils/sha/hash/digest.dart @@ -0,0 +1,41 @@ +//This class was primarily influenced by: +// Copyright 2015, the Dart project authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import 'dart:typed_data'; + +import 'package:equatable/equatable.dart'; + +/// [Digest] representing the final output of a hashing algorithm, encapsulating the computed hash value as a list of bytes. +class Digest extends Equatable { + final Uint8List byteList; + + const Digest(this.byteList); + + @override + List get props => [byteList]; +} diff --git a/lib/src/utils/sha/hash/digest_sink.dart b/lib/src/utils/sha/hash/digest_sink.dart new file mode 100644 index 0000000..aac1a5e --- /dev/null +++ b/lib/src/utils/sha/hash/digest_sink.dart @@ -0,0 +1,48 @@ +//This class was primarily influenced by: +// Copyright 2015, the Dart project authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; + +/// [DigestSink] acts as a simple sink for capturing a digestValue. It ensures that only the first received digest is stored, preventing overwrites. +class DigestSink implements Sink { + Digest? _valueDigest; + + @override + void add(Digest valueDigest) { + if (_valueDigest != null) { + return; + } + _valueDigest = valueDigest; + } + + @override + void close() {} + + Digest get valueDigest => _valueDigest!; +} diff --git a/lib/src/utils/sha/sha256/a_sha_32bit_sink.dart b/lib/src/utils/sha/sha256/a_sha_32bit_sink.dart new file mode 100644 index 0000000..d33e4c6 --- /dev/null +++ b/lib/src/utils/sha/sha256/a_sha_32bit_sink.dart @@ -0,0 +1,138 @@ +//This class was primarily influenced by: +// Copyright 2015, the Dart project authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import 'dart:typed_data'; + +import 'package:codec_utils/src/utils/sha/hash/a_hash_sink.dart'; +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; + +/// [ASha32BitSink] provides hashing logic for SHA256. It processes input data in 512-bit chunks, expanding them into a 64-entry message schedule using bitwise operations. The class applies transformations, including bit rotations, modular additions, and logical operations. +abstract class ASha32BitSink extends AHashSink { + static const List _roundConstantsList = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, // + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ]; + + final Uint32List _extendedUint32List = Uint32List(64); + final Uint32List _digestUint32List; + + ASha32BitSink(Sink sink, this._digestUint32List) : super(sink, 16); + + @override + Uint32List get digestUint32List => _digestUint32List; + + @override + void updateHash(Uint32List inputUint32List) { + for (int i = 0; i < 16; i++) { + _extendedUint32List[i] = inputUint32List[i]; + } + for (int i = 16; i < 64; i++) { + _extendedUint32List[i] = _applyAdd32(_applyAdd32(_applySmallSigma1(_extendedUint32List[i - 2]), _extendedUint32List[i - 7]), + _applyAdd32(_applySmallSigma0(_extendedUint32List[i - 15]), _extendedUint32List[i - 16])); + } + + int aHash = _digestUint32List[0]; + int bHash = _digestUint32List[1]; + int cHash = _digestUint32List[2]; + int dHash = _digestUint32List[3]; + int eHash = _digestUint32List[4]; + int fHash = _digestUint32List[5]; + int gHash = _digestUint32List[6]; + int hHash = _digestUint32List[7]; + + for (int i = 0; i < 64; i++) { + int tmp1 = _applyAdd32(_applyAdd32(hHash, _applyBigSigma1(eHash)), + _applyAdd32(_applyChoiceBits(eHash, fHash, gHash), _applyAdd32(_roundConstantsList[i], _extendedUint32List[i]))); + + int tmp2 = _applyAdd32(_applyBigSigma0(aHash), _applyMajorityBits(aHash, bHash, cHash)); + + hHash = gHash; + gHash = fHash; + fHash = eHash; + eHash = _applyAdd32(dHash, tmp1); + dHash = cHash; + cHash = bHash; + bHash = aHash; + aHash = _applyAdd32(tmp1, tmp2); + } + _digestUint32List[0] = _applyAdd32(aHash, _digestUint32List[0]); + _digestUint32List[1] = _applyAdd32(bHash, _digestUint32List[1]); + _digestUint32List[2] = _applyAdd32(cHash, _digestUint32List[2]); + _digestUint32List[3] = _applyAdd32(dHash, _digestUint32List[3]); + _digestUint32List[4] = _applyAdd32(eHash, _digestUint32List[4]); + _digestUint32List[5] = _applyAdd32(fHash, _digestUint32List[5]); + _digestUint32List[6] = _applyAdd32(gHash, _digestUint32List[6]); + _digestUint32List[7] = _applyAdd32(hHash, _digestUint32List[7]); + } + + int _applyChoiceBits(int xHash, int yHash, int zHash) { + return (xHash & yHash) ^ ((~xHash & AHashSink.mask32) & zHash); + } + + int _applyMajorityBits(int xHash, int yHash, int zHash) { + return (xHash & yHash) ^ (xHash & zHash) ^ (yHash & zHash); + } + + int _applyBigSigma0(int bits) { + return _rotationRight32(2, bits) ^ _rotationRight32(13, bits) ^ _rotationRight32(22, bits); + } + + int _applyBigSigma1(int bits) { + return _rotationRight32(6, bits) ^ _rotationRight32(11, bits) ^ _rotationRight32(25, bits); + } + + int _applySmallSigma0(int bits) { + return _rotationRight32(7, bits) ^ _rotationRight32(18, bits) ^ (bits >> 3); + } + + int _applySmallSigma1(int bits) { + return _rotationRight32(17, bits) ^ _rotationRight32(19, bits) ^ (bits >> 10); + } + + int _applyAdd32(int hash, int operand) { + return (hash + operand) & AHashSink.mask32; + } + + int _rotationRight32(int bits, int value) { + return (value >> bits) | ((value << (32 - bits)) & AHashSink.mask32); + } +} diff --git a/lib/src/utils/sha/sha256/sha256.dart b/lib/src/utils/sha/sha256/sha256.dart new file mode 100644 index 0000000..ac8003a --- /dev/null +++ b/lib/src/utils/sha/sha256/sha256.dart @@ -0,0 +1,50 @@ +//This class was primarily influenced by: +// Copyright 2015, the Dart project authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import 'dart:convert'; + +import 'package:codec_utils/src/utils/sha/hash/a_hash.dart'; +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; +import 'package:codec_utils/src/utils/sha/sha256/sha256_sink.dart'; + +/// [Sha256] is a singleton implementation of the SHA-256 hashing algorithm - defines the block size. +/// This class, with its implementation of the SHA-256 algorithm, is not compatible with the AMD4Digest class. +class Sha256 extends AHash { + static const int _bytesPerWord = 4; + static final Sha256 _sha256 = Sha256._(); + + factory Sha256() => _sha256; + + Sha256._(); + + @override + int get blockSize => 16 * _bytesPerWord; + + @override + ByteConversionSink startChunkedConversion(Sink sink) => ByteConversionSink.from(Sha256Sink(sink)); +} diff --git a/lib/src/utils/sha/sha256/sha256_sink.dart b/lib/src/utils/sha/sha256/sha256_sink.dart new file mode 100644 index 0000000..2c393c8 --- /dev/null +++ b/lib/src/utils/sha/sha256/sha256_sink.dart @@ -0,0 +1,50 @@ +//This class was primarily influenced by: +// Copyright 2015, the Dart project authors. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import 'dart:typed_data'; + +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; +import 'package:codec_utils/src/utils/sha/sha256/a_sha_32bit_sink.dart'; + +/// [Sha256Sink] initializes the hash state. This class processes input and applies SHA-256 transformations to compute the final digest. +class Sha256Sink extends ASha32BitSink { + Sha256Sink(Sink sink) + : super( + sink, + Uint32List.fromList([ + 0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19, + ]), + ); +} diff --git a/lib/src/utils/xoshiro.dart b/lib/src/utils/xoshiro.dart index ba774e0..d69bf7a 100644 --- a/lib/src/utils/xoshiro.dart +++ b/lib/src/utils/xoshiro.dart @@ -26,7 +26,8 @@ import 'dart:math'; -import 'package:crypto/crypto.dart'; +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; +import 'package:codec_utils/src/utils/sha/sha256/sha256.dart'; /// Implements the Xoshiro (xor/shift/rotate) pseudorandom number generator. class Xoshiro implements Random { @@ -38,14 +39,14 @@ class Xoshiro implements Random { /// Creates a new instance of [Xoshiro] with the specified seed. factory Xoshiro(List seed) { - Digest digest = sha256.convert(seed); + Digest digest = Sha256().convert(seed); List s = List.generate(4, (_) => BigInt.zero); for (int i = 0; i < 4; i++) { int o = i * 8; BigInt v = BigInt.zero; for (int n = 0; n < 8; n++) { v = (v << 8).toUnsigned(64); - v = v ^ BigInt.from(digest.bytes[o + n]); + v = v ^ BigInt.from(digest.byteList[o + n]); } s[i] = v.toUnsigned(64); } diff --git a/pubspec.yaml b/pubspec.yaml index fd4052d..ec1de77 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: codec_utils description: "Dart package containing utility methods for data encoding and decoding" -version: 0.0.4 +version: 0.0.5 publish_to: none environment: @@ -15,14 +15,14 @@ dependencies: # https://pub.dev/packages/logger logger: 2.4.0 - # Implementations of SHA, MD5, and HMAC cryptographic functions. - # https://pub:dev/packages/crypto - crypto: 3.0.3 - # Library implementing Bitcoins BIP173 (Bech32 encoding) specification in a Flutter friendly fashion. # https://pub.dev/packages/bech32 bech32: 0.2.2 + # Utility functions and classes related to the dart:typed_data library. + # https://pub.dev/packages/typed_data + typed_data: ^1.3.0 + # CBOR library for Dart. An RFC8949 compliant encoding/decoding CBOR implementation. # https://pub.dev/packages/cbor cbor: diff --git a/test/unit/utils/sha/hash/digest_sink_test.dart b/test/unit/utils/sha/hash/digest_sink_test.dart new file mode 100644 index 0000000..0e02860 --- /dev/null +++ b/test/unit/utils/sha/hash/digest_sink_test.dart @@ -0,0 +1,28 @@ +import 'dart:typed_data'; + +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; +import 'package:codec_utils/src/utils/sha/hash/digest_sink.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +// ignore_for_file: cascade_invocations +void main() { + group('Test of DigestSink.add()', () { + test('Should [return valueDigest] constructed from given data', () { + // Arrange + DigestSink actualDigestSink = DigestSink(); + Digest actualDigest1 = Digest(Uint8List.fromList([1, 2, 3, 4])); + Digest actualDigest2 = Digest(Uint8List.fromList([5, 6, 7, 8])); + + // Act + actualDigestSink.add(actualDigest1); + actualDigestSink.add(actualDigest2); + Digest actualValueDigest = actualDigestSink.valueDigest; + + // Assert + Digest expectedValueDigest = Digest(Uint8List.fromList([1, 2, 3, 4])); + + expect(actualValueDigest, expectedValueDigest); + }); + }); +} diff --git a/test/unit/utils/sha/sha256/sha256_sink_test.dart b/test/unit/utils/sha/sha256/sha256_sink_test.dart new file mode 100644 index 0000000..ae02cf9 --- /dev/null +++ b/test/unit/utils/sha/sha256/sha256_sink_test.dart @@ -0,0 +1,30 @@ +import 'dart:typed_data'; + +import 'package:codec_utils/src/utils/sha/hash/digest_sink.dart'; +import 'package:codec_utils/src/utils/sha/sha256/sha256_sink.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +// ignore_for_file: cascade_invocations +void main() { + group('Tests of Sha256Sink.updateHash()', () { + test('Should [return hash] constructed from given data', () { + // Arrange + String actualDataToHash = 'abcdefghijklmnopqrstuvwxyz'; + Uint32List actualUint32List = Uint32List.fromList(actualDataToHash.codeUnits); + DigestSink actualDigestSink = DigestSink(); + Sha256Sink actualSha256Sink = Sha256Sink(actualDigestSink); + + // Act + actualSha256Sink.updateHash(actualUint32List); + + Uint32List actualDigestUint32List = actualSha256Sink.digestUint32List; + + // Assert + Uint32List expectedDigestUint32List = + Uint32List.fromList([402731661, 1055510437, 791927637, 2947656330, 1514833154, 2081851324, 3567160327, 163114491]); + + expect(actualDigestUint32List, expectedDigestUint32List); + }); + }); +} diff --git a/test/unit/utils/sha/sha256/sha256_test.dart b/test/unit/utils/sha/sha256/sha256_test.dart new file mode 100644 index 0000000..3155436 --- /dev/null +++ b/test/unit/utils/sha/sha256/sha256_test.dart @@ -0,0 +1,25 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:codec_utils/src/utils/sha/hash/digest.dart'; +import 'package:codec_utils/src/utils/sha/sha256/sha256.dart'; +import 'package:test/test.dart'; + +/// For calculating [expectedHashString] an online calculator was used: https://emn178.github.io/online-tools/sha256.html +void main() { + group('Tests of Sha256.convert()', () { + test('Should [return hash] constructed from given data', () { + // Arrange + Uint8List actualDataToHash = utf8.encode('123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~'); + + // Act + Digest actualDigest = Sha256().convert(actualDataToHash); + String actualHashString = base64Encode(actualDigest.byteList); + + // Assert + String expectedHashString = '3wD7XtFKJSyhir71QWYpVt043ekXhh67rrDyHE+EsiQ='; + + expect(actualHashString, expectedHashString); + }); + }); +}