Skip to content

Commit

Permalink
Merge pull request #1185 from atsign-foundation/replace_encryption_ut…
Browse files Browse the repository at this point in the history
…il_methods

feat: Replace EncryptionUtil decryption methods with at_chops
  • Loading branch information
murali-shris authored Jan 3, 2024
2 parents 795a70f + a3612b1 commit e43bc1a
Show file tree
Hide file tree
Showing 8 changed files with 540 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:at_chops/at_chops.dart';
import 'package:at_client/at_client.dart';
import 'package:at_client/src/decryption_service/decryption.dart';
import 'package:at_client/src/encryption_service/abstract_atkey_encryption.dart';
Expand All @@ -10,10 +11,11 @@ import 'package:at_utils/at_logger.dart';
class LocalKeyDecryption extends AbstractAtKeyEncryption
implements AtKeyDecryption {
late final AtSignLogger _logger;
final AtClient _atClient;

LocalKeyDecryption(AtClient atClient) : super(atClient) {
LocalKeyDecryption(this._atClient) : super(_atClient) {
_logger =
AtSignLogger('LocalKeyDecryption (${atClient.getCurrentAtSign()})');
AtSignLogger('LocalKeyDecryption (${_atClient.getCurrentAtSign()})');
}

@override
Expand All @@ -32,7 +34,25 @@ class LocalKeyDecryption extends AbstractAtKeyEncryption
intent: Intent.fetchEncryptionSharedKey,
exceptionScenario: ExceptionScenario.fetchEncryptionKeys);
}
return EncryptionUtil.decryptValue(encryptedValue, symmetricKey,
ivBase64: atKey.metadata?.ivNonce);
InitialisationVector iV;
if (atKey.metadata?.ivNonce != null) {
iV = AtChopsUtil.generateIVFromBase64String(atKey.metadata!.ivNonce!);
} else {
iV = AtChopsUtil.generateIVLegacy();
}
AtEncryptionResult decryptionResultFromAtChops;
try {
var encryptionAlgo = AESEncryptionAlgo(AESKey(symmetricKey));
decryptionResultFromAtChops = _atClient.atChops!.decryptString(
encryptedValue, EncryptionKeyType.aes256,
encryptionAlgorithm: encryptionAlgo, iv: iV);
_logger.finer(
'decryptionResultFromAtChops: ${decryptionResultFromAtChops.result}');
} on AtDecryptionException catch (e) {
_logger.severe(
'decryption exception during of key: ${atKey.key}. Reason: ${e.toString()}');
rethrow;
}
return decryptionResultFromAtChops.result;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:at_client/at_client.dart';
import 'package:at_chops/at_chops.dart';
import 'package:at_utils/at_logger.dart';
import 'package:at_client/src/decryption_service/decryption.dart';
import 'package:at_client/src/response/default_response_parser.dart';

