Skip to content

Commit

Permalink
feat: modified examples for apkam enrollment
Browse files Browse the repository at this point in the history
  • Loading branch information
murali-shris committed Oct 26, 2023
1 parent 5b07733 commit 1fcd19d
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 21 deletions.
30 changes: 30 additions & 0 deletions packages/at_onboarding_cli/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
List of steps to run the examples for checking apkam enrollment

1. Onboard an atsign which has privilege to approve/deny enrollments
dart example/onboard.dart <atsign> <path_store_keys_file>
e.g. dart example/onboard.dart @alice🛠 /home/alice/.atsign/@alice🛠_wavikey.atKeys
2. Authenticate using the onboarded atsign
dart example/apkam_authenticate.dart <atsign> <path_of_keys_file_from_#1>
e.g. dart example/apkam_authenticate.dart @alice🛠 /home/alice/.atsign/@alice🛠_wavikey.atKeys
3. Run client to approve enrollments
dart example/enroll_app_listen.dart <atsign> <path_of_keys_file_from_#1>
e.g dart example/enroll_app_listen.dart @alice🛠 /home/alice/.atsign/@alice🛠_wavikey.atKeys
4. Get OTP for enrollment
- 4.1 Pkam through ssl client
pkam:enrollmentId:<enrollmentId>:<pkamSignature>
enrollmentId - get from the .atKeys file
pkamChallenge - generate using the below commnd
at_tools/packages/at_pkam>
dart bin/main.dart -p <keys_file_path> <from_response>
e.g dart bin/main.dart -p /home/alice/.atsign/@alice🛠_wavikey.atKeys -r _70138292-07b5-4e47-8c94-e02e38220775@alice🛠:883ea0aa-c526-400a-926e-48cae9281de9
- 4.2 Once authenticated run otp:get
5. Request enrollment
- 5.1 Submit enrollment from new client
dart example/apkam_enroll.dart <atsign> <path_to_store_keys_file> <otp>
e.g. dart example/apkam_enroll.dart @alice🛠 /home/murali/.atsign/@alice🛠_buzzkey.atKeys DY4UT4
- 5.2 Approve the enrollment from the client from #3
- 5.3 Enrollment should be successful and keys file stored in the path specified
6. Authenticate using the enrolled keys file
- 6.1 dart example/onboard.dart <atsign> <path_of_keys_file_from_#5.1>


23 changes: 17 additions & 6 deletions packages/at_onboarding_cli/example/apkam_authenticate.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
import 'dart:convert';
import 'dart:io';

import 'package:at_commons/at_commons.dart';
import 'package:at_onboarding_cli/at_onboarding_cli.dart';
import 'package:at_utils/at_logger.dart';

Future<void> main() async {
// final enrollIdFromServer = '867307c7-53bd-4736-8fe7-1520de58ce78';
AtSignLogger.root_level = 'finest';
final atSign = '@alice🛠';
Future<void> main(List<String> args) async {
AtSignLogger.root_level = 'info';
final atSign = args[0];
AtOnboardingPreference atOnboardingPreference = AtOnboardingPreference()
..namespace =
'wavi' // unique identifier that can be used to identify data from your app
..atKeysFilePath = '/home/murali/.atsign/@alice🛠_key.atKeys'
..atKeysFilePath = args[1]
..rootDomain = 'vip.ve.atsign.zone';
AtOnboardingService? onboardingService = AtOnboardingServiceImpl(
atSign, atOnboardingPreference);
atSign, atOnboardingPreference,
enrollmentId: _getEnrollmentIdFromKeysFile(args[1]));
await onboardingService.authenticate(); // when authenticating
// AtLookUp? atLookup = onboardingService.atLookUp;
// AtClient? client = onboardingService.atClient;
// print(await client?.getKeys());
// print(await atLookup?.scan(regex: 'publickey'));
// await onboardingService.close();
}

