From c5667e3d0fe8a2a4a5ac45bc5ae2ab30c6d5728c Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Wed, 5 Jul 2023 19:10:14 +0530 Subject: [PATCH 1/9] refactor: rename OnboardingUtil -> RegistrarApiUtil --- .../registrar_api_util.dart} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename packages/at_onboarding_cli/lib/src/{util/onboarding_util.dart => register_cli/registrar_api_util.dart} (99%) diff --git a/packages/at_onboarding_cli/lib/src/util/onboarding_util.dart b/packages/at_onboarding_cli/lib/src/register_cli/registrar_api_util.dart similarity index 99% rename from packages/at_onboarding_cli/lib/src/util/onboarding_util.dart rename to packages/at_onboarding_cli/lib/src/register_cli/registrar_api_util.dart index 5ef2b4bc..60fc0e99 100644 --- a/packages/at_onboarding_cli/lib/src/util/onboarding_util.dart +++ b/packages/at_onboarding_cli/lib/src/register_cli/registrar_api_util.dart @@ -8,7 +8,7 @@ import 'package:http/http.dart'; import 'package:http/io_client.dart'; ///class containing utilities to perform registration of a free atsign -class OnboardingUtil { +class RegistrarApiUtil { IOClient? _ioClient; void _createClient() { @@ -174,7 +174,7 @@ class OnboardingUtil { throw at_client.InvalidDataException(jsonDecodedBody['message']); } - /// calls utility methods from [OnboardingUtil] that + /// calls utility methods from [RegistrarApiUtil] that /// 1) send verification code to the registered email /// 2) fetch the CRAM key from registrar using the verification code Future getCramUsingOtp(String atsign, String registrarUrl) async { From a452edf08adfc0e14133d3c6af572d80eab8731b Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Wed, 5 Jul 2023 19:11:14 +0530 Subject: [PATCH 2/9] refactor: consume OnboardingUtil -> RegistrarApiUtil renaming --- .../lib/src/register_cli/register.dart | 26 +++++++++---------- .../lib/src/util/register_api_task.dart | 8 +++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/at_onboarding_cli/lib/src/register_cli/register.dart b/packages/at_onboarding_cli/lib/src/register_cli/register.dart index eedc8eb5..3df829d3 100644 --- a/packages/at_onboarding_cli/lib/src/register_cli/register.dart +++ b/packages/at_onboarding_cli/lib/src/register_cli/register.dart @@ -11,8 +11,8 @@ import 'package:at_onboarding_cli/src/util/register_api_result.dart'; import 'package:at_onboarding_cli/src/util/register_api_task.dart'; import 'package:at_utils/at_logger.dart'; -import '../util/onboarding_util.dart'; import '../util/registrar_api_constants.dart'; +import 'registrar_api_util.dart'; ///Class containing logic to register a free atsign to email provided ///through [args] by utilizing methods defined in [RegisterUtil] @@ -20,7 +20,7 @@ import '../util/registrar_api_constants.dart'; class Register { Future main(List args) async { Map params = HashMap(); - OnboardingUtil registerUtil = OnboardingUtil(); + RegistrarApiUtil registrarApiUtil = RegistrarApiUtil(); final argParser = ArgParser() ..addOption('email', @@ -44,7 +44,7 @@ class Register { exit(6); } - if (registerUtil.validateEmail(argResults['email'])) { + if (registrarApiUtil.validateEmail(argResults['email'])) { params['email'] = argResults['email']; } else { stderr.writeln( @@ -58,13 +58,13 @@ class Register { //create stream of tasks each of type [RegisterApiTask] and then // call start on the stream of tasks - await RegistrationFlow(params, registerUtil) + await RegistrationFlow(params, registrarApiUtil) .add(GetFreeAtsign()) .add(RegisterAtsign()) .add(ValidateOtp()) .start(); - activate_cli.main(['-a', params['atsign']!, '-c', params['cramkey']!]); + // activate_cli.main(['-a', params['atsign']!, '-c', params['cramkey']!]); } } @@ -75,7 +75,7 @@ class Register { class RegistrationFlow { List processFlow = []; RegisterApiResult result = RegisterApiResult(); - late OnboardingUtil registerUtil; + late RegistrarApiUtil registerUtil; Map params; RegistrationFlow(this.params, this.registerUtil); @@ -118,7 +118,7 @@ class GetFreeAtsign extends RegisterApiTask { .writeln('[Information] Getting your randomly generated free atSign…'); try { List atsignList = - await registerUtil.getFreeAtSigns(authority: params['authority']!); + await registrarApiUtil.getFreeAtSigns(authority: params['authority']!); result.data['atsign'] = atsignList[0]; stdout.writeln('[Information] Your new atSign is **@${atsignList[0]}**'); result.apiCallStatus = ApiCallStatus.success; @@ -142,7 +142,7 @@ class RegisterAtsign extends RegisterApiTask { stdout.writeln( '[Information] Sending verification code to: ${params['email']}'); try { - result.data['otpSent'] = (await registerUtil.registerAtSign( + result.data['otpSent'] = (await registrarApiUtil.registerAtSign( params['atsign']!, params['email']!, authority: params['authority']!)) .toString(); @@ -164,21 +164,21 @@ class RegisterAtsign extends RegisterApiTask { ///HTTP GET/POST request class ValidateOtp extends RegisterApiTask { @override - void init(Map params, OnboardingUtil registerUtil) { + void init(Map params, RegistrarApiUtil registrarApiUtil) { params['confirmation'] = 'false'; this.params = params; - this.registerUtil = registerUtil; + registrarApiUtil = registrarApiUtil; result.data = HashMap(); } @override Future run() async { if (params['otp'] == null) { - params['otp'] = registerUtil.getVerificationCodeFromUser(); + params['otp'] = registrarApiUtil.getVerificationCodeFromUser(); } stdout.writeln('[Information] Validating your verification code...'); try { - String apiResponse = await registerUtil.validateOtp( + String apiResponse = await registrarApiUtil.validateOtp( params['atsign']!, params['email']!, params['otp']!, confirmation: params['confirmation']!, authority: params['authority']!); @@ -186,7 +186,7 @@ class ValidateOtp extends RegisterApiTask { stderr.writeln( '[Unable to proceed] The verification code you entered is either invalid or expired.\n' ' Check your verification code and try again.'); - params['otp'] = registerUtil.getVerificationCodeFromUser(); + params['otp'] = registrarApiUtil.getVerificationCodeFromUser(); result.apiCallStatus = ApiCallStatus.retry; result.exceptionMessage = 'Incorrect otp entered 3 times. Max retries reached.'; diff --git a/packages/at_onboarding_cli/lib/src/util/register_api_task.dart b/packages/at_onboarding_cli/lib/src/util/register_api_task.dart index d45b0e0d..e79ee9e2 100644 --- a/packages/at_onboarding_cli/lib/src/util/register_api_task.dart +++ b/packages/at_onboarding_cli/lib/src/util/register_api_task.dart @@ -1,7 +1,7 @@ import 'dart:collection'; -import 'package:at_onboarding_cli/src/util/onboarding_util.dart'; import 'package:at_onboarding_cli/src/util/register_api_result.dart'; +import 'package:at_onboarding_cli/src/register_cli/registrar_api_util.dart'; /// Represents a task in an AtSign registration cycle abstract class RegisterApiTask { @@ -11,17 +11,17 @@ abstract class RegisterApiTask { late Map params; - late OnboardingUtil registerUtil; + late RegistrarApiUtil registrarApiUtil; RegisterApiResult result = RegisterApiResult(); ///Initializes the Task object with necessary parameters ///[params] is a map that contains necessary data to complete atsign /// registration process - void init(Map params, OnboardingUtil registerUtil) { + void init(Map params, RegistrarApiUtil onboardingUtil) { this.params = params; result.data = HashMap(); - this.registerUtil = registerUtil; + onboardingUtil = onboardingUtil; } ///Implementing classes need to implement required logic in this method to From 776532587e3b7508b0fadf23267a80d9be5714fc Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Wed, 5 Jul 2023 19:11:46 +0530 Subject: [PATCH 3/9] docs: fix minor typo --- packages/at_onboarding_cli/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/at_onboarding_cli/README.md b/packages/at_onboarding_cli/README.md index d8cb2b8d..e0e2f5e4 100644 --- a/packages/at_onboarding_cli/README.md +++ b/packages/at_onboarding_cli/README.md @@ -7,7 +7,7 @@ ## Introduction at_onboarding_cli is a library to authenticate and onboard atSigns. -## Get Started +## Getting Started To add this package as the dependency in your pubspec.yaml From bc4df4d5e4f98366991baeb82fa86a963ecf732d Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Wed, 5 Jul 2023 19:12:32 +0530 Subject: [PATCH 4/9] refactor: Introduce OnboardingCalls that includes all onboarding processes --- .../lib/src/onboard/OnboardingCalls.dart | 250 ++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 packages/at_onboarding_cli/lib/src/onboard/OnboardingCalls.dart diff --git a/packages/at_onboarding_cli/lib/src/onboard/OnboardingCalls.dart b/packages/at_onboarding_cli/lib/src/onboard/OnboardingCalls.dart new file mode 100644 index 00000000..169f0012 --- /dev/null +++ b/packages/at_onboarding_cli/lib/src/onboard/OnboardingCalls.dart @@ -0,0 +1,250 @@ +import 'dart:io'; + +import 'package:at_client/at_client.dart'; +import 'package:at_commons/at_builders.dart'; +import 'package:at_lookup/at_lookup.dart'; +import 'package:at_onboarding_cli/src/util/at_onboarding_exceptions.dart'; +import 'package:at_utils/at_logger.dart'; + +import '../register_cli/registrar_api_util.dart'; + +abstract class OnboardingTask { + int retryCount = 0; + int maxRetries = 3; + late TaskStatus taskStatus; + late AtLookupImpl atLookup; + late AtSignLogger logger; + late final String? _secretKey; + dynamic e; + Future run(); + + OnboardingTask(this.atLookup, this.logger, this._secretKey, + {required this.maxRetries}); + + bool shouldRetry() { + return retryCount++ < maxRetries ? true : false; + } + + Future retry() async { + if (shouldRetry()) { + await run(); + } else { + e.runtimeType == Exception + ? throw AtOnboardingException(e) + : AtOnboardingException(e.toString()); + } + } +} + +enum TaskStatus { success, failure } + +class SetPkamPublicKey extends OnboardingTask { + SetPkamPublicKey(AtLookupImpl atLookup, AtSignLogger logger, String secretKey) + : super(atLookup, logger, secretKey, maxRetries: 3); + + @override + Future run() async { + try { + logger.finer('Updating PkamPublicKey to remote secondary'); + stdout.writeln( + '[Information] Updating your authentication/security keys into secondary server'); + final pkamPublicKey = _secretKey; + String updateCommand = 'update:$AT_PKAM_PUBLIC_KEY $pkamPublicKey\n'; + String? pkamUpdateResult = + await atLookup.executeCommand(updateCommand, auth: false); + logger.info('PkamPublicKey update result: $pkamUpdateResult'); + } on Exception catch (e) { + logger.finer('SetPkamPublicKey(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } on Error catch (e) { + logger.finer('SetPkamPublicKey(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } + } +} + +class SetEncryptionPublicKey extends OnboardingTask { + SetEncryptionPublicKey( + AtLookupImpl atLookup, AtSignLogger logger, String secretKey) + : super(atLookup, logger, secretKey, maxRetries: 3); + + @override + Future run() async { + try { + UpdateVerbBuilder updateBuilder = UpdateVerbBuilder() + ..atKey = 'publickey' + ..isPublic = true + ..value = _secretKey; + String? encryptKeyUpdateResult = + await atLookup.executeVerb(updateBuilder); + logger + .info('Encryption public key update result $encryptKeyUpdateResult'); + } on Exception catch (e) { + logger.finer('SetEncryptionPublicKey(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } on Error catch (e) { + logger.finer('SetEncryptionPublicKey(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } + } +} + +class DeleteCramKey extends OnboardingTask { + DeleteCramKey(AtLookupImpl atLookup, AtSignLogger logger) + : super(atLookup, logger, null, maxRetries: 10); + + @override + Future run() async { + try { + DeleteVerbBuilder deleteBuilder = DeleteVerbBuilder() + ..atKey = AT_CRAM_SECRET; + String? deleteResponse = await atLookup.executeVerb(deleteBuilder); + logger.info('Cram secret delete response : $deleteResponse'); + } on Exception catch (e) { + logger.finer('DeleteCramKey(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } on Error catch (e) { + logger.finer('DeleteCramKey(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } + } +} + +class FetchCramSecret extends OnboardingTask { + FetchCramSecret(AtLookupImpl atLookup, AtSignLogger logger, this._atsign, + this.registrarUrl) + : super(atLookup, logger, null, maxRetries: 3); + + late final String? _cram; + late final String _atsign; + late final String registrarUrl; + @override + Future run() async { + try { + _cram = await RegistrarApiUtil().getCramUsingOtp(_atsign, registrarUrl); + } on Exception catch (e) { + logger.finer('FetchCramKey(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } on Error catch (e) { + logger.finer('FetchCramKey(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } + if (_cram == null) { + retry(); + } + return _cram!; + } + + @override + Future retry() async { + if (shouldRetry()) { + await Future.delayed(Duration(seconds: 2)); + await run(); + } else { + logger.info( + 'FetchCramSecret exhausted maximum retries of $retryCount | Caught: $e'); + throw AtKeyNotFoundException( + 'Could not fetch cram secret for \'$_atsign\' from registrar'); + } + } +} + +class FindSecondary extends OnboardingTask { + SecondaryAddress? secondaryAddress; + final String _atsign; + SecureSocket? secureSocket; + bool connectionFlag = false; + + FindSecondary(AtLookupImpl atLookup, AtSignLogger logger, this._atsign) + : super(atLookup, logger, null, maxRetries: 50); + + @override + Future run() async { + try { + secondaryAddress = + await atLookup.secondaryAddressFinder.findSecondary(_atsign); + } on Exception catch (e) { + logger.finer('FindSecondary(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } on Error catch (e) { + logger.finer('FindSecondary(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } + if (secondaryAddress == null) { + retry(); + } + return secondaryAddress!; + } + + @override + Future retry() async { + if (shouldRetry()) { + await Future.delayed(Duration(seconds: 2)); + await run(); + } else { + logger.info( + 'FindSecondary exhausted maximum retries of $retryCount | Caught: $e'); + throw SecondaryNotFoundException('Could not find secondary address for ' + '$_atsign after $retryCount retries. Please retry the process'); + } + } +} + +class ConnectToSecondary extends OnboardingTask { + ConnectToSecondary( + AtLookupImpl atLookup, AtSignLogger logger, this.secondaryAddress) + : super(atLookup, logger, null, maxRetries: 50); + + SecureSocket? secureSocket; + SecondaryAddress secondaryAddress; + bool connectionFlag = false; + + @override + Future run() async { + try { + stdout.writeln( + '[Information] Connecting to secondary ...$retryCount/$maxRetries'); + secureSocket = await SecureSocket.connect( + secondaryAddress.host, secondaryAddress.port, + timeout: Duration( + seconds: + 30)); // 30-second timeout should be enough even for slow networks + connectionFlag = secureSocket?.remoteAddress != null && + secureSocket?.remotePort != null; + } on Exception catch (e) { + logger.finer('ConnectToSecondary(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } on Error catch (e) { + logger.finer('ConnectToSecondary(Attempt: #$retryCount) Caught: $e'); + taskStatus = TaskStatus.failure; + this.e = e; + retry(); + } + + if (!connectionFlag) { + retry(); + } + } +} From d4a4af979273543dfe59daf06b12212cba6b8165 Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Wed, 5 Jul 2023 19:12:51 +0530 Subject: [PATCH 5/9] refactor: consume OnboardingCalls --- .../onboard/at_onboarding_service_impl.dart | 272 +++++++----------- 1 file changed, 106 insertions(+), 166 deletions(-) diff --git a/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart b/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart index 58fa7bf7..7f3af640 100644 --- a/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart +++ b/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart @@ -1,25 +1,21 @@ // ignore_for_file: unnecessary_null_comparison - import 'dart:convert'; import 'dart:io'; import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart'; +import 'package:at_onboarding_cli/src/onboard/OnboardingCalls.dart'; import 'package:at_onboarding_cli/src/util/at_onboarding_exceptions.dart'; import 'package:at_server_status/at_server_status.dart'; import 'package:at_utils/at_utils.dart'; -import 'package:at_commons/at_builders.dart'; import 'package:at_onboarding_cli/src/factory/service_factories.dart'; import 'package:at_lookup/at_lookup.dart'; import 'package:at_onboarding_cli/at_onboarding_cli.dart'; import 'package:crypton/crypton.dart'; import 'package:encrypt/encrypt.dart'; -import 'package:zxing2/qrcode.dart'; -import 'package:image/image.dart'; import 'package:path/path.dart' as path; import '../util/home_directory_util.dart'; -import '../util/onboarding_util.dart'; ///class containing service that can onboard/activate/authenticate @signs class AtOnboardingServiceImpl implements AtOnboardingService { @@ -41,7 +37,6 @@ class AtOnboardingServiceImpl implements AtOnboardingService { {this.atServiceFactory}) { // performs atSign format checks on the atSign _atSign = AtUtils.fixAtSign(atsign); - // set default LocalStorage paths for this instance atOnboardingPreference.commitLogPath ??= HomeDirectoryUtil.getCommitLogPath(_atSign); @@ -52,6 +47,14 @@ class AtOnboardingServiceImpl implements AtOnboardingService { HomeDirectoryUtil.getAtKeysPath(_atSign); } + Future _init(Map atKeysFileDataMap) async { + atChops ??= _createAtChops(atKeysFileDataMap); + await _initAtClient(atChops!); + _atLookUp!.atChops = atChops; + _atClient!.atChops = atChops; + _atClient!.getPreferences()!.useAtChops = true; + } + Future _initAtClient(AtChops atChops) async { AtClientManager atClientManager = AtClientManager.getInstance(); if (atOnboardingPreference.skipSync == true) { @@ -67,40 +70,21 @@ class AtOnboardingServiceImpl implements AtOnboardingService { _atClient ??= atClientManager.atClient; } - Future _init(Map atKeysFileDataMap) async { - atChops ??= _createAtChops(atKeysFileDataMap); - await _initAtClient(atChops!); - _atLookUp!.atChops = atChops; - _atClient!.atChops = atChops; - _atClient!.getPreferences()!.useAtChops = true; - } - - @override - @Deprecated('Use getter') - Future getAtClient() async { - return _atClient; - } - @override Future onboard() async { + bool cramAuthStatus = false; // cram auth doesn't use at_chops. So create at_lookup here. AtLookupImpl atLookUpImpl = AtLookupImpl(_atSign, atOnboardingPreference.rootDomain, atOnboardingPreference.rootPort); - - // get cram_secret from either from AtOnboardingPreference - // or fetch from the registrar using verification code sent to email - atOnboardingPreference.cramSecret ??= await OnboardingUtil() - .getCramUsingOtp(_atSign, atOnboardingPreference.registrarUrl); - if (atOnboardingPreference.cramSecret == null) { - logger.info('Root Server address is ${atOnboardingPreference.rootDomain}:' - '${atOnboardingPreference.rootPort}'); - logger - .info('Registrar url is \'${atOnboardingPreference.registrarUrl}\''); - throw AtKeyNotFoundException( - 'Could not fetch cram secret for \'$_atSign\' from registrar'); - } - - // check and wait till secondary exists + // fetch cram using verification code if not provided through preferences + atOnboardingPreference.cramSecret ??= await FetchCramSecret( + atLookUp as AtLookupImpl, + logger, + _atSign, + atOnboardingPreference.registrarUrl) + .run(); + // check if secondary exists + // If secondary does not exist wait until secondary server is spun up await _waitUntilSecondaryCreated(atLookUpImpl); if (await isOnboarded()) { @@ -109,25 +93,28 @@ class AtOnboardingServiceImpl implements AtOnboardingService { try { // authenticate into secondary using cram secret - _isAtsignOnboarded = (await atLookUpImpl - .authenticate_cram(atOnboardingPreference.cramSecret)); + cramAuthStatus = await atLookUpImpl + .authenticate_cram(atOnboardingPreference.cramSecret); - if (_isAtsignOnboarded) { + if (cramAuthStatus) { logger.info('Cram authentication successful'); - await _activateAtsign(atLookUpImpl); + _isAtsignOnboarded = await _activateAtsign(atLookUpImpl); } else { throw AtActivateException( 'Cram authentication failed. Please check the cram key' ' and try again \n(or) contact support@atsign.com'); } } on Exception catch (e) { + await _deleteKeysFile(e.toString()); if (e.toString().contains('Auth failed')) { throw AtActivateException( 'Cram authentication failed. Please check the cram key' ' and try again \n(or) contact support@atsign.com'); } - logger.severe('Caught exception: $e'); + logger.finer('Caught exception: $e'); + rethrow; } on Error catch (e) { + await _deleteKeysFile(e.toString()); logger.severe('Caught error: $e'); } finally { await atLookUpImpl.close(); @@ -135,57 +122,56 @@ class AtOnboardingServiceImpl implements AtOnboardingService { return _isAtsignOnboarded; } + Future _fetchCram() async {} + ///method to generate/update encryption key-pairs to activate an atsign - Future _activateAtsign(AtLookupImpl atLookUpImpl) async { - //1. Generate pkam key pair(if authMode is keyFile), encryption key pair and self encryption key - Map atKeysMap = await _generateKeyPairs(); - - //2. generate .atKeys file using a copy of atKeysMap - await _generateAtKeysFile(Map.of(atKeysMap)); - - //3. Updating pkamPublicKey to remote secondary - logger.finer('Updating PkamPublicKey to remote secondary'); - final pkamPublicKey = atKeysMap[AuthKeyType.pkamPublicKey]; - String updateCommand = 'update:$AT_PKAM_PUBLIC_KEY $pkamPublicKey\n'; - String? pkamUpdateResult = - await atLookUpImpl.executeCommand(updateCommand, auth: false); - logger.info('PkamPublicKey update result: $pkamUpdateResult'); - - //4. initialise atClient and atChops and attempt a pkam auth to server. - await _init(atKeysMap); - _isPkamAuthenticated = (await _atLookUp?.pkamAuthenticate())!; - - //5. If Pkam auth is success, update encryption public key to secondary and delete cram key from server - if (_isPkamAuthenticated) { - final encryptionPublicKey = atKeysMap[AuthKeyType.encryptionPublicKey]; - UpdateVerbBuilder updateBuilder = UpdateVerbBuilder() - ..atKey = 'publickey' - ..isPublic = true - ..value = encryptionPublicKey - ..sharedBy = _atSign; - String? encryptKeyUpdateResult = - await atLookUpImpl.executeVerb(updateBuilder); - logger - .info('Encryption public key update result $encryptKeyUpdateResult'); - // deleting cram secret from the keystore as cram auth is complete - DeleteVerbBuilder deleteBuilder = DeleteVerbBuilder() - ..atKey = AT_CRAM_SECRET; - String? deleteResponse = await atLookUpImpl.executeVerb(deleteBuilder); - logger.info('Cram secret delete response : $deleteResponse'); - //displays status of the atsign - logger.finer(await getServerStatus()); - stdout.writeln('[Success]----------atSign activated---------'); - } else { - throw UnAuthenticatedException('Unable to authenticate.' - ' Please provide a valid keys file'); + Future _activateAtsign(AtLookupImpl atLookup) async { + try { + //1. Generate pkam key pair(if authMode is keyFile), encryption key pair and self encryption key + Map atKeysMap = await _generateKeyPairs(); + + //2. generate .atKeys file using a copy of atKeysMap + await _generateAtKeysFile(Map.of(atKeysMap)); + + //3. Updating pkamPublicKey to remote secondary + await SetPkamPublicKey( + atLookup, logger, atKeysMap[AuthKeyType.pkamPublicKey]!) + .run(); + + //4. initialise atClient and atChops and attempt a pkam auth to server. + await _init(atKeysMap); + + //5. Authenticate into secondary using the new PkamKeys + _isPkamAuthenticated = (await _atLookUp?.pkamAuthenticate())!; + + //6. If Pkam auth is success + if (_isPkamAuthenticated) { + //6.1 Update encryption public key to secondary + await SetEncryptionPublicKey( + atLookup, logger, atKeysMap[AuthKeyType.pkamPublicKey]!) + .run(); + + //6.2 Delete cram secret from the keystore as no longer needed + await DeleteCramKey(atLookup, logger).run(); + + //displays status of the atsign + logger.finer(await getServerStatus()); + stdout.writeln('[Success]----------atSign activated---------'); + return true; + } else { + throw UnAuthenticatedException( + 'PkamAuthentication failed hence Activation unsuccessful'); + } + } on Exception catch (e) { + logger.severe('Unable to complete activation | Cause: $e'); + throw UnAuthenticatedException('Activation Failed | Cause: $e'); } } Future> _generateKeyPairs() async { // generate user encryption keypair logger.info('Generating encryption keypair'); - var encryptionKeyPair = generateRsaKeypair(); - + var atEncryptionKeyPair = generateRsaKeypair(); //generate selfEncryptionKey var selfEncryptionKey = generateAESKey(); @@ -212,8 +198,8 @@ class AtOnboardingServiceImpl implements AtOnboardingService { // encryption key pair and self encryption symmetric key // are not available to injected at_chops. Set it here atChops!.atChopsKeys.atEncryptionKeyPair = AtEncryptionKeyPair.create( - encryptionKeyPair.publicKey.toString(), - encryptionKeyPair.privateKey.toString()); + atEncryptionKeyPair.publicKey.toString(), + atEncryptionKeyPair.privateKey.toString()); atChops!.atChopsKeys.symmetricKey = AESKey(selfEncryptionKey); } //Standard order of an atKeys file is -> @@ -221,9 +207,9 @@ class AtOnboardingServiceImpl implements AtOnboardingService { // @sign: selfEncryptionKey[self encryption key again] // note: "->" stands for "followed by" atKeysMap[AuthKeyType.encryptionPublicKey] = - encryptionKeyPair.publicKey.toString(); + atEncryptionKeyPair.publicKey.toString(); atKeysMap[AuthKeyType.encryptionPrivateKey] = - encryptionKeyPair.privateKey.toString(); + atEncryptionKeyPair.privateKey.toString(); atKeysMap[AuthKeyType.selfEncryptionKey] = selfEncryptionKey; atKeysMap[_atSign] = selfEncryptionKey; @@ -285,11 +271,11 @@ class AtOnboardingServiceImpl implements AtOnboardingService { logger .finer('PkamPrivateKey persist to localSecondary: status $response'); } - response = await _atClient?.getLocalSecondary()?.putValue( - '$AT_ENCRYPTION_PUBLIC_KEY$_atSign', - atKeysMap[AuthKeyType.encryptionPublicKey]!); - logger.finer( - 'EncryptionPublicKey persist to localSecondary: status $response'); + // response = await _atClient?.getLocalSecondary()?.putValue( + // '$AT_ENCRYPTION_PUBLIC_KEY$_atSign', + // atKeysMap[AuthKeyType.encryptionPublicKey]!); + // logger.finer( + // 'EncryptionPublicKey persist to localSecondary: status $response'); response = await _atClient?.getLocalSecondary()?.putValue( AT_ENCRYPTION_PRIVATE_KEY, atKeysMap[AuthKeyType.encryptionPrivateKey]!); @@ -321,9 +307,8 @@ class AtOnboardingServiceImpl implements AtOnboardingService { try { _isPkamAuthenticated = (await _atLookUp?.pkamAuthenticate())!; } on Exception catch (e) { - logger.severe('Caught exception: $e'); - throw UnAuthenticatedException('Unable to authenticate.' - ' Please provide a valid keys file'); + logger.severe('Caught exception in \'authenticate()\': $e'); + throw UnAuthenticatedException('Auth failed due to $e'); } logger.finer( 'PKAM auth result: ${_isPkamAuthenticated ? 'success' : 'failed'}'); @@ -346,8 +331,8 @@ class AtOnboardingServiceImpl implements AtOnboardingService { return AtChopsImpl(atChopsKeys); } - ///method to read and return data from .atKeysFile - ///returns map containing encryption keys + /// Method to read and return data from .atKeysFile. + /// Returns map containing encryption keys Future> _readAtKeysFile(String? atKeysFilePath) async { if (atKeysFilePath == null || atKeysFilePath.isEmpty) { throw AtClientException.message( @@ -414,6 +399,7 @@ class AtOnboardingServiceImpl implements AtOnboardingService { try { secondaryStatus = await getServerStatus(); } catch (e) { + // Uses stderr for uniformity with activate_cli log format stderr.writeln('[Error] $e'); } if (secondaryStatus.status() == AtSignStatus.activated) { @@ -422,83 +408,32 @@ class AtOnboardingServiceImpl implements AtOnboardingService { } else if (secondaryStatus.status() == AtSignStatus.teapot) { return false; } + // Uses stderr for uniformity with activate_cli log format stderr.writeln( '[Error] atsign($_atSign) status is \'${secondaryStatus.status()!.name}\''); throw AtActivateException('Could not determine atsign activation status', intent: Intent.fetchData); } - ///extracts cram secret from qrCode - @Deprecated('qr_code based cram authentication not supported anymore') - static String? getSecretFromQr(String? path) { - if (path == null) { - return null; - } - try { - Image? image = decodePng(File(path).readAsBytesSync()); - LuminanceSource source = RGBLuminanceSource( - image!.width, image.height, image.getBytes().buffer.asInt32List()); - BinaryBitmap bitmap = BinaryBitmap(HybridBinarizer(source)); - Result result = QRCodeReader().decode(bitmap); - String secret = result.text.split(':')[1]; - return secret; - } on Exception catch (e) { - stdout.writeln('exception while getting secret from QR code: $e'); - return null; + Future _deleteKeysFile(String cause) async { + if (!(await isOnboarded())) { + File keysFile = File(atOnboardingPreference.atKeysFilePath!); + if (keysFile.existsSync()) { + keysFile.delete(recursive: false); + logger.finer( + 'AtKeys file at path \'${atOnboardingPreference.atKeysFilePath}\'' + ' has been deleted | Cause: $cause}'); + } } } - ///Method to check if secondary belonging to [_atSign] exists + ///Method to check if secondary server for [_atSign] exists ///If not, wait until secondary is created Future _waitUntilSecondaryCreated(AtLookupImpl atLookupImpl) async { - final maxRetries = 50; - int retryCount = 1; - SecondaryAddress? secondaryAddress; - SecureSocket? secureSocket; - bool connectionFlag = false; - - while (retryCount <= maxRetries && secondaryAddress == null) { - await Future.delayed(Duration(seconds: 2)); - logger.finer('retrying find secondary.......$retryCount/$maxRetries'); - try { - secondaryAddress = - await atLookupImpl.secondaryAddressFinder.findSecondary(_atSign); - } on Exception catch (e, trace) { - logger.finer(e); - logger.finer(trace); - } on Error catch (e, trace) { - logger.finer(e); - logger.finer(trace); - } - retryCount++; - } - if (secondaryAddress == null) { - throw SecondaryNotFoundException('Could not find secondary address for ' - '$_atSign after $retryCount retries. Please retry the process'); - } - //resetting retry counter to be used for different operation - retryCount = 1; - - while (!connectionFlag && retryCount <= maxRetries) { - await Future.delayed(Duration(seconds: 2)); - stdout.writeln('Connecting to secondary ...$retryCount/$maxRetries'); - try { - secureSocket = await SecureSocket.connect( - secondaryAddress.host, secondaryAddress.port, - timeout: Duration( - seconds: - 30)); // 30-second timeout should be enough even for slow networks - connectionFlag = secureSocket.remoteAddress != null && - secureSocket.remotePort != null; - } on Exception catch (e, trace) { - logger.finer(e); - logger.finer(trace); - } on Error catch (e, trace) { - logger.finer(e); - logger.finer(trace); - } - retryCount++; - } + SecondaryAddress secondaryAddress = + await FindSecondary(atLookupImpl, logger, _atSign).run(); + + await ConnectToSecondary(atLookupImpl, logger, secondaryAddress).run(); } @override @@ -508,8 +443,7 @@ class AtOnboardingServiceImpl implements AtOnboardingService { } _atLookUp = null; _atClient = null; - logger.info( - 'Closing current instance of at_onboarding_cli (exit code: $exitCode)'); + logger.info('Closing current instance of at_onboarding_cli'); } @override @@ -518,6 +452,12 @@ class AtOnboardingServiceImpl implements AtOnboardingService { return _atLookUp; } + @override + @Deprecated('Use getter') + Future getAtClient() async { + return _atClient; + } + @override set atClient(AtClient? atClient) { _atClient = atClient; From 8c78cd29c11712d598df47f00c4e5d6ea560c8fe Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Wed, 5 Jul 2023 19:14:27 +0530 Subject: [PATCH 6/9] fix: minor fixes --- .../lib/src/onboard/at_onboarding_service_impl.dart | 2 -- packages/at_onboarding_cli/lib/src/register_cli/register.dart | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart b/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart index 7f3af640..41d9ca9d 100644 --- a/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart +++ b/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart @@ -122,8 +122,6 @@ class AtOnboardingServiceImpl implements AtOnboardingService { return _isAtsignOnboarded; } - Future _fetchCram() async {} - ///method to generate/update encryption key-pairs to activate an atsign Future _activateAtsign(AtLookupImpl atLookup) async { try { diff --git a/packages/at_onboarding_cli/lib/src/register_cli/register.dart b/packages/at_onboarding_cli/lib/src/register_cli/register.dart index 3df829d3..4f9191f1 100644 --- a/packages/at_onboarding_cli/lib/src/register_cli/register.dart +++ b/packages/at_onboarding_cli/lib/src/register_cli/register.dart @@ -64,7 +64,7 @@ class Register { .add(ValidateOtp()) .start(); - // activate_cli.main(['-a', params['atsign']!, '-c', params['cramkey']!]); + activate_cli.main(['-a', params['atsign']!, '-c', params['cramkey']!]); } } From bc88c597c4eb038cd0ad687e2e2ab08bad53c229 Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Wed, 5 Jul 2023 19:35:16 +0530 Subject: [PATCH 7/9] fix: analyzer issues --- .../example/get_cram_key_example.dart | 6 +++--- .../lib/src/onboard/at_onboarding_service_impl.dart | 12 ++++++------ .../lib/src/util/at_onboarding_preference.dart | 6 ++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/at_onboarding_cli/example/get_cram_key_example.dart b/packages/at_onboarding_cli/example/get_cram_key_example.dart index d7242148..21ee466b 100644 --- a/packages/at_onboarding_cli/example/get_cram_key_example.dart +++ b/packages/at_onboarding_cli/example/get_cram_key_example.dart @@ -1,9 +1,9 @@ -import 'package:at_onboarding_cli/src/util/onboarding_util.dart'; +import 'package:at_onboarding_cli/src/register_cli/registrar_api_util.dart'; Future main() async { - await OnboardingUtil().requestAuthenticationOtp( + await RegistrarApiUtil().requestAuthenticationOtp( 'your atsign here'); // requires a registered atsign - String cramKey = await OnboardingUtil().getCramKey('your atsign here', + String cramKey = await RegistrarApiUtil().getCramKey('your atsign here', 'verification code'); // verification code received on the registered email print('Your cram key is: $cramKey'); } diff --git a/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart b/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart index 41d9ca9d..06ea7a09 100644 --- a/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart +++ b/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart'; -import 'package:at_onboarding_cli/src/onboard/OnboardingCalls.dart'; +import 'package:at_onboarding_cli/src/onboard/onboarding_tasks.dart'; import 'package:at_onboarding_cli/src/util/at_onboarding_exceptions.dart'; import 'package:at_server_status/at_server_status.dart'; import 'package:at_utils/at_utils.dart'; @@ -269,11 +269,11 @@ class AtOnboardingServiceImpl implements AtOnboardingService { logger .finer('PkamPrivateKey persist to localSecondary: status $response'); } - // response = await _atClient?.getLocalSecondary()?.putValue( - // '$AT_ENCRYPTION_PUBLIC_KEY$_atSign', - // atKeysMap[AuthKeyType.encryptionPublicKey]!); - // logger.finer( - // 'EncryptionPublicKey persist to localSecondary: status $response'); + response = await _atClient?.getLocalSecondary()?.putValue( + '$AT_ENCRYPTION_PUBLIC_KEY$_atSign', + atKeysMap[AuthKeyType.encryptionPublicKey]!); + logger.finer( + 'EncryptionPublicKey persist to localSecondary: status $response'); response = await _atClient?.getLocalSecondary()?.putValue( AT_ENCRYPTION_PRIVATE_KEY, atKeysMap[AuthKeyType.encryptionPrivateKey]!); diff --git a/packages/at_onboarding_cli/lib/src/util/at_onboarding_preference.dart b/packages/at_onboarding_cli/lib/src/util/at_onboarding_preference.dart index c696c8ce..8fabf7c1 100644 --- a/packages/at_onboarding_cli/lib/src/util/at_onboarding_preference.dart +++ b/packages/at_onboarding_cli/lib/src/util/at_onboarding_preference.dart @@ -8,14 +8,12 @@ class AtOnboardingPreference extends AtClientPreference { /// specify path of .atKeysFile containing encryption keys String? atKeysFilePath; - /// specify path of qr code containing cram secret - @Deprecated('qr_code based cram authentication not supported anymore') - String? qrCodePath; - /// signing algorithm to use for pkam authentication + @override SigningAlgoType signingAlgoType = SigningAlgoType.rsa2048; /// hashing algorithm to use for pkam authentication + @override HashingAlgoType hashingAlgoType = HashingAlgoType.sha256; PkamAuthMode authMode = PkamAuthMode.keysFile; From a0e9c1f541bb688ae40ec1591018be1260bb0bff Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Wed, 5 Jul 2023 19:36:45 +0530 Subject: [PATCH 8/9] refactor: OnboardingCalls.dart -> onboarding_tasks.dart --- .../src/onboard/{OnboardingCalls.dart => onboarding_tasks.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/at_onboarding_cli/lib/src/onboard/{OnboardingCalls.dart => onboarding_tasks.dart} (100%) diff --git a/packages/at_onboarding_cli/lib/src/onboard/OnboardingCalls.dart b/packages/at_onboarding_cli/lib/src/onboard/onboarding_tasks.dart similarity index 100% rename from packages/at_onboarding_cli/lib/src/onboard/OnboardingCalls.dart rename to packages/at_onboarding_cli/lib/src/onboard/onboarding_tasks.dart From 9efd8ff3811c85f5fa96e5c384115d8db3d6a52a Mon Sep 17 00:00:00 2001 From: Srie Teja Date: Fri, 29 Sep 2023 11:47:50 +0530 Subject: [PATCH 9/9] refactor: re-arrange methods --- .../src/onboard/at_onboarding_service_impl.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart b/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart index 06ea7a09..93e9df99 100644 --- a/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart +++ b/packages/at_onboarding_cli/lib/src/onboard/at_onboarding_service_impl.dart @@ -47,14 +47,6 @@ class AtOnboardingServiceImpl implements AtOnboardingService { HomeDirectoryUtil.getAtKeysPath(_atSign); } - Future _init(Map atKeysFileDataMap) async { - atChops ??= _createAtChops(atKeysFileDataMap); - await _initAtClient(atChops!); - _atLookUp!.atChops = atChops; - _atClient!.atChops = atChops; - _atClient!.getPreferences()!.useAtChops = true; - } - Future _initAtClient(AtChops atChops) async { AtClientManager atClientManager = AtClientManager.getInstance(); if (atOnboardingPreference.skipSync == true) { @@ -70,6 +62,14 @@ class AtOnboardingServiceImpl implements AtOnboardingService { _atClient ??= atClientManager.atClient; } + Future _init(Map atKeysFileDataMap) async { + atChops ??= _createAtChops(atKeysFileDataMap); + await _initAtClient(atChops!); + _atLookUp!.atChops = atChops; + _atClient!.atChops = atChops; + _atClient!.getPreferences()!.useAtChops = true; + } + @override Future onboard() async { bool cramAuthStatus = false;