Expand All @@ -9,7 +11,11 @@ import 'package:at_client/src/response/default_response_parser.dart';
/// llookup:@bob:phone@bob
class SelfKeyDecryption implements AtKeyDecryption {
final AtClient _atClient;
SelfKeyDecryption(this._atClient);
late final AtSignLogger _logger;
SelfKeyDecryption(this._atClient) {
_logger =
AtSignLogger('SelfKeyDecryption (${_atClient.getCurrentAtSign()})');
}
@override
Future<dynamic> decrypt(AtKey atKey, dynamic encryptedValue) async {
if (encryptedValue == null ||
Expand All @@ -29,8 +35,24 @@ class SelfKeyDecryption implements AtKeyDecryption {
exceptionScenario: ExceptionScenario.fetchEncryptionKeys);
}

return EncryptionUtil.decryptValue(encryptedValue,
DefaultResponseParser().parse(selfEncryptionKey).response,
ivBase64: atKey.metadata?.ivNonce);
InitialisationVector iV;
if (atKey.metadata?.ivNonce != null) {
iV = AtChopsUtil.generateIVFromBase64String(atKey.metadata!.ivNonce!);
} else {
iV = AtChopsUtil.generateIVLegacy();
}
AtEncryptionResult decryptionResultFromAtChops;
try {
var encryptionAlgo = AESEncryptionAlgo(
AESKey(DefaultResponseParser().parse(selfEncryptionKey).response));
decryptionResultFromAtChops = _atClient.atChops!.decryptString(
encryptedValue, EncryptionKeyType.aes256,
encryptionAlgorithm: encryptionAlgo, iv: iV);
} on AtDecryptionException catch (e) {
_logger.severe(
'decryption exception during of key: ${atKey.key}. Reason: ${e.toString()}');
rethrow;
}
return decryptionResultFromAtChops.result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import 'package:at_client/src/util/encryption_util.dart';
import 'package:at_commons/at_builders.dart';
import 'package:at_commons/at_commons.dart';
import 'package:at_utils/at_logger.dart';
import 'package:meta/meta.dart';
import 'package:at_chops/at_chops.dart';

/// Class responsible for decrypting the value of shared key's that are not owned
Expand All @@ -14,13 +13,12 @@ import 'package:at_chops/at_chops.dart';
/// CurrentAtSign: @bob
/// lookup:phone@alice
class SharedKeyDecryption implements AtKeyDecryption {
@visibleForTesting
final AtClient atClient;
final AtClient _atClient;
late final AtSignLogger _logger;

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

@override
Expand All @@ -31,64 +29,76 @@ class SharedKeyDecryption implements AtKeyDecryption {
exceptionScenario: ExceptionScenario.decryptionFailed);
}
String? encryptedSharedKey;
if (atKey.metadata != null && atKey.metadata!.pubKeyCS != null) {
if (atKey.metadata != null) {
encryptedSharedKey = atKey.metadata!.sharedKeyEnc;
String? currentAtSignPublicKey;
try {
currentAtSignPublicKey = (await atClient
.getLocalSecondary()!
.getEncryptionPublicKey(atClient.getCurrentAtSign()!))
?.trim();
} on KeyNotFoundException {
throw AtPublicKeyNotFoundException(
'Failed to fetch the current atSign public key - public:publickey${atClient.getCurrentAtSign()!}',
intent: Intent.fetchEncryptionPublicKey,
exceptionScenario: ExceptionScenario.localVerbExecutionFailed);
}
if (currentAtSignPublicKey != null &&
atKey.metadata!.pubKeyCS !=
EncryptionUtil.md5CheckSum(currentAtSignPublicKey)) {
throw AtPublicKeyChangeException(
'Public key has changed. Cannot decrypt shared key ${atKey.toString()}',
intent: Intent.fetchEncryptionPublicKey,
exceptionScenario: ExceptionScenario.encryptionFailed);
}
} else {
encryptedSharedKey = await _getEncryptedSharedKey(atKey);
}
if (encryptedSharedKey == null ||
encryptedSharedKey.isEmpty ||
encryptedSharedKey == 'null') {
encryptedSharedKey ??= await _getEncryptedSharedKey(atKey);
if (encryptedSharedKey.isEmpty || encryptedSharedKey == 'null') {
throw SharedKeyNotFoundException('shared encryption key not found',
intent: Intent.fetchEncryptionSharedKey,
exceptionScenario: ExceptionScenario.fetchEncryptionKeys);
}
String decryptedValue = '';
String? currentAtSignPublicKey;
try {
currentAtSignPublicKey = (await _atClient
.getLocalSecondary()!
.getEncryptionPublicKey(_atClient.getCurrentAtSign()!))
?.trim();
} on KeyNotFoundException {
throw AtPublicKeyNotFoundException(
'Failed to fetch the current atSign public key - public:publickey${_atClient.getCurrentAtSign()!}',
intent: Intent.fetchEncryptionPublicKey,
exceptionScenario: ExceptionScenario.localVerbExecutionFailed);
}
if (currentAtSignPublicKey != null &&
atKey.metadata != null &&
atKey.metadata!.pubKeyCS != null &&
atKey.metadata!.pubKeyCS !=
EncryptionUtil.md5CheckSum(currentAtSignPublicKey)) {
throw AtPublicKeyChangeException(
'Public key has changed. Cannot decrypt shared key ${atKey.toString()}',
intent: Intent.fetchEncryptionPublicKey,
exceptionScenario: ExceptionScenario.decryptionFailed);
}

AtEncryptionResult decryptionResultFromAtChops;
try {
final decryptionResult = atClient.atChops!
InitialisationVector iV;
if (atKey.metadata?.ivNonce != null) {
iV = AtChopsUtil.generateIVFromBase64String(atKey.metadata!.ivNonce!);
} else {
iV = AtChopsUtil.generateIVLegacy();
}
final decryptionResult = _atClient.atChops!
.decryptString(encryptedSharedKey, EncryptionKeyType.rsa2048);
decryptedValue = EncryptionUtil.decryptValue(
encryptedValue, decryptionResult.result,
ivBase64: atKey.metadata?.ivNonce);
var encryptionAlgo = AESEncryptionAlgo(AESKey(
DefaultResponseParser().parse(decryptionResult.result).response));
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()}');
rethrow;
}
return decryptedValue;
return decryptionResultFromAtChops.result;
}

Future<String> _getEncryptedSharedKey(AtKey atKey) async {
String? encryptedSharedKey = '';
var localLookupSharedKeyBuilder = LLookupVerbBuilder()
..atKey = AtConstants.atEncryptionSharedKey
..sharedWith = atClient.getCurrentAtSign()
..sharedWith = _atClient.getCurrentAtSign()
..sharedBy = atKey.sharedBy
..isCached = true;
try {
encryptedSharedKey = await atClient
encryptedSharedKey = await _atClient
.getLocalSecondary()!
.executeVerb(localLookupSharedKeyBuilder);
} on KeyNotFoundException {
Expand All @@ -102,7 +112,7 @@ class SharedKeyDecryption implements AtKeyDecryption {
..atKey = AtConstants.atEncryptionSharedKey
..sharedBy = atKey.sharedBy
..auth = true;
encryptedSharedKey = await atClient
encryptedSharedKey = await _atClient
.getRemoteSecondary()!
.executeVerb(sharedKeyLookUpBuilder);
encryptedSharedKey =
Expand Down
2 changes: 2 additions & 0 deletions packages/at_client/lib/src/util/encryption_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import 'package:encrypt/encrypt.dart';
import 'package:crypto/crypto.dart';
import 'package:at_utils/at_logger.dart';

//#TODO Replace calls to methods in this class with at_chops methods and
// move this class to test folder in next major release
class EncryptionUtil {
static final _logger = AtSignLogger('EncryptionUtil');

Expand Down
36 changes: 4 additions & 32 deletions packages/at_client/test/decryption_service_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class MockGetRequestTransformer extends Mock implements GetRequestTransformer {}

class MockSecondaryManager extends Mock implements SecondaryManager {}

class FakeLocalLookUpVerbBuilder extends Fake implements LLookupVerbBuilder {}

void main() {
AtLookupImpl mockAtLookup = MockAtLookup();
AtClientImpl mockAtClientImpl = MockAtClientImpl();
Expand All @@ -35,12 +37,9 @@ void main() {
..atKey = 'phone.wavi'
..sharedBy = '@alice';

var llookupVerbBuilder = LLookupVerbBuilder()
..atKey = 'shared_key.sitaram'
..sharedBy = '@murali';

setUp(() {
reset(mockAtLookup);
registerFallbackValue(FakeLocalLookUpVerbBuilder());
when(() => mockAtLookup.executeVerb(lookupVerbBuilder)).thenAnswer(
(_) async =>
throw AtExceptionUtils.get('AT0015', 'Connection timeout'));
Expand All @@ -49,7 +48,7 @@ void main() {
when(() => mockAtClientImpl.getCurrentAtSign()).thenAnswer((_) => '@xyz');
when(() => mockLocalSecondary.getEncryptionPublicKey('@xyz'))
.thenAnswer((_) => Future.value('dummy_encryption_public_key'));
when(() => mockLocalSecondary.executeVerb(llookupVerbBuilder))
when(() => mockLocalSecondary.executeVerb(any<LLookupVerbBuilder>()))
.thenAnswer((_) async => 'dummy_shared_key');
when(() => mockAtClientImpl.atChops).thenAnswer((_) => mockAtChops);
});
Expand Down Expand Up @@ -89,33 +88,6 @@ void main() {
});

group('A group of tests to verify exceptions in decryption service', () {
test(
'A test to verify exception is thrown when public key checksum changes',
() {
var atKey = (AtKey.shared('phone', namespace: 'wavi', sharedBy: '@murali')
..sharedWith('@sitaram'))
.build();
atKey.metadata = Metadata()..pubKeyCS = '1234';
var sharedKeyDecryption = SharedKeyDecryption(mockAtClientImpl);
expect(() => sharedKeyDecryption.decrypt(atKey, '123'),
throwsA(predicate((dynamic e) => e is AtPublicKeyChangeException)));
});

test('A test to verify exception is thrown when shared key is not found',
() {
var atKey = (AtKey.shared('phone', namespace: 'wavi', sharedBy: '@murali')
..sharedWith('@sitaram'))
.build();
atKey.metadata = Metadata()
..pubKeyCS = 'd4f6d9483907286a0563b9fdeb01aa61';
var sharedKeyDecryption = SharedKeyDecryption(mockAtClientImpl);
expect(
() => sharedKeyDecryption.decrypt(atKey, '123'),
throwsA(predicate((dynamic e) =>
e is SharedKeyNotFoundException &&
e.message == 'shared encryption key not found')));
});

test(
'A test to verify exception is thrown when current atsign public key is not found',
() {
Expand Down
Loading

0 comments on commit e43bc1a

Please sign in to comment.