Skip to content

Commit

Permalink
feat: replace encryption util encryption methods with at_chops
Browse files Browse the repository at this point in the history
  • Loading branch information
murali-shris committed Jan 10, 2024
1 parent cef5f61 commit 57cb917
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,6 @@ class SharedKeyDecryption implements AtKeyDecryption {
decryptionResultFromAtChops = _atClient.atChops!.decryptString(
encryptedValue, EncryptionKeyType.aes256,
encryptionAlgorithm: encryptionAlgo, iv: iV);
} on AtKeyException catch (e) {
e.stack(AtChainedException(
Intent.decryptData,
ExceptionScenario.decryptionFailed,
'Failed to decrypt ${atKey.toString()}'));
rethrow;
} on AtDecryptionException catch (e) {
_logger.severe(
'decryption exception during of key: ${atKey.key}. Reason: ${e.toString()}');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import 'package:at_client/src/response/default_response_parser.dart';
import 'package:at_client/src/util/at_client_util.dart';
import 'package:at_client/src/util/encryption_util.dart';
import 'package:at_commons/at_commons.dart';
import 'package:at_chops/at_chops.dart';
import 'package:at_utils/at_logger.dart';

///Class responsible for encrypting the selfKey's
class SelfKeyEncryption implements AtKeyEncryption {
late final AtSignLogger _logger;

final AtClient atClient;
final AtClient _atClient;

SelfKeyEncryption(this.atClient) {
SelfKeyEncryption(this._atClient) {
_logger =
AtSignLogger('SelfKeyEncryption (${atClient.getCurrentAtSign()})');
AtSignLogger('SelfKeyEncryption (${_atClient.getCurrentAtSign()})');
}

@override
Expand All @@ -29,12 +30,27 @@ class SelfKeyEncryption implements AtKeyEncryption {
}
// Get AES key for current atSign
var selfEncryptionKey =
await _getSelfEncryptionKey(atClient.getLocalSecondary()!);
await _getSelfEncryptionKey(_atClient.getLocalSecondary()!);
selfEncryptionKey =
DefaultResponseParser().parse(selfEncryptionKey).response;
// Encrypt value using sharedKey
return EncryptionUtil.encryptValue(value, selfEncryptionKey,
ivBase64: atKey.metadata.ivNonce);
AtEncryptionResult encryptionResultFromAtChops;
try {
InitialisationVector iV;
if (atKey.metadata.ivNonce != null) {
iV = AtChopsUtil.generateIVFromBase64String(atKey.metadata.ivNonce!);
} else {
iV = AtChopsUtil.generateIVLegacy();
}
var encryptionAlgo = AESEncryptionAlgo(AESKey(selfEncryptionKey));
encryptionResultFromAtChops = _atClient.atChops!.encryptString(
value, EncryptionKeyType.aes256,
encryptionAlgorithm: encryptionAlgo, iv: iV);
} on AtEncryptionException catch (e) {
_logger.severe(
'encryption exception during self encryption of key: ${atKey.key}. Reason: ${e.toString()}');
rethrow;
}
return encryptionResultFromAtChops.result;
}

Future<String> _getSelfEncryptionKey(LocalSecondary localSecondary) async {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import 'package:at_client/at_client.dart';
import 'package:at_utils/at_logger.dart';
import 'package:at_chops/at_chops.dart';
import 'package:at_client/src/encryption_service/abstract_atkey_encryption.dart';

///Class responsible for encrypting the value of the SharedKey's
class SharedKeyEncryption extends AbstractAtKeyEncryption {
SharedKeyEncryption(AtClient atClient) : super(atClient);
final AtClient _atClient;
late final AtSignLogger _logger;
SharedKeyEncryption(this._atClient) : super(_atClient) {
_logger =
AtSignLogger('SelfKeyEncryption (${_atClient.getCurrentAtSign()})');
}

@override
Future<dynamic> encrypt(AtKey atKey, dynamic value,
Expand All @@ -17,9 +24,23 @@ class SharedKeyEncryption extends AbstractAtKeyEncryption {
// encryption key and setting it in super.sharedKey
await super.encrypt(atKey, value,
storeSharedKeyEncryptedWithData: storeSharedKeyEncryptedWithData);

// Encrypt the value
return EncryptionUtil.encryptValue(value, sharedKey,
ivBase64: atKey.metadata.ivNonce);
AtEncryptionResult encryptionResultFromAtChops;
try {
InitialisationVector iV;
if (atKey.metadata.ivNonce != null) {
iV = AtChopsUtil.generateIVFromBase64String(atKey.metadata.ivNonce!);
} else {
iV = AtChopsUtil.generateIVLegacy();
}
var encryptionAlgo = AESEncryptionAlgo(AESKey(sharedKey));
encryptionResultFromAtChops = _atClient.atChops!.encryptString(
value, EncryptionKeyType.aes256,
encryptionAlgorithm: encryptionAlgo, iv: iV);
} on AtEncryptionException catch (e) {
_logger.severe(
'encryption exception during shared key encryption of key: ${atKey.key}. Reason: ${e.toString()}');
rethrow;
}
return encryptionResultFromAtChops.result;
}
}
73 changes: 28 additions & 45 deletions packages/at_client/test/encryption_service_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ void main() {
when(() => mockAtClient.atChops).thenAnswer((_) => mockAtChops);
when(() => mockAtClient.getLocalSecondary())
.thenAnswer((_) => mockLocalSecondary);
when(() => mockAtClient.getRemoteSecondary())
.thenAnswer((_) => mockRemoteSecondary);
mockSigningResult = AtSigningResult()..result = 'mock_signing_result';
registerFallbackValue(FakeAtSigningInput());
when(() => mockAtChops.sign(any())).thenAnswer((_) => mockSigningResult);
Expand Down Expand Up @@ -76,42 +78,6 @@ void main() {
});
});

group('A group of tests related positive scenario of encryption', () {
test(
'A test to verify value gets legacy encrypted when self encryption key is available',
() async {
var selfEncryptionKey = 'REqkIcl9HPekt0T7+rZhkrBvpysaPOeC2QL1PVuWlus=';
var value = 'self_key_value';
when(() => mockLocalSecondary.getEncryptionSelfKey())
.thenAnswer((_) => Future.value(selfEncryptionKey));
var selfKeyEncryption = SelfKeyEncryption(mockAtClient);
var encryptedData = await selfKeyEncryption.encrypt(
AtKey.self('phone', namespace: 'wavi').build(), value);
var response =
EncryptionUtil.decryptValue(encryptedData, selfEncryptionKey);
expect(response, value);
});

test(
'A test to verify value gets encrypted when self encryption key is available',
() async {
var selfEncryptionKey = 'REqkIcl9HPekt0T7+rZhkrBvpysaPOeC2QL1PVuWlus=';
var value = 'self_key_value';
when(() => mockLocalSecondary.getEncryptionSelfKey())
.thenAnswer((_) => Future.value(selfEncryptionKey));
var selfKeyEncryption = SelfKeyEncryption(mockAtClient);

var atKey = AtKey.self('phone', namespace: 'wavi').build();
atKey.metadata.ivNonce = EncryptionUtil.generateIV();

var encryptedData = await selfKeyEncryption.encrypt(atKey, value);
var response = EncryptionUtil.decryptValue(
encryptedData, selfEncryptionKey,
ivBase64: atKey.metadata.ivNonce);
expect(response, value);
});
});

group('A group of test to sign the public data', () {
test('A test to verify the sign the public data', () async {
String encryptionPrivateKey =
Expand Down Expand Up @@ -370,10 +336,11 @@ void main() {
when(() => mockLocalSecondary
.executeVerb(any(that: EncryptionPublicKeyMatcher())))
.thenAnswer((_) => Future.value(encryptionPublicKey));
when(() => mockAtChops.decryptString(
encryptedSharedKey, EncryptionKeyType.rsa2048))
.thenAnswer((_) => (AtEncryptionResult()..result = sharedKey));

var encryptionKeyPair =
AtEncryptionKeyPair.create(encryptionPublicKey, encryptionPrivateKey);
AtChopsKeys atChopsKeys = AtChopsKeys.create(encryptionKeyPair, null);
var atChopsImpl = AtChopsImpl(atChopsKeys);
when(() => mockAtClient.atChops).thenAnswer((_) => atChopsImpl);
var encryptedValue = await sharedKeyEncryption.encrypt(atKey, value);
expect(atKey.metadata.sharedKeyEnc.isNotNull, true);
expect(atKey.metadata.pubKeyCS.isNotNull, true);
Expand Down Expand Up @@ -407,10 +374,11 @@ void main() {
when(() => mockLocalSecondary
.executeVerb(any(that: EncryptionPublicKeyMatcher())))
.thenAnswer((_) => Future.value(encryptionPublicKey));
when(() => mockAtChops.decryptString(
encryptedSharedKey, EncryptionKeyType.rsa2048))
.thenAnswer((_) => (AtEncryptionResult()..result = sharedKey));

var encryptionKeyPair =
AtEncryptionKeyPair.create(encryptionPublicKey, encryptionPrivateKey);
AtChopsKeys atChopsKeys = AtChopsKeys.create(encryptionKeyPair, null);
var atChopsImpl = AtChopsImpl(atChopsKeys);
when(() => mockAtClient.atChops).thenAnswer((_) => atChopsImpl);
var encryptedValue = await sharedKeyEncryption.encrypt(atKey, value);
var decryptedSharedKey =
// ignore: deprecated_member_use_from_same_package
Expand Down Expand Up @@ -453,6 +421,11 @@ void main() {
sync: false)).thenAnswer((_) => Future.value('data:1'));
when(() => mockLocalSecondary.getEncryptionPublicKey('@alice'))
.thenAnswer((_) => Future.value(encryptionPublicKey));
var encryptionKeyPair =
AtEncryptionKeyPair.create(encryptionPublicKey, encryptionPrivateKey);
AtChopsKeys atChopsKeys = AtChopsKeys.create(encryptionKeyPair, null);
var atChopsImpl = AtChopsImpl(atChopsKeys);
when(() => mockAtClient.atChops).thenAnswer((_) => atChopsImpl);

var atKey = (AtKey.shared('phone', namespace: 'wavi', sharedBy: '@alice')
..sharedWith('@bob'))
Expand Down Expand Up @@ -490,9 +463,17 @@ void main() {
.thenAnswer((_) => Future.value(encryptionPublicKey));
when(() => mockLocalSecondary.executeVerb(
any(that: UpdateEncryptedSharedKeyMatcher()),
sync: true)).thenAnswer((_) => Future.value('data:1'));
sync: false)).thenAnswer((_) => Future.value('data:1'));
when(() => mockRemoteSecondary.executeVerb(
any(that: UpdateEncryptedSharedKeyMatcher()),
sync: false)).thenAnswer((_) => Future.value('data:1'));
when(() => mockLocalSecondary.getEncryptionPublicKey('@alice'))
.thenAnswer((_) => Future.value(encryptionPublicKey));
var encryptionKeyPair =
AtEncryptionKeyPair.create(encryptionPublicKey, encryptionPrivateKey);
AtChopsKeys atChopsKeys = AtChopsKeys.create(encryptionKeyPair, null);
var atChopsImpl = AtChopsImpl(atChopsKeys);
when(() => mockAtClient.atChops).thenAnswer((_) => atChopsImpl);

var atKey = (AtKey.shared('phone', namespace: 'wavi', sharedBy: '@alice')
..sharedWith('@bob'))
Expand Down Expand Up @@ -597,7 +578,9 @@ class UpdateEncryptedSharedKeyMatcher extends Matcher {

@override
bool matches(item, Map matchState) {
print('inside matches');
if (item is UpdateVerbBuilder && item.atKey.key.contains('shared_key')) {
print('match');
return true;
}
return false;
Expand Down
78 changes: 78 additions & 0 deletions packages/at_client/test/self_key_encryption_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:at_chops/at_chops.dart';
import 'package:at_client/src/decryption_service/self_key_decryption.dart';
import 'package:at_client/src/encryption_service/self_key_encryption.dart';
import 'package:at_commons/at_builders.dart';
import 'package:test/test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:at_client/at_client.dart';
import 'package:at_lookup/at_lookup.dart';

class MockAtClientImpl extends Mock implements AtClient {}

class MockLocalSecondary extends Mock implements LocalSecondary {}

class MockRemoteSecondary extends Mock implements RemoteSecondary {}

class MockAtLookupImpl extends Mock implements AtLookUp {}

class FakeLocalLookUpVerbBuilder extends Fake implements LLookupVerbBuilder {}

void main() {
AtClient mockAtClient = MockAtClientImpl();
AtLookUp mockAtLookUp = MockAtLookupImpl();
LocalSecondary mockLocalSecondary = MockLocalSecondary();
RemoteSecondary mockRemoteSecondary = MockRemoteSecondary();
setUp(() {
reset(mockAtLookUp);
when(() => mockAtClient.getLocalSecondary())
.thenAnswer((_) => mockLocalSecondary);
when(() => mockAtClient.getRemoteSecondary())
.thenAnswer((_) => mockRemoteSecondary);

registerFallbackValue(FakeLocalLookUpVerbBuilder());
});

test('test to check encryption/decryption of self keys', () async {
// This test encrypts a self key value and then checks whether decrypted value is same as original value
// If @alice wants to maintain a location without sharing to anyone then the key-value format will be @alice:location@alice New Jersey
// @alice uses self encryption AES key generated during onboarding process to encrypt the value. Same key is used for decryption
var selfKeyEncryption = SelfKeyEncryption(mockAtClient);
var selfKeyDecryption = SelfKeyDecryption(mockAtClient);
// generate new AES key for the test
var aliceSelfEncryptionKey =
AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256).key;
// set atChops
AtChopsKeys atChopsKeys = AtChopsKeys.create(null, null);
atChopsKeys.selfEncryptionKey = AESKey(aliceSelfEncryptionKey);
var atChopsImpl = AtChopsImpl(atChopsKeys);
when(() => mockAtClient.atChops).thenAnswer((_) => atChopsImpl);
when(() => mockLocalSecondary.getEncryptionSelfKey())
.thenAnswer((_) => Future.value(aliceSelfEncryptionKey));
var selfKey = AtKey()
..sharedBy = '@alice'
..sharedWith = '@bob'
..key = 'location';
var location = 'New Jersey';
var encryptedValue = await selfKeyEncryption.encrypt(selfKey, location);
expect(encryptedValue != location, true);
var decryptionResult =
await selfKeyDecryption.decrypt(selfKey, encryptedValue);
expect(decryptionResult, location);
});
test(
'test to check self key encryption throws exception when passed value is not string type',
() async {
var selfKeyEncryption = SelfKeyEncryption(mockAtClient);
var selfKey = AtKey()
..sharedBy = '@alice'
..sharedWith = '@bob'
..key = 'location';
var locations = ['new jersey', 'new york'];
expect(
() async => await selfKeyEncryption.encrypt(selfKey, locations),
throwsA(predicate((dynamic e) =>
e is AtEncryptionException &&
e.message ==
'Invalid value type found: List<String>. Valid value type is String')));
});
}
Loading

0 comments on commit 57cb917

Please sign in to comment.