Skip to content

Commit

Permalink
CryptoModule (#117)
Browse files Browse the repository at this point in the history
* cryptorHeader for new cryptoModule

* format!

* added AesCbcCryptor, ICryptor

* format!

* * LegacyCryptor, * added: newCryptoModule

* new crytoModule

* test: crypto Module

* dart fix

* format!! again

* * expose CryptoModule for users, * deprecation warning for `cipherKey`

* code cleanup

* show CryptoConfiguration for legacy cryptor configuration support

* Update CODEOWNERS

* * Added empty content handling while encry and decrypt,
* Added more information in exception string for reason fo failure.
* naming convention fix for CryptoModule factory.
* code formatting.

* removed unused class ILegacyCryptor

* fix: encrypt/decrypt File methods

* fix: explicit cipherKey at method params defaulting to legacy cryptor

* added condition to apply user specified cryptoModule for encryption/decryption,
* refactoring to fix broken format issue with decrypted hostory messages,
* files encryption/ decryption conditions refined

* update LICENSE

* PubNub SDK v4.3.0 release.

---------

Co-authored-by: PubNub Release Bot <[email protected]>
  • Loading branch information
mohitpubnub and pubnub-release-bot authored Oct 16, 2023
1 parent 639bb30 commit ba5935d
Show file tree
Hide file tree
Showing 41 changed files with 983 additions and 189 deletions.
4 changes: 2 additions & 2 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
* @are @mohitpubnub @parfeon
.github/* @parfeon @are @mohitpubnub
* @jguz-pubnub @mohitpubnub @parfeon
.github/* @jguz-pubnub @parfeon @mohitpubnub
README.md @techwritermat @kazydek
9 changes: 8 additions & 1 deletion .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
---
changelog:
- date: 2023-10-16
version: v4.3.0
changes:
- type: feature
text: "Add crypto module that allows configure SDK to encrypt and decrypt messages."
- type: bug
text: "Improved security of crypto implementation by adding enhanced AES-CBC cryptor."
- date: 2023-07-27
version: v4.2.4
changes:
Expand Down Expand Up @@ -425,7 +432,7 @@ supported-platforms:
platforms:
- "Dart SDK >=2.6.0 <3.0.0"
version: "PubNub Dart SDK"
version: "4.2.4"
version: "4.3.0"
sdks:
-
full-name: PubNub Dart SDK
Expand Down
25 changes: 25 additions & 0 deletions acceptance_tests/lib/src/steps/crypto_module/_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'dart:io';

String getCryptoFilePath(String filename) {
var assets = Directory(
'../../service-contract-mock/contract/features/encryption/assets');
return '${assets.path}/$filename';
}

bool listEquals<E>(List<E> list1, List<E> list2) {
if (identical(list1, list2)) {
return true;
}

if (list1.length != list2.length) {
return false;
}

for (var i = 0; i < list1.length; i += 1) {
if (list1[i] != list2[i]) {
return false;
}
}

return true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:acceptance_tests/src/steps/crypto_module/step_given_cipher_key.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_given_legacy_crypto.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_given_multiple_cryptors.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_given_vector.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_then_decrypt_success.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_then_decrypted.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_then_outcome.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_when_decrypt.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_when_decrypt_as.dart';
import 'package:acceptance_tests/src/steps/crypto_module/step_when_encrypt.dart';

import '../../world.dart';

import 'package:gherkin/gherkin.dart';

import 'step_given_crypto_module.dart';

final List<StepDefinitionGeneric<PubNubWorld>> cryptoSteps = [
StepGivenCryptoModule(),
StepGivenCipherKey(),
StepGivenVector(),
StepWhenDecryptFileAs(),
StepThenDecryptedContentEquals(),
StepGivenLegacyCryptoModule(),
StepWhenDecryptFile(),
StepThenOutcome(),
StepWhenEncrypt(),
ThenDecryptSuccessWithLegacy(),
StepGivenCryptoModuleWithMultipleCryptors()
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepGivenCipherKey extends Given1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'with {string} cipher key');

@override
Future<void> executeStep(String cipherKey) async {
world.scenarioContext['cipherKey'] = cipherKey;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepGivenCryptoModule extends Given1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'Crypto module with {string} cryptor');

@override
Future<void> executeStep(String id) async {
world.scenarioContext['cryptorId'] = id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepGivenLegacyCryptoModule
extends Given2WithWorld<String, String, PubNubWorld> {
@override
RegExp get pattern =>
RegExp(r'Legacy code with {string} cipher key and {vector} vector');

@override
Future<void> executeStep(String cipherKey, String vector) async {
world.scenarioContext['useRandomIntializationVector'] =
vector == 'constant' ? false : true;
world.scenarioContext['cipherKey'] = cipherKey;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepGivenCryptoModuleWithMultipleCryptors
extends Given2WithWorld<String, String, PubNubWorld> {
@override
RegExp get pattern => RegExp(
r'Crypto module with default {string} and additional {string} cryptors');

@override
Future<void> executeStep(
String defaultCryptorId, String additionalCryptorId) async {
world.scenarioContext['defaultCryptorId'] = defaultCryptorId;
world.scenarioContext['additionalCryptorId'] = additionalCryptorId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:gherkin/gherkin.dart';

import '../../world.dart';

class StepGivenVector extends Given1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'with {string} vector');

@override
Future<void> executeStep(String vector) async {
world.scenarioContext['useRandomIntializationVector'] =
vector == 'constant' ? false : true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:gherkin/gherkin.dart';
import 'package:pubnub/core.dart';
import 'package:test/test.dart';

import '../../world.dart';
import '_utils.dart';

class ThenDecryptSuccessWithLegacy extends ThenWithWorld<PubNubWorld> {
@override
RegExp get pattern =>
RegExp(r'Successfully decrypt an encrypted file with legacy code');

@override
Future<void> executeStep() async {
ICryptoModule cryptoModule = world.scenarioContext['cryptoModule'];
var decryptedData =
cryptoModule.decrypt(world.scenarioContext['encryptedData']);
this.expect(
listEquals(decryptedData, world.scenarioContext['fileData']), isTrue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'dart:io';

import 'package:gherkin/gherkin.dart';
import 'package:test/test.dart';

import '../../world.dart';
import '_utils.dart';

class StepThenDecryptedContentEquals
extends Then1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern =>
RegExp(r'Decrypted file content equal to the {string} file content');

@override
Future<void> executeStep(String file) async {
var sourceContent =
File(getCryptoFilePath(file)).readAsBytesSync().toList();
var ec = world.scenarioContext['decryptedContent'];
this.expect(listEquals(ec, sourceContent), isTrue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:gherkin/gherkin.dart';
import 'package:pubnub/core.dart';
import 'package:test/expect.dart';

import '../../world.dart';

class StepThenOutcome extends Then1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'I receive {string}');

@override
Future<void> executeStep(String expected) async {
if (expected == 'success') {
this.expect(world.latestException, isNull);
} else {
var outcome = world.latestException;
this.expect((outcome as CryptoException).message, contains(expected));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'dart:io';

import 'package:gherkin/gherkin.dart';
import 'package:pubnub/core.dart';
import 'package:pubnub/crypto.dart';

import '../../world.dart';
import '_utils.dart';

class StepWhenDecryptFile extends When1WithWorld<String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'I decrypt {string} file');

@override
Future<void> executeStep(String file) async {
late ICryptoModule cryptoModule;
var cipherKey = CipherKey.fromUtf8(world.scenarioContext['cipherKey']);
if (world.scenarioContext['cryptorId'] == 'acrh') {
cryptoModule = CryptoModule(defaultCryptor: AesCbcCryptor(cipherKey));
}
var data = File(getCryptoFilePath(file)).readAsBytesSync().toList();
try {
cryptoModule.decrypt(data);
} catch (e) {
world.latestException = e;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'dart:io';

import 'package:gherkin/gherkin.dart';
import 'package:pubnub/core.dart';
import 'package:pubnub/crypto.dart';

import '../../world.dart';
import '_utils.dart';

class StepWhenDecryptFileAs
extends When2WithWorld<String, String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'I decrypt {string} file as {string}');

@override
Future<void> executeStep(String file, String format) async {
var cryptorId = world.scenarioContext['cryptorId'];
var cipherKey = CipherKey.fromUtf8(world.scenarioContext['cipherKey']);
late ICryptor cryptor;
late ICryptoModule cryptoModule;
if (cryptorId == 'legacy') {
cryptor = LegacyCryptor(cipherKey,
cryptoConfiguration: CryptoConfiguration(
useRandomInitializationVector:
world.scenarioContext['useRandomIntializationVector']));
cryptoModule = CryptoModule(defaultCryptor: cryptor);
} else if (cryptorId == 'acrh') {
cryptor = AesCbcCryptor(cipherKey);
cryptoModule = CryptoModule(defaultCryptor: cryptor);
}
var defaultCryptorId = world.scenarioContext['defaultCryptorId'];
if (defaultCryptorId == 'legacy') {
var defaultCryptor = cryptor = LegacyCryptor(cipherKey,
cryptoConfiguration: CryptoConfiguration(
useRandomInitializationVector:
world.scenarioContext['useRandomIntializationVector']));
var additionalCryptor = AesCbcCryptor(cipherKey);
cryptoModule = CryptoModule(
defaultCryptor: defaultCryptor, cryptors: [additionalCryptor]);
} else if (defaultCryptorId == 'acrh') {
var additionalCryptor = cryptor = LegacyCryptor(cipherKey,
cryptoConfiguration: CryptoConfiguration(
useRandomInitializationVector:
world.scenarioContext['useRandomIntializationVector']));
var defaultCryptor = AesCbcCryptor(cipherKey);
cryptoModule = CryptoModule(
defaultCryptor: defaultCryptor, cryptors: [additionalCryptor]);
}
var fileData = File(getCryptoFilePath(file)).readAsBytesSync().toList();
try {
world.scenarioContext['decryptedContent'] =
cryptoModule.decrypt(fileData);
} catch (e) {
world.latestException = e;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'dart:io';

import 'package:gherkin/gherkin.dart';
import 'package:pubnub/core.dart';
import 'package:pubnub/crypto.dart';

import '../../world.dart';
import '_utils.dart';

class StepWhenEncrypt extends When2WithWorld<String, String, PubNubWorld> {
@override
RegExp get pattern => RegExp(r'I encrypt {string} file as {string}');

@override
Future<void> executeStep(String file, String format) async {
if (format == 'binary') {
var fileData = File(getCryptoFilePath(file)).readAsBytesSync().toList();
world.scenarioContext['fileData'] = fileData;

var cryptor = LegacyCryptor(
CipherKey.fromUtf8(world.scenarioContext['cipherKey']),
cryptoConfiguration: CryptoConfiguration(
useRandomInitializationVector:
world.scenarioContext['useRandomIntializationVector']));
var cryptoModule = CryptoModule(defaultCryptor: cryptor);
world.scenarioContext['cryptoModule'] = cryptoModule;
try {
var encryptedData = cryptoModule.encrypt(fileData);
world.scenarioContext['encryptedData'] = encryptedData;
} catch (e) {
world.latestException = e;
}
}
}
}
2 changes: 2 additions & 0 deletions acceptance_tests/lib/src/steps/steps.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:gherkin/gherkin.dart';

import '../world.dart';
import 'crypto_module/crypto_module_steps.dart';
import 'step_given_demo_keyset.dart';
import 'pam_v3/pamv3_steps.dart' show pamv3Steps;
import 'step_given_channel.dart';
Expand All @@ -23,6 +24,7 @@ import 'steps_files.dart';
import 'steps_push.dart';

final List<StepDefinitionGeneric<PubNubWorld>> steps = [
...cryptoSteps,
...pamv3Steps,
StepGivenChannel(),
StepGivenDemoKeyset(),
Expand Down
9 changes: 9 additions & 0 deletions pubnub/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## v4.3.0
October 16 2023

#### Added
- Add crypto module that allows configure SDK to encrypt and decrypt messages.

#### Fixed
- Improved security of crypto implementation by adding enhanced AES-CBC cryptor.

## v4.2.4
July 27 2023

Expand Down
Loading

0 comments on commit ba5935d

Please sign in to comment.