Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: at_chops : Support for password protected atKeys file #708

Merged
merged 2 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/at_chops/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## 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
implementations.
## 2.1.0
- feat: New library available called `at_chops_types` which provides type definitions for using custom algorithms with at_chops
- feat: New library available called `at_chops_types` which provides type definitions for using custom algorithms with
at_chops
## 2.0.1
- fix: throw Exception when input IV is null for decryption(with Symmetric Encryption)
- build[deps]: Upgraded the following packages:
Expand Down
31 changes: 20 additions & 11 deletions packages/at_chops/lib/at_chops.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
library at_chops;

export 'src/algorithm/aes_encryption_algo.dart';
export 'src/algorithm/algo_type.dart';
export 'src/algorithm/at_iv.dart';
export 'src/algorithm/default_signing_algo.dart';
export 'src/algorithm/ecc_signing_algo.dart';
export 'src/algorithm/pkam_signing_algo.dart';
export 'src/algorithm/rsa_encryption_algo.dart';
export 'src/at_chops_base.dart';
export 'src/at_chops_impl.dart';

// Class to encrypt/decrypt atKeys file based on the password specified.
export 'src/at_keys_crypto.dart';
export 'src/key/at_key_pair.dart';
export 'src/key/at_private_key.dart';
export 'src/key/at_public_key.dart';
export 'src/key/impl/aes_key.dart';
export 'src/key/impl/at_chops_keys.dart';
export 'src/key/impl/at_encryption_key_pair.dart';
export 'src/key/impl/at_pkam_key_pair.dart';
export 'src/key/impl/aes_key.dart';
export 'src/key/key_type.dart';
export 'src/metadata/at_signing_input.dart';
export 'src/metadata/encryption_metadata.dart';
export 'src/metadata/encryption_result.dart';
export 'src/metadata/signing_metadata.dart';
export 'src/metadata/signing_result.dart';

// A model class which represents the encrypted AtKeys with a passphrase.
export 'src/model/at_encrypted.dart';

// Class representing the hashing parameters to pass to an hashing algorithm.
export 'src/model/hash_params.dart' hide HashParams;
export 'src/util/at_chops_util.dart';
export 'src/algorithm/algo_type.dart';
export 'src/algorithm/at_iv.dart';
export 'src/algorithm/aes_encryption_algo.dart';
export 'src/algorithm/rsa_encryption_algo.dart';
export 'src/algorithm/default_signing_algo.dart';
export 'src/algorithm/pkam_signing_algo.dart';
export 'src/algorithm/ecc_signing_algo.dart';
export 'src/key/at_key_pair.dart';
export 'src/key/at_public_key.dart';
export 'src/key/at_private_key.dart';
75 changes: 72 additions & 3 deletions packages/at_chops/lib/src/algorithm/aes_encryption_algo.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import 'dart:typed_data';

import 'package:at_chops/at_chops.dart';
import 'package:at_chops/src/algorithm/at_algorithm.dart';
import 'package:at_chops/src/algorithm/at_iv.dart';
import 'package:at_chops/src/key/impl/aes_key.dart';
import 'package:at_commons/at_commons.dart';
import 'package:encrypt/encrypt.dart';

