From d600977e53523dedd484fc0194e616e36aa9cac0 Mon Sep 17 00:00:00 2001 From: Sitaram Kalluri Date: Wed, 20 Nov 2024 16:11:54 +0530 Subject: [PATCH] feat: at_auth : Add "passPhrase" in "AtAuthRequest" to support password protected atKeys file --- packages/at_auth/CHANGELOG.md | 8 +++ packages/at_auth/lib/at_auth.dart | 12 +---- packages/at_auth/lib/src/at_auth_impl.dart | 53 +++++++++++++------ .../at_auth/lib/src/auth/at_auth_request.dart | 3 ++ .../at_auth/lib/src/keys/at_auth_keys.dart | 1 + packages/at_auth/pubspec.yaml | 12 ++--- 6 files changed, 58 insertions(+), 31 deletions(-) diff --git a/packages/at_auth/CHANGELOG.md b/packages/at_auth/CHANGELOG.md index a8036cad..ac800a78 100644 --- a/packages/at_auth/CHANGELOG.md +++ b/packages/at_auth/CHANGELOG.md @@ -1,3 +1,11 @@ +## 2.0.8 +- feat: Add "passPhrase" in "AtAuthRequest" to support password protected atKeys file +- build[deps]: Upgraded the following packages: + - at_commons to v5.0.2 + - at_auth to v2.2.0 + - lints to v5.0.0 + - test to v1.25.8 + - mocktail to v1.0.4 ## 2.0.7 - build[deps]: Upgraded the following packages: - at_commons to v5.0.0 diff --git a/packages/at_auth/lib/at_auth.dart b/packages/at_auth/lib/at_auth.dart index bb586a8d..fed67374 100644 --- a/packages/at_auth/lib/at_auth.dart +++ b/packages/at_auth/lib/at_auth.dart @@ -1,35 +1,27 @@ /// The [AtAuth] package contains common logic for onboarding/authenticating an atSign to a secondary server -library at_auth; +library; import 'package:at_auth/src/auth_interface.dart'; import 'package:at_auth/src/auth_interface_impl.dart'; export 'src/at_auth_base.dart'; -export 'src/auth_constants.dart'; - export 'src/auth/at_auth_request.dart'; export 'src/auth/at_auth_response.dart'; - +export 'src/auth_constants.dart'; // Contains method related to submit, approve and deny an enrollment. export 'src/enroll/at_enrollment_base.dart'; - // Contains fields related to enrollment response received from the secondary server export 'src/enroll/at_enrollment_response.dart'; - // The abstract class contains fields related to enrollment request export 'src/enroll/base_enrollment_request.dart'; - /// The class contains fields to submit enrollment request for APKAM keys which generate keys for /// an application with restricted access to the namespaces. export 'src/enroll/enrollment_request.dart'; - /// This class serves as the entity responsible for either approving or denying an enrollment request export 'src/enroll/enrollment_request_decision.dart'; - /// The class stores enrollment request details. It notifies the approving app upon receiving a /// request from the requesting app, for approval or denial. export 'src/enroll/enrollment_server_response.dart'; - export 'src/exception/at_auth_exceptions.dart'; export 'src/keys/at_auth_keys.dart'; export 'src/onboard/at_onboarding_request.dart'; diff --git a/packages/at_auth/lib/src/at_auth_impl.dart b/packages/at_auth/lib/src/at_auth_impl.dart index 3861137d..71145115 100644 --- a/packages/at_auth/lib/src/at_auth_impl.dart +++ b/packages/at_auth/lib/src/at_auth_impl.dart @@ -49,11 +49,9 @@ class AtAuthImpl implements AtAuth { AtAuthKeys? atAuthKeys; var enrollmentIdFromRequest = atAuthRequest.enrollmentId; if (atAuthRequest.atKeysFilePath != null) { - atAuthKeys = _decryptAtKeysFile( - await _readAtKeysFile(atAuthRequest.atKeysFilePath), - atAuthRequest.authMode); + atAuthKeys = await _prepareAtAuthKeysFromFilePath(atAuthRequest); } else if (atAuthRequest.encryptedKeysMap != null) { - atAuthKeys = _decryptAtKeysFile( + atAuthKeys = _decryptAtKeysWithSelfEncKey( atAuthRequest.encryptedKeysMap!, PkamAuthMode.keysFile); } else { atAuthKeys = atAuthRequest.atAuthKeys; @@ -247,7 +245,7 @@ class AtAuthImpl implements AtAuth { return enrollmentIdFromServer!; } - AtAuthKeys _decryptAtKeysFile( + AtAuthKeys _decryptAtKeysWithSelfEncKey( Map jsonData, PkamAuthMode authMode) { var securityKeys = AtAuthKeys(); String decryptionKey = jsonData[auth_constants.defaultSelfEncryptionKey]!; @@ -285,21 +283,46 @@ class AtAuthImpl implements AtAuth { ///method to read and return data from .atKeysFile ///returns map containing encryption keys - Future> _readAtKeysFile(String? atKeysFilePath) async { - if (atKeysFilePath == null || atKeysFilePath.isEmpty) { + Future _prepareAtAuthKeysFromFilePath( + AtAuthRequest atAuthRequest) async { + if (atAuthRequest.atKeysFilePath == null || + atAuthRequest.atKeysFilePath!.isEmpty) { throw AtException( 'atKeys filePath is empty. atKeysFile is required to authenticate'); } - if (!File(atKeysFilePath).existsSync()) { + if (!File(atAuthRequest.atKeysFilePath!).existsSync()) { throw AtException( - 'provided keys file does not exist. Please check whether the file path $atKeysFilePath is valid'); + 'provided keys file does not exist. Please check whether the file path ${atAuthRequest.atKeysFilePath} is valid'); } - String atAuthData = await File(atKeysFilePath).readAsString(); - Map jsonData = {}; - json.decode(atAuthData).forEach((String key, dynamic value) { - jsonData[key] = value.toString(); - }); - return jsonData; + + String atAuthData = + await File(atAuthRequest.atKeysFilePath!).readAsString(); + Map decodedAtKeysData = jsonDecode(atAuthData); + // If it contains "iv(InitializationVector)", it means the data is encrypted with a + // passphrase. Decrypt it. + if (decodedAtKeysData.containsKey('iv') && + atAuthRequest.passPhrase.isNullOrEmpty) { + throw AtDecryptionException( + 'Pass Phrase is required for password protected atKeys file'); + } + if (decodedAtKeysData.containsKey('iv')) { + _logger.info( + 'Found encrypted atKeys files. Decrypting with the given pass-phrase'); + AtEncrypted atEncrypted = AtEncrypted.fromJson(decodedAtKeysData); + + if (atEncrypted.hashingAlgoType == null) { + throw AtDecryptionException( + 'Hashing algo type is required for decryption of password protected atKeys file'); + } + + String decryptedAtKeys = + await AtKeysCrypto.fromHashingAlgorithm(atEncrypted.hashingAlgoType!) + .decrypt(atEncrypted, atAuthRequest.passPhrase!); + decodedAtKeysData = jsonDecode(decryptedAtKeys); + } + // This is to decrypt the atKeys encrypted with self Encryption key. + return _decryptAtKeysWithSelfEncKey( + decodedAtKeysData, atAuthRequest.authMode); } AtAuthKeys _generateKeyPairs(PkamAuthMode authMode, {String? publicKeyId}) { diff --git a/packages/at_auth/lib/src/auth/at_auth_request.dart b/packages/at_auth/lib/src/auth/at_auth_request.dart index ca473494..658b5406 100644 --- a/packages/at_auth/lib/src/auth/at_auth_request.dart +++ b/packages/at_auth/lib/src/auth/at_auth_request.dart @@ -38,4 +38,7 @@ class AtAuthRequest { /// Hashing algorithm to use for pkam authentication HashingAlgoType hashingAlgoType = HashingAlgoType.sha256; + + /// The pass phrase to password protect the AtKeys file. + String? passPhrase; } diff --git a/packages/at_auth/lib/src/keys/at_auth_keys.dart b/packages/at_auth/lib/src/keys/at_auth_keys.dart index f69f18ea..3d78e806 100644 --- a/packages/at_auth/lib/src/keys/at_auth_keys.dart +++ b/packages/at_auth/lib/src/keys/at_auth_keys.dart @@ -14,6 +14,7 @@ class AtAuthKeys { AtAuthKeys(); + @Deprecated('Use toJson()') Map toMap() { var keysMap = {}; keysMap[auth_constants.apkamPrivateKey] = apkamPrivateKey; diff --git a/packages/at_auth/pubspec.yaml b/packages/at_auth/pubspec.yaml index e1f14ff9..32944fe9 100644 --- a/packages/at_auth/pubspec.yaml +++ b/packages/at_auth/pubspec.yaml @@ -1,6 +1,6 @@ name: at_auth description: Package that implements common logic for onboarding/authenticating an atsign to a secondary server -version: 2.0.7 +version: 2.0.8 homepage: https://atsign.com/ repository: https://github.com/atsign-foundation/at_libraries @@ -9,15 +9,15 @@ environment: dependencies: args: ^2.4.1 - at_commons: ^5.0.0 + at_commons: ^5.0.2 at_lookup: ^3.0.49 - at_chops: ^2.0.1 + at_chops: ^2.2.0 at_utils: ^3.0.19 meta: ^1.8.0 at_demo_data: ^1.0.3 crypton: ^2.2.1 dev_dependencies: - lints: ^2.0.0 - test: ^1.24.7 - mocktail: ^0.3.0 + lints: ^5.0.0 + test: ^1.25.8 + mocktail: ^1.0.4