String _getEnrollmentIdFromKeysFile(String keysFilePath) {
String atAuthData = File(keysFilePath).readAsStringSync();
final enrollmentId = jsonDecode(atAuthData)[AtConstants.enrollmentId];
print('**** enrollmentId: $enrollmentId');
return enrollmentId;
}
22 changes: 17 additions & 5 deletions packages/at_onboarding_cli/example/apkam_enroll.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import 'dart:convert';
import 'dart:io';

import 'package:at_client/at_client.dart';
import 'package:at_onboarding_cli/at_onboarding_cli.dart';
import 'package:at_utils/at_logger.dart';

Future<void> main() async {
Future<void> main(List<String> args) async {
AtSignLogger.root_level = 'finer';
final atSign = '@alice';
final atSign = args[0];
AtOnboardingPreference atOnboardingPreference = AtOnboardingPreference()
..namespace =
'buzz' // unique identifier that can be used to identify data from your app
..atKeysFilePath = '/home/user/atsign/alice_buzzkey.atKeys'
..atKeysFilePath = args[1]
..appName = 'buzz'
..deviceName = 'iphone'
..rootDomain = 'vip.ve.atsign.zone';
..rootDomain = 'vip.ve.atsign.zone'
..apkamAuthRetryDurationMins = 3;
AtOnboardingService? onboardingService =
AtOnboardingServiceImpl(atSign, atOnboardingPreference);
Map<String, String> namespaces = {"buzz": "rw"};
// run totp:get from enrolled client and pass the otp
var enrollmentResponse =
await onboardingService.enroll('buzz', 'iphone', "068881", namespaces);
await onboardingService.enroll('buzz', 'iphone', args[2], namespaces);
print('enrollmentResponse: $enrollmentResponse');
}

String _getEnrollmentIdFromKeysFile(String keysFilePath) {
String atAuthData = File(keysFilePath).readAsStringSync();
final enrollmentId = jsonDecode(atAuthData)[AtConstants.enrollmentId];
print('**** enrollmentId: $enrollmentId');
return enrollmentId;
}
137 changes: 137 additions & 0 deletions packages/at_onboarding_cli/example/enroll_app_listen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import 'dart:convert';

import 'package:at_auth/at_auth.dart';
import 'package:at_chops/at_chops.dart';
import 'package:at_client/at_client.dart';
import 'dart:io';
import 'package:at_auth/src/auth_constants.dart' as auth_constants;

import 'atsign_preference.dart';

/// dart enroll_app_listen.dart <atsign> <path_to_key_file>
void main(List<String> arguments) async {
var aliceAtSign = arguments[0];
try {
var atAuthKeys = _decryptAtKeysFile(await _readAtKeysFile(arguments[1]));
var atChops = _createAtChops(atAuthKeys);
final atClientManager = await AtClientManager.getInstance()
.setCurrentAtSign(
aliceAtSign,
'wavi',
AtSignPreference.getAlicePreference(
aliceAtSign, atAuthKeys.enrollmentId!),
atChops: atChops,
enrollmentId: atAuthKeys.enrollmentId);

// alice - listen for notification
atClientManager.atClient.notificationService
.subscribe(regex: '.__manage')
.listen((notification) {
_notificationCallback(notification, atClientManager.atClient, atAuthKeys);
});
} on Exception catch (e, trace) {
print(e.toString());
print(trace);
}

print('end of test');
}

Future<void> _notificationCallback(AtNotification notification,
AtClient atClient, AtAuthKeys atAuthKeys) async {
print('alice enroll notification received: ${notification.toString()}');
final notificationKey = notification.key;
final enrollmentId =
notificationKey.substring(0, notificationKey.indexOf('.new.enrollments'));
print('Approve enrollmentId $enrollmentId?');
String? approveResponse = stdin.readLineSync();
print('approved?: $approveResponse');
var enrollRequest;
var enrollParamsJson = {};
enrollParamsJson['enrollmentId'] = enrollmentId;
if (approveResponse == 'yes') {
final encryptedApkamSymmetricKey =
jsonDecode(notification.value!)['encryptedApkamSymmetricKey'];
final apkamSymmetricKey = EncryptionUtil.decryptKey(
encryptedApkamSymmetricKey, atAuthKeys.defaultEncryptionPrivateKey!);
print('decrypted apkam symmetric key: $apkamSymmetricKey');
var encryptedDefaultPrivateEncKey = EncryptionUtil.encryptValue(
atAuthKeys.defaultEncryptionPrivateKey!, apkamSymmetricKey);
var encryptedDefaultSelfEncKey = EncryptionUtil.encryptValue(
atAuthKeys.defaultSelfEncryptionKey!, apkamSymmetricKey);
enrollParamsJson['encryptedDefaultEncryptedPrivateKey'] =
encryptedDefaultPrivateEncKey;
enrollParamsJson['encryptedDefaultSelfEncryptionKey'] =
encryptedDefaultSelfEncKey;
enrollRequest = 'enroll:approve:${jsonEncode(enrollParamsJson)}\n';
} else {
enrollRequest = 'enroll:deny:${jsonEncode(enrollParamsJson)}\n';
}
print('enroll request to server: $enrollRequest');
String? enrollResponse = await atClient
.getRemoteSecondary()!
.executeCommand(enrollRequest, auth: true);
print('enrollResponse: $enrollResponse');
}

AtAuthKeys _decryptAtKeysFile(Map<String, String> jsonData) {
var securityKeys = AtAuthKeys();
String decryptionKey = jsonData[auth_constants.defaultSelfEncryptionKey]!;
var atChops =
AtChopsImpl(AtChopsKeys()..selfEncryptionKey = AESKey(decryptionKey));
securityKeys.defaultEncryptionPublicKey = atChops
.decryptString(jsonData[auth_constants.defaultEncryptionPublicKey]!,
EncryptionKeyType.aes256,
keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy())
.result;
securityKeys.defaultEncryptionPrivateKey = atChops
.decryptString(jsonData[auth_constants.defaultEncryptionPrivateKey]!,
EncryptionKeyType.aes256,
keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy())
.result;
securityKeys.defaultSelfEncryptionKey = decryptionKey;
securityKeys.apkamPublicKey = atChops
.decryptString(
jsonData[auth_constants.apkamPublicKey]!, EncryptionKeyType.aes256,
keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy())
.result;
securityKeys.apkamPrivateKey = atChops
.decryptString(
jsonData[auth_constants.apkamPrivateKey]!, EncryptionKeyType.aes256,
keyName: 'selfEncryptionKey', iv: AtChopsUtil.generateIVLegacy())
.result;
securityKeys.apkamSymmetricKey = jsonData[auth_constants.apkamSymmetricKey];
securityKeys.enrollmentId = jsonData[AtConstants.enrollmentId];
return securityKeys;
}

Future<Map<String, String>> _readAtKeysFile(String? atKeysFilePath) async {
if (atKeysFilePath == null || atKeysFilePath.isEmpty) {
throw AtException(
'atKeys filePath is empty. atKeysFile is required to authenticate');
}
if (!File(atKeysFilePath).existsSync()) {
throw AtException(
'provided keys file does not exist. Please check whether the file path $atKeysFilePath is valid');
}
String atAuthData = await File(atKeysFilePath).readAsString();
Map<String, String> jsonData = <String, String>{};
json.decode(atAuthData).forEach((String key, dynamic value) {
jsonData[key] = value.toString();
});
return jsonData;
}

AtChops _createAtChops(AtAuthKeys atKeysFile) {
final atEncryptionKeyPair = AtEncryptionKeyPair.create(
atKeysFile.defaultEncryptionPublicKey!,
atKeysFile.defaultEncryptionPrivateKey!);
final atPkamKeyPair = AtPkamKeyPair.create(
atKeysFile.apkamPublicKey!, atKeysFile.apkamPrivateKey!);
final atChopsKeys = AtChopsKeys.create(atEncryptionKeyPair, atPkamKeyPair);
if (atKeysFile.apkamSymmetricKey != null) {
atChopsKeys.apkamSymmetricKey = AESKey(atKeysFile.apkamSymmetricKey!);
}
atChopsKeys.selfEncryptionKey = AESKey(atKeysFile.defaultSelfEncryptionKey!);
return AtChopsImpl(atChopsKeys);
}
6 changes: 3 additions & 3 deletions packages/at_onboarding_cli/example/onboard.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import 'package:at_onboarding_cli/at_onboarding_cli.dart';
import 'package:at_utils/at_logger.dart';

Future<void> main() async {
Future<void> main(List<String> args) async {
AtSignLogger.root_level = 'finest';
final atSign = '@alice🛠';
final atSign = args[0];
AtOnboardingPreference atOnboardingPreference = AtOnboardingPreference()
..namespace =
'wavi' // unique identifier that can be used to identify data from your app
..cramSecret =
'b26455a907582760ebf35bc4847de549bc41c24b25c8b1c58d5964f7b4f8a43bc55b0e9a601c9a9657d9a8b8bbc32f88b4e38ffaca03c8710ebae1b14ca9f364'
..atKeysFilePath = '/home/murali/.atsign/@alice🛠_key.atKeys'
..atKeysFilePath = args[1]
..appName = 'wavi'
..deviceName = 'pixel'
..rootDomain = 'vip.ve.atsign.zone'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ class AtOnboardingServiceImpl implements AtOnboardingService {

// set default LocalStorage paths for this instance
atOnboardingPreference.commitLogPath ??=
HomeDirectoryUtil.getCommitLogPath(_atSign);
HomeDirectoryUtil.getCommitLogPath(_atSign, enrollmentId: enrollmentId);
atOnboardingPreference.hiveStoragePath ??=
HomeDirectoryUtil.getHiveStoragePath(_atSign);
HomeDirectoryUtil.getHiveStoragePath(_atSign,
enrollmentId: enrollmentId);
atOnboardingPreference.isLocalStoreRequired = true;
atOnboardingPreference.atKeysFilePath ??=
HomeDirectoryUtil.getAtKeysPath(_atSign);
Expand Down
18 changes: 13 additions & 5 deletions packages/at_onboarding_cli/lib/src/util/home_directory_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,27 @@ class HomeDirectoryUtil {
return path.join(homeDir!, '.atsign', 'keys', '${atsign}_key.atKeys');
}

static String getStorageDirectory(String atsign) {
static String getStorageDirectory(String atsign, {String? enrollmentId}) {
if (homeDir == null) {
throw AtClientException.message('Could not find home directory');
}
if (enrollmentId != null) {
return path.join(homeDir!, '.atsign', 'at_onboarding_cli', 'storage',
atsign, enrollmentId);
}
return path.join(
homeDir!, '.atsign', 'at_onboarding_cli', 'storage', atsign);
}

static String getCommitLogPath(String atsign) {
return path.join(getStorageDirectory(atsign), 'commitLog');
static String getCommitLogPath(String atsign, {String? enrollmentId}) {
return path.join(
getStorageDirectory(atsign, enrollmentId: enrollmentId), 'commitLog');
}

static String getHiveStoragePath(String atsign) {
return path.join(HomeDirectoryUtil.getStorageDirectory(atsign), 'hive');
static String getHiveStoragePath(String atsign, {String? enrollmentId}) {
return path.join(
HomeDirectoryUtil.getStorageDirectory(atsign,
enrollmentId: enrollmentId),
'hive');
}
}
8 changes: 8 additions & 0 deletions packages/at_onboarding_cli/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ dependencies:
at_server_status: ^1.0.3
at_utils: ^3.0.15

dependency_overrides:
at_client:
git:
url: https://github.com/atsign-foundation/at_client_sdk/
path: packages/at_client
ref: enrollmentid_bug


dev_dependencies:
lints: ^2.1.0
test: ^1.24.2
Expand Down

0 comments on commit 1fcd19d

Please sign in to comment.