Skip to content

Commit

Permalink
Merge pull request #708 from atsign-foundation/604-support-password-p…
Browse files Browse the repository at this point in the history
…rotected-atKeys-at-chops

feat: at_chops : Support for password protected atKeys file
  • Loading branch information
sitaram-kalluri authored Nov 14, 2024
2 parents 52e7696 + 280ec88 commit a5dc099
Show file tree
Hide file tree
Showing 16 changed files with 493 additions and 35 deletions.
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

0 comments on commit a5dc099

Please sign in to comment.