diff --git a/.github/workflows/version_and_tests.yaml b/.github/workflows/version_and_tests.yaml index d33f4219..c31f6d69 100644 --- a/.github/workflows/version_and_tests.yaml +++ b/.github/workflows/version_and_tests.yaml @@ -8,7 +8,7 @@ on: jobs: app-version-check: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: # https://github.com/marketplace/actions/checkout - uses: actions/checkout@main @@ -42,7 +42,7 @@ jobs: exit 1 run-unit-tests: needs: app-version-check - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: # https://github.com/marketplace/actions/checkout - uses: actions/checkout@main diff --git a/lib/cryptography_utils.dart b/lib/cryptography_utils.dart index 07ce3447..5d611019 100644 --- a/lib/cryptography_utils.dart +++ b/lib/cryptography_utils.dart @@ -2,6 +2,7 @@ library cryptography_utils; export 'src/bip/bip.dart'; export 'src/cdsa/cdsa.dart'; +export 'src/encryption/aes/aes256_randomized.dart'; export 'src/hash/hmac.dart'; export 'src/hash/keccak/keccak.dart'; export 'src/hash/pbkdf2.dart'; diff --git a/lib/src/encryption/aes/aes256_randomized.dart b/lib/src/encryption/aes/aes256_randomized.dart new file mode 100644 index 00000000..a4931e6c --- /dev/null +++ b/lib/src/encryption/aes/aes256_randomized.dart @@ -0,0 +1,93 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/cryptography_utils.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_ctr.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_iv.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_key.dart'; +import 'package:cryptography_utils/src/encryption/aes/invalid_password_exception.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_text.dart'; +import 'package:cryptography_utils/src/utils/secure_random.dart'; + +/// The `AES256Randomized` class provides secure encryption and decryption of text (strings) +/// using the AES-256 algorithm in CTR mode. The class enables confidential storage of textual data +/// and ensuring that access is only possible with the correct password. +/// +/// Moreover, the use of a random salt (of length 16) and the SHA-256 hash function enhances security and makes attacks significantly more difficult. +/// +class AES256Randomized { + /// Encrypts the provided plaintext using the given password. + /// A random 16-byte salt is generated, and the password is hashed using SHA-256 to produce the encryption key. + /// The salt and password hash are combined and hashed again to derive a secure value, + /// from which the initialization vector (IV), and checksum are extracted. + /// The plaintext is then encrypted using AES-256 in CTR mode and returned as a Base64-encoded string, + /// with the salt prepended and the checksum appended. + static String encrypt(String password, String decryptedString) { + Uint8List decryptedUint8List = utf8.encode(decryptedString); + Uint8List randomUint8List = SecureRandom.getBytes(length: 16, max: 255); + + Uint8List hashedPasswordUint8List = Sha256().convert(utf8.encode(password)).byteList; + Uint8List securePasswordUint8List = Sha256().convert(randomUint8List + hashedPasswordUint8List).byteList; + + AESKey aesKey = AESKey(hashedPasswordUint8List); + + Uint8List initializationVectorUint8List = Uint8List.fromList(securePasswordUint8List.getRange(0, 16).toList()); + AESIV aesIV = AESIV(initializationVectorUint8List); + + Uint8List encryptedUint8List = AESctr.encrypt(decryptedUint8List, aesKey, aesIV).uint8List; + + List checksumUint8List = securePasswordUint8List.getRange(securePasswordUint8List.length - 4, securePasswordUint8List.length).toList(); + List encryptedStringUint8List = randomUint8List + encryptedUint8List + checksumUint8List; + String encryptedString = base64Encode(encryptedStringUint8List); + + return encryptedString; + } + + /// Decrypts the provided encrypted string using the specified password. + /// The password is hashed using SHA-256 to produce the encryption key. + /// The salt and checksum are extracted from the encrypted input. + /// Then, the salt and password hashes are combined and hashed again to derive a secure value, + /// from which the initialization vector (IV) is reconstructed and the checksum is validated. + /// The encrypted data is then decrypted using AES-256 in CTR mode, + /// and the resulting plaintext is returned as a string. + /// + /// Throws [InvalidPasswordException] if decryption fails, for example due to an incorrect password. + static String decrypt(String password, String encryptedString) { + try { + Uint8List hashedPasswordUint8List = Sha256().convert(utf8.encode(password)).byteList; + + Uint8List encryptedStringUint8List = base64Decode(encryptedString); + Uint8List randomUint8List = Uint8List.fromList(encryptedStringUint8List.getRange(0, 16).toList()); + CipherText cipherText = CipherText(Uint8List.fromList(encryptedStringUint8List.getRange(16, encryptedStringUint8List.length - 4).toList())); + Uint8List securePasswordUint8List = Sha256().convert(randomUint8List + hashedPasswordUint8List).byteList; + + AESKey aesKey = AESKey(hashedPasswordUint8List); + + Uint8List initializationVectorUint8List = Uint8List.fromList(securePasswordUint8List.getRange(0, 16).toList()); + AESIV aesIV = AESIV(initializationVectorUint8List); + Uint8List decryptedUint8List = AESctr.decrypt(cipherText, aesKey, aesIV); + String decryptedString = utf8.decode(decryptedUint8List); + return decryptedString; + } catch (e) { + throw InvalidPasswordException('Decryption failed: ${e.toString()}'); + } + } + + /// Validates whether the provided [password] matches the checksum of the [encryptedString]. + /// This function checks if the password-derived checksum matches the stored one in the encrypted payload, + /// without actually decrypting the content. + /// Returns `true` if the password is valid; otherwise `false`. + static bool isPasswordValid(String password, String encryptedString) { + Uint8List hashedPasswordUint8List = Sha256().convert(utf8.encode(password)).byteList; + + Uint8List encryptedStringUint8List = base64Decode(encryptedString); + Uint8List randomUint8List = Uint8List.fromList(encryptedStringUint8List.getRange(0, 16).toList()); + List expectedChecksumUint8List = + encryptedStringUint8List.getRange(encryptedStringUint8List.length - 4, encryptedStringUint8List.length).toList(); + + Uint8List securePasswordUint8List = Sha256().convert(randomUint8List + hashedPasswordUint8List).byteList; + List actualChecksumUint8List = securePasswordUint8List.getRange(securePasswordUint8List.length - 4, securePasswordUint8List.length).toList(); + + return actualChecksumUint8List.toString() == expectedChecksumUint8List.toString(); + } +} diff --git a/lib/src/encryption/aes/aes_constants.dart b/lib/src/encryption/aes/aes_constants.dart new file mode 100644 index 00000000..1a9aec9b --- /dev/null +++ b/lib/src/encryption/aes/aes_constants.dart @@ -0,0 +1,84 @@ +class AESConstants { + static const List rConList = [ + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, // + 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, + 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 + ]; + + static const List sList = [ + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, // + 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, + 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, + 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, + 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, + 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, + 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, + 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, + 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, + 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, + 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, + 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, + 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, + 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, + 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, + 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, + 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, + 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, + 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 + ]; + + static const List t0List = [ + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, // + 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, + 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, + 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, + 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, + 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, + 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, + 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, + 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, + 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, + 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, + 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, + 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, + 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, + 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, + 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, + 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, + 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, + 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, + 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, + 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, + 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, + 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, + 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, + 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, + 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, + 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, + 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, + 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, + 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, + 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, + 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, + 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, + 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, + 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, + 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, + 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, + 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, + 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, + 0x3a16162c + ]; +} diff --git a/lib/src/encryption/aes/aes_ctr.dart b/lib/src/encryption/aes/aes_ctr.dart new file mode 100644 index 00000000..5647f897 --- /dev/null +++ b/lib/src/encryption/aes/aes_ctr.dart @@ -0,0 +1,113 @@ +//Copyright (c) 2018, Leo Cavalcante +// All rights reserved. +// +// 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 the copyright holder 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 HOLDER 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/encryption/aes/aes_engine.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_iv.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_key.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/ctr_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/padded_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/padding/pkcs7_padding.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_key_with_iv.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_mode.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_text.dart'; + +/// AES provides encryption and decryption using the AES algorithm in stream cipher mode (SIC/CTR). +/// This implementation wraps the AES engine with a counter-based stream cipher and +/// uses PKCS7 padding to support data of arbitrary length. +/// +/// ****************** implemented modes ****************** +/// +/// CTR (Counter mode) – transforms a block cipher into a stream cipher. +/// It generates a stream block by encrypting a nonce combined with a counter, +/// then XORs the stream with the plaintext. CTR mode allows parallel encryption, +/// random read/write access, and avoids block dependencies, making it suitable for +/// high-performance applications, secure disk encryption, and network protocols. +/// +/// ****************** NOT implemented modes ****************** +/// +/// - ECB (Electronic Codebook) – the simplest block cipher mode, where each plaintext block is encrypted independently. +/// It is generally **not recommended for practical use** due to security weaknesses, but can be useful for educational or testing purposes. +/// +/// - CBC (Cipher Block Chaining) - block cipher mode where each plaintext block is XORed with the previous ciphertext block +/// before encryption. Suitable for encrypting files, messages, or large datasets, +/// providing confidentiality by introducing dependency between blocks. +/// +/// - OFB (Output feedback)- operates as a stream cipher by generating keystream blocks independent of the plaintext, +/// which are then XORed with the plaintext blocks. Well-suited for network communications and streaming applications. +/// +/// - CFB (Cipher feedback ) - also a stream cipher mode that encrypts small increments of plaintext, +/// allowing for encryption of data in units smaller than the block size. An error in one ciphertext byte propagates +/// to several subsequent plaintext bytes during decryption. Generally less efficient compared to other modes. +/// +/// - XTS (XEX-based Tweaked Codebook mode with ciphertext Stealing) - block cipher mode for encrypting data on storage devices. +/// It allows encryption of data whose length is not a multiple of the block size **without padding** by using ciphertext stealing. +/// Provides high performance and protection against ciphertext manipulation, +/// commonly used for encrypting sectors on hard drives, SSDs, and USB drives. + +// TODO(Balldyna): To support multiple AES modes in the future, this class should be refactored. +// Consider the following steps: +// - introduce an enum (`AesMode`) to represent supported block cipher modes +// - add appropriate try-catch blocks for mode-specific behavior +// - rename this class to reflect its extended capability (e.g., 'AES'). + +class AESctr { + /// Encrypts the given [uint8List] using the specified [aesKey] and [aesIV]. + /// The encryption process uses a padded block cipher with PKCS7 padding + /// and AES in CTR mode to ensure compatibility with inputs of any size. + /// Returns an [CipherText] object containing the encrypted bytes. + static CipherText encrypt(Uint8List uint8List, AESKey aesKey, AESIV aesIV) { + PaddedBlockCipher paddedBlockCipher = PaddedBlockCipher( + blockCipher: CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: CipherKeyWithIV(aesKey, aesIV.uint8List), + ), + ), + cipherPadding: Pkcs7Padding(), + ); + return CipherText(paddedBlockCipher.process(uint8List)); + } + + /// Decrypts the given [cipherText] data using the specified [aesKey] and [aesIV]. + /// The decryption process matches the encryption configuration, + /// using PKCS7 padding removal and AES in CTR mode. + /// Returns the original plaintext as a [Uint8List]. + static Uint8List decrypt(CipherText cipherText, AESKey aesKey, AESIV aesIV) { + PaddedBlockCipher paddedBlockCipher = PaddedBlockCipher( + blockCipher: CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.decryption, + cipherKeyWithIV: CipherKeyWithIV(aesKey, aesIV.uint8List), + ), + ), + cipherPadding: Pkcs7Padding(), + ); + return paddedBlockCipher.process(cipherText.uint8List); + } +} diff --git a/lib/src/encryption/aes/aes_engine.dart b/lib/src/encryption/aes/aes_engine.dart new file mode 100644 index 00000000..39ad8c20 --- /dev/null +++ b/lib/src/encryption/aes/aes_engine.dart @@ -0,0 +1,340 @@ +// This class was primarily influenced by: +// "pointycastle" - Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/aes/aes_constants.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_key.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/a_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_key_with_iv.dart'; +import 'package:cryptography_utils/src/utils/int32_utils.dart'; + +/// AES block cipher engine for encryption and decryption. +/// This class supports AES key expansion and encryption/decryption routines using a 128/192/256-bit key. +class AESEngine extends ABlockCipher { + static const int _blockSize = 16; + + late int _rounds; + late List _sList; + late List> _workingKeyList; + + AESEngine({ + required super.cipherMode, + required CipherKeyWithIV super.cipherKeyWithIV, + }) : super(blockSize: _blockSize) { + _rounds = 0; + _sList = List.from(AESConstants.sList); + _workingKeyList = _generateWorkingKey(); + } + + /// Processes a full block directly and returns the resulting ciphertext/plaintext. + /// This method wraps [processBlock] and returns a new list containing the output bytes. + @override + Uint8List process(Uint8List uint8List) { + Uint8List outUint8List = Uint8List(blockSize); + int length = processBlock(uint8List, 0, outUint8List, 0); + return outUint8List.sublist(0, length); + } + + /// Encrypts or decrypts a single block of input data. + /// The operation depends on whether the cipher was initialized for encryption or decryption. + /// Returns the block size (always 16). + @override + int processBlock(Uint8List inputUint8List, int inputOffset, Uint8List outputUint8List, int outputOffset) { + _encryptBlock(inputUint8List, inputOffset, outputUint8List, outputOffset, _workingKeyList); + return _blockSize; + } + + /// Expands the AES key into a key schedule used during encryption or decryption. + /// Depending on the key length (128, 192, or 256 bits), this method: + /// - Calculates the number of rounds (10, 12, or 14), + /// - Generates round keys using the Rijndael key schedule algorithm, + /// - Organizes the keys into round-specific 4x4 matrices for easier block processing. + /// The generated key schedule is used for SubBytes, ShiftRows, MixColumns, and AddRoundKey steps. + List> _generateWorkingKey() { + Uint8List aesKeyUint8List = _aesKey!.uint8List; + _rounds = _aesKey!.columns + 6; + + List> wList = List>.generate(_rounds + 1, (int i) => List.filled(4, 0, growable: false)); + + switch (_aesKey!.columns) { + case 4: + int col0 = Int32Utils.unpack(aesKeyUint8List, 0, Endian.little); + wList[0][0] = col0; + int col1 = Int32Utils.unpack(aesKeyUint8List, 4, Endian.little); + wList[0][1] = col1; + int col2 = Int32Utils.unpack(aesKeyUint8List, 8, Endian.little); + wList[0][2] = col2; + int col3 = Int32Utils.unpack(aesKeyUint8List, 12, Endian.little); + wList[0][3] = col3; + + for (int i = 1; i <= 10; ++i) { + int colX = _subWord(Int32Utils.rotateRight(col3, 8)) ^ AESConstants.rConList[i - 1]; + col0 ^= colX; + wList[i][0] = col0; + col1 ^= col0; + wList[i][1] = col1; + col2 ^= col1; + wList[i][2] = col2; + col3 ^= col2; + wList[i][3] = col3; + } + break; + case 6: + int col0 = Int32Utils.unpack(aesKeyUint8List, 0, Endian.little); + wList[0][0] = col0; + int col1 = Int32Utils.unpack(aesKeyUint8List, 4, Endian.little); + wList[0][1] = col1; + int col2 = Int32Utils.unpack(aesKeyUint8List, 8, Endian.little); + wList[0][2] = col2; + int col3 = Int32Utils.unpack(aesKeyUint8List, 12, Endian.little); + wList[0][3] = col3; + + int col4 = Int32Utils.unpack(aesKeyUint8List, 16, Endian.little); + int col5 = Int32Utils.unpack(aesKeyUint8List, 20, Endian.little); + + int i = 1, rcon = 1, colx; + + for (;;) { + wList[i][0] = col4; + wList[i][1] = col5; + colx = _subWord(Int32Utils.rotateRight(col5, 8)) ^ rcon; + rcon <<= 1; + col0 ^= colx; + wList[i][2] = col0; + col1 ^= col0; + wList[i][3] = col1; + + col2 ^= col1; + wList[i + 1][0] = col2; + col3 ^= col2; + wList[i + 1][1] = col3; + col4 ^= col3; + wList[i + 1][2] = col4; + col5 ^= col4; + wList[i + 1][3] = col5; + + colx = _subWord(Int32Utils.rotateRight(col5, 8)) ^ rcon; + rcon <<= 1; + col0 ^= colx; + wList[i + 2][0] = col0; + col1 ^= col0; + wList[i + 2][1] = col1; + col2 ^= col1; + wList[i + 2][2] = col2; + col3 ^= col2; + wList[i + 2][3] = col3; + + if ((i + 3) >= 13) { + break; + } + col4 ^= col3; + col5 ^= col4; + } + break; + case 8: + { + int col0 = Int32Utils.unpack(aesKeyUint8List, 0, Endian.little); + wList[0][0] = col0; + int col1 = Int32Utils.unpack(aesKeyUint8List, 4, Endian.little); + wList[0][1] = col1; + int col2 = Int32Utils.unpack(aesKeyUint8List, 8, Endian.little); + wList[0][2] = col2; + int col3 = Int32Utils.unpack(aesKeyUint8List, 12, Endian.little); + wList[0][3] = col3; + + int col4 = Int32Utils.unpack(aesKeyUint8List, 16, Endian.little); + wList[1][0] = col4; + int col5 = Int32Utils.unpack(aesKeyUint8List, 20, Endian.little); + wList[1][1] = col5; + int col6 = Int32Utils.unpack(aesKeyUint8List, 24, Endian.little); + wList[1][2] = col6; + int col7 = Int32Utils.unpack(aesKeyUint8List, 28, Endian.little); + wList[1][3] = col7; + + int i = 2, rcon = 1, colx; + + for (;;) { + colx = _subWord(Int32Utils.rotateRight(col7, 8)) ^ rcon; + rcon <<= 1; + col0 ^= colx; + wList[i][0] = col0; + col1 ^= col0; + wList[i][1] = col1; + col2 ^= col1; + wList[i][2] = col2; + col3 ^= col2; + wList[i][3] = col3; + ++i; + + if (i >= 15) { + break; + } + + colx = _subWord(col3); + col4 ^= colx; + wList[i][0] = col4; + col5 ^= col4; + wList[i][1] = col5; + col6 ^= col5; + wList[i][2] = col6; + col7 ^= col6; + wList[i][3] = col7; + ++i; + } + break; + } + default: + {} + } + + return wList; + } + + AESKey? get _aesKey => (cipherKeyWithIV as CipherKeyWithIV).cipherKey; + + /// Applies S-box substitution to each byte of a 32-bit word during AES key expansion. + /// The `SubWord` operation is used in the key schedule during round key generation. + /// Each byte is independently substituted using the AES S-box. + /// Returns a 32-bit word after applying the S-box substitution to each byte. + int _subWord(int value) { + return AESConstants.sList[value & 255] & 255 | + ((AESConstants.sList[(value >> 8) & 255] & 255) << 8) | + ((AESConstants.sList[(value >> 16) & 255] & 255) << 16) | + AESConstants.sList[(value >> 24) & 255] << 24; + } + + /// Encrypts a single 16-byte block using AES and the previously generated key schedule. + /// Implements the standard AES round transformations: + /// - Initial AddRoundKey + /// - [Nr - 1] rounds of: SubBytes → ShiftRows → MixColumns → AddRoundKey + /// - Final round: SubBytes → ShiftRows → kwList (no MixColumns) + /// Parameters: + /// - [inputUint8List]: Input block of plaintext bytes. + /// - [inputOffset]: Offset into the input buffer. + /// - [outputUint8List]: Output buffer to write ciphertext into. + /// - [outputOffset]: Offset into the output buffer. + /// - [kwList]: Precomputed expanded key schedule. + void _encryptBlock(Uint8List inputUint8List, int inputOffset, Uint8List outputUint8List, int outputOffset, List> kwList) { + int c0 = Int32Utils.unpack(inputUint8List, inputOffset + 0, Endian.little); + int c1 = Int32Utils.unpack(inputUint8List, inputOffset + 4, Endian.little); + int c2 = Int32Utils.unpack(inputUint8List, inputOffset + 8, Endian.little); + int c3 = Int32Utils.unpack(inputUint8List, inputOffset + 12, Endian.little); + + int t0 = c0 ^ kwList[0][0]; + int t1 = c1 ^ kwList[0][1]; + int t2 = c2 ^ kwList[0][2]; + + int r = 1, r0, r1, r2, r3 = c3 ^ kwList[0][3]; + + while (r < _rounds - 1) { + r0 = AESConstants.t0List[t0 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(t1 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t2 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 24) & 255], 8) ^ + kwList[r][0]; + + r1 = AESConstants.t0List[t1 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(t2 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t0 >> 24) & 255], 8) ^ + kwList[r][1]; + + r2 = AESConstants.t0List[t2 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t0 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t1 >> 24) & 255], 8) ^ + kwList[r][2]; + + r3 = AESConstants.t0List[r3 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(t0 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t1 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t2 >> 24) & 255], 8) ^ + kwList[r++][3]; + t0 = AESConstants.t0List[r0 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(r1 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r2 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 24) & 255], 8) ^ + kwList[r][0]; + t1 = AESConstants.t0List[r1 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(r2 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r0 >> 24) & 255], 8) ^ + kwList[r][1]; + t2 = AESConstants.t0List[r2 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r0 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r1 >> 24) & 255], 8) ^ + kwList[r][2]; + r3 = AESConstants.t0List[r3 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(r0 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r1 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r2 >> 24) & 255], 8) ^ + kwList[r++][3]; + } + + r0 = AESConstants.t0List[t0 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(t1 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t2 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 24) & 255], 8) ^ + kwList[r][0]; + r1 = AESConstants.t0List[t1 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(t2 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t0 >> 24) & 255], 8) ^ + kwList[r][1]; + r2 = AESConstants.t0List[t2 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(r3 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t0 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t1 >> 24) & 255], 8) ^ + kwList[r][2]; + r3 = AESConstants.t0List[r3 & 255] ^ + Int32Utils.rotateRight(AESConstants.t0List[(t0 >> 8) & 255], 24) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t1 >> 16) & 255], 16) ^ + Int32Utils.rotateRight(AESConstants.t0List[(t2 >> 24) & 255], 8) ^ + kwList[r++][3]; + + c0 = (AESConstants.sList[r0 & 255] & 255) ^ + ((AESConstants.sList[(r1 >> 8) & 255] & 255) << 8) ^ + ((_sList[(r2 >> 16) & 255] & 255) << 16) ^ + (_sList[(r3 >> 24) & 255] << 24) ^ + kwList[r][0]; + c1 = (_sList[r1 & 255] & 255) ^ + ((AESConstants.sList[(r2 >> 8) & 255] & 255) << 8) ^ + ((AESConstants.sList[(r3 >> 16) & 255] & 255) << 16) ^ + (_sList[(r0 >> 24) & 255] << 24) ^ + kwList[r][1]; + c2 = (_sList[r2 & 255] & 255) ^ + ((AESConstants.sList[(r3 >> 8) & 255] & 255) << 8) ^ + ((AESConstants.sList[(r0 >> 16) & 255] & 255) << 16) ^ + (AESConstants.sList[(r1 >> 24) & 255] << 24) ^ + kwList[r][2]; + c3 = (_sList[r3 & 255] & 255) ^ + ((_sList[(r0 >> 8) & 255] & 255) << 8) ^ + ((_sList[(r1 >> 16) & 255] & 255) << 16) ^ + (AESConstants.sList[(r2 >> 24) & 255] << 24) ^ + kwList[r][3]; + + Int32Utils.pack(c0, outputUint8List, outputOffset, Endian.little); + Int32Utils.pack(c1, outputUint8List, outputOffset + 4, Endian.little); + Int32Utils.pack(c2, outputUint8List, outputOffset + 8, Endian.little); + Int32Utils.pack(c3, outputUint8List, outputOffset + 12, Endian.little); + } +} diff --git a/lib/src/encryption/aes/aes_iv.dart b/lib/src/encryption/aes/aes_iv.dart new file mode 100644 index 00000000..15973f03 --- /dev/null +++ b/lib/src/encryption/aes/aes_iv.dart @@ -0,0 +1,14 @@ +import 'dart:typed_data'; + +import 'package:equatable/equatable.dart'; + +/// Represents the initialization vector (IV) used in AES encryption. +/// Ensures input randomness and uniqueness for secure encryption. +class AESIV extends Equatable { + final Uint8List uint8List; + + const AESIV(this.uint8List); + + @override + List get props => [uint8List]; +} diff --git a/lib/src/encryption/aes/aes_key.dart b/lib/src/encryption/aes/aes_key.dart new file mode 100644 index 00000000..95ad20d4 --- /dev/null +++ b/lib/src/encryption/aes/aes_key.dart @@ -0,0 +1,20 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/cipher/i_cipher_key.dart'; +import 'package:cryptography_utils/src/utils/int32_utils.dart'; +import 'package:equatable/equatable.dart'; + +/// Represents AES key. +/// Provides utility methods to access key length and column size for AES operations. +class AESKey extends Equatable implements ICipherKey { + final Uint8List uint8List; + + const AESKey(this.uint8List); + + int get length => uint8List.length; + + int get columns => Int32Utils.shiftRight(length, 2); + + @override + List get props => [uint8List]; +} diff --git a/lib/src/encryption/aes/invalid_password_exception.dart b/lib/src/encryption/aes/invalid_password_exception.dart new file mode 100644 index 00000000..1a8605f5 --- /dev/null +++ b/lib/src/encryption/aes/invalid_password_exception.dart @@ -0,0 +1,8 @@ +class InvalidPasswordException implements Exception { + final String? message; + + InvalidPasswordException([this.message]); + + @override + String toString() => message ?? runtimeType.toString(); +} diff --git a/lib/src/encryption/cipher/block_cipher/a_block_cipher.dart b/lib/src/encryption/cipher/block_cipher/a_block_cipher.dart new file mode 100644 index 00000000..769016a5 --- /dev/null +++ b/lib/src/encryption/cipher/block_cipher/a_block_cipher.dart @@ -0,0 +1,37 @@ +// This class was primarily influenced by: +// "pointycastle" - Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/cipher/cipher_key_with_iv.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_mode.dart'; +import 'package:cryptography_utils/src/encryption/cipher/i_cipher_key.dart'; + +abstract class ABlockCipher { + final int blockSize; + final CipherMode cipherMode; + final CipherKeyWithIV cipherKeyWithIV; + + const ABlockCipher({required this.blockSize, required this.cipherMode, required this.cipherKeyWithIV}); + + Uint8List process(Uint8List uint8List); + + int processBlock(Uint8List inputUint8List, int inputOffset, Uint8List outputUint8List, int outputOffset); +} diff --git a/lib/src/encryption/cipher/block_cipher/ctr_block_cipher.dart b/lib/src/encryption/cipher/block_cipher/ctr_block_cipher.dart new file mode 100644 index 00000000..78ac5058 --- /dev/null +++ b/lib/src/encryption/cipher/block_cipher/ctr_block_cipher.dart @@ -0,0 +1,58 @@ +// This class was primarily influenced by: +// "pointycastle" - Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/a_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/stream_cipher/ctr_stream_cipher.dart'; + +/// A block cipher that adapts a stream cipher in Counter (CTR) mode. +/// This class wraps a [CTRStreamCipher], allowing it to be used in +/// contexts where a block cipher interface is expected. +/// Internally, it uses an AES block cipher configured with CTR mode, which turns +/// it into a stream cipher suitable for processing inputs of any size. +class CTRBlockCipher extends ABlockCipher { + final CTRStreamCipher ctrStreamCipher; + + CTRBlockCipher(ABlockCipher blockCipher) + : ctrStreamCipher = CTRStreamCipher(blockCipher), + super(blockSize: blockCipher.blockSize, cipherMode: blockCipher.cipherMode, cipherKeyWithIV: blockCipher.cipherKeyWithIV); + + /// Processes a full [uint8List] input using the underlying stream cipher logic. + /// Returns a new [Uint8List] with the encrypted or decrypted output. + /// Input and output lengths are identical. + @override + Uint8List process(Uint8List uint8List) { + Uint8List outputUint8List = Uint8List(uint8List.length); + ctrStreamCipher.processBytes(uint8List, 0, uint8List.length, outputUint8List, 0); + return outputUint8List; + } + + /// Processes a single block of input data from [inputUint8List] starting at [inputOffset], + /// and writes the result to [outputUint8List] starting at [outputOffset]. + /// This uses the underlying stream cipher's [processBytes] to perform XOR + /// with the encrypted counter value. Returns the number of bytes written, + /// always equal to [blockSize]. + @override + int processBlock(Uint8List inputUint8List, int inputOffset, Uint8List outputUint8List, int outputOffset) { + ctrStreamCipher.processBytes(inputUint8List, inputOffset, blockSize, outputUint8List, outputOffset); + return blockSize; + } +} diff --git a/lib/src/encryption/cipher/block_cipher/invalid_length_exception.dart b/lib/src/encryption/cipher/block_cipher/invalid_length_exception.dart new file mode 100644 index 00000000..0d4fa6e1 --- /dev/null +++ b/lib/src/encryption/cipher/block_cipher/invalid_length_exception.dart @@ -0,0 +1,8 @@ +class InvalidLengthException implements Exception { + final String? message; + + InvalidLengthException([this.message]); + + @override + String toString() => message ?? runtimeType.toString(); +} diff --git a/lib/src/encryption/cipher/block_cipher/invalid_padding_value_exception.dart b/lib/src/encryption/cipher/block_cipher/invalid_padding_value_exception.dart new file mode 100644 index 00000000..bab23fa0 --- /dev/null +++ b/lib/src/encryption/cipher/block_cipher/invalid_padding_value_exception.dart @@ -0,0 +1,8 @@ +class InvalidPaddingValueException implements Exception { + final String? message; + + InvalidPaddingValueException([this.message]); + + @override + String toString() => message ?? runtimeType.toString(); +} diff --git a/lib/src/encryption/cipher/block_cipher/padded_block_cipher.dart b/lib/src/encryption/cipher/block_cipher/padded_block_cipher.dart new file mode 100644 index 00000000..a6efef1e --- /dev/null +++ b/lib/src/encryption/cipher/block_cipher/padded_block_cipher.dart @@ -0,0 +1,111 @@ +// This class was primarily influenced by: +// "pointycastle" - Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/a_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/invalid_length_exception.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/padding/i_cipher_padding.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_mode.dart'; + +/// A block cipher wrapper that applies padding to input data when its length +/// is not a multiple of the cipher's block size. +/// +/// Padding adds extra bytes to the final block to ensure proper alignment with the block cipher. +/// Beyond alignment, padding also helps with length disambiguation, +/// message integrity validation, and resistance to certain cryptographic attacks. +/// +/// This class delegates core encryption and decryption operations +/// to an underlying [ABlockCipher] instance, and applies padding and unpadding. +class PaddedBlockCipher extends ABlockCipher { + final ABlockCipher _blockCipher; + final ICipherPadding _cipherPadding; + + PaddedBlockCipher({required ABlockCipher blockCipher, required ICipherPadding cipherPadding}) + : _blockCipher = blockCipher, + _cipherPadding = cipherPadding, + super(blockSize: blockCipher.blockSize, cipherKeyWithIV: blockCipher.cipherKeyWithIV, cipherMode: blockCipher.cipherMode); + + /// Processes an [uint8List] of input data with padding and returns the output. + /// Applies padding for encryption. + /// For decryption, input must be a multiple of block size and valid padding must exist. + @override + Uint8List process(Uint8List uint8List) { + int inputBlocksCount = (uint8List.length + blockSize - 1) ~/ blockSize; + int outputBlocksCount; + + if (cipherMode == CipherMode.encryption) { + outputBlocksCount = (uint8List.length + blockSize) ~/ blockSize; + } else { + if ((uint8List.length % blockSize) != 0) { + throw InvalidLengthException("Input data length must be a multiple of cipher's block size - $blockSize is ${uint8List.length}"); + } + outputBlocksCount = inputBlocksCount; + } + + Uint8List outputUint8List = Uint8List(outputBlocksCount * blockSize); + for (int i = 0; i < (inputBlocksCount - 1); i++) { + int offset = i * blockSize; + processBlock(uint8List, offset, outputUint8List, offset); + } + int lastBlockOffset = (inputBlocksCount - 1) * blockSize; + int lastBlockSize = _doFinal(uint8List, lastBlockOffset, outputUint8List, lastBlockOffset); + + return outputUint8List.sublist(0, lastBlockOffset + lastBlockSize); + } + + /// Processes a single block of data from [inputUint8List] at [inputOffset], + /// writing the result to [outputUint8List] at [outputOffset]. + /// This delegates to the underlying [blockCipher]'s [processBlock] method. + /// Returns the number of bytes written, which equals [blockSize]. + @override + int processBlock(Uint8List inputUint8List, int inputOffset, Uint8List outputUint8List, int outputOffset) { + return _blockCipher.processBlock(inputUint8List, inputOffset, outputUint8List, outputOffset); + } + + /// Finishes encryption/decryption and returns the number of bytes written. + /// For encryption, handles final block padding and possible extra block. + /// For decryption, strips padding and validates its correctness. + int _doFinal(Uint8List inputUint8List, int inputOffset, Uint8List outputUint8List, int outputOffset) { + if (cipherMode == CipherMode.encryption) { + Uint8List lastInputBlockUint8List = Uint8List(blockSize)..setAll(0, inputUint8List.sublist(inputOffset)); + int remainder = inputUint8List.length - inputOffset; + if (remainder < blockSize) { + _cipherPadding.addPadding(lastInputBlockUint8List, inputUint8List.length - inputOffset); + processBlock(lastInputBlockUint8List, 0, outputUint8List, outputOffset); + + return blockSize; + } else { + processBlock(inputUint8List, inputOffset, outputUint8List, outputOffset); + _cipherPadding.addPadding(lastInputBlockUint8List, 0); + processBlock(lastInputBlockUint8List, 0, outputUint8List, outputOffset + blockSize); + + return 2 * blockSize; + } + } else { + processBlock(inputUint8List, inputOffset, outputUint8List, outputOffset); + int paddedCount = _cipherPadding.countPadding(outputUint8List.sublist(outputOffset)); + int paddedOffsetInBlock = blockSize - paddedCount; + outputUint8List.fillRange(outputOffset + paddedOffsetInBlock, outputUint8List.length, 0); + + return paddedOffsetInBlock; + } + } +} diff --git a/lib/src/encryption/cipher/block_cipher/padding/i_cipher_padding.dart b/lib/src/encryption/cipher/block_cipher/padding/i_cipher_padding.dart new file mode 100644 index 00000000..eccc33b7 --- /dev/null +++ b/lib/src/encryption/cipher/block_cipher/padding/i_cipher_padding.dart @@ -0,0 +1,7 @@ +import 'dart:typed_data'; + +abstract interface class ICipherPadding { + int addPadding(Uint8List uint8List, int offset); + + int countPadding(Uint8List uint8List); +} diff --git a/lib/src/encryption/cipher/block_cipher/padding/pkcs7_padding.dart b/lib/src/encryption/cipher/block_cipher/padding/pkcs7_padding.dart new file mode 100644 index 00000000..3af97bef --- /dev/null +++ b/lib/src/encryption/cipher/block_cipher/padding/pkcs7_padding.dart @@ -0,0 +1,73 @@ +// This class was primarily influenced by: +// "pointycastle" - Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/invalid_length_exception.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/invalid_padding_value_exception.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/padding/i_cipher_padding.dart'; +import 'package:cryptography_utils/src/utils/binary_utils.dart'; + +/// This class provides functionality for: +/// - Adding PKCS7 padding to input data prior to encryption. +/// - Validating and determining the length of PKCS7 padding after decryption. +/// +/// In PKCS7, padding completes the encryption block by appending missing bytes, each of which has a value equal +/// to the total number of added bytes. In case of AES algorithm, the block size is 16 bytes. +/// This makes it possible to accurately remove the extra data and verify its correctness during decryption. +/// +/// PKCS7 padding is used to ensure that the input to a block cipher is a multiple of the block size. + +class Pkcs7Padding implements ICipherPadding { + /// Adds PKCS7 padding to the given [uint8List] starting from [offset]. + /// The value written into each padded byte is the number of padding bytes added. + /// This makes it possible to identify and remove padding later. + /// Returns the number of bytes added as padding. + @override + int addPadding(Uint8List uint8List, int offset) { + int index = offset; + int code = uint8List.length - index; + + while (index < uint8List.length) { + uint8List[index] = code; + index++; + } + return code; + } + + /// Calculates the number of padding bytes at the end of [uint8List]. + /// Verifies that all padding bytes are valid. + /// Throws [InvalidLengthException] if the padding is corrupted, or [InvalidPaddingValueException] if it is inconsistent with PKCS7 format. + @override + int countPadding(Uint8List uint8List) { + int count = BinaryUtils.maskTo8Bits(uint8List[uint8List.length - 1]); + + if (count > uint8List.length || count == 0) { + throw InvalidLengthException('Invalid padding length, should be $count is ${uint8List.length}'); + } + + for (int i = 1; i <= count; i++) { + if (uint8List[uint8List.length - i] != count) { + throw InvalidPaddingValueException('Invalid padding value, should be: $count is ${uint8List[uint8List.length - i]}'); + } + } + return count; + } +} diff --git a/lib/src/encryption/cipher/cipher_key_with_iv.dart b/lib/src/encryption/cipher/cipher_key_with_iv.dart new file mode 100644 index 00000000..bc18b718 --- /dev/null +++ b/lib/src/encryption/cipher/cipher_key_with_iv.dart @@ -0,0 +1,15 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/cipher/i_cipher_key.dart'; +import 'package:equatable/equatable.dart'; + +/// A container class that combines a cipher key and its associated IV (initialization vector). +class CipherKeyWithIV extends Equatable { + final Uint8List ivUint8List; + final T cipherKey; + + const CipherKeyWithIV(this.cipherKey, this.ivUint8List); + + @override + List get props => [ivUint8List, cipherKey]; +} diff --git a/lib/src/encryption/cipher/cipher_mode.dart b/lib/src/encryption/cipher/cipher_mode.dart new file mode 100644 index 00000000..16bfbf52 --- /dev/null +++ b/lib/src/encryption/cipher/cipher_mode.dart @@ -0,0 +1,4 @@ +enum CipherMode { + encryption, + decryption, +} diff --git a/lib/src/encryption/cipher/cipher_text.dart b/lib/src/encryption/cipher/cipher_text.dart new file mode 100644 index 00000000..5610dd38 --- /dev/null +++ b/lib/src/encryption/cipher/cipher_text.dart @@ -0,0 +1,14 @@ +import 'dart:typed_data'; + +import 'package:equatable/equatable.dart'; + +/// This class represents ciphertext and encapsulates the raw encrypted bytes. +/// It is typically used to hold the output of an encryption operation. +class CipherText extends Equatable { + final Uint8List uint8List; + + const CipherText(this.uint8List); + + @override + List get props => [uint8List]; +} diff --git a/lib/src/encryption/cipher/i_cipher_key.dart b/lib/src/encryption/cipher/i_cipher_key.dart new file mode 100644 index 00000000..725da82c --- /dev/null +++ b/lib/src/encryption/cipher/i_cipher_key.dart @@ -0,0 +1 @@ +abstract interface class ICipherKey {} diff --git a/lib/src/encryption/cipher/stream_cipher/ctr_stream_cipher.dart b/lib/src/encryption/cipher/stream_cipher/ctr_stream_cipher.dart new file mode 100644 index 00000000..761f95b1 --- /dev/null +++ b/lib/src/encryption/cipher/stream_cipher/ctr_stream_cipher.dart @@ -0,0 +1,76 @@ +// This class was primarily influenced by: +// "pointycastle" - Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/a_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_key_with_iv.dart'; +import 'package:cryptography_utils/src/encryption/cipher/i_cipher_key.dart'; +import 'package:cryptography_utils/src/utils/binary_utils.dart'; + +/// A stream cipher implementation using Counter (CTR). +/// This class wraps a block cipher, and turns it into a stream cipher +/// by generating a keystream using encrypted counter values, which are XOR'd with the plaintext. +/// Each counter block is incremented for every processed block of data. +class CTRStreamCipher { + final ABlockCipher blockCipher; + late Uint8List _counterUint8List; + late Uint8List _counterOutUint8List; + late int _consumed; + + /// Constructs a [CTRStreamCipher] with the given [blockCipher]. + /// The cipher parameter must include an IV. Initializes internal state and prepares buffers. + CTRStreamCipher(this.blockCipher) { + CipherKeyWithIV cipherKeyWithIV = blockCipher.cipherKeyWithIV; + _counterUint8List = Uint8List.fromList(cipherKeyWithIV.ivUint8List); + _counterOutUint8List = Uint8List(blockCipher.blockSize); + _consumed = _counterOutUint8List.length; + } + + /// Encrypts or decrypts [length] bytes from [inputUint8List] starting at [inputOffset], + /// and writes the result to [outputUint8List] starting at [outOffset]. + /// Internally uses the encrypted counter for XOR. + void processBytes(Uint8List inputUint8List, int inputOffset, int length, Uint8List outputUint8List, int outOffset) { + for (int i = 0; i < length; i++) { + outputUint8List[outOffset + i] = _returnByte(inputUint8List[inputOffset + i]); + } + } + + /// XORs a single byte with a byte from the encrypted counter. + /// When all bytes of the current counter are consumed, it encrypts the next counter block. + int _returnByte(int input) { + if (_consumed >= _counterOutUint8List.length) { + blockCipher.processBlock(_counterUint8List, 0, _counterOutUint8List, 0); + _incrementCounter(); + _consumed = 0; + } + return BinaryUtils.maskTo8Bits(input) ^ _counterOutUint8List[_consumed++]; + } + + /// Increments the counter value (big endian-style). + void _incrementCounter() { + for (int i = _counterUint8List.length - 1; i >= 0; i--) { + _counterUint8List[i]++; + if (_counterUint8List[i] != 0) { + break; + } + } + } +} diff --git a/lib/src/hash/ripemd/a_md4_digest.dart b/lib/src/hash/ripemd/a_md4_digest.dart index ba785dde..5c660463 100644 --- a/lib/src/hash/ripemd/a_md4_digest.dart +++ b/lib/src/hash/ripemd/a_md4_digest.dart @@ -20,6 +20,7 @@ import 'dart:typed_data'; +import 'package:cryptography_utils/src/utils/int32_utils.dart'; import 'package:cryptography_utils/src/utils/register64/register64.dart'; /// [AMD4Digest] provides message digestion, including byte processing, block management, and state updates. @@ -180,7 +181,7 @@ abstract class AMD4Digest { void _packState(Uint8List outputUint8List, int outputOffset) { for (int i = 0; i < _packedStateSize; i++) { - _packInput32(_stateList[i], outputUint8List, outputOffset + i * 4, _endian); + Int32Utils.pack(_stateList[i], outputUint8List, outputOffset + i * 4, _endian); } } @@ -192,7 +193,7 @@ abstract class AMD4Digest { } void _processWord(Uint8List inputUint8List, int inputOffset) { - _bufferList[_bufferOffset++] = _unpackInput32Bits(inputUint8List, inputOffset, _endian); + _bufferList[_bufferOffset++] = Int32Utils.unpack(inputUint8List, inputOffset, _endian); if (_bufferOffset == 16) { _finalizeBlockProcessing(); @@ -209,13 +210,4 @@ abstract class AMD4Digest { int _mask8Bits(int input) { return input & 0xFF; } - - int _unpackInput32Bits(Uint8List inputUint8List, int offset, Endian endian) { - ByteData byteData = ByteData.view(inputUint8List.buffer, inputUint8List.offsetInBytes, inputUint8List.length); - return byteData.getUint32(offset, endian); - } - - void _packInput32(int x, Uint8List outputUint8List, int offset, Endian endian) { - ByteData.view(outputUint8List.buffer, outputUint8List.offsetInBytes, outputUint8List.length).setUint32(offset, x, endian); - } } diff --git a/lib/src/hash/ripemd/ripemd160.dart b/lib/src/hash/ripemd/ripemd160.dart index 4edcfd79..1aa79cee 100644 --- a/lib/src/hash/ripemd/ripemd160.dart +++ b/lib/src/hash/ripemd/ripemd160.dart @@ -21,15 +21,13 @@ import 'dart:typed_data'; import 'package:cryptography_utils/src/hash/ripemd/a_md4_digest.dart'; -import 'package:cryptography_utils/src/utils/register64/register64.dart'; +import 'package:cryptography_utils/src/utils/int32_utils.dart'; /// [Ripemd160] is an implementation of RIPEMD-160 algorithm which is a secure hash function /// producing a 160-bit output, used for address encoding of Bitcoin and Cosmos. class Ripemd160 extends AMD4Digest { static const int _digestLength = 20; - final Register64 register64 = Register64(); - late int _aHash; late int _bHash; late int _cHash; @@ -92,380 +90,380 @@ class Ripemd160 extends AMD4Digest { void _doRounds16Left() { _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleXorCombination(_bHash, _cHash, _dHash) + bufferList[0], 11), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleXorCombination(_aHash, _bHash, _cHash) + bufferList[1], 14), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleXorCombination(_eHash, _aHash, _bHash) + bufferList[2], 15), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleXorCombination(_dHash, _eHash, _aHash) + bufferList[3], 12), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleXorCombination(_cHash, _dHash, _eHash) + bufferList[4], 5), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleXorCombination(_bHash, _cHash, _dHash) + bufferList[5], 8), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleXorCombination(_aHash, _bHash, _cHash) + bufferList[6], 7), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleXorCombination(_eHash, _aHash, _bHash) + bufferList[7], 9), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleXorCombination(_dHash, _eHash, _aHash) + bufferList[8], 11), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleXorCombination(_cHash, _dHash, _eHash) + bufferList[9], 13), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleXorCombination(_bHash, _cHash, _dHash) + bufferList[10], 14), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleXorCombination(_aHash, _bHash, _cHash) + bufferList[11], 15), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleXorCombination(_eHash, _aHash, _bHash) + bufferList[12], 6), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleXorCombination(_dHash, _eHash, _aHash) + bufferList[13], 7), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleXorCombination(_cHash, _dHash, _eHash) + bufferList[14], 9), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleXorCombination(_bHash, _cHash, _dHash) + bufferList[15], 8), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); } void _doRounds16Right() { int roundsValue = 0x50a28be6; _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleXorWithNegation(_bbHash, _ccHash, _ddHash) + bufferList[5] + roundsValue, 8), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleXorWithNegation(_aaHash, _bbHash, _ccHash) + bufferList[14] + roundsValue, 9), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleXorWithNegation(_eeHash, _aaHash, _bbHash) + bufferList[7] + roundsValue, 9), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleXorWithNegation(_ddHash, _eeHash, _aaHash) + bufferList[0] + roundsValue, 11), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleXorWithNegation(_ccHash, _ddHash, _eeHash) + bufferList[9] + roundsValue, 13), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleXorWithNegation(_bbHash, _ccHash, _ddHash) + bufferList[2] + roundsValue, 15), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleXorWithNegation(_aaHash, _bbHash, _ccHash) + bufferList[11] + roundsValue, 15), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleXorWithNegation(_eeHash, _aaHash, _bbHash) + bufferList[4] + roundsValue, 5), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleXorWithNegation(_ddHash, _eeHash, _aaHash) + bufferList[13] + roundsValue, 7), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleXorWithNegation(_ccHash, _ddHash, _eeHash) + bufferList[6] + roundsValue, 7), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleXorWithNegation(_bbHash, _ccHash, _ddHash) + bufferList[15] + roundsValue, 8), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleXorWithNegation(_aaHash, _bbHash, _ccHash) + bufferList[8] + roundsValue, 11), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleXorWithNegation(_eeHash, _aaHash, _bbHash) + bufferList[1] + roundsValue, 14), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleXorWithNegation(_ddHash, _eeHash, _aaHash) + bufferList[10] + roundsValue, 14), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleXorWithNegation(_ccHash, _ddHash, _eeHash) + bufferList[3] + roundsValue, 12), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleXorWithNegation(_bbHash, _ccHash, _ddHash) + bufferList[12] + roundsValue, 6), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); } void _doRounds31Left() { int roundsValue = 0x5a827999; _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _selectBitsByInput(_aHash, _bHash, _cHash) + bufferList[7] + roundsValue, 7), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _selectBitsByInput(_eHash, _aHash, _bHash) + bufferList[4] + roundsValue, 6), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _selectBitsByInput(_dHash, _eHash, _aHash) + bufferList[13] + roundsValue, 8), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _selectBitsByInput(_cHash, _dHash, _eHash) + bufferList[1] + roundsValue, 13), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _selectBitsByInput(_bHash, _cHash, _dHash) + bufferList[10] + roundsValue, 11), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _selectBitsByInput(_aHash, _bHash, _cHash) + bufferList[6] + roundsValue, 9), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _selectBitsByInput(_eHash, _aHash, _bHash) + bufferList[15] + roundsValue, 7), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _selectBitsByInput(_dHash, _eHash, _aHash) + bufferList[3] + roundsValue, 15), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _selectBitsByInput(_cHash, _dHash, _eHash) + bufferList[12] + roundsValue, 7), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _selectBitsByInput(_bHash, _cHash, _dHash) + bufferList[0] + roundsValue, 12), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _selectBitsByInput(_aHash, _bHash, _cHash) + bufferList[9] + roundsValue, 15), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _selectBitsByInput(_eHash, _aHash, _bHash) + bufferList[5] + roundsValue, 9), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _selectBitsByInput(_dHash, _eHash, _aHash) + bufferList[2] + roundsValue, 11), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _selectBitsByInput(_cHash, _dHash, _eHash) + bufferList[14] + roundsValue, 7), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _selectBitsByInput(_bHash, _cHash, _dHash) + bufferList[11] + roundsValue, 13), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _selectBitsByInput(_aHash, _bHash, _cHash) + bufferList[8] + roundsValue, 12), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); } void _doRounds31Right() { int roundsValue = 0x5c4dd124; _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _selectBitsByValue(_aaHash, _bbHash, _ccHash) + bufferList[6] + roundsValue, 9), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _selectBitsByValue(_eeHash, _aaHash, _bbHash) + bufferList[11] + roundsValue, 13), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _selectBitsByValue(_ddHash, _eeHash, _aaHash) + bufferList[3] + roundsValue, 15), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _selectBitsByValue(_ccHash, _ddHash, _eeHash) + bufferList[7] + roundsValue, 7), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _selectBitsByValue(_bbHash, _ccHash, _ddHash) + bufferList[0] + roundsValue, 12), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _selectBitsByValue(_aaHash, _bbHash, _ccHash) + bufferList[13] + roundsValue, 8), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _selectBitsByValue(_eeHash, _aaHash, _bbHash) + bufferList[5] + roundsValue, 9), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _selectBitsByValue(_ddHash, _eeHash, _aaHash) + bufferList[10] + roundsValue, 11), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _selectBitsByValue(_ccHash, _ddHash, _eeHash) + bufferList[14] + roundsValue, 7), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _selectBitsByValue(_bbHash, _ccHash, _ddHash) + bufferList[15] + roundsValue, 7), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _selectBitsByValue(_aaHash, _bbHash, _ccHash) + bufferList[8] + roundsValue, 12), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _selectBitsByValue(_eeHash, _aaHash, _bbHash) + bufferList[12] + roundsValue, 7), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _selectBitsByValue(_ddHash, _eeHash, _aaHash) + bufferList[4] + roundsValue, 6), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _selectBitsByValue(_ccHash, _ddHash, _eeHash) + bufferList[9] + roundsValue, 15), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _selectBitsByValue(_bbHash, _ccHash, _ddHash) + bufferList[1] + roundsValue, 13), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _selectBitsByValue(_aaHash, _bbHash, _ccHash) + bufferList[2] + roundsValue, 11), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); } void _doRounds47Left() { int roundsValue = 0x6ed9eba1; _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleOrAndXor(_eHash, _aHash, _bHash) + bufferList[3] + roundsValue, 11), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleOrAndXor(_dHash, _eHash, _aHash) + bufferList[10] + roundsValue, 13), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleOrAndXor(_cHash, _dHash, _eHash) + bufferList[14] + roundsValue, 6), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleOrAndXor(_bHash, _cHash, _dHash) + bufferList[4] + roundsValue, 7), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleOrAndXor(_aHash, _bHash, _cHash) + bufferList[9] + roundsValue, 14), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleOrAndXor(_eHash, _aHash, _bHash) + bufferList[15] + roundsValue, 9), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleOrAndXor(_dHash, _eHash, _aHash) + bufferList[8] + roundsValue, 13), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleOrAndXor(_cHash, _dHash, _eHash) + bufferList[1] + roundsValue, 15), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleOrAndXor(_bHash, _cHash, _dHash) + bufferList[2] + roundsValue, 14), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleOrAndXor(_aHash, _bHash, _cHash) + bufferList[7] + roundsValue, 8), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleOrAndXor(_eHash, _aHash, _bHash) + bufferList[0] + roundsValue, 13), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleOrAndXor(_dHash, _eHash, _aHash) + bufferList[6] + roundsValue, 6), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleOrAndXor(_cHash, _dHash, _eHash) + bufferList[13] + roundsValue, 5), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleOrAndXor(_bHash, _cHash, _dHash) + bufferList[11] + roundsValue, 12), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleOrAndXor(_aHash, _bHash, _cHash) + bufferList[5] + roundsValue, 7), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleOrAndXor(_eHash, _aHash, _bHash) + bufferList[12] + roundsValue, 5), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); } void _doRounds47Right() { int roundsValue = 0x6d703ef3; _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleOrAndXor(_eeHash, _aaHash, _bbHash) + bufferList[15] + roundsValue, 9), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleOrAndXor(_ddHash, _eeHash, _aaHash) + bufferList[5] + roundsValue, 7), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleOrAndXor(_ccHash, _ddHash, _eeHash) + bufferList[1] + roundsValue, 15), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleOrAndXor(_bbHash, _ccHash, _ddHash) + bufferList[3] + roundsValue, 11), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleOrAndXor(_aaHash, _bbHash, _ccHash) + bufferList[7] + roundsValue, 8), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleOrAndXor(_eeHash, _aaHash, _bbHash) + bufferList[14] + roundsValue, 6), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleOrAndXor(_ddHash, _eeHash, _aaHash) + bufferList[6] + roundsValue, 6), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleOrAndXor(_ccHash, _ddHash, _eeHash) + bufferList[9] + roundsValue, 14), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleOrAndXor(_bbHash, _ccHash, _ddHash) + bufferList[11] + roundsValue, 12), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleOrAndXor(_aaHash, _bbHash, _ccHash) + bufferList[8] + roundsValue, 13), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleOrAndXor(_eeHash, _aaHash, _bbHash) + bufferList[12] + roundsValue, 5), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleOrAndXor(_ddHash, _eeHash, _aaHash) + bufferList[2] + roundsValue, 14), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleOrAndXor(_ccHash, _ddHash, _eeHash) + bufferList[10] + roundsValue, 13), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleOrAndXor(_bbHash, _ccHash, _ddHash) + bufferList[0] + roundsValue, 13), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleOrAndXor(_aaHash, _bbHash, _ccHash) + bufferList[4] + roundsValue, 7), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleOrAndXor(_eeHash, _aaHash, _bbHash) + bufferList[13] + roundsValue, 5), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); } void _doRounds63Left() { int roundsValue = 0x8f1bbcdc; _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _selectBitsByValue(_dHash, _eHash, _aHash) + bufferList[1] + roundsValue, 11), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _selectBitsByValue(_cHash, _dHash, _eHash) + bufferList[9] + roundsValue, 12), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _selectBitsByValue(_bHash, _cHash, _dHash) + bufferList[11] + roundsValue, 14), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _selectBitsByValue(_aHash, _bHash, _cHash) + bufferList[10] + roundsValue, 15), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _selectBitsByValue(_eHash, _aHash, _bHash) + bufferList[0] + roundsValue, 14), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _selectBitsByValue(_dHash, _eHash, _aHash) + bufferList[8] + roundsValue, 15), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _selectBitsByValue(_cHash, _dHash, _eHash) + bufferList[12] + roundsValue, 9), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _selectBitsByValue(_bHash, _cHash, _dHash) + bufferList[4] + roundsValue, 8), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _selectBitsByValue(_aHash, _bHash, _cHash) + bufferList[13] + roundsValue, 9), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _selectBitsByValue(_eHash, _aHash, _bHash) + bufferList[3] + roundsValue, 14), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _selectBitsByValue(_dHash, _eHash, _aHash) + bufferList[7] + roundsValue, 5), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _selectBitsByValue(_cHash, _dHash, _eHash) + bufferList[15] + roundsValue, 6), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _selectBitsByValue(_bHash, _cHash, _dHash) + bufferList[14] + roundsValue, 8), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _selectBitsByValue(_aHash, _bHash, _cHash) + bufferList[5] + roundsValue, 6), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _selectBitsByValue(_eHash, _aHash, _bHash) + bufferList[6] + roundsValue, 5), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _selectBitsByValue(_dHash, _eHash, _aHash) + bufferList[2] + roundsValue, 12), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); } void _doRounds63Right() { int roundsValue = 0x7a6d76e9; _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _selectBitsByInput(_ddHash, _eeHash, _aaHash) + bufferList[8] + roundsValue, 15), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _selectBitsByInput(_ccHash, _ddHash, _eeHash) + bufferList[6] + roundsValue, 5), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _selectBitsByInput(_bbHash, _ccHash, _ddHash) + bufferList[4] + roundsValue, 8), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _selectBitsByInput(_aaHash, _bbHash, _ccHash) + bufferList[1] + roundsValue, 11), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _selectBitsByInput(_eeHash, _aaHash, _bbHash) + bufferList[3] + roundsValue, 14), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _selectBitsByInput(_ddHash, _eeHash, _aaHash) + bufferList[11] + roundsValue, 14), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _selectBitsByInput(_ccHash, _ddHash, _eeHash) + bufferList[15] + roundsValue, 6), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _selectBitsByInput(_bbHash, _ccHash, _ddHash) + bufferList[0] + roundsValue, 14), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _selectBitsByInput(_aaHash, _bbHash, _ccHash) + bufferList[5] + roundsValue, 6), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _selectBitsByInput(_eeHash, _aaHash, _bbHash) + bufferList[12] + roundsValue, 9), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _selectBitsByInput(_ddHash, _eeHash, _aaHash) + bufferList[2] + roundsValue, 12), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _selectBitsByInput(_ccHash, _ddHash, _eeHash) + bufferList[13] + roundsValue, 9), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _selectBitsByInput(_bbHash, _ccHash, _ddHash) + bufferList[9] + roundsValue, 12), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _selectBitsByInput(_aaHash, _bbHash, _ccHash) + bufferList[7] + roundsValue, 5), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _selectBitsByInput(_eeHash, _aaHash, _bbHash) + bufferList[10] + roundsValue, 15), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _selectBitsByInput(_ddHash, _eeHash, _aaHash) + bufferList[14] + roundsValue, 8), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); } void _doRounds79Left() { int roundsValue = 0xa953fd4e; _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleXorWithNegation(_cHash, _dHash, _eHash) + bufferList[4] + roundsValue, 9), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleXorWithNegation(_bHash, _cHash, _dHash) + bufferList[0] + roundsValue, 15), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleXorWithNegation(_aHash, _bHash, _cHash) + bufferList[5] + roundsValue, 5), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleXorWithNegation(_eHash, _aHash, _bHash) + bufferList[9] + roundsValue, 11), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleXorWithNegation(_dHash, _eHash, _aHash) + bufferList[7] + roundsValue, 6), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleXorWithNegation(_cHash, _dHash, _eHash) + bufferList[12] + roundsValue, 8), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleXorWithNegation(_bHash, _cHash, _dHash) + bufferList[2] + roundsValue, 13), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleXorWithNegation(_aHash, _bHash, _cHash) + bufferList[10] + roundsValue, 12), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleXorWithNegation(_eHash, _aHash, _bHash) + bufferList[14] + roundsValue, 5), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleXorWithNegation(_dHash, _eHash, _aHash) + bufferList[1] + roundsValue, 12), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleXorWithNegation(_cHash, _dHash, _eHash) + bufferList[3] + roundsValue, 13), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); _aHash = _doSum32Bits(_doCircularRotateLeft(_aHash + _handleXorWithNegation(_bHash, _cHash, _dHash) + bufferList[8] + roundsValue, 14), _eHash); - _cHash = register64.rotateLeft32Bits(_cHash, 10); + _cHash = Int32Utils.rotateLeft(_cHash, 10); _eHash = _doSum32Bits(_doCircularRotateLeft(_eHash + _handleXorWithNegation(_aHash, _bHash, _cHash) + bufferList[11] + roundsValue, 11), _dHash); - _bHash = register64.rotateLeft32Bits(_bHash, 10); + _bHash = Int32Utils.rotateLeft(_bHash, 10); _dHash = _doSum32Bits(_doCircularRotateLeft(_dHash + _handleXorWithNegation(_eHash, _aHash, _bHash) + bufferList[6] + roundsValue, 8), _cHash); - _aHash = register64.rotateLeft32Bits(_aHash, 10); + _aHash = Int32Utils.rotateLeft(_aHash, 10); _cHash = _doSum32Bits(_doCircularRotateLeft(_cHash + _handleXorWithNegation(_dHash, _eHash, _aHash) + bufferList[15] + roundsValue, 5), _bHash); - _eHash = register64.rotateLeft32Bits(_eHash, 10); + _eHash = Int32Utils.rotateLeft(_eHash, 10); _bHash = _doSum32Bits(_doCircularRotateLeft(_bHash + _handleXorWithNegation(_cHash, _dHash, _eHash) + bufferList[13] + roundsValue, 6), _aHash); - _dHash = register64.rotateLeft32Bits(_dHash, 10); + _dHash = Int32Utils.rotateLeft(_dHash, 10); } void _doRounds79Right() { _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleXorCombination(_ccHash, _ddHash, _eeHash) + bufferList[12], 8), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleXorCombination(_bbHash, _ccHash, _ddHash) + bufferList[15], 5), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleXorCombination(_aaHash, _bbHash, _ccHash) + bufferList[10], 12), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleXorCombination(_eeHash, _aaHash, _bbHash) + bufferList[4], 9), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleXorCombination(_ddHash, _eeHash, _aaHash) + bufferList[1], 12), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleXorCombination(_ccHash, _ddHash, _eeHash) + bufferList[5], 5), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleXorCombination(_bbHash, _ccHash, _ddHash) + bufferList[8], 14), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleXorCombination(_aaHash, _bbHash, _ccHash) + bufferList[7], 6), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleXorCombination(_eeHash, _aaHash, _bbHash) + bufferList[6], 8), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleXorCombination(_ddHash, _eeHash, _aaHash) + bufferList[2], 13), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleXorCombination(_ccHash, _ddHash, _eeHash) + bufferList[13], 6), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); _aaHash = _doSum32Bits(_doCircularRotateLeft(_aaHash + _handleXorCombination(_bbHash, _ccHash, _ddHash) + bufferList[14], 5), _eeHash); - _ccHash = register64.rotateLeft32Bits(_ccHash, 10); + _ccHash = Int32Utils.rotateLeft(_ccHash, 10); _eeHash = _doSum32Bits(_doCircularRotateLeft(_eeHash + _handleXorCombination(_aaHash, _bbHash, _ccHash) + bufferList[0], 15), _ddHash); - _bbHash = register64.rotateLeft32Bits(_bbHash, 10); + _bbHash = Int32Utils.rotateLeft(_bbHash, 10); _ddHash = _doSum32Bits(_doCircularRotateLeft(_ddHash + _handleXorCombination(_eeHash, _aaHash, _bbHash) + bufferList[3], 13), _ccHash); - _aaHash = register64.rotateLeft32Bits(_aaHash, 10); + _aaHash = Int32Utils.rotateLeft(_aaHash, 10); _ccHash = _doSum32Bits(_doCircularRotateLeft(_ccHash + _handleXorCombination(_ddHash, _eeHash, _aaHash) + bufferList[9], 11), _bbHash); - _eeHash = register64.rotateLeft32Bits(_eeHash, 10); + _eeHash = Int32Utils.rotateLeft(_eeHash, 10); _bbHash = _doSum32Bits(_doCircularRotateLeft(_bbHash + _handleXorCombination(_ccHash, _ddHash, _eeHash) + bufferList[11], 11), _aaHash); - _ddHash = register64.rotateLeft32Bits(_ddHash, 10); + _ddHash = Int32Utils.rotateLeft(_ddHash, 10); } int _handleXorCombination(int input, int offset, int value) { @@ -493,7 +491,7 @@ class Ripemd160 extends AMD4Digest { } int _doCircularRotateLeft(int chunk32Bits, int offset) { - return register64.rotateLeft32Bits(_mask32Bits(chunk32Bits), offset); + return Int32Utils.rotateLeft(_mask32Bits(chunk32Bits), offset); } int _doSum32Bits(int chunk32Bits, int output) { diff --git a/lib/src/utils/binary_utils.dart b/lib/src/utils/binary_utils.dart index 3bee324c..c8e6c3df 100644 --- a/lib/src/utils/binary_utils.dart +++ b/lib/src/utils/binary_utils.dart @@ -7,6 +7,11 @@ class BinaryUtils { return bytes.map((int byte) => byte.toRadixString(2).padLeft(padding, '0')).join(''); } + static int maskTo8Bits(int value) { + int mask8Bits = 0xFF; + return value & mask8Bits; + } + static String intToBinary(int number, {int padding = 8}) { return number.toRadixString(2).padLeft(padding, '0'); } diff --git a/lib/src/utils/int32_utils.dart b/lib/src/utils/int32_utils.dart new file mode 100644 index 00000000..688145b4 --- /dev/null +++ b/lib/src/utils/int32_utils.dart @@ -0,0 +1,72 @@ +import 'dart:typed_data'; + +class Int32Utils { + static const int _mask5Bits = 0x1F; + static const int _mask32Bits = 0xFFFFFFFF; + + static const List _mask32BitsList = [ + 0xFFFFFFFF, + 0x7FFFFFFF, + 0x3FFFFFFF, + 0x1FFFFFFF, + 0x0FFFFFFF, + 0x07FFFFFF, + 0x03FFFFFF, + 0x01FFFFFF, + 0x00FFFFFF, + 0x007FFFFF, + 0x003FFFFF, + 0x001FFFFF, + 0x000FFFFF, + 0x0007FFFF, + 0x0003FFFF, + 0x0001FFFF, + 0x0000FFFF, + 0x00007FFF, + 0x00003FFF, + 0x00001FFF, + 0x00000FFF, + 0x000007FF, + 0x000003FF, + 0x000001FF, + 0x000000FF, + 0x0000007F, + 0x0000003F, + 0x0000001F, + 0x0000000F, + 0x00000007, + 0x00000003, + 0x00000001, + 0x00000000 + ]; + + static void pack(int x, Uint8List outputUint8List, int offset, Endian endian) { + ByteData.view(outputUint8List.buffer, outputUint8List.offsetInBytes, outputUint8List.length).setUint32(offset, x, endian); + } + + static int shiftLeft(int chunk32Bits, int shiftValue) { + int maskedN = shiftValue & _mask5Bits; + int maskedX = chunk32Bits & _mask32BitsList[maskedN]; + return (maskedX << maskedN) & _mask32Bits; + } + + static int shiftRight(int chunk32Bits, int shiftValue) { + int maskedShiftValue = shiftValue & _mask5Bits; + return chunk32Bits >> maskedShiftValue; + } + + static int rotateLeft(int chunk32Bits, int offset) { + int maskedOffset = offset & _mask5Bits; + return shiftLeft(chunk32Bits, maskedOffset) | (chunk32Bits >> (32 - maskedOffset)); + } + + static int rotateRight(int chunk32Bits, int offset) { + int maskedOffset = offset & _mask5Bits; + return (chunk32Bits >> maskedOffset) | shiftLeft(chunk32Bits, 32 - maskedOffset); + } + + static int unpack(Uint8List inputUint8List, int offset, Endian endian) { + ByteData byteData = ByteData.view(inputUint8List.buffer, inputUint8List.offsetInBytes, inputUint8List.length); + return byteData.getUint32(offset, endian); + } +} diff --git a/lib/src/utils/register64/register64.dart b/lib/src/utils/register64/register64.dart index d7034d66..d141943f 100644 --- a/lib/src/utils/register64/register64.dart +++ b/lib/src/utils/register64/register64.dart @@ -18,51 +18,15 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +import 'package:cryptography_utils/src/utils/int32_utils.dart'; import 'package:equatable/equatable.dart'; ///[Register64] serve as abstractions for managing the 64-bit lanes and the state matrix of the algorithm, ensuring clean and modular handling /// of the algorithm’s 1600-bit internal state. Register64 is object represented by two 32-bit integers, together represent a 64-bit value. class Register64 with EquatableMixin { - static const int _mask5Bits = 0x1F; static const int _mask6Bits = 0x3F; static const int _mask32Bits = 0xFFFFFFFF; - static const List _mask32BitsList = [ - 0xFFFFFFFF, - 0x7FFFFFFF, - 0x3FFFFFFF, - 0x1FFFFFFF, - 0x0FFFFFFF, - 0x07FFFFFF, - 0x03FFFFFF, - 0x01FFFFFF, - 0x00FFFFFF, - 0x007FFFFF, - 0x003FFFFF, - 0x001FFFFF, - 0x000FFFFF, - 0x0007FFFF, - 0x0003FFFF, - 0x0001FFFF, - 0x0000FFFF, - 0x00007FFF, - 0x00003FFF, - 0x00001FFF, - 0x00000FFF, - 0x000007FF, - 0x000003FF, - 0x000001FF, - 0x000000FF, - 0x0000007F, - 0x0000003F, - 0x0000001F, - 0x0000000F, - 0x00000007, - 0x00000003, - 0x00000001, - 0x00000000 - ]; - late int _lowerHalf; late int _upperHalf; @@ -99,11 +63,6 @@ class Register64 with EquatableMixin { _lowerHalf ^= otherRegister64._lowerHalf; } - int rotateLeft32Bits(int chunk32Bits, int offset) { - int maskedOffset = offset & _mask5Bits; - return _shiftLeft32Bits(chunk32Bits, maskedOffset) | (chunk32Bits >> (32 - maskedOffset)); - } - void setInt(int initialValue, [int? lower32Bits]) { if (lower32Bits != null) { _upperHalf = initialValue; @@ -119,12 +78,12 @@ class Register64 with EquatableMixin { if (maskedN == 0) { return; } else if (maskedN >= 32) { - _upperHalf = _shiftLeft32Bits(_lowerHalf, maskedN - 32); + _upperHalf = Int32Utils.shiftLeft(_lowerHalf, maskedN - 32); _lowerHalf = 0; } else { - _upperHalf = _shiftLeft32Bits(_upperHalf, maskedN); + _upperHalf = Int32Utils.shiftLeft(_upperHalf, maskedN); _upperHalf |= _lowerHalf >> (32 - maskedN); - _lowerHalf = _shiftLeft32Bits(_lowerHalf, maskedN); + _lowerHalf = Int32Utils.shiftLeft(_lowerHalf, maskedN); } } @@ -137,7 +96,7 @@ class Register64 with EquatableMixin { _upperHalf = 0; } else { _lowerHalf = _lowerHalf >> maskedN; - _lowerHalf |= _shiftLeft32Bits(_upperHalf, 32 - maskedN); + _lowerHalf |= Int32Utils.shiftLeft(_upperHalf, 32 - maskedN); _upperHalf = upperHalf >> maskedN; } } @@ -152,12 +111,6 @@ class Register64 with EquatableMixin { } } - int _shiftLeft32Bits(int chunk32Bits, int shiftValue) { - int maskedN = shiftValue & _mask5Bits; - int maskedX = chunk32Bits & _mask32BitsList[maskedN]; - return (maskedX << maskedN) & _mask32Bits; - } - @override List get props => [_upperHalf, _lowerHalf]; } diff --git a/pubspec.yaml b/pubspec.yaml index 6ed118ab..872874e1 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.23 +version: 0.0.24 environment: sdk: ">=3.2.6" diff --git a/test/encryption/aes/aes256_randomized_test.dart b/test/encryption/aes/aes256_randomized_test.dart new file mode 100644 index 00000000..c0ded2d8 --- /dev/null +++ b/test/encryption/aes/aes256_randomized_test.dart @@ -0,0 +1,65 @@ +import 'package:cryptography_utils/src/encryption/aes/aes256_randomized.dart'; +import 'package:cryptography_utils/src/encryption/aes/invalid_password_exception.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +void main() { + // Arrange + // Actual values for tests + String actualPassword = 'xyzTest123'; + String actualStringToEncrypt = 'Abctest'; + String actualStringToDecrypt = 'MyAWit7BGm0uhMVm/rQwb9526GPVYlL8lZxio4vYMNMW3wLX'; + + // Expected values for tests + String expectedDecryptedString = 'Abctest'; + + group('Tests of AES256Randomized.encrypt()', () { + // Output is always a random string because AES changes the initialization vector with Random Secure + // and we cannot match the hardcoded expected result. That's why we check whether it is possible to encode and decode text + test('Should correctly encrypt given string via AES256 algorithm and check with decrypt method', () async { + // Act + String actualEncryptedString = AES256Randomized.encrypt(actualPassword, actualStringToEncrypt); + String actualDecryptedString = AES256Randomized.decrypt(actualPassword, actualEncryptedString); + + + // Assert + expect(actualDecryptedString, expectedDecryptedString); + }); + }); + + group('Tests of AES256Randomized.decrypt()', () { + test('Should [return STRING] decrypted from given hash via AES256 algorithm', () async { + // Act + String actualDecryptedString = AES256Randomized.decrypt(actualPassword, actualStringToDecrypt); + + // Assert + expect(actualDecryptedString, expectedDecryptedString); + }); + + test('Should [throw InvalidPasswordException] if password is not correct', () { + // Assert + expect( + () => AES256Randomized.decrypt('incorrect_password', actualStringToDecrypt), + throwsA(isA()), + ); + }); + }); + + group('Tests of AES256Randomized.isPasswordValid()', () { + test('Should [return TRUE] if [password CORRECT]', () async { + // Act + bool actualPasswordValid = AES256Randomized.isPasswordValid(actualPassword, actualStringToDecrypt); + + // Assert + expect(actualPasswordValid, true); + }); + + test('Should [return FALSE] if [password INCORRECT]', () async { + // Act + bool actualPasswordValid = AES256Randomized.isPasswordValid('incorrect_password', actualStringToDecrypt); + + // Assert + expect(actualPasswordValid, false); + }); + }); +} diff --git a/test/encryption/aes/aes_ctr_test.dart b/test/encryption/aes/aes_ctr_test.dart new file mode 100644 index 00000000..9a7ad1b3 --- /dev/null +++ b/test/encryption/aes/aes_ctr_test.dart @@ -0,0 +1,46 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/aes/aes_ctr.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_iv.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_key.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_text.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +void main() { + group('Tests of AESctr.encrypt()', () { + test('Should [return encrypted byte] constructed from given String', () { + // Arrange + Uint8List actualInputUint8List = Uint8List.fromList(utf8.encode('abcTest123!')); + AESKey actualAESKey = AESKey(Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])); + AESIV actualAESIV = AESIV(Uint8List.fromList([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0])); + + // Act + CipherText actualCipherText = AESctr.encrypt(actualInputUint8List, actualAESKey, actualAESIV); + + // Assert + CipherText expectedCipherText = + CipherText(Uint8List.fromList([65, 203, 154, 198, 209, 63, 47, 217, 54, 44, 221, 217, 105, 171, 156, 111])); + + expect(actualCipherText, expectedCipherText); + }); + }); + + group('Tests of AESctr.decrypt()', () { + test('Should [return decrypted String] constructed from encrypted data', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])); + AESIV actualAESIV = AESIV(Uint8List.fromList([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0])); + + // Act + CipherText actualCipherText = CipherText(Uint8List.fromList([65, 203, 154, 198, 209, 63, 47, 217, 54, 44, 221, 217, 105, 171, 156, 111])); + Uint8List actualDecryptedUint8List = AESctr.decrypt(actualCipherText, actualAESKey, actualAESIV); + + // Assert + Uint8List expectedDecryptedUint8List = Uint8List.fromList(utf8.encode('abcTest123!')); + + expect(actualDecryptedUint8List, expectedDecryptedUint8List); + }); + }); +} diff --git a/test/encryption/aes/aes_engine_test.dart b/test/encryption/aes/aes_engine_test.dart new file mode 100644 index 00000000..95f0e405 --- /dev/null +++ b/test/encryption/aes/aes_engine_test.dart @@ -0,0 +1,52 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/aes/aes_engine.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_iv.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_key.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_key_with_iv.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_mode.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +void main() { + group('Tests of AesEngine.process()', () { + test('Should [return encrypted bytes] constructed from given data', () { + // Arrange + AESIV actualAESIV = AESIV(Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])); + AESKey actualAESKey = AESKey(Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])); + + AESEngine actualAESEngine = + AESEngine(cipherMode: CipherMode.encryption, cipherKeyWithIV: CipherKeyWithIV(actualAESKey, actualAESIV.uint8List)); + Uint8List actualInputUint8Lis = Uint8List.fromList([171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171]); + + // Act + Uint8List actualUint8List = actualAESEngine.process(actualInputUint8Lis); + + // Assert + Uint8List expectedUint8List = Uint8List.fromList([72, 77, 234, 245, 115, 247, 44, 82, 70, 226, 70, 8, 248, 231, 87, 199]); + + expect(actualUint8List, expectedUint8List); + }); + }); + + group('Tests of AesEngine.processBlock()', () { + test('Should [return encrypted bytes] based on given data', () { + // Arrange + AESIV actualAESIV = AESIV(Uint8List.fromList([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160])); + AESKey actualAESKey = AESKey(Uint8List.fromList([160, 150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10])); + + AESEngine actualAESEngine = + AESEngine(cipherMode: CipherMode.encryption, cipherKeyWithIV: CipherKeyWithIV(actualAESKey, actualAESIV.uint8List)); + Uint8List actualInputUint8Lis = Uint8List.fromList([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31]); + + // Act + Uint8List actualUint8List = Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + actualAESEngine.processBlock(actualInputUint8Lis, 0, actualUint8List, 0); + + // Assert + Uint8List expectedUint8List = Uint8List.fromList([180, 192, 253, 203, 105, 158, 34, 129, 232, 178, 157, 100, 108, 54, 160, 241]); + + expect(actualUint8List, expectedUint8List); + }); + }); +} diff --git a/test/encryption/cipher/block_cipher/ctr_block_cipher_test.dart b/test/encryption/cipher/block_cipher/ctr_block_cipher_test.dart new file mode 100644 index 00000000..d39f108f --- /dev/null +++ b/test/encryption/cipher/block_cipher/ctr_block_cipher_test.dart @@ -0,0 +1,145 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/aes/aes_engine.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_iv.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_key.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/ctr_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_key_with_iv.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_mode.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +// ignore_for_file: cascade_invocations +void main() { + group('Tests of CTRBlockCipher.process()', () { + test('Should [return encrypted data] constructed from given data', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])); + AESIV actualAESIV = AESIV(Uint8List.fromList([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + CTRBlockCipher actualCTRBlockCipher = CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ); + + Uint8List actualInputUint8List = Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + + // Act + Uint8List actualEncryptedUint8List = actualCTRBlockCipher.process(actualInputUint8List); + + // Assert + Uint8List expectedEncryptedUint8List = Uint8List.fromList([32, 168, 251, 145, 176, 73, 93, 239, 12, 22]); + + expect(actualEncryptedUint8List, expectedEncryptedUint8List); + }); + + test('Should [return decrypted data] constructed from encrypted data', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36])); + AESIV actualAESIV = AESIV(Uint8List.fromList([36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + CTRBlockCipher actualCTRBlockCipher = CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.decryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ); + + Uint8List actualEncryptedUint8List = Uint8List.fromList([3145, 210, 107, 61, 134, 27, 92, 89, 244, 170]); + + // Act + Uint8List actualDecryptedUint8List = actualCTRBlockCipher.process(actualEncryptedUint8List); + + // Assert + Uint8List expectedDecryptedUint8List = Uint8List.fromList([119, 28, 186, 114, 119, 121, 28, 154, 34, 12]); + + expect(actualDecryptedUint8List, expectedDecryptedUint8List); + }); + + test('Should [return encrypted data] with the same length as input', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160])); + AESIV actualAESIV = AESIV(Uint8List.fromList([160, 150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + CTRBlockCipher actualCTRBlockCipher = CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ); + Uint8List actualInputUint8List = Uint8List.fromList([21, 22, 23, 24, 25, 26, 27, 28, 29, 30]); + + // Act + Uint8List actualOutputUint8List = actualCTRBlockCipher.process(actualInputUint8List); + int actualLength = actualOutputUint8List.length; + + // Assert + int expectedLength = 10; + Uint8List expectedOutputUint8List = Uint8List.fromList([41, 148, 235, 8, 86, 92, 112, 15, 188, 226]); + + expect(actualLength, expectedLength); + expect(actualOutputUint8List, expectedOutputUint8List); + }); + }); + + group('Tests of CTRBlockCipher.processBlock()', () { + test('Should [return encrypted block] with the same length as input block', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160])); + AESIV actualAESIV = AESIV(Uint8List.fromList([160, 150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + CTRBlockCipher actualCTRBlockCipher = CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ); + + Uint8List actualInputUint8List = Uint8List.fromList([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31]); + + // Act + Uint8List actualOutputUint8List = Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + int actualLength = actualCTRBlockCipher.processBlock(actualInputUint8List, 0, actualOutputUint8List, 0); + + // Assert + int expectedProcessedLength = 16; + Uint8List expectedOutputUint8List = Uint8List.fromList([61, 129, 249, 23, 70, 77, 102, 28, 176, 239, 153, 4, 163, 233, 186, 167]); + + expect(actualLength, expectedProcessedLength); + expect(actualOutputUint8List, expectedOutputUint8List); + }); + + test('Should [return decrypted block] with the same length as constructed from encrypted block', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([21, 31, 41, 51, 61, 71, 81, 91, 101, 111, 121, 131, 141, 151, 161, 171])); + AESIV actualAESIV = AESIV(Uint8List.fromList([171, 161, 151, 141, 131, 121, 111, 101, 91, 81, 71, 61, 51, 41, 31, 21])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + CTRBlockCipher actualCTRBlockCipher = CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ); + + Uint8List actualEncryptedUint8List = Uint8List.fromList([99, 42, 78, 164, 55, 208, 120, 67, 142, 26, 231, 198, 15, 104, 87, 63]); + + // Act + Uint8List actualDecryptedUint8List = Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + int actualLength = actualCTRBlockCipher.processBlock(actualEncryptedUint8List, 0, actualDecryptedUint8List, 0); + + // Assert + Uint8List expectedEncryptedUint8List = Uint8List.fromList([120, 50, 101, 88, 208, 103, 214, 40, 31, 95, 29, 8, 176, 252, 21, 105]); + int expectedProcessedLength = 16; + + expect(actualLength, expectedProcessedLength); + expect(actualDecryptedUint8List, expectedEncryptedUint8List); + }); + }); +} diff --git a/test/encryption/cipher/block_cipher/padded_block_cipher_test.dart b/test/encryption/cipher/block_cipher/padded_block_cipher_test.dart new file mode 100644 index 00000000..f94abbd8 --- /dev/null +++ b/test/encryption/cipher/block_cipher/padded_block_cipher_test.dart @@ -0,0 +1,133 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/aes/aes_engine.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_iv.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_key.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/ctr_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/invalid_length_exception.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/padded_block_cipher.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/padding/pkcs7_padding.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_key_with_iv.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_mode.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +// ignore_for_file: cascade_invocations +void main() { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])); + AESIV actualAESIV = AESIV(Uint8List.fromList([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0])); + + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + group('Tests of PaddedBlockCipher.process()', () { + test('Should [return encrypted bytes] constructed from given data', () { + // Arrange + PaddedBlockCipher actualPaddedBlockCipher = PaddedBlockCipher( + blockCipher: CTRBlockCipher(AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + )), + cipherPadding: Pkcs7Padding(), + ); + + // Act + Uint8List actualInputUint8List = Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + Uint8List actualEncryptedUint8List = actualPaddedBlockCipher.process(actualInputUint8List); + + // Assert + Uint8List expectedEncryptedUint8List = Uint8List.fromList([33, 171, 250, 150, 177, 74, 92, 224, 13, 21, 250, 218, 106, 168, 159, 108]); + + expect(actualEncryptedUint8List, expectedEncryptedUint8List); + }); + + test('Should [return decrypted bytes] constructed from encrypted data', () { + // Arrange + PaddedBlockCipher actualPaddedBlockCipher = PaddedBlockCipher( + blockCipher: CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.decryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ), + cipherPadding: Pkcs7Padding(), + ); + + // Act + Uint8List actualEncryptedUint8List = Uint8List.fromList([33, 171, 250, 150, 177, 74, 92, 224, 13, 21, 250, 218, 106, 168, 159, 108]); + Uint8List actualDecryptedUint8List = actualPaddedBlockCipher.process(actualEncryptedUint8List); + + // Assert + Uint8List expectedDecryptedUint8List = Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + expect(actualDecryptedUint8List, expectedDecryptedUint8List); + }); + + test('Should [throw InvalidLengthException] when trying to DECRYPT invalid data', () { + // Arrange + PaddedBlockCipher actualPaddedBlockCipher = PaddedBlockCipher( + blockCipher: CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.decryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ), + cipherPadding: Pkcs7Padding(), + ); + + Uint8List actualInput = Uint8List(30); + + // Assert + expect(() => actualPaddedBlockCipher.process(actualInput), throwsA(isA())); + }); + }); + + group('Tests of PaddedBlockCipher.processBlock()', () { + test('Should [return encrypted bytes] constructed from given data', () { + // Arrange + PaddedBlockCipher actualPaddedBlockCipher = PaddedBlockCipher( + blockCipher: CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ), + cipherPadding: Pkcs7Padding(), + ); + Uint8List actualInputUint8List = Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + + // Act + Uint8List actualOutputUint8List = Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + actualPaddedBlockCipher.processBlock(actualInputUint8List, 0, actualOutputUint8List, 0); + + // Assert + Uint8List expectedOutputUint8List = Uint8List.fromList([32, 168, 251, 145, 176, 73, 93, 239, 12, 22, 246, 215, 96, 163, 151, 101]); + + expect(actualOutputUint8List, expectedOutputUint8List); + }); + + test('Should [return decrypted bytes] constructed from encrypted data', () { + // Arrange + PaddedBlockCipher actualPaddedBlockCipher = PaddedBlockCipher( + blockCipher: CTRBlockCipher( + AESEngine( + cipherMode: CipherMode.decryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ), + ), + cipherPadding: Pkcs7Padding(), + ); + + Uint8List actualEncryptedBlock = Uint8List.fromList([32, 168, 251, 145, 176, 73, 93, 239, 12, 22, 246, 215, 96, 163, 151, 101]); + + // Act + Uint8List actualDecryptedUint8List = Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + actualPaddedBlockCipher.processBlock(actualEncryptedBlock, 0, actualDecryptedUint8List, 0); + + // Assert + Uint8List expectedDecryptedUint8List = Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + + expect(actualDecryptedUint8List, expectedDecryptedUint8List); + }); + }); +} diff --git a/test/encryption/cipher/block_cipher/padding/pkcs7_padding_test.dart b/test/encryption/cipher/block_cipher/padding/pkcs7_padding_test.dart new file mode 100644 index 00000000..b490b4f9 --- /dev/null +++ b/test/encryption/cipher/block_cipher/padding/pkcs7_padding_test.dart @@ -0,0 +1,70 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/invalid_length_exception.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/invalid_padding_value_exception.dart'; +import 'package:cryptography_utils/src/encryption/cipher/block_cipher/padding/pkcs7_padding.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +void main() { + group('Tests of Pkcs7Padding.addPadding()', () { + test('Should [return number of bytes added and padding] constructed from given data', () { + // Arrange + Uint8List actualPaddedUint8List = Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0]); + + // Act + int actualPaddedCode = Pkcs7Padding().addPadding(actualPaddedUint8List, 8); + + // Assert + int expectedPaddedCode = 8; + Uint8List expectedPaddedUint8List = Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8]); + + expect(actualPaddedCode, expectedPaddedCode); + expect(actualPaddedUint8List, expectedPaddedUint8List); + }); + }); + + group('Tests of Pkcs7Padding.countPadding()', () { + test('Should [return counted padding] constructed from given data', () { + // Arrange + Uint8List actualPaddedUint8List = Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8]); + + // Act + int actualCount = Pkcs7Padding().countPadding(actualPaddedUint8List); + + // Assert + int expectedCount = 8; + + expect(actualCount, expectedCount); + }); + + test('Should [return padding count] when last 7 bytes match', () { + // Arrange + Uint8List actualPadded = Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7]); + + // Act + int actualPaddedCounted = Pkcs7Padding().countPadding(actualPadded); + + // Assert + int expectedPaddedCounted = 7; + + expect(actualPaddedCounted, expectedPaddedCounted); + }); + + test('Should [throw InvalidLengthException] when trying to DECRYPT invalid ciphertext', () { + // Arrange + Uint8List actualUint8List = Uint8List.fromList([1, 2, 3, 5]); + + // Assert + expect(() => Pkcs7Padding().countPadding(actualUint8List), throwsA(isA())); + }); + + test('Should [throw InvalidPaddingValueException] when trying to DECRYPT invalid ciphertext', () { + // Arrange + Uint8List actualUint8List = Uint8List.fromList([1, 2, 3, 3, 2, 3]); + + // Assert + expect(() => Pkcs7Padding().countPadding(actualUint8List), throwsA(isA())); + }); + }); +} diff --git a/test/encryption/cipher/stream_cipher/ctr_stream_cipher_test.dart b/test/encryption/cipher/stream_cipher/ctr_stream_cipher_test.dart new file mode 100644 index 00000000..4e6d754c --- /dev/null +++ b/test/encryption/cipher/stream_cipher/ctr_stream_cipher_test.dart @@ -0,0 +1,111 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/encryption/aes/aes_engine.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_iv.dart'; +import 'package:cryptography_utils/src/encryption/aes/aes_key.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_key_with_iv.dart'; +import 'package:cryptography_utils/src/encryption/cipher/cipher_mode.dart'; +import 'package:cryptography_utils/src/encryption/cipher/stream_cipher/ctr_stream_cipher.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +// ignore_for_file: cascade_invocations +void main() { + group('Tests of CTRStreamCipher.processBytes()', () { + test('Should [return encrypted byte] constructed from given data', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])); + AESIV actualAESIV = AESIV(Uint8List.fromList([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + AESEngine actualBlockCipher = AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ); + + Uint8List inputUint8List = Uint8List.fromList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + CTRStreamCipher actualCTRStreamCipher = CTRStreamCipher(actualBlockCipher); + + // Act + Uint8List actualEncryptedUint8List = Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + actualCTRStreamCipher.processBytes(inputUint8List, 0, inputUint8List.length, actualEncryptedUint8List, 0); + + // Assert + Uint8List expectedEncryptedUint8List = Uint8List.fromList([32, 168, 251, 145, 176, 73, 93, 239, 12, 22]); + + expect(actualEncryptedUint8List, expectedEncryptedUint8List); + }); + + test('Should [return decrypted byte] constructed from encrypted data', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([21, 31, 41, 51, 61, 71, 81, 91, 101, 111, 121, 131, 141, 151, 161, 171])); + AESIV actualAESIV = AESIV(Uint8List.fromList([171, 161, 151, 141, 131, 121, 111, 101, 91, 81, 71, 61, 51, 41, 31, 21])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + AESEngine actualBlockCipher = AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ); + + Uint8List actualEncryptedUint8List = Uint8List.fromList([222, 109, 178, 62, 17, 88, 139, 243, 15, 200]); + CTRStreamCipher actualCTRStreamCipher = CTRStreamCipher(actualBlockCipher); + + // Act + Uint8List actualDecryptedUint8List = Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + actualCTRStreamCipher.processBytes(actualEncryptedUint8List, 0, actualEncryptedUint8List.length, actualDecryptedUint8List, 0); + + // Assert + Uint8List expectedDecryptedUint8List = Uint8List.fromList([197, 117, 153, 194, 246, 239, 37, 152, 158, 141]); + + expect(actualDecryptedUint8List, expectedDecryptedUint8List); + }); + + test('Should [return encrypted byte] constructed from partial bytes stream', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([5, 15, 25, 35, 45, 55, 65, 75, 85, 95, 105, 115, 125, 135, 145, 155])); + AESIV actualAESIV = AESIV(Uint8List.fromList([155, 145, 135, 125, 115, 105, 95, 85, 75, 65, 55, 45, 35, 25, 15, 5])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + AESEngine actualBlockCipher = AESEngine( + cipherMode: CipherMode.encryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ); + + Uint8List inputUint8List = Uint8List.fromList([0xDE, 0xAD, 0xBE]); + CTRStreamCipher actualCTRStreamCipher = CTRStreamCipher(actualBlockCipher); + + // Act + Uint8List actualEncryptedUint8List = Uint8List.fromList([0, 0, 0]); + actualCTRStreamCipher.processBytes(inputUint8List, 0, 3, actualEncryptedUint8List, 0); + + // Assert + Uint8List expectedEncryptedUint8List = Uint8List.fromList([51, 183, 159]); + + expect(actualEncryptedUint8List, expectedEncryptedUint8List); + }); + + test('Should [return decrypted byte] constructed from encrypted partial bytes stream', () { + // Arrange + AESKey actualAESKey = AESKey(Uint8List.fromList([6, 16, 26, 36, 46, 56, 66, 76, 86, 96, 106, 116, 126, 136, 146, 156])); + AESIV actualAESIV = AESIV(Uint8List.fromList([156, 146, 136, 126, 116, 106, 96, 86, 76, 66, 56, 46, 36, 26, 16, 6])); + CipherKeyWithIV actualCipherKeyWithIV = CipherKeyWithIV(actualAESKey, actualAESIV.uint8List); + + AESEngine actualBlockCipher = AESEngine( + cipherMode: CipherMode.decryption, + cipherKeyWithIV: actualCipherKeyWithIV, + ); + + Uint8List encryptedUint8List = Uint8List.fromList([211, 49, 27]); + CTRStreamCipher actualCTRStreamCipher = CTRStreamCipher(actualBlockCipher); + + // Act + Uint8List actualDecryptedUint8List = Uint8List.fromList([0, 0, 0]); + actualCTRStreamCipher.processBytes(encryptedUint8List, 0, 3, actualDecryptedUint8List, 0); + + // Assert + Uint8List expectedDecryptedUint8List = Uint8List.fromList([229, 128, 198]); + + expect(actualDecryptedUint8List, expectedDecryptedUint8List); + }); + }); +} diff --git a/test/utils/binary_utils_test.dart b/test/utils/binary_utils_test.dart index faf7b702..73b94f3f 100644 --- a/test/utils/binary_utils_test.dart +++ b/test/utils/binary_utils_test.dart @@ -76,6 +76,26 @@ void main() { }); }); + group('Tests for BinaryUtils.maskTo8Bits()', () { + test('Should [return value within 8-bit range] constructed from given data', () { + // Act + int actualValue = BinaryUtils.maskTo8Bits(0x7F); + + // Assert + int expectedValue = 0x7F; + expect(actualValue, expectedValue); + }); + + test('Should [return value within 8-bit range] constructed frm given data which is beyond 8-bits', () { + // Act + int actualValue = BinaryUtils.maskTo8Bits(0x1FF); + + // Assert + int expectedValue = 0xFF; + expect(actualValue, expectedValue); + }); + }); + group('Tests of BinaryUtils.intToBinary()', () { test('Should [return binary] from given int (default padding)', () { // Assert diff --git a/test/utils/int32_utils_test.dart b/test/utils/int32_utils_test.dart new file mode 100644 index 00000000..3ae8a254 --- /dev/null +++ b/test/utils/int32_utils_test.dart @@ -0,0 +1,160 @@ +import 'dart:typed_data'; + +import 'package:cryptography_utils/src/utils/int32_utils.dart'; +import 'package:cryptography_utils/src/utils/register64/register64.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +void main() { + group('Tests for Int32Utils.pack()', () { + test('Should [return value in big Endian] constructed from given data', () { + // Arrange + Uint8List buffer = Uint8List(8); + + // Act + Int32Utils.pack(0xAABBCCDD, buffer, 4, Endian.big); + Uint8List actualUint8List = buffer.sublist(4, 8); + + // Assert + Uint8List expectedUint8List = Uint8List.fromList([0xAA, 0xBB, 0xCC, 0xDD]); + + expect(actualUint8List, expectedUint8List); + }); + + test('Should [return value in little Endian] constructed from given data', () { + // Arrange + Uint8List buffer = Uint8List(4); + + // Act + Int32Utils.pack(0x12345678, buffer, 0, Endian.little); + Uint8List actualUint8List = buffer.sublist(0, 4); + + // Assert + Uint8List expectedUint8List = Uint8List.fromList([0x78, 0x56, 0x34, 0x12]); + + expect(actualUint8List, expectedUint8List); + }); + }); + + group('Tests for Int32Utils.shiftLeft()', () { + test('Should [return value] when shiftLeft() is called and shiftValue is different than zero', () { + // Act + int actualValue = Int32Utils.shiftLeft(0x12345678, 5); + + // Assert + int expectedValue = 0x468ACF00; + + expect(actualValue, equals(expectedValue)); + }); + + test('Should [return value] when shiftLeft() is called and shiftValue is equal to zero', () { + // Act + int actualValue = Int32Utils.shiftLeft(0x12345678, 0); + + // Assert + int expectedValue = 0x12345678; + + expect(actualValue, expectedValue); + }); + }); + + group('Tests for Int32Utils.shiftRight()', () { + test('Should [return value] when shiftRight() is called and shiftValue is different than zero', () { + // Act + int actualValue = Int32Utils.shiftRight(0x80000000, 1); + + // Assert + int expectedValue = 0x40000000; + + expect(actualValue, expectedValue); + }); + + test('Should [return value] when shiftRight() is called and shiftValue is equal to zero', () { + // Act + int actualValue = Int32Utils.shiftRight(0x80000000, 0); + + // Assert + int expectedValue = 0x80000000; + + expect(actualValue, expectedValue); + }); + }); + + group('Tests for Int32Utils.rotateLeft()', () { + test('Should [return value] when rotateLeft32Bits() is called and offset is equal to 0', () { + // Arrange + Register64 actualRegister64 = Register64(0x00000000, 0x12345678); + + // Act + int actualValue = Int32Utils.rotateLeft(actualRegister64.lowerHalf, 0); + + // Assert + int expectedValue = 0x12345678; + + expect(actualValue, expectedValue); + }); + + test('Should [return value] when rotateLeft() is called and offset is different than 0', () { + // Arrange + Register64 actualRegister64 = Register64(0x00000000, 0x12345678); + + // Act + int actualValue = Int32Utils.rotateLeft(actualRegister64.lowerHalf, 40); + + // Assert + int expectedValue = 0x34567812; + + expect(actualValue, expectedValue); + }); + }); + + group('Tests for Int32Utils.rotateRight()', () { + test('Should [return value] when rotateRight() is called and offset is different than 0', () { + // Act + int actualValue = Int32Utils.rotateRight(0x12345678, 8); + + // Assert + int expectedValue = 0x78123456; + + expect(actualValue, expectedValue); + }); + + test('Should [return value] when rotateRight() is called and offset is equal to 0', () { + // Act + int actualValue = Int32Utils.rotateRight(0x12345678, 0); + + // Assert + int expectedValue = 0x12345678; + + expect(actualValue, expectedValue); + }); + }); + + group('Tests for Int32Utils.unpack()', () { + test('Should [return value in little Endian] constructed from given data', () { + // Arrange + Uint8List inputBytes = Uint8List.fromList([0x78, 0x56, 0x34, 0x12]); + + // Act + int actualValue = Int32Utils.unpack(inputBytes, 0, Endian.little); + + // Assert + int expectedValue = 0x12345678; + + expect(actualValue, expectedValue); + }); + + test('Should [return value in big Endian] constructed from given data', () { + // Arrange + Uint8List inputBytes = Uint8List.fromList([0x12, 0x34, 0x56, 0x78]); + + // Act + int actualValue = Int32Utils.unpack(inputBytes, 0, Endian.big); + + // Assert + int expectedValue = 0x12345678; + + expect(actualValue, expectedValue); + }); + }); +} diff --git a/test/utils/register64/register64_test.dart b/test/utils/register64/register64_test.dart index 2e442c17..bc150c8d 100644 --- a/test/utils/register64/register64_test.dart +++ b/test/utils/register64/register64_test.dart @@ -34,42 +34,6 @@ void main() { }); }); - group('Tests for Register64.setInt()', () { - test('Should [return values] when set is called WITH one parameter.', () { - // Arrange - Register64 actualRegister64 = Register64(); - - // Act - actualRegister64.setInt(0x2A); - int actualUpperValue = actualRegister64.upperHalf; - int actualLowerValue = actualRegister64.lowerHalf; - - // Assert - int expectedUpperValue = 0x0; - int expectedLowerValue = 0x2A; - - expect(actualUpperValue, expectedUpperValue); - expect(actualLowerValue, expectedLowerValue); - }); - - test('Should [return values] when set is called WITH two parameters.', () { - // Arrange - Register64 actualRegister64 = Register64(); - - // Act - actualRegister64.setInt(0x12345678, 0x9ABCDEF0); - int actualUpperValue = actualRegister64.upperHalf; - int actualLowerValue = actualRegister64.lowerHalf; - - // Assert - int expectedUpperValue = 0x12345678; - int expectedLowerValue = 0x9ABCDEF0; - - expect(actualUpperValue, expectedUpperValue); - expect(actualLowerValue, expectedLowerValue); - }); - }); - group('Tests for Register64.setRegister64()', () { test('Should [return values] when set is called.', () { // Arrange @@ -111,6 +75,26 @@ void main() { }); }); + group('Tests for Register64.performNot()', () { + test('Should [return result of logical NOT operation] when performNot() called', () { + // Arrange + Register64 actualRegister64 = Register64(0x12345678, 0x9ABCDEF0); + + // Act + actualRegister64.performNot(); + + int actualUpperValue = actualRegister64.upperHalf; + int actualLowerValue = actualRegister64.lowerHalf; + + // Assert + int expectedUpperValue = 0xEDCBA987; + int expectedLowerValue = 0x6543210F; + + expect(actualUpperValue, expectedUpperValue); + expect(actualLowerValue, expectedLowerValue); + }); + }); + group('Tests for Register64.performOr()', () { test('Should [return result of logical OR operation] when performOr() called', () { // Arrange @@ -152,20 +136,36 @@ void main() { }); }); - group('Tests for Register64.performNot()', () { - test('Should [return result of logical NOT operation] when performNot() called', () { + group('Tests for Register64.setInt()', () { + test('Should [return values] when set is called WITH one parameter.', () { // Arrange - Register64 actualRegister64 = Register64(0x12345678, 0x9ABCDEF0); + Register64 actualRegister64 = Register64(); // Act - actualRegister64.performNot(); + actualRegister64.setInt(0x2A); + int actualUpperValue = actualRegister64.upperHalf; + int actualLowerValue = actualRegister64.lowerHalf; + // Assert + int expectedUpperValue = 0x0; + int expectedLowerValue = 0x2A; + + expect(actualUpperValue, expectedUpperValue); + expect(actualLowerValue, expectedLowerValue); + }); + + test('Should [return values] when set is called WITH two parameters.', () { + // Arrange + Register64 actualRegister64 = Register64(); + + // Act + actualRegister64.setInt(0x12345678, 0x9ABCDEF0); int actualUpperValue = actualRegister64.upperHalf; int actualLowerValue = actualRegister64.lowerHalf; // Assert - int expectedUpperValue = 0xEDCBA987; - int expectedLowerValue = 0x6543210F; + int expectedUpperValue = 0x12345678; + int expectedLowerValue = 0x9ABCDEF0; expect(actualUpperValue, expectedUpperValue); expect(actualLowerValue, expectedLowerValue); @@ -190,6 +190,7 @@ void main() { expect(actualUpperValue, expectedUpperValue); expect(actualLowerValue, expectedLowerValue); }); + test('Should [return result shifted] when shiftLeft() is called and shiftValue is different than zero', () { // Arrange Register64 actualRegister64 = Register64(0x00000000, 0x9ABCDEF0); @@ -227,6 +228,7 @@ void main() { expect(actualUpperValue, expectedUpperValue); expect(actualLowerValue, expectedLowerValue); }); + test('Should [return result shifted by the shiftedValue] when shiftRight() is called and shiftValue is different than zero', () { // Arrange Register64 actualRegister64 = Register64(0x80000000, 0x00000000); @@ -246,33 +248,6 @@ void main() { }); }); - group('Tests for Register64.rotateLeft32Bits()', () { - test('Should [return value] when rotateLeft32Bits() is called and offset is equal to 0', () { - // Arrange - Register64 actualRegister64 = Register64(0x00000000, 0x12345678); - - // Act - int actualValue = actualRegister64.rotateLeft32Bits(actualRegister64.lowerHalf, 0); - - // Assert - int expectedValue = 0x12345678; - - expect(actualValue, expectedValue); - }); - - test('Should [return value] when rotateLeft32Bits() is called and offset is different than 0', () { - // Arrange - Register64 actualRegister64 = Register64(0x00000000, 0x12345678); - - // Act - int actualValue = actualRegister64.rotateLeft32Bits(actualRegister64.lowerHalf, 40); - - // Assert - int expectedValue = 0x34567812; - - expect(actualValue, expectedValue); - }); - }); group('Tests for Register64.sumInt()', () { test('Should [return lowerHalf] when sumInt() is called and Chunk32Bits is equal to 0', () { // Arrange