class AESEncryptionAlgo implements SymmetricEncryptionAlgorithm {
/// A class that provides AES encryption and decryption for Uint8List,
/// implementing the [SymmetricEncryptionAlgorithm] interface.
class AESEncryptionAlgo
implements SymmetricEncryptionAlgorithm<Uint8List, Uint8List> {
final AESKey _aesKey;

AESEncryptionAlgo(this._aesKey);

@override
Expand All @@ -33,3 +37,68 @@ class AESEncryptionAlgo implements SymmetricEncryptionAlgorithm {
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<String, String> {
/// 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;
}
}
15 changes: 14 additions & 1 deletion packages/at_chops/lib/src/algorithm/algo_type.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
// ignore: constant_identifier_names
import 'package:at_commons/at_commons.dart';

enum SigningAlgoType { ecc_secp256r1, rsa2048, rsa4096 }

enum HashingAlgoType { sha256, sha512, md5 }
enum HashingAlgoType {
sha256,
sha512,
md5,
argon2id;

static HashingAlgoType fromString(String name) {
return HashingAlgoType.values.firstWhere(
(algo) => algo.name == name.toLowerCase(),
orElse: () => throw AtException('Invalid hashing algo type'));
}
}
52 changes: 52 additions & 0 deletions packages/at_chops/lib/src/algorithm/argon2id_hashing_algo.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'dart:async';
import 'dart:convert';

import 'package:at_chops/src/algorithm/at_algorithm.dart';
import 'package:at_chops/src/model/hash_params.dart';
import 'package:cryptography/cryptography.dart';

/// A class that implements the Argon2id hashing algorithm for password hashing.
///
/// This class provides a method to hash a given password using the Argon2id
/// algorithm, which is a memory-hard, CPU-intensive key derivation function
/// suitable for password hashing and encryption key derivation.
///
/// The class uses the `cryptography` package's `Argon2id` algorithm for deriving
/// a key from a password and encodes the result into a Base64 string.
class Argon2idHashingAlgo implements AtHashingAlgorithm<String, String> {
/// Hashes a given password using the Argon2id algorithm.
///
/// The [password] parameter is required, and it represents the password or
/// passphrase to be hashed.
///
/// The [hashParams] parameter is optional. It allows customizing the Argon2id
/// parameters, such as:
/// - [HashParams.parallelism]: The degree of parallelism (threads) to use.
/// - [HashParams.memory]: The amount of memory (in KB) to use.
/// - [HashParams.iterations]: The number of iterations (time cost) to apply.
/// - [HashParams.hashLength]: The length of the resulting hash (in bytes).
///
/// If [hashParams] is not provided, default values will be used.
///
/// The method returns a [Future] that resolves to a Base64-encoded string
/// representing the hashed value of the input password.
///
/// Throws:
/// - [ArgumentError] if the provided password is null or empty.
///
/// Returns a Base64-encoded string representing the derived key.
@override
Future<String> hash(String password, {ArgonHashParams? hashParams}) async {
hashParams ??= ArgonHashParams();
final argon2id = Argon2id(
parallelism: hashParams.parallelism,
memory: hashParams.memory,
iterations: hashParams.iterations,
hashLength: hashParams.hashLength);

SecretKey secretKey = await argon2id.deriveKeyFromPassword(
password: password, nonce: password.codeUnits);

return Base64Encoder().convert(await secretKey.extractBytes());
}
}
23 changes: 14 additions & 9 deletions packages/at_chops/lib/src/algorithm/at_algorithm.dart
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';

import 'package:at_chops/src/algorithm/at_iv.dart';
import 'package:at_chops/src/key/at_key_pair.dart';
import 'package:at_chops/src/key/at_private_key.dart';
import 'package:at_chops/src/key/at_public_key.dart';
import 'package:at_chops/src/model/hash_params.dart';

/// Interface for encrypting and decrypting data. Check [DefaultEncryptionAlgo] for sample implementation.
abstract class AtEncryptionAlgorithm {
abstract class AtEncryptionAlgorithm<T, V> {
/// Encrypts the passed bytes. Bytes are passed as [Uint8List]. Encode String data type to [Uint8List] using [utf8.encode].
Uint8List encrypt(Uint8List plainData);
V encrypt(T plainData);

/// Decrypts the passed encrypted bytes.
Uint8List decrypt(Uint8List encryptedData);
V decrypt(T encryptedData);
}

/// Interface for symmetric encryption algorithms. Check [AESEncryptionAlgo] for sample implementation.
abstract class SymmetricEncryptionAlgorithm extends AtEncryptionAlgorithm {
abstract class SymmetricEncryptionAlgorithm<T, V>
extends AtEncryptionAlgorithm<T, V> {
@override
Uint8List encrypt(Uint8List plainData, {InitialisationVector iv});
V encrypt(T plainData, {InitialisationVector iv});

@override
Uint8List decrypt(Uint8List encryptedData, {InitialisationVector iv});
V decrypt(T encryptedData, {InitialisationVector iv});
}

/// Interface for asymmetric encryption algorithms. Check [DefaultEncryptionAlgo] for sample implementation.
abstract class ASymmetricEncryptionAlgorithm extends AtEncryptionAlgorithm {
abstract class ASymmetricEncryptionAlgorithm
extends AtEncryptionAlgorithm<Uint8List, Uint8List> {
AtPublicKey? atPublicKey;
AtPrivateKey? atPrivateKey;

Expand All @@ -48,7 +53,7 @@ abstract class AtSigningAlgorithm {
}

/// Interface for hashing data. Refer [DefaultHash] for sample implementation.
abstract class AtHashingAlgorithm {
abstract class AtHashingAlgorithm<K, V> {
/// Hashes the passed data
String hash(Uint8List data);
FutureOr<V> hash(K data, {covariant HashParams? hashParams});
}
29 changes: 29 additions & 0 deletions packages/at_chops/lib/src/algorithm/at_hashing_algo_factory.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:at_chops/src/algorithm/algo_type.dart';
import 'package:at_chops/src/algorithm/argon2id_hashing_algo.dart';
import 'package:at_chops/src/algorithm/at_algorithm.dart';
import 'package:at_chops/src/algorithm/default_hashing_algo.dart';
import 'package:at_commons/at_commons.dart';

/// A factory class for creating instances of different hashing algorithms
/// based on the specified [HashingAlgoType].
///
/// The [AtHashingAlgorithmFactory] class provides a static method
/// [getHashingAlgorithm] which returns the appropriate hashing algorithm
/// implementation corresponding to the provided [HashingAlgoType].
class AtHashingAlgorithmFactory {
/// Returns an instance of [AtHashingAlgorithm] based on the provided [HashingAlgoType].
///
/// The method supports the following hashing algorithms:
/// - [HashingAlgoType.md5]: returns an instance of [DefaultHash] (MD5 hashing).
/// - [HashingAlgoType.argon2id]: returns an instance of [Argon2idHashingAlgo] (Argon2id hashing).
///
/// Throws an [AtException] if an unsupported hashing algorithm is passed.
static AtHashingAlgorithm getHashingAlgorithm(HashingAlgoType algoType) {
switch (algoType) {
case HashingAlgoType.argon2id:
return Argon2idHashingAlgo();
default:
throw AtException('Unsupported hashing algorithm');
}
}
}
5 changes: 3 additions & 2 deletions packages/at_chops/lib/src/algorithm/default_hashing_algo.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import 'package:at_chops/src/algorithm/at_algorithm.dart';
import 'package:at_chops/src/model/hash_params.dart';
import 'package:crypto/crypto.dart';

class DefaultHash implements AtHashingAlgorithm {
class DefaultHash implements AtHashingAlgorithm<List<int>, String> {
@override
String hash(List<int> data) {
String hash(List<int> data, {HashParams? hashParams}) {
return md5.convert(data).toString();
}
}
9 changes: 7 additions & 2 deletions packages/at_chops/lib/src/at_chops_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import 'package:at_chops/src/algorithm/aes_encryption_algo.dart';
import 'package:at_chops/src/algorithm/algo_type.dart';
import 'package:at_chops/src/algorithm/at_algorithm.dart';
import 'package:at_chops/src/algorithm/at_iv.dart';
import 'package:at_chops/src/algorithm/rsa_encryption_algo.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/pkam_signing_algo.dart';
import 'package:at_chops/src/algorithm/rsa_encryption_algo.dart';
import 'package:at_chops/src/at_chops_base.dart';
import 'package:at_chops/src/key/at_key_pair.dart';
import 'package:at_chops/src/key/impl/aes_key.dart';
Expand All @@ -25,6 +25,8 @@ import 'package:at_chops/src/metadata/signing_result.dart';
import 'package:at_commons/at_commons.dart';
import 'package:at_utils/at_logger.dart';

import 'algorithm/default_hashing_algo.dart';

class AtChopsImpl extends AtChops {
AtChopsImpl(super.atChopsKeys);

Expand Down Expand Up @@ -143,7 +145,10 @@ class AtChopsImpl extends AtChops {

@override
String hash(Uint8List signedData, AtHashingAlgorithm hashingAlgorithm) {
return hashingAlgorithm.hash(signedData);
if (hashingAlgorithm.runtimeType == DefaultHash) {
return DefaultHash().hash(signedData);
}
throw AtException('$hashingAlgorithm is not supported');
}

@override
Expand Down
Loading