diff --git a/CHANGELOG.md b/CHANGELOG.md index 394471f..49227b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ ## 2.0.0 -* Reformat code & minor public API changes +* Add secure group communication APIs * Add documentations -* Update documentations -* Add unit testing & update of dependencies version +* Add tests +* Exposed public APIs +* Update LICENSE +* Update dependencies versions diff --git a/README.md b/README.md index 1ed9fa1..b4e2dc6 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,71 @@ The ad hoc library supports the following operations: - Send encrypted data to an existing secure group - Provides notifications of specific events related to the library (e.g., connection established, or data received) +### TransferManager + +To initialise the library, it is done as follows: +```dart +bool verbose = true; +TransferManager transferManager = TransferManager(verbose); +``` + +It is also possible to modify the behaviour of the library by configuring a __Config__ object. + +```dart +bool verbose = false; +Config config = Config(); + +config.label = "Example name"; // Use for communication +config.public = true; // Join any group formation + +TransferManager transferManager = TransferManager(verbose, config); +``` + +### Listen to events + +As different events can occurs in the ad hoc network, the broadcast stream exposed by __TransferManager__ can be listen to. + +```dart +TransferManager transferManager = TransferManager(false); + +void _listen() { + _manager.eventStream.listen((event) { + switch (event.type) { + case AdHocType.onDeviceDiscovered: + var device = event.payload as AdHocDevice; + break; + case AdHocType.onDiscoveryStarted: + break; + case AdHocType.onDiscoveryCompleted: + var discovered = event.payload as Map + break; + case AdHocType.onDataReceived: + var data = event.payload as Object; + break; + case AdHocType.onForwardData: + var data = event.payload as Object; + break; + case AdHocType.onConnection: + var device = event.payload as AdHocDevice; + break; + case AdHocType.onConnectionClosed: + var device = event.payload as AdHocDevice; + break; + case AdHocType.onInternalException: + var exception = event.payload as Exception; + break; + case AdHocType.onGroupInfo: + var info = event.payload as int; + break; + case AdHocType.onGroupDataReceived: + var data = event.payload as Object; + break; + default: + } + } +} +``` + ## Application Example ## example diff --git a/example/lib/main.dart b/example/lib/main.dart index e19ed28..b51bce8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -15,7 +15,7 @@ import 'search_bar.dart'; void main() => runApp(AdHocMusicClient()); -enum MenuOptions { add, search, display, group } +enum MenuOptions { add, search, display } const platform = MethodChannel('adhoc.music.player/main'); @@ -48,7 +48,7 @@ class _AdHocMusicClientState extends State { @override void initState() { super.initState(); - // _manager.enableBle(3600); + _manager.enableBle(3600); _manager.eventStream.listen(_processAdHocEvent); _manager.open = true; } @@ -87,13 +87,6 @@ class _AdHocMusicClientState extends State { case MenuOptions.display: setState(() => _display = !_display); break; - - case MenuOptions.group: - _manager.createGroup(); - Future.delayed(Duration(seconds: 30), () { - _manager.sendMessageToGroup('Hello'); - }); - break; } }, itemBuilder: (context) => >[ @@ -118,13 +111,6 @@ class _AdHocMusicClientState extends State { title: Text('Switch view'), ), ), - const PopupMenuItem( - value: MenuOptions.group, - child: ListTile( - leading: Icon(Icons.create), - title: Text('Create a group'), - ), - ), ], ), ], @@ -146,10 +132,17 @@ class _AdHocMusicClientState extends State { Expanded( child: ListView( children: _discovered.map((device) { + var type = device.mac.ble == '' ? 'Wi-Fi' : 'BLE'; + var mac = device.mac.ble == '' ? device.mac.wifi : device.mac.ble; + if (device.mac.ble != '' && device.mac.wifi != '') { + type = 'Wi-Fi/BLE'; + mac = '${device.mac.wifi}/${device.mac.ble}'; + } + return Card( child: ListTile( title: Center(child: Text(device.name)), - subtitle: Center(child: Text('${device.mac}')), + subtitle: Center(child: Text('$type: $mac')), onTap: () async { await _manager.connect(device); setState(() => _discovered.removeWhere((element) => (element.mac == device.mac))); diff --git a/lib/adhoc_plugin.dart b/lib/adhoc_plugin.dart index c70835a..a5553c4 100644 --- a/lib/adhoc_plugin.dart +++ b/lib/adhoc_plugin.dart @@ -3,4 +3,4 @@ library adhoc_plugin; export 'src/appframework/export.dart'; export 'src/datalink/export.dart'; export 'src/network/export.dart'; -export 'src/presentation/export.dart'; \ No newline at end of file +export 'src/presentation/export.dart'; diff --git a/lib/src/appframework/config.dart b/lib/src/appframework/config.dart index ae259bb..0cefb73 100644 --- a/lib/src/appframework/config.dart +++ b/lib/src/appframework/config.dart @@ -1,19 +1,15 @@ +import 'package:adhoc_plugin/src/appframework/constants.dart'; import 'package:adhoc_plugin/src/appframework/exceptions/bad_server_port.dart'; import 'package:uuid/uuid.dart'; /// Class allowing to modify the library's behaviour via parameters. class Config { - /// Minimum value allowed for the socket port (Wi-Fi Direct) - static const MIN_PORT = 1023; - /// Maximum value allowed for the socket port (Wi-Fi Direct) - static const MAX_PORT = 65535; - late int _serverPort; late String label; late bool flood; - late bool open; + late bool public; late int timeOut; late int expiryTime; late int validityPeriod; @@ -27,7 +23,7 @@ class Config { /// If [flood] is set to true, then internal flooding mechanisms are activated, /// e.g., flood new connection events. /// - /// If [open] is set to true, then the current device will always join the + /// If [public] is set to true, then the current device will always join the /// first group formation message received. /// /// If [serverPort] is given, then it is used instead of the default one @@ -43,13 +39,13 @@ class Config { /// If [validityCheck] is given, then it is used instead of the default one /// (7200 seconds). Config({ - String label = '', bool flood = false, bool open = false, + String label = '', bool flood = false, bool public = false, int serverPort = 52000, int expiryTime = 10, int validityPeriod = 7200, int validityCheck = 7200 }) { this.label = (label == '') ? Uuid().v4() : label; this.flood = flood; - this.open = open; + this.public = public; this.serverPort = serverPort; this.expiryTime = expiryTime; this.validityPeriod = validityPeriod; diff --git a/lib/src/appframework/constants.dart b/lib/src/appframework/constants.dart index bb0cc85..615f79a 100644 --- a/lib/src/appframework/constants.dart +++ b/lib/src/appframework/constants.dart @@ -10,3 +10,9 @@ enum AdHocType { onGroupInfo, onGroupDataReceived, } + +/// Minimum value allowed for the socket port (Wi-Fi Direct) +const MIN_PORT = 1023; + +/// Maximum value allowed for the socket port (Wi-Fi Direct) +const MAX_PORT = 65535; diff --git a/lib/src/appframework/event.dart b/lib/src/appframework/event.dart index 573d1fd..a1c1d14 100644 --- a/lib/src/appframework/event.dart +++ b/lib/src/appframework/event.dart @@ -1,11 +1,15 @@ import 'package:adhoc_plugin/src/appframework/constants.dart'; import 'package:adhoc_plugin/src/datalink/service/adhoc_device.dart'; - +/// Class encapsulating the data received from lower layers of the library. class Event { late final AdHocType type; late AdHocDevice? device; late Object? data; + /// Creates an [Event] object. + /// + /// The event is determined by its [type], which in turn indicates if this + /// object has a payload [data] or a sender ad hoc device representation [device]. Event(this.type, {this.device, this.data}); } diff --git a/lib/src/appframework/transfer_manager.dart b/lib/src/appframework/transfer_manager.dart index ebae60e..2b7a665 100644 --- a/lib/src/appframework/transfer_manager.dart +++ b/lib/src/appframework/transfer_manager.dart @@ -74,33 +74,39 @@ class TransferManager { } /// Stance about joining group formation - set open(bool open) => _presentationManager.groupController.open = open; + set open(bool state) => _presentationManager.groupController.public = state; /*-------------------------------Group Methods--------------------------------*/ /// Creates a secure group. /// + /// If [labels] is given, then the group init request message is sent to those + /// particular addresses. Otherwise, the message is broadcasted. + /// /// Throws a [DeviceFailureException] if the Wi-Fi/Bluetooth adapter is not /// enabled. - void createGroup() { + void createGroup([List? labels]) { if (_datalinkManager.checkState() == 0) { throw DeviceFailureException('No wifi and bluetooth connectivity'); } - _presentationManager.groupController.createGroup(); + _presentationManager.groupController.createGroup(labels); } /// Joins an existing secure group. /// + /// If [label] is given, then the group join request message is sent to that + /// particular address. Otherwise, the join request message is broadcasted. + /// /// Throws a [DeviceFailureException] if the Wi-Fi/Bluetooth adapter is not /// enabled. - void joinGroup() { + void joinGroup([String? label]) { if (_datalinkManager.checkState() == 0) { throw DeviceFailureException('No wifi and bluetooth connectivity'); } - _presentationManager.groupController.joinSecureGroup(); + _presentationManager.groupController.joinSecureGroup(label); } @@ -309,7 +315,10 @@ class TransferManager { } - /// Enables the Wi-Fi adapter. + /// Initialises the underlying Wi-Fi data structures. + /// + /// Note: It is not possible to enable/disable Wi-Fi starting with Build.VERSION_CODES#Q. + /// https://developer.android.com/reference/android/net/wifi/WifiManager#setWifiEnabled(boolean) /// /// The device is set into discovery mode for [duration] ms. /// @@ -372,6 +381,9 @@ class TransferManager { /*------------------------------Private Methods------------------------------*/ + /// Listens to lower layers event stream. + /// + /// Its main purpose is to encapsulate the data into Event object. void _initialize() { _presentationManager.eventStream.listen((event) { AdHocDevice? device; diff --git a/lib/src/datalink/ble/ble_client.dart b/lib/src/datalink/ble/ble_client.dart index dd22139..2feffc9 100644 --- a/lib/src/datalink/ble/ble_client.dart +++ b/lib/src/datalink/ble/ble_client.dart @@ -16,7 +16,7 @@ class BleClient extends ServiceClient { late final BleAdHocDevice _device; late FlutterReactiveBle _reactiveBle; - late bool _isInitialized; + late bool _isInitialised; /// Creates a [BleClient] object. /// @@ -26,10 +26,13 @@ class BleClient extends ServiceClient { /// /// Connection attempts to a remote device are done at most [attempts] times. /// A connection attempt waiting time is set to [timeOut] ms. - BleClient(bool verbose, this._device, int attempts, int timeOut) - : super(verbose, attempts, timeOut) { + BleClient( + bool verbose, this._device, int attempts, int timeOut + ) : super( + verbose, attempts, timeOut + ) { _reactiveBle = FlutterReactiveBle(); - _isInitialized = false; + _isInitialised = false; } /*-------------------------------Public methods-------------------------------*/ @@ -70,7 +73,7 @@ class BleClient extends ServiceClient { /// Cancels the connection with the remote device. @override - void disconnect() { + Future disconnect() async { stopListening(); if (_connectionSub != null) { // Abort connection with the remote host @@ -161,7 +164,7 @@ class BleClient extends ServiceClient { /// Initializes the environment upon a successful connection performed. Future _initEnvironment() async { - if (_isInitialized) { + if (_isInitialised) { return; } @@ -175,6 +178,6 @@ class BleClient extends ServiceClient { controller.add(AdHocEvent(CONNECTION_PERFORMED, [_device.mac.ble, _device.address, CLIENT])); state = STATE_CONNECTED; - _isInitialized = true; + _isInitialised = true; } } diff --git a/lib/src/datalink/ble/ble_server.dart b/lib/src/datalink/ble/ble_server.dart index d9c50a2..58d0ed7 100644 --- a/lib/src/datalink/ble/ble_server.dart +++ b/lib/src/datalink/ble/ble_server.dart @@ -18,14 +18,12 @@ class BleServer extends ServiceServer { static int id = 0; late HashMap _mapMacMTU; - late HashMap> _duplicate; /// Creates a [BleServer] object. /// /// The debug/verbose mode is set if [verbose] is true. BleServer(bool verbose) : super(verbose) { _mapMacMTU = HashMap(); - _duplicate = HashMap(); } /*-------------------------------Public methods-------------------------------*/ @@ -54,14 +52,12 @@ class BleServer extends ServiceServer { if (state) { addActiveConnection(mac); _mapMacMTU.putIfAbsent(mac, () => MIN_MTU); - _duplicate.putIfAbsent(mac, () => {}); // Notify upper layer of a connection performed controller.add(AdHocEvent(CONNECTION_PERFORMED, [mac, uuid, SERVER])); } else { removeConnection(mac); _mapMacMTU.remove(mac); - _duplicate.remove(mac); // Notify upper layer of a connection aborted controller.add(AdHocEvent(CONNECTION_ABORTED, mac)); @@ -77,13 +73,6 @@ class BleServer extends ServiceServer { json.decode(Utf8Decoder().convert(bytes)) as Map ); - var seqNum = message.header.seqNum!; - if (_duplicate[map['mac'] as String]!.contains(seqNum)) { - break; - } else { - _duplicate[map['mac'] as String]!.add(seqNum); - } - // Update the header of the message if (message.header.mac.ble == '') { var uuid = BLUETOOTHLE_UUID + (map['mac'] as String) diff --git a/lib/src/datalink/service/adhoc_device.dart b/lib/src/datalink/service/adhoc_device.dart index 219e1f5..25ccce6 100644 --- a/lib/src/datalink/service/adhoc_device.dart +++ b/lib/src/datalink/service/adhoc_device.dart @@ -34,11 +34,7 @@ class AdHocDevice { /// If [type] is given, then it defines the type of technology used, i.e., /// "0" stands for Wi-Fi Direct and "1" stands for Bluetooth Low Energy. AdHocDevice({ - String? label, - String? address, - String? name, - Identifier? mac, - int type = -1, + String? label, String? address, String? name, Identifier? mac, int type = -1, }) { this.address = checkString(address); _label = checkString(label); diff --git a/lib/src/datalink/service/service_client.dart b/lib/src/datalink/service/service_client.dart index f3ca31e..e94bc5a 100644 --- a/lib/src/datalink/service/service_client.dart +++ b/lib/src/datalink/service/service_client.dart @@ -21,11 +21,7 @@ abstract class ServiceClient extends Service { /// /// A connection attempt is said to be a failure if nothing happens after /// [timeOut] ms. - ServiceClient( - bool verbose, - this._attempts, - this._timeOut, - ) : super(verbose) { + ServiceClient(bool verbose, this._attempts, this._timeOut) : super(verbose) { _backOffTime = Random().nextInt(HIGH - LOW) + LOW; } diff --git a/lib/src/datalink/utils/msg_header.dart b/lib/src/datalink/utils/msg_header.dart index 2320000..7f596ea 100644 --- a/lib/src/datalink/utils/msg_header.dart +++ b/lib/src/datalink/utils/msg_header.dart @@ -33,13 +33,8 @@ class Header { /// The type of technology used by the device is determined by [deviceType]. /// It can be either Bluetooth Low Energy or Wi-Fi Direct. Header({ - required int messageType, - required String label, - int? seqNum, - String? name, - String? address, - Identifier? mac, - int? deviceType + required int messageType, required String label, int? seqNum, String? name, + String? address, Identifier? mac, int? deviceType }) { this.address = address; this.deviceType = deviceType; diff --git a/lib/src/datalink/wifi/wifi_client.dart b/lib/src/datalink/wifi/wifi_client.dart index a0f4e20..808c64f 100644 --- a/lib/src/datalink/wifi/wifi_client.dart +++ b/lib/src/datalink/wifi/wifi_client.dart @@ -28,12 +28,10 @@ class WifiClient extends ServiceClient { /// A connection attempt is said to be a failure if nothing happens after /// [timeOut] ms. WifiClient( - bool verbose, - this._port, - this._serverIP, - int attempts, - int timeOut, - ) : super(verbose, attempts, timeOut); + bool verbose, this._port, this._serverIP, int attempts, int timeOut, + ) : super( + verbose, attempts, timeOut + ); /*-------------------------------Public methods-------------------------------*/ diff --git a/lib/src/network/aodv/aodv_manager.dart b/lib/src/network/aodv/aodv_manager.dart index d97fcfa..eff07f5 100644 --- a/lib/src/network/aodv/aodv_manager.dart +++ b/lib/src/network/aodv/aodv_manager.dart @@ -20,7 +20,7 @@ import 'package:adhoc_plugin/src/network/datalinkmanager/datalink_manager.dart'; import 'package:adhoc_plugin/src/network/exceptions/aodv_message.dart'; import 'package:adhoc_plugin/src/network/exceptions/aodv_unknown_dest.dart'; import 'package:adhoc_plugin/src/network/exceptions/aodv_unknown_type.dart'; -import 'package:adhoc_plugin/src/presentation/certificate_repository.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate_repository.dart'; import 'package:adhoc_plugin/src/presentation/constants.dart'; import 'package:flutter/foundation.dart'; diff --git a/lib/src/network/aodv/rrep.dart b/lib/src/network/aodv/rrep.dart index 3db382c..1b091b0 100644 --- a/lib/src/network/aodv/rrep.dart +++ b/lib/src/network/aodv/rrep.dart @@ -1,5 +1,5 @@ import 'package:adhoc_plugin/src/network/aodv/aodv_message.dart'; -import 'package:adhoc_plugin/src/presentation/certificate.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate.dart'; import 'package:json_annotation/json_annotation.dart'; part 'rrep.g.dart'; diff --git a/lib/src/network/aodv/rreq.dart b/lib/src/network/aodv/rreq.dart index 1d310a6..bd846a8 100644 --- a/lib/src/network/aodv/rreq.dart +++ b/lib/src/network/aodv/rreq.dart @@ -1,5 +1,5 @@ import 'package:adhoc_plugin/src/network/aodv/aodv_message.dart'; -import 'package:adhoc_plugin/src/presentation/certificate.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate.dart'; import 'package:json_annotation/json_annotation.dart'; part 'rreq.g.dart'; diff --git a/lib/src/network/datalinkmanager/constants.dart b/lib/src/network/datalinkmanager/constants.dart index 8715ad1..8d7c44d 100644 --- a/lib/src/network/datalinkmanager/constants.dart +++ b/lib/src/network/datalinkmanager/constants.dart @@ -12,7 +12,7 @@ const DISCONNECT_BROADCAST = 204; // Broadcast a disconnection event const BROADCAST = 205; // Broadcast message // Constants for upper layer notifications -const DATA_RECEIVED = 209; // Data received -const FORWARD_DATA = 210; // Data to be forwarded to next hop -const MESSAGE_EVENT = 211; // Message received -const BROKEN_LINK = 212; // Broken link detected +const DATA_RECEIVED = 206; // Data received +const FORWARD_DATA = 207; // Data to be forwarded to next hop +const MESSAGE_EVENT = 208; // Message received +const BROKEN_LINK = 209; // Broken link detected diff --git a/lib/src/network/datalinkmanager/wrapper_ble.dart b/lib/src/network/datalinkmanager/wrapper_ble.dart index 44ea5cd..00008ec 100644 --- a/lib/src/network/datalinkmanager/wrapper_ble.dart +++ b/lib/src/network/datalinkmanager/wrapper_ble.dart @@ -35,6 +35,7 @@ class WrapperBle extends WrapperNetwork { StreamSubscription? _eventSub; late BleAdHocManager _bleAdHocManager; + late HashMap> _duplicate; /// Creates a [WrapperBle] object. /// @@ -45,6 +46,7 @@ class WrapperBle extends WrapperNetwork { WrapperBle(bool verbose, Config config, HashMap mapMacDevices) : super(verbose, config, mapMacDevices) { type = BLE; + _duplicate = HashMap(); init(verbose, null); } @@ -268,6 +270,9 @@ class WrapperBle extends WrapperNetwork { var mac = data[0] as String; var uuid = data[1] as String; var serviceType = data[2] as int; + + _duplicate.putIfAbsent(mac, () => {}); + if (serviceType == SERVER) { break; } @@ -346,6 +351,13 @@ class WrapperBle extends WrapperNetwork { /// /// The [message] represents a message send through the network. void _processMsgReceived(final MessageAdHoc message) { + var seqNum = message.header.seqNum!; + if (_duplicate[message.header.mac.ble]!.contains(seqNum)) { + return; + } else { + _duplicate[message.header.mac.ble]!.add(seqNum); + } + switch (message.header.messageType) { case CONNECT_SERVER: // Recover this own node MAC and BLE address diff --git a/lib/src/presentation/crypto_engine.dart b/lib/src/presentation/crypto/crypto_engine.dart similarity index 97% rename from lib/src/presentation/crypto_engine.dart rename to lib/src/presentation/crypto/crypto_engine.dart index 6500c9d..acc2f22 100644 --- a/lib/src/presentation/crypto_engine.dart +++ b/lib/src/presentation/crypto/crypto_engine.dart @@ -4,10 +4,10 @@ import 'dart:isolate'; import 'dart:math'; import 'dart:typed_data'; -import 'package:adhoc_plugin/src/presentation/certificate.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate.dart'; import 'package:adhoc_plugin/src/presentation/constants.dart'; -import 'package:adhoc_plugin/src/presentation/reply.dart'; -import 'package:adhoc_plugin/src/presentation/request.dart'; +import 'package:adhoc_plugin/src/presentation/crypto/reply.dart'; +import 'package:adhoc_plugin/src/presentation/crypto/request.dart'; import 'package:cryptography/cryptography.dart' as crypto; import 'package:pointycastle/export.dart'; diff --git a/lib/src/presentation/reply.dart b/lib/src/presentation/crypto/reply.dart similarity index 100% rename from lib/src/presentation/reply.dart rename to lib/src/presentation/crypto/reply.dart diff --git a/lib/src/presentation/request.dart b/lib/src/presentation/crypto/request.dart similarity index 100% rename from lib/src/presentation/request.dart rename to lib/src/presentation/crypto/request.dart diff --git a/lib/src/presentation/export.dart b/lib/src/presentation/export.dart index 8b13789..509e654 100644 --- a/lib/src/presentation/export.dart +++ b/lib/src/presentation/export.dart @@ -1 +1,3 @@ - +export 'exceptions/destination_unreachable.dart'; +export 'exceptions/group_not_formed.dart'; +export 'exceptions/verification_failed.dart'; diff --git a/lib/src/presentation/secure_group_controller.dart b/lib/src/presentation/group/group_controller.dart similarity index 52% rename from lib/src/presentation/secure_group_controller.dart rename to lib/src/presentation/group/group_controller.dart index 6331e97..fe776c7 100644 --- a/lib/src/presentation/secure_group_controller.dart +++ b/lib/src/presentation/group/group_controller.dart @@ -11,23 +11,30 @@ import 'package:adhoc_plugin/src/network/aodv/aodv_manager.dart'; import 'package:adhoc_plugin/src/network/datalinkmanager/constants.dart'; import 'package:adhoc_plugin/src/network/datalinkmanager/datalink_manager.dart'; import 'package:adhoc_plugin/src/presentation/constants.dart'; -import 'package:adhoc_plugin/src/presentation/crypto_engine.dart'; +import 'package:adhoc_plugin/src/presentation/crypto/crypto_engine.dart'; import 'package:adhoc_plugin/src/presentation/exceptions/group_not_formed.dart'; +import 'package:adhoc_plugin/src/presentation/group/group_init.dart'; +import 'package:adhoc_plugin/src/presentation/group/group_join.dart'; +import 'package:adhoc_plugin/src/presentation/group/group_leave.dart'; +import 'package:adhoc_plugin/src/presentation/group/group_list.dart'; +import 'package:adhoc_plugin/src/presentation/group/group_value.dart'; import 'package:adhoc_plugin/src/presentation/secure_data.dart'; import 'package:cryptography/cryptography.dart'; import 'package:ninja_prime/ninja_prime.dart'; - /// Class managing the creation and maintenance of a secure group -class SecureGroupController { +class GroupController { final AodvManager _aodvManager; final DataLinkManager _datalinkManager; final CryptoEngine _engine; final Stream _eventStream; late String _ownLabel; late StreamController _controller; + late bool _open; + late bool _timerExpired; late bool _isGroupFormed; + late bool _isFormationGoingOn; late Set _setFloodEvents; /// Order of the finite cyclic group @@ -44,8 +51,6 @@ class SecureGroupController { SecretKey? _groupKey; /// Time allowed for joining the group creation process late int _expiryTime; - /// State of secure group - late bool _isGroupFormation; /// Group member's key share recovered late int _recovered; /// Label of the group initiator/owner @@ -59,23 +64,24 @@ class SecureGroupController { /// List containing the group member label late List _memberLabel; - /// Creates a [SecureGroupController] object. + /// Creates a [GroupController] object. /// /// This object is configured according to [config], which contains specific /// configurations. - SecureGroupController( + GroupController( this._engine, this._aodvManager, this._datalinkManager, this._eventStream, Config config ) { _ownLabel = _aodvManager.label; _controller = StreamController.broadcast(); - _open = config.open; + _open = config.public; _isGroupFormed = false; _setFloodEvents = {}; _k = null; _d = null; _expiryTime = config.expiryTime; - _isGroupFormation = false; + _isFormationGoingOn = false; + _timerExpired = false; _recovered = 0; _DHShare = HashMap(); _memberShare = HashMap(); @@ -89,22 +95,27 @@ class SecureGroupController { /// Stream of ad hoc event notifications of lower layers. Stream get eventStream => _controller.stream; - /// Stance about joining group formation - set open(bool open) => _open = open; + /// Stance about joining group formation for any init request + set public(bool state) => _open = state; /*-------------------------------Public methods-------------------------------*/ /// Initiates a secure group creation process + /// + /// If [members] is given, then the group formation request is send to every + /// member in the list. void createGroup([List? members]) { - if (_isGroupFormation) { + if (_isFormationGoingOn || _isGroupFormed) { return; } - _isGroupFormation = true; + _isFormationGoingOn = true; _groupOwner = _ownLabel; - var timestamp = _ownLabel + DateTime.now().toIso8601String(); + _memberLabel.add(_ownLabel); + // Timestamp for flood control + var timestamp = _ownLabel + DateTime.now().toIso8601String(); var low = 32; var seed = max(low + low, Random(42).nextInt(192)); @@ -112,49 +123,51 @@ class SecureGroupController { _p = randomPrimeBigInt(seed); _g = randomPrimeBigInt(low); - _memberLabel.add(_ownLabel); - - var message = SecureData( - GROUP_INIT, [timestamp, _groupOwner, _p.toString(), _g.toString()] - ); + var info = GroupInit(timestamp, _p.toString(), _g.toString(), _ownLabel, false); + var message = SecureData(GROUP_INIT, info.toJson()); if (members == null) { // Broadcast formation group advertisement _datalinkManager.broadcastObject(message); - - Timer(Duration(seconds: _expiryTime), _timerExpired); } else { + info.invitation = true; + // Send to labels specified only for (final label in members) { _aodvManager.sendMessageTo(label, message); } - - Timer(Duration(seconds: _expiryTime), _timerExpired); } + + Timer(Duration(seconds: _expiryTime), _broadcastTimerExpired); } /// Joins an existing secure group - void joinSecureGroup() { - if (!_isGroupFormation) { - return; - } - + /// + /// If [label] is given, then the join group request is sent to label + void joinSecureGroup([String? label]) { // Send a group join request - var msg = SecureData(GROUP_JOIN, []); - _datalinkManager.broadcastObject(msg); + if (label == null) { + var msg = SecureData(GROUP_JOIN, []); + + _datalinkManager.broadcastObject(msg); + } else { + var msg = SecureData(GROUP_JOIN_REQ, [_ownLabel]); + + _aodvManager.sendMessageTo(label, msg); + } } /// Leaves an existing secure group void leaveSecureGroup() { - if (!_isGroupFormation) { + if (!_isFormationGoingOn || !_isGroupFormed) { return; } _isGroupFormed = false; // Send a leave group notification - var msg = SecureData(GROUP_LEAVE, []); + var msg = SecureData(GROUP_LEAVE, GroupLeave(_ownLabel, )); _aodvManager.sendMessageTo(_groupOwner!, msg); // Reset cryptographic parameters @@ -164,7 +177,7 @@ class SecureGroupController { _memberShare.clear(); _DHShare.clear(); _CRTShare.clear(); - _isGroupFormation = false; + _isFormationGoingOn = false; } @@ -175,7 +188,7 @@ class SecureGroupController { /// Throws a [GroupNotFormedException] exception if the group is not formed /// or the device is not part of any secure group. void sendMessageToGroup(Object? data) async { - if (_isGroupFormed == false) { + if (!_isGroupFormed) { throw GroupNotFormedException(); } @@ -199,7 +212,12 @@ class SecureGroupController { void _initialize() { _eventStream.listen((event) { if (event.type == DATA_RECEIVED) { - _processDataReceived(event); + var sender = (event.payload as List)[0] as AdHocDevice; + var payload = SecureData.fromJson( + (event.payload as List)[1] as Map + ); + + _processDataReceived(sender, payload); } }); } @@ -215,8 +233,10 @@ class SecureGroupController { /// Triggers the start of the group key agreement. - void _timerExpired() { - var message = SecureData(GROUP_LIST, [_memberLabel]); + void _broadcastTimerExpired() { + _timerExpired = true; + + var message = SecureData(GROUP_LIST, GroupList(_memberLabel)); for (final label in _memberLabel) { if (label != _ownLabel) { @@ -227,7 +247,7 @@ class SecureGroupController { var y = _computeDHShare(); _DHShare.putIfAbsent(_ownLabel, () => y); - message = SecureData(GROUP_SHARE, [y.toString()]); + message = SecureData(GROUP_SHARE, GroupValue(y.toString())); for (final label in _memberLabel) { if (label != _ownLabel) { @@ -297,9 +317,9 @@ class SecureGroupController { // Bézout's identity to obtain crt_ij var coefficients = _solveBezoutIdentity(mij, pij); var crtij = (_k! * coefficients[1] * pij) + (_d! * coefficients[0] * mij); - // if (crtij < BigInt.zero) { - // crtij % (mij * pij); - // } + if (crtij < BigInt.zero) { + crtij % (mij * pij); + } return crtij; } @@ -357,6 +377,7 @@ class SecureGroupController { return BigInt.from(hash.bytes.reduce((a, b) => a + b)); } + /// Computes the group key. /// /// The way the group key is computed is defined by [type]. @@ -397,8 +418,7 @@ class SecureGroupController { var keyBytes = _toBytes(groupKeySum); var length = keyBytes.length; if (length < keyLengthRequired) { - keyBytes = - Uint8List.fromList(keyBytes.toList() + List.filled(keyLengthRequired - length, 42)); + keyBytes = Uint8List.fromList(keyBytes.toList() + List.filled(keyLengthRequired - length, 42)); } else if (length > keyLengthRequired) { keyBytes = keyBytes.sublist(0, keyLengthRequired); } @@ -406,228 +426,263 @@ class SecureGroupController { final algorithm = Chacha20(macAlgorithm: Hmac.sha256()); _groupKey = await algorithm.newSecretKeyFromBytes(keyBytes); - if (_isGroupFormed == false) { + if (!_isGroupFormed) { _isGroupFormed = true; _controller.add(AdHocEvent(GROUP_STATUS, type)); } + + _isFormationGoingOn = false; } /// Processes the data received. /// /// The data is retrieved from the [event] payload. - void _processDataReceived(AdHocEvent event) async { - var pdu = SecureData.fromJson((event.payload as List)[1] as Map); - var sender = (event.payload as List)[0] as AdHocDevice; + void _processDataReceived(AdHocDevice sender, SecureData secureData) async { var senderLabel = sender.label!; - if (pdu.type > GROUP_STATUS) { - return; - } - - var payload = pdu.payload as List; - switch (event.type) { - case GROUP_INIT: - var timestamp = payload[0] as String; - if (!_setFloodEvents.contains(timestamp)) { - _setFloodEvents.add(timestamp); - _datalinkManager.broadcastObjectExcept(pdu, senderLabel); - } - - if (_open == false || _isGroupFormation == true) { - return; + switch (secureData.type) { + case GROUP_INIT: + // Retrieve group advertisement data + var data = GroupInit.fromJson(secureData.payload as Map); + + // Advertisement flood control + if (!_setFloodEvents.contains(data.timestamp)) { + _setFloodEvents.add(data.timestamp); + // If private invitation, then do not broadcast + if (!data.invitation) { + _datalinkManager.broadcastObjectExcept(secureData, senderLabel); } + } - _isGroupFormation = true; + // Config specifies to reject all public advertisement + if (!_open && !data.invitation) { + return; + } + // Reject own advertisement + if (_ownLabel == data.initiator) { + return; + } else { // Store group owner label - _groupOwner = payload[1] as String; - // Reject own advertisement - if (_groupOwner == _ownLabel) { - return; - } + _groupOwner = data.initiator; + } - // Store Diffie-Hellman parameters - _p = BigInt.parse(payload[2] as String); - _g = BigInt.parse(payload[3] as String); - // Reply to the group formation - var msg = SecureData(GROUP_REPLY, []); - _aodvManager.sendMessageTo(senderLabel, msg); - break; + // Store Diffie-Hellman parameters + _p = BigInt.parse(data.modulo); + _g = BigInt.parse(data.generator); + // Reply to the group formation + var msg = SecureData(GROUP_REPLY, null); + _aodvManager.sendMessageTo(senderLabel, msg); + break; + + + case GROUP_REPLY: + if (_timerExpired) { + return; + } - case GROUP_REPLY: - if (!_memberLabel.contains(senderLabel)) { - _memberLabel.add(senderLabel); + if (!_memberLabel.contains(senderLabel)) { + _memberLabel.add(senderLabel); + } + break; + + + case GROUP_LIST: + // Retrieve group list data + var data = GroupList.fromJson(secureData.payload as Map); + // Get all the label of the group member + _memberLabel.addAll(data.labels); + + // Compute own public Diffie-Hellman share + var y = _computeDHShare(); + _DHShare.putIfAbsent(_ownLabel, () => y); + + // Broadcast it to group member + var msg = SecureData(GROUP_SHARE, GroupValue(y.toString()).toJson()); + for (final label in _memberLabel) { + if (label != _ownLabel) { + _aodvManager.sendMessageTo(label, msg.toJson()); } - break; + } + break; - case GROUP_LIST: - // Get all the label of the group member - _memberLabel.addAll((payload[0] as List).cast()); - // Compute own public Diffie-Hellman share - var y = _computeDHShare(); - _DHShare.putIfAbsent(_ownLabel, () => y); + case GROUP_SHARE: + // Retrieve group share data + var data = GroupValue.fromJson(secureData.payload as Map); + // Store the public Diffie-Hellman share of group memeber + var yj = BigInt.parse(data.value); + _DHShare.putIfAbsent(senderLabel, () => yj); - // Broadcast it to group member - var msg = SecureData(GROUP_SHARE, [y.toString()]); - for (final label in _memberLabel) { - if (label != _ownLabel) { - _aodvManager.sendMessageTo(label, msg); + // Once received all, solve the CRT system of congruence for each + // group member + if (_DHShare.length == _memberLabel.length) { + for (var label in _memberLabel) { + if (label == _ownLabel) { + continue; } + + yj = _DHShare[label]!; + + var mij = _computeMemberShare(label, yj); + var crtij = _computeCRTShare(label, yj, mij); + var msg = SecureData(GROUP_KEY, GroupValue(crtij.toString()).toJson()); + + _aodvManager.sendMessageTo(label, msg.toJson()); } - break; + } + break; - case GROUP_SHARE: - // Store the public Diffie-Hellman share of group memeber - var yj = BigInt.parse(payload[0] as String); - _DHShare.putIfAbsent(senderLabel, () => yj); - // Once received all, solve the CRT system of congruence for each - // group member - if (_DHShare.length == _memberLabel.length) { - for (var label in _memberLabel) { - yj = _DHShare[label]!; + case GROUP_KEY: + // Retrieve group key share data + var data = GroupValue.fromJson(secureData.payload as Map); + var crtji = BigInt.parse(data.value); + // Store the solution of the CRT system of congruence + _CRTShare.putIfAbsent(senderLabel, () { _recovered += 1; return crtji; }); - var mij = _computeMemberShare(senderLabel, yj); - var crtij = _computeCRTShare(senderLabel, yj, mij); + // Compute the group key + if (_recovered == _memberLabel.length) { + _computeGroupKey(GROUP_INIT); + } + break; - var msg = SecureData(GROUP_KEY, [GROUP_INIT, crtij.toString()]); - _aodvManager.sendMessageTo(senderLabel, msg); - } - } - break; + case GROUP_JOIN: + // Send the group join request to the group owner + var msg = SecureData(GROUP_JOIN_REQ, GroupValue(senderLabel).toJson()); + _aodvManager.sendMessageTo(_groupOwner!, msg); + break; - case GROUP_KEY: - // Store the solution of the CRT system of congruence - var crtji = BigInt.parse(payload[1] as String); - _CRTShare.putIfAbsent(senderLabel, () { _recovered += 1; return crtji; }); - // Compute the group key - if (_recovered == _memberLabel.length) { - _computeGroupKey(GROUP_INIT); - } + case GROUP_JOIN_REQ: + // Retrieve joining member label data + var data = GroupValue.fromJson(secureData.payload as Map); + var joiningMember = data.value; + if (_memberLabel.contains(joiningMember)) { break; + } else { + _memberLabel.add(joiningMember); + } - case GROUP_JOIN: - // Send the group join request to the group owner - var msg = SecureData(GROUP_JOIN_REQ, [senderLabel]); - _aodvManager.sendMessageTo(_groupOwner!, msg); - break; + // Compute hash of the group key and send it along public Diffie-Hellman + // shares of all group members to the joining member. + var groupKeyHash = await _computeGroupKeyHash(); + var labels = List.empty(growable: true); + var values = List.empty(growable: true); + _DHShare.forEach((label, value) { + labels.add(label); + values.add(value.toString()); + }); - case GROUP_JOIN_REQ: - // Group owner responds to the group join request received - var joiningMember = payload[0] as String; - _memberLabel.add(joiningMember); + var response = GroupJoin( + hash: groupKeyHash.toString(), labels: labels, values: values + ); - if (_memberLabel.contains(joiningMember)) { - return; - } + var msg = SecureData(GROUP_JOIN_REP, response.toJson()); - // Compute hash of the group key and send it along public Diffie-Hellman - // shares of all group members to the joining member. - var groupKeyHash = await _computeGroupKeyHash(); - var labels = List.empty(growable: true); - var values = List.empty(growable: true); - _DHShare.forEach((label, value) { - labels.add(label); - values.add(value.toString()); - }); + _aodvManager.sendMessageTo(joiningMember, msg.toJson()); + break; - var msg = SecureData(GROUP_JOIN_REP, [groupKeyHash.toString(), labels, values]); - _aodvManager.sendMessageTo(joiningMember, msg); - break; + case GROUP_JOIN_REP: + // Retrieve joining member label data + var data = GroupJoin.fromJson(secureData.payload as Map); - case GROUP_JOIN_REP: - // New member proceeds with the protocol from Step 3. - if (payload.length == 3) { - // Hash of group key - _groupKey = SecretKey(_toBytes(BigInt.parse(payload[0] as String))); - - // Public Diffie-Hellman shares of group members - var labels = (payload[1] as List).cast(); - var values = (payload[2] as List).cast(); - var y = _computeDHShare(); - - // Broadcast own Diffie-Hellman public share - _DHShare.putIfAbsent(_ownLabel, () => y); - for (var i = 0; i < labels.length; i++) { - _DHShare.putIfAbsent(labels[i], () => BigInt.parse(values[i])); - } + // New member proceeds with the protocol from Step 3. + if (data.hash != null) { + // Hash of group key + _groupKey = SecretKey(_toBytes(BigInt.parse(data.hash!))); + + // Public Diffie-Hellman shares of group members + var labels = data.labels; + var values = data.values; + var y = _computeDHShare(); - // Solve systems of congruences - for (final label in _memberLabel) { - if (label != _ownLabel) { - var mij = _computeMemberShare(label, _DHShare[label]!); - var crtij = _computeCRTShare(label, _DHShare[label]!, mij); + // Broadcast own Diffie-Hellman public share + _DHShare.putIfAbsent(_ownLabel, () => y); + for (var i = 0; i < labels!.length; i++) { + _DHShare.putIfAbsent(labels[i], () => BigInt.parse(values![i])); + } - var msg = SecureData(GROUP_JOIN_REP, [y.toString(), crtij.toString()]); + // Solve systems of congruences + for (final label in _memberLabel) { + if (label != _ownLabel) { + var mij = _computeMemberShare(label, _DHShare[label]!); + var crtij = _computeCRTShare(label, _DHShare[label]!, mij); - _aodvManager.sendMessageTo(label, msg); - } + var response = GroupJoin(share: y.toString(), solution: crtij.toString()); + var msg = SecureData(GROUP_JOIN_REP, response.toJson()); + + _aodvManager.sendMessageTo(label, msg); } - // Compute group key - _computeGroupKey(GROUP_JOIN, null); - } else { // Old member updating the group key - var yj = BigInt.parse(payload[0] as String); - var mij = _computeMemberShare(senderLabel, yj); - var crtij = BigInt.parse(payload[1] as String); - - // Store the value send by the joining member - _DHShare.putIfAbsent(senderLabel, () => yj); - _memberShare.putIfAbsent(senderLabel, () => mij); - _CRTShare.putIfAbsent(senderLabel, () => crtij); - - // Compute group key - _computeGroupKey(GROUP_JOIN, senderLabel); } - break; + // Compute group key + _computeGroupKey(GROUP_JOIN, null); + } else { // Old member updating the group key + var yj = BigInt.parse(data.share!); + var mij = _computeMemberShare(senderLabel, yj); + var crtij = BigInt.parse(data.solution!); + + // Store the value send by the joining member + _DHShare.putIfAbsent(senderLabel, () => yj); + _memberShare.putIfAbsent(senderLabel, () => mij); + _CRTShare.putIfAbsent(senderLabel, () => crtij); + + // Compute group key + _computeGroupKey(GROUP_JOIN, senderLabel); + } + break; + + + case GROUP_LEAVE: + // Retrieve joining member label data + var data = GroupLeave.fromJson(secureData.payload as Map); - case GROUP_LEAVE: - // Group owner redraw new key share and broadcast it to remaining group member - if (_groupOwner == _ownLabel) { - _memberLabel.remove(senderLabel); - _k = null; - - for (var label in _memberLabel) { - if (label != _ownLabel) { - var yj = _DHShare[label]!; - var mij = _memberShare[label]!; - // Redraw new key share - var crtij = _computeCRTShare(label, yj, mij); - - var msg = SecureData( - GROUP_LEAVE, [senderLabel, crtij.toString()] - ); - - _aodvManager.sendMessageTo(label, msg); - } + // Group owner redraw new key share and broadcast it to remaining group member + if (_groupOwner == _ownLabel) { + _memberLabel.remove(senderLabel); + _k = null; + + for (var label in _memberLabel) { + if (label != _ownLabel) { + var yj = _DHShare[label]!; + var mij = _memberShare[label]!; + // Redraw new key share + var crtij = _computeCRTShare(label, yj, mij); + + var response = GroupLeave(data.leavingLabel, newSolution: crtij.toString()); + var msg = SecureData(GROUP_LEAVE, response.toJson()); + + _aodvManager.sendMessageTo(label, msg); } - // Compute group key - _computeGroupKey(GROUP_LEAVE); - } else { - // Remove value of leaving member - var leavingMember = payload[0] as String; - _memberLabel.remove(leavingMember); - _DHShare.remove(leavingMember); - _memberShare.remove(leavingMember); - _CRTShare.remove(leavingMember); - - // Compute group key - _CRTShare[_groupOwner!] = BigInt.parse(payload[1] as String); - _computeGroupKey(GROUP_LEAVE); } - break; + // Compute group key + _computeGroupKey(GROUP_LEAVE); + } else { + // Remove value of leaving member + var leavingMember = data.leavingLabel; + _memberLabel.remove(leavingMember); + _DHShare.remove(leavingMember); + _memberShare.remove(leavingMember); + _CRTShare.remove(leavingMember); + + // Compute group key + _CRTShare[_groupOwner!] = BigInt.parse(data.newSolution!); + _computeGroupKey(GROUP_LEAVE); + } + break; - case GROUP_DATA: - // Decrypt group data received - var decrypted = await _engine.decrypt(payload, sharedKey: _groupKey!); - // Notify upper layers of group data received - _controller.add(AdHocEvent(DATA_RECEIVED, [sender, decrypted])); - break; + case GROUP_DATA: + // Decrypt group data received + var decrypted = await _engine.decrypt(secureData.payload as List, sharedKey: _groupKey!); + var processed = JsonCodec().decode(Utf8Decoder().convert(decrypted)); + + // Notify upper layers of group data received + _controller.add(AdHocEvent(DATA_RECEIVED, [sender, processed])); + break; default: } diff --git a/lib/src/presentation/group/group_init.dart b/lib/src/presentation/group/group_init.dart new file mode 100644 index 0000000..4f7a7cb --- /dev/null +++ b/lib/src/presentation/group/group_init.dart @@ -0,0 +1,25 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'group_init.g.dart'; + +@JsonSerializable() +class GroupInit { + late final String timestamp; + late final String modulo; + late final String generator; + late final String initiator; + late final bool invitation; + + GroupInit(this.timestamp, this.modulo, this.generator, this.initiator, this.invitation); + + /// Creates a [GroupInit] object from a JSON representation. + /// + /// Factory constructor that creates a [GroupInit] based on the information + /// given by [json]. + factory GroupInit.fromJson(Map json) => _$GroupInitFromJson(json); + +/*-------------------------------Public methods-------------------------------*/ + + /// Returns the JSON representation as a [Map] of this [GroupInit] instance. + Map toJson() => _$GroupInitToJson(this); +} \ No newline at end of file diff --git a/lib/src/presentation/group/group_init.g.dart b/lib/src/presentation/group/group_init.g.dart new file mode 100644 index 0000000..50094a5 --- /dev/null +++ b/lib/src/presentation/group/group_init.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'group_init.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GroupInit _$GroupInitFromJson(Map json) { + return GroupInit( + json['timestamp'] as String, + json['modulo'] as String, + json['generator'] as String, + json['initiator'] as String, + json['invitation'] as bool, + ); +} + +Map _$GroupInitToJson(GroupInit instance) => { + 'timestamp': instance.timestamp, + 'modulo': instance.modulo, + 'generator': instance.generator, + 'initiator': instance.initiator, + 'invitation': instance.invitation, + }; diff --git a/lib/src/presentation/group/group_join.dart b/lib/src/presentation/group/group_join.dart new file mode 100644 index 0000000..d1ecd54 --- /dev/null +++ b/lib/src/presentation/group/group_join.dart @@ -0,0 +1,26 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'group_join.g.dart'; + +@JsonSerializable() +class GroupJoin { + String? hash; + List? labels; + List? values; + + String? share; + String? solution; + + GroupJoin({this.hash, this.labels, this.values, this.share, this.solution}); + + /// Creates a [GroupJoin] object from a JSON representation. + /// + /// Factory constructor that creates a [GroupJoin] based on the information + /// given by [json]. + factory GroupJoin.fromJson(Map json) => _$GroupJoinFromJson(json); + +/*-------------------------------Public methods-------------------------------*/ + + /// Returns the JSON representation as a [Map] of this [GroupJoin] instance. + Map toJson() => _$GroupJoinToJson(this); +} \ No newline at end of file diff --git a/lib/src/presentation/group/group_join.g.dart b/lib/src/presentation/group/group_join.g.dart new file mode 100644 index 0000000..7f040be --- /dev/null +++ b/lib/src/presentation/group/group_join.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'group_join.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GroupJoin _$GroupJoinFromJson(Map json) { + return GroupJoin( + hash: json['hash'] as String?, + labels: + (json['labels'] as List?)?.map((e) => e as String).toList(), + values: + (json['values'] as List?)?.map((e) => e as String).toList(), + share: json['share'] as String?, + solution: json['solution'] as String?, + ); +} + +Map _$GroupJoinToJson(GroupJoin instance) => { + 'hash': instance.hash, + 'labels': instance.labels, + 'values': instance.values, + 'share': instance.share, + 'solution': instance.solution, + }; diff --git a/lib/src/presentation/group/group_leave.dart b/lib/src/presentation/group/group_leave.dart new file mode 100644 index 0000000..009c1a8 --- /dev/null +++ b/lib/src/presentation/group/group_leave.dart @@ -0,0 +1,22 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'group_leave.g.dart'; + +@JsonSerializable() +class GroupLeave { + late final String leavingLabel; + String? newSolution; + + GroupLeave(this.leavingLabel, {this.newSolution}); + + /// Creates a [GroupLeave] object from a JSON representation. + /// + /// Factory constructor that creates a [GroupLeave] based on the information + /// given by [json]. + factory GroupLeave.fromJson(Map json) => _$GroupLeaveFromJson(json); + +/*-------------------------------Public methods-------------------------------*/ + + /// Returns the JSON representation as a [Map] of this [GroupLeave] instance. + Map toJson() => _$GroupLeaveToJson(this); +} \ No newline at end of file diff --git a/lib/src/presentation/group/group_leave.g.dart b/lib/src/presentation/group/group_leave.g.dart new file mode 100644 index 0000000..4d13e9f --- /dev/null +++ b/lib/src/presentation/group/group_leave.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'group_leave.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GroupLeave _$GroupLeaveFromJson(Map json) { + return GroupLeave( + json['leavingLabel'] as String, + newSolution: json['newSolution'] as String?, + ); +} + +Map _$GroupLeaveToJson(GroupLeave instance) => + { + 'leavingLabel': instance.leavingLabel, + 'newSolution': instance.newSolution, + }; diff --git a/lib/src/presentation/group/group_list.dart b/lib/src/presentation/group/group_list.dart new file mode 100644 index 0000000..c4f8e91 --- /dev/null +++ b/lib/src/presentation/group/group_list.dart @@ -0,0 +1,21 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'group_list.g.dart'; + +@JsonSerializable() +class GroupList { + late final List labels; + + GroupList(this.labels); + + /// Creates a [GroupList] object from a JSON representation. + /// + /// Factory constructor that creates a [GroupList] based on the information + /// given by [json]. + factory GroupList.fromJson(Map json) => _$GroupListFromJson(json); + +/*-------------------------------Public methods-------------------------------*/ + + /// Returns the JSON representation as a [Map] of this [GroupList] instance. + Map toJson() => _$GroupListToJson(this); +} \ No newline at end of file diff --git a/lib/src/presentation/group/group_list.g.dart b/lib/src/presentation/group/group_list.g.dart new file mode 100644 index 0000000..51784c8 --- /dev/null +++ b/lib/src/presentation/group/group_list.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'group_list.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GroupList _$GroupListFromJson(Map json) { + return GroupList( + (json['labels'] as List).map((e) => e as String).toList(), + ); +} + +Map _$GroupListToJson(GroupList instance) => { + 'labels': instance.labels, + }; diff --git a/lib/src/presentation/group/group_value.dart b/lib/src/presentation/group/group_value.dart new file mode 100644 index 0000000..af81af5 --- /dev/null +++ b/lib/src/presentation/group/group_value.dart @@ -0,0 +1,21 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'group_value.g.dart'; + +@JsonSerializable() +class GroupValue { + late final String value; + + GroupValue(this.value); + + /// Creates a [GroupValue] object from a JSON representation. + /// + /// Factory constructor that creates a [GroupValue] based on the information + /// given by [json]. + factory GroupValue.fromJson(Map json) => _$GroupValueFromJson(json); + +/*-------------------------------Public methods-------------------------------*/ + + /// Returns the JSON representation as a [Map] of this [GroupValue] instance. + Map toJson() => _$GroupValueToJson(this); +} \ No newline at end of file diff --git a/lib/src/presentation/group/group_value.g.dart b/lib/src/presentation/group/group_value.g.dart new file mode 100644 index 0000000..c9fe5b6 --- /dev/null +++ b/lib/src/presentation/group/group_value.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'group_value.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GroupValue _$GroupValueFromJson(Map json) { + return GroupValue( + json['value'] as String, + ); +} + +Map _$GroupValueToJson(GroupValue instance) => + { + 'value': instance.value, + }; diff --git a/lib/src/presentation/certificate.dart b/lib/src/presentation/key_mgnmt/certificate.dart similarity index 100% rename from lib/src/presentation/certificate.dart rename to lib/src/presentation/key_mgnmt/certificate.dart diff --git a/lib/src/presentation/certificate.g.dart b/lib/src/presentation/key_mgnmt/certificate.g.dart similarity index 100% rename from lib/src/presentation/certificate.g.dart rename to lib/src/presentation/key_mgnmt/certificate.g.dart diff --git a/lib/src/presentation/certificate_repository.dart b/lib/src/presentation/key_mgnmt/certificate_repository.dart similarity index 97% rename from lib/src/presentation/certificate_repository.dart rename to lib/src/presentation/key_mgnmt/certificate_repository.dart index cd117e6..bb870c9 100644 --- a/lib/src/presentation/certificate_repository.dart +++ b/lib/src/presentation/key_mgnmt/certificate_repository.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:collection'; import 'package:adhoc_plugin/src/appframework/config.dart'; -import 'package:adhoc_plugin/src/presentation/certificate.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate.dart'; /// Class representing the certificate repository. It performs certificates /// management, i.e., certificate addition, removal, and periodic validity check. diff --git a/lib/src/presentation/presentation_manager.dart b/lib/src/presentation/presentation_manager.dart index e12822f..eed843e 100644 --- a/lib/src/presentation/presentation_manager.dart +++ b/lib/src/presentation/presentation_manager.dart @@ -10,13 +10,13 @@ import 'package:adhoc_plugin/src/datalink/utils/utils.dart'; import 'package:adhoc_plugin/src/network/aodv/aodv_manager.dart'; import 'package:adhoc_plugin/src/network/datalinkmanager/constants.dart'; import 'package:adhoc_plugin/src/network/datalinkmanager/datalink_manager.dart'; -import 'package:adhoc_plugin/src/presentation/certificate.dart'; -import 'package:adhoc_plugin/src/presentation/certificate_repository.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate_repository.dart'; import 'package:adhoc_plugin/src/presentation/constants.dart'; -import 'package:adhoc_plugin/src/presentation/crypto_engine.dart'; +import 'package:adhoc_plugin/src/presentation/crypto/crypto_engine.dart'; import 'package:adhoc_plugin/src/presentation/exceptions/verification_failed.dart'; import 'package:adhoc_plugin/src/presentation/secure_data.dart'; -import 'package:adhoc_plugin/src/presentation/secure_group_controller.dart'; +import 'package:adhoc_plugin/src/presentation/group/group_controller.dart'; import 'package:pointycastle/pointycastle.dart'; /// Class representing the core of the secure data layer. It performs @@ -30,7 +30,7 @@ class PresentationManager { late AodvManager _aodvManager; late DataLinkManager _datalinkManager; late CertificateRepository _repository; - late SecureGroupController _groupController; + late GroupController _groupController; late StreamController _controller; late HashMap> _buffer; @@ -49,7 +49,7 @@ class PresentationManager { _datalinkManager = _aodvManager.dataLinkManager; _engine = CryptoEngine(); _engine.initialize(); - _groupController = SecureGroupController( + _groupController = GroupController( _engine, _aodvManager, _datalinkManager, _aodvManager.eventStream, config ); _controller = StreamController.broadcast(); @@ -62,7 +62,7 @@ class PresentationManager { /*------------------------------Getters & Setters-----------------------------*/ /// Secure group manager used by this instance. - SecureGroupController get groupController => _groupController; + GroupController get groupController => _groupController; /// Data-link manager used by the AODV manager. DataLinkManager get datalinkManager => _datalinkManager; @@ -88,7 +88,7 @@ class PresentationManager { if (certificate == null) { // Request certificate as it is not in the certificate repository // (Certificate Chain Discovery) - _aodvManager.sendMessageTo(destination, SecureData(CERT_REQ, []).toJson()); + _aodvManager.sendMessageTo(destination, SecureData(CERT_REQ, null).toJson()); // Buffer the encrypted message to send _buffer.update( diff --git a/test/network/package_datalink_test.mocks.dart b/test/network/package_datalink_test.mocks.dart index e39949e..a3b960d 100644 --- a/test/network/package_datalink_test.mocks.dart +++ b/test/network/package_datalink_test.mocks.dart @@ -84,9 +84,6 @@ class MockHeader extends _i1.Mock implements _i2.Header { super.noSuchMethod(Invocation.setter(#messageType, _messageType), returnValueForMissingStub: null); @override - int get seqNum => - (super.noSuchMethod(Invocation.getter(#seqNum), returnValue: 0) as int); - @override set seqNum(int? _seqNum) => super.noSuchMethod(Invocation.setter(#seqNum, _seqNum), returnValueForMissingStub: null); diff --git a/test/presentation/certificate_test.dart b/test/presentation/certificate_test.dart index 73540cc..0f8a82b 100644 --- a/test/presentation/certificate_test.dart +++ b/test/presentation/certificate_test.dart @@ -1,8 +1,8 @@ import 'dart:typed_data'; import 'package:adhoc_plugin/src/appframework/config.dart'; -import 'package:adhoc_plugin/src/presentation/certificate.dart'; -import 'package:adhoc_plugin/src/presentation/certificate_repository.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate_repository.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; diff --git a/test/presentation/certificate_test.mocks.dart b/test/presentation/certificate_test.mocks.dart index 3f5f1f5..335f0a5 100644 --- a/test/presentation/certificate_test.mocks.dart +++ b/test/presentation/certificate_test.mocks.dart @@ -4,7 +4,8 @@ import 'dart:typed_data' as _i4; -import 'package:adhoc_plugin/src/presentation/certificate.dart' as _i3; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate.dart' + as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:pointycastle/asymmetric/api.dart' as _i2; diff --git a/test/presentation/crypto_engine_test.dart b/test/presentation/crypto_engine_test.dart index 5246142..971fced 100644 --- a/test/presentation/crypto_engine_test.dart +++ b/test/presentation/crypto_engine_test.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:adhoc_plugin/src/presentation/certificate.dart'; -import 'package:adhoc_plugin/src/presentation/crypto_engine.dart'; +import 'package:adhoc_plugin/src/presentation/key_mgnmt/certificate.dart'; +import 'package:adhoc_plugin/src/presentation/crypto/crypto_engine.dart'; import 'package:cryptography/cryptography.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pointycastle/pointycastle.dart';