From cf74276f7f6b26b1582957c05a94f25e0ef3ca06 Mon Sep 17 00:00:00 2001 From: Murali Date: Wed, 4 Dec 2024 16:27:47 +0530 Subject: [PATCH 01/13] feat: better crypto AES impl --- .../at_chops/example/at_chops_example.dart | 6 +- .../lib/src/algorithm/aes_ctr_factory.dart | 19 ++++ .../src/algorithm/aes_encryption_algo.dart | 47 +++++--- .../lib/src/algorithm/at_algorithm.dart | 8 +- .../lib/src/algorithm/padding/padding.dart | 6 + .../src/algorithm/padding/padding_params.dart | 3 + .../src/algorithm/padding/pkcs7padding.dart | 51 +++++++++ packages/at_chops/lib/src/at_chops_base.dart | 9 +- packages/at_chops/lib/src/at_chops_impl.dart | 29 +++-- .../at_chops/lib/src/key/impl/aes_key.dart | 9 ++ packages/at_chops/pubspec.yaml | 3 +- .../at_chops/test/aes_encrption_old_impl.dart | 103 ++++++++++++++++++ .../test/aes_encryption_algo_test.dart | 71 ++++++++++++ packages/at_chops/test/aes_key_test.dart | 27 +++++ packages/at_chops/test/at_chops_test.dart | 51 ++++----- .../at_chops/test/pkcs7_padding_test.dart | 82 ++++++++++++++ 16 files changed, 462 insertions(+), 62 deletions(-) create mode 100644 packages/at_chops/lib/src/algorithm/aes_ctr_factory.dart create mode 100644 packages/at_chops/lib/src/algorithm/padding/padding.dart create mode 100644 packages/at_chops/lib/src/algorithm/padding/padding_params.dart create mode 100644 packages/at_chops/lib/src/algorithm/padding/pkcs7padding.dart create mode 100644 packages/at_chops/test/aes_encrption_old_impl.dart create mode 100644 packages/at_chops/test/aes_encryption_algo_test.dart create mode 100644 packages/at_chops/test/pkcs7_padding_test.dart diff --git a/packages/at_chops/example/at_chops_example.dart b/packages/at_chops/example/at_chops_example.dart index f540670b..cba53060 100644 --- a/packages/at_chops/example/at_chops_example.dart +++ b/packages/at_chops/example/at_chops_example.dart @@ -41,11 +41,11 @@ void main(List args) async { final data = 'Hello World'; //1.1 encrypt the data using [atEncryptionKeyPair.publicKey] final encryptionResult = - atChops.encryptString(data, EncryptionKeyType.rsa2048); + await atChops.encryptString(data, EncryptionKeyType.rsa2048); //1.2 decrypt the data using [atEncryptionKeyPair.privateKey] - final decryptionResult = - atChops.decryptString(encryptionResult.result, EncryptionKeyType.rsa2048); + final decryptionResult = await atChops.decryptString( + encryptionResult.result, EncryptionKeyType.rsa2048); assert(data == decryptionResult.result, true); // 2 - Signing and data verification using asymmetric key pair diff --git a/packages/at_chops/lib/src/algorithm/aes_ctr_factory.dart b/packages/at_chops/lib/src/algorithm/aes_ctr_factory.dart new file mode 100644 index 00000000..f8c16aee --- /dev/null +++ b/packages/at_chops/lib/src/algorithm/aes_ctr_factory.dart @@ -0,0 +1,19 @@ +import 'package:at_chops/at_chops.dart'; +import 'package:at_commons/at_commons.dart'; +import 'package:better_cryptography/better_cryptography.dart'; + +class AesCtrFactory { + static AesCtr createEncryptionAlgo(AESKey aesKey) { + switch (aesKey.getLength()) { + case 16: + return AesCtr.with128bits(macAlgorithm: MacAlgorithm.empty); + case 24: + return AesCtr.with192bits(macAlgorithm: MacAlgorithm.empty); + case 32: + return AesCtr.with256bits(macAlgorithm: MacAlgorithm.empty); + default: + throw AtEncryptionException( + 'Invalid AES key length. Valid lengths are 16/24/32 bytes'); + } + } +} diff --git a/packages/at_chops/lib/src/algorithm/aes_encryption_algo.dart b/packages/at_chops/lib/src/algorithm/aes_encryption_algo.dart index 06c42f8f..8524e189 100644 --- a/packages/at_chops/lib/src/algorithm/aes_encryption_algo.dart +++ b/packages/at_chops/lib/src/algorithm/aes_encryption_algo.dart @@ -1,32 +1,53 @@ +import 'dart:async'; import 'dart:typed_data'; - +import 'dart:convert'; +import 'package:at_chops/src/algorithm/aes_ctr_factory.dart'; +import 'package:at_chops/src/algorithm/padding/padding.dart'; import 'package:at_chops/at_chops.dart'; import 'package:at_chops/src/algorithm/at_algorithm.dart'; +import 'package:at_chops/src/algorithm/padding/padding_params.dart'; +import 'package:at_chops/src/algorithm/padding/pkcs7padding.dart'; import 'package:at_commons/at_commons.dart'; import 'package:encrypt/encrypt.dart'; +import 'package:better_cryptography/better_cryptography.dart'; /// A class that provides AES encryption and decryption for Uint8List, /// implementing the [SymmetricEncryptionAlgorithm] interface. class AESEncryptionAlgo implements SymmetricEncryptionAlgorithm { final AESKey _aesKey; - - AESEncryptionAlgo(this._aesKey); + PaddingAlgorithm? paddingAlgo; + AESEncryptionAlgo(this._aesKey) { + paddingAlgo ??= PKCS7Padding(PaddingParams()..blockSize = 16); + } @override - Uint8List encrypt(Uint8List plainData, {InitialisationVector? iv}) { - var aesEncrypter = Encrypter(AES(Key.fromBase64(_aesKey.key))); - final encrypted = - aesEncrypter.encryptBytes(plainData, iv: _getIVFromBytes(iv?.ivBytes)); - return encrypted.bytes; + FutureOr encrypt(Uint8List plainData, + {InitialisationVector? iv}) async { + final encryptionAlgo = AesCtrFactory.createEncryptionAlgo(_aesKey); + var paddedData = paddingAlgo!.addPadding(plainData); + final secretKey = await encryptionAlgo + .newSecretKeyFromBytes(base64Decode(_aesKey.toString())); + var secretBox = await encryptionAlgo.encrypt(paddedData, + nonce: _getIVFromBytes(iv?.ivBytes)!.bytes, secretKey: secretKey); + return Uint8List.fromList(secretBox.cipherText); } @override - Uint8List decrypt(Uint8List encryptedData, {InitialisationVector? iv}) { - var aesKey = AES(Key.fromBase64(_aesKey.toString())); - var decrypter = Encrypter(aesKey); - return Uint8List.fromList(decrypter.decryptBytes(Encrypted(encryptedData), - iv: _getIVFromBytes(iv?.ivBytes))); + FutureOr decrypt(Uint8List encryptedData, + {InitialisationVector? iv}) async { + final encryptionAlgo = AesCtrFactory.createEncryptionAlgo(_aesKey); + var secretBox = SecretBox( + encryptedData, + nonce: _getIVFromBytes(iv?.ivBytes)!.bytes, + mac: Mac.empty, + ); + final secretKey = await encryptionAlgo + .newSecretKeyFromBytes(base64Decode(_aesKey.toString())); + var decryptedBytesWithPadding = + await encryptionAlgo.decrypt(secretBox, secretKey: secretKey); + var decryptedBytes = paddingAlgo!.removePadding(decryptedBytesWithPadding); + return Uint8List.fromList(decryptedBytes); } IV? _getIVFromBytes(Uint8List? ivBytes) { diff --git a/packages/at_chops/lib/src/algorithm/at_algorithm.dart b/packages/at_chops/lib/src/algorithm/at_algorithm.dart index f41a6d71..d3e7bde8 100644 --- a/packages/at_chops/lib/src/algorithm/at_algorithm.dart +++ b/packages/at_chops/lib/src/algorithm/at_algorithm.dart @@ -11,20 +11,20 @@ import 'package:at_chops/src/model/hash_params.dart'; /// Interface for encrypting and decrypting data. Check [DefaultEncryptionAlgo] for sample implementation. abstract class AtEncryptionAlgorithm { /// Encrypts the passed bytes. Bytes are passed as [Uint8List]. Encode String data type to [Uint8List] using [utf8.encode]. - V encrypt(T plainData); + FutureOr encrypt(T plainData); /// Decrypts the passed encrypted bytes. - V decrypt(T encryptedData); + FutureOr decrypt(T encryptedData); } /// Interface for symmetric encryption algorithms. Check [AESEncryptionAlgo] for sample implementation. abstract class SymmetricEncryptionAlgorithm extends AtEncryptionAlgorithm { @override - V encrypt(T plainData, {InitialisationVector iv}); + FutureOr encrypt(T plainData, {InitialisationVector iv}); @override - V decrypt(T encryptedData, {InitialisationVector iv}); + FutureOr decrypt(T encryptedData, {InitialisationVector iv}); } /// Interface for asymmetric encryption algorithms. Check [DefaultEncryptionAlgo] for sample implementation. diff --git a/packages/at_chops/lib/src/algorithm/padding/padding.dart b/packages/at_chops/lib/src/algorithm/padding/padding.dart new file mode 100644 index 00000000..dd860159 --- /dev/null +++ b/packages/at_chops/lib/src/algorithm/padding/padding.dart @@ -0,0 +1,6 @@ +import 'package:at_chops/src/algorithm/padding/padding_params.dart'; + +abstract class PaddingAlgorithm { + List addPadding(List data); + List removePadding(List data); +} diff --git a/packages/at_chops/lib/src/algorithm/padding/padding_params.dart b/packages/at_chops/lib/src/algorithm/padding/padding_params.dart new file mode 100644 index 00000000..e2206aa4 --- /dev/null +++ b/packages/at_chops/lib/src/algorithm/padding/padding_params.dart @@ -0,0 +1,3 @@ +class PaddingParams { + int? blockSize; +} diff --git a/packages/at_chops/lib/src/algorithm/padding/pkcs7padding.dart b/packages/at_chops/lib/src/algorithm/padding/pkcs7padding.dart new file mode 100644 index 00000000..cc74c41c --- /dev/null +++ b/packages/at_chops/lib/src/algorithm/padding/pkcs7padding.dart @@ -0,0 +1,51 @@ +import 'package:at_chops/src/algorithm/padding/padding.dart'; +import 'package:at_chops/src/algorithm/padding/padding_params.dart'; +import 'package:at_commons/at_commons.dart'; + +class PKCS7Padding implements PaddingAlgorithm { + final PaddingParams _paddingParams; + PKCS7Padding(this._paddingParams); + @override + List addPadding(List data) { + if (_paddingParams.blockSize == null || + _paddingParams.blockSize! <= 0 || + _paddingParams.blockSize! > 255) { + throw AtEncryptionException('Block size must be between 1 and 255.'); + } + + // Calculate the number of padding bytes needed + int padding = + _paddingParams.blockSize! - (data.length % _paddingParams.blockSize!); + + // Add padding bytes to the data + List paddedData = List.from(data); + paddedData.addAll(List.filled(padding, padding)); + + return paddedData; + } + + @override + List removePadding(List data) { + if (data.isEmpty) { + throw AtDecryptionException('Encrypted data cannot be empty'); + } + + // Get the value of the last byte (padding length) + int paddingLength = data.last; + + // Validate padding length + if (paddingLength <= 0 || paddingLength > data.length) { + throw AtDecryptionException('Invalid padding length'); + } + + // Check if all padding bytes are valid + for (int i = data.length - paddingLength; i < data.length; i++) { + if (data[i] != paddingLength) { + throw AtDecryptionException('Invalid PKCS7 padding'); + } + } + + // Return the data without padding + return data.sublist(0, data.length - paddingLength); + } +} diff --git a/packages/at_chops/lib/src/at_chops_base.dart b/packages/at_chops/lib/src/at_chops_base.dart index 02bb2f1a..19430ed4 100644 --- a/packages/at_chops/lib/src/at_chops_base.dart +++ b/packages/at_chops/lib/src/at_chops_base.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; @@ -37,7 +38,7 @@ abstract class AtChops { /// If [encryptionKeyType] is [EncryptionKeyType.rsa2048] then [encryptionAlgorithm] will be set to [RsaEncryptionAlgo] /// [keyName] specifies which key pair to use if user has multiple key pairs configured. /// If [keyName] is not passed default encryption/decryption keypair from .atKeys file will be used. - AtEncryptionResult encryptBytes( + FutureOr encryptBytes( Uint8List data, EncryptionKeyType encryptionKeyType, {AtEncryptionAlgorithm? encryptionAlgorithm, String? keyName, @@ -47,7 +48,7 @@ abstract class AtChops { /// If [encryptionKeyType] is [EncryptionKeyType.rsa2048] then [encryptionAlgorithm] will be set to [RsaEncryptionAlgo] /// [keyName] specifies which key pair to use if user has multiple key pairs configured. /// If [keyName] is not passed default encryption/decryption keypair from .atKeys file will be used. - AtEncryptionResult encryptString( + FutureOr encryptString( String data, EncryptionKeyType encryptionKeyType, {AtEncryptionAlgorithm? encryptionAlgorithm, String? keyName, @@ -57,7 +58,7 @@ abstract class AtChops { /// If [encryptionKeyType] is [EncryptionKeyType.rsa2048] then [encryptionAlgorithm] will be set to [RsaEncryptionAlgo] /// [keyName] specifies which key pair to use if user has multiple key pairs configured. /// If [keyName] is not passed default encryption/decryption keypair from .atKeys file will be used. - AtEncryptionResult decryptBytes( + FutureOr decryptBytes( Uint8List data, EncryptionKeyType encryptionKeyType, {AtEncryptionAlgorithm? encryptionAlgorithm, String? keyName, @@ -67,7 +68,7 @@ abstract class AtChops { /// If [encryptionKeyType] is [EncryptionKeyType.rsa2048] then [encryptionAlgorithm] will be set to [RsaEncryptionAlgo] /// [keyName] specifies which key pair to use if user has multiple key pairs configured. /// If [keyName] is not passed default encryption/decryption keypair from .atKeys file will be used. - AtEncryptionResult decryptString( + FutureOr decryptString( String data, EncryptionKeyType encryptionKeyType, {AtEncryptionAlgorithm? encryptionAlgorithm, String? keyName, diff --git a/packages/at_chops/lib/src/at_chops_impl.dart b/packages/at_chops/lib/src/at_chops_impl.dart index 55b18450..5e0f215f 100644 --- a/packages/at_chops/lib/src/at_chops_impl.dart +++ b/packages/at_chops/lib/src/at_chops_impl.dart @@ -1,5 +1,6 @@ // ignore_for_file: unnecessary_cast +import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; @@ -9,6 +10,8 @@ import 'package:at_chops/src/algorithm/at_algorithm.dart'; import 'package:at_chops/src/algorithm/at_iv.dart'; import 'package:at_chops/src/algorithm/default_signing_algo.dart'; import 'package:at_chops/src/algorithm/ecc_signing_algo.dart'; +import 'package:at_chops/src/algorithm/padding/padding_params.dart'; +import 'package:at_chops/src/algorithm/padding/pkcs7padding.dart'; import 'package:at_chops/src/algorithm/pkam_signing_algo.dart'; import 'package:at_chops/src/algorithm/rsa_encryption_algo.dart'; import 'package:at_chops/src/at_chops_base.dart'; @@ -33,11 +36,11 @@ class AtChopsImpl extends AtChops { final AtSignLogger _logger = AtSignLogger('AtChopsImpl'); @override - AtEncryptionResult decryptBytes( + FutureOr decryptBytes( Uint8List data, EncryptionKeyType encryptionKeyType, {AtEncryptionAlgorithm? encryptionAlgorithm, String? keyName, - InitialisationVector? iv}) { + InitialisationVector? iv}) async { try { encryptionAlgorithm ??= _getEncryptionAlgorithm(encryptionKeyType, keyName)!; @@ -52,7 +55,8 @@ class AtChopsImpl extends AtChops { ..atEncryptionMetaData = atEncryptionMetaData ..atEncryptionResultType = AtEncryptionResultType.bytes; if (encryptionAlgorithm is SymmetricEncryptionAlgorithm) { - atEncryptionResult.result = encryptionAlgorithm.decrypt(data, iv: iv!); + atEncryptionResult.result = + await encryptionAlgorithm.decrypt(data, iv: iv!); atEncryptionMetaData.iv = iv; } else { atEncryptionResult.result = encryptionAlgorithm.decrypt(data); @@ -70,13 +74,13 @@ class AtChopsImpl extends AtChops { /// Decode the encrypted string to base64. /// Decode the encrypted byte to utf8 to support emoji chars. @override - AtEncryptionResult decryptString( + FutureOr decryptString( String data, EncryptionKeyType encryptionKeyType, {AtEncryptionAlgorithm? encryptionAlgorithm, String? keyName, - InitialisationVector? iv}) { + InitialisationVector? iv}) async { try { - final decryptionResult = decryptBytes( + final decryptionResult = await decryptBytes( base64Decode(data), encryptionKeyType, encryptionAlgorithm: encryptionAlgorithm, keyName: keyName, iv: iv); final atEncryptionResult = AtEncryptionResult() @@ -90,11 +94,11 @@ class AtChopsImpl extends AtChops { } @override - AtEncryptionResult encryptBytes( + FutureOr encryptBytes( Uint8List data, EncryptionKeyType encryptionKeyType, {AtEncryptionAlgorithm? encryptionAlgorithm, String? keyName, - InitialisationVector? iv}) { + InitialisationVector? iv}) async { try { encryptionAlgorithm ??= _getEncryptionAlgorithm(encryptionKeyType, keyName)!; @@ -105,7 +109,8 @@ class AtChopsImpl extends AtChops { ..atEncryptionMetaData = atEncryptionMetaData ..atEncryptionResultType = AtEncryptionResultType.bytes; if (encryptionAlgorithm is SymmetricEncryptionAlgorithm) { - atEncryptionResult.result = encryptionAlgorithm.encrypt(data, iv: iv!); + atEncryptionResult.result = + await encryptionAlgorithm.encrypt(data, iv: iv!); atEncryptionMetaData.iv = iv; } else { atEncryptionResult.result = encryptionAlgorithm.encrypt(data); @@ -123,14 +128,14 @@ class AtChopsImpl extends AtChops { /// Encode the input string to utf8 to support emoji chars. /// Encode the encrypted bytes to base64. @override - AtEncryptionResult encryptString( + FutureOr encryptString( String data, EncryptionKeyType encryptionKeyType, {AtEncryptionAlgorithm? encryptionAlgorithm, String? keyName, - InitialisationVector? iv}) { + InitialisationVector? iv}) async { try { final utfEncodedData = utf8.encode(data); - final encryptionResult = encryptBytes( + final encryptionResult = await encryptBytes( Uint8List.fromList(utfEncodedData), encryptionKeyType, keyName: keyName, encryptionAlgorithm: encryptionAlgorithm, iv: iv); final atEncryptionResult = AtEncryptionResult() diff --git a/packages/at_chops/lib/src/key/impl/aes_key.dart b/packages/at_chops/lib/src/key/impl/aes_key.dart index 1d066a38..cc6c2ec0 100644 --- a/packages/at_chops/lib/src/key/impl/aes_key.dart +++ b/packages/at_chops/lib/src/key/impl/aes_key.dart @@ -1,5 +1,6 @@ import 'package:at_chops/src/key/at_key_pair.dart'; import 'package:encrypt/encrypt.dart'; +import 'dart:convert'; /// Represents an AES key for symmetric encryption. class AESKey extends SymmetricKey { @@ -15,6 +16,14 @@ class AESKey extends SymmetricKey { return AESKey(aesKey.key.base64); } + /// Returns the key length in bytes. + /// e.g for 128 bit key length will be 16 + /// for 192 bit key length will be 24 + /// for 256 bit key length will be 32 + int getLength() { + return base64.decode(_aesKey).length; + } + @override String toString() { return _aesKey; diff --git a/packages/at_chops/pubspec.yaml b/packages/at_chops/pubspec.yaml index 2999b53a..2051008d 100644 --- a/packages/at_chops/pubspec.yaml +++ b/packages/at_chops/pubspec.yaml @@ -14,10 +14,11 @@ dependencies: ecdsa: ^0.1.0 dart_periphery: ^0.9.5 elliptic: ^0.3.10 - pointycastle: ^3.7.4 + pointycastle: ^3.9.1 at_commons: ^5.0.2 at_utils: ^3.0.19 cryptography: ^2.7.0 + better_cryptography: ^1.0.0+1 dev_dependencies: lints: ^5.0.0 diff --git a/packages/at_chops/test/aes_encrption_old_impl.dart b/packages/at_chops/test/aes_encrption_old_impl.dart new file mode 100644 index 00000000..6ad7b0ef --- /dev/null +++ b/packages/at_chops/test/aes_encrption_old_impl.dart @@ -0,0 +1,103 @@ +import 'dart:typed_data'; + +import 'package:at_chops/at_chops.dart'; +import 'package:at_chops/src/algorithm/at_algorithm.dart'; +import 'package:at_commons/at_commons.dart'; +import 'package:encrypt/encrypt.dart'; + +/// Old AES implementation. Used for testing backward compatibility. +class AESEncryptionAlgoV1 + implements SymmetricEncryptionAlgorithm { + final AESKey _aesKey; + + AESEncryptionAlgoV1(this._aesKey); + + @override + Uint8List encrypt(Uint8List plainData, {InitialisationVector? iv}) { + var aesEncrypter = Encrypter(AES(Key.fromBase64(_aesKey.key))); + final encrypted = + aesEncrypter.encryptBytes(plainData, iv: _getIVFromBytes(iv?.ivBytes)); + return encrypted.bytes; + } + + @override + Uint8List decrypt(Uint8List encryptedData, {InitialisationVector? iv}) { + var aesKey = AES(Key.fromBase64(_aesKey.toString())); + var decrypter = Encrypter(aesKey); + return Uint8List.fromList(decrypter.decryptBytes(Encrypted(encryptedData), + iv: _getIVFromBytes(iv?.ivBytes))); + } + + IV? _getIVFromBytes(Uint8List? ivBytes) { + if (ivBytes != null) { + return IV(ivBytes); + } + // From the bad old days when we weren't setting IVs + return IV(Uint8List(16)); + } +} + +/// A class that provides AES encryption and decryption for strings, +/// implementing the [SymmetricEncryptionAlgorithm] interface. +/// +/// This class uses an [AESKey] to perform encryption and decryption of strings. +/// The key and an [InitialisationVector] (IV) are used for encryption, and the +/// same key must be used for decryption. +class StringAESEncryptor + implements SymmetricEncryptionAlgorithm { + /// The AES key used for encryption and decryption. + final AESKey _aesKey; + + /// Constructs an instance of [StringAESEncryptor] with the provided [_aesKey]. + /// + /// [_aesKey]: The key used for AES encryption and decryption, represented + /// in Base64 format. + StringAESEncryptor(this._aesKey); + + /// Decrypts the given [encryptedData] using the provided [iv] (Initialisation Vector). + /// + /// The [iv] used for encryption must be the same for decryption. If [iv] is + /// not provided, an [AtDecryptionException] will be thrown, as the IV is + /// mandatory for the AES decryption process. + /// + /// - [encryptedData]: The Base64-encoded string that represents the encrypted data. + /// - [iv]: The Initialisation Vector used during decryption. Must be the same + /// IV that was used to encrypt the data. + /// + /// Returns a [String] that represents the decrypted data. + /// + /// Throws an [AtDecryptionException] if the [iv] is missing. + + @override + String decrypt(String encryptedData, {InitialisationVector? iv}) { + // The IV used for encryption, the same IV must be used for decryption. + if (iv == null) { + throw AtDecryptionException( + 'Initialisation Vector (IV) is required for decryption'); + } + var aesEncrypter = Encrypter(AES(Key.fromBase64(_aesKey.key))); + return aesEncrypter.decrypt(Encrypted.fromBase64(encryptedData), + iv: IV(iv.ivBytes)); + } + + /// Encrypts the given [plainData] using AES encryption and an optional [iv]. + /// The resulting encrypted data will be Base64-encoded. + /// + /// - [plainData]: The string that needs to be encrypted. + /// - [iv]: The Initialisation Vector used for encryption. If not provided, + /// AtEncryptionException will be thrown. + /// + /// Returns a [String] that contains the encrypted data, encoded in Base64 format. + /// + /// Throws an [AtEncryptionException] if the [iv] is missing. + @override + String encrypt(String plainData, {InitialisationVector? iv}) { + if (iv == null) { + throw AtEncryptionException( + 'Initialisation Vector (IV) is required for encryption'); + } + var aesEncrypter = Encrypter(AES(Key.fromBase64(_aesKey.key))); + final encrypted = aesEncrypter.encrypt(plainData, iv: IV(iv.ivBytes)); + return encrypted.base64; + } +} diff --git a/packages/at_chops/test/aes_encryption_algo_test.dart b/packages/at_chops/test/aes_encryption_algo_test.dart new file mode 100644 index 00000000..b5138829 --- /dev/null +++ b/packages/at_chops/test/aes_encryption_algo_test.dart @@ -0,0 +1,71 @@ +import 'dart:convert'; +import 'package:at_chops/at_chops.dart'; +import 'package:test/test.dart'; +import 'aes_encrption_old_impl.dart'; + +void main() { + group( + 'A group of tests to verify compatibility between old AES algo vs better crypto AES algo', + () { + test('Encrypt with old algo and decrypt with better crypto', () async { + var data = 'Hello World'; + var aesKey = AESKey.generate(32); + var iv = AtChopsUtil.generateRandomIV(16); + final encryptionAlgo = AESEncryptionAlgoV1(aesKey); + var encryptedBytes = encryptionAlgo.encrypt(utf8.encode(data), iv: iv); + var betterCryptoAESAlgo = AESEncryptionAlgo(aesKey); + var decryptedBytes = + await betterCryptoAESAlgo.decrypt(encryptedBytes, iv: iv); + expect(utf8.decode(decryptedBytes), data); + }); + test('Encrypt with better crypto AES algo and decrypt with old algo', + () async { + var data = 'Hello World12345'; + var aesKey = AESKey.generate(32); + var iv = AtChopsUtil.generateRandomIV(16); + final betterCryptoAESAlgo = AESEncryptionAlgo(aesKey); + var encryptedBytes = + await betterCryptoAESAlgo.encrypt(utf8.encode(data), iv: iv); + var oldAlgo = AESEncryptionAlgoV1(aesKey); + var decryptedBytes = oldAlgo.decrypt(encryptedBytes, iv: iv); + expect(utf8.decode(decryptedBytes), data); + }); + }); + group( + 'A group of tests to verify AES encryption decryption with different key lengths', + () { + test('Test encryption and decryption for 128 bit AES key', () async { + var data = 'Hello World🛠'; + var aesKey = AESKey.generate(16); + var iv = AtChopsUtil.generateRandomIV(16); + final betterCryptoAESAlgo = AESEncryptionAlgo(aesKey); + var encryptedBytes = + await betterCryptoAESAlgo.encrypt(utf8.encode(data), iv: iv); + var decryptedBytes = + await betterCryptoAESAlgo.decrypt(encryptedBytes, iv: iv); + expect(utf8.decode(decryptedBytes), data); + }); + test('Test encryption and decryption for 192 bit AES key', () async { + var data = 'Hello\nWorld🛠\n123asdasd!@&^'; + var aesKey = AESKey.generate(24); + var iv = AtChopsUtil.generateRandomIV(16); + final betterCryptoAESAlgo = AESEncryptionAlgo(aesKey); + var encryptedBytes = + await betterCryptoAESAlgo.encrypt(utf8.encode(data), iv: iv); + var decryptedBytes = + await betterCryptoAESAlgo.decrypt(encryptedBytes, iv: iv); + expect(utf8.decode(decryptedBytes), data); + }); + test('Test encryption and decryption for 256 bit AES key', () async { + var data = '🛠Hello\nWorld🛠\n123asdasd!@&^\'🛠'; + var aesKey = AESKey.generate(32); + var iv = AtChopsUtil.generateRandomIV(16); + final betterCryptoAESAlgo = AESEncryptionAlgo(aesKey); + var encryptedBytes = + await betterCryptoAESAlgo.encrypt(utf8.encode(data), iv: iv); + var decryptedBytes = + await betterCryptoAESAlgo.decrypt(encryptedBytes, iv: iv); + expect(utf8.decode(decryptedBytes), data); + }); + }); +} diff --git a/packages/at_chops/test/aes_key_test.dart b/packages/at_chops/test/aes_key_test.dart index b2cc3e1f..76e58d11 100644 --- a/packages/at_chops/test/aes_key_test.dart +++ b/packages/at_chops/test/aes_key_test.dart @@ -22,5 +22,32 @@ void main() { final aesKey_2 = AESKey.generate(32); expect(aesKey_1, isNot(aesKey_2)); }); + test('check random key generated length for 128 bit key', () { + final aesKey = AESKey.generate(16); + expect(aesKey.getLength(), 16); + }); + test('check random key generated length for 192 bit key', () { + final aesKey = AESKey.generate(24); + expect(aesKey.getLength(), 24); + }); + test('check random key generated length for 256 bit key', () { + final aesKey = AESKey.generate(32); + expect(aesKey.getLength(), 32); + }); + test('verify key length for 256 bit key constructed from string', () { + final aesKey_1 = AESKey.generate(32); + final aesKey = AESKey(aesKey_1.key); + expect(aesKey.getLength(), 32); + }); + test('verify key length for 192 bit key constructed from string', () { + final aesKey_1 = AESKey.generate(24); + final aesKey = AESKey(aesKey_1.key); + expect(aesKey.getLength(), 24); + }); + test('verify key length for 128 bit key constructed from string', () { + final aesKey_1 = AESKey.generate(16); + final aesKey = AESKey(aesKey_1.key); + expect(aesKey.getLength(), 16); + }); }); } diff --git a/packages/at_chops/test/at_chops_test.dart b/packages/at_chops/test/at_chops_test.dart index 24b07d8e..f799be1d 100644 --- a/packages/at_chops/test/at_chops_test.dart +++ b/packages/at_chops/test/at_chops_test.dart @@ -11,14 +11,14 @@ import 'package:test/test.dart'; void main() { AtSignLogger.root_level = 'finest'; group('A group of tests for encryption and decryption', () { - test('Test rsa encryption/decryption string', () { + test('Test rsa encryption/decryption string', () async { final atEncryptionKeyPair = AtChopsUtil.generateAtEncryptionKeyPair(); final atChopsKeys = AtChopsKeys.create(atEncryptionKeyPair, null); final atChops = AtChopsImpl(atChopsKeys); final data = 'Hello World'; final encryptionResult = - atChops.encryptString(data, EncryptionKeyType.rsa2048); + await atChops.encryptString(data, EncryptionKeyType.rsa2048); expect(encryptionResult.atEncryptionMetaData, isNotNull); expect(encryptionResult.result, isNotEmpty); expect(encryptionResult.atEncryptionMetaData.encryptionKeyType, @@ -26,7 +26,7 @@ void main() { expect(encryptionResult.atEncryptionMetaData.atEncryptionAlgorithm, 'RsaEncryptionAlgo'); - final decryptionResult = atChops.decryptString( + final decryptionResult = await atChops.decryptString( encryptionResult.result, EncryptionKeyType.rsa2048); expect(decryptionResult.atEncryptionMetaData, isNotNull); expect(decryptionResult.result, isNotEmpty); @@ -37,14 +37,15 @@ void main() { expect(decryptionResult.result, data); }); - test('Test symmetric encrypt/decrypt bytes with initialisation vector', () { + test('Test symmetric encrypt/decrypt bytes with initialisation vector', + () async { String data = 'Hello World'; final aesKey = AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256); final atChopsKeys = AtChopsKeys()..selfEncryptionKey = aesKey; final atChops = AtChopsImpl(atChopsKeys); final iv = AtChopsUtil.generateRandomIV(16); - final encryptionResult = atChops.encryptBytes( + final encryptionResult = await atChops.encryptBytes( // ignore: unnecessary_cast utf8.encode(data) as Uint8List, EncryptionKeyType.aes256, @@ -57,7 +58,7 @@ void main() { 'AESEncryptionAlgo'); expect(encryptionResult.atEncryptionMetaData.iv, iv); - final decryptionResult = atChops.decryptBytes( + final decryptionResult = await atChops.decryptBytes( encryptionResult.result, EncryptionKeyType.aes256, iv: iv); expect(decryptionResult.result, isNotEmpty); @@ -69,14 +70,14 @@ void main() { expect(utf8.decode(decryptionResult.result), data); }); - test('Test symmetric encrypt/decrypt bytes with emoji char', () { + test('Test symmetric encrypt/decrypt bytes with emoji char', () async { String data = 'Hello World🛠'; final aesKey = AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256); final atChopsKeys = AtChopsKeys()..selfEncryptionKey = aesKey; final atChops = AtChopsImpl(atChopsKeys); final iv = AtChopsUtil.generateRandomIV(16); - final encryptionResult = atChops.encryptBytes( + final encryptionResult = await atChops.encryptBytes( // ignore: unnecessary_cast utf8.encode(data) as Uint8List, EncryptionKeyType.aes256, @@ -89,7 +90,7 @@ void main() { 'AESEncryptionAlgo'); expect(encryptionResult.atEncryptionMetaData.iv, iv); - final decryptionResult = atChops.decryptBytes( + final decryptionResult = await atChops.decryptBytes( encryptionResult.result, EncryptionKeyType.aes256, iv: iv); expect(decryptionResult.result, isNotEmpty); @@ -101,14 +102,14 @@ void main() { expect(utf8.decode(decryptionResult.result), data); }); - test('Test symmetric encrypt/decrypt bytes with special chars', () { + test('Test symmetric encrypt/decrypt bytes with special chars', () async { String data = 'Hello World🛠'; final aesKey = AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256); final atChopsKeys = AtChopsKeys()..selfEncryptionKey = aesKey; final atChops = AtChopsImpl(atChopsKeys); final iv = AtChopsUtil.generateRandomIV(16); - final encryptionResult = atChops.encryptBytes( + final encryptionResult = await atChops.encryptBytes( // ignore: unnecessary_cast utf8.encode(data) as Uint8List, EncryptionKeyType.aes256, @@ -121,7 +122,7 @@ void main() { 'AESEncryptionAlgo'); expect(encryptionResult.atEncryptionMetaData.iv, iv); - final decryptionResult = atChops.decryptBytes( + final decryptionResult = await atChops.decryptBytes( encryptionResult.result, EncryptionKeyType.aes256, iv: iv); expect(decryptionResult.result, isNotEmpty); @@ -134,7 +135,7 @@ void main() { }); test('Test symmetric encrypt/decrypt string with initialisation vector', - () { + () async { String data = 'Hello World'; final aesKey = AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256); final atChopsKeys = AtChopsKeys()..selfEncryptionKey = aesKey; @@ -142,7 +143,7 @@ void main() { final iv = AtChopsUtil.generateRandomIV(16); final encryptionResult = - atChops.encryptString(data, EncryptionKeyType.aes256, iv: iv); + await atChops.encryptString(data, EncryptionKeyType.aes256, iv: iv); expect(encryptionResult.atEncryptionMetaData, isNotNull); expect(encryptionResult.result, isNotEmpty); expect(encryptionResult.atEncryptionMetaData.encryptionKeyType, @@ -151,7 +152,7 @@ void main() { 'AESEncryptionAlgo'); expect(encryptionResult.atEncryptionMetaData.iv, iv); - final decryptionResult = atChops.decryptString( + final decryptionResult = await atChops.decryptString( encryptionResult.result, EncryptionKeyType.aes256, iv: iv); expect(decryptionResult.result, isNotEmpty); @@ -163,7 +164,7 @@ void main() { expect(decryptionResult.result, data); }); - test('Test symmetric encrypt/decrypt string with special chars', () { + test('Test symmetric encrypt/decrypt string with special chars', () async { String data = 'Hello``*+%'; final aesKey = AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256); final atChopsKeys = AtChopsKeys()..selfEncryptionKey = aesKey; @@ -171,7 +172,7 @@ void main() { final iv = AtChopsUtil.generateRandomIV(16); final encryptionResult = - atChops.encryptString(data, EncryptionKeyType.aes256, iv: iv); + await atChops.encryptString(data, EncryptionKeyType.aes256, iv: iv); expect(encryptionResult.atEncryptionMetaData, isNotNull); expect(encryptionResult.result, isNotEmpty); expect(encryptionResult.atEncryptionMetaData.encryptionKeyType, @@ -180,7 +181,7 @@ void main() { 'AESEncryptionAlgo'); expect(encryptionResult.atEncryptionMetaData.iv, iv); - final decryptionResult = atChops.decryptString( + final decryptionResult = await atChops.decryptString( encryptionResult.result, EncryptionKeyType.aes256, iv: iv); expect(decryptionResult.result, isNotEmpty); @@ -192,7 +193,7 @@ void main() { expect(decryptionResult.result, data); }); - test('Test symmetric encrypt/decrypt string with emoji', () { + test('Test symmetric encrypt/decrypt string with emoji', () async { String data = 'Hello World🛠'; final aesKey = AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256); final atChopsKeys = AtChopsKeys()..selfEncryptionKey = aesKey; @@ -200,7 +201,7 @@ void main() { final iv = AtChopsUtil.generateRandomIV(16); final encryptionResult = - atChops.encryptString(data, EncryptionKeyType.aes256, iv: iv); + await atChops.encryptString(data, EncryptionKeyType.aes256, iv: iv); expect(encryptionResult.atEncryptionMetaData, isNotNull); expect(encryptionResult.result, isNotEmpty); expect(encryptionResult.atEncryptionMetaData.encryptionKeyType, @@ -209,7 +210,7 @@ void main() { 'AESEncryptionAlgo'); expect(encryptionResult.atEncryptionMetaData.iv, iv); - final decryptionResult = atChops.decryptString( + final decryptionResult = await atChops.decryptString( encryptionResult.result, EncryptionKeyType.aes256, iv: iv); expect(decryptionResult.result, isNotEmpty); @@ -221,18 +222,18 @@ void main() { expect(decryptionResult.result, data); }); - test('validate decryption behaviour with null iv', () { + test('validate decryption behaviour with null iv', () async { final aesKey = AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256); final atChopsKeys = AtChopsKeys()..selfEncryptionKey = aesKey; final atChops = AtChopsImpl(atChopsKeys); var iv = AtChopsUtil.generateRandomIV(15); var utf8EncData = utf8.encode('abcd'); - var encryptionResult = - atChops.encryptBytes(utf8EncData, EncryptionKeyType.aes256, iv: iv); + var encryptionResult = await atChops + .encryptBytes(utf8EncData, EncryptionKeyType.aes256, iv: iv); expect( - () => atChops.decryptBytes( + () async => await atChops.decryptBytes( encryptionResult.result, EncryptionKeyType.aes256, iv: null), throwsA(predicate((e) => e is AtDecryptionException && diff --git a/packages/at_chops/test/pkcs7_padding_test.dart b/packages/at_chops/test/pkcs7_padding_test.dart new file mode 100644 index 00000000..57984ad0 --- /dev/null +++ b/packages/at_chops/test/pkcs7_padding_test.dart @@ -0,0 +1,82 @@ +import 'package:at_chops/src/algorithm/padding/padding_params.dart'; +import 'package:at_chops/src/algorithm/padding/pkcs7padding.dart'; +import 'package:at_commons/at_commons.dart'; +import 'package:test/test.dart'; + +void main() { + group('A group of tests to verify pkcs7 padding', () { + test('A test to verify padding when data length is less than block size', + () { + final paddingAlgo = PKCS7Padding(PaddingParams()..blockSize = 16); + var dataString = 'Hello World'; + var unPaddedData = dataString.codeUnits; + var paddedData = paddingAlgo.addPadding(dataString.codeUnits); + int padValue = 5; //expected padding value + expect(paddedData.last, padValue); + for (int i = 1; i <= padValue; i++) { + expect(paddedData[paddedData.length - i], padValue); + } + var dataAfterRemovingPadding = paddingAlgo.removePadding(paddedData); + expect(dataAfterRemovingPadding, unPaddedData); + }); + test('A test to verify padding when data length is equal to block size', + () { + final paddingAlgo = PKCS7Padding(PaddingParams()..blockSize = 16); + var dataString = 'Hello World12345'; + var unPaddedData = dataString.codeUnits; + var paddedData = paddingAlgo.addPadding(dataString.codeUnits); + int padValue = 16; //expected padding value + expect(paddedData.last, padValue); + for (int i = 1; i <= padValue; i++) { + expect(paddedData[paddedData.length - i], padValue); + } + var dataAfterRemovingPadding = paddingAlgo.removePadding(paddedData); + expect(dataAfterRemovingPadding, unPaddedData); + }); + test( + 'A test to verify padding when data length is one less than block size', + () { + final paddingAlgo = PKCS7Padding(PaddingParams()..blockSize = 16); + var dataString = 'Hello World1234'; + var unPaddedData = dataString.codeUnits; + var paddedData = paddingAlgo.addPadding(dataString.codeUnits); + int padValue = 1; //expected padding value + expect(paddedData.last, padValue); + for (int i = 1; i <= padValue; i++) { + expect(paddedData[paddedData.length - i], padValue); + } + var dataAfterRemovingPadding = paddingAlgo.removePadding(paddedData); + expect(dataAfterRemovingPadding, unPaddedData); + }); + test('A test to verify invalid blocksize', () { + var paddingAlgo = PKCS7Padding(PaddingParams()); + var dataString = 'Hello World1234'; + expect( + () => paddingAlgo.addPadding(dataString.codeUnits), + throwsA(predicate((e) => + e is AtEncryptionException && + e.toString().contains('Block size must be between 1 and 255.')))); + paddingAlgo = PKCS7Padding(PaddingParams()..blockSize = 0); + expect( + () => paddingAlgo.addPadding(dataString.codeUnits), + throwsA(predicate((e) => + e is AtEncryptionException && + e.toString().contains('Block size must be between 1 and 255.')))); + paddingAlgo = PKCS7Padding(PaddingParams()..blockSize = 300); + expect( + () => paddingAlgo.addPadding(dataString.codeUnits), + throwsA(predicate((e) => + e is AtEncryptionException && + e.toString().contains('Block size must be between 1 and 255.')))); + }); + test('A test to verify invalid input data to remove padding', () { + var paddingAlgo = PKCS7Padding(PaddingParams()); + List invalidInput = []; + expect( + () => paddingAlgo.removePadding(invalidInput), + throwsA(predicate((e) => + e is AtDecryptionException && + e.toString().contains('Encrypted data cannot be empty')))); + }); + }); +} From 009e7ee6c01e569930d2ae17497e1609ea4d1382 Mon Sep 17 00:00:00 2001 From: Murali Date: Mon, 9 Dec 2024 12:28:26 +0530 Subject: [PATCH 02/13] fix: at_auth added await to encrypt methods --- packages/at_auth/lib/src/at_auth_impl.dart | 42 +++++++++---------- .../lib/src/enroll/at_enrollment_impl.dart | 28 +++++++------ packages/at_auth/pubspec.yaml | 7 ++++ 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/packages/at_auth/lib/src/at_auth_impl.dart b/packages/at_auth/lib/src/at_auth_impl.dart index 913e5ee2..0f5a2294 100644 --- a/packages/at_auth/lib/src/at_auth_impl.dart +++ b/packages/at_auth/lib/src/at_auth_impl.dart @@ -51,7 +51,7 @@ class AtAuthImpl implements AtAuth { if (atAuthRequest.atKeysFilePath != null) { atAuthKeys = await _prepareAtAuthKeysFromFilePath(atAuthRequest); } else if (atAuthRequest.encryptedKeysMap != null) { - atAuthKeys = _decryptAtKeysWithSelfEncKey( + atAuthKeys = await _decryptAtKeysWithSelfEncKey( atAuthRequest.encryptedKeysMap!, PkamAuthMode.keysFile); } else { atAuthKeys = atAuthRequest.atAuthKeys; @@ -206,18 +206,16 @@ class AtAuthImpl implements AtAuth { AESEncryptionAlgo symmetricEncryptionAlgo = AESEncryptionAlgo(AESKey(atAuthKeys.apkamSymmetricKey!)); // Encrypt the defaultEncryptionPrivateKey with APKAM Symmetric key - String encryptedDefaultEncryptionPrivateKey = atChops! - .encryptString( + String encryptedDefaultEncryptionPrivateKey = (await atChops!.encryptString( atAuthKeys.defaultEncryptionPrivateKey!, EncryptionKeyType.aes256, encryptionAlgorithm: symmetricEncryptionAlgo, - iv: AtChopsUtil.generateIVLegacy()) + iv: AtChopsUtil.generateIVLegacy())) .result; // Encrypt the Self Encryption Key with APKAM Symmetric key - String encryptedDefaultSelfEncryptionKey = atChops! - .encryptString( + String encryptedDefaultSelfEncryptionKey = (await atChops!.encryptString( atAuthKeys.defaultSelfEncryptionKey!, EncryptionKeyType.aes256, encryptionAlgorithm: symmetricEncryptionAlgo, - iv: AtChopsUtil.generateIVLegacy()) + iv: AtChopsUtil.generateIVLegacy())) .result; _logger.finer('apkamPublicKey: ${atAuthKeys.apkamPublicKey}'); @@ -247,35 +245,37 @@ class AtAuthImpl implements AtAuth { return enrollmentIdFromServer!; } - AtAuthKeys _decryptAtKeysWithSelfEncKey( - Map jsonData, PkamAuthMode authMode) { + Future _decryptAtKeysWithSelfEncKey( + Map jsonData, PkamAuthMode authMode) async { var securityKeys = AtAuthKeys(); String decryptionKey = jsonData[auth_constants.defaultSelfEncryptionKey]!; var atChops = AtChopsImpl(AtChopsKeys()..selfEncryptionKey = AESKey(decryptionKey)); - securityKeys.defaultEncryptionPublicKey = atChops - .decryptString(jsonData[auth_constants.defaultEncryptionPublicKey]!, + securityKeys.defaultEncryptionPublicKey = (await atChops.decryptString( + jsonData[auth_constants.defaultEncryptionPublicKey]!, EncryptionKeyType.aes256, - keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy()) + keyName: 'selfEncryptionKey', + iv: AtChopsUtil.generateIVLegacy())) .result; - securityKeys.defaultEncryptionPrivateKey = atChops - .decryptString(jsonData[auth_constants.defaultEncryptionPrivateKey]!, + securityKeys.defaultEncryptionPrivateKey = (await atChops.decryptString( + jsonData[auth_constants.defaultEncryptionPrivateKey]!, EncryptionKeyType.aes256, - keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy()) + keyName: 'selfEncryptionKey', + iv: AtChopsUtil.generateIVLegacy())) .result; securityKeys.defaultSelfEncryptionKey = decryptionKey; - securityKeys.apkamPublicKey = atChops - .decryptString( + securityKeys.apkamPublicKey = (await atChops.decryptString( jsonData[auth_constants.apkamPublicKey]!, EncryptionKeyType.aes256, - keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy()) + keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy())) .result; // pkam private key will not be saved in keyfile if auth mode is sim/any other secure element. // decrypt the private key only when auth mode is keysFile if (authMode == PkamAuthMode.keysFile) { - securityKeys.apkamPrivateKey = atChops - .decryptString(jsonData[auth_constants.apkamPrivateKey]!, + securityKeys.apkamPrivateKey = (await atChops.decryptString( + jsonData[auth_constants.apkamPrivateKey]!, EncryptionKeyType.aes256, - keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy()) + keyName: 'selfEncryptionKey', + iv: AtChopsUtil.generateIVLegacy())) .result; } securityKeys.apkamSymmetricKey = jsonData[auth_constants.apkamSymmetricKey]; diff --git a/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart b/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart index 05dbf3b4..e5f9c247 100644 --- a/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart +++ b/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart @@ -117,21 +117,23 @@ class AtEnrollmentImpl implements AtEnrollmentBase { atLookUp.atChops?.atChopsKeys.apkamSymmetricKey = AESKey(apkamSymmetricKey); // Fetch the encryptionPrivateKey from the atChops and encrypt with APKAM Symmetric key. - String encryptedDefaultEncryptionPrivateKey = atLookUp.atChops - ?.encryptString( - atLookUp.atChops!.atChopsKeys.atEncryptionKeyPair!.atPrivateKey - .privateKey, - EncryptionKeyType.aes256, - keyName: 'apkamSymmetricKey', - iv: AtChopsUtil.generateIVLegacy()) - .result; + String encryptedDefaultEncryptionPrivateKey = (await atLookUp.atChops + ?.encryptString( + atLookUp.atChops!.atChopsKeys.atEncryptionKeyPair!.atPrivateKey + .privateKey, + EncryptionKeyType.aes256, + keyName: 'apkamSymmetricKey', + iv: AtChopsUtil.generateIVLegacy())) + ?.result; // Fetch the selfEncryptionKey from the atChops and encrypt with APKAM Symmetric key. - String encryptedDefaultSelfEncryptionKey = atLookUp.atChops - ?.encryptString(atLookUp.atChops!.atChopsKeys.selfEncryptionKey!.key, - EncryptionKeyType.aes256, - keyName: 'apkamSymmetricKey', iv: AtChopsUtil.generateIVLegacy()) - .result; + String encryptedDefaultSelfEncryptionKey = (await atLookUp.atChops + ?.encryptString( + atLookUp.atChops!.atChopsKeys.selfEncryptionKey!.key, + EncryptionKeyType.aes256, + keyName: 'apkamSymmetricKey', + iv: AtChopsUtil.generateIVLegacy())) + ?.result; String command = 'enroll:approve:${jsonEncode({ 'enrollmentId': enrollmentRequestDecision.enrollmentId, diff --git a/packages/at_auth/pubspec.yaml b/packages/at_auth/pubspec.yaml index d2ab04c8..f671f69f 100644 --- a/packages/at_auth/pubspec.yaml +++ b/packages/at_auth/pubspec.yaml @@ -17,6 +17,13 @@ dependencies: at_demo_data: ^1.0.3 crypton: ^2.2.1 +dependency_overrides: + at_chops: + git: + url: https://github.com/atsign-foundation/at_libraries.git + path: packages/at_chops + ref: at_chops_faster_aes + dev_dependencies: lints: ^5.0.0 test: ^1.25.8 From 836af9fde37112da3d412f0e81ba19eb8e45dafe Mon Sep 17 00:00:00 2001 From: Murali Date: Mon, 9 Dec 2024 12:37:07 +0530 Subject: [PATCH 03/13] fix: at_auth unit tests --- packages/at_auth/test/enrollment_test.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/at_auth/test/enrollment_test.dart b/packages/at_auth/test/enrollment_test.dart index 2622515d..86ed250a 100644 --- a/packages/at_auth/test/enrollment_test.dart +++ b/packages/at_auth/test/enrollment_test.dart @@ -61,10 +61,10 @@ void main() { any( that: startsWith( 'keys:get:keyName:123.${AtConstants.defaultEncryptionPrivateKey}')), - auth: true)).thenAnswer((_) => Future.value(jsonEncode({ - 'value': atChopsImpl - .encryptString(encryptionPrivateKey, EncryptionKeyType.aes256, - keyName: 'apkamSymmetricKey', iv: iv) + auth: true)).thenAnswer((_) async => Future.value(jsonEncode({ + 'value': (await atChopsImpl.encryptString( + encryptionPrivateKey, EncryptionKeyType.aes256, + keyName: 'apkamSymmetricKey', iv: iv)) .result }))); @@ -73,10 +73,10 @@ void main() { any( that: startsWith( 'keys:get:keyName:123.${AtConstants.defaultSelfEncryptionKey}')), - auth: true)).thenAnswer((_) => Future.value(jsonEncode({ - 'value': atChopsImpl - .encryptString(selfEncryptionKey, EncryptionKeyType.aes256, - keyName: 'apkamSymmetricKey', iv: iv) + auth: true)).thenAnswer((_) async => Future.value(jsonEncode({ + 'value': (await atChopsImpl.encryptString( + selfEncryptionKey, EncryptionKeyType.aes256, + keyName: 'apkamSymmetricKey', iv: iv)) .result }))); when(() => mockAtLookUp.pkamAuthenticate(enrollmentId: '123')) From f8975b515a9997d8276942348ed4ddef977c6459 Mon Sep 17 00:00:00 2001 From: Murali Date: Wed, 18 Dec 2024 16:45:43 +0530 Subject: [PATCH 04/13] fix: move at_auth changes to new PR --- packages/at_auth/lib/src/at_auth_impl.dart | 40 +++++++++---------- .../lib/src/enroll/at_enrollment_impl.dart | 24 +++++------ packages/at_auth/pubspec.yaml | 7 ---- packages/at_auth/test/enrollment_test.dart | 16 ++++---- 4 files changed, 39 insertions(+), 48 deletions(-) diff --git a/packages/at_auth/lib/src/at_auth_impl.dart b/packages/at_auth/lib/src/at_auth_impl.dart index 0f5a2294..0df0b46f 100644 --- a/packages/at_auth/lib/src/at_auth_impl.dart +++ b/packages/at_auth/lib/src/at_auth_impl.dart @@ -206,16 +206,18 @@ class AtAuthImpl implements AtAuth { AESEncryptionAlgo symmetricEncryptionAlgo = AESEncryptionAlgo(AESKey(atAuthKeys.apkamSymmetricKey!)); // Encrypt the defaultEncryptionPrivateKey with APKAM Symmetric key - String encryptedDefaultEncryptionPrivateKey = (await atChops!.encryptString( + String encryptedDefaultEncryptionPrivateKey = atChops! + .encryptString( atAuthKeys.defaultEncryptionPrivateKey!, EncryptionKeyType.aes256, encryptionAlgorithm: symmetricEncryptionAlgo, - iv: AtChopsUtil.generateIVLegacy())) + iv: AtChopsUtil.generateIVLegacy()) .result; // Encrypt the Self Encryption Key with APKAM Symmetric key - String encryptedDefaultSelfEncryptionKey = (await atChops!.encryptString( + String encryptedDefaultSelfEncryptionKey = atChops! + .encryptString( atAuthKeys.defaultSelfEncryptionKey!, EncryptionKeyType.aes256, encryptionAlgorithm: symmetricEncryptionAlgo, - iv: AtChopsUtil.generateIVLegacy())) + iv: AtChopsUtil.generateIVLegacy()) .result; _logger.finer('apkamPublicKey: ${atAuthKeys.apkamPublicKey}'); @@ -245,37 +247,35 @@ class AtAuthImpl implements AtAuth { return enrollmentIdFromServer!; } - Future _decryptAtKeysWithSelfEncKey( - Map jsonData, PkamAuthMode authMode) async { + AtAuthKeys _decryptAtKeysWithSelfEncKey( + Map jsonData, PkamAuthMode authMode) { var securityKeys = AtAuthKeys(); String decryptionKey = jsonData[auth_constants.defaultSelfEncryptionKey]!; var atChops = AtChopsImpl(AtChopsKeys()..selfEncryptionKey = AESKey(decryptionKey)); - securityKeys.defaultEncryptionPublicKey = (await atChops.decryptString( - jsonData[auth_constants.defaultEncryptionPublicKey]!, + securityKeys.defaultEncryptionPublicKey = atChops + .decryptString(jsonData[auth_constants.defaultEncryptionPublicKey]!, EncryptionKeyType.aes256, - keyName: 'selfEncryptionKey', - iv: AtChopsUtil.generateIVLegacy())) + keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy()) .result; - securityKeys.defaultEncryptionPrivateKey = (await atChops.decryptString( - jsonData[auth_constants.defaultEncryptionPrivateKey]!, + securityKeys.defaultEncryptionPrivateKey = atChops + .decryptString(jsonData[auth_constants.defaultEncryptionPrivateKey]!, EncryptionKeyType.aes256, - keyName: 'selfEncryptionKey', - iv: AtChopsUtil.generateIVLegacy())) + keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy()) .result; securityKeys.defaultSelfEncryptionKey = decryptionKey; - securityKeys.apkamPublicKey = (await atChops.decryptString( + securityKeys.apkamPublicKey = atChops + .decryptString( jsonData[auth_constants.apkamPublicKey]!, EncryptionKeyType.aes256, - keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy())) + keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy()) .result; // pkam private key will not be saved in keyfile if auth mode is sim/any other secure element. // decrypt the private key only when auth mode is keysFile if (authMode == PkamAuthMode.keysFile) { - securityKeys.apkamPrivateKey = (await atChops.decryptString( - jsonData[auth_constants.apkamPrivateKey]!, + securityKeys.apkamPrivateKey = atChops + .decryptString(jsonData[auth_constants.apkamPrivateKey]!, EncryptionKeyType.aes256, - keyName: 'selfEncryptionKey', - iv: AtChopsUtil.generateIVLegacy())) + keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy()) .result; } securityKeys.apkamSymmetricKey = jsonData[auth_constants.apkamSymmetricKey]; diff --git a/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart b/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart index e5f9c247..2ecd6931 100644 --- a/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart +++ b/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart @@ -117,22 +117,20 @@ class AtEnrollmentImpl implements AtEnrollmentBase { atLookUp.atChops?.atChopsKeys.apkamSymmetricKey = AESKey(apkamSymmetricKey); // Fetch the encryptionPrivateKey from the atChops and encrypt with APKAM Symmetric key. - String encryptedDefaultEncryptionPrivateKey = (await atLookUp.atChops - ?.encryptString( - atLookUp.atChops!.atChopsKeys.atEncryptionKeyPair!.atPrivateKey - .privateKey, - EncryptionKeyType.aes256, - keyName: 'apkamSymmetricKey', - iv: AtChopsUtil.generateIVLegacy())) + String encryptedDefaultEncryptionPrivateKey = atLookUp.atChops + ?.encryptString( + atLookUp.atChops!.atChopsKeys.atEncryptionKeyPair!.atPrivateKey + .privateKey, + EncryptionKeyType.aes256, + keyName: 'apkamSymmetricKey', + iv: AtChopsUtil.generateIVLegacy()) ?.result; // Fetch the selfEncryptionKey from the atChops and encrypt with APKAM Symmetric key. - String encryptedDefaultSelfEncryptionKey = (await atLookUp.atChops - ?.encryptString( - atLookUp.atChops!.atChopsKeys.selfEncryptionKey!.key, - EncryptionKeyType.aes256, - keyName: 'apkamSymmetricKey', - iv: AtChopsUtil.generateIVLegacy())) + String encryptedDefaultSelfEncryptionKey = atLookUp.atChops + ?.encryptString(atLookUp.atChops!.atChopsKeys.selfEncryptionKey!.key, + EncryptionKeyType.aes256, + keyName: 'apkamSymmetricKey', iv: AtChopsUtil.generateIVLegacy()) ?.result; String command = 'enroll:approve:${jsonEncode({ diff --git a/packages/at_auth/pubspec.yaml b/packages/at_auth/pubspec.yaml index f671f69f..d2ab04c8 100644 --- a/packages/at_auth/pubspec.yaml +++ b/packages/at_auth/pubspec.yaml @@ -17,13 +17,6 @@ dependencies: at_demo_data: ^1.0.3 crypton: ^2.2.1 -dependency_overrides: - at_chops: - git: - url: https://github.com/atsign-foundation/at_libraries.git - path: packages/at_chops - ref: at_chops_faster_aes - dev_dependencies: lints: ^5.0.0 test: ^1.25.8 diff --git a/packages/at_auth/test/enrollment_test.dart b/packages/at_auth/test/enrollment_test.dart index 86ed250a..2622515d 100644 --- a/packages/at_auth/test/enrollment_test.dart +++ b/packages/at_auth/test/enrollment_test.dart @@ -61,10 +61,10 @@ void main() { any( that: startsWith( 'keys:get:keyName:123.${AtConstants.defaultEncryptionPrivateKey}')), - auth: true)).thenAnswer((_) async => Future.value(jsonEncode({ - 'value': (await atChopsImpl.encryptString( - encryptionPrivateKey, EncryptionKeyType.aes256, - keyName: 'apkamSymmetricKey', iv: iv)) + auth: true)).thenAnswer((_) => Future.value(jsonEncode({ + 'value': atChopsImpl + .encryptString(encryptionPrivateKey, EncryptionKeyType.aes256, + keyName: 'apkamSymmetricKey', iv: iv) .result }))); @@ -73,10 +73,10 @@ void main() { any( that: startsWith( 'keys:get:keyName:123.${AtConstants.defaultSelfEncryptionKey}')), - auth: true)).thenAnswer((_) async => Future.value(jsonEncode({ - 'value': (await atChopsImpl.encryptString( - selfEncryptionKey, EncryptionKeyType.aes256, - keyName: 'apkamSymmetricKey', iv: iv)) + auth: true)).thenAnswer((_) => Future.value(jsonEncode({ + 'value': atChopsImpl + .encryptString(selfEncryptionKey, EncryptionKeyType.aes256, + keyName: 'apkamSymmetricKey', iv: iv) .result }))); when(() => mockAtLookUp.pkamAuthenticate(enrollmentId: '123')) From 7aad63e94d4c0f94eb48c39610308695d508ca81 Mon Sep 17 00:00:00 2001 From: Murali Date: Wed, 18 Dec 2024 17:23:33 +0530 Subject: [PATCH 05/13] fix: at_auth revert changes --- packages/at_auth/lib/src/at_auth_impl.dart | 2 +- packages/at_auth/lib/src/enroll/at_enrollment_impl.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/at_auth/lib/src/at_auth_impl.dart b/packages/at_auth/lib/src/at_auth_impl.dart index 0df0b46f..913e5ee2 100644 --- a/packages/at_auth/lib/src/at_auth_impl.dart +++ b/packages/at_auth/lib/src/at_auth_impl.dart @@ -51,7 +51,7 @@ class AtAuthImpl implements AtAuth { if (atAuthRequest.atKeysFilePath != null) { atAuthKeys = await _prepareAtAuthKeysFromFilePath(atAuthRequest); } else if (atAuthRequest.encryptedKeysMap != null) { - atAuthKeys = await _decryptAtKeysWithSelfEncKey( + atAuthKeys = _decryptAtKeysWithSelfEncKey( atAuthRequest.encryptedKeysMap!, PkamAuthMode.keysFile); } else { atAuthKeys = atAuthRequest.atAuthKeys; diff --git a/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart b/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart index 2ecd6931..05dbf3b4 100644 --- a/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart +++ b/packages/at_auth/lib/src/enroll/at_enrollment_impl.dart @@ -124,14 +124,14 @@ class AtEnrollmentImpl implements AtEnrollmentBase { EncryptionKeyType.aes256, keyName: 'apkamSymmetricKey', iv: AtChopsUtil.generateIVLegacy()) - ?.result; + .result; // Fetch the selfEncryptionKey from the atChops and encrypt with APKAM Symmetric key. String encryptedDefaultSelfEncryptionKey = atLookUp.atChops ?.encryptString(atLookUp.atChops!.atChopsKeys.selfEncryptionKey!.key, EncryptionKeyType.aes256, keyName: 'apkamSymmetricKey', iv: AtChopsUtil.generateIVLegacy()) - ?.result; + .result; String command = 'enroll:approve:${jsonEncode({ 'enrollmentId': enrollmentRequestDecision.enrollmentId, From aa635f3d1461e2fd35573a43cce832a8a43b016f Mon Sep 17 00:00:00 2001 From: Murali Date: Wed, 18 Dec 2024 21:27:52 +0530 Subject: [PATCH 06/13] fix: analyzer issue --- packages/at_chops/lib/src/at_chops_impl.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/at_chops/lib/src/at_chops_impl.dart b/packages/at_chops/lib/src/at_chops_impl.dart index 5e0f215f..808c46f8 100644 --- a/packages/at_chops/lib/src/at_chops_impl.dart +++ b/packages/at_chops/lib/src/at_chops_impl.dart @@ -10,8 +10,6 @@ import 'package:at_chops/src/algorithm/at_algorithm.dart'; import 'package:at_chops/src/algorithm/at_iv.dart'; import 'package:at_chops/src/algorithm/default_signing_algo.dart'; import 'package:at_chops/src/algorithm/ecc_signing_algo.dart'; -import 'package:at_chops/src/algorithm/padding/padding_params.dart'; -import 'package:at_chops/src/algorithm/padding/pkcs7padding.dart'; import 'package:at_chops/src/algorithm/pkam_signing_algo.dart'; import 'package:at_chops/src/algorithm/rsa_encryption_algo.dart'; import 'package:at_chops/src/at_chops_base.dart'; From fbec869e75ed2f83c5b5731f001e7aa1bb3deae0 Mon Sep 17 00:00:00 2001 From: Murali Date: Wed, 18 Dec 2024 21:32:40 +0530 Subject: [PATCH 07/13] fix: analyzer issue --- packages/at_chops/lib/src/algorithm/padding/padding.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/at_chops/lib/src/algorithm/padding/padding.dart b/packages/at_chops/lib/src/algorithm/padding/padding.dart index dd860159..29046475 100644 --- a/packages/at_chops/lib/src/algorithm/padding/padding.dart +++ b/packages/at_chops/lib/src/algorithm/padding/padding.dart @@ -1,5 +1,3 @@ -import 'package:at_chops/src/algorithm/padding/padding_params.dart'; - abstract class PaddingAlgorithm { List addPadding(List data); List removePadding(List data); From e32fed43d8351607197d2f88df70faadfa314aee Mon Sep 17 00:00:00 2001 From: Murali Date: Wed, 18 Dec 2024 23:36:20 +0530 Subject: [PATCH 08/13] git: add dependency overrides for at_auth in func test --- tests/at_onboarding_cli_functional_tests/pubspec.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/at_onboarding_cli_functional_tests/pubspec.yaml b/tests/at_onboarding_cli_functional_tests/pubspec.yaml index 1e6171f4..0729abf5 100644 --- a/tests/at_onboarding_cli_functional_tests/pubspec.yaml +++ b/tests/at_onboarding_cli_functional_tests/pubspec.yaml @@ -14,7 +14,10 @@ dependencies: dependency_overrides: at_auth: - path: ../../packages/at_auth + git: + url: https://github.com/atsign-foundation/at_libraries.git + path: packages/at_auth + ref: at_auth_faster_aes_changes at_onboarding_cli: path: ../../packages/at_onboarding_cli at_commons: From 6812b7ca2b8b5780bba98b2a889fc3fa413b4544 Mon Sep 17 00:00:00 2001 From: Murali Date: Wed, 18 Dec 2024 23:53:49 +0530 Subject: [PATCH 09/13] fix: changelog and pubspec --- packages/at_chops/CHANGELOG.md | 2 ++ packages/at_chops/pubspec.yaml | 2 +- tests/at_onboarding_cli_functional_tests/pubspec.yaml | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/at_chops/CHANGELOG.md b/packages/at_chops/CHANGELOG.md index 049f27bd..42b31488 100644 --- a/packages/at_chops/CHANGELOG.md +++ b/packages/at_chops/CHANGELOG.md @@ -1,3 +1,5 @@ +## 3.0.0 +- feat: Faster AES encryption/decryption using better_crypto ## 2.2.0 - feat: Implement "argon2id" hashing algorithm to generate hash from a given passphrase. - feat: Add generics to "AtEncryptionAlgorithm" and "AtHashingAlgorithm" to support multiple data types in their diff --git a/packages/at_chops/pubspec.yaml b/packages/at_chops/pubspec.yaml index 2051008d..9a9ec254 100644 --- a/packages/at_chops/pubspec.yaml +++ b/packages/at_chops/pubspec.yaml @@ -1,6 +1,6 @@ name: at_chops description: Package for at_protocol cryptographic and hashing operations -version: 2.2.0 +version: 3.0.0 repository: https://github.com/atsign-foundation/at_libraries environment: diff --git a/tests/at_onboarding_cli_functional_tests/pubspec.yaml b/tests/at_onboarding_cli_functional_tests/pubspec.yaml index 0729abf5..ed05b86c 100644 --- a/tests/at_onboarding_cli_functional_tests/pubspec.yaml +++ b/tests/at_onboarding_cli_functional_tests/pubspec.yaml @@ -23,7 +23,10 @@ dependency_overrides: at_commons: path: ../../packages/at_commons at_chops: - path: ../../packages/at_chops + git: + url: https://github.com/atsign-foundation/at_libraries.git + path: packages/at_chops + ref: at_chops_faster_aes at_cli_commons: path: ../../packages/at_cli_commons From a9d2c02a973275b957fb261eb0bc1ccde71e8c70 Mon Sep 17 00:00:00 2001 From: Murali Date: Thu, 19 Dec 2024 11:38:41 +0530 Subject: [PATCH 10/13] fix: onboarding func test failure --- tests/at_onboarding_cli_functional_tests/pubspec.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/at_onboarding_cli_functional_tests/pubspec.yaml b/tests/at_onboarding_cli_functional_tests/pubspec.yaml index ed05b86c..58ae0a99 100644 --- a/tests/at_onboarding_cli_functional_tests/pubspec.yaml +++ b/tests/at_onboarding_cli_functional_tests/pubspec.yaml @@ -18,6 +18,11 @@ dependency_overrides: url: https://github.com/atsign-foundation/at_libraries.git path: packages/at_auth ref: at_auth_faster_aes_changes + at_client: + git: + url: https://github.com/atsign-foundation/at_client.git + path: packages/at_client + ref: uptake_atchops_better_crypto at_onboarding_cli: path: ../../packages/at_onboarding_cli at_commons: From 9ffae1ad912ff2601b1a266c4829fa33dcde6394 Mon Sep 17 00:00:00 2001 From: Murali Date: Thu, 19 Dec 2024 11:52:37 +0530 Subject: [PATCH 11/13] fix: pubspec --- tests/at_onboarding_cli_functional_tests/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/at_onboarding_cli_functional_tests/pubspec.yaml b/tests/at_onboarding_cli_functional_tests/pubspec.yaml index 58ae0a99..b430affb 100644 --- a/tests/at_onboarding_cli_functional_tests/pubspec.yaml +++ b/tests/at_onboarding_cli_functional_tests/pubspec.yaml @@ -20,7 +20,7 @@ dependency_overrides: ref: at_auth_faster_aes_changes at_client: git: - url: https://github.com/atsign-foundation/at_client.git + url: https://github.com/atsign-foundation/at_client_sdk.git path: packages/at_client ref: uptake_atchops_better_crypto at_onboarding_cli: From 632985c3eab9ec689d74cf0a2a518fc5ec89739d Mon Sep 17 00:00:00 2001 From: Murali Date: Thu, 19 Dec 2024 15:44:27 +0530 Subject: [PATCH 12/13] fix: added dart docs --- .../lib/src/algorithm/aes_ctr_factory.dart | 25 ++++++++++++++ .../lib/src/algorithm/padding/padding.dart | 34 +++++++++++++++++++ .../src/algorithm/padding/padding_params.dart | 12 ++++++- .../at_chops/test/pkcs7_padding_test.dart | 4 +-- 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/packages/at_chops/lib/src/algorithm/aes_ctr_factory.dart b/packages/at_chops/lib/src/algorithm/aes_ctr_factory.dart index f8c16aee..712d145d 100644 --- a/packages/at_chops/lib/src/algorithm/aes_ctr_factory.dart +++ b/packages/at_chops/lib/src/algorithm/aes_ctr_factory.dart @@ -2,7 +2,32 @@ import 'package:at_chops/at_chops.dart'; import 'package:at_commons/at_commons.dart'; import 'package:better_cryptography/better_cryptography.dart'; +/// A factory class to create AES-CTR encryption algorithms based on the key length. +/// +/// The `AesCtrFactory` class provides a static method to create an instance of +/// the `AesCtr` encryption algorithm. The key length of the provided `AESKey` +/// determines the specific variant of AES-CTR to be instantiated. class AesCtrFactory { + /// Creates an `AesCtr` encryption algorithm based on the key length of the given [aesKey]. + /// + /// The `aesKey` must have a length of 16, 24, or 32 bytes to correspond to AES-128, AES-192, + /// or AES-256 respectively. A `MacAlgorithm.empty` is used for each variant. + /// + /// Throws an [AtEncryptionException] if the provided key length is invalid. + /// + /// Example usage: + /// ```dart + /// AESKey aesKey =AESKey.generate(32);//pass the length in bytes + /// AesCtr encryptionAlgo = AesCtrFactory.createEncryptionAlgo(aesKey); + /// ``` + /// + /// - [aesKey]: An instance of `AESKey` containing the encryption key. + /// - Returns: An instance of `AesCtr` configured for the appropriate key length. + /// + /// Supported key lengths: + /// - 16 bytes for AES-128 + /// - 24 bytes for AES-192 + /// - 32 bytes for AES-256 static AesCtr createEncryptionAlgo(AESKey aesKey) { switch (aesKey.getLength()) { case 16: diff --git a/packages/at_chops/lib/src/algorithm/padding/padding.dart b/packages/at_chops/lib/src/algorithm/padding/padding.dart index 29046475..124b6d25 100644 --- a/packages/at_chops/lib/src/algorithm/padding/padding.dart +++ b/packages/at_chops/lib/src/algorithm/padding/padding.dart @@ -1,4 +1,38 @@ +/// An abstract class that defines a padding algorithm for AES encryption and decryption. +/// +/// The `PaddingAlgorithm` class provides methods to add padding bytes during encryption +/// and to remove padding bytes during decryption. Padding ensures that the input data +/// size is compatible with the block size required by AES encryption algorithms. abstract class PaddingAlgorithm { + /// Adds padding bytes to the given [data] to make its length a multiple of the AES block size. + /// + /// This method appends padding bytes to the input data so that its length becomes + /// a multiple of the AES block size (16 bytes). The exact padding scheme is + /// implementation-dependent. + /// + /// - [data]: A list of bytes representing the input data to be padded. + /// - Returns: A new list of bytes containing the padded data. + /// + /// Example usage: + /// ```dart + /// PaddingAlgorithm paddingAlgorithm = PKCS7Padding(); + /// List paddedData = paddingAlgorithm.addPadding([0x01, 0x02, 0x03]); + /// ``` List addPadding(List data); + + /// Removes padding bytes from the given [data], restoring it to its original unpadded form. + /// + /// This method removes any padding bytes that were added during encryption to return + /// the data to its original state. The exact removal logic depends on the padding + /// scheme used during encryption. + /// + /// - [data]: A list of bytes representing the padded input data. + /// - Returns: A new list of bytes containing the original unpadded data. + /// + /// Example usage: + /// ```dart + /// PaddingAlgorithm paddingAlgorithm = PKCS7Padding(); + /// List unpaddedData = paddingAlgorithm.removePadding([0x01, 0x02, 0x03, 0x05, 0x05, 0x05]); + /// ``` List removePadding(List data); } diff --git a/packages/at_chops/lib/src/algorithm/padding/padding_params.dart b/packages/at_chops/lib/src/algorithm/padding/padding_params.dart index e2206aa4..ca7a4617 100644 --- a/packages/at_chops/lib/src/algorithm/padding/padding_params.dart +++ b/packages/at_chops/lib/src/algorithm/padding/padding_params.dart @@ -1,3 +1,13 @@ +/// A class that defines parameters for padding algorithms used in AES encryption. +/// +/// The `PaddingParams` class provides configurable parameters required for +/// padding algorithms, such as the block size. These parameters are used to +/// ensure that data conforms to the block size required by AES encryption. class PaddingParams { - int? blockSize; + /// The block size (in bytes) used for padding. + /// + /// The default value is `16`, which corresponds to the block size of AES encryption. + /// This value determines the size to which input data will be padded to ensure + /// compatibility with the encryption algorithm. + int blockSize = 16; } diff --git a/packages/at_chops/test/pkcs7_padding_test.dart b/packages/at_chops/test/pkcs7_padding_test.dart index 57984ad0..bee04e5c 100644 --- a/packages/at_chops/test/pkcs7_padding_test.dart +++ b/packages/at_chops/test/pkcs7_padding_test.dart @@ -48,8 +48,8 @@ void main() { var dataAfterRemovingPadding = paddingAlgo.removePadding(paddedData); expect(dataAfterRemovingPadding, unPaddedData); }); - test('A test to verify invalid blocksize', () { - var paddingAlgo = PKCS7Padding(PaddingParams()); + test('A test to verify invalid block size', () { + var paddingAlgo = PKCS7Padding(PaddingParams()..blockSize = -10); var dataString = 'Hello World1234'; expect( () => paddingAlgo.addPadding(dataString.codeUnits), From 8ac963743631e69758989325802910703dcec1af Mon Sep 17 00:00:00 2001 From: Murali Date: Thu, 19 Dec 2024 15:50:43 +0530 Subject: [PATCH 13/13] fix: analyzer issue --- .../at_chops/lib/src/algorithm/padding/pkcs7padding.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/at_chops/lib/src/algorithm/padding/pkcs7padding.dart b/packages/at_chops/lib/src/algorithm/padding/pkcs7padding.dart index cc74c41c..b2f9172c 100644 --- a/packages/at_chops/lib/src/algorithm/padding/pkcs7padding.dart +++ b/packages/at_chops/lib/src/algorithm/padding/pkcs7padding.dart @@ -7,15 +7,13 @@ class PKCS7Padding implements PaddingAlgorithm { PKCS7Padding(this._paddingParams); @override List addPadding(List data) { - if (_paddingParams.blockSize == null || - _paddingParams.blockSize! <= 0 || - _paddingParams.blockSize! > 255) { + if (_paddingParams.blockSize <= 0 || _paddingParams.blockSize > 255) { throw AtEncryptionException('Block size must be between 1 and 255.'); } // Calculate the number of padding bytes needed int padding = - _paddingParams.blockSize! - (data.length % _paddingParams.blockSize!); + _paddingParams.blockSize - (data.length % _paddingParams.blockSize); // Add padding bytes to the data List paddedData = List.from(data);