From e6c23c59ee6ed4144ffdfc9a5f7ff4926453f4ca Mon Sep 17 00:00:00 2001 From: balladyna <91394225+balladyna@users.noreply.github.com> Date: Mon, 10 Feb 2025 19:58:04 +0100 Subject: [PATCH] Feature: Crypto dart library The purpose of this branch is to reimplement the SHA-256 and SHA-512 algorithms based on the crypto library. This addition enables secure hashing, which is essential for supporting operations related to Bitcoin, Ethereum and Cosmos, such as generating addresses, validating mnemonics and signing transactions. List of changes: - created a_hash.dart, to serve as an abstraction for cryptographic hash functions by managing the conversion of byte input into a digest - created a_hash_sink.dart, to efficiently process streaming hash input by handling incoming data in chunks, managing buffering and ensuring correct endian formatting - created digest.dart, to represent the final output of hashing algorithm - created digest_sink.dart, to ensure that only the first received digest is stored, preventing overwrites - created a_sha_32bit_sink.dart, sha256.dart and sha256_sink.dart, to initialize the digest state, configure hash computation, define a block size and provide hashing logic for SHA-256 - created a_sha_64bit_sink.dart, sha512.dart and sha512_sink.dart, to initialize the digest state, configure hash computation, define a block size and provide hashing logic for SHA-512 --- lib/cryptography_utils.dart | 3 + .../bitcoin_p2pkh_address_encoder.dart | 4 +- .../bitcoin/bitcoin_p2sh_address_encoder.dart | 5 +- .../bitcoin_p2wpkh_address_encoder.dart | 3 +- .../cosmos_address_encoder.dart | 3 +- .../secp256k1_derivator.dart | 12 +- .../bip/bip32/keys/a_bip32_public_key.dart | 3 +- lib/src/bip/bip39/mnemonic.dart | 4 +- lib/src/cdsa/ecdsa/signer/ecdsa_signer.dart | 4 +- lib/src/cdsa/ecdsa/signer/ecdsa_verifier.dart | 4 +- lib/src/cdsa/ecdsa/signer/rfc6979.dart | 6 +- lib/src/hash/hmac.dart | 10 +- lib/src/hash/pbkdf2.dart | 8 +- lib/src/hash/sha/hash/a_hash.dart | 50 +++ lib/src/hash/sha/hash/a_hash_sink.dart | 140 +++++++++ lib/src/hash/sha/hash/digest.dart | 41 +++ lib/src/hash/sha/hash/digest_sink.dart | 48 +++ lib/src/hash/sha/sha256/a_sha_32bit_sink.dart | 138 +++++++++ lib/src/hash/sha/sha256/sha256.dart | 49 +++ lib/src/hash/sha/sha256/sha256_sink.dart | 50 +++ lib/src/hash/sha/sha512/a_sha_64bit_sink.dart | 292 ++++++++++++++++++ lib/src/hash/sha/sha512/sha512.dart | 49 +++ lib/src/hash/sha/sha512/sha_512_sink.dart | 58 ++++ lib/src/signer/cosmos/cosmos_signer.dart | 5 +- lib/src/signer/cosmos/cosmos_verifier.dart | 5 +- lib/src/signer/ethereum/ethereum_signer.dart | 3 +- .../signer/ethereum/ethereum_verifier.dart | 3 +- lib/src/transactions/cosmos/cosmos_tx.dart | 3 +- pubspec.yaml | 10 +- test/cdsa/ecdsa/signer/ecdsa_signer_test.dart | 3 +- .../ecdsa/signer/ecdsa_verifier_test.dart | 5 +- test/cdsa/ecdsa/signer/rfc6979_test.dart | 3 +- test/hash/hmac_test.dart | 79 +++-- test/hash/sha/hash/digest_sink_test.dart | 28 ++ test/hash/sha/sha256/sha256_sink_test.dart | 30 ++ test/hash/sha/sha256/sha256_test.dart | 25 ++ test/hash/sha/sha512/sha512_sink_test.dart | 33 ++ test/hash/sha/sha512/sha512_test.dart | 26 ++ 38 files changed, 1160 insertions(+), 85 deletions(-) create mode 100644 lib/src/hash/sha/hash/a_hash.dart create mode 100644 lib/src/hash/sha/hash/a_hash_sink.dart create mode 100644 lib/src/hash/sha/hash/digest.dart create mode 100644 lib/src/hash/sha/hash/digest_sink.dart create mode 100644 lib/src/hash/sha/sha256/a_sha_32bit_sink.dart create mode 100644 lib/src/hash/sha/sha256/sha256.dart create mode 100644 lib/src/hash/sha/sha256/sha256_sink.dart create mode 100644 lib/src/hash/sha/sha512/a_sha_64bit_sink.dart create mode 100644 lib/src/hash/sha/sha512/sha512.dart create mode 100644 lib/src/hash/sha/sha512/sha_512_sink.dart create mode 100644 test/hash/sha/hash/digest_sink_test.dart create mode 100644 test/hash/sha/sha256/sha256_sink_test.dart create mode 100644 test/hash/sha/sha256/sha256_test.dart create mode 100644 test/hash/sha/sha512/sha512_sink_test.dart create mode 100644 test/hash/sha/sha512/sha512_test.dart diff --git a/lib/cryptography_utils.dart b/lib/cryptography_utils.dart index 405058bd..eea2d787 100644 --- a/lib/cryptography_utils.dart +++ b/lib/cryptography_utils.dart @@ -6,6 +6,9 @@ export 'src/hash/hmac.dart'; export 'src/hash/keccak/keccak.dart'; export 'src/hash/pbkdf2.dart'; export 'src/hash/ripemd160.dart'; +export 'src/hash/sha/hash/digest.dart'; +export 'src/hash/sha/sha256/sha256.dart'; +export 'src/hash/sha/sha512/sha512.dart'; export 'src/signer/export.dart'; export 'src/slip/slip.dart'; export 'src/transactions/export.dart'; diff --git a/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2pkh_address_encoder.dart b/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2pkh_address_encoder.dart index cae6d51e..568f874f 100644 --- a/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2pkh_address_encoder.dart +++ b/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2pkh_address_encoder.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import 'package:codec_utils/codec_utils.dart'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; /// The [BitcoinP2PKHAddressEncoder] class is designed for encoding P2PKH (Pay-to-PubKey-Hash) addresses in accordance with Bitcoin. @@ -9,6 +8,7 @@ import 'package:cryptography_utils/cryptography_utils.dart'; /// Through these methods, it produces addresses prefixed with '1 and encoded with Base58Check'. class BitcoinP2PKHAddressEncoder extends ABlockchainAddressEncoder { static const List _networkVersionBytes = [0x00]; + final PublicKeyMode publicKeyMode; BitcoinP2PKHAddressEncoder({ @@ -25,7 +25,7 @@ class BitcoinP2PKHAddressEncoder extends ABlockchainAddressEncoder[..._networkVersionBytes, ...publicKeyHash])); diff --git a/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2sh_address_encoder.dart b/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2sh_address_encoder.dart index f07f916d..04635789 100644 --- a/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2sh_address_encoder.dart +++ b/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2sh_address_encoder.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import 'package:codec_utils/codec_utils.dart'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; /// The [BitcoinP2SHAddressEncoder] class is designed for encoding P2SH (Pay-to-Script-Hash) addresses in accordance with Bitcoin. @@ -26,11 +25,11 @@ class BitcoinP2SHAddressEncoder extends ABlockchainAddressEncoder _addScriptSignature(Secp256k1PublicKey publicKey) { - Uint8List publicKeyFingerprint = Uint8List.fromList(sha256.convert(publicKey.compressed).bytes); + Uint8List publicKeyFingerprint = Sha256().convert(publicKey.compressed).byteList; Uint8List publicKeyHash = Ripemd160().process(publicKeyFingerprint); Uint8List signatureBytes = Uint8List.fromList([..._scriptBytes, ...publicKeyHash]); - Uint8List signatureFingerprint = Uint8List.fromList(sha256.convert(signatureBytes).bytes); + Uint8List signatureFingerprint = Sha256().convert(signatureBytes).byteList; Uint8List signatureHash = Ripemd160().process(signatureFingerprint); return signatureHash; diff --git a/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2wpkh_address_encoder.dart b/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2wpkh_address_encoder.dart index 672d5a1c..4258bb05 100644 --- a/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2wpkh_address_encoder.dart +++ b/lib/src/bip/bip32/address_encoders/bitcoin/bitcoin_p2wpkh_address_encoder.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import 'package:codec_utils/codec_utils.dart'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; /// The [BitcoinP2WPKHAddressEncoder] class is designed for encoding P2WPKH (Pay-to-Witness-Public-Key-Hash) addresses in accordance with Bitcoin. @@ -24,7 +23,7 @@ class BitcoinP2WPKHAddressEncoder extends ABlockchainAddressEncoder @override String encodePublicKey(Secp256k1PublicKey publicKey) { - Uint8List publicKeyFingerprint = Uint8List.fromList(sha256.convert(publicKey.compressed).bytes); + Uint8List publicKeyFingerprint = Sha256().convert(publicKey.compressed).byteList; Uint8List publicKeyHash = Ripemd160().process(publicKeyFingerprint); return Bech32Codec.encode(Bech32Pair(hrp: hrp, data: publicKeyHash)); diff --git a/lib/src/bip/bip32/derivators/legacy_derivators/secp256k1_derivator.dart b/lib/src/bip/bip32/derivators/legacy_derivators/secp256k1_derivator.dart index 0aa5c62a..633c0753 100644 --- a/lib/src/bip/bip32/derivators/legacy_derivators/secp256k1_derivator.dart +++ b/lib/src/bip/bip32/derivators/legacy_derivators/secp256k1_derivator.dart @@ -31,7 +31,6 @@ import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:cryptography_utils/src/bip/bip32/derivators/derivator_type.dart'; import 'package:cryptography_utils/src/utils/big_int_utils.dart'; @@ -60,7 +59,7 @@ class Secp256k1Derivator extends ALegacyDerivator { Future deriveMasterKey(Mnemonic mnemonic) async { LegacyMnemonicSeedGenerator seedGenerator = LegacyMnemonicSeedGenerator(); Uint8List seed = await seedGenerator.generateSeed(mnemonic); - Uint8List hmacHash = HMAC(hash: sha512, key: Bip32HMACKeys.hmacKeySecp256k1Bytes).process(seed); + Uint8List hmacHash = HMAC(hash: Sha512(), key: Bip32HMACKeys.hmacKeySecp256k1Bytes).process(seed); Uint8List privateKey = hmacHash.sublist(0, 32); Uint8List chainCode = hmacHash.sublist(32); @@ -99,7 +98,7 @@ class Secp256k1Derivator extends ALegacyDerivator { } List data = [...parentSecp256k1PublicKey.compressed, ...legacyDerivationPathElement.toBytes()]; - Uint8List hmacHash = HMAC(hash: sha512, key: parentBip32KeyMetadata.chainCode!).process(data); + Uint8List hmacHash = HMAC(hash: Sha512(), key: parentBip32KeyMetadata.chainCode!).process(data); Uint8List scalarBytes = hmacHash.sublist(0, 32); Uint8List chainCodeBytes = hmacHash.sublist(32); @@ -125,8 +124,9 @@ class Secp256k1Derivator extends ALegacyDerivator { throw ArgumentError('Cannot derive hardened key without chain code'); } - Uint8List data = Uint8List.fromList([..._hardenedPrivateKeyPrefix, ...parentSecp256k1PrivateKey.bytes, ...legacyDerivationPathElement.toBytes()]); - Uint8List hmacHash = HMAC(hash: sha512, key: parentBip32KeyMetadata.chainCode!).process(data); + Uint8List data = + Uint8List.fromList([..._hardenedPrivateKeyPrefix, ...parentSecp256k1PrivateKey.bytes, ...legacyDerivationPathElement.toBytes()]); + Uint8List hmacHash = HMAC(hash: Sha512(), key: parentBip32KeyMetadata.chainCode!).process(data); Uint8List scalarBytes = hmacHash.sublist(0, 32); Uint8List chainCodeBytes = hmacHash.sublist(32); @@ -156,7 +156,7 @@ class Secp256k1Derivator extends ALegacyDerivator { } Uint8List data = Uint8List.fromList([...parentSecp256k1PrivateKey.publicKey.compressed, ...legacyDerivationPathElement.toBytes()]); - Uint8List hmacHash = HMAC(hash: sha512, key: parentBip32KeyMetadata.chainCode!).process(data); + Uint8List hmacHash = HMAC(hash: Sha512(), key: parentBip32KeyMetadata.chainCode!).process(data); Uint8List scalarBytes = hmacHash.sublist(0, 32); Uint8List chainCodeBytes = hmacHash.sublist(32); diff --git a/lib/src/bip/bip32/keys/a_bip32_public_key.dart b/lib/src/bip/bip32/keys/a_bip32_public_key.dart index 4dd9733b..a2a56b81 100644 --- a/lib/src/bip/bip32/keys/a_bip32_public_key.dart +++ b/lib/src/bip/bip32/keys/a_bip32_public_key.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import 'package:codec_utils/codec_utils.dart'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:cryptography_utils/src/utils/big_int_utils.dart'; import 'package:equatable/equatable.dart'; @@ -32,7 +31,7 @@ abstract class ABip32PublicKey extends Equatable { }); static BigInt calcFingerprint(Uint8List publicKeyBytes) { - Uint8List sha256Fingerprint = Uint8List.fromList(sha256.convert(publicKeyBytes).bytes); + Uint8List sha256Fingerprint = Sha256().convert(publicKeyBytes).byteList; Uint8List ripemd160Fingerprint = Uint8List.fromList(Ripemd160().process(sha256Fingerprint)); return BigIntUtils.decode(ripemd160Fingerprint.sublist(0, 4)); } diff --git a/lib/src/bip/bip39/mnemonic.dart b/lib/src/bip/bip39/mnemonic.dart index f27101a4..99fe3057 100644 --- a/lib/src/bip/bip39/mnemonic.dart +++ b/lib/src/bip/bip39/mnemonic.dart @@ -1,10 +1,8 @@ import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:cryptography_utils/src/utils/binary_utils.dart'; import 'package:cryptography_utils/src/utils/secure_random.dart'; - import 'package:equatable/equatable.dart'; /// The [Mnemonic] class is designed for handling mnemonic phrases, which are human-readable sequences of words used to generate and recover cryptographic keys. @@ -77,7 +75,7 @@ class Mnemonic extends Equatable { static String _calculateChecksum(Uint8List entropy) { int entropyBitsLength = entropy.length * 8; int checksumSize = entropyBitsLength ~/ 32; - List hash = sha256.convert(entropy).bytes; + List hash = Sha256().convert(entropy).byteList; return BinaryUtils.bytesToBinary(hash).substring(0, checksumSize); } diff --git a/lib/src/cdsa/ecdsa/signer/ecdsa_signer.dart b/lib/src/cdsa/ecdsa/signer/ecdsa_signer.dart index cff026da..d53b3822 100644 --- a/lib/src/cdsa/ecdsa/signer/ecdsa_signer.dart +++ b/lib/src/cdsa/ecdsa/signer/ecdsa_signer.dart @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/a_hash.dart'; import 'package:cryptography_utils/src/utils/big_int_utils.dart'; /// This class implements the functionality necessary to generate and verify digital signatures using @@ -29,7 +29,7 @@ import 'package:cryptography_utils/src/utils/big_int_utils.dart'; /// which can then be verified by others with the signer's public key. class ECDSASigner { /// The hash function used for generating the message digest. - final Hash hashFunction; + final AHash hashFunction; /// The ECDSA private key used for signing the message. final ECPrivateKey ecPrivateKey; diff --git a/lib/src/cdsa/ecdsa/signer/ecdsa_verifier.dart b/lib/src/cdsa/ecdsa/signer/ecdsa_verifier.dart index 3e54f42c..fd2bcb94 100644 --- a/lib/src/cdsa/ecdsa/signer/ecdsa_verifier.dart +++ b/lib/src/cdsa/ecdsa/signer/ecdsa_verifier.dart @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/a_hash.dart'; import 'package:cryptography_utils/src/utils/big_int_utils.dart'; import 'package:cryptography_utils/src/utils/ec_point_utils.dart'; @@ -31,7 +31,7 @@ import 'package:cryptography_utils/src/utils/ec_point_utils.dart'; /// of the corresponding private key and that the data has not been altered since it was signed. class ECDSAVerifier { /// The hash function used for generating the message digest. - final Hash hashFunction; + final AHash hashFunction; /// The ECDSA public key which will be used to verification. final ECPublicKey ecPublicKey; diff --git a/lib/src/cdsa/ecdsa/signer/rfc6979.dart b/lib/src/cdsa/ecdsa/signer/rfc6979.dart index f585f39c..c564af9d 100644 --- a/lib/src/cdsa/ecdsa/signer/rfc6979.dart +++ b/lib/src/cdsa/ecdsa/signer/rfc6979.dart @@ -20,15 +20,15 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/a_hash.dart'; import 'package:cryptography_utils/src/utils/big_int_utils.dart'; import 'package:cryptography_utils/src/utils/bytes_utils.dart'; /// The `RFC6979` class implements the deterministic generation of the ephemeral key 'k' for ECDSA signatures as outlined in RFC 6979. /// https://www.rfc-editor.org/rfc/rfc6979.html#section-3.2 class RFC6979 { - late final Hash _hashFunction; + late final AHash _hashFunction; late final int _hLen; late final List _m; @@ -49,7 +49,7 @@ class RFC6979 { required BigInt d, /// RFC6979 H parameter - required Hash hashFunction, + required AHash hashFunction, }) { _m = m; _n = n; diff --git a/lib/src/hash/hmac.dart b/lib/src/hash/hmac.dart index 44e6eaa4..48727231 100644 --- a/lib/src/hash/hmac.dart +++ b/lib/src/hash/hmac.dart @@ -1,11 +1,11 @@ import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/a_hash.dart'; /// Hash-based Message Authentication Code. /// It computes a MAC using a given hash function with a secret key and input data. class HMAC { - final Hash hash; + final AHash hash; late final List key; final List _chunks = []; @@ -50,8 +50,8 @@ class HMAC { Uint8List get digest { Uint8List message = _chunks.fold(Uint8List(0), (Uint8List previous, Uint8List chunk) => Uint8List.fromList(previous + chunk)); - List innerHash = hash.convert(iKeyPad + message).bytes; - List finalHash = hash.convert(oKeyPad + innerHash).bytes; + List innerHash = hash.convert(iKeyPad + message).byteList; + List finalHash = hash.convert(oKeyPad + innerHash).byteList; return Uint8List.fromList(finalHash); } @@ -60,7 +60,7 @@ class HMAC { Uint8List _normalizeKey(List key) { Uint8List normalizedKey = Uint8List.fromList(key); if (normalizedKey.length > digestSize) { - normalizedKey = Uint8List.fromList(hash.convert(key).bytes); + normalizedKey = Uint8List.fromList(hash.convert(key).byteList); } if (normalizedKey.length < digestSize) { List padding = List.filled(digestSize - normalizedKey.length, 0); diff --git a/lib/src/hash/pbkdf2.dart b/lib/src/hash/pbkdf2.dart index 7bcf73fe..e49795a3 100644 --- a/lib/src/hash/pbkdf2.dart +++ b/lib/src/hash/pbkdf2.dart @@ -31,8 +31,8 @@ import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/a_hash.dart'; /// The [PBKDF2] class utilizes the PBKDF2 (Password-Based Key Derivation Function 2) hashing algorithm /// to generate secure cryptographic keys from passwords. It is frequently applied for hashing passwords @@ -40,13 +40,13 @@ import 'package:cryptography_utils/cryptography_utils.dart'; class PBKDF2 { final int iterations; final int outputLength; - final Hash hash; + final AHash hash; PBKDF2({ this.iterations = 2048, this.outputLength = 64, - this.hash = sha512, - }); + AHash? hash, + }) : hash = hash ?? Sha512(); /// Derives a cryptographic key from the provided password and salt using the PBKDF2 algorithm. Uint8List process(Uint8List password, Uint8List salt) { diff --git a/lib/src/hash/sha/hash/a_hash.dart b/lib/src/hash/sha/hash/a_hash.dart new file mode 100644 index 00000000..14f2f9b3 --- /dev/null +++ b/lib/src/hash/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:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:cryptography_utils/src/hash/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/hash/sha/hash/a_hash_sink.dart b/lib/src/hash/sha/hash/a_hash_sink.dart new file mode 100644 index 00000000..051fa58b --- /dev/null +++ b/lib/src/hash/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:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:typed_data/typed_data.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/hash/sha/hash/digest.dart b/lib/src/hash/sha/hash/digest.dart new file mode 100644 index 00000000..9b0844f0 --- /dev/null +++ b/lib/src/hash/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/hash/sha/hash/digest_sink.dart b/lib/src/hash/sha/hash/digest_sink.dart new file mode 100644 index 00000000..707f0b4c --- /dev/null +++ b/lib/src/hash/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:cryptography_utils/src/hash/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/hash/sha/sha256/a_sha_32bit_sink.dart b/lib/src/hash/sha/sha256/a_sha_32bit_sink.dart new file mode 100644 index 00000000..9c5dbd02 --- /dev/null +++ b/lib/src/hash/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:cryptography_utils/src/hash/sha/hash/a_hash_sink.dart'; +import 'package:cryptography_utils/src/hash/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/hash/sha/sha256/sha256.dart b/lib/src/hash/sha/sha256/sha256.dart new file mode 100644 index 00000000..1006e333 --- /dev/null +++ b/lib/src/hash/sha/sha256/sha256.dart @@ -0,0 +1,49 @@ +//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:cryptography_utils/src/hash/sha/hash/a_hash.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:cryptography_utils/src/hash/sha/sha256/sha256_sink.dart'; + +/// [Sha256] is a singleton implementation of the SHA-256 hashing algorithm - defines the block size. +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/hash/sha/sha256/sha256_sink.dart b/lib/src/hash/sha/sha256/sha256_sink.dart new file mode 100644 index 00000000..b8568f67 --- /dev/null +++ b/lib/src/hash/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:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:cryptography_utils/src/hash/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/hash/sha/sha512/a_sha_64bit_sink.dart b/lib/src/hash/sha/sha512/a_sha_64bit_sink.dart new file mode 100644 index 00000000..db114f87 --- /dev/null +++ b/lib/src/hash/sha/sha512/a_sha_64bit_sink.dart @@ -0,0 +1,292 @@ +//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:cryptography_utils/src/hash/sha/hash/a_hash_sink.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/digest.dart'; + +///[ASha64BitSink] is an abstract class responsible for managing the internal state of the SHA-64 bit algorithm, using a 1600-bit state and multiple intermediate variables. It processes data through a series of transformations, including rotations, shifts, and bitwise operations, to generate the hash output. +abstract class ASha64BitSink extends AHashSink { + static const int _hashVarAIndex = _sigmaIndex4 + 2; + static const int _hashVarBIndex = _hashVarAIndex + 2; + static const int _hashVarCIndex = _hashVarBIndex + 2; + static const int _hashVarDIndex = _hashVarCIndex + 2; + static const int _hashVarEIndex = _hashVarDIndex + 2; + static const int _hashVarFIndex = _hashVarEIndex + 2; + static const int _hashVarGIndex = _hashVarFIndex + 2; + static const int _hashVarHIndex = _hashVarGIndex + 2; + static const int _rotateRightIndex1 = 0; + static const int _rotateRightIndex2 = _rotateRightIndex1 + 2; + static const int _sigmaIndex1 = _rotateRightIndex2 + 2; + static const int _sigmaIndex2 = _sigmaIndex1 + 2; + static const int _sigmaIndex3 = _sigmaIndex2 + 2; + static const int _sigmaIndex4 = _sigmaIndex3 + 2; + static const int _tmp1Index = _hashVarHIndex + 2; + static const int _tmp2Index = _tmp1Index + 2; + static const int _tmp3Index = _tmp2Index + 2; + static const int _tmp4Index = _tmp3Index + 2; + static const int _tmp5Index = _tmp4Index + 2; + static const int digestBytes = 16; + + final Uint32List _digestUint32List; + final Uint32List _extendedUint32List = Uint32List(160); + final Uint32List _numUint32List = Uint32List(12 + 16 + 10); + final Uint32List _roundConstUint32List = Uint32List.fromList([ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, // + 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, + 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, + 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, + 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, + 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, + 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, + 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, + 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, + 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, + 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, + 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, + 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, + 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, + 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, + 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, + 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, + 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, + 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, + 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, + 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, + 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, + 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, + 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, + 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, + 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, + 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, + 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, + 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, + 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, + 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, + 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, + 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, + 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, + 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, + 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817, + ]); + + ASha64BitSink(Sink sink, this._digestUint32List) : super(sink, 32, signaturesBytes: 16); + + @override + Uint32List get digestUint32List { + return Uint32List.view(_digestUint32List.buffer, 0, digestBytes); + } + + @override + void updateHash(Uint32List inputUint32List) { + for (int i = 0; i < 32; i++) { + _extendedUint32List[i] = inputUint32List[i]; + } + + for (int i = 32; i < 160; i += 2) { + _applySmallSigma1(_extendedUint32List, i - 2 * 2, _numUint32List, _tmp1Index); + _applyAdd(_numUint32List, _tmp1Index, _extendedUint32List, i - 7 * 2, _numUint32List, _tmp2Index); + + _applySmallSigma0(_extendedUint32List, i - 15 * 2, _numUint32List, _tmp1Index); + + _applyAdd(_numUint32List, _tmp1Index, _extendedUint32List, i - 16 * 2, _numUint32List, _tmp3Index); + _applyAdd(_numUint32List, _tmp2Index, _numUint32List, _tmp3Index, _extendedUint32List, i); + } + + _numUint32List.setRange(_hashVarAIndex, _hashVarHIndex + 2, _digestUint32List); + + for (int i = 0; i < 160; i += 2) { + _applyBigSigma1(_numUint32List, _hashVarEIndex, _numUint32List, _tmp1Index); + _applyAdd(_numUint32List, _hashVarHIndex, _numUint32List, _tmp1Index, _numUint32List, _tmp2Index); + + _applyChoiceBits(_numUint32List, _hashVarEIndex, _numUint32List, _hashVarFIndex, _numUint32List, _hashVarGIndex, _numUint32List, _tmp3Index); + + _applyAdd(_numUint32List, _tmp2Index, _numUint32List, _tmp3Index, _numUint32List, _tmp4Index); + _applyAdd(_roundConstUint32List, i, _extendedUint32List, i, _numUint32List, _tmp5Index); + _applyAdd(_numUint32List, _tmp4Index, _numUint32List, _tmp5Index, _numUint32List, _tmp1Index); + + _applyBigSigma0(_numUint32List, _hashVarAIndex, _numUint32List, _tmp3Index); + + _applyMajorityBits(_numUint32List, _hashVarAIndex, _numUint32List, _hashVarBIndex, _numUint32List, _hashVarCIndex, _numUint32List, _tmp4Index); + _applyAdd(_numUint32List, _tmp3Index, _numUint32List, _tmp4Index, _numUint32List, _tmp2Index); + + _numUint32List[_hashVarHIndex] = _numUint32List[_hashVarGIndex]; + _numUint32List[_hashVarHIndex + 1] = _numUint32List[_hashVarGIndex + 1]; + _numUint32List[_hashVarGIndex] = _numUint32List[_hashVarFIndex]; + _numUint32List[_hashVarGIndex + 1] = _numUint32List[_hashVarFIndex + 1]; + _numUint32List[_hashVarFIndex] = _numUint32List[_hashVarEIndex]; + _numUint32List[_hashVarFIndex + 1] = _numUint32List[_hashVarEIndex + 1]; + + _applyAdd(_numUint32List, _hashVarDIndex, _numUint32List, _tmp1Index, _numUint32List, _hashVarEIndex); + + _numUint32List[_hashVarDIndex] = _numUint32List[_hashVarCIndex]; + _numUint32List[_hashVarDIndex + 1] = _numUint32List[_hashVarCIndex + 1]; + _numUint32List[_hashVarCIndex] = _numUint32List[_hashVarBIndex]; + _numUint32List[_hashVarCIndex + 1] = _numUint32List[_hashVarBIndex + 1]; + _numUint32List[_hashVarBIndex] = _numUint32List[_hashVarAIndex]; + _numUint32List[_hashVarBIndex + 1] = _numUint32List[_hashVarAIndex + 1]; + + _applyAdd(_numUint32List, _tmp1Index, _numUint32List, _tmp2Index, _numUint32List, _hashVarAIndex); + } + + _applyAddWithCarry(_digestUint32List, 0, _numUint32List, _hashVarAIndex); + _applyAddWithCarry(_digestUint32List, 2, _numUint32List, _hashVarBIndex); + _applyAddWithCarry(_digestUint32List, 4, _numUint32List, _hashVarCIndex); + _applyAddWithCarry(_digestUint32List, 6, _numUint32List, _hashVarDIndex); + _applyAddWithCarry(_digestUint32List, 8, _numUint32List, _hashVarEIndex); + _applyAddWithCarry(_digestUint32List, 10, _numUint32List, _hashVarFIndex); + _applyAddWithCarry(_digestUint32List, 12, _numUint32List, _hashVarGIndex); + _applyAddWithCarry(_digestUint32List, 14, _numUint32List, _hashVarHIndex); + } + + void _applyBigSigma0(Uint32List inputUint32List, int offsetInput, Uint32List outputUint32List, int offsetRightOutput) { + _rotateRight(28, inputUint32List, offsetInput, _numUint32List, _sigmaIndex1); + _rotateRight(34, inputUint32List, offsetInput, _numUint32List, _sigmaIndex2); + _rotateRight(39, inputUint32List, offsetInput, _numUint32List, _sigmaIndex3); + + _applyXor(_numUint32List, _sigmaIndex2, _numUint32List, _sigmaIndex3, _numUint32List, _sigmaIndex4); + _applyXor(_numUint32List, _sigmaIndex1, _numUint32List, _sigmaIndex4, outputUint32List, offsetRightOutput); + } + + void _applyBigSigma1(Uint32List inputUint32List, int offsetInput, Uint32List outputUint32List, int offsetRightOutput) { + _rotateRight(14, inputUint32List, offsetInput, _numUint32List, _sigmaIndex1); + _rotateRight(18, inputUint32List, offsetInput, _numUint32List, _sigmaIndex2); + _rotateRight(41, inputUint32List, offsetInput, _numUint32List, _sigmaIndex3); + + _applyXor(_numUint32List, _sigmaIndex2, _numUint32List, _sigmaIndex3, _numUint32List, _sigmaIndex4); + _applyXor(_numUint32List, _sigmaIndex1, _numUint32List, _sigmaIndex4, outputUint32List, offsetRightOutput); + } + + void _applySmallSigma0(Uint32List inputUint32List, int offsetInput, Uint32List outputUint32List, int offsetRightOutput) { + _rotateRight(1, inputUint32List, offsetInput, _numUint32List, _sigmaIndex1); + _rotateRight(8, inputUint32List, offsetInput, _numUint32List, _sigmaIndex2); + + _shiftRight(7, inputUint32List, offsetInput, _numUint32List, _sigmaIndex3); + + _applyXor(_numUint32List, _sigmaIndex2, _numUint32List, _sigmaIndex3, _numUint32List, _sigmaIndex4); + _applyXor(_numUint32List, _sigmaIndex1, _numUint32List, _sigmaIndex4, outputUint32List, offsetRightOutput); + } + + void _applySmallSigma1(Uint32List inputUint32List, int offsetInput, Uint32List outputUint32List, int offsetRightOutput) { + _rotateRight(19, inputUint32List, offsetInput, _numUint32List, _sigmaIndex1); + _rotateRight(61, inputUint32List, offsetInput, _numUint32List, _sigmaIndex2); + + _shiftRight(6, inputUint32List, offsetInput, _numUint32List, _sigmaIndex3); + + _applyXor(_numUint32List, _sigmaIndex2, _numUint32List, _sigmaIndex3, _numUint32List, _sigmaIndex4); + _applyXor(_numUint32List, _sigmaIndex1, _numUint32List, _sigmaIndex4, outputUint32List, offsetRightOutput); + } + + void _rotateRight(int bits, Uint32List inputUint32List, int offsetInput, Uint32List outputUint32List, int offsetRightOutput) { + _shiftRight(bits, inputUint32List, offsetInput, _numUint32List, _rotateRightIndex1); + _shiftLeft(64 - bits, inputUint32List, offsetInput, _numUint32List, _rotateRightIndex2); + + _applyOr(_numUint32List, _rotateRightIndex1, _numUint32List, _rotateRightIndex2, outputUint32List, offsetRightOutput); + } + + void _applyChoiceBits(Uint32List xInputUint32List, int xOffsetInput, Uint32List yInputUint32List, int yOffsetInput, Uint32List zInputUint32List, + int zOffsetInput, Uint32List outputUint32List, int offsetRightOutput) { + outputUint32List[0 + offsetRightOutput] = + (xInputUint32List[0 + xOffsetInput] & (yInputUint32List[0 + yOffsetInput] ^ zInputUint32List[0 + zOffsetInput])) ^ + zInputUint32List[0 + zOffsetInput]; + + outputUint32List[1 + offsetRightOutput] = + (xInputUint32List[1 + xOffsetInput] & (yInputUint32List[1 + yOffsetInput] ^ zInputUint32List[1 + zOffsetInput])) ^ + zInputUint32List[1 + zOffsetInput]; + } + + void _applyMajorityBits(Uint32List xInputUint32List, int xOffsetInput, Uint32List yInputUint32List, int yOffsetInput, Uint32List zInputUint32List, + int zOffsetInput, Uint32List outputUint32List, int offsetRightOutput) { + outputUint32List[0 + offsetRightOutput] = + (xInputUint32List[0 + xOffsetInput] & (yInputUint32List[0 + yOffsetInput] | zInputUint32List[0 + zOffsetInput])) | + (yInputUint32List[0 + yOffsetInput] & zInputUint32List[0 + zOffsetInput]); + + outputUint32List[1 + offsetRightOutput] = + (xInputUint32List[1 + xOffsetInput] & (yInputUint32List[1 + yOffsetInput] | zInputUint32List[1 + zOffsetInput])) | + (yInputUint32List[1 + yOffsetInput] & zInputUint32List[1 + zOffsetInput]); + } + + void _shiftRight(int bits, Uint32List inputUint32List, int offsetInput, Uint32List outputUint32List, int offsetRightOutput) { + outputUint32List[0 + offsetRightOutput] = ((bits < 32) && (bits >= 0)) ? (inputUint32List[0 + offsetInput] >> bits) : 0; + + outputUint32List[1 + offsetRightOutput] = (bits > 32) + ? (inputUint32List[0 + offsetInput] >> (bits - 32)) + : (bits == 32) + ? inputUint32List[0 + offsetInput] + : (bits >= 0) + ? ((inputUint32List[0 + offsetInput]) << (32 - bits)) | (inputUint32List[1 + offsetInput] >> bits) + : 0; + } + + void _shiftLeft(int bits, Uint32List inputUint32List, int offsetInput, Uint32List outputUint32List, int offsetRightOutput) { + outputUint32List[0 + offsetRightOutput] = (bits > 32) + ? (inputUint32List[1 + offsetInput] << (bits - 32)) + : (bits == 32) + ? inputUint32List[1 + offsetInput] + : (bits >= 0) + ? ((inputUint32List[0 + offsetInput]) << bits) | (inputUint32List[1 + offsetInput] >> (32 - bits)) + : 0; + + outputUint32List[1 + offsetRightOutput] = ((bits < 32) && (bits >= 0)) ? (inputUint32List[1 + offsetInput] << bits) : 0; + } + + void _applyOr(Uint32List inputOneUint32List, int offsetOneInput, Uint32List inputTwoUint32List, int offsetTwoInput, Uint32List outputUint32List, + int offsetRightOutput) { + outputUint32List[0 + offsetRightOutput] = inputOneUint32List[0 + offsetOneInput] | inputTwoUint32List[0 + offsetTwoInput]; + + outputUint32List[1 + offsetRightOutput] = inputOneUint32List[1 + offsetOneInput] | inputTwoUint32List[1 + offsetTwoInput]; + } + + void _applyXor(Uint32List inputOneUint32List, int offsetOneInput, Uint32List inputTwoUint32List, int offsetTwoInput, Uint32List outputUint32List, + int offsetRightOutput) { + outputUint32List[0 + offsetRightOutput] = inputOneUint32List[0 + offsetOneInput] ^ inputTwoUint32List[0 + offsetTwoInput]; + + outputUint32List[1 + offsetRightOutput] = inputOneUint32List[1 + offsetOneInput] ^ inputTwoUint32List[1 + offsetTwoInput]; + } + + void _applyAdd(Uint32List inputOneUint32List, int offsetOneInput, Uint32List inputTwoUint32List, int offsetTwoInput, Uint32List outputUint32List, + int offsetRightOutput) { + outputUint32List[1 + offsetRightOutput] = inputOneUint32List[1 + offsetOneInput] + inputTwoUint32List[1 + offsetTwoInput]; + + outputUint32List[0 + offsetRightOutput] = inputOneUint32List[0 + offsetOneInput] + + inputTwoUint32List[0 + offsetTwoInput] + + (outputUint32List[1 + offsetRightOutput] < inputOneUint32List[1 + offsetOneInput] ? 1 : 0); + } + + void _applyAddWithCarry(Uint32List inputOneUint32List, int offsetOneInput, Uint32List inputTwoUint32List, int offsetTwoInput) { + int addTmp = inputOneUint32List[1 + offsetOneInput]; + + inputOneUint32List[1 + offsetOneInput] += inputTwoUint32List[1 + offsetTwoInput]; + inputOneUint32List[0 + offsetOneInput] += inputTwoUint32List[0 + offsetTwoInput] + (inputOneUint32List[1 + offsetOneInput] < addTmp ? 1 : 0); + } +} diff --git a/lib/src/hash/sha/sha512/sha512.dart b/lib/src/hash/sha/sha512/sha512.dart new file mode 100644 index 00000000..2f5f3cf2 --- /dev/null +++ b/lib/src/hash/sha/sha512/sha512.dart @@ -0,0 +1,49 @@ +//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:cryptography_utils/src/hash/sha/hash/a_hash.dart'; +import 'package:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:cryptography_utils/src/hash/sha/sha512/sha_512_sink.dart'; + +///[Sha512] configure the hash computation, defining a block size of 32 words (128 bytes), and provides a method for starting the chunked conversion process with a custom Sha512Sink. +class Sha512 extends AHash { + static const int _bytesPerWord = 4; + static final Sha512 _sha512 = Sha512._(); + + factory Sha512() => _sha512; + + Sha512._(); + + @override + int get blockSize => 32 * _bytesPerWord; + + @override + ByteConversionSink startChunkedConversion(Sink sink) => ByteConversionSink.from(Sha512Sink(sink)); +} diff --git a/lib/src/hash/sha/sha512/sha_512_sink.dart b/lib/src/hash/sha/sha512/sha_512_sink.dart new file mode 100644 index 00000000..c13def49 --- /dev/null +++ b/lib/src/hash/sha/sha512/sha_512_sink.dart @@ -0,0 +1,58 @@ +//This class was primarily influenced by: +// crypto - 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:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:cryptography_utils/src/hash/sha/sha512/a_sha_64bit_sink.dart'; + +///[Sha512Sink] initializes the digest state with predefined constants and specifies that the digest length is 16 bytes. +class Sha512Sink extends ASha64BitSink { + Sha512Sink(Sink sink) + : super( + sink, + Uint32List.fromList([ + 0x6a09e667, + 0xf3bcc908, + 0xbb67ae85, + 0x84caa73b, + 0x3c6ef372, + 0xfe94f82b, + 0xa54ff53a, + 0x5f1d36f1, + 0x510e527f, + 0xade682d1, + 0x9b05688c, + 0x2b3e6c1f, + 0x1f83d9ab, + 0xfb41bd6b, + 0x5be0cd19, + 0x137e2179, + ]), + ); +} diff --git a/lib/src/signer/cosmos/cosmos_signer.dart b/lib/src/signer/cosmos/cosmos_signer.dart index 4dc4d758..55783893 100644 --- a/lib/src/signer/cosmos/cosmos_signer.dart +++ b/lib/src/signer/cosmos/cosmos_signer.dart @@ -21,7 +21,6 @@ import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; /// Provides functionality for creating Cosmos-compatible digital signatures using an ECDSA private key. @@ -34,11 +33,11 @@ class CosmosSigner { /// Signs given [CosmosSignDoc] using SIGN_MODE_DIRECT and returns the signature as an [CosmosSignature]. CosmosSignature signDirect(CosmosSignDoc signDoc) { - ECDSASigner ecdsaSigner = ECDSASigner(hashFunction: sha256, ecPrivateKey: _ecPrivateKey); + ECDSASigner ecdsaSigner = ECDSASigner(hashFunction: Sha256(), ecPrivateKey: _ecPrivateKey); CosmosVerifier cosmosVerifier = CosmosVerifier(_ecPrivateKey.ecPublicKey); Uint8List signBytes = signDoc.getDirectSignBytes(); - Uint8List hashedSignBytes = Uint8List.fromList(sha256.convert(signBytes).bytes); + Uint8List hashedSignBytes = Sha256().convert(signBytes).byteList; ECSignature ecSignature = ecdsaSigner.sign(hashedSignBytes); diff --git a/lib/src/signer/cosmos/cosmos_verifier.dart b/lib/src/signer/cosmos/cosmos_verifier.dart index 49da1bb3..17eeaaa2 100644 --- a/lib/src/signer/cosmos/cosmos_verifier.dart +++ b/lib/src/signer/cosmos/cosmos_verifier.dart @@ -1,6 +1,5 @@ import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:cryptography_utils/src/cdsa/ecdsa/signer/ecdsa_verifier.dart'; @@ -17,11 +16,11 @@ class CosmosVerifier { /// Verifies a signature against a provided digest and the public key stored in this verifier. bool isSignatureValid(CosmosSignDoc signDoc, CosmosSignature cosmosSignature) { - ECDSAVerifier ecdsaVerifier = ECDSAVerifier(hashFunction: sha256, ecPublicKey: _ecPublicKey); + ECDSAVerifier ecdsaVerifier = ECDSAVerifier(hashFunction: Sha256(), ecPublicKey: _ecPublicKey); ECSignature ecSignature = ECSignature.fromBytes(cosmosSignature.bytes, ecCurve: _ecPublicKey.G.curve); Uint8List signBytes = signDoc.getDirectSignBytes(); - Uint8List hashedSignBytes = Uint8List.fromList(sha256.convert(signBytes).bytes); + Uint8List hashedSignBytes = Sha256().convert(signBytes).byteList; return ecdsaVerifier.isSignatureValid(hashedSignBytes, ecSignature); } diff --git a/lib/src/signer/ethereum/ethereum_signer.dart b/lib/src/signer/ethereum/ethereum_signer.dart index dc5d479e..c639d411 100644 --- a/lib/src/signer/ethereum/ethereum_signer.dart +++ b/lib/src/signer/ethereum/ethereum_signer.dart @@ -22,7 +22,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:cryptography_utils/src/hash/keccak/keccak_bit_length.dart'; @@ -52,7 +51,7 @@ class EthereumSigner { /// Signs a digest and returns the signature as an [EthereumSignature]. This method can optionally hash the /// digest before signing, which is typically required when the input is not already a hash. EthereumSignature sign(Uint8List digest, {bool hashMessage = true}) { - ECDSASigner ecdsaSigner = ECDSASigner(hashFunction: sha256, ecPrivateKey: _ecPrivateKey); + ECDSASigner ecdsaSigner = ECDSASigner(hashFunction: Sha256(), ecPrivateKey: _ecPrivateKey); EthereumVerifier ethereumVerifier = EthereumVerifier(_ecPrivateKey.ecPublicKey); Uint8List hash = hashMessage ? Keccak(KeccakBitLength.keccak256).process(digest) : digest; diff --git a/lib/src/signer/ethereum/ethereum_verifier.dart b/lib/src/signer/ethereum/ethereum_verifier.dart index a8de8a62..a5c910eb 100644 --- a/lib/src/signer/ethereum/ethereum_verifier.dart +++ b/lib/src/signer/ethereum/ethereum_verifier.dart @@ -20,7 +20,6 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:cryptography_utils/src/cdsa/ecdsa/signer/ecdsa_verifier.dart'; import 'package:cryptography_utils/src/hash/keccak/keccak_bit_length.dart'; @@ -43,7 +42,7 @@ class EthereumVerifier { Uint8List signatureBytes = ethereumSignature.bytes.sublist(0, EthereumSignature.ethSignatureLength); Uint8List hashDigest = hashMessage ? Keccak(KeccakBitLength.keccak256).process(digest) : digest; - ECDSAVerifier ecdsaVerifier = ECDSAVerifier(hashFunction: sha256, ecPublicKey: _ecPublicKey); + ECDSAVerifier ecdsaVerifier = ECDSAVerifier(hashFunction: Sha256(), ecPublicKey: _ecPublicKey); ECSignature ecSignature = ECSignature.fromBytes(signatureBytes, ecCurve: _ecPublicKey.G.curve); return ecdsaVerifier.isSignatureValid(hashDigest, ecSignature); } diff --git a/lib/src/transactions/cosmos/cosmos_tx.dart b/lib/src/transactions/cosmos/cosmos_tx.dart index ab3dda14..297893c2 100644 --- a/lib/src/transactions/cosmos/cosmos_tx.dart +++ b/lib/src/transactions/cosmos/cosmos_tx.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import 'package:codec_utils/codec_utils.dart'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; /// [CosmosTx] is the standard type used for broadcasting transactions. @@ -53,7 +52,7 @@ class CosmosTx extends AProtobufObject { /// Returns the transaction hash. String getTransactionHash() { - return HexCodec.encode(sha256.convert(toProtoBytes()).bytes, includePrefixBool: true); + return HexCodec.encode(Sha256().convert(toProtoBytes()).byteList, includePrefixBool: true); } @override diff --git a/pubspec.yaml b/pubspec.yaml index 843a3211..91c7be80 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: cryptography_utils description: "Dart package containing utility methods for common cryptographic and blockchain-specific operations" publish_to: none -version: 0.0.20 +version: 0.0.21 environment: sdk: ">=3.2.6" @@ -11,10 +11,6 @@ dependencies: # https://pub.dev/packages/equatable equatable: 2.0.5 - # Implementations of SHA, MD5, and HMAC cryptographic functions. - # https://pub.dev/packages/crypto - crypto: 3.0.3 - # A Dart library implementing cryptographic algorithms and primitives, modeled on the BouncyCastle library. # https://pub.dev/packages/pointycastle pointycastle: 3.9.1 @@ -27,6 +23,10 @@ dependencies: # https://pub.dev/packages/decimal decimal: 2.3.3 + # Utility functions and classes related to the dart:typed_data library. + # https://pub.dev/packages/typed_data + typed_data: ^1.3.0 + # Dart package containing utility methods for data encoding and decoding. codec_utils: git: diff --git a/test/cdsa/ecdsa/signer/ecdsa_signer_test.dart b/test/cdsa/ecdsa/signer/ecdsa_signer_test.dart index ad163107..d5867ffe 100644 --- a/test/cdsa/ecdsa/signer/ecdsa_signer_test.dart +++ b/test/cdsa/ecdsa/signer/ecdsa_signer_test.dart @@ -1,6 +1,5 @@ import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:test/test.dart'; @@ -16,7 +15,7 @@ void main() { // Act ECSignature actualECSignature = ECDSASigner( - hashFunction: sha256, + hashFunction: Sha256(), ecPrivateKey: actualECPrivateKey, ).sign(actualMessage); diff --git a/test/cdsa/ecdsa/signer/ecdsa_verifier_test.dart b/test/cdsa/ecdsa/signer/ecdsa_verifier_test.dart index 0f616282..9c42039d 100644 --- a/test/cdsa/ecdsa/signer/ecdsa_verifier_test.dart +++ b/test/cdsa/ecdsa/signer/ecdsa_verifier_test.dart @@ -1,6 +1,5 @@ import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:cryptography_utils/src/cdsa/ecdsa/signer/ecdsa_verifier.dart'; import 'package:test/test.dart'; @@ -28,7 +27,7 @@ void main() { // Act bool actualSignatureValidBool = ECDSAVerifier( - hashFunction: sha256, + hashFunction: Sha256(), ecPublicKey: actualECPublicKey, ).isSignatureValid(actualMessage, actualECSignature); @@ -57,7 +56,7 @@ void main() { // Act bool actualSignatureValidBool = ECDSAVerifier( - hashFunction: sha256, + hashFunction: Sha256(), ecPublicKey: actualECPublicKey, ).isSignatureValid(actualMessage, actualECSignature); diff --git a/test/cdsa/ecdsa/signer/rfc6979_test.dart b/test/cdsa/ecdsa/signer/rfc6979_test.dart index 8b43d56b..6c38f102 100644 --- a/test/cdsa/ecdsa/signer/rfc6979_test.dart +++ b/test/cdsa/ecdsa/signer/rfc6979_test.dart @@ -1,4 +1,3 @@ -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:test/test.dart'; @@ -10,7 +9,7 @@ void main() { m: 'Hello Word'.codeUnits, n: BigInt.parse('115792089237316195423570985008687907852837564279074904382605163141518161494337'), d: BigInt.parse('15864759622800253937020257025334897817812874204769186060960403729801414344643'), - hashFunction: sha256, + hashFunction: Sha256(), ); test('Should [return 1st k] for given message', () { diff --git a/test/hash/hmac_test.dart b/test/hash/hmac_test.dart index 5e54d0c1..0354da42 100644 --- a/test/hash/hmac_test.dart +++ b/test/hash/hmac_test.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:crypto/crypto.dart'; import 'package:cryptography_utils/cryptography_utils.dart'; import 'package:test/test.dart'; @@ -13,7 +12,7 @@ void main() { Uint8List actualHMACKey = base64Decode('Qml0Y29pbiBzZWVk'); // Act - Uint8List actualHMACResult = HMAC(hash: sha256, key: actualHMACKey).process(actualDataToHash); + Uint8List actualHMACResult = HMAC(hash: Sha256(), key: actualHMACKey).process(actualDataToHash); // Assert Uint8List expectedHMACResult = base64Decode('v2mD8MCO7DhGtDEDnyf1bextorPfq2WWl4tMV9uGqEU='); @@ -27,7 +26,7 @@ void main() { Uint8List actualHMACKey = base64Decode('BucEJucuy4B7Xo9OvwvDO0Z3HokXk3DjIQC51BiJGdU='); // Act - Uint8List actualHMACResult = HMAC(hash: sha256, key: actualHMACKey).process(actualDataToHash); + Uint8List actualHMACResult = HMAC(hash: Sha256(), key: actualHMACKey).process(actualDataToHash); // Assert Uint8List expectedHMACResult = base64Decode('LmFRERNSnjn0Z1qgGrzq/iv9zSZA4kzJ/3U7CeZ5+wE='); @@ -42,7 +41,7 @@ void main() { 'ZXhjbHVkZSB3ZXN0IG5vYmxlIHB1cml0eSBiZXlvbmQgaWxsbmVzcyBzb3VwIHJlc2VtYmxlIGF0b20gb2J2aW91cyBtZXRob2QgZmVzdGl2YWwgbmFtZSBpZGVudGlmeSBlbGVwaGFudCBzYXRpc2Z5IHdlZGRpbmcgaG9uZXkgY2VydGFpbiB0b2UgZXJvZGU='); // Act - Uint8List actualHMACResult = HMAC(hash: sha256, key: actualHMACKey).process(actualDataToHash); + Uint8List actualHMACResult = HMAC(hash: Sha256(), key: actualHMACKey).process(actualDataToHash); // Assert Uint8List expectedHMACResult = base64Decode('LmFRERNSnjn0Z1qgGrzq/iv9zSZA4kzJ/3U7CeZ5+wE='); @@ -58,7 +57,7 @@ void main() { Uint8List actualHMACKey = base64Decode('Qml0Y29pbiBzZWVk'); // Act - Uint8List actualHMACResult = HMAC(hash: sha512, key: actualHMACKey).process(actualDataToHash); + Uint8List actualHMACResult = HMAC(hash: Sha512(), key: actualHMACKey).process(actualDataToHash); // Assert Uint8List expectedHMACResult = base64Decode('tNAYHKi1A1v+dYxeuRBssVzo0wD/BccFLM8Ek2MUKfghQaRFgE4jqQND6+UBYd2CADPxl03Yu1wIorDCokjZlA=='); @@ -72,7 +71,7 @@ void main() { Uint8List actualHMACKey = base64Decode('YLx7mbfIG2DJhqGhJdI0TgJGcDPyouYG/jURk8BZNr5N706v+vpVknd9X4as7jVrOkgBHFOkLmWuGSdWXTkqLw=='); // Act - Uint8List actualHMACResult = HMAC(hash: sha512, key: actualHMACKey).process(actualDataToHash); + Uint8List actualHMACResult = HMAC(hash: Sha512(), key: actualHMACKey).process(actualDataToHash); // Assert Uint8List expectedHMACResult = base64Decode('rJmboYqazBqeHW/vUXGSTcj7hE67pNaxyJoqS7/VnR0QIxDuK7+t2YWo/7X1SoAmXXoGxYQobJj8RWLZpb/LYg=='); @@ -87,7 +86,7 @@ void main() { 'ZXhjbHVkZSB3ZXN0IG5vYmxlIHB1cml0eSBiZXlvbmQgaWxsbmVzcyBzb3VwIHJlc2VtYmxlIGF0b20gb2J2aW91cyBtZXRob2QgZmVzdGl2YWwgbmFtZSBpZGVudGlmeSBlbGVwaGFudCBzYXRpc2Z5IHdlZGRpbmcgaG9uZXkgY2VydGFpbiB0b2UgZXJvZGU='); // Act - Uint8List actualHMACResult = HMAC(hash: sha512, key: actualHMACKey).process(actualDataToHash); + Uint8List actualHMACResult = HMAC(hash: Sha512(), key: actualHMACKey).process(actualDataToHash); // Assert Uint8List expectedHMACResult = base64Decode('rJmboYqazBqeHW/vUXGSTcj7hE67pNaxyJoqS7/VnR0QIxDuK7+t2YWo/7X1SoAmXXoGxYQobJj8RWLZpb/LYg=='); @@ -100,10 +99,15 @@ void main() { test('Should [return HMAC digest] constructed using SHA256 algorithm (key length < digest size)', () { // Arrange Uint8List actualHMACKey = base64Decode('Qml0Y29pbiBzZWVk'); - List actualDataChunks = [base64Decode('RE9HRQ=='), base64Decode('V0lMTA=='), base64Decode('UFVNUA=='), base64Decode('U09PTg==')]; + List actualDataChunks = [ + base64Decode('RE9HRQ=='), + base64Decode('V0lMTA=='), + base64Decode('UFVNUA=='), + base64Decode('U09PTg==') + ]; // Act - Uint8List actualHMACResult = HMAC(hash: sha256, key: actualHMACKey).processChunks(actualDataChunks); + Uint8List actualHMACResult = HMAC(hash: Sha256(), key: actualHMACKey).processChunks(actualDataChunks); // Assert Uint8List expectedHMACResult = base64Decode('7uihKHgHk+0F3U6SLKY0bkDMOXM8sypysER56JBcH5E='); @@ -114,10 +118,15 @@ void main() { test('Should [return HMAC digest] constructed using SHA256 algorithm (key length == digest size)', () { // Arrange Uint8List actualHMACKey = base64Decode('BucEJucuy4B7Xo9OvwvDO0Z3HokXk3DjIQC51BiJGdU='); - List actualDataChunks = [base64Decode('RE9HRQ=='), base64Decode('V0lMTA=='), base64Decode('UFVNUA=='), base64Decode('U09PTg==')]; + List actualDataChunks = [ + base64Decode('RE9HRQ=='), + base64Decode('V0lMTA=='), + base64Decode('UFVNUA=='), + base64Decode('U09PTg==') + ]; // Act - Uint8List actualHMACResult = HMAC(hash: sha256, key: actualHMACKey).processChunks(actualDataChunks); + Uint8List actualHMACResult = HMAC(hash: Sha256(), key: actualHMACKey).processChunks(actualDataChunks); // Assert Uint8List expectedHMACResult = base64Decode('XvKsFtyfohW5qhl46GezrMqKHGLP1i9LZ8HBRcmW5cA='); @@ -129,10 +138,15 @@ void main() { // Arrange Uint8List actualHMACKey = base64Decode( 'ZXhjbHVkZSB3ZXN0IG5vYmxlIHB1cml0eSBiZXlvbmQgaWxsbmVzcyBzb3VwIHJlc2VtYmxlIGF0b20gb2J2aW91cyBtZXRob2QgZmVzdGl2YWwgbmFtZSBpZGVudGlmeSBlbGVwaGFudCBzYXRpc2Z5IHdlZGRpbmcgaG9uZXkgY2VydGFpbiB0b2UgZXJvZGU='); - List actualDataChunks = [base64Decode('RE9HRQ=='), base64Decode('V0lMTA=='), base64Decode('UFVNUA=='), base64Decode('U09PTg==')]; + List actualDataChunks = [ + base64Decode('RE9HRQ=='), + base64Decode('V0lMTA=='), + base64Decode('UFVNUA=='), + base64Decode('U09PTg==') + ]; // Act - Uint8List actualHMACResult = HMAC(hash: sha256, key: actualHMACKey).processChunks(actualDataChunks); + Uint8List actualHMACResult = HMAC(hash: Sha256(), key: actualHMACKey).processChunks(actualDataChunks); // Assert Uint8List expectedHMACResult = base64Decode('XvKsFtyfohW5qhl46GezrMqKHGLP1i9LZ8HBRcmW5cA='); @@ -145,10 +159,15 @@ void main() { test('Should [return HMAC digest] constructed using SHA512 algorithm (key length < digest size)', () { // Arrange Uint8List actualHMACKey = base64Decode('Qml0Y29pbiBzZWVk'); - List actualDataChunks = [base64Decode('RE9HRQ=='), base64Decode('V0lMTA=='), base64Decode('UFVNUA=='), base64Decode('U09PTg==')]; + List actualDataChunks = [ + base64Decode('RE9HRQ=='), + base64Decode('V0lMTA=='), + base64Decode('UFVNUA=='), + base64Decode('U09PTg==') + ]; // Act - Uint8List actualHMACResult = HMAC(hash: sha512, key: actualHMACKey).processChunks(actualDataChunks); + Uint8List actualHMACResult = HMAC(hash: Sha512(), key: actualHMACKey).processChunks(actualDataChunks); // Assert Uint8List expectedHMACResult = base64Decode('eEpNI3pCXfnKPZ8jEXJTmKl1hJDoiEHl66YCHD9q4f7KULIwqrfw3FMraqHhfObTv692Kmans1yNHwkVG//v/g=='); @@ -159,10 +178,15 @@ void main() { test('Should [return HMAC digest] constructed using SHA512 algorithm (key length == digest size)', () { // Arrange Uint8List actualHMACKey = base64Decode('YLx7mbfIG2DJhqGhJdI0TgJGcDPyouYG/jURk8BZNr5N706v+vpVknd9X4as7jVrOkgBHFOkLmWuGSdWXTkqLw=='); - List actualDataChunks = [base64Decode('RE9HRQ=='), base64Decode('V0lMTA=='), base64Decode('UFVNUA=='), base64Decode('U09PTg==')]; + List actualDataChunks = [ + base64Decode('RE9HRQ=='), + base64Decode('V0lMTA=='), + base64Decode('UFVNUA=='), + base64Decode('U09PTg==') + ]; // Act - Uint8List actualHMACResult = HMAC(hash: sha512, key: actualHMACKey).processChunks(actualDataChunks); + Uint8List actualHMACResult = HMAC(hash: Sha512(), key: actualHMACKey).processChunks(actualDataChunks); // Assert Uint8List expectedHMACResult = base64Decode('30yTALZp0ra21ujiDOOFLf56WpOXeBrWL+Bg/LQLw7y1mgncKn9rRwX3whAiQ8miZWNNnVmM9vjMFE1OFmU/EA=='); @@ -174,10 +198,15 @@ void main() { // Arrange Uint8List actualHMACKey = base64Decode( 'ZXhjbHVkZSB3ZXN0IG5vYmxlIHB1cml0eSBiZXlvbmQgaWxsbmVzcyBzb3VwIHJlc2VtYmxlIGF0b20gb2J2aW91cyBtZXRob2QgZmVzdGl2YWwgbmFtZSBpZGVudGlmeSBlbGVwaGFudCBzYXRpc2Z5IHdlZGRpbmcgaG9uZXkgY2VydGFpbiB0b2UgZXJvZGU='); - List actualDataChunks = [base64Decode('RE9HRQ=='), base64Decode('V0lMTA=='), base64Decode('UFVNUA=='), base64Decode('U09PTg==')]; + List actualDataChunks = [ + base64Decode('RE9HRQ=='), + base64Decode('V0lMTA=='), + base64Decode('UFVNUA=='), + base64Decode('U09PTg==') + ]; // Act - Uint8List actualHMACResult = HMAC(hash: sha512, key: actualHMACKey).processChunks(actualDataChunks); + Uint8List actualHMACResult = HMAC(hash: Sha512(), key: actualHMACKey).processChunks(actualDataChunks); // Assert Uint8List expectedHMACResult = base64Decode('30yTALZp0ra21ujiDOOFLf56WpOXeBrWL+Bg/LQLw7y1mgncKn9rRwX3whAiQ8miZWNNnVmM9vjMFE1OFmU/EA=='); @@ -189,7 +218,7 @@ void main() { group('Tests of HMAC.update() method and HMAC.digest getter', () { group('Tests for SHA256 algorithm (key length < digest size)', () { Uint8List actualHMACKey = base64Decode('Qml0Y29pbiBzZWVk'); - HMAC actualHMAC = HMAC(hash: sha256, key: actualHMACKey); + HMAC actualHMAC = HMAC(hash: Sha256(), key: actualHMACKey); test('Should [return HMAC digest] constructed using SHA256 algorithm (empty chunks)', () { // Act @@ -232,7 +261,7 @@ void main() { group('Tests for SHA256 algorithm (key length == digest size)', () { Uint8List actualHMACKey = base64Decode('BucEJucuy4B7Xo9OvwvDO0Z3HokXk3DjIQC51BiJGdU='); - HMAC actualHMAC = HMAC(hash: sha256, key: actualHMACKey); + HMAC actualHMAC = HMAC(hash: Sha256(), key: actualHMACKey); test('Should [return HMAC digest] constructed using SHA256 algorithm (empty chunks)', () { // Act @@ -276,7 +305,7 @@ void main() { group('Tests for SHA256 algorithm (key length > digest size)', () { Uint8List actualHMACKey = base64Decode( 'ZXhjbHVkZSB3ZXN0IG5vYmxlIHB1cml0eSBiZXlvbmQgaWxsbmVzcyBzb3VwIHJlc2VtYmxlIGF0b20gb2J2aW91cyBtZXRob2QgZmVzdGl2YWwgbmFtZSBpZGVudGlmeSBlbGVwaGFudCBzYXRpc2Z5IHdlZGRpbmcgaG9uZXkgY2VydGFpbiB0b2UgZXJvZGU='); - HMAC actualHMAC = HMAC(hash: sha256, key: actualHMACKey); + HMAC actualHMAC = HMAC(hash: Sha256(), key: actualHMACKey); test('Should [return HMAC digest] constructed using SHA256 algorithm (empty chunks)', () { // Act @@ -321,7 +350,7 @@ void main() { group('Tests for SHA512 algorithm (key length < digest size)', () { Uint8List actualHMACKey = base64Decode('Qml0Y29pbiBzZWVk'); - HMAC actualHMAC = HMAC(hash: sha512, key: actualHMACKey); + HMAC actualHMAC = HMAC(hash: Sha512(), key: actualHMACKey); test('Should [return HMAC digest] constructed using SHA256 algorithm (empty chunks)', () { // Act @@ -364,7 +393,7 @@ void main() { group('Tests for SHA512 algorithm (key length == digest size)', () { Uint8List actualHMACKey = base64Decode('YLx7mbfIG2DJhqGhJdI0TgJGcDPyouYG/jURk8BZNr5N706v+vpVknd9X4as7jVrOkgBHFOkLmWuGSdWXTkqLw=='); - HMAC actualHMAC = HMAC(hash: sha512, key: actualHMACKey); + HMAC actualHMAC = HMAC(hash: Sha512(), key: actualHMACKey); test('Should [return HMAC digest] constructed using SHA256 algorithm (empty chunks)', () { // Act @@ -408,7 +437,7 @@ void main() { group('Tests for SHA512 algorithm (key length > digest size)', () { Uint8List actualHMACKey = base64Decode( 'ZXhjbHVkZSB3ZXN0IG5vYmxlIHB1cml0eSBiZXlvbmQgaWxsbmVzcyBzb3VwIHJlc2VtYmxlIGF0b20gb2J2aW91cyBtZXRob2QgZmVzdGl2YWwgbmFtZSBpZGVudGlmeSBlbGVwaGFudCBzYXRpc2Z5IHdlZGRpbmcgaG9uZXkgY2VydGFpbiB0b2UgZXJvZGU='); - HMAC actualHMAC = HMAC(hash: sha512, key: actualHMACKey); + HMAC actualHMAC = HMAC(hash: Sha512(), key: actualHMACKey); test('Should [return HMAC digest] constructed using SHA256 algorithm (empty chunks)', () { // Act diff --git a/test/hash/sha/hash/digest_sink_test.dart b/test/hash/sha/hash/digest_sink_test.dart new file mode 100644 index 00000000..3f407adf --- /dev/null +++ b/test/hash/sha/hash/digest_sink_test.dart @@ -0,0 +1,28 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:cryptography_utils/src/hash/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/hash/sha/sha256/sha256_sink_test.dart b/test/hash/sha/sha256/sha256_sink_test.dart new file mode 100644 index 00000000..6e2df112 --- /dev/null +++ b/test/hash/sha/sha256/sha256_sink_test.dart @@ -0,0 +1,30 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/hash/sha/hash/digest_sink.dart'; +import 'package:cryptography_utils/src/hash/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/hash/sha/sha256/sha256_test.dart b/test/hash/sha/sha256/sha256_test.dart new file mode 100644 index 00000000..1ed0a6bb --- /dev/null +++ b/test/hash/sha/sha256/sha256_test.dart @@ -0,0 +1,25 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:cryptography_utils/src/hash/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); + }); + }); +} diff --git a/test/hash/sha/sha512/sha512_sink_test.dart b/test/hash/sha/sha512/sha512_sink_test.dart new file mode 100644 index 00000000..524b5983 --- /dev/null +++ b/test/hash/sha/sha512/sha512_sink_test.dart @@ -0,0 +1,33 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/hash/sha/hash/digest_sink.dart'; +import 'package:cryptography_utils/src/hash/sha/sha512/sha_512_sink.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +// ignore_for_file: cascade_invocations +void main() { + group('Tests of Sha512Sink.updateHash()', () { + test('Should [return hash] constructed from given data', () { + // Arrange + String actualDataToHash = '123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~'; + Uint32List actualUint32List = Uint32List.fromList(actualDataToHash.codeUnits); + DigestSink actualDigestSink = DigestSink(); + Sha512Sink actualSha512Sink = Sha512Sink(actualDigestSink); + + // Act + actualSha512Sink.updateHash(actualUint32List); + Uint32List actualDigestUint32List = actualSha512Sink.digestUint32List; + + // Assert + Uint32List expectedDigestUint32List = Uint32List.fromList([ + 3492118963, 1627087956, 3150871713, 3630638959, // + 3359712394, 1593454716, 1080073699, 2608846795, + 1817179099, 2060845797, 3341868678, 1197038725, + 1082521416, 2706134826, 1898010856, 931400256 + ]); + + expect(actualDigestUint32List, expectedDigestUint32List); + }); + }); +} diff --git a/test/hash/sha/sha512/sha512_test.dart b/test/hash/sha/sha512/sha512_test.dart new file mode 100644 index 00000000..fa349abf --- /dev/null +++ b/test/hash/sha/sha512/sha512_test.dart @@ -0,0 +1,26 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/hash/sha/hash/digest.dart'; +import 'package:cryptography_utils/src/hash/sha/sha512/sha512.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +/// For calculating [expectedHashString] an online calculator was used: https://emn178.github.io/online-tools/sha512.html +void main() { + group('Tests of Sha512.convert()', () { + test('Should [return hash] constructed from given data', () { + // Arrange + Uint8List actualDataToHash = utf8.encode('123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~'); + + // Act + Digest actualDigest = Sha512().convert(actualDataToHash); + String actualDigestString = base64Encode(actualDigest.byteList); + + // Assert + String expectedDigestString = '+kbk+wTR/3g89X/h3p3p4V9g62usHUWM+x/aqFGvJtGUqASue9AFWm5Uc08g6E76HxO3DskHdInuJlzRqVnMSQ=='; + + expect(actualDigestString, expectedDigestString); + }); + }); +}