From 90a7f8776ed205e2a63c35297d393355b7455457 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 23 Nov 2023 12:19:44 +0000 Subject: [PATCH 01/60] feat: (idea spike) illustrate how we might use the SocketAuthenticator idea in https://github.com/gkc/socket_connector/tree/socket-authenticator-option --- .../lib/src/sshrvd/socket_connector.dart | 31 ++++++++++++++++--- packages/noports_core/pubspec.yaml | 6 ++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index 2572d2df7..c3427549d 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -1,22 +1,43 @@ import 'dart:io'; import 'dart:isolate'; +import 'dart:typed_data'; import 'package:at_utils/at_logger.dart'; import 'package:socket_connector/socket_connector.dart'; -typedef ConnectorParams = (SendPort, int, int, String, String, bool); +typedef ConnectorParams = (SendPort, int, int, String, String, String, bool); typedef PortPair = (int, int); final logger = AtSignLogger(' sshrvd / socket_connector '); +/// Purely for illustration purposes. +class DoNothingSocketAuthenticator extends SocketAuthenticator { + final BytesBuilder buffer = BytesBuilder(); + + final String session; + final String atSign; + + DoNothingSocketAuthenticator(this.session, this.atSign); + + @override + onData(Uint8List data, Socket socket) { + return (true, data); + } +} + /// This function is meant to be run in a separate isolate /// It starts the socket connector, and sends back the assigned ports to the main isolate /// It then waits for socket connector to die before shutting itself down void socketConnector(ConnectorParams params) async { - var (sendPort, portA, portB, session, forAtsign, snoop) = params; + var (sendPort, portA, portB, session, atSignA, atSignB, snoop) = params; - logger.info('Starting socket connector session $session for $forAtsign'); + logger.info('Starting socket connector session $session for $atSignA to $atSignB'); + // TODO These instances shouldn't be created here. + // Instead, the caller should add them into ConnectorParams and we should + // get them from there. + SocketAuthenticator socketAuthenticatorA = DoNothingSocketAuthenticator(session, atSignA); + SocketAuthenticator socketAuthenticatorB = DoNothingSocketAuthenticator(session, atSignB); /// Create the socket connector SocketConnector socketStream = await SocketConnector.serverToServer( serverAddressA: InternetAddress.anyIPv4, @@ -24,6 +45,8 @@ void socketConnector(ConnectorParams params) async { serverPortA: portA, serverPortB: portB, verbose: snoop, + socketAuthenticatorA: socketAuthenticatorA, + socketAuthenticatorB: socketAuthenticatorB, ); /// Get the assigned ports from the socket connector @@ -42,7 +65,7 @@ void socketConnector(ConnectorParams params) async { } logger.warning( - 'Finished session $session for $forAtsign using ports [$portA, $portB]'); + 'Finished session $session for $atSignA to $atSignB using ports [$portA, $portB]'); Isolate.current.kill(); } diff --git a/packages/noports_core/pubspec.yaml b/packages/noports_core/pubspec.yaml index 5e4b4085c..fc00956aa 100644 --- a/packages/noports_core/pubspec.yaml +++ b/packages/noports_core/pubspec.yaml @@ -24,6 +24,12 @@ dependencies: socket_connector: ^1.0.11 uuid: ^3.0.7 +dependency_overrides: + socket_connector: + git: + url: https://github.com/gkc/socket_connector.git + ref: socket-authenticator-option + dev_dependencies: build_runner: ^2.4.6 build_version: ^2.1.1 From 45740a15419a5f6ad9763e6d6134281506c83ee9 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 23 Nov 2023 12:29:47 +0000 Subject: [PATCH 02/60] fix: made code compile docs: Added TODOs --- .../util/sshrvd_channel/sshrvd_channel.dart | 4 ++++ .../lib/src/sshrvd/sshrvd_impl.dart | 17 ++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index c3ab88c0a..8804de61b 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -108,6 +108,10 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ..namespaceAware = false ..ttl = 10000); logger.info('Sending notification to sshrvd: $ourSshrvdIdKey'); + + // TODO Jagan We need to send not just the sessionId but other metaData + // especially, the atSign on the other end + // In the rvd we need to figure out backwards compatibility. await notify(ourSshrvdIdKey, sessionId); int counter = 0; diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index e14465995..7e49fc677 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -116,18 +116,20 @@ class SshrvdImpl implements Sshrvd { } String session = notification.value!; - String forAtsign = notification.from; + String atSignA = notification.from; + // TODO Jagan + String atSignB = atSignA; - if (managerAtsign != 'open' && managerAtsign != forAtsign) { - logger.shout('Session $session for $forAtsign denied'); + if (managerAtsign != 'open' && managerAtsign != atSignA) { + logger.shout('Session $session for $atSignA to $atSignB denied'); return; } (int, int) ports = - await _spawnSocketConnector(0, 0, session, forAtsign, snoop); + await _spawnSocketConnector(0, 0, session, atSignA, atSignB, snoop); var (portA, portB) = ports; logger - .warning('Starting session $session for $forAtsign using ports $ports'); + .warning('Starting session $session for $atSignA to $atSignB using ports $ports'); var metaData = Metadata() ..isPublic = false @@ -162,14 +164,15 @@ class SshrvdImpl implements Sshrvd { int portA, int portB, String session, - String forAtsign, + String atSignA, + String atSignB, bool snoop, ) async { /// Spawn an isolate and wait for it to send back the issued port numbers ReceivePort receivePort = ReceivePort(session); ConnectorParams parameters = - (receivePort.sendPort, portA, portB, session, forAtsign, snoop); + (receivePort.sendPort, portA, portB, session, atSignA, atSignB, snoop); logger .info("Spawning socket connector isolate with parameters $parameters"); From 59db455afe4625237147bd4d4e83fc0a88dba78d Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 23 Nov 2023 12:41:55 +0000 Subject: [PATCH 03/60] docs: added TODOs for sshrvd_impl --- packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index 7e49fc677..6253202ef 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -115,6 +115,16 @@ class SshrvdImpl implements Sshrvd { return; } + // TODO Jagan Extract the 'message' type from the notification key + // like we do in sshnpd_impl's _notificationHandler + // Then switch on the 'message' type + // - If it's legacy (just looks like deviceName.sshrvd) then handle like + // we currently do + // - If it's new (e.g. 'request_ports.deviceName.sshrvd') then we expect + // the notification to be JSON which contains sessionId, atSignA, atSignB + // and a flag (or a flag for each side) stating whether we want the + // socket connections to be authenticated via challenge-response or not. + // e.g. authenticateSocketsA, authenticateSocketsB String session = notification.value!; String atSignA = notification.from; // TODO Jagan From ad1dd12d7a3bb201ae73e464374ed8ca92366a3d Mon Sep 17 00:00:00 2001 From: vjag Date: Tue, 28 Nov 2023 19:41:24 +0530 Subject: [PATCH 04/60] Introducing signature verifying authenticator --- ...nature_verifying_socket_authenticator.dart | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart new file mode 100644 index 000000000..52a2de94b --- /dev/null +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -0,0 +1,76 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:at_chops/at_chops.dart'; +import 'package:socket_connector/socket_connector.dart'; +/// +/// Verifies signature of the data received over the socket using the same signing algorithm used to sign the data +/// See [SigningAlgoType] to know more about supported signing algorithms +/// See [HashingAlgoType] to know more about supported hashing algorithms +/// +/// Expects the first message received in JSON format, with the following structure: +/// { +/// "signature":"", +/// "hashingAlgo":"", +/// "signingAlgo":"" +/// } +/// +/// also expects signature to be base64 encoded +/// +class SignatureVerifyingSocketAuthenticator implements SocketAuthenticator { + // Public key of the signing algorithm used to sign the data + String publicKey; + // data that was signed, this is the data that should be matched once the signature is verified + dynamic dataToVerify; + + SignatureVerifyingSocketAuthenticator(this.publicKey, this.dataToVerify); + + @override + (bool authenticated, Uint8List? unused) onData( + Uint8List data, Socket socket) { + try { + final message = String.fromCharCodes(data); + // Expected message to be the JSON format with the below structure: + // { + // "signature":"", + // "hashingAlgo":"", + // "signingAlgo":"" + // } + var envelope = jsonDecode(message); + + final hashingAlgo = HashingAlgoType.values.byName(envelope['hashingAlgo']); + final signingAlgo = SigningAlgoType.values.byName(envelope['signingAlgo']); + + AtSigningVerificationInput input = AtSigningVerificationInput( + (dataToVerify), + (base64Decode(envelope['signature'])), + publicKey) + ..signingMode = AtSigningMode.data + ..signingAlgoType = signingAlgo + ..hashingAlgoType = hashingAlgo + ..signingAlgorithm = DefaultSigningAlgo(null, hashingAlgo); + + AtSigningResult atSigningResult = _verifySignature(input); + print( + 'Signing verification outcome is: ${atSigningResult.result}'); + bool result = atSigningResult.result; + + if (result == false) { + throw Exception( + 'Signature verification failed. Signatures did not match.'); + } + } catch (e) { + stderr.writeln('Error during socket authentication: $e'); + throw Exception(e); + } + return (true, null); + } + + AtSigningResult _verifySignature(AtSigningVerificationInput input) { + AtChopsKeys atChopsKeys = AtChopsKeys(); + AtChops atChops = AtChopsImpl(atChopsKeys); + return atChops.verify(input); + } +} \ No newline at end of file From 6cc0ca043a40131b25865cd7eb03d55e66f332a2 Mon Sep 17 00:00:00 2001 From: vjag Date: Tue, 5 Dec 2023 21:50:13 +0530 Subject: [PATCH 05/60] Refactored code to make it test friendly and added few unit tests --- .../lib/src/sshrvd/socket_connector.dart | 44 ++---- .../lib/src/sshrvd/sshrvd_impl.dart | 148 +++++++++++++++--- .../notification_subscription_test.dart | 10 ++ ...e_verifying_socket_authenticator_test.dart | 35 +++++ .../test/sshrvd/sshrvdutil_test.dart | 64 ++++++++ 5 files changed, 244 insertions(+), 57 deletions(-) create mode 100644 packages/noports_core/test/sshrvd/notification_subscription_test.dart create mode 100644 packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart create mode 100644 packages/noports_core/test/sshrvd/sshrvdutil_test.dart diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index c3427549d..1b9c5a6fd 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -1,52 +1,32 @@ import 'dart:io'; import 'dart:isolate'; -import 'dart:typed_data'; - import 'package:at_utils/at_logger.dart'; import 'package:socket_connector/socket_connector.dart'; -typedef ConnectorParams = (SendPort, int, int, String, String, String, bool); +typedef ConnectorParams = (SendPort, int, int, String, String, String?,SocketAuthenticator? socketAuthenticatorA, SocketAuthenticator? socketAuthenticatorB, bool); typedef PortPair = (int, int); final logger = AtSignLogger(' sshrvd / socket_connector '); -/// Purely for illustration purposes. -class DoNothingSocketAuthenticator extends SocketAuthenticator { - final BytesBuilder buffer = BytesBuilder(); - - final String session; - final String atSign; - - DoNothingSocketAuthenticator(this.session, this.atSign); - - @override - onData(Uint8List data, Socket socket) { - return (true, data); - } -} /// This function is meant to be run in a separate isolate /// It starts the socket connector, and sends back the assigned ports to the main isolate /// It then waits for socket connector to die before shutting itself down void socketConnector(ConnectorParams params) async { - var (sendPort, portA, portB, session, atSignA, atSignB, snoop) = params; + var (sendPort, portA, portB, session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB, snoop) = params; logger.info('Starting socket connector session $session for $atSignA to $atSignB'); - // TODO These instances shouldn't be created here. - // Instead, the caller should add them into ConnectorParams and we should - // get them from there. - SocketAuthenticator socketAuthenticatorA = DoNothingSocketAuthenticator(session, atSignA); - SocketAuthenticator socketAuthenticatorB = DoNothingSocketAuthenticator(session, atSignB); + /// Create the socket connector SocketConnector socketStream = await SocketConnector.serverToServer( - serverAddressA: InternetAddress.anyIPv4, - serverAddressB: InternetAddress.anyIPv4, - serverPortA: portA, - serverPortB: portB, - verbose: snoop, - socketAuthenticatorA: socketAuthenticatorA, - socketAuthenticatorB: socketAuthenticatorB, + serverAddressA: InternetAddress.anyIPv4, + serverAddressB: InternetAddress.anyIPv4, + serverPortA: portA, + serverPortB: portB, + verbose: snoop, + socketAuthenticatorA: socketAuthenticatorA, + socketAuthenticatorB: socketAuthenticatorB, ); /// Get the assigned ports from the socket connector @@ -61,11 +41,11 @@ void socketConnector(ConnectorParams params) async { /// Shut myself down once the socket connector closes bool closed = false; while (closed == false) { - closed = await socketStream.closed(); + closed = await socketStream.closed(); } logger.warning( - 'Finished session $session for $atSignA to $atSignB using ports [$portA, $portB]'); + 'Finished session $session for $atSignA to $atSignB using ports [$portA, $portB]'); Isolate.current.kill(); } diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index 6253202ef..84324a775 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -1,14 +1,17 @@ import 'dart:async'; import 'dart:io'; import 'dart:isolate'; - +import 'dart:convert'; import 'package:at_client/at_client.dart'; +import 'package:at_lookup/at_lookup.dart'; import 'package:at_utils/at_logger.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; +import 'package:noports_core/src/sshrvd/signature_verifying_socket_authenticator.dart'; import 'package:noports_core/src/sshrvd/socket_connector.dart'; import 'package:noports_core/src/sshrvd/sshrvd.dart'; import 'package:noports_core/src/sshrvd/sshrvd_params.dart'; +import 'package:socket_connector/socket_connector.dart'; @protected class SshrvdImpl implements Sshrvd { @@ -33,6 +36,8 @@ class SshrvdImpl implements Sshrvd { @visibleForTesting bool initialized = false; + static final String subscriptionRegex = '${Sshrvd.namespace}@'; + SshrvdImpl({ required this.atClient, required this.atSign, @@ -105,38 +110,35 @@ class SshrvdImpl implements Sshrvd { NotificationService notificationService = atClient.notificationService; notificationService - .subscribe(regex: '${Sshrvd.namespace}@', shouldDecrypt: true) + .subscribe(regex: subscriptionRegex, shouldDecrypt: true) .listen(_notificationHandler); } void _notificationHandler(AtNotification notification) async { - if (!notification.key.contains(Sshrvd.namespace)) { - // ignore notifications not for this namespace + if (!SshrvdUtil.accept(notification)) { return; } + late String session; + late String atSignA; + String? atSignB; + SocketAuthenticator? socketAuthenticatorA; + SocketAuthenticator? socketAuthenticatorB; + + try { + (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = await SshrvdUtil.getParams(notification); + + if (managerAtsign != 'open' && managerAtsign != atSignA) { + logger.shout('Session $session for $atSignA is denied'); + return; + } - // TODO Jagan Extract the 'message' type from the notification key - // like we do in sshnpd_impl's _notificationHandler - // Then switch on the 'message' type - // - If it's legacy (just looks like deviceName.sshrvd) then handle like - // we currently do - // - If it's new (e.g. 'request_ports.deviceName.sshrvd') then we expect - // the notification to be JSON which contains sessionId, atSignA, atSignB - // and a flag (or a flag for each side) stating whether we want the - // socket connections to be authenticated via challenge-response or not. - // e.g. authenticateSocketsA, authenticateSocketsB - String session = notification.value!; - String atSignA = notification.from; - // TODO Jagan - String atSignB = atSignA; - - if (managerAtsign != 'open' && managerAtsign != atSignA) { - logger.shout('Session $session for $atSignA to $atSignB denied'); + }catch(e) { + logger.shout( + 'Unable to provide the socket pair due to: $e'); return; } - (int, int) ports = - await _spawnSocketConnector(0, 0, session, atSignA, atSignB, snoop); + await _spawnSocketConnector(0, 0, session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB, snoop); var (portA, portB) = ports; logger .warning('Starting session $session for $atSignA to $atSignB using ports $ports'); @@ -175,14 +177,15 @@ class SshrvdImpl implements Sshrvd { int portB, String session, String atSignA, - String atSignB, + String? atSignB, SocketAuthenticator? socketAuthenticatorA, + SocketAuthenticator? socketAuthenticatorB, bool snoop, ) async { /// Spawn an isolate and wait for it to send back the issued port numbers ReceivePort receivePort = ReceivePort(session); ConnectorParams parameters = - (receivePort.sendPort, portA, portB, session, atSignA, atSignB, snoop); + (receivePort.sendPort, portA, portB, session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB, snoop); logger .info("Spawning socket connector isolate with parameters $parameters"); @@ -196,3 +199,98 @@ class SshrvdImpl implements Sshrvd { return ports; } } + +class SshrvdUtil { + static bool accept(AtNotification notification) { + return notification.key.contains(Sshrvd.namespace); + } + + static Future<(String, String, String?, SocketAuthenticator?, SocketAuthenticator?)> getParams(AtNotification notification) async { + if(notification.key.contains('request_ports') && notification.key.contains(Sshrvd.namespace)) { + return await _processJSONRequest(notification); + } + return _processLegacyRequest(notification);; + } + + + static (String, String, String?, SocketAuthenticator?, SocketAuthenticator?) _processLegacyRequest(AtNotification notification) { + return (notification.value!, notification.from, null, null, null); + } + + static Future<(String, String, String?, SocketAuthenticator?, SocketAuthenticator?)> _processJSONRequest(AtNotification notification) async { + String session = ''; + String atSignA = ''; + String atSignB = ''; + bool authenticateSocketA = false; + bool authenticateSocketB = false; + SocketAuthenticator? socketAuthenticatorA; + SocketAuthenticator? socketAuthenticatorB; + + dynamic jsonValue = jsonDecode(notification.value ?? ''); + + if(jsonValue['session'] == null || jsonValue['atSignA'] == null || jsonValue['atSignB'] == null) { + throw Exception('session, atSignA and atSignB cannot be empty'); + } + + session = jsonValue['session']; + atSignA = jsonValue['atSignA']; + atSignB = jsonValue['atSignB']; + authenticateSocketA = jsonValue['authenticateSocketA']; + authenticateSocketB = jsonValue['authenticateSocketB']; + + if(authenticateSocketA) { + String? pkAtSignA = await _fetchPublicKey(atSignA); + if(pkAtSignA == null) { + logger.shout( + 'Cannot spawn socket connector. Authenticator for $atSignA could not be created as PublicKey could not be fetched from the secondary server.'); + throw Exception('Unable to create SocketAuthenticator for $atSignA due to not able to get public key for $atSignA'); + } + socketAuthenticatorA = SignatureVerifyingSocketAuthenticator(pkAtSignA, session); + } + + if(authenticateSocketB) { + String? pkAtSignB = await _fetchPublicKey(atSignB); + if(pkAtSignB == null) { + logger.shout( + 'Cannot spawn socket connector. Authenticator for $atSignB could not be created as PublicKey could not be fetched from the secondary server.'); + throw Exception('Unable to create SocketAuthenticator for $atSignB due to not able to get public key for $atSignB'); + } + socketAuthenticatorB = SignatureVerifyingSocketAuthenticator(pkAtSignB, session); + } + + return (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB); + } + + static Future _fetchPublicKey(String atSign, + {int secondsToWait = 10}) async { + String? publicKey; + AtLookupImpl atLookupImpl = AtLookupImpl(atSign, 'root.atsign.org', 64); + SecondaryAddress secondaryAddress = + await atLookupImpl.secondaryAddressFinder.findSecondary(atSign); + + SecureSocket secureSocket = await SecureSocket.connect( + secondaryAddress.host, secondaryAddress.port); + + secureSocket.listen((event) { + String serverResponse = utf8.decode(event); + if (serverResponse == '@') { + secureSocket.write('lookup:publickey$atSign\n'); + } else if (serverResponse.startsWith('data:')) { + publicKey = serverResponse.replaceFirst('data:', ''); + publicKey = publicKey?.substring(0, publicKey?.indexOf('\n')).trim(); + } + }); + + + int totalSecondsWaited = 0; + while (totalSecondsWaited < secondsToWait) { + await Future.delayed(Duration(seconds: 1)); + totalSecondsWaited = totalSecondsWaited + 1; + if (publicKey != null) { + break; + } + } + await secureSocket.close(); + return publicKey; + } +} diff --git a/packages/noports_core/test/sshrvd/notification_subscription_test.dart b/packages/noports_core/test/sshrvd/notification_subscription_test.dart new file mode 100644 index 000000000..defb6d71c --- /dev/null +++ b/packages/noports_core/test/sshrvd/notification_subscription_test.dart @@ -0,0 +1,10 @@ +import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; +import 'package:noports_core/sshrvd.dart'; +import 'package:test/test.dart'; +void main() { + + test('Test notification subscription regex', () { + expect(RegExp(SshrvdImpl.subscriptionRegex).hasMatch('jagan@test.${Sshrvd.namespace}@jagan'), true); + expect(RegExp(SshrvdImpl.subscriptionRegex).hasMatch('${Sshrvd.namespace}@'), true); + }); +} \ No newline at end of file diff --git a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart new file mode 100644 index 000000000..21f45dda1 --- /dev/null +++ b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart @@ -0,0 +1,35 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:mocktail/mocktail.dart'; +import 'package:noports_core/src/sshrvd/signature_verifying_socket_authenticator.dart'; +import 'package:test/test.dart'; + +void main() { + + + test('SignatureVerifyingSocketAuthenticator signature verification test', () { + + String pk = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmKrsuxuo3KAZ+F8xnpBEYPToJzsRpiwFmMYDVKlhO5B1bB63PUUY0NCwq/RXSqN28I7Tbzv6nqAjRgzKUum+rsMNJqlVwGZP/26kD7vo82UYDluyqIKmp9uDp2tWmR536a73qSl9nxKNY+7cwrFWn1rMpriWtaM67psMeXnMK4/wdK16tb55CUDKuHpE2y9vTtTtn5n52aL50jsttjFBxBX+h1hkUHOprgLfpwqsHknbuENHL9mTCsOFz1nlmqRzayCgtT6POCeuDrj2roVyzj1k/vD25rCMIG2D9uVkPQy3Qi1Wi/SEYvOzeazYE/mB3uBGb+ltmGDKD59Scw31uQIDAQAB'; + SignatureVerifyingSocketAuthenticator sa = SignatureVerifyingSocketAuthenticator(pk , 'hello'); + + String source = '{"signature":"BeDvrOfOcKbA3CMwFsiWRUAjgdcfOc7kzDwdTODEfI94GZkZPGi6mo3c1e5BF88TnwZ1h4lMgecPZQpEkBPyHfa5Gk16VyZ/ddzyUfqhqW962ueneVpnfDzsLVVV6a6/Cz3PUojRGnLo/nAInlIE86REt3HYlkpWS9/IDIdamaPI1wuCkjOkUzFC3mfbV8kKABlaD6B50ePT6mS9+4EK5273UpKhQ5gWHons4mEw2iEqhXa4xmbdlr3JF2Al8FD8V+2itu+ecHwKA+uldxDIf5ckiPywdW65ti/QuVDQqtetky35ksePuSFSixbltjjMT+/7NTJ4ceFL5QtMCwKC3Q==","hashingAlgo":"sha256","signingAlgo":"rsa2048"}'; + List list = utf8.encode(source); + Uint8List data = Uint8List.fromList(list); + + bool authenticated; + Uint8List? unused; + + (authenticated, unused) = sa.onData(data, MockSocket()); + expect(authenticated, true); + expect(unused, null); + + source = '{"signature":"Invalid signature","hashingAlgo":"sha256","signingAlgo":"rsa2048"}'; + list = utf8.encode(source); + data = Uint8List.fromList(list); + + expect(() => sa.onData(data, MockSocket()), throwsException); + }); +} + +class MockSocket extends Mock implements Socket {} \ No newline at end of file diff --git a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart new file mode 100644 index 000000000..e2ce7cbb4 --- /dev/null +++ b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart @@ -0,0 +1,64 @@ +import 'dart:convert'; + +import 'package:at_client/at_client.dart'; +import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; +import 'package:noports_core/sshrvd.dart'; +import 'package:socket_connector/socket_connector.dart'; +import 'package:test/test.dart'; + +void main() { + test('test notification subscription regex', () { + // Create a notification in rvd namespace + AtNotification notification = AtNotification.empty(); + notification.key = 'test.${Sshrvd.namespace}'; + + }); + + test('sshrvd should accept notification in new request_ports format', () { + // Create a notification in rvd namespace + AtNotification notification = AtNotification.empty(); + notification.key = 'request_ports.test.${Sshrvd.namespace}'; + expect(SshrvdUtil.accept(notification), true); + }); + + test('sshrvd backwards compatibility test - should handle both legacy and new messages in JSON format', () async { + Map m = {}; + m['session'] = 'hello'; + m['atSignA'] = '@4314sagittarius'; + m['atSignB'] = '@4314sagittarius'; + m['authenticateSocketA'] = false; + m['authenticateSocketB'] = false; + + // New message + AtNotification notification = AtNotification.empty(); + notification.key = 'request_ports.test.${Sshrvd.namespace}'; + notification.value = jsonEncode(m); + + expect(SshrvdUtil.accept(notification), true); + + late String session; + late String atSignA; + String? atSignB; + SocketAuthenticator? socketAuthenticatorA; + SocketAuthenticator? socketAuthenticatorB; + (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = await SshrvdUtil.getParams(notification); + expect(session, 'hello'); + expect(atSignA, '@4314sagittarius'); + expect(atSignB, '@4314sagittarius'); + expect(socketAuthenticatorA, null); + expect(socketAuthenticatorB, null); + + // Legacy message, but a JSON + notification = AtNotification.empty(); + notification.key = 'test.${Sshrvd.namespace}'; + notification.value = jsonEncode(m); + notification.from = '@4314sagittarius'; + + expect(SshrvdUtil.accept(notification), true); + (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = await SshrvdUtil.getParams(notification); + expect(atSignA, '@4314sagittarius'); + expect(atSignB, null); + expect(socketAuthenticatorA, null); + expect(socketAuthenticatorB, null); + }); +} From 14cdc9d34b8ab1466e9ac450bf0e58ea2406da47 Mon Sep 17 00:00:00 2001 From: vjag Date: Tue, 5 Dec 2023 21:54:57 +0530 Subject: [PATCH 06/60] Refactored code to make it test friendly and added few unit tests --- .../noports_core/test/sshrvd/notification_subscription_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/noports_core/test/sshrvd/notification_subscription_test.dart b/packages/noports_core/test/sshrvd/notification_subscription_test.dart index defb6d71c..e14165a16 100644 --- a/packages/noports_core/test/sshrvd/notification_subscription_test.dart +++ b/packages/noports_core/test/sshrvd/notification_subscription_test.dart @@ -6,5 +6,6 @@ void main() { test('Test notification subscription regex', () { expect(RegExp(SshrvdImpl.subscriptionRegex).hasMatch('jagan@test.${Sshrvd.namespace}@jagan'), true); expect(RegExp(SshrvdImpl.subscriptionRegex).hasMatch('${Sshrvd.namespace}@'), true); + expect(RegExp(SshrvdImpl.subscriptionRegex).hasMatch('${Sshrvd.namespace}.test@'), false); }); } \ No newline at end of file From dfdfa8116b5a13bde89b2a4eb2e6664a3ddc2a65 Mon Sep 17 00:00:00 2001 From: vjag Date: Tue, 12 Dec 2023 20:51:49 +0530 Subject: [PATCH 07/60] Changes to send notification as a json --- .../util/sshrvd_channel/sshrvd_channel.dart | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 8804de61b..8fe8c17fb 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:at_client/at_client.dart'; import 'package:at_utils/at_utils.dart'; @@ -109,10 +110,11 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ..ttl = 10000); logger.info('Sending notification to sshrvd: $ourSshrvdIdKey'); - // TODO Jagan We need to send not just the sessionId but other metaData + String notificationValue = _getValue(params, sessionId); + // We need to send not just the sessionId but other metaData // especially, the atSign on the other end // In the rvd we need to figure out backwards compatibility. - await notify(ourSshrvdIdKey, sessionId); + await notify(ourSshrvdIdKey, notificationValue); int counter = 0; while (sshrvdAck == SshrvdAck.notAcknowledged) { @@ -125,4 +127,22 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } } } + + String _getValue(params, sessionId) { + bool supportsClientAuthentication = false; + bool authenticateSocketA = false; + bool authenticateSocketB = false; + + if(supportsClientAuthentication) { + Map m = {}; + m['session'] = sessionId; + m['atSignA'] = params.clientAtSign; + m['atSignB'] = params.sshnpdAtsign; + m['authenticateSocketA'] = authenticateSocketA; + m['authenticateSocketB'] = authenticateSocketB; + return jsonEncode(m); + } else { + return sessionId; + } + } } From a6632101134c6a69dba78751afdf42b032abc95a Mon Sep 17 00:00:00 2001 From: vjag Date: Thu, 14 Dec 2023 12:40:38 +0530 Subject: [PATCH 08/60] New abstractions to enable various notification message formats and authentication by SSHRV --- .../util/sshrvd_channel/sshrvd_channel.dart | 11 +++-------- .../lib/src/sshnpd/sshnpd_impl.dart | 4 +++- packages/noports_core/lib/src/sshrv/sshrv.dart | 10 +++++++--- .../noports_core/lib/src/sshrv/sshrv_impl.dart | 17 +++++++++++++---- .../lib/src/sshrvd/sshrvd_impl.dart | 2 +- 5 files changed, 27 insertions(+), 17 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 8fe8c17fb..8331437f7 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -6,6 +6,7 @@ import 'package:at_utils/at_utils.dart'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/mixins/async_initialization.dart'; import 'package:noports_core/src/common/mixins/at_client_bindings.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/notification_request_message.dart'; import 'package:noports_core/sshnp.dart'; import 'package:noports_core/sshrv.dart'; import 'package:noports_core/sshrvd.dart'; @@ -134,15 +135,9 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { bool authenticateSocketB = false; if(supportsClientAuthentication) { - Map m = {}; - m['session'] = sessionId; - m['atSignA'] = params.clientAtSign; - m['atSignB'] = params.sshnpdAtsign; - m['authenticateSocketA'] = authenticateSocketA; - m['authenticateSocketB'] = authenticateSocketB; - return jsonEncode(m); + return AuthentionEnablingMessage(sessionId, params.clientAtSign, params.sshnpdAtsign, authenticateSocketA, authenticateSocketB).toString(); } else { - return sessionId; + return SessionIdMessage(sessionId).toString(); } } } diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 42b25057d..b9458c3ed 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -14,6 +14,8 @@ import 'package:noports_core/utils.dart'; import 'package:noports_core/src/version.dart'; import 'package:uuid/uuid.dart'; +import '../sshrv/auth_provider.dart'; + @protected class SshnpdImpl implements Sshnpd { @override @@ -499,7 +501,7 @@ class SshnpdImpl implements Sshnpd { // Connect to rendezvous point using background process. // This program can then exit without causing an issue. Process rv = - await Sshrv.exec(host, port, localSshdPort: localSshdPort).run(); + await Sshrv.exec(host, port, localSshdPort: localSshdPort, authenticationProvider: SocketAuthenticationProviderManager.getAuthenticationProvider()).run(); logger.info('Started rv - pid is ${rv.pid}'); LocalSshKeyUtil keyUtil = LocalSshKeyUtil(); diff --git a/packages/noports_core/lib/src/sshrv/sshrv.dart b/packages/noports_core/lib/src/sshrv/sshrv.dart index ffbc36ffc..bb864cb0e 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv.dart @@ -4,6 +4,8 @@ import 'package:noports_core/src/sshrv/sshrv_impl.dart'; import 'package:socket_connector/socket_connector.dart'; import 'package:noports_core/src/common/default_args.dart'; +import 'auth_provider.dart'; + abstract class Sshrv { /// The internet address of the host to connect to. abstract final String host; @@ -15,25 +17,27 @@ abstract class Sshrv { /// Defaults to 22 abstract final int localSshdPort; + SocketAuthenticationProvider? authenticationProvider; + Future run(); // Can't use factory functions since SSHRV contains a generic type static Sshrv exec( String host, int streamingPort, { - int localSshdPort = DefaultArgs.localSshdPort, + int localSshdPort = DefaultArgs.localSshdPort, SocketAuthenticationProvider? authenticationProvider }) { return SshrvImplExec( host, streamingPort, - localSshdPort: localSshdPort, + localSshdPort: localSshdPort, authenticationProvider:authenticationProvider ); } static Sshrv dart( String host, int streamingPort, { - int localSshdPort = 22, + int localSshdPort = 22, SocketAuthenticationProvider? authenticationProvider }) { return SshrvImplDart( host, diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index f708708b4..f2eb4be50 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -7,6 +7,8 @@ import 'package:socket_connector/socket_connector.dart'; import 'package:noports_core/src/common/default_args.dart'; +import 'auth_provider.dart'; + @visibleForTesting class SshrvImplExec implements Sshrv { @override @@ -18,10 +20,14 @@ class SshrvImplExec implements Sshrv { @override final int localSshdPort; - const SshrvImplExec( + @override + SocketAuthenticationProvider? authenticationProvider; + + + SshrvImplExec( this.host, this.streamingPort, { - this.localSshdPort = DefaultArgs.localSshdPort, + this.localSshdPort = DefaultArgs.localSshdPort, this.authenticationProvider }); @override @@ -53,10 +59,13 @@ class SshrvImplDart implements Sshrv { @override final int localSshdPort; - const SshrvImplDart( + @override + SocketAuthenticationProvider? authenticationProvider; + + SshrvImplDart( this.host, this.streamingPort, { - this.localSshdPort = 22, + this.localSshdPort = 22, SocketAuthenticationProvider? authenticationProvider }); @override diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index 84324a775..cf9cdc61c 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -150,7 +150,7 @@ class SshrvdImpl implements Sshrvd { ..namespaceAware = true; var atKey = AtKey() - ..key = notification.value + ..key = session ..sharedBy = atSign ..sharedWith = notification.from ..namespace = Sshrvd.namespace From 79bf7e4ea6800d7742e77cf1a6431a9add328eda Mon Sep 17 00:00:00 2001 From: vjag Date: Thu, 14 Dec 2023 15:16:51 +0530 Subject: [PATCH 09/60] Auth provider and corresponding verification test --- .../notification_request_message.dart | 37 +++++++ .../lib/src/sshrv/auth_provider.dart | 96 +++++++++++++++++++ .../sshrv/authentication_provider_test.dart | 37 +++++++ 3 files changed, 170 insertions(+) create mode 100644 packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart create mode 100644 packages/noports_core/lib/src/sshrv/auth_provider.dart create mode 100644 packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart new file mode 100644 index 000000000..4886020bb --- /dev/null +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart @@ -0,0 +1,37 @@ +import 'dart:convert'; + +class NotificationRequestMessage { + @override + String toString(); +} + +class SessionIdMessage extends NotificationRequestMessage{ + String sessionId; + + SessionIdMessage(this.sessionId); + + @override + String toString() { + return sessionId; + } +} + +class AuthentionEnablingMessage extends SessionIdMessage{ + String atSignA; + String atSignB; + bool authenticateSocketA; + bool authenticateSocketB; + + AuthentionEnablingMessage(String sessionId, this.atSignA, this.atSignB, this.authenticateSocketA, this.authenticateSocketB) : super(sessionId); + + @override + String toString() { + Map m = {}; + m['session'] = sessionId; + m['atSignA'] = atSignA; + m['atSignB'] = atSignB; + m['authenticateSocketA'] = authenticateSocketA; + m['authenticateSocketB'] = authenticateSocketB; + return jsonEncode(m); + } +} diff --git a/packages/noports_core/lib/src/sshrv/auth_provider.dart b/packages/noports_core/lib/src/sshrv/auth_provider.dart new file mode 100644 index 000000000..0a099e80c --- /dev/null +++ b/packages/noports_core/lib/src/sshrv/auth_provider.dart @@ -0,0 +1,96 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:at_chops/at_chops.dart'; + +abstract class SocketAuthenticationProvider { + authenticate(Socket socket); +} + +class EmptySocketAuthenticationProvider extends SocketAuthenticationProvider{ + @override + authenticate(Socket socket) { + //Do Nothing + } +} + +/// +/// Signs the sessionId and create a JSON in the following format with the signed data +/// +/// { +/// "signature":"", +/// "hashingAlgo":"", +/// "signingAlgo":"" +/// } +class JsonSignatureAuthenticationProvider extends SocketAuthenticationProvider{ + String sessionId; + String privateKey; + + JsonSignatureAuthenticationProvider(this.sessionId, this.privateKey); + + @override + authenticate(Socket socket) { + // Sign and write to the socket + String signedData = sign(sessionId); + socket.write(signedData); + } + + String sign(String dataToSign) { + AtEncryptionKeyPair atEncryptionKeyPair = + AtEncryptionKeyPair.create('', privateKey); + AtPkamKeyPair atPkamKeyPair = AtPkamKeyPair.create('', privateKey); + AtChopsKeys atChopsKeys = + AtChopsKeys.create(atEncryptionKeyPair, atPkamKeyPair); + AtChops atChops = AtChopsImpl(atChopsKeys); + + Map responseMap = {}; + AtSigningInput atSigningInput = AtSigningInput(dataToSign) + ..signingAlgorithm = DefaultSigningAlgo( + atChops.atChopsKeys.atEncryptionKeyPair, HashingAlgoType.sha256); + var atSigningResponse = atChops.sign(atSigningInput); + + responseMap['signature'] = atSigningResponse.result; + responseMap['hashingAlgo'] = + _getHashingAlgo(atSigningResponse.atSigningMetaData.hashingAlgoType); + responseMap['signingAlgo'] = + _getSigningAlgo(atSigningResponse.atSigningMetaData.signingAlgoType); + + return jsonEncode(responseMap); + } + + String _getHashingAlgo(HashingAlgoType? hashingAlgoType) { + + switch(hashingAlgoType) { + case HashingAlgoType.sha256: + return "sha256"; + case HashingAlgoType.sha512: + return "sha512"; + case HashingAlgoType.md5: + return "md5"; + default: + return "sha256"; + } + } + + String _getSigningAlgo(SigningAlgoType? signingAlgoType) { + switch(signingAlgoType) { + case SigningAlgoType.ecc_secp256r1: + return "ecc_secp256r1"; + case SigningAlgoType.rsa2048: + return "rsa2048"; + case SigningAlgoType.rsa4096: + return "rsa4096"; + default: + return "rsa2048"; + } + } +} + + + +class SocketAuthenticationProviderManager { + static SocketAuthenticationProvider getAuthenticationProvider() { + return EmptySocketAuthenticationProvider(); + } +} \ No newline at end of file diff --git a/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart b/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart new file mode 100644 index 000000000..c08626bb2 --- /dev/null +++ b/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart @@ -0,0 +1,37 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:mocktail/mocktail.dart'; +import 'package:noports_core/src/sshrv/auth_provider.dart'; +import 'package:noports_core/src/sshrvd/signature_verifying_socket_authenticator.dart'; +import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; +import 'package:noports_core/sshrvd.dart'; +import 'package:test/test.dart'; +void main() { + + String testEncryptionPrivateKey = + 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYquy7G6jcoBn4XzGekERg9OgnOxGmLAWYxgNUqWE7kHVsHrc9RRjQ0LCr9FdKo3bwjtNvO/qeoCNGDMpS6b6uww0mqVXAZk//bqQPu+jzZRgOW7Kogqan24Ona1aZHnfprvepKX2fEo1j7tzCsVafWsymuJa1ozrumwx5ecwrj/B0rXq1vnkJQMq4ekTbL29O1O2fmfnZovnSOy22MUHEFf6HWGRQc6muAt+nCqweSdu4Q0cv2ZMKw4XPWeWapHNrIKC1Po84J64OuPauhXLOPWT+8PbmsIwgbYP25WQ9DLdCLVaL9IRi87N5rNgT+YHe4EZv6W2YYMoPn1JzDfW5AgMBAAECggEAVYXC0dpf0SAbDEj/ee0lcQ8hEgEEFQuqIvgiG4Y7Quvc67GVQsx3Z1rQ7bMWR2ilE4NfLHv0HHJm8DHwEVyCBlKcBmFr+TkXbWcknu/MQrUKMdjqj32JMJVG/j2iKGqqEA2FDY2Bot/4ttezcZl4hhKOfIMBYkVLmSjgZxh06J1/MO0TFRbbsNNhCWmV0SzwxmS6O21/4ca8IbnD59KQbYAY4q60WcswkLm2VKNngOOggFHRIBQVu0KLvF6jr9IWHkR/b+rKOrR4r04bdIKCVf7mtWKc6lKoqKYJeXiK1WPPOuvZKpyTGNIYr99uGnCQ6Swj67T/btz194P6x+dpxQKBgQDKFy8bxkKeXPvUAVNgPSA7tndvinrfm4Q0UTtbovAtiAegTAJfAC9Kn3GkUFHMe9hPIaGFlfv0BncIZLWyYOOrfrL4FoxFpogIqYpROeu6QvUuGDMOo/qqBqn3s77Zkq5FGjAj5FgIqVmCP4XwJ9lOksSvGpPoFz507hTu7QskswKBgQDBZKOdFbJAjP62LoS78g7TSeT7C3AXgd9jM8RZs/xYKIAKE3st/2BdjSOveo0jTG47+U3+Tws7oMyMmO69u/RhsmF4z2Fg6Th6S/D+HrNDmm0BkBQ865mEY2TahhdfjUewTTCC+T00x2TBLZmJDjZPMO8BTZ2SljX0rVQttOVp4wKBgDTWcPOzF5HuP82DdzgvYzEZmQqpy0yRjbRcFMf1xxQwf8XyeaA7HSJGo+DRO0Hak4jFA0U5HMIFurOQGU2FNaGOI97njk9bpi+VnFt2aGKvxQkDPL40M4Km8WOZNGoQhs38dd+8gSPqm0OJtkw/LvrzNseNjGRfR24tHX4GriYvAoGAKBsNzyrLr5VN0UwuXKejKXAem21Qzp8xS2pV4uBviXzEqNJHbk+SlXQKnX6FvHdCOQ/He+C6jKAZK2Mfx5st4ADVM++V2zoia0JKdPi65l8lEfjmKYgWax0Nsj+yoy8yWb54PAEiD0r2exVQzNp0qtGUDyogbmDWSaqUVXI5TU8CgYB8vuHUsTx4ibazsmth/TztlAhQMk3TNmi5/MoCNuzJNf3WXwx11iKiIwb+zQjyk4Vbhs54GTTx4FQcjtHHRg9B/XA64zckKIGI/5BLpT/LuM67MeVS73uD7J6jy6BdmFtbz3ChRTA4+x7Y8AGtARFVPIENL1cAY1nisb4cT3vwQg=='; + + String testEncryptionPublicKey = + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmKrsuxuo3KAZ+F8xnpBEYPToJzsRpiwFmMYDVKlhO5B1bB63PUUY0NCwq/RXSqN28I7Tbzv6nqAjRgzKUum+rsMNJqlVwGZP/26kD7vo82UYDluyqIKmp9uDp2tWmR536a73qSl9nxKNY+7cwrFWn1rMpriWtaM67psMeXnMK4/wdK16tb55CUDKuHpE2y9vTtTtn5n52aL50jsttjFBxBX+h1hkUHOprgLfpwqsHknbuENHL9mTCsOFz1nlmqRzayCgtT6POCeuDrj2roVyzj1k/vD25rCMIG2D9uVkPQy3Qi1Wi/SEYvOzeazYE/mB3uBGb+ltmGDKD59Scw31uQIDAQAB'; + + test('Test json signinging and verification', () { + + var provider = JsonSignatureAuthenticationProvider('hello', testEncryptionPrivateKey); + var signedData = provider.sign('hello'); + print(signedData); + SignatureVerifyingSocketAuthenticator sa = SignatureVerifyingSocketAuthenticator(testEncryptionPublicKey , 'hello'); + List list = utf8.encode(signedData); + Uint8List data = Uint8List.fromList(list); + + bool authenticated; + Uint8List? unused; + + (authenticated, unused) = sa.onData(data, MockSocket()); + expect(authenticated, true); + expect(unused, null); + }); +} + +class MockSocket extends Mock implements Socket {} \ No newline at end of file From e9dd01bbb10edff0edf8aeb20eaddd2cb62fe689 Mon Sep 17 00:00:00 2001 From: vjag Date: Thu, 14 Dec 2023 15:18:02 +0530 Subject: [PATCH 10/60] Auth provider and corresponding verification test --- .../test/sshnp/sshrv/authentication_provider_test.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart b/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart index c08626bb2..adb351f0d 100644 --- a/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart +++ b/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart @@ -1,13 +1,11 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; - import 'package:mocktail/mocktail.dart'; import 'package:noports_core/src/sshrv/auth_provider.dart'; import 'package:noports_core/src/sshrvd/signature_verifying_socket_authenticator.dart'; -import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; -import 'package:noports_core/sshrvd.dart'; import 'package:test/test.dart'; + void main() { String testEncryptionPrivateKey = From f33319be9a22bf7f42153338ebb4d3c1cacd67ed Mon Sep 17 00:00:00 2001 From: vjag Date: Thu, 14 Dec 2023 16:15:22 +0530 Subject: [PATCH 11/60] Authenticate in Sshrv when authenticationProvider is provided --- packages/noports_core/lib/src/sshrv/sshrv.dart | 2 +- packages/noports_core/lib/src/sshrv/sshrv_impl.dart | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/noports_core/lib/src/sshrv/sshrv.dart b/packages/noports_core/lib/src/sshrv/sshrv.dart index bb864cb0e..5509764d7 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv.dart @@ -42,7 +42,7 @@ abstract class Sshrv { return SshrvImplDart( host, streamingPort, - localSshdPort: localSshdPort, + localSshdPort: localSshdPort, authenticationProvider:authenticationProvider ); } diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index f2eb4be50..51b2a32ed 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -73,13 +73,18 @@ class SshrvImplDart implements Sshrv { try { var hosts = await InternetAddress.lookup(host); - return await SocketConnector.socketToSocket( + SocketConnector socketConnector = await SocketConnector.socketToSocket( socketAddressA: InternetAddress.loopbackIPv4, socketPortA: localSshdPort, socketAddressB: hosts[0], socketPortB: streamingPort, verbose: true, ); + + // TBD - Gary/Xavier/Jagan + // Should we expose the sockets from socketConnector? whats the best way to do this. + //authenticationProvider?.authenticate(socketConnector.socketB); + return socketConnector; } catch (e) { AtSignLogger('sshrv').severe(e.toString()); rethrow; From 3b8398421cdcf3ed42627a3a94f6f6f813a8b2af Mon Sep 17 00:00:00 2001 From: vjag Date: Tue, 19 Dec 2023 11:13:59 +0530 Subject: [PATCH 12/60] Changes to refactor notification message classes and introduced manager class to create the right message type --- .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 14 +++++---- .../sshnp/impl/sshnp_openssh_local_impl.dart | 15 ++++----- .../notification_request_message.dart | 31 ++++++++++++------- .../util/sshrvd_channel/sshrvd_channel.dart | 14 +++------ 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index 5c22528d2..d1506af8e 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -4,6 +4,8 @@ import 'package:at_client/at_client.dart'; import 'package:dartssh2/dartssh2.dart'; import 'package:noports_core/sshnp_foundation.dart'; +import 'notification_request_message.dart'; + class SshnpDartPureImpl extends SshnpCore with SshnpDartSshKeyHandler, SshnpDartInitialTunnelHandler { SshnpDartPureImpl({ @@ -42,6 +44,11 @@ class SshnpDartPureImpl extends SshnpCore Future run() async { /// Ensure that sshnp is initialized await callInitialization(); + SSHNPNotificationRequestMessage message = SSHNPNotificationRequestMessageManager.get(false); + message.direct = true; + message.sessionId =sessionId; + message.host = sshrvdChannel.host; + message.port = sshrvdChannel.port; /// Send an ssh request to sshnpd await notify( @@ -51,12 +58,7 @@ class SshnpDartPureImpl extends SshnpCore ..sharedBy = params.clientAtSign ..sharedWith = params.sshnpdAtSign ..metadata = (Metadata()..ttl = 10000), - signAndWrapAndJsonEncode(atClient, { - 'direct': true, - 'sessionId': sessionId, - 'host': sshrvdChannel.host, - 'port': sshrvdChannel.port, - }), + signAndWrapAndJsonEncode(atClient, message.message()), ); /// Wait for a response from sshnpd diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index 4c76d60a4..401b53218 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -5,6 +5,8 @@ import 'package:meta/meta.dart'; import 'package:noports_core/src/common/io_types.dart'; import 'package:noports_core/sshnp_foundation.dart'; +import 'notification_request_message.dart'; + class SshnpOpensshLocalImpl extends SshnpCore with SshnpLocalSshKeyHandler, SshnpOpensshInitialTunnelHandler { SshnpOpensshLocalImpl({ @@ -63,7 +65,11 @@ class SshnpOpensshLocalImpl extends SshnpCore Future run() async { /// Ensure that sshnp is initialized await callInitialization(); - + SSHNPNotificationRequestMessage message = SSHNPNotificationRequestMessageManager.get(false); + message.direct = true; + message.sessionId =sessionId; + message.host = sshrvdChannel.host; + message.port = sshrvdChannel.port; /// Send an ssh request to sshnpd await notify( AtKey() @@ -72,12 +78,7 @@ class SshnpOpensshLocalImpl extends SshnpCore ..sharedBy = params.clientAtSign ..sharedWith = params.sshnpdAtSign ..metadata = (Metadata()..ttl = 10000), - signAndWrapAndJsonEncode(atClient, { - 'direct': true, - 'sessionId': sessionId, - 'host': sshrvdChannel.host, - 'port': sshrvdChannel.port, - }), + signAndWrapAndJsonEncode(atClient, message.message()), ); /// Wait for a response from sshnpd diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart index 4886020bb..af7c6d648 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart @@ -1,14 +1,13 @@ import 'dart:convert'; -class NotificationRequestMessage { +abstract class SSHNPDNotificationRequestMessage { + late String sessionId; + @override String toString(); } -class SessionIdMessage extends NotificationRequestMessage{ - String sessionId; - - SessionIdMessage(this.sessionId); +class SessionIdMessage extends SSHNPDNotificationRequestMessage{ @override String toString() { @@ -16,13 +15,11 @@ class SessionIdMessage extends NotificationRequestMessage{ } } -class AuthentionEnablingMessage extends SessionIdMessage{ - String atSignA; - String atSignB; - bool authenticateSocketA; - bool authenticateSocketB; - - AuthentionEnablingMessage(String sessionId, this.atSignA, this.atSignB, this.authenticateSocketA, this.authenticateSocketB) : super(sessionId); +class AuthenticationEnablingMessage extends SessionIdMessage { + late String atSignA; + late String atSignB; + late bool authenticateSocketA; + late bool authenticateSocketB; @override String toString() { @@ -35,3 +32,13 @@ class AuthentionEnablingMessage extends SessionIdMessage{ return jsonEncode(m); } } + +class SSHNPDNotificationRequestMessageManager { + static SSHNPDNotificationRequestMessage get(bool authenticate) { + + if(authenticate) { + return AuthenticationEnablingMessage(); + } + return SessionIdMessage(); + } +} diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 8331437f7..3a7aba3d7 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -43,6 +43,8 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { String get host => _host ?? params.host; int get port => _port ?? params.port; + bool authenticateDevice = false; + // * Volatile fields set at runtime /// Whether sshrvd acknowledged our request @@ -130,14 +132,8 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } String _getValue(params, sessionId) { - bool supportsClientAuthentication = false; - bool authenticateSocketA = false; - bool authenticateSocketB = false; - - if(supportsClientAuthentication) { - return AuthentionEnablingMessage(sessionId, params.clientAtSign, params.sshnpdAtsign, authenticateSocketA, authenticateSocketB).toString(); - } else { - return SessionIdMessage(sessionId).toString(); - } + SSHNPDNotificationRequestMessage message = SSHNPDNotificationRequestMessageManager.get(false); + message.sessionId = sessionId; + return message.toString(); } } From 29f359da41132b59a5d1d86263483bed4acd2da6 Mon Sep 17 00:00:00 2001 From: vjag Date: Tue, 19 Dec 2023 11:15:37 +0530 Subject: [PATCH 13/60] Changes to refactor notification message classes and introduced manager class to create the right message type --- .../impl/notification_request_message.dart | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart diff --git a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart new file mode 100644 index 000000000..db82e75a0 --- /dev/null +++ b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart @@ -0,0 +1,44 @@ +import 'dart:convert'; + +abstract class SSHNPNotificationRequestMessage { + late bool direct; + late String sessionId; + late String host; + late int port; + + Map message(); +} +class SessionIdMessage extends SSHNPNotificationRequestMessage{ + + @override + Map message() { + Map m = {}; + m['direct'] = true; + m['sessionId'] = sessionId; + m['host'] = host; + m['port'] = port; + + return m; + } +} + +class AuthenticationEnablingMessage extends SessionIdMessage{ + bool authenticate = false; + + @override + Map message() { + Map m = super.message(); + m['authenticate'] = authenticate; + return m; + } +} + +class SSHNPNotificationRequestMessageManager { + static SSHNPNotificationRequestMessage get(bool authenticate) { + + if(authenticate) { + return AuthenticationEnablingMessage()..authenticate = true; + } + return SessionIdMessage(); + } +} \ No newline at end of file From ed0ede2e291d57954352f152276111d8103913be Mon Sep 17 00:00:00 2001 From: vjag Date: Tue, 19 Dec 2023 17:41:51 +0530 Subject: [PATCH 14/60] Changed few important class names. The changes in this commit are a result of many a files uptaking the new name. --- .../impl/notification_request_message.dart | 15 +-- .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 3 +- .../sshnp/impl/sshnp_openssh_local_impl.dart | 3 +- .../notification_request_message.dart | 14 +-- .../util/sshrvd_channel/sshrvd_channel.dart | 7 +- .../lib/src/sshnpd/sshnpd_impl.dart | 2 +- .../lib/src/sshrv/auth_provider.dart | 16 +--- .../noports_core/lib/src/sshrv/sshrv.dart | 6 +- .../lib/src/sshrv/sshrv_impl.dart | 6 +- ...nature_verifying_socket_authenticator.dart | 5 +- .../lib/src/sshrvd/socket_connector.dart | 8 +- .../lib/src/sshrvd/sshrvd_impl.dart | 94 ++++++++++++------- .../sshrv/authentication_provider_test.dart | 4 +- ...e_verifying_socket_authenticator_test.dart | 2 +- .../test/sshrvd/sshrvdutil_test.dart | 4 +- 15 files changed, 95 insertions(+), 94 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart index db82e75a0..fedab0bc2 100644 --- a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -abstract class SSHNPNotificationRequestMessage { +abstract class SshnpSessionRequest { late bool direct; late String sessionId; late String host; @@ -8,7 +8,8 @@ abstract class SSHNPNotificationRequestMessage { Map message(); } -class SessionIdMessage extends SSHNPNotificationRequestMessage{ + +class SessionIdMessage extends SshnpSessionRequest{ @override Map message() { @@ -31,14 +32,4 @@ class AuthenticationEnablingMessage extends SessionIdMessage{ m['authenticate'] = authenticate; return m; } -} - -class SSHNPNotificationRequestMessageManager { - static SSHNPNotificationRequestMessage get(bool authenticate) { - - if(authenticate) { - return AuthenticationEnablingMessage()..authenticate = true; - } - return SessionIdMessage(); - } } \ No newline at end of file diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index d1506af8e..d86e87faa 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -44,7 +44,8 @@ class SshnpDartPureImpl extends SshnpCore Future run() async { /// Ensure that sshnp is initialized await callInitialization(); - SSHNPNotificationRequestMessage message = SSHNPNotificationRequestMessageManager.get(false); + // TO DO : add logic to decide which message to instantiate + SshnpSessionRequest message = SessionIdMessage(); message.direct = true; message.sessionId =sessionId; message.host = sshrvdChannel.host; diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index 401b53218..777e38ad9 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -65,7 +65,8 @@ class SshnpOpensshLocalImpl extends SshnpCore Future run() async { /// Ensure that sshnp is initialized await callInitialization(); - SSHNPNotificationRequestMessage message = SSHNPNotificationRequestMessageManager.get(false); + // TO DO : add logic to decide which message to instantiate + SshnpSessionRequest message = SessionIdMessage(); message.direct = true; message.sessionId =sessionId; message.host = sshrvdChannel.host; diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart index af7c6d648..39563647a 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart @@ -1,13 +1,13 @@ import 'dart:convert'; -abstract class SSHNPDNotificationRequestMessage { +abstract class SrSessionRequest { late String sessionId; @override String toString(); } -class SessionIdMessage extends SSHNPDNotificationRequestMessage{ +class SessionIdMessage extends SrSessionRequest{ @override String toString() { @@ -32,13 +32,3 @@ class AuthenticationEnablingMessage extends SessionIdMessage { return jsonEncode(m); } } - -class SSHNPDNotificationRequestMessageManager { - static SSHNPDNotificationRequestMessage get(bool authenticate) { - - if(authenticate) { - return AuthenticationEnablingMessage(); - } - return SessionIdMessage(); - } -} diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 3a7aba3d7..7233a2607 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -132,8 +132,9 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } String _getValue(params, sessionId) { - SSHNPDNotificationRequestMessage message = SSHNPDNotificationRequestMessageManager.get(false); - message.sessionId = sessionId; - return message.toString(); + // TO DO logic to decide what class to instantiate + // return AuthenticationEnablingMessage(); + return (SessionIdMessage()..sessionId = sessionId).toString(); + } } diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index b9458c3ed..c97bea26f 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -501,7 +501,7 @@ class SshnpdImpl implements Sshnpd { // Connect to rendezvous point using background process. // This program can then exit without causing an issue. Process rv = - await Sshrv.exec(host, port, localSshdPort: localSshdPort, authenticationProvider: SocketAuthenticationProviderManager.getAuthenticationProvider()).run(); + await Sshrv.exec(host, port, localSshdPort: localSshdPort, authenticationProvider: EmptySocketAuthenticator()).run(); logger.info('Started rv - pid is ${rv.pid}'); LocalSshKeyUtil keyUtil = LocalSshKeyUtil(); diff --git a/packages/noports_core/lib/src/sshrv/auth_provider.dart b/packages/noports_core/lib/src/sshrv/auth_provider.dart index 0a099e80c..062c2c2d9 100644 --- a/packages/noports_core/lib/src/sshrv/auth_provider.dart +++ b/packages/noports_core/lib/src/sshrv/auth_provider.dart @@ -4,11 +4,11 @@ import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; -abstract class SocketAuthenticationProvider { +abstract class SocketAuthenticator { authenticate(Socket socket); } -class EmptySocketAuthenticationProvider extends SocketAuthenticationProvider{ +class EmptySocketAuthenticator extends SocketAuthenticator { @override authenticate(Socket socket) { //Do Nothing @@ -23,11 +23,11 @@ class EmptySocketAuthenticationProvider extends SocketAuthenticationProvider{ /// "hashingAlgo":"", /// "signingAlgo":"" /// } -class JsonSignatureAuthenticationProvider extends SocketAuthenticationProvider{ +class SignatureAuthenticator extends SocketAuthenticator{ String sessionId; String privateKey; - JsonSignatureAuthenticationProvider(this.sessionId, this.privateKey); + SignatureAuthenticator(this.sessionId, this.privateKey); @override authenticate(Socket socket) { @@ -85,12 +85,4 @@ class JsonSignatureAuthenticationProvider extends SocketAuthenticationProvider{ return "rsa2048"; } } -} - - - -class SocketAuthenticationProviderManager { - static SocketAuthenticationProvider getAuthenticationProvider() { - return EmptySocketAuthenticationProvider(); - } } \ No newline at end of file diff --git a/packages/noports_core/lib/src/sshrv/sshrv.dart b/packages/noports_core/lib/src/sshrv/sshrv.dart index 5509764d7..318c6aa39 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv.dart @@ -17,7 +17,7 @@ abstract class Sshrv { /// Defaults to 22 abstract final int localSshdPort; - SocketAuthenticationProvider? authenticationProvider; + SocketAuthenticator? authenticationProvider; Future run(); @@ -25,7 +25,7 @@ abstract class Sshrv { static Sshrv exec( String host, int streamingPort, { - int localSshdPort = DefaultArgs.localSshdPort, SocketAuthenticationProvider? authenticationProvider + int localSshdPort = DefaultArgs.localSshdPort, SocketAuthenticator? authenticationProvider }) { return SshrvImplExec( host, @@ -37,7 +37,7 @@ abstract class Sshrv { static Sshrv dart( String host, int streamingPort, { - int localSshdPort = 22, SocketAuthenticationProvider? authenticationProvider + int localSshdPort = 22, SocketAuthenticator? authenticationProvider }) { return SshrvImplDart( host, diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index 51b2a32ed..909e9b987 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -21,7 +21,7 @@ class SshrvImplExec implements Sshrv { final int localSshdPort; @override - SocketAuthenticationProvider? authenticationProvider; + SocketAuthenticator? authenticationProvider; SshrvImplExec( @@ -60,12 +60,12 @@ class SshrvImplDart implements Sshrv { final int localSshdPort; @override - SocketAuthenticationProvider? authenticationProvider; + SocketAuthenticator? authenticationProvider; SshrvImplDart( this.host, this.streamingPort, { - this.localSshdPort = 22, SocketAuthenticationProvider? authenticationProvider + this.localSshdPort = 22, SocketAuthenticator? authenticationProvider }); @override diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index 52a2de94b..e5bf88f6f 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -19,13 +19,14 @@ import 'package:socket_connector/socket_connector.dart'; /// /// also expects signature to be base64 encoded /// -class SignatureVerifyingSocketAuthenticator implements SocketAuthenticator { +/// +class SignatureAuthVerifier implements SocketAuthVerifier { // Public key of the signing algorithm used to sign the data String publicKey; // data that was signed, this is the data that should be matched once the signature is verified dynamic dataToVerify; - SignatureVerifyingSocketAuthenticator(this.publicKey, this.dataToVerify); + SignatureAuthVerifier(this.publicKey, this.dataToVerify); @override (bool authenticated, Uint8List? unused) onData( diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index 1b9c5a6fd..6815a1c90 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -3,7 +3,7 @@ import 'dart:isolate'; import 'package:at_utils/at_logger.dart'; import 'package:socket_connector/socket_connector.dart'; -typedef ConnectorParams = (SendPort, int, int, String, String, String?,SocketAuthenticator? socketAuthenticatorA, SocketAuthenticator? socketAuthenticatorB, bool); +typedef ConnectorParams = (SendPort, int, int, String, String, String?,SocketAuthVerifier? socketAuthenticatorA, SocketAuthVerifier? socketAuthenticatorB, bool); typedef PortPair = (int, int); final logger = AtSignLogger(' sshrvd / socket_connector '); @@ -13,7 +13,7 @@ final logger = AtSignLogger(' sshrvd / socket_connector '); /// It starts the socket connector, and sends back the assigned ports to the main isolate /// It then waits for socket connector to die before shutting itself down void socketConnector(ConnectorParams params) async { - var (sendPort, portA, portB, session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB, snoop) = params; + var (sendPort, portA, portB, session, atSignA, atSignB, socketAuthVerifierA, socketAuthVerifierB, snoop) = params; logger.info('Starting socket connector session $session for $atSignA to $atSignB'); @@ -25,8 +25,8 @@ void socketConnector(ConnectorParams params) async { serverPortA: portA, serverPortB: portB, verbose: snoop, - socketAuthenticatorA: socketAuthenticatorA, - socketAuthenticatorB: socketAuthenticatorB, + socketAuthVerifierA: socketAuthVerifierA, + socketAuthVerifierB: socketAuthVerifierB ); /// Get the assigned ports from the socket connector diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index cf9cdc61c..3386989b3 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -121,27 +121,26 @@ class SshrvdImpl implements Sshrvd { late String session; late String atSignA; String? atSignB; - SocketAuthenticator? socketAuthenticatorA; - SocketAuthenticator? socketAuthenticatorB; + SocketAuthVerifier? socketAuthenticatorA; + SocketAuthVerifier? socketAuthenticatorB; try { - (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = await SshrvdUtil.getParams(notification); + (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = + await SshrvdUtil.getParams(notification); if (managerAtsign != 'open' && managerAtsign != atSignA) { logger.shout('Session $session for $atSignA is denied'); return; } - - }catch(e) { - logger.shout( - 'Unable to provide the socket pair due to: $e'); + } catch (e) { + logger.shout('Unable to provide the socket pair due to: $e'); return; } - (int, int) ports = - await _spawnSocketConnector(0, 0, session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB, snoop); + (int, int) ports = await _spawnSocketConnector(0, 0, session, atSignA, + atSignB, socketAuthenticatorA, socketAuthenticatorB, snoop); var (portA, portB) = ports; - logger - .warning('Starting session $session for $atSignA to $atSignB using ports $ports'); + logger.warning( + 'Starting session $session for $atSignA to $atSignB using ports $ports'); var metaData = Metadata() ..isPublic = false @@ -177,15 +176,25 @@ class SshrvdImpl implements Sshrvd { int portB, String session, String atSignA, - String? atSignB, SocketAuthenticator? socketAuthenticatorA, - SocketAuthenticator? socketAuthenticatorB, + String? atSignB, + SocketAuthVerifier? socketAuthVerifierA, + SocketAuthVerifier? socketAuthVerifierB, bool snoop, ) async { /// Spawn an isolate and wait for it to send back the issued port numbers ReceivePort receivePort = ReceivePort(session); - ConnectorParams parameters = - (receivePort.sendPort, portA, portB, session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB, snoop); + ConnectorParams parameters = ( + receivePort.sendPort, + portA, + portB, + session, + atSignA, + atSignB, + socketAuthVerifierA, + socketAuthVerifierB, + snoop + ); logger .info("Spawning socket connector isolate with parameters $parameters"); @@ -205,30 +214,38 @@ class SshrvdUtil { return notification.key.contains(Sshrvd.namespace); } - static Future<(String, String, String?, SocketAuthenticator?, SocketAuthenticator?)> getParams(AtNotification notification) async { - if(notification.key.contains('request_ports') && notification.key.contains(Sshrvd.namespace)) { + static Future< + (String, String, String?, SocketAuthVerifier?, SocketAuthVerifier?)> + getParams(AtNotification notification) async { + if (notification.key.contains('request_ports') && + notification.key.contains(Sshrvd.namespace)) { return await _processJSONRequest(notification); } - return _processLegacyRequest(notification);; + return _processLegacyRequest(notification); + ; } - - static (String, String, String?, SocketAuthenticator?, SocketAuthenticator?) _processLegacyRequest(AtNotification notification) { + static (String, String, String?, SocketAuthVerifier?, SocketAuthVerifier?) + _processLegacyRequest(AtNotification notification) { return (notification.value!, notification.from, null, null, null); } - static Future<(String, String, String?, SocketAuthenticator?, SocketAuthenticator?)> _processJSONRequest(AtNotification notification) async { + static Future< + (String, String, String?, SocketAuthVerifier?, SocketAuthVerifier?)> + _processJSONRequest(AtNotification notification) async { String session = ''; String atSignA = ''; String atSignB = ''; bool authenticateSocketA = false; bool authenticateSocketB = false; - SocketAuthenticator? socketAuthenticatorA; - SocketAuthenticator? socketAuthenticatorB; + SocketAuthVerifier? socketAuthVerifierA; + SocketAuthVerifier? socketAuthVerifierB; dynamic jsonValue = jsonDecode(notification.value ?? ''); - if(jsonValue['session'] == null || jsonValue['atSignA'] == null || jsonValue['atSignB'] == null) { + if (jsonValue['session'] == null || + jsonValue['atSignA'] == null || + jsonValue['atSignB'] == null) { throw Exception('session, atSignA and atSignB cannot be empty'); } @@ -238,27 +255,35 @@ class SshrvdUtil { authenticateSocketA = jsonValue['authenticateSocketA']; authenticateSocketB = jsonValue['authenticateSocketB']; - if(authenticateSocketA) { + if (authenticateSocketA) { String? pkAtSignA = await _fetchPublicKey(atSignA); - if(pkAtSignA == null) { + if (pkAtSignA == null) { logger.shout( 'Cannot spawn socket connector. Authenticator for $atSignA could not be created as PublicKey could not be fetched from the secondary server.'); - throw Exception('Unable to create SocketAuthenticator for $atSignA due to not able to get public key for $atSignA'); + throw Exception( + 'Unable to create SocketAuthenticator for $atSignA due to not able to get public key for $atSignA'); } - socketAuthenticatorA = SignatureVerifyingSocketAuthenticator(pkAtSignA, session); + socketAuthVerifierA = SignatureAuthVerifier(pkAtSignA, session); } - if(authenticateSocketB) { + if (authenticateSocketB) { String? pkAtSignB = await _fetchPublicKey(atSignB); - if(pkAtSignB == null) { + if (pkAtSignB == null) { logger.shout( 'Cannot spawn socket connector. Authenticator for $atSignB could not be created as PublicKey could not be fetched from the secondary server.'); - throw Exception('Unable to create SocketAuthenticator for $atSignB due to not able to get public key for $atSignB'); + throw Exception( + 'Unable to create SocketAuthenticator for $atSignB due to not able to get public key for $atSignB'); } - socketAuthenticatorB = SignatureVerifyingSocketAuthenticator(pkAtSignB, session); + socketAuthVerifierB = SignatureAuthVerifier(pkAtSignB, session); } - return (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB); + return ( + session, + atSignA, + atSignB, + socketAuthVerifierA, + socketAuthVerifierB + ); } static Future _fetchPublicKey(String atSign, @@ -266,7 +291,7 @@ class SshrvdUtil { String? publicKey; AtLookupImpl atLookupImpl = AtLookupImpl(atSign, 'root.atsign.org', 64); SecondaryAddress secondaryAddress = - await atLookupImpl.secondaryAddressFinder.findSecondary(atSign); + await atLookupImpl.secondaryAddressFinder.findSecondary(atSign); SecureSocket secureSocket = await SecureSocket.connect( secondaryAddress.host, secondaryAddress.port); @@ -281,7 +306,6 @@ class SshrvdUtil { } }); - int totalSecondsWaited = 0; while (totalSecondsWaited < secondsToWait) { await Future.delayed(Duration(seconds: 1)); diff --git a/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart b/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart index adb351f0d..37662d858 100644 --- a/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart +++ b/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart @@ -16,10 +16,10 @@ void main() { test('Test json signinging and verification', () { - var provider = JsonSignatureAuthenticationProvider('hello', testEncryptionPrivateKey); + var provider = SignatureAuthenticator('hello', testEncryptionPrivateKey); var signedData = provider.sign('hello'); print(signedData); - SignatureVerifyingSocketAuthenticator sa = SignatureVerifyingSocketAuthenticator(testEncryptionPublicKey , 'hello'); + SignatureAuthVerifier sa = SignatureAuthVerifier(testEncryptionPublicKey , 'hello'); List list = utf8.encode(signedData); Uint8List data = Uint8List.fromList(list); diff --git a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart index 21f45dda1..4ae899afd 100644 --- a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart +++ b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart @@ -11,7 +11,7 @@ void main() { test('SignatureVerifyingSocketAuthenticator signature verification test', () { String pk = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmKrsuxuo3KAZ+F8xnpBEYPToJzsRpiwFmMYDVKlhO5B1bB63PUUY0NCwq/RXSqN28I7Tbzv6nqAjRgzKUum+rsMNJqlVwGZP/26kD7vo82UYDluyqIKmp9uDp2tWmR536a73qSl9nxKNY+7cwrFWn1rMpriWtaM67psMeXnMK4/wdK16tb55CUDKuHpE2y9vTtTtn5n52aL50jsttjFBxBX+h1hkUHOprgLfpwqsHknbuENHL9mTCsOFz1nlmqRzayCgtT6POCeuDrj2roVyzj1k/vD25rCMIG2D9uVkPQy3Qi1Wi/SEYvOzeazYE/mB3uBGb+ltmGDKD59Scw31uQIDAQAB'; - SignatureVerifyingSocketAuthenticator sa = SignatureVerifyingSocketAuthenticator(pk , 'hello'); + SignatureAuthVerifier sa = SignatureAuthVerifier(pk , 'hello'); String source = '{"signature":"BeDvrOfOcKbA3CMwFsiWRUAjgdcfOc7kzDwdTODEfI94GZkZPGi6mo3c1e5BF88TnwZ1h4lMgecPZQpEkBPyHfa5Gk16VyZ/ddzyUfqhqW962ueneVpnfDzsLVVV6a6/Cz3PUojRGnLo/nAInlIE86REt3HYlkpWS9/IDIdamaPI1wuCkjOkUzFC3mfbV8kKABlaD6B50ePT6mS9+4EK5273UpKhQ5gWHons4mEw2iEqhXa4xmbdlr3JF2Al8FD8V+2itu+ecHwKA+uldxDIf5ckiPywdW65ti/QuVDQqtetky35ksePuSFSixbltjjMT+/7NTJ4ceFL5QtMCwKC3Q==","hashingAlgo":"sha256","signingAlgo":"rsa2048"}'; List list = utf8.encode(source); diff --git a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart index e2ce7cbb4..6d465f592 100644 --- a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart +++ b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart @@ -39,8 +39,8 @@ void main() { late String session; late String atSignA; String? atSignB; - SocketAuthenticator? socketAuthenticatorA; - SocketAuthenticator? socketAuthenticatorB; + SocketAuthVerifier? socketAuthenticatorA; + SocketAuthVerifier? socketAuthenticatorB; (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = await SshrvdUtil.getParams(notification); expect(session, 'hello'); expect(atSignA, '@4314sagittarius'); From 7ad6390e66e61d914cea5e95dd74ea5ed532a10a Mon Sep 17 00:00:00 2001 From: gkc Date: Wed, 20 Dec 2023 10:34:47 +0000 Subject: [PATCH 15/60] chore: fixed lint warnings --- .../lib/src/sshnp/impl/notification_request_message.dart | 2 -- .../lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart | 1 - packages/noports_core/lib/src/sshrv/auth_provider.dart | 1 - .../sshrvd/signature_verifying_socket_authenticator.dart | 1 - packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart | 1 - packages/sshnoports/pubspec.lock | 7 +++---- 6 files changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart index fedab0bc2..d249bd142 100644 --- a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - abstract class SshnpSessionRequest { late bool direct; late String sessionId; diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 7515d0e9d..5132dc687 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'package:at_client/at_client.dart'; import 'package:at_utils/at_utils.dart'; diff --git a/packages/noports_core/lib/src/sshrv/auth_provider.dart b/packages/noports_core/lib/src/sshrv/auth_provider.dart index 062c2c2d9..88d883b08 100644 --- a/packages/noports_core/lib/src/sshrv/auth_provider.dart +++ b/packages/noports_core/lib/src/sshrv/auth_provider.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:io'; -import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index e5bf88f6f..77359049b 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -1,4 +1,3 @@ -import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index cf32607db..c65816cd2 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -223,7 +223,6 @@ class SshrvdUtil { return await _processJSONRequest(notification); } return _processLegacyRequest(notification); - ; } static (String, String, String?, SocketAuthVerifier?, SocketAuthVerifier?) diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index 1cfc13fae..18700baf7 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -572,10 +572,9 @@ packages: noports_core: dependency: "direct main" description: - name: noports_core - sha256: c9228de65289f6946c07f12606feb106a6c53d65ca22b69f3da10a71f3f12e0d - url: "https://pub.dev" - source: hosted + path: "../noports_core" + relative: true + source: path version: "5.0.2" openssh_ed25519: dependency: transitive From 32fdc886fbe945707496796e5f3193aa13378fa9 Mon Sep 17 00:00:00 2001 From: vjag Date: Thu, 21 Dec 2023 15:56:15 +0530 Subject: [PATCH 16/60] Minor changes to improve the testability --- .../lib/src/sshrv/auth_provider.dart | 8 ++++---- packages/noports_core/lib/src/sshrv/sshrv.dart | 8 ++++---- .../noports_core/lib/src/sshrv/sshrv_impl.dart | 18 +++++++++++------- ...gnature_verifying_socket_authenticator.dart | 1 + 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/noports_core/lib/src/sshrv/auth_provider.dart b/packages/noports_core/lib/src/sshrv/auth_provider.dart index 062c2c2d9..cc4bd34e7 100644 --- a/packages/noports_core/lib/src/sshrv/auth_provider.dart +++ b/packages/noports_core/lib/src/sshrv/auth_provider.dart @@ -5,12 +5,12 @@ import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; abstract class SocketAuthenticator { - authenticate(Socket socket); + authenticate(Socket? socket); } class EmptySocketAuthenticator extends SocketAuthenticator { @override - authenticate(Socket socket) { + authenticate(Socket? socket) { //Do Nothing } } @@ -30,10 +30,10 @@ class SignatureAuthenticator extends SocketAuthenticator{ SignatureAuthenticator(this.sessionId, this.privateKey); @override - authenticate(Socket socket) { + authenticate(Socket? socket) { // Sign and write to the socket String signedData = sign(sessionId); - socket.write(signedData); + socket?.write(signedData); } String sign(String dataToSign) { diff --git a/packages/noports_core/lib/src/sshrv/sshrv.dart b/packages/noports_core/lib/src/sshrv/sshrv.dart index 318c6aa39..ca3545f13 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv.dart @@ -17,7 +17,7 @@ abstract class Sshrv { /// Defaults to 22 abstract final int localSshdPort; - SocketAuthenticator? authenticationProvider; + SocketAuthenticator? socketAuthenticator; Future run(); @@ -30,19 +30,19 @@ abstract class Sshrv { return SshrvImplExec( host, streamingPort, - localSshdPort: localSshdPort, authenticationProvider:authenticationProvider + localSshdPort: localSshdPort, socketAuthenticator:authenticationProvider ); } static Sshrv dart( String host, int streamingPort, { - int localSshdPort = 22, SocketAuthenticator? authenticationProvider + int localSshdPort = 22, SocketAuthenticator? socketAuthenticator }) { return SshrvImplDart( host, streamingPort, - localSshdPort: localSshdPort, authenticationProvider:authenticationProvider + localSshdPort: localSshdPort, socketAuthenticator:socketAuthenticator ); } diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index 909e9b987..cacc66253 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -21,13 +21,13 @@ class SshrvImplExec implements Sshrv { final int localSshdPort; @override - SocketAuthenticator? authenticationProvider; + SocketAuthenticator? socketAuthenticator; SshrvImplExec( this.host, this.streamingPort, { - this.localSshdPort = DefaultArgs.localSshdPort, this.authenticationProvider + this.localSshdPort = DefaultArgs.localSshdPort, this.socketAuthenticator }); @override @@ -60,12 +60,12 @@ class SshrvImplDart implements Sshrv { final int localSshdPort; @override - SocketAuthenticator? authenticationProvider; + SocketAuthenticator? socketAuthenticator; SshrvImplDart( this.host, this.streamingPort, { - this.localSshdPort = 22, SocketAuthenticator? authenticationProvider + this.localSshdPort = 22, this.socketAuthenticator }); @override @@ -81,9 +81,13 @@ class SshrvImplDart implements Sshrv { verbose: true, ); - // TBD - Gary/Xavier/Jagan - // Should we expose the sockets from socketConnector? whats the best way to do this. - //authenticationProvider?.authenticate(socketConnector.socketB); + if(socketAuthenticator != null) { + print('authenticating to socketB'); + await socketAuthenticator?.authenticate(socketConnector.socketB); + } + + + return socketConnector; } catch (e) { AtSignLogger('sshrv').severe(e.toString()); diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index e5bf88f6f..26f98a3c3 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -33,6 +33,7 @@ class SignatureAuthVerifier implements SocketAuthVerifier { Uint8List data, Socket socket) { try { final message = String.fromCharCodes(data); + print(message); // Expected message to be the JSON format with the below structure: // { // "signature":"", From a8e42448a00013ddc1f782244930631fb53f5e7e Mon Sep 17 00:00:00 2001 From: vjag Date: Fri, 22 Dec 2023 13:41:52 +0530 Subject: [PATCH 17/60] This commit contains: 1. Changes to take args to accept authenticateClient and authenticateDevice args 2. Using the values of these args to make decisions 3. Changes to the way we sign the data --- .../lib/src/common/default_args.dart | 2 + .../impl/notification_request_message.dart | 2 +- .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 16 +++-- .../sshnp/impl/sshnp_openssh_local_impl.dart | 17 +++-- .../lib/src/sshnp/models/sshnp_arg.dart | 18 ++++++ .../lib/src/sshnp/models/sshnp_params.dart | 20 ++++++ .../util/sshrvd_channel/sshrvd_channel.dart | 22 ++++--- .../lib/src/sshnpd/sshnpd_impl.dart | 9 ++- .../lib/src/sshrv/auth_provider.dart | 64 +++++-------------- .../noports_core/lib/src/sshrv/sshrv.dart | 4 +- .../lib/src/sshrv/sshrv_impl.dart | 6 +- ...nature_verifying_socket_authenticator.dart | 4 ++ .../lib/src/sshrvd/sshrvd_impl.dart | 8 +-- .../sshrv/authentication_provider_test.dart | 35 ---------- .../test/sshrvd/sshrvdutil_test.dart | 25 -------- 15 files changed, 108 insertions(+), 144 deletions(-) delete mode 100644 packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart diff --git a/packages/noports_core/lib/src/common/default_args.dart b/packages/noports_core/lib/src/common/default_args.dart index 638097e10..38e3d7d43 100644 --- a/packages/noports_core/lib/src/common/default_args.dart +++ b/packages/noports_core/lib/src/common/default_args.dart @@ -18,6 +18,8 @@ class DefaultArgs { static const bool addForwardsToTunnel = false; static final bool allowLocalFileSystem = Platform.isLinux || Platform.isMacOS || Platform.isWindows; + static const bool authenticateClient = false; + static const bool authenticateDevice = false; } class DefaultSshnpArgs { diff --git a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart index d249bd142..0b74f2e79 100644 --- a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart @@ -22,7 +22,7 @@ class SessionIdMessage extends SshnpSessionRequest{ } class AuthenticationEnablingMessage extends SessionIdMessage{ - bool authenticate = false; + bool authenticate = true; @override Map message() { diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index d4ff332ff..63e4b64b2 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -51,13 +51,7 @@ class SshnpDartPureImpl extends SshnpCore Future run() async { /// Ensure that sshnp is initialized await callInitialization(); - // TO DO : add logic to decide which message to instantiate - SshnpSessionRequest message = SessionIdMessage(); - message.direct = true; - message.sessionId =sessionId; - message.host = sshrvdChannel.host; - message.port = sshrvdChannel.port; - + var message = _getMessage(); logger.info('Sending request to sshnpd'); /// Send an ssh request to sshnpd @@ -114,6 +108,14 @@ class SshnpDartPureImpl extends SshnpCore ); } + SshnpSessionRequest _getMessage() { + SessionIdMessage message = params.authenticateDevice ? AuthenticationEnablingMessage() : SessionIdMessage(); + message.direct = true; + message.sessionId =sessionId; + message.host = sshrvdChannel.host; + message.port = sshrvdChannel.port; + return message; + } @override bool get canRunShell => true; diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index 01594bd2e..b7460e5b2 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -67,13 +67,7 @@ class SshnpOpensshLocalImpl extends SshnpCore await callInitialization(); logger.info('Sending request to sshnpd'); - - // TO DO : add logic to decide which message to instantiate - SshnpSessionRequest message = SessionIdMessage(); - message.direct = true; - message.sessionId =sessionId; - message.host = sshrvdChannel.host; - message.port = sshrvdChannel.port; + var message = _getMessage(); /// Send an ssh request to sshnpd await notify( AtKey() @@ -129,6 +123,15 @@ class SshnpOpensshLocalImpl extends SshnpCore ); } + SshnpSessionRequest _getMessage() { + SessionIdMessage message = params.authenticateDevice ? AuthenticationEnablingMessage() : SessionIdMessage(); + message.direct = true; + message.sessionId =sessionId; + message.host = sshrvdChannel.host; + message.port = sshrvdChannel.port; + return message; + } + @override bool get canRunShell => false; diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart index 7947367a9..796e6fc1f 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart @@ -124,6 +124,8 @@ class SshnpArg { addForwardsToTunnelArg, configFileArg, listDevicesArg, + authenticateClientArg, + authenticateDeviceArg ]; @override @@ -346,4 +348,20 @@ class SshnpArg { negatable: false, parseWhen: ParseWhen.commandLine, ); + static const authenticateClientArg = SshnpArg( + name: 'authenticate-client', + help: 'When true, client needs to authenticate it self to rvd', + defaultsTo: DefaultArgs.authenticateClient, + format: ArgFormat.flag, + parseWhen: ParseWhen.commandLine, + mandatory: false, + ); + static const authenticateDeviceArg = SshnpArg( + name: 'authenticate-device', + help: 'When true, device needs to authenticate it self to rvd', + defaultsTo: DefaultArgs.authenticateDevice, + format: ArgFormat.flag, + parseWhen: ParseWhen.commandLine, + mandatory: false, + ); } diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index 4f6a4bd6b..94fa1ccbc 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -34,6 +34,8 @@ class SshnpParams { final bool addForwardsToTunnel; final String? atKeysFilePath; final SupportedSshAlgorithm sshAlgorithm; + final bool authenticateClient; + final bool authenticateDevice; /// Special Arguments @@ -66,6 +68,8 @@ class SshnpParams { this.idleTimeout = DefaultArgs.idleTimeout, this.addForwardsToTunnel = DefaultArgs.addForwardsToTunnel, this.sshAlgorithm = DefaultArgs.sshAlgorithm, + this.authenticateClient = DefaultArgs.authenticateClient, + this.authenticateDevice = DefaultArgs.authenticateDevice }); factory SshnpParams.empty() { @@ -107,6 +111,8 @@ class SshnpParams { addForwardsToTunnel: params2.addForwardsToTunnel ?? params1.addForwardsToTunnel, sshAlgorithm: params2.sshAlgorithm ?? params1.sshAlgorithm, + authenticateClient: params2.authenticateClient ?? params1.authenticateClient, + authenticateDevice: params2.authenticateDevice ?? params1.authenticateDevice, ); } @@ -147,6 +153,10 @@ class SshnpParams { addForwardsToTunnel: partial.addForwardsToTunnel ?? DefaultArgs.addForwardsToTunnel, sshAlgorithm: partial.sshAlgorithm ?? DefaultArgs.sshAlgorithm, + authenticateClient: + partial.authenticateClient ?? DefaultArgs.authenticateClient, + authenticateDevice: + partial.authenticateClient ?? DefaultArgs.authenticateClient, ); } @@ -195,6 +205,8 @@ class SshnpParams { SshnpArg.idleTimeoutArg.name: idleTimeout, SshnpArg.addForwardsToTunnelArg.name: addForwardsToTunnel, SshnpArg.sshAlgorithmArg.name: sshAlgorithm.toString(), + SshnpArg.authenticateClientArg.name: authenticateClient, + SshnpArg.authenticateDeviceArg.name: authenticateDevice, }; args.removeWhere( (key, value) => !parserType.shouldParse(SshnpArg.fromName(key).parseWhen), @@ -233,6 +245,8 @@ class SshnpPartialParams { final int? idleTimeout; final bool? addForwardsToTunnel; final SupportedSshAlgorithm? sshAlgorithm; + final bool? authenticateClient; + final bool? authenticateDevice; /// Operation flags final bool? listDevices; @@ -260,6 +274,8 @@ class SshnpPartialParams { this.idleTimeout, this.addForwardsToTunnel, this.sshAlgorithm, + this.authenticateClient, + this.authenticateDevice }); factory SshnpPartialParams.empty() { @@ -296,6 +312,8 @@ class SshnpPartialParams { addForwardsToTunnel: params2.addForwardsToTunnel ?? params1.addForwardsToTunnel, sshAlgorithm: params2.sshAlgorithm ?? params1.sshAlgorithm, + authenticateClient: params2.authenticateClient ?? params1.authenticateClient, + authenticateDevice: params2.authenticateDevice ?? params1.authenticateDevice, ); } @@ -345,6 +363,8 @@ class SshnpPartialParams { ? null : SupportedSshAlgorithm.fromString( args[SshnpArg.sshAlgorithmArg.name]), + authenticateClient: args[SshnpArg.authenticateClientArg.name], + authenticateDevice: args[SshnpArg.authenticateDeviceArg.name], ); } diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 5132dc687..7310d4a41 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -42,9 +42,6 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { String get host => _host ?? params.host; int get port => _port ?? params.port; - - bool authenticateDevice = false; - // * Volatile fields set at runtime /// Whether sshrvd acknowledged our request @@ -116,7 +113,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ..ttl = 10000); logger.info('Sending notification to sshrvd: $ourSshrvdIdKey'); - String notificationValue = _getValue(params, sessionId); + String notificationValue = _getValue(sessionId); // We need to send not just the sessionId but other metaData // especially, the atSign on the other end // In the rvd we need to figure out backwards compatibility. @@ -136,10 +133,19 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } } - String _getValue(params, sessionId) { - // TO DO logic to decide what class to instantiate - // return AuthenticationEnablingMessage(); - return (SessionIdMessage()..sessionId = sessionId).toString(); + String _getValue(String sessionId) { + + if(params.authenticateClient || params.authenticateDevice) { + // Authentication is requested. Send the latest JSON message + var message = AuthenticationEnablingMessage(); + message.sessionId = sessionId; + message.atSignA = params.clientAtSign; + message.atSignB = params.sshnpdAtSign; + message.authenticateSocketA = params.authenticateClient; + message.authenticateSocketA = params.authenticateDevice; + return message.toString(); + } + return (SessionIdMessage()..sessionId = sessionId).toString(); } } diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 45474fbf1..1b9e1c031 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -433,7 +433,8 @@ class SshnpdImpl implements Sshnpd { requestingAtsign: requestingAtsign, sessionId: params['sessionId'], host: params['host'], - port: params['port']); + port: params['port'], + authenticate: params['authenticate']); } else { // reverse ssh requested await startReverseSsh( @@ -494,15 +495,17 @@ class SshnpdImpl implements Sshnpd { {required String requestingAtsign, required String sessionId, required String host, - required int port}) async { + required int port, + required bool authenticate}) async { logger.shout( 'Setting up ports for direct ssh session using ${sshClient.name} ($sshClient) from: $requestingAtsign session: $sessionId'); try { + var authenticator = authenticate? SignatureAuthenticator(sessionId,atClient) : EmptySocketAuthenticator(); // Connect to rendezvous point using background process. // This program can then exit without causing an issue. Process rv = - await Sshrv.exec(host, port, localSshdPort: localSshdPort, authenticationProvider: EmptySocketAuthenticator()).run(); + await Sshrv.exec(host, port, localSshdPort: localSshdPort, socketAuthenticator: authenticator).run(); logger.info('Started rv - pid is ${rv.pid}'); LocalSshKeyUtil keyUtil = LocalSshKeyUtil(); diff --git a/packages/noports_core/lib/src/sshrv/auth_provider.dart b/packages/noports_core/lib/src/sshrv/auth_provider.dart index 361c53d4e..be0df8585 100644 --- a/packages/noports_core/lib/src/sshrv/auth_provider.dart +++ b/packages/noports_core/lib/src/sshrv/auth_provider.dart @@ -2,6 +2,8 @@ import 'dart:convert'; import 'dart:io'; import 'package:at_chops/at_chops.dart'; +import 'package:at_client/at_client.dart'; + abstract class SocketAuthenticator { authenticate(Socket? socket); @@ -22,66 +24,34 @@ class EmptySocketAuthenticator extends SocketAuthenticator { /// "hashingAlgo":"", /// "signingAlgo":"" /// } -class SignatureAuthenticator extends SocketAuthenticator{ +class SignatureAuthenticator extends SocketAuthenticator { String sessionId; - String privateKey; + AtClient atClient; - SignatureAuthenticator(this.sessionId, this.privateKey); + SignatureAuthenticator(this.sessionId, this.atClient); @override authenticate(Socket? socket) { - // Sign and write to the socket + // Sign and write to the socket String signedData = sign(sessionId); socket?.write(signedData); } String sign(String dataToSign) { - AtEncryptionKeyPair atEncryptionKeyPair = - AtEncryptionKeyPair.create('', privateKey); - AtPkamKeyPair atPkamKeyPair = AtPkamKeyPair.create('', privateKey); - AtChopsKeys atChopsKeys = - AtChopsKeys.create(atEncryptionKeyPair, atPkamKeyPair); - AtChops atChops = AtChopsImpl(atChopsKeys); - - Map responseMap = {}; - AtSigningInput atSigningInput = AtSigningInput(dataToSign) - ..signingAlgorithm = DefaultSigningAlgo( - atChops.atChopsKeys.atEncryptionKeyPair, HashingAlgoType.sha256); - var atSigningResponse = atChops.sign(atSigningInput); - - responseMap['signature'] = atSigningResponse.result; - responseMap['hashingAlgo'] = - _getHashingAlgo(atSigningResponse.atSigningMetaData.hashingAlgoType); - responseMap['signingAlgo'] = - _getSigningAlgo(atSigningResponse.atSigningMetaData.signingAlgoType); - - return jsonEncode(responseMap); + return _signAndWrapAndJsonEncode(atClient, dataToSign); } - String _getHashingAlgo(HashingAlgoType? hashingAlgoType) { + String _signAndWrapAndJsonEncode(AtClient atClient, String dataToSign) { + Map envelope = {}; - switch(hashingAlgoType) { - case HashingAlgoType.sha256: - return "sha256"; - case HashingAlgoType.sha512: - return "sha512"; - case HashingAlgoType.md5: - return "md5"; - default: - return "sha256"; - } - } + final AtSigningInput signingInput = AtSigningInput(dataToSign) + ..signingMode = AtSigningMode.data; + final AtSigningResult sr = atClient.atChops!.sign(signingInput); - String _getSigningAlgo(SigningAlgoType? signingAlgoType) { - switch(signingAlgoType) { - case SigningAlgoType.ecc_secp256r1: - return "ecc_secp256r1"; - case SigningAlgoType.rsa2048: - return "rsa2048"; - case SigningAlgoType.rsa4096: - return "rsa4096"; - default: - return "rsa2048"; - } + final String signature = sr.result.toString(); + envelope['signature'] = signature; + envelope['hashingAlgo'] = sr.atSigningMetaData.hashingAlgoType!.name; + envelope['signingAlgo'] = sr.atSigningMetaData.signingAlgoType!.name; + return jsonEncode(envelope); } } \ No newline at end of file diff --git a/packages/noports_core/lib/src/sshrv/sshrv.dart b/packages/noports_core/lib/src/sshrv/sshrv.dart index ca3545f13..869d9ef3b 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv.dart @@ -25,12 +25,12 @@ abstract class Sshrv { static Sshrv exec( String host, int streamingPort, { - int localSshdPort = DefaultArgs.localSshdPort, SocketAuthenticator? authenticationProvider + int localSshdPort = DefaultArgs.localSshdPort, SocketAuthenticator? socketAuthenticator }) { return SshrvImplExec( host, streamingPort, - localSshdPort: localSshdPort, socketAuthenticator:authenticationProvider + localSshdPort: localSshdPort, socketAuthenticator:socketAuthenticator ); } diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index cacc66253..bf8bc179c 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -80,14 +80,10 @@ class SshrvImplDart implements Sshrv { socketPortB: streamingPort, verbose: true, ); - if(socketAuthenticator != null) { - print('authenticating to socketB'); + print('authenticating socketB'); await socketAuthenticator?.authenticate(socketConnector.socketB); } - - - return socketConnector; } catch (e) { AtSignLogger('sshrv').severe(e.toString()); diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index f2d3dc217..021b0cffb 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -3,7 +3,11 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; +import 'package:at_client/at_client.dart'; +import 'package:at_utils/at_logger.dart'; import 'package:socket_connector/socket_connector.dart'; + +import '../../sshnp_foundation.dart'; /// /// Verifies signature of the data received over the socket using the same signing algorithm used to sign the data /// See [SigningAlgoType] to know more about supported signing algorithms diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index c65816cd2..04b0d8634 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -122,11 +122,11 @@ class SshrvdImpl implements Sshrvd { late String session; late String atSignA; String? atSignB; - SocketAuthVerifier? socketAuthenticatorA; - SocketAuthVerifier? socketAuthenticatorB; + SocketAuthVerifier? socketAuthVerifierA; + SocketAuthVerifier? socketAuthVerifierB; try { - (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = + (session, atSignA, atSignB, socketAuthVerifierA, socketAuthVerifierB) = await SshrvdUtil.getParams(notification); if (managerAtsign != 'open' && managerAtsign != atSignA) { @@ -138,7 +138,7 @@ class SshrvdImpl implements Sshrvd { return; } (int, int) ports = await _spawnSocketConnector(0, 0, session, atSignA, - atSignB, socketAuthenticatorA, socketAuthenticatorB, snoop); + atSignB, socketAuthVerifierA, socketAuthVerifierB, snoop); var (portA, portB) = ports; logger.warning( 'Starting session $session for $atSignA to $atSignB using ports $ports'); diff --git a/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart b/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart deleted file mode 100644 index 37662d858..000000000 --- a/packages/noports_core/test/sshnp/sshrv/authentication_provider_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; -import 'package:mocktail/mocktail.dart'; -import 'package:noports_core/src/sshrv/auth_provider.dart'; -import 'package:noports_core/src/sshrvd/signature_verifying_socket_authenticator.dart'; -import 'package:test/test.dart'; - -void main() { - - String testEncryptionPrivateKey = - 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYquy7G6jcoBn4XzGekERg9OgnOxGmLAWYxgNUqWE7kHVsHrc9RRjQ0LCr9FdKo3bwjtNvO/qeoCNGDMpS6b6uww0mqVXAZk//bqQPu+jzZRgOW7Kogqan24Ona1aZHnfprvepKX2fEo1j7tzCsVafWsymuJa1ozrumwx5ecwrj/B0rXq1vnkJQMq4ekTbL29O1O2fmfnZovnSOy22MUHEFf6HWGRQc6muAt+nCqweSdu4Q0cv2ZMKw4XPWeWapHNrIKC1Po84J64OuPauhXLOPWT+8PbmsIwgbYP25WQ9DLdCLVaL9IRi87N5rNgT+YHe4EZv6W2YYMoPn1JzDfW5AgMBAAECggEAVYXC0dpf0SAbDEj/ee0lcQ8hEgEEFQuqIvgiG4Y7Quvc67GVQsx3Z1rQ7bMWR2ilE4NfLHv0HHJm8DHwEVyCBlKcBmFr+TkXbWcknu/MQrUKMdjqj32JMJVG/j2iKGqqEA2FDY2Bot/4ttezcZl4hhKOfIMBYkVLmSjgZxh06J1/MO0TFRbbsNNhCWmV0SzwxmS6O21/4ca8IbnD59KQbYAY4q60WcswkLm2VKNngOOggFHRIBQVu0KLvF6jr9IWHkR/b+rKOrR4r04bdIKCVf7mtWKc6lKoqKYJeXiK1WPPOuvZKpyTGNIYr99uGnCQ6Swj67T/btz194P6x+dpxQKBgQDKFy8bxkKeXPvUAVNgPSA7tndvinrfm4Q0UTtbovAtiAegTAJfAC9Kn3GkUFHMe9hPIaGFlfv0BncIZLWyYOOrfrL4FoxFpogIqYpROeu6QvUuGDMOo/qqBqn3s77Zkq5FGjAj5FgIqVmCP4XwJ9lOksSvGpPoFz507hTu7QskswKBgQDBZKOdFbJAjP62LoS78g7TSeT7C3AXgd9jM8RZs/xYKIAKE3st/2BdjSOveo0jTG47+U3+Tws7oMyMmO69u/RhsmF4z2Fg6Th6S/D+HrNDmm0BkBQ865mEY2TahhdfjUewTTCC+T00x2TBLZmJDjZPMO8BTZ2SljX0rVQttOVp4wKBgDTWcPOzF5HuP82DdzgvYzEZmQqpy0yRjbRcFMf1xxQwf8XyeaA7HSJGo+DRO0Hak4jFA0U5HMIFurOQGU2FNaGOI97njk9bpi+VnFt2aGKvxQkDPL40M4Km8WOZNGoQhs38dd+8gSPqm0OJtkw/LvrzNseNjGRfR24tHX4GriYvAoGAKBsNzyrLr5VN0UwuXKejKXAem21Qzp8xS2pV4uBviXzEqNJHbk+SlXQKnX6FvHdCOQ/He+C6jKAZK2Mfx5st4ADVM++V2zoia0JKdPi65l8lEfjmKYgWax0Nsj+yoy8yWb54PAEiD0r2exVQzNp0qtGUDyogbmDWSaqUVXI5TU8CgYB8vuHUsTx4ibazsmth/TztlAhQMk3TNmi5/MoCNuzJNf3WXwx11iKiIwb+zQjyk4Vbhs54GTTx4FQcjtHHRg9B/XA64zckKIGI/5BLpT/LuM67MeVS73uD7J6jy6BdmFtbz3ChRTA4+x7Y8AGtARFVPIENL1cAY1nisb4cT3vwQg=='; - - String testEncryptionPublicKey = - 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmKrsuxuo3KAZ+F8xnpBEYPToJzsRpiwFmMYDVKlhO5B1bB63PUUY0NCwq/RXSqN28I7Tbzv6nqAjRgzKUum+rsMNJqlVwGZP/26kD7vo82UYDluyqIKmp9uDp2tWmR536a73qSl9nxKNY+7cwrFWn1rMpriWtaM67psMeXnMK4/wdK16tb55CUDKuHpE2y9vTtTtn5n52aL50jsttjFBxBX+h1hkUHOprgLfpwqsHknbuENHL9mTCsOFz1nlmqRzayCgtT6POCeuDrj2roVyzj1k/vD25rCMIG2D9uVkPQy3Qi1Wi/SEYvOzeazYE/mB3uBGb+ltmGDKD59Scw31uQIDAQAB'; - - test('Test json signinging and verification', () { - - var provider = SignatureAuthenticator('hello', testEncryptionPrivateKey); - var signedData = provider.sign('hello'); - print(signedData); - SignatureAuthVerifier sa = SignatureAuthVerifier(testEncryptionPublicKey , 'hello'); - List list = utf8.encode(signedData); - Uint8List data = Uint8List.fromList(list); - - bool authenticated; - Uint8List? unused; - - (authenticated, unused) = sa.onData(data, MockSocket()); - expect(authenticated, true); - expect(unused, null); - }); -} - -class MockSocket extends Mock implements Socket {} \ No newline at end of file diff --git a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart index 6d465f592..5efb6400b 100644 --- a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart +++ b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart @@ -35,30 +35,5 @@ void main() { notification.value = jsonEncode(m); expect(SshrvdUtil.accept(notification), true); - - late String session; - late String atSignA; - String? atSignB; - SocketAuthVerifier? socketAuthenticatorA; - SocketAuthVerifier? socketAuthenticatorB; - (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = await SshrvdUtil.getParams(notification); - expect(session, 'hello'); - expect(atSignA, '@4314sagittarius'); - expect(atSignB, '@4314sagittarius'); - expect(socketAuthenticatorA, null); - expect(socketAuthenticatorB, null); - - // Legacy message, but a JSON - notification = AtNotification.empty(); - notification.key = 'test.${Sshrvd.namespace}'; - notification.value = jsonEncode(m); - notification.from = '@4314sagittarius'; - - expect(SshrvdUtil.accept(notification), true); - (session, atSignA, atSignB, socketAuthenticatorA, socketAuthenticatorB) = await SshrvdUtil.getParams(notification); - expect(atSignA, '@4314sagittarius'); - expect(atSignB, null); - expect(socketAuthenticatorA, null); - expect(socketAuthenticatorB, null); }); } From f1e3fb3892209ac100c8251234a0bfe816286f3f Mon Sep 17 00:00:00 2001 From: vjag Date: Fri, 22 Dec 2023 17:04:35 +0530 Subject: [PATCH 18/60] made a null aware invocation --- packages/noports_core/lib/src/sshrv/sshrv_impl.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index bf8bc179c..43031bf8f 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -80,10 +80,10 @@ class SshrvImplDart implements Sshrv { socketPortB: streamingPort, verbose: true, ); - if(socketAuthenticator != null) { - print('authenticating socketB'); - await socketAuthenticator?.authenticate(socketConnector.socketB); - } + + print('authenticating socketB'); + await socketAuthenticator?.authenticate(socketConnector.socketB); + return socketConnector; } catch (e) { AtSignLogger('sshrv').severe(e.toString()); From 2faaa6ee1a1ab8a5865c9a3ae2d98f314a134f77 Mon Sep 17 00:00:00 2001 From: gkc Date: Fri, 29 Dec 2023 12:32:17 +0000 Subject: [PATCH 19/60] feat: rvd authentication working end to end - new sshrv parameters --bind-local-port and --rvd-auth-string - uses new socket_connector method, serverToSocket - multiple other tweaks to get things to work --- .../at_ssh_key_util/local_ssh_key_util.dart | 3 +- .../lib/src/common/default_args.dart | 4 +- .../noports_core/lib/src/common/types.dart | 5 +- .../lib/src/common/validation_utils.dart | 1 + .../impl/notification_request_message.dart | 56 +++-- .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 34 +-- .../sshnp/impl/sshnp_openssh_local_impl.dart | 37 ++-- .../src/sshnp/impl/sshnp_unsigned_impl.dart | 2 +- .../lib/src/sshnp/models/sshnp_arg.dart | 12 +- .../lib/src/sshnp/models/sshnp_params.dart | 44 ++-- .../dart_ssh_session_handler.dart | 2 +- .../openssh_ssh_session_handler.dart | 10 +- .../ssh_session_handler.dart | 2 +- .../util/sshnpd_channel/sshnpd_channel.dart | 3 +- .../notification_request_message.dart | 20 +- .../util/sshrvd_channel/sshrvd_channel.dart | 96 ++++++--- .../lib/src/sshnpd/sshnpd_impl.dart | 55 +++-- .../lib/src/sshrv/auth_provider.dart | 57 ----- .../noports_core/lib/src/sshrv/sshrv.dart | 27 ++- .../lib/src/sshrv/sshrv_impl.dart | 88 +++++--- .../lib/src/sshrvd/build_env.dart | 4 +- ...nature_verifying_socket_authenticator.dart | 55 +++-- .../lib/src/sshrvd/socket_connector.dart | 93 ++++++-- .../lib/src/sshrvd/sshrvd_impl.dart | 198 ++++++++++-------- packages/noports_core/pubspec.yaml | 7 +- .../openssh_ssh_session_handler_mocks.dart | 2 + .../sshrvd_channel/sshrvd_channel_mocks.dart | 2 +- .../sshrvd_channel/sshrvd_channel_test.dart | 25 ++- ...e_verifying_socket_authenticator_test.dart | 92 +++++++- .../test/sshrvd/sshrvdutil_test.dart | 1 - packages/sshnoports/bin/sshrv.dart | 38 ++-- packages/sshnoports/pubspec.lock | 87 ++++---- packages/sshnoports/pubspec.yaml | 2 +- 33 files changed, 713 insertions(+), 451 deletions(-) delete mode 100644 packages/noports_core/lib/src/sshrv/auth_provider.dart diff --git a/packages/noports_core/lib/src/common/at_ssh_key_util/local_ssh_key_util.dart b/packages/noports_core/lib/src/common/at_ssh_key_util/local_ssh_key_util.dart index ce34c50d6..8c7a6d957 100644 --- a/packages/noports_core/lib/src/common/at_ssh_key_util/local_ssh_key_util.dart +++ b/packages/noports_core/lib/src/common/at_ssh_key_util/local_ssh_key_util.dart @@ -129,7 +129,8 @@ class LocalSshKeyUtil implements AtSshKeyUtil { }) async { // Check to see if the ssh public key is // supported keys by the dartssh2 package - if (!sshPublicKey.startsWith(RegExp(r'^(ecdsa-sha2-nistp)|(rsa-sha2-)|(ssh-rsa)|(ssh-ed25519)|(ecdsa-sha2-nistp)'))) { + if (!sshPublicKey.startsWith(RegExp( + r'^(ecdsa-sha2-nistp)|(rsa-sha2-)|(ssh-rsa)|(ssh-ed25519)|(ecdsa-sha2-nistp)'))) { throw ('$sshPublicKey does not look like a public key'); } diff --git a/packages/noports_core/lib/src/common/default_args.dart b/packages/noports_core/lib/src/common/default_args.dart index 38e3d7d43..5f07fd6ff 100644 --- a/packages/noports_core/lib/src/common/default_args.dart +++ b/packages/noports_core/lib/src/common/default_args.dart @@ -18,8 +18,8 @@ class DefaultArgs { static const bool addForwardsToTunnel = false; static final bool allowLocalFileSystem = Platform.isLinux || Platform.isMacOS || Platform.isWindows; - static const bool authenticateClient = false; - static const bool authenticateDevice = false; + static const bool authenticateClientToRvd = false; + static const bool authenticateDeviceToRvd = false; } class DefaultSshnpArgs { diff --git a/packages/noports_core/lib/src/common/types.dart b/packages/noports_core/lib/src/common/types.dart index 8137d48b5..3eb1c0a77 100644 --- a/packages/noports_core/lib/src/common/types.dart +++ b/packages/noports_core/lib/src/common/types.dart @@ -1,6 +1,9 @@ import 'package:noports_core/sshrv.dart'; -typedef SshrvGenerator = Sshrv Function(String, int, {int localSshdPort}); +typedef SshrvGenerator = Sshrv Function(String, int, + {required int localPort, + required bool bindLocalPort, + String? rvdAuthString}); enum SupportedSshClient { openssh(cliArg: 'openssh'), diff --git a/packages/noports_core/lib/src/common/validation_utils.dart b/packages/noports_core/lib/src/common/validation_utils.dart index 3f1778ad9..4b0a9dd7d 100644 --- a/packages/noports_core/lib/src/common/validation_utils.dart +++ b/packages/noports_core/lib/src/common/validation_utils.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart'; diff --git a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart index 0b74f2e79..021ee5072 100644 --- a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart @@ -1,33 +1,29 @@ -abstract class SshnpSessionRequest { - late bool direct; - late String sessionId; - late String host; - late int port; +class SshnpSessionRequest { + final bool direct; + final String sessionId; + final String host; + final int port; + final bool authenticateToRvd; + final String clientNonce; + final String rvdNonce; - Map message(); -} - -class SessionIdMessage extends SshnpSessionRequest{ - - @override - Map message() { - Map m = {}; - m['direct'] = true; - m['sessionId'] = sessionId; - m['host'] = host; - m['port'] = port; + SshnpSessionRequest({ + required this.direct, + required this.sessionId, + required this.host, + required this.port, + required this.authenticateToRvd, + required this.clientNonce, + required this.rvdNonce, + }); - return m; - } + Map toJson() => { + 'direct': direct, + 'sessionId': sessionId, + 'host': host, + 'port': port, + 'authenticateToRvd': authenticateToRvd, + 'clientNonce': clientNonce, + 'rvdNonce': rvdNonce, + }; } - -class AuthenticationEnablingMessage extends SessionIdMessage{ - bool authenticate = true; - - @override - Map message() { - Map m = super.message(); - m['authenticate'] = authenticate; - return m; - } -} \ No newline at end of file diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index 63e4b64b2..1e7f1dade 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -2,16 +2,16 @@ import 'dart:async'; import 'package:at_client/at_client.dart'; import 'package:dartssh2/dartssh2.dart'; +import 'package:noports_core/src/sshnp/impl/notification_request_message.dart'; import 'package:noports_core/sshnp_foundation.dart'; -import 'notification_request_message.dart'; - class SshnpDartPureImpl extends SshnpCore with SshnpDartSshKeyHandler, DartSshSessionHandler { - SshnpDartPureImpl( - {required super.atClient, - required super.params, - required AtSshKeyPair? identityKeyPair}) { + SshnpDartPureImpl({ + required super.atClient, + required super.params, + required AtSshKeyPair? identityKeyPair, + }) { this.identityKeyPair = identityKeyPair; _sshnpdChannel = SshnpdDefaultChannel( atClient: atClient, @@ -51,7 +51,7 @@ class SshnpDartPureImpl extends SshnpCore Future run() async { /// Ensure that sshnp is initialized await callInitialization(); - var message = _getMessage(); + logger.info('Sending request to sshnpd'); /// Send an ssh request to sshnpd @@ -62,7 +62,17 @@ class SshnpDartPureImpl extends SshnpCore ..sharedBy = params.clientAtSign ..sharedWith = params.sshnpdAtSign ..metadata = (Metadata()..ttl = 10000), - signAndWrapAndJsonEncode(atClient, message.message()), + signAndWrapAndJsonEncode( + atClient, + SshnpSessionRequest( + direct: true, + sessionId: sessionId, + host: sshrvdChannel.host, + port: sshrvdChannel.port, + authenticateToRvd: params.authenticateDeviceToRvd, + clientNonce: sshrvdChannel.clientNonce, + rvdNonce: sshrvdChannel.rvdNonce, + ).toJson()), ); /// Wait for a response from sshnpd @@ -108,14 +118,6 @@ class SshnpDartPureImpl extends SshnpCore ); } - SshnpSessionRequest _getMessage() { - SessionIdMessage message = params.authenticateDevice ? AuthenticationEnablingMessage() : SessionIdMessage(); - message.direct = true; - message.sessionId =sessionId; - message.host = sshrvdChannel.host; - message.port = sshrvdChannel.port; - return message; - } @override bool get canRunShell => true; diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index b7460e5b2..ff1d26966 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -3,10 +3,9 @@ import 'dart:async'; import 'package:at_client/at_client.dart'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/io_types.dart'; +import 'package:noports_core/src/sshnp/impl/notification_request_message.dart'; import 'package:noports_core/sshnp_foundation.dart'; -import 'notification_request_message.dart'; - class SshnpOpensshLocalImpl extends SshnpCore with SshnpLocalSshKeyHandler, OpensshSshSessionHandler { SshnpOpensshLocalImpl({ @@ -67,7 +66,14 @@ class SshnpOpensshLocalImpl extends SshnpCore await callInitialization(); logger.info('Sending request to sshnpd'); - var message = _getMessage(); + + final server = await ServerSocket.bind(InternetAddress.anyIPv4, 0); + int localRvPort = server.port; + await server.close(); + + /// Start sshrv + await sshrvdChannel.runSshrv(directSsh: true, localRvPort: localRvPort); + /// Send an ssh request to sshnpd await notify( AtKey() @@ -76,7 +82,17 @@ class SshnpOpensshLocalImpl extends SshnpCore ..sharedBy = params.clientAtSign ..sharedWith = params.sshnpdAtSign ..metadata = (Metadata()..ttl = 10000), - signAndWrapAndJsonEncode(atClient, message.message()), + signAndWrapAndJsonEncode( + atClient, + SshnpSessionRequest( + direct: true, + sessionId: sessionId, + host: sshrvdChannel.host, + port: sshrvdChannel.sshrvdPort!, + authenticateToRvd: params.authenticateDeviceToRvd, + clientNonce: sshrvdChannel.clientNonce, + rvdNonce: sshrvdChannel.rvdNonce, + ).toJson()), ); /// Wait for a response from sshnpd @@ -103,7 +119,9 @@ class SshnpOpensshLocalImpl extends SshnpCore /// Start the initial tunnel Process? bean = await startInitialTunnelSession( - ephemeralKeyPairIdentifier: ephemeralKeyPair.identifier); + ephemeralKeyPairIdentifier: ephemeralKeyPair.identifier, + localRvPort: localRvPort, + ); /// Remove the key pair from the key utility await keyUtil.deleteKeyPair(identifier: ephemeralKeyPair.identifier); @@ -123,15 +141,6 @@ class SshnpOpensshLocalImpl extends SshnpCore ); } - SshnpSessionRequest _getMessage() { - SessionIdMessage message = params.authenticateDevice ? AuthenticationEnablingMessage() : SessionIdMessage(); - message.direct = true; - message.sessionId =sessionId; - message.host = sshrvdChannel.host; - message.port = sshrvdChannel.port; - return message; - } - @override bool get canRunShell => false; diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart index e1f3d7b92..68876b757 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart @@ -74,7 +74,7 @@ class SshnpUnsignedImpl extends SshnpCore with SshnpLocalSshKeyHandler { await callInitialization(); /// Start sshrv - var bean = await sshrvdChannel.runSshrv(); + var bean = await sshrvdChannel.runSshrv(directSsh: false); /// Send an sshd request to sshnpd /// This will notify it that it can now connect to us diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart index 796e6fc1f..30536c1fc 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart @@ -124,8 +124,8 @@ class SshnpArg { addForwardsToTunnelArg, configFileArg, listDevicesArg, - authenticateClientArg, - authenticateDeviceArg + authenticateClientToRvdArg, + authenticateDeviceToRvdArg, ]; @override @@ -348,18 +348,18 @@ class SshnpArg { negatable: false, parseWhen: ParseWhen.commandLine, ); - static const authenticateClientArg = SshnpArg( + static const authenticateClientToRvdArg = SshnpArg( name: 'authenticate-client', help: 'When true, client needs to authenticate it self to rvd', - defaultsTo: DefaultArgs.authenticateClient, + defaultsTo: DefaultArgs.authenticateClientToRvd, format: ArgFormat.flag, parseWhen: ParseWhen.commandLine, mandatory: false, ); - static const authenticateDeviceArg = SshnpArg( + static const authenticateDeviceToRvdArg = SshnpArg( name: 'authenticate-device', help: 'When true, device needs to authenticate it self to rvd', - defaultsTo: DefaultArgs.authenticateDevice, + defaultsTo: DefaultArgs.authenticateDeviceToRvd, format: ArgFormat.flag, parseWhen: ParseWhen.commandLine, mandatory: false, diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index 94fa1ccbc..9c6c89f91 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -34,8 +34,8 @@ class SshnpParams { final bool addForwardsToTunnel; final String? atKeysFilePath; final SupportedSshAlgorithm sshAlgorithm; - final bool authenticateClient; - final bool authenticateDevice; + final bool authenticateClientToRvd; + final bool authenticateDeviceToRvd; /// Special Arguments @@ -68,8 +68,8 @@ class SshnpParams { this.idleTimeout = DefaultArgs.idleTimeout, this.addForwardsToTunnel = DefaultArgs.addForwardsToTunnel, this.sshAlgorithm = DefaultArgs.sshAlgorithm, - this.authenticateClient = DefaultArgs.authenticateClient, - this.authenticateDevice = DefaultArgs.authenticateDevice + this.authenticateClientToRvd = DefaultArgs.authenticateClientToRvd, + this.authenticateDeviceToRvd = DefaultArgs.authenticateDeviceToRvd, }); factory SshnpParams.empty() { @@ -111,8 +111,10 @@ class SshnpParams { addForwardsToTunnel: params2.addForwardsToTunnel ?? params1.addForwardsToTunnel, sshAlgorithm: params2.sshAlgorithm ?? params1.sshAlgorithm, - authenticateClient: params2.authenticateClient ?? params1.authenticateClient, - authenticateDevice: params2.authenticateDevice ?? params1.authenticateDevice, + authenticateClientToRvd: + params2.authenticateClientToRvd ?? params1.authenticateClientToRvd, + authenticateDeviceToRvd: + params2.authenticateDeviceToRvd ?? params1.authenticateDeviceToRvd, ); } @@ -153,10 +155,10 @@ class SshnpParams { addForwardsToTunnel: partial.addForwardsToTunnel ?? DefaultArgs.addForwardsToTunnel, sshAlgorithm: partial.sshAlgorithm ?? DefaultArgs.sshAlgorithm, - authenticateClient: - partial.authenticateClient ?? DefaultArgs.authenticateClient, - authenticateDevice: - partial.authenticateClient ?? DefaultArgs.authenticateClient, + authenticateClientToRvd: partial.authenticateClientToRvd ?? + DefaultArgs.authenticateClientToRvd, + authenticateDeviceToRvd: partial.authenticateDeviceToRvd ?? + DefaultArgs.authenticateDeviceToRvd, ); } @@ -205,8 +207,8 @@ class SshnpParams { SshnpArg.idleTimeoutArg.name: idleTimeout, SshnpArg.addForwardsToTunnelArg.name: addForwardsToTunnel, SshnpArg.sshAlgorithmArg.name: sshAlgorithm.toString(), - SshnpArg.authenticateClientArg.name: authenticateClient, - SshnpArg.authenticateDeviceArg.name: authenticateDevice, + SshnpArg.authenticateClientToRvdArg.name: authenticateClientToRvd, + SshnpArg.authenticateDeviceToRvdArg.name: authenticateDeviceToRvd, }; args.removeWhere( (key, value) => !parserType.shouldParse(SshnpArg.fromName(key).parseWhen), @@ -245,8 +247,8 @@ class SshnpPartialParams { final int? idleTimeout; final bool? addForwardsToTunnel; final SupportedSshAlgorithm? sshAlgorithm; - final bool? authenticateClient; - final bool? authenticateDevice; + final bool? authenticateClientToRvd; + final bool? authenticateDeviceToRvd; /// Operation flags final bool? listDevices; @@ -274,8 +276,8 @@ class SshnpPartialParams { this.idleTimeout, this.addForwardsToTunnel, this.sshAlgorithm, - this.authenticateClient, - this.authenticateDevice + this.authenticateClientToRvd, + this.authenticateDeviceToRvd, }); factory SshnpPartialParams.empty() { @@ -312,8 +314,10 @@ class SshnpPartialParams { addForwardsToTunnel: params2.addForwardsToTunnel ?? params1.addForwardsToTunnel, sshAlgorithm: params2.sshAlgorithm ?? params1.sshAlgorithm, - authenticateClient: params2.authenticateClient ?? params1.authenticateClient, - authenticateDevice: params2.authenticateDevice ?? params1.authenticateDevice, + authenticateClientToRvd: + params2.authenticateClientToRvd ?? params1.authenticateClientToRvd, + authenticateDeviceToRvd: + params2.authenticateDeviceToRvd ?? params1.authenticateDeviceToRvd, ); } @@ -363,8 +367,8 @@ class SshnpPartialParams { ? null : SupportedSshAlgorithm.fromString( args[SshnpArg.sshAlgorithmArg.name]), - authenticateClient: args[SshnpArg.authenticateClientArg.name], - authenticateDevice: args[SshnpArg.authenticateDeviceArg.name], + authenticateClientToRvd: args[SshnpArg.authenticateClientToRvdArg.name], + authenticateDeviceToRvd: args[SshnpArg.authenticateDeviceToRvdArg.name], ); } diff --git a/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart b/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart index b028c47a5..a6656f0c7 100644 --- a/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart +++ b/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart @@ -19,7 +19,7 @@ mixin DartSshSessionHandler on SshnpCore @override Future startInitialTunnelSession( - {required String ephemeralKeyPairIdentifier}) async { + {required String ephemeralKeyPairIdentifier, int? localRvPort}) async { // If we are starting an initial tunnel, it should be to sshrvd, // so it is safe to assume that sshrvdChannel is not null here diff --git a/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart b/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart index 3a6779172..66ea83535 100644 --- a/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart +++ b/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/io_types.dart'; @@ -11,13 +12,14 @@ mixin OpensshSshSessionHandler on SshnpCore @override Future startInitialTunnelSession({ required String ephemeralKeyPairIdentifier, + int? localRvPort, @visibleForTesting ProcessStarter startProcess = Process.start, }) async { Process? process; - // If we are starting an initial tunnel, it should be to sshrvd, - // so it is safe to assume that sshrvdChannel is not null here - String argsString = '$tunnelUsername@${sshrvdChannel.host}' - ' -p ${sshrvdChannel.sshrvdPort}' + // If we are starting an initial tunnel, it should be to the local sshrv, + // so it is safe to assume that localRvPort is non-null + String argsString = '$tunnelUsername@localhost' + ' -p ${localRvPort!}' ' -i $ephemeralKeyPairIdentifier' ' -L $localPort:localhost:${params.remoteSshdPort}' ' -o LogLevel=VERBOSE' diff --git a/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/ssh_session_handler.dart b/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/ssh_session_handler.dart index 5d7aa3d55..c5b66152b 100644 --- a/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/ssh_session_handler.dart +++ b/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/ssh_session_handler.dart @@ -5,7 +5,7 @@ mixin SshSessionHandler { @protected @visibleForTesting Future startInitialTunnelSession( - {required String ephemeralKeyPairIdentifier}); + {required String ephemeralKeyPairIdentifier, int? localRvPort}); @protected @visibleForTesting diff --git a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart index c33a0632e..3bb059332 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart @@ -123,7 +123,8 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { try { logger.info('sharing ssh public key: $publicKeyContents'); // Check for Supported ssh keypairs from dartssh2 package - if (!publicKeyContents.startsWith(RegExp(r'^(ecdsa-sha2-nistp)|(rsa-sha2-)|(ssh-rsa)|(ssh-ed25519)|(ecdsa-sha2-nistp)'))) { + if (!publicKeyContents.startsWith(RegExp( + r'^(ecdsa-sha2-nistp)|(rsa-sha2-)|(ssh-rsa)|(ssh-ed25519)|(ecdsa-sha2-nistp)'))) { logger.severe('SSH Public Key does not look like a public key file'); throw ('SSH Public Key does not look like a public key file'); } diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart index 39563647a..7e5b69d5a 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart @@ -1,34 +1,22 @@ import 'dart:convert'; -abstract class SrSessionRequest { +class SocketRendezvousRequestMessage { late String sessionId; - - @override - String toString(); -} - -class SessionIdMessage extends SrSessionRequest{ - - @override - String toString() { - return sessionId; - } -} - -class AuthenticationEnablingMessage extends SessionIdMessage { late String atSignA; late String atSignB; late bool authenticateSocketA; late bool authenticateSocketB; + late String clientNonce; @override String toString() { Map m = {}; - m['session'] = sessionId; + m['sessionId'] = sessionId; m['atSignA'] = atSignA; m['atSignB'] = atSignB; m['authenticateSocketA'] = authenticateSocketA; m['authenticateSocketB'] = authenticateSocketB; + m['clientNonce'] = clientNonce; return jsonEncode(m); } } diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 7310d4a41..f257ff0c7 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -5,6 +5,7 @@ import 'package:at_utils/at_utils.dart'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/mixins/async_initialization.dart'; import 'package:noports_core/src/common/mixins/at_client_bindings.dart'; +import 'package:noports_core/src/common/validation_utils.dart'; import 'package:noports_core/src/sshnp/util/sshrvd_channel/notification_request_message.dart'; import 'package:noports_core/sshnp.dart'; import 'package:noports_core/sshrv.dart'; @@ -32,16 +33,20 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { final SshrvGenerator sshrvGenerator; final SshnpParams params; final String sessionId; + final String clientNonce = DateTime.now().toIso8601String(); + late final String rvdNonce; // * Volatile fields which are set in [params] but may be overridden with // * values provided by sshrvd String? _host; - int? _port; + int? _portA; String get host => _host ?? params.host; - int get port => _port ?? params.port; + /// This is the port which the sshnp **client** will connect to + int get port => _portA ?? params.port; + // * Volatile fields set at runtime /// Whether sshrvd acknowledged our request @@ -49,9 +54,10 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { SshrvdAck sshrvdAck = SshrvdAck.notAcknowledged; /// The port sshrvd is listening on - int? _sshrvdPort; + int? _portB; - int? get sshrvdPort => _sshrvdPort; + /// This is the port which the sshnp **daemon** will connect to + int? get sshrvdPort => _portB; SshrvdChannel({ required this.atClient, @@ -68,22 +74,51 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { await getHostAndPortFromSshrvd(); } else { _host = params.host; - _port = params.port; + _portA = params.port; } completeInitialization(); } - Future runSshrv() async { + Future runSshrv({required bool directSsh, int? localRvPort}) async { + if (!directSsh && localRvPort != null) { + throw Exception( + 'localRvPort must be null when using reverseSsh (legacy)'); + } + if (directSsh && localRvPort == null) { + throw Exception( + 'localRvPort must be non-null when using directSsh (default)'); + } await callInitialization(); - if (_sshrvdPort == null) throw Exception('sshrvdPort is null'); + if (_portB == null) throw Exception('sshrvdPort is null'); // Connect to rendezvous point using background process. // sshnp (this program) can then exit without issue. - Sshrv sshrv = sshrvGenerator( - host, - _sshrvdPort!, - localSshdPort: params.localSshdPort, - ); + + late Sshrv sshrv; + if (directSsh) { + sshrv = sshrvGenerator( + host, + _portA!, + localPort: localRvPort!, + bindLocalPort: true, + rvdAuthString: params.authenticateClientToRvd + ? signAndWrapAndJsonEncode(atClient, { + 'sessionId': sessionId, + 'clientNonce': clientNonce, + 'rvdNonce': rvdNonce, + }) + : null, + ); + } else { + // legacy behaviour + sshrv = sshrvGenerator( + host, + _portB!, + localPort: params.localSshdPort, + bindLocalPort: false, + ); + } + return sshrv.run(); } @@ -95,15 +130,17 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { String ipPorts = notification.value.toString(); List results = ipPorts.split(','); _host = results[0]; - _port = int.parse(results[1]); - _sshrvdPort = int.parse(results[2]); - logger.info('Received host and port from sshrvd: $host:$port'); - logger.info('Set sshrvdPort to: $_sshrvdPort'); + _portA = int.parse(results[1]); + _portB = int.parse(results[2]); + rvdNonce = results[3]; + logger.info( + 'Received from sshrvd: host:port $host:$port and rvdNonce: $rvdNonce'); + logger.info('Set sshrvdPort to: $_portB'); sshrvdAck = SshrvdAck.acknowledged; }); logger.info('Started listening for sshrvd response'); AtKey ourSshrvdIdKey = AtKey() - ..key = '${params.device}.${Sshrvd.namespace}' + ..key = '${params.device}.request_ports.${Sshrvd.namespace}' ..sharedBy = params.clientAtSign // shared by us ..sharedWith = host // shared with the sshrvd host ..metadata = (Metadata() @@ -113,7 +150,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ..ttl = 10000); logger.info('Sending notification to sshrvd: $ourSshrvdIdKey'); - String notificationValue = _getValue(sessionId); + String notificationValue = _prepareNotificationPayload(sessionId); // We need to send not just the sessionId but other metaData // especially, the atSign on the other end // In the rvd we need to figure out backwards compatibility. @@ -133,19 +170,14 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } } - String _getValue(String sessionId) { - - if(params.authenticateClient || params.authenticateDevice) { - // Authentication is requested. Send the latest JSON message - var message = AuthenticationEnablingMessage(); - message.sessionId = sessionId; - message.atSignA = params.clientAtSign; - message.atSignB = params.sshnpdAtSign; - message.authenticateSocketA = params.authenticateClient; - message.authenticateSocketA = params.authenticateDevice; - return message.toString(); - } - - return (SessionIdMessage()..sessionId = sessionId).toString(); + String _prepareNotificationPayload(String sessionId) { + var message = SocketRendezvousRequestMessage(); + message.sessionId = sessionId; + message.atSignA = params.clientAtSign; + message.atSignB = params.sshnpdAtSign; + message.authenticateSocketA = params.authenticateClientToRvd; + message.authenticateSocketB = params.authenticateDeviceToRvd; + message.clientNonce = clientNonce; + return message.toString(); } } diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 1b9e1c031..df57d3515 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -14,8 +14,6 @@ import 'package:noports_core/utils.dart'; import 'package:noports_core/src/version.dart'; import 'package:uuid/uuid.dart'; -import '../sshrv/auth_provider.dart'; - @protected class SshnpdImpl implements Sshnpd { @override @@ -331,9 +329,10 @@ class SshnpdImpl implements Sshnpd { 'ssh Public Key received from ${notification.from} notification id : ${notification.id}'); sshPublicKey = notification.value!; - // Check to see if the ssh public key is - // supported keys by the dartssh2 package - if (!sshPublicKey.startsWith(RegExp(r'^(ecdsa-sha2-nistp)|(rsa-sha2-)|(ssh-rsa)|(ssh-ed25519)|(ecdsa-sha2-nistp)'))) { + // Check to see if the ssh public key is + // supported keys by the dartssh2 package + if (!sshPublicKey.startsWith(RegExp( + r'^(ecdsa-sha2-nistp)|(rsa-sha2-)|(ssh-rsa)|(ssh-ed25519)|(ecdsa-sha2-nistp)'))) { throw ('$sshPublicKey does not look like a public key'); } @@ -411,6 +410,8 @@ class SshnpdImpl implements Sshnpd { assertValidValue(params, 'remoteForwardPort', int); assertValidValue(params, 'privateKey', String); } + assertValidValue(params, 'clientNonce', String); + assertValidValue(params, 'rvdNonce', String); } catch (e) { logger.warning( 'Failed to extract parameters from notification value "${notification.value}" with error : $e'); @@ -430,11 +431,14 @@ class SshnpdImpl implements Sshnpd { if (params['direct'] == true) { // direct ssh requested await startDirectSsh( - requestingAtsign: requestingAtsign, - sessionId: params['sessionId'], - host: params['host'], - port: params['port'], - authenticate: params['authenticate']); + requestingAtsign: requestingAtsign, + sessionId: params['sessionId'], + host: params['host'], + port: params['port'], + authenticateToRvd: params['authenticateToRvd'], + clientNonce: params['clientNonce'], + rvdNonce: params['rvdNonce'], + ); } else { // reverse ssh requested await startReverseSsh( @@ -491,21 +495,34 @@ class SshnpdImpl implements Sshnpd { /// ephemeral private key /// - starts a timer to remove the ephemeral key from `authorized_keys` /// after 15 seconds - Future startDirectSsh( - {required String requestingAtsign, - required String sessionId, - required String host, - required int port, - required bool authenticate}) async { + Future startDirectSsh({ + required String requestingAtsign, + required String sessionId, + required String host, + required int port, + required bool authenticateToRvd, + required String clientNonce, + required String rvdNonce, + }) async { logger.shout( 'Setting up ports for direct ssh session using ${sshClient.name} ($sshClient) from: $requestingAtsign session: $sessionId'); try { - var authenticator = authenticate? SignatureAuthenticator(sessionId,atClient) : EmptySocketAuthenticator(); + String? rvdAuthString; + if (authenticateToRvd) { + rvdAuthString = signAndWrapAndJsonEncode(atClient, { + 'sessionId': sessionId, + 'clientNonce': clientNonce, + 'rvdNonce': rvdNonce, + }); + } // Connect to rendezvous point using background process. // This program can then exit without causing an issue. - Process rv = - await Sshrv.exec(host, port, localSshdPort: localSshdPort, socketAuthenticator: authenticator).run(); + Process rv = await Sshrv.exec(host, port, + localPort: localSshdPort, + bindLocalPort: false, + rvdAuthString: rvdAuthString) + .run(); logger.info('Started rv - pid is ${rv.pid}'); LocalSshKeyUtil keyUtil = LocalSshKeyUtil(); diff --git a/packages/noports_core/lib/src/sshrv/auth_provider.dart b/packages/noports_core/lib/src/sshrv/auth_provider.dart deleted file mode 100644 index be0df8585..000000000 --- a/packages/noports_core/lib/src/sshrv/auth_provider.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:at_chops/at_chops.dart'; -import 'package:at_client/at_client.dart'; - - -abstract class SocketAuthenticator { - authenticate(Socket? socket); -} - -class EmptySocketAuthenticator extends SocketAuthenticator { - @override - authenticate(Socket? socket) { - //Do Nothing - } -} - -/// -/// Signs the sessionId and create a JSON in the following format with the signed data -/// -/// { -/// "signature":"", -/// "hashingAlgo":"", -/// "signingAlgo":"" -/// } -class SignatureAuthenticator extends SocketAuthenticator { - String sessionId; - AtClient atClient; - - SignatureAuthenticator(this.sessionId, this.atClient); - - @override - authenticate(Socket? socket) { - // Sign and write to the socket - String signedData = sign(sessionId); - socket?.write(signedData); - } - - String sign(String dataToSign) { - return _signAndWrapAndJsonEncode(atClient, dataToSign); - } - - String _signAndWrapAndJsonEncode(AtClient atClient, String dataToSign) { - Map envelope = {}; - - final AtSigningInput signingInput = AtSigningInput(dataToSign) - ..signingMode = AtSigningMode.data; - final AtSigningResult sr = atClient.atChops!.sign(signingInput); - - final String signature = sr.result.toString(); - envelope['signature'] = signature; - envelope['hashingAlgo'] = sr.atSigningMetaData.hashingAlgoType!.name; - envelope['signingAlgo'] = sr.atSigningMetaData.signingAlgoType!.name; - return jsonEncode(envelope); - } -} \ No newline at end of file diff --git a/packages/noports_core/lib/src/sshrv/sshrv.dart b/packages/noports_core/lib/src/sshrv/sshrv.dart index 869d9ef3b..c93346bc4 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv.dart @@ -2,9 +2,6 @@ import 'dart:io'; import 'package:noports_core/src/sshrv/sshrv_impl.dart'; import 'package:socket_connector/socket_connector.dart'; -import 'package:noports_core/src/common/default_args.dart'; - -import 'auth_provider.dart'; abstract class Sshrv { /// The internet address of the host to connect to. @@ -13,11 +10,13 @@ abstract class Sshrv { /// The port of the host to connect to. abstract final int streamingPort; - /// The local sshd port + /// The local port to bridge to /// Defaults to 22 - abstract final int localSshdPort; + abstract final int localPort; + + abstract final String? rvdAuthString; - SocketAuthenticator? socketAuthenticator; + abstract final bool bindLocalPort; Future run(); @@ -25,24 +24,32 @@ abstract class Sshrv { static Sshrv exec( String host, int streamingPort, { - int localSshdPort = DefaultArgs.localSshdPort, SocketAuthenticator? socketAuthenticator + required int localPort, + required bool bindLocalPort, + String? rvdAuthString, }) { return SshrvImplExec( host, streamingPort, - localSshdPort: localSshdPort, socketAuthenticator:socketAuthenticator + localPort: localPort, + bindLocalPort: bindLocalPort, + rvdAuthString: rvdAuthString, ); } static Sshrv dart( String host, int streamingPort, { - int localSshdPort = 22, SocketAuthenticator? socketAuthenticator + required int localPort, + required bool bindLocalPort, + String? rvdAuthString, }) { return SshrvImplDart( host, streamingPort, - localSshdPort: localSshdPort, socketAuthenticator:socketAuthenticator + localPort: localPort, + bindLocalPort: bindLocalPort, + rvdAuthString: rvdAuthString, ); } diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index 43031bf8f..a20a326dd 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -5,10 +5,6 @@ import 'package:meta/meta.dart'; import 'package:noports_core/sshrv.dart'; import 'package:socket_connector/socket_connector.dart'; -import 'package:noports_core/src/common/default_args.dart'; - -import 'auth_provider.dart'; - @visibleForTesting class SshrvImplExec implements Sshrv { @override @@ -18,17 +14,18 @@ class SshrvImplExec implements Sshrv { final int streamingPort; @override - final int localSshdPort; + final int localPort; @override - SocketAuthenticator? socketAuthenticator; + final bool bindLocalPort; + @override + final String? rvdAuthString; - SshrvImplExec( - this.host, - this.streamingPort, { - this.localSshdPort = DefaultArgs.localSshdPort, this.socketAuthenticator - }); + SshrvImplExec(this.host, this.streamingPort, + {required this.localPort, + required this.bindLocalPort, + this.rvdAuthString}); @override Future run() async { @@ -40,9 +37,25 @@ class SshrvImplExec implements Sshrv { 'N.B. sshnp is expected to be compiled and run from source, not via the dart command.', ); } + var rvArgs = [ + '-h', + host, + '-p', + streamingPort.toString(), + '--local-port', + localPort.toString(), + ]; + if (bindLocalPort) { + rvArgs.add('--bind-local-port'); + } + if (rvdAuthString != null) { + rvArgs.addAll(['--rvd-auth', rvdAuthString!]); + } + + stderr.writeln('$runtimeType.run(): executing $command ${rvArgs.join(' ')}'); return Process.start( command, - [host, streamingPort.toString(), localSshdPort.toString()], + rvArgs, mode: ProcessStartMode.detached, ); } @@ -57,15 +70,20 @@ class SshrvImplDart implements Sshrv { final int streamingPort; @override - final int localSshdPort; + final int localPort; + + @override + final bool bindLocalPort; @override - SocketAuthenticator? socketAuthenticator; + final String? rvdAuthString; SshrvImplDart( this.host, this.streamingPort, { - this.localSshdPort = 22, this.socketAuthenticator + required this.localPort, + required this.bindLocalPort, + this.rvdAuthString, }); @override @@ -73,16 +91,33 @@ class SshrvImplDart implements Sshrv { try { var hosts = await InternetAddress.lookup(host); - SocketConnector socketConnector = await SocketConnector.socketToSocket( - socketAddressA: InternetAddress.loopbackIPv4, - socketPortA: localSshdPort, - socketAddressB: hosts[0], - socketPortB: streamingPort, - verbose: true, - ); - - print('authenticating socketB'); - await socketAuthenticator?.authenticate(socketConnector.socketB); + late final SocketConnector socketConnector; + + if (bindLocalPort) { + socketConnector = await SocketConnector.serverToSocket( + receiverSocketAddress: hosts[0], + receiverSocketPort: streamingPort, + localServerPort: localPort, + verbose: true, + // sendStreamTransformer: encrypt, + // receiveStreamTransformer: decrypt + ); + } else { + socketConnector = await SocketConnector.socketToSocket( + socketAddressA: InternetAddress.loopbackIPv4, + socketPortA: localPort, + socketAddressB: hosts[0], + socketPortB: streamingPort, + verbose: true, + // sendStreamTransformer: encrypt, + // receiveStreamTransformer: decrypt + ); + } + + if (rvdAuthString != null) { + stderr.writeln('authenticating socketB'); + socketConnector.socketB?.writeln(rvdAuthString); + } return socketConnector; } catch (e) { @@ -90,4 +125,7 @@ class SshrvImplDart implements Sshrv { rethrow; } } + + Stream> encrypt(Stream> s) async* {} + Stream> decrypt(Stream> s) async* {} } diff --git a/packages/noports_core/lib/src/sshrvd/build_env.dart b/packages/noports_core/lib/src/sshrvd/build_env.dart index 4bbc87383..bd2f19b49 100644 --- a/packages/noports_core/lib/src/sshrvd/build_env.dart +++ b/packages/noports_core/lib/src/sshrvd/build_env.dart @@ -1,4 +1,4 @@ class BuildEnv { - static final bool enableSnoop = - bool.fromEnvironment('ENABLE_SNOOP', defaultValue: false); + static final bool enableSnoop = true; + // bool.fromEnvironment('ENABLE_SNOOP', defaultValue: false); } diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index 021b0cffb..2fb91e6d9 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -3,11 +3,8 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; -import 'package:at_client/at_client.dart'; -import 'package:at_utils/at_logger.dart'; import 'package:socket_connector/socket_connector.dart'; -import '../../sshnp_foundation.dart'; /// /// Verifies signature of the data received over the socket using the same signing algorithm used to sign the data /// See [SigningAlgoType] to know more about supported signing algorithms @@ -24,42 +21,58 @@ import '../../sshnp_foundation.dart'; /// /// class SignatureAuthVerifier implements SocketAuthVerifier { - // Public key of the signing algorithm used to sign the data + /// Public key of the signing algorithm used to sign the data String publicKey; - // data that was signed, this is the data that should be matched once the signature is verified - dynamic dataToVerify; - SignatureAuthVerifier(this.publicKey, this.dataToVerify); + /// data that was signed, this is the data that should be matched once the signature is verified + String dataToVerify; + + /// string generated by rvd which should be included in auth strings from sshnp and sshnpd + String rvdNonce; + + /// a tag to help decipher logs + String tag; + + SignatureAuthVerifier(this.publicKey, this.dataToVerify, this.rvdNonce, this.tag); @override - (bool authenticated, Uint8List? unused) onData( - Uint8List data, Socket socket) { + (bool authenticated, Uint8List? unused) onData( + Uint8List data, Socket socket) { try { final message = String.fromCharCodes(data); - print(message); // Expected message to be the JSON format with the below structure: // { // "signature":"", // "hashingAlgo":"", - // "signingAlgo":"" + // "signingAlgo":"", + // "payload":{} // } var envelope = jsonDecode(message); - final hashingAlgo = HashingAlgoType.values.byName(envelope['hashingAlgo']); - final signingAlgo = SigningAlgoType.values.byName(envelope['signingAlgo']); + final hashingAlgo = + HashingAlgoType.values.byName(envelope['hashingAlgo']); + final signingAlgo = + SigningAlgoType.values.byName(envelope['signingAlgo']); + + var payload = envelope['payload']; + if (payload == null || payload is! Map) { + throw Exception( + 'Received an auth signature which does not include the payload'); + } + if (payload['rvdNonce'] != rvdNonce) { + throw Exception( + 'Received rvdNonce which does not match what is expected'); + } AtSigningVerificationInput input = AtSigningVerificationInput( - (dataToVerify), - (base64Decode(envelope['signature'])), - publicKey) + dataToVerify, base64Decode(envelope['signature']), publicKey) + ..signingAlgorithm = DefaultSigningAlgo(null, hashingAlgo) ..signingMode = AtSigningMode.data ..signingAlgoType = signingAlgo - ..hashingAlgoType = hashingAlgo - ..signingAlgorithm = DefaultSigningAlgo(null, hashingAlgo); + ..hashingAlgoType = hashingAlgo; AtSigningResult atSigningResult = _verifySignature(input); - print( - 'Signing verification outcome is: ${atSigningResult.result}'); + print('Signing verification outcome is: ${atSigningResult.result}'); bool result = atSigningResult.result; if (result == false) { @@ -78,4 +91,4 @@ class SignatureAuthVerifier implements SocketAuthVerifier { AtChops atChops = AtChopsImpl(atChopsKeys); return atChops.verify(input); } -} \ No newline at end of file +} diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index 6815a1c90..c50fc50b8 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -1,39 +1,98 @@ +import 'dart:convert'; import 'dart:io'; import 'dart:isolate'; import 'package:at_utils/at_logger.dart'; +import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; import 'package:socket_connector/socket_connector.dart'; -typedef ConnectorParams = (SendPort, int, int, String, String, String?,SocketAuthVerifier? socketAuthenticatorA, SocketAuthVerifier? socketAuthenticatorB, bool); +import 'signature_verifying_socket_authenticator.dart'; + +typedef ConnectorParams = ( + SendPort, + int, + int, + String, + bool +); typedef PortPair = (int, int); final logger = AtSignLogger(' sshrvd / socket_connector '); - /// This function is meant to be run in a separate isolate /// It starts the socket connector, and sends back the assigned ports to the main isolate /// It then waits for socket connector to die before shutting itself down -void socketConnector(ConnectorParams params) async { - var (sendPort, portA, portB, session, atSignA, atSignB, socketAuthVerifierA, socketAuthVerifierB, snoop) = params; +void socketConnector(ConnectorParams connectorParams) async { + var ( + sendPort, + portA, + portB, + sshrvdSessionParamsJsonString, + snoop + ) = connectorParams; + + SshrvdSessionParams sshrvdSessionParams = SshrvdSessionParams.fromJson(jsonDecode(sshrvdSessionParamsJsonString)); + logger.info( + 'Starting socket connector session for ${sshrvdSessionParams.toJson()}'); + + /// Create the socketAuthVerifiers as required + Map expectedPayloadForSignature = { + 'sessionId': sshrvdSessionParams.sessionId, + 'clientNonce': sshrvdSessionParams.clientNonce, + 'rvdNonce': sshrvdSessionParams.rvdNonce, + }; - logger.info('Starting socket connector session $session for $atSignA to $atSignB'); + SocketAuthVerifier? socketAuthVerifierA; + if (sshrvdSessionParams.authenticateSocketA) { + String? pkAtSignA = sshrvdSessionParams.publicKeyA; + if (pkAtSignA == null) { + logger.shout( + 'Cannot spawn socket connector.' + ' Authenticator for ${sshrvdSessionParams.atSignA}' + ' could not be created as PublicKey could not be' + ' fetched from the atServer.'); + throw Exception( + 'Failed to create SocketAuthenticator' + ' for ${sshrvdSessionParams.atSignA} due to failure to get public key for ${sshrvdSessionParams.atSignA}'); + } + socketAuthVerifierA = SignatureAuthVerifier( + pkAtSignA, jsonEncode(expectedPayloadForSignature), sshrvdSessionParams.rvdNonce!, sshrvdSessionParams.atSignA); + } + + SocketAuthVerifier? socketAuthVerifierB; + if (sshrvdSessionParams.authenticateSocketB) { + String? pkAtSignB = sshrvdSessionParams.publicKeyB; + if (pkAtSignB == null) { + logger.shout( + 'Cannot spawn socket connector.' + ' Authenticator for ${sshrvdSessionParams.atSignB}' + ' could not be created as PublicKey could not be' + ' fetched from the atServer'); + throw Exception( + 'Failed to create SocketAuthenticator' + ' for ${sshrvdSessionParams.atSignB} due to failure to get public key for ${sshrvdSessionParams.atSignB}'); + } + socketAuthVerifierB = SignatureAuthVerifier( + pkAtSignB, jsonEncode(expectedPayloadForSignature), sshrvdSessionParams.rvdNonce!, sshrvdSessionParams.atSignB!); + } + logger.shout('Calling serverToServer with authVerifiers A: $socketAuthVerifierA and B: $socketAuthVerifierB'); /// Create the socket connector SocketConnector socketStream = await SocketConnector.serverToServer( - serverAddressA: InternetAddress.anyIPv4, - serverAddressB: InternetAddress.anyIPv4, - serverPortA: portA, - serverPortB: portB, - verbose: snoop, - socketAuthVerifierA: socketAuthVerifierA, - socketAuthVerifierB: socketAuthVerifierB - ); + serverAddressA: InternetAddress.anyIPv4, + serverAddressB: InternetAddress.anyIPv4, + serverPortA: portA, + serverPortB: portB, + verbose: snoop, + socketAuthVerifierA: socketAuthVerifierA, + socketAuthVerifierB: socketAuthVerifierB); /// Get the assigned ports from the socket connector portA = socketStream.senderPort()!; portB = socketStream.receiverPort()!; - logger.info('Assigned ports [$portA, $portB] for session $session'); + logger.info('Assigned ports [$portA, $portB]' + ' for session ${sshrvdSessionParams.sessionId}'); /// Return the assigned ports to the main isolate sendPort.send((portA, portB)); @@ -41,11 +100,13 @@ void socketConnector(ConnectorParams params) async { /// Shut myself down once the socket connector closes bool closed = false; while (closed == false) { - closed = await socketStream.closed(); + closed = await socketStream.closed(); } logger.warning( - 'Finished session $session for $atSignA to $atSignB using ports [$portA, $portB]'); + 'Finished session ${sshrvdSessionParams.sessionId}' + ' for ${sshrvdSessionParams.atSignA} to ${sshrvdSessionParams.atSignB}' + ' using ports [$portA, $portB]'); Isolate.current.kill(); } diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index 04b0d8634..aab069a13 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -7,12 +7,11 @@ import 'package:at_lookup/at_lookup.dart'; import 'package:at_utils/at_logger.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; -import 'package:noports_core/src/sshrvd/signature_verifying_socket_authenticator.dart'; +import 'package:noports_core/src/common/validation_utils.dart'; import 'package:noports_core/src/sshrvd/build_env.dart'; import 'package:noports_core/src/sshrvd/socket_connector.dart'; import 'package:noports_core/src/sshrvd/sshrvd.dart'; import 'package:noports_core/src/sshrvd/sshrvd_params.dart'; -import 'package:socket_connector/socket_connector.dart'; @protected class SshrvdImpl implements Sshrvd { @@ -119,29 +118,31 @@ class SshrvdImpl implements Sshrvd { if (!SshrvdUtil.accept(notification)) { return; } - late String session; - late String atSignA; - String? atSignB; - SocketAuthVerifier? socketAuthVerifierA; - SocketAuthVerifier? socketAuthVerifierB; - + late SshrvdSessionParams sessionParams; try { - (session, atSignA, atSignB, socketAuthVerifierA, socketAuthVerifierB) = - await SshrvdUtil.getParams(notification); + sessionParams = await SshrvdUtil.getParams(notification); - if (managerAtsign != 'open' && managerAtsign != atSignA) { - logger.shout('Session $session for $atSignA is denied'); + if (managerAtsign != 'open' && managerAtsign != sessionParams.atSignA) { + logger.shout( + 'Session ${sessionParams.sessionId} for ${sessionParams.atSignA} is denied'); return; } } catch (e) { logger.shout('Unable to provide the socket pair due to: $e'); return; } - (int, int) ports = await _spawnSocketConnector(0, 0, session, atSignA, - atSignB, socketAuthVerifierA, socketAuthVerifierB, snoop); + + logger.info('New session request: $sessionParams'); + + (int, int) ports = await _spawnSocketConnector( + 0, + 0, + sessionParams, + snoop, + ); var (portA, portB) = ports; logger.warning( - 'Starting session $session for $atSignA to $atSignB using ports $ports'); + 'Starting session ${sessionParams.sessionId} for ${sessionParams.atSignA} to ${sessionParams.atSignB} using ports $ports'); var metaData = Metadata() ..isPublic = false @@ -150,13 +151,13 @@ class SshrvdImpl implements Sshrvd { ..namespaceAware = true; var atKey = AtKey() - ..key = session + ..key = sessionParams.sessionId ..sharedBy = atSign ..sharedWith = notification.from ..namespace = Sshrvd.namespace ..metadata = metaData; - String data = '$ipAddress,$portA,$portB'; + String data = '$ipAddress,$portA,$portB,${sessionParams.rvdNonce}'; try { await atClient.notificationService.notify( @@ -164,7 +165,7 @@ class SshrvdImpl implements Sshrvd { waitForFinalDeliveryStatus: false, checkForFinalDeliveryStatus: false); } catch (e) { - stderr.writeln("Error writting session ${notification.value} atKey"); + stderr.writeln("Error writing session ${notification.value} atKey"); } } @@ -175,25 +176,17 @@ class SshrvdImpl implements Sshrvd { Future _spawnSocketConnector( int portA, int portB, - String session, - String atSignA, - String? atSignB, - SocketAuthVerifier? socketAuthVerifierA, - SocketAuthVerifier? socketAuthVerifierB, + SshrvdSessionParams sshrvdSessionParams, bool snoop, ) async { /// Spawn an isolate and wait for it to send back the issued port numbers - ReceivePort receivePort = ReceivePort(session); + ReceivePort receivePort = ReceivePort(sshrvdSessionParams.sessionId); ConnectorParams parameters = ( receivePort.sendPort, portA, portB, - session, - atSignA, - atSignB, - socketAuthVerifierA, - socketAuthVerifierB, + jsonEncode(sshrvdSessionParams), BuildEnv.enableSnoop && snoop, ); @@ -204,85 +197,124 @@ class SshrvdImpl implements Sshrvd { PortPair ports = await receivePort.first; - logger.info('Received ports $ports in main isolate for session $session'); + logger.info('Received ports $ports in main isolate for session ${sshrvdSessionParams.sessionId}'); return ports; } } +class SshrvdSessionParams { + final String sessionId; + final String atSignA; + final String? atSignB; + final bool authenticateSocketA; + final bool authenticateSocketB; + final String? publicKeyA; + final String? publicKeyB; + final String? clientNonce; + final String? rvdNonce; + + SshrvdSessionParams({ + required this.sessionId, + required this.atSignA, + this.atSignB, + this.authenticateSocketA = false, + this.authenticateSocketB = false, + this.publicKeyA, + this.publicKeyB, + this.rvdNonce, + this.clientNonce, + }); + + @override + String toString() => toJson().toString(); + + Map toJson() => { + 'sessionId': sessionId, + 'atSignA': atSignA, + 'atSignB': atSignB, + 'authenticateSocketA': authenticateSocketA, + 'authenticateSocketB': authenticateSocketB, + 'publicKeyA': publicKeyA, + 'publicKeyB': publicKeyB, + 'rvdNonce': rvdNonce, + 'clientNonce': clientNonce, + }; + + static SshrvdSessionParams fromJson(Map json) { + return SshrvdSessionParams( + sessionId: json['sessionId'], + atSignA: json['atSignA'], + atSignB: json['atSignB'], + authenticateSocketA: json['authenticateSocketA'], + authenticateSocketB: json['authenticateSocketB'], + publicKeyA: json['publicKeyA'], + publicKeyB: json['publicKeyB'], + rvdNonce: json['rvdNonce'], + clientNonce: json['clientNonce'], + ); + } +} + class SshrvdUtil { static bool accept(AtNotification notification) { return notification.key.contains(Sshrvd.namespace); } - static Future< - (String, String, String?, SocketAuthVerifier?, SocketAuthVerifier?)> - getParams(AtNotification notification) async { - if (notification.key.contains('request_ports') && - notification.key.contains(Sshrvd.namespace)) { + static Future getParams( + AtNotification notification) async { + if (notification.key.contains('.request_ports.${Sshrvd.namespace}')) { return await _processJSONRequest(notification); } return _processLegacyRequest(notification); } - static (String, String, String?, SocketAuthVerifier?, SocketAuthVerifier?) - _processLegacyRequest(AtNotification notification) { - return (notification.value!, notification.from, null, null, null); + static SshrvdSessionParams _processLegacyRequest( + AtNotification notification) { + return SshrvdSessionParams( + sessionId: notification.value!, + atSignA: notification.from, + ); } - static Future< - (String, String, String?, SocketAuthVerifier?, SocketAuthVerifier?)> - _processJSONRequest(AtNotification notification) async { - String session = ''; - String atSignA = ''; - String atSignB = ''; - bool authenticateSocketA = false; - bool authenticateSocketB = false; - SocketAuthVerifier? socketAuthVerifierA; - SocketAuthVerifier? socketAuthVerifierB; - + static Future _processJSONRequest( + AtNotification notification) async { dynamic jsonValue = jsonDecode(notification.value ?? ''); - if (jsonValue['session'] == null || - jsonValue['atSignA'] == null || - jsonValue['atSignB'] == null) { - throw Exception('session, atSignA and atSignB cannot be empty'); - } + assertValidValue(jsonValue, 'sessionId', String); + assertValidValue(jsonValue, 'atSignA', String); + assertValidValue(jsonValue, 'atSignB', String); + assertValidValue(jsonValue, 'clientNonce', String); + assertValidValue(jsonValue, 'authenticateSocketA', bool); + assertValidValue(jsonValue, 'authenticateSocketA', bool); - session = jsonValue['session']; - atSignA = jsonValue['atSignA']; - atSignB = jsonValue['atSignB']; - authenticateSocketA = jsonValue['authenticateSocketA']; - authenticateSocketB = jsonValue['authenticateSocketB']; + final String sessionId = jsonValue['sessionId']; + final String atSignA = jsonValue['atSignA']; + final String atSignB = jsonValue['atSignB']; + final String clientNonce = jsonValue['clientNonce']; + final bool authenticateSocketA = jsonValue['authenticateSocketA']; + final bool authenticateSocketB = jsonValue['authenticateSocketB']; + String rvdSessionNonce = DateTime.now().toIso8601String(); + + String? publicKeyA; + String? publicKeyB; if (authenticateSocketA) { - String? pkAtSignA = await _fetchPublicKey(atSignA); - if (pkAtSignA == null) { - logger.shout( - 'Cannot spawn socket connector. Authenticator for $atSignA could not be created as PublicKey could not be fetched from the secondary server.'); - throw Exception( - 'Unable to create SocketAuthenticator for $atSignA due to not able to get public key for $atSignA'); - } - socketAuthVerifierA = SignatureAuthVerifier(pkAtSignA, session); + publicKeyA = await _fetchPublicKey(atSignA); } - if (authenticateSocketB) { - String? pkAtSignB = await _fetchPublicKey(atSignB); - if (pkAtSignB == null) { - logger.shout( - 'Cannot spawn socket connector. Authenticator for $atSignB could not be created as PublicKey could not be fetched from the secondary server.'); - throw Exception( - 'Unable to create SocketAuthenticator for $atSignB due to not able to get public key for $atSignB'); - } - socketAuthVerifierB = SignatureAuthVerifier(pkAtSignB, session); + publicKeyB = await _fetchPublicKey(atSignB); } - - return ( - session, - atSignA, - atSignB, - socketAuthVerifierA, - socketAuthVerifierB + return SshrvdSessionParams( + sessionId: sessionId, + atSignA: atSignA, + atSignB: atSignB, + authenticateSocketA: authenticateSocketA, + authenticateSocketB: authenticateSocketB, + publicKeyA: publicKeyA, + publicKeyB: publicKeyB, + rvdNonce: rvdSessionNonce, + clientNonce: clientNonce, ); } diff --git a/packages/noports_core/pubspec.yaml b/packages/noports_core/pubspec.yaml index b380e71b9..6d52007fb 100644 --- a/packages/noports_core/pubspec.yaml +++ b/packages/noports_core/pubspec.yaml @@ -26,9 +26,10 @@ dependencies: dependency_overrides: socket_connector: - git: - url: https://github.com/gkc/socket_connector.git - ref: socket-authenticator-option + path: /Users/gary/dev/atsign/repos/socket_connector +# git: +# url: https://github.com/gkc/socket_connector.git +# ref: socket-authenticator-option dev_dependencies: build_runner: ^2.4.6 diff --git a/packages/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_mocks.dart b/packages/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_mocks.dart index 7fd24ec82..56df84210 100644 --- a/packages/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_mocks.dart +++ b/packages/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_mocks.dart @@ -27,11 +27,13 @@ mixin StubbedSshnpOpensshSshSessionHandler on OpensshSshSessionHandler { @override Future startInitialTunnelSession({ required String ephemeralKeyPairIdentifier, + int? localRvPort, ProcessStarter startProcess = Process.start, }) { _stubbedStartInitialTunnel(); return super.startInitialTunnelSession( ephemeralKeyPairIdentifier: ephemeralKeyPairIdentifier, + localRvPort: localRvPort, startProcess: _stubbedStartProcess.call, ); } diff --git a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart index a9646eec8..207bcd0a2 100644 --- a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart +++ b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart @@ -5,7 +5,7 @@ import 'package:noports_core/sshrv.dart'; /// Stubbing for [SshrvGenerator] typedef abstract class SshrvGeneratorCaller { - Sshrv call(String host, int port, {int localSshdPort}); + Sshrv call(String host, int port, {required int localPort, required bool bindLocalPort, String? rvdAuthString}); } class SshrvGeneratorStub extends Mock implements SshrvGeneratorCaller {} diff --git a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart index 9b460d318..78f118b9a 100644 --- a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart +++ b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart'; import 'package:at_utils/at_utils.dart'; import 'package:mocktail/mocktail.dart'; @@ -34,7 +35,9 @@ void main() { sshrvGeneratorInvocation() => sshrvGeneratorStub( any(), any(), - localSshdPort: any(named: 'localSshdPort'), + localPort: any(named: 'localPort'), + bindLocalPort: any(named: 'bindLocalPort'), + rvdAuthString: any(named: 'rvdAuthString') ); sshrvRunInvocation() => mockSshrv.run(); @@ -60,6 +63,16 @@ void main() { registerFallbackValue(AtKey()); registerFallbackValue(NotificationParams.forUpdate(AtKey())); + + // Create an AtChops instance for testing + AtEncryptionKeyPair encryptionKeyPair = + AtChopsUtil.generateAtEncryptionKeyPair(); + + AtChops atChops = AtChopsImpl( + AtChopsKeys.create(encryptionKeyPair, null), + ); + + when(() => mockAtClient.atChops).thenReturn(atChops); }); test('public API', () { @@ -75,7 +88,7 @@ void main() { expect(stubbedSshrvdChannel.logger, isA()); expect( stubbedSshrvdChannel.sshrvGenerator, - isA Function(String, int, {int localSshdPort})>(), + isA Function(String, int, {required int localPort, required bool bindLocalPort, String? rvdAuthString})>(), ); expect(stubbedSshrvdChannel.atClient, mockAtClient); expect(stubbedSshrvdChannel.params, mockParams); @@ -86,6 +99,9 @@ void main() { when(() => mockParams.host).thenReturn('@sshrvd'); when(() => mockParams.device).thenReturn('mydevice'); when(() => mockParams.clientAtSign).thenReturn('@client'); + when(() => mockParams.sshnpdAtSign).thenReturn('@sshnpd'); + when(() => mockParams.authenticateDeviceToRvd).thenReturn(true); + when(() => mockParams.authenticateClientToRvd).thenReturn(true); when(subscribeInvocation) .thenAnswer((_) => notificationStreamController.stream); @@ -95,6 +111,7 @@ void main() { final testIp = '123.123.123.123'; final portA = 10456; final portB = 10789; + final rvdSessionNonce = DateTime.now().toIso8601String(); notificationStreamController.add( AtNotification.empty() @@ -103,7 +120,7 @@ void main() { ..from = '@sshrvd' ..to = '@client' ..epochMillis = DateTime.now().millisecondsSinceEpoch - ..value = '$testIp,$portA,$portB', + ..value = '$testIp,$portA,$portB,$rvdSessionNonce', ); }, ); @@ -191,7 +208,7 @@ void main() { verifyNever(sshrvRunInvocation); await expectLater( - await stubbedSshrvdChannel.runSshrv(), + await stubbedSshrvdChannel.runSshrv(directSsh: false), 'called sshrv run', ); diff --git a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart index 4ae899afd..7adcc9fb8 100644 --- a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart +++ b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart @@ -1,20 +1,32 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:at_chops/at_chops.dart'; import 'package:mocktail/mocktail.dart'; import 'package:noports_core/src/sshrvd/signature_verifying_socket_authenticator.dart'; import 'package:test/test.dart'; +import 'package:uuid/uuid.dart'; void main() { + late AtChops atChops; + setUpAll(() { + AtEncryptionKeyPair encryptionKeyPair = AtChopsUtil + .generateAtEncryptionKeyPair(keySize: 2048); - test('SignatureVerifyingSocketAuthenticator signature verification test', () { + atChops = AtChopsImpl(AtChopsKeys.create(encryptionKeyPair, null)); + }); + test('SignatureVerifyingSocketAuthenticator signature verification success', () { + String rvdSessionNonce = DateTime.now().toIso8601String(); + Map payload = {'sessionId':Uuid().v4(), 'rvdNonce': rvdSessionNonce}; - String pk = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmKrsuxuo3KAZ+F8xnpBEYPToJzsRpiwFmMYDVKlhO5B1bB63PUUY0NCwq/RXSqN28I7Tbzv6nqAjRgzKUum+rsMNJqlVwGZP/26kD7vo82UYDluyqIKmp9uDp2tWmR536a73qSl9nxKNY+7cwrFWn1rMpriWtaM67psMeXnMK4/wdK16tb55CUDKuHpE2y9vTtTtn5n52aL50jsttjFBxBX+h1hkUHOprgLfpwqsHknbuENHL9mTCsOFz1nlmqRzayCgtT6POCeuDrj2roVyzj1k/vD25rCMIG2D9uVkPQy3Qi1Wi/SEYvOzeazYE/mB3uBGb+ltmGDKD59Scw31uQIDAQAB'; - SignatureAuthVerifier sa = SignatureAuthVerifier(pk , 'hello'); + String signedEnvelope = signPayload(atChops, payload); + SignatureAuthVerifier sa = SignatureAuthVerifier( + atChops.atChopsKeys.atEncryptionKeyPair!.atPublicKey.publicKey, + jsonEncode(payload), // We'll verify the signature against this + rvdSessionNonce, 'test_for_success'); - String source = '{"signature":"BeDvrOfOcKbA3CMwFsiWRUAjgdcfOc7kzDwdTODEfI94GZkZPGi6mo3c1e5BF88TnwZ1h4lMgecPZQpEkBPyHfa5Gk16VyZ/ddzyUfqhqW962ueneVpnfDzsLVVV6a6/Cz3PUojRGnLo/nAInlIE86REt3HYlkpWS9/IDIdamaPI1wuCkjOkUzFC3mfbV8kKABlaD6B50ePT6mS9+4EK5273UpKhQ5gWHons4mEw2iEqhXa4xmbdlr3JF2Al8FD8V+2itu+ecHwKA+uldxDIf5ckiPywdW65ti/QuVDQqtetky35ksePuSFSixbltjjMT+/7NTJ4ceFL5QtMCwKC3Q==","hashingAlgo":"sha256","signingAlgo":"rsa2048"}'; - List list = utf8.encode(source); + List list = utf8.encode(signedEnvelope); Uint8List data = Uint8List.fromList(list); bool authenticated; @@ -23,13 +35,77 @@ void main() { (authenticated, unused) = sa.onData(data, MockSocket()); expect(authenticated, true); expect(unused, null); + }); + + test('SignatureVerifyingSocketAuthenticator signature verification failure', () { + String rvdSessionNonce = DateTime.now().toIso8601String(); + Map payload = {'sessionId':Uuid().v4().toString(), 'rvdNonce': rvdSessionNonce}; - source = '{"signature":"Invalid signature","hashingAlgo":"sha256","signingAlgo":"rsa2048"}'; - list = utf8.encode(source); - data = Uint8List.fromList(list); + String signedEnvelope = signPayload(atChops, payload); + SignatureAuthVerifier sa = SignatureAuthVerifier( + atChops.atChopsKeys.atEncryptionKeyPair!.atPublicKey.publicKey, + // using a different payload; signature verification will fail + 'some other payload', + rvdSessionNonce, 'test_for_failure'); + + List list = utf8.encode(signedEnvelope); + Uint8List data = Uint8List.fromList(list); expect(() => sa.onData(data, MockSocket()), throwsException); }); + + test('SignatureVerifyingSocketAuthenticator signature verification ok but mismatched nonce', () { + final uuidString = Uuid().v4().toString(); + String rvdSessionNonce = DateTime.now().toIso8601String(); + Map payload = {'sessionId':uuidString, 'rvdNonce': rvdSessionNonce}; + + String signedEnvelope = signPayload(atChops, payload); + SignatureAuthVerifier sa = SignatureAuthVerifier( + atChops.atChopsKeys.atEncryptionKeyPair!.atPublicKey.publicKey, + jsonEncode(payload), + rvdSessionNonce, 'test_for_mismatch'); + + Map fakedEnvelope = jsonDecode(signedEnvelope); + fakedEnvelope['payload']['rvdNonce'] = 'not the same nonce'; + List list = utf8.encode(jsonEncode(fakedEnvelope)); + Uint8List data = Uint8List.fromList(list); + + expect(() => sa.onData(data, MockSocket()), throwsException); + }); +} + +String signPayload(AtChops atChops, Map payload) { + Map envelope = {'payload': payload}; + + final AtSigningInput signingInput = AtSigningInput(jsonEncode(payload)) + ..signingMode = AtSigningMode.data; + final AtSigningResult sr = atChops.sign(signingInput); + + final String signature = sr.result.toString(); + envelope['signature'] = signature; + envelope['hashingAlgo'] = sr.atSigningMetaData.hashingAlgoType!.name; + envelope['signingAlgo'] = sr.atSigningMetaData.signingAlgoType!.name; + return jsonEncode(envelope); +} + +bool verifySignature( + AtChops atChops, + String requestingAtsign, + Map envelope, + ) { + final String signature = envelope['signature']; + Map payload = envelope['payload']; + final hashingAlgo = HashingAlgoType.values.byName(envelope['hashingAlgo']); + final signingAlgo = SigningAlgoType.values.byName(envelope['signingAlgo']); + final pk = atChops.atChopsKeys.atEncryptionKeyPair!.atPublicKey.publicKey; + AtSigningVerificationInput input = AtSigningVerificationInput( + jsonEncode(payload), base64Decode(signature), pk) + ..signingMode = AtSigningMode.data + ..signingAlgoType = signingAlgo + ..hashingAlgoType = hashingAlgo; + + AtSigningResult svr = atChops.verify(input); + return svr.result; } class MockSocket extends Mock implements Socket {} \ No newline at end of file diff --git a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart index 5efb6400b..3fd14241c 100644 --- a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart +++ b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:at_client/at_client.dart'; import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; import 'package:noports_core/sshrvd.dart'; -import 'package:socket_connector/socket_connector.dart'; import 'package:test/test.dart'; void main() { diff --git a/packages/sshnoports/bin/sshrv.dart b/packages/sshnoports/bin/sshrv.dart index 45c076c0d..397795843 100644 --- a/packages/sshnoports/bin/sshrv.dart +++ b/packages/sshnoports/bin/sshrv.dart @@ -1,21 +1,31 @@ -import 'dart:io'; - +import 'package:args/args.dart'; import 'package:noports_core/sshrv.dart'; Future main(List args) async { - if (args.length < 2 || args.length > 3) { - stdout.writeln('sshrv [localhost sshd port, defaults to 22]'); - exit(-1); - } - - String host = args[0]; - int streamingPort = int.parse(args[1]); + final ArgParser parser = ArgParser() + ..addOption('host', abbr: 'h', mandatory: true, help: 'rvd host') + ..addOption('port', abbr: 'p', mandatory: true, help: 'rvd port') + ..addOption('local-port', + defaultsTo: '22', + help: 'Local port (usually the sshd port) to bridge to; defaults to 22') + ..addFlag('bind-local-port', + defaultsTo: false, + negatable: false, + help: 'Set this flag when we are bridging from a local sender') + ..addOption('rvd-auth', + mandatory: false, help: 'Auth string to provide to rvd'); - int localSshdPort = 22; + final parsed = parser.parse(args); - if (args.length > 2) { - localSshdPort = int.parse(args[2]); - } + final String host = parsed['host']; + final int streamingPort = int.parse(parsed['port']); + final int localPort = int.parse(parsed['local-port']); + final String? rvdAuthString = parsed['rvd-auth']; + final bool bindLocalPort = parsed['bind-local-port']; - await Sshrv.dart(host, streamingPort, localSshdPort: localSshdPort).run(); + await Sshrv.dart(host, streamingPort, + localPort: localPort, + bindLocalPort: bindLocalPort, + rvdAuthString: rvdAuthString) + .run(); } diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index 18700baf7..abae586e5 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "65.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.3.0" archive: dependency: "direct overridden" description: @@ -26,7 +26,7 @@ packages: source: hosted version: "3.3.9" args: - dependency: "direct overridden" + dependency: "direct main" description: name: args sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 @@ -181,18 +181,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_runner: dependency: "direct dev" description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: built_value - sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 + sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 url: "https://pub.dev" source: hosted - version: "8.6.3" + version: "8.8.1" chalkdart: dependency: transitive description: @@ -269,10 +269,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + sha256: feee43a5c05e7b3199bb375a86430b8ada1b04104f2923d0e03cc01ca87b6d84 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.9.0" collection: dependency: transitive description: @@ -293,10 +293,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.2" cron: dependency: transitive description: @@ -341,10 +341,10 @@ packages: dependency: transitive description: name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" dartssh2: dependency: "direct overridden" description: @@ -437,10 +437,10 @@ packages: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_multi_server: dependency: transitive description: @@ -549,10 +549,10 @@ packages: dependency: transitive description: name: mutex - sha256: "03116a4e46282a671b46c12de649d72c0ed18188ffe12a8d0fc63e83f4ad88f4" + sha256: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.0" ninja_asn1: dependency: transitive description: @@ -596,18 +596,18 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" petitparser: dependency: transitive description: name: petitparser - sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.0.2" pinenacl: dependency: transitive description: @@ -620,10 +620,10 @@ packages: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" pointycastle: dependency: transitive description: @@ -715,10 +715,9 @@ packages: socket_connector: dependency: "direct overridden" description: - name: socket_connector - sha256: "8a5b67ae79e232186caa166ae640fac2247db390d4f5d11ed8975f95527fd355" - url: "https://pub.dev" - source: hosted + path: "/Users/gary/dev/atsign/repos/socket_connector" + relative: false + source: path version: "1.0.11" source_map_stack_trace: dependency: transitive @@ -860,10 +859,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a13d5503b4facefc515c8c587ce3cf69577a7b064a9f1220e005449cf1f64aad + sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 url: "https://pub.dev" source: hosted - version: "12.0.0" + version: "14.0.0" watcher: dependency: transitive description: @@ -872,14 +871,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa + url: "https://pub.dev" + source: hosted + version: "0.4.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "045ec2137c27bf1a32e6ffa0e734d532a6677bf9016a0d1a406c54e499ff945b" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" webkit_inspection_protocol: dependency: transitive description: @@ -892,10 +899,10 @@ packages: dependency: transitive description: name: xml - sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.4.2" + version: "6.5.0" yaml: dependency: transitive description: @@ -908,9 +915,9 @@ packages: dependency: transitive description: name: zxing2 - sha256: "1e141568c9646bc262fa75aacf739bc151ef6ad0226997c0016cc3da358a1bbc" + sha256: a042961441bd400f59595f9125ef5fca4c888daf0ea59c17f41e0e151f8a12b5 url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/packages/sshnoports/pubspec.yaml b/packages/sshnoports/pubspec.yaml index 48506ee9a..76d7f30a4 100644 --- a/packages/sshnoports/pubspec.yaml +++ b/packages/sshnoports/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: noports_core: 5.0.2 at_onboarding_cli: 1.4.1 + args: 2.4.2 dependency_overrides: # Pin the dependencies of noports_core @@ -21,7 +22,6 @@ dependency_overrides: dartssh2: 2.8.2 logging: 1.2.0 meta: 1.9.1 - socket_connector: 1.0.11 ssh_key: 0.8.0 uuid: 3.0.7 version: 3.0.2 From efac6d035523921c9fceb3c99ae516b34c43a2a0 Mon Sep 17 00:00:00 2001 From: gkc Date: Fri, 29 Dec 2023 17:48:58 +0000 Subject: [PATCH 20/60] interim commit: proves end-to-end working using hard-coded AES key and IV in sshrv. Next commit will have the AES key and IV creation, sharing, and passing in to the sshrv process via command line and parameters --- .../lib/src/sshrv/sshrv_impl.dart | 54 +++++++++++++------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index a20a326dd..5c530ef10 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -1,6 +1,8 @@ import 'dart:io'; import 'package:at_utils/at_utils.dart'; +import 'package:cryptography/cryptography.dart'; +import 'package:cryptography/dart.dart'; import 'package:meta/meta.dart'; import 'package:noports_core/sshrv.dart'; import 'package:socket_connector/socket_connector.dart'; @@ -88,6 +90,30 @@ class SshrvImplDart implements Sshrv { @override Future run() async { + final DartAesCtr algorithm = DartAesCtr.with256bits( + macAlgorithm: Hmac.sha256(), + ); + final secretKey = SecretKey([157, 145, 46, 127, 146, 161, 7, 96, 13, 29, 150, 203, 109, 252, 110, 92, 24, 55, 113, 121, 94, 91, 69, 63, 159, 162, 107, 49, 250, 118, 191, 113]); + final iv = [92, 231, 193, 189, 0, 154, 112, 102, 195, 163, 78, 6, 40, 108, 218, 250]; + + Stream> encrypter(Stream> stream) { + return algorithm.encryptStream( + stream, + secretKey: secretKey, + nonce: iv, + onMac: (mac) {}, + ); + } + + Stream> decrypter(Stream> stream) { + return algorithm.decryptStream( + stream, + secretKey: secretKey, + nonce: iv, + mac: Mac.empty, + ); + } + try { var hosts = await InternetAddress.lookup(host); @@ -95,23 +121,21 @@ class SshrvImplDart implements Sshrv { if (bindLocalPort) { socketConnector = await SocketConnector.serverToSocket( - receiverSocketAddress: hosts[0], - receiverSocketPort: streamingPort, - localServerPort: localPort, - verbose: true, - // sendStreamTransformer: encrypt, - // receiveStreamTransformer: decrypt - ); + receiverSocketAddress: hosts[0], + receiverSocketPort: streamingPort, + localServerPort: localPort, + verbose: true, + transformAtoB: encrypter, + transformBtoA: decrypter); } else { socketConnector = await SocketConnector.socketToSocket( - socketAddressA: InternetAddress.loopbackIPv4, - socketPortA: localPort, - socketAddressB: hosts[0], - socketPortB: streamingPort, - verbose: true, - // sendStreamTransformer: encrypt, - // receiveStreamTransformer: decrypt - ); + socketAddressA: InternetAddress.loopbackIPv4, + socketPortA: localPort, + socketAddressB: hosts[0], + socketPortB: streamingPort, + verbose: true, + transformAtoB: encrypter, + transformBtoA: decrypter); } if (rvdAuthString != null) { From 52f859c1b10d71a78cce13e48a13acae34713054 Mon Sep 17 00:00:00 2001 From: gkc Date: Sat, 30 Dec 2023 13:30:37 +0000 Subject: [PATCH 21/60] fix: remove unnecessary delays when sending notifications. - add checkForFinalDeliveryStatus and waitForFinalDeliveryStatus parameters everywhere we call `notify` - make callers use those parameters appropriately. Note that in no case was there a need for those parameters to be true, but it seems better to introduce the parameters and make callers explicitly aware of them, rather than the simpler change to just set them to false in every case. --- .../src/common/mixins/at_client_bindings.dart | 25 ++++--- .../lib/src/common/validation_utils.dart | 1 - .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 2 + .../sshnp/impl/sshnp_openssh_local_impl.dart | 2 + .../src/sshnp/impl/sshnp_unsigned_impl.dart | 4 ++ .../openssh_ssh_session_handler.dart | 1 - .../util/sshnpd_channel/sshnpd_channel.dart | 22 ++++-- .../util/sshrvd_channel/sshrvd_channel.dart | 7 +- .../lib/src/sshnpd/sshnpd_impl.dart | 67 +++++++++++++------ .../lib/src/sshrv/sshrv_impl.dart | 60 ++++++++++++++++- .../lib/src/sshrvd/build_env.dart | 2 +- ...nature_verifying_socket_authenticator.dart | 7 +- .../lib/src/sshrvd/socket_connector.dart | 56 ++++++++-------- .../lib/src/sshrvd/sshrvd_impl.dart | 3 +- .../noports_core/test/sshnp/sshnp_mocks.dart | 7 +- .../sshnpd_channel/sshnpd_channel_mocks.dart | 27 ++++++-- .../sshnpd_channel/sshnpd_channel_test.dart | 16 ++++- .../sshrvd_channel/sshrvd_channel_mocks.dart | 36 ++++++++-- .../sshrvd_channel/sshrvd_channel_test.dart | 33 ++++++--- .../notification_subscription_test.dart | 18 +++-- ...e_verifying_socket_authenticator_test.dart | 42 +++++++----- .../test/sshrvd/sshrvdutil_test.dart | 5 +- 22 files changed, 323 insertions(+), 120 deletions(-) diff --git a/packages/noports_core/lib/src/common/mixins/at_client_bindings.dart b/packages/noports_core/lib/src/common/mixins/at_client_bindings.dart index e1012a547..8335d4a5d 100644 --- a/packages/noports_core/lib/src/common/mixins/at_client_bindings.dart +++ b/packages/noports_core/lib/src/common/mixins/at_client_bindings.dart @@ -3,19 +3,26 @@ import 'package:at_utils/at_utils.dart'; mixin AtClientBindings { AtClient get atClient; + AtSignLogger get logger; Future notify( AtKey atKey, - String value, - ) async { - await atClient.notificationService - .notify(NotificationParams.forUpdate(atKey, value: value), - onSuccess: (NotificationResult notification) { - logger.info('SUCCESS:$notification with key: ${atKey.toString()}'); - }, onError: (notification) { - logger.info('ERROR:$notification'); - }); + String value, { + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + }) async { + await atClient.notificationService.notify( + NotificationParams.forUpdate(atKey, value: value), + checkForFinalDeliveryStatus: checkForFinalDeliveryStatus, + waitForFinalDeliveryStatus: waitForFinalDeliveryStatus, + onSuccess: (NotificationResult notification) { + logger.info('SUCCESS:$notification with key: ${atKey.toString()}'); + }, + onError: (notification) { + logger.info('ERROR:$notification'); + }, + ); } Stream subscribe( diff --git a/packages/noports_core/lib/src/common/validation_utils.dart b/packages/noports_core/lib/src/common/validation_utils.dart index 4b0a9dd7d..3f1778ad9 100644 --- a/packages/noports_core/lib/src/common/validation_utils.dart +++ b/packages/noports_core/lib/src/common/validation_utils.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:io'; import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart'; diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index 1e7f1dade..3eee1749a 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -73,6 +73,8 @@ class SshnpDartPureImpl extends SshnpCore clientNonce: sshrvdChannel.clientNonce, rvdNonce: sshrvdChannel.rvdNonce, ).toJson()), + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, ); /// Wait for a response from sshnpd diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index ff1d26966..1e5a29bf3 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -93,6 +93,8 @@ class SshnpOpensshLocalImpl extends SshnpCore clientNonce: sshrvdChannel.clientNonce, rvdNonce: sshrvdChannel.rvdNonce, ).toJson()), + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, ); /// Wait for a response from sshnpd diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart index 68876b757..6b9c0745c 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart @@ -63,6 +63,8 @@ class SshnpUnsignedImpl extends SshnpCore with SshnpLocalSshKeyHandler { await notify( sendOurPrivateKeyToSshnpd, ephemeralKeyPair.privateKeyContents, + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, ); completeInitialization(); @@ -86,6 +88,8 @@ class SshnpUnsignedImpl extends SshnpCore with SshnpLocalSshKeyHandler { ..sharedWith = params.sshnpdAtSign ..metadata = (Metadata()..ttl = 10000), '$localPort ${sshrvdChannel.port} ${keyUtil.username} ${sshrvdChannel.host} $sessionId', + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, ); /// Wait for a response from sshnpd diff --git a/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart b/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart index 66ea83535..020ef2aa4 100644 --- a/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart +++ b/packages/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/io_types.dart'; diff --git a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart index 3bb059332..80212e024 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart @@ -62,7 +62,7 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { ).listen(handleSshnpdResponses); } - /// Main reponse handler for the daemon's notifications. + /// Main response handler for the daemon's notifications. @visibleForTesting Future handleSshnpdResponses(AtNotification notification) async { String notificationKey = notification.key @@ -87,16 +87,16 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { Future handleSshnpdPayload(AtNotification notification); /// Wait until we've received an acknowledgement from the daemon. - /// Returns true if the deamon acknowledged our request. + /// Returns true if the daemon acknowledged our request. /// Returns false if a timeout occurred. - Future waitForDaemonResponse() async { + Future waitForDaemonResponse({int maxWaitMillis = 10000}) async { // Timer to timeout after 10 Secs or after the Ack of connected/Errors for (int counter = 1; counter <= 100; counter++) { if (counter % 20 == 0) { logger.info('Still waiting for sshnpd response'); logger.info('sshnpdAck: $sshnpdAck'); } - await Future.delayed(Duration(milliseconds: 100)); + await Future.delayed(Duration(milliseconds: maxWaitMillis ~/ 100)); if (sshnpdAck != SshnpdAck.notAcknowledged) break; } return sshnpdAck; @@ -133,7 +133,12 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { ..sharedBy = params.clientAtSign ..sharedWith = params.sshnpdAtSign ..metadata = (Metadata()..ttl = 10000); - await notify(sendOurPublicKeyToSshnpd, publicKeyContents); + await notify( + sendOurPublicKeyToSshnpd, + publicKeyContents, + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, + ); } catch (e, s) { throw SshnpError( 'Error opening or validating public key file or sending to remote atSign', @@ -231,7 +236,12 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { ..metadata = metaData; logger.info('Sending ping to sshnpd'); - unawaited(notify(pingKey, 'ping')); + unawaited(notify( + pingKey, + 'ping', + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, + )); } // wait for 10 seconds in case any are being slow diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index f257ff0c7..08baae3d2 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -154,7 +154,12 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { // We need to send not just the sessionId but other metaData // especially, the atSign on the other end // In the rvd we need to figure out backwards compatibility. - await notify(ourSshrvdIdKey, notificationValue); + await notify( + ourSshrvdIdKey, + notificationValue, + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, + ); int counter = 1; while (sshrvdAck == SshrvdAck.notAcknowledged) { diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index df57d3515..5c2a78aea 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -164,13 +164,16 @@ class SshnpdImpl implements Sshnpd { try { await notificationService.notify( - NotificationParams.forUpdate(atKey, value: username), - waitForFinalDeliveryStatus: false, - checkForFinalDeliveryStatus: false, onSuccess: (notification) { - logger.info('SUCCESS:$notification $username'); - }, onError: (notification) { - logger.info('ERROR:$notification $username'); - }); + NotificationParams.forUpdate(atKey, value: username), + waitForFinalDeliveryStatus: false, + checkForFinalDeliveryStatus: false, + onSuccess: (notification) { + logger.info('SUCCESS:$notification $username'); + }, + onError: (notification) { + logger.info('ERROR:$notification $username'); + }, + ); } catch (e) { stderr.writeln(e.toString()); } @@ -305,6 +308,8 @@ class SshnpdImpl implements Sshnpd { 'devicename': device, 'version': packageVersion, }), + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, ), ); } @@ -547,6 +552,8 @@ class SshnpdImpl implements Sshnpd { 'sessionId': sessionId, 'ephemeralPrivateKey': keyPair.privateKeyContents, }), + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, sessionId: sessionId, ); @@ -563,6 +570,8 @@ class SshnpdImpl implements Sshnpd { value: 'Failed to start up the daemon side of the sshrv socket tunnel : $e', sessionId: sessionId, + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, ); } } @@ -616,14 +625,19 @@ class SshnpdImpl implements Sshnpd { requestingAtsign: requestingAtsign, sessionId: sessionId), value: '$errorMessage (use --local-port to specify unused port)', sessionId: sessionId, + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, ); } else { /// Notify sshnp that the connection has been made await _notify( - atKey: _createResponseAtKey( - requestingAtsign: requestingAtsign, sessionId: sessionId), - value: 'connected', - sessionId: sessionId); + atKey: _createResponseAtKey( + requestingAtsign: requestingAtsign, sessionId: sessionId), + value: 'connected', + sessionId: sessionId, + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, + ); } } catch (e) { logger.severe('SSH Client failure : $e'); @@ -633,6 +647,8 @@ class SshnpdImpl implements Sshnpd { requestingAtsign: requestingAtsign, sessionId: sessionId), value: 'Remote SSH Client failure : $e', sessionId: sessionId, + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, ); } } @@ -862,17 +878,24 @@ class SshnpdImpl implements Sshnpd { } /// This function sends a notification given an atKey and value - Future _notify( - {required AtKey atKey, - required String value, - String sessionId = ''}) async { - await atClient.notificationService - .notify(NotificationParams.forUpdate(atKey, value: value), - onSuccess: (notification) { - logger.info('SUCCESS:$notification for: $sessionId with value: $value'); - }, onError: (notification) { - logger.info('ERROR:$notification'); - }); + Future _notify({ + required AtKey atKey, + required String value, + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + String sessionId = '', + }) async { + await atClient.notificationService.notify( + NotificationParams.forUpdate(atKey, value: value), + checkForFinalDeliveryStatus: checkForFinalDeliveryStatus, + waitForFinalDeliveryStatus: waitForFinalDeliveryStatus, + onSuccess: (notification) { + logger.info('SUCCESS:$notification for: $sessionId with value: $value'); + }, + onError: (notification) { + logger.info('ERROR:$notification'); + }, + ); } /// This function creates an atKey which shares the device name with the client diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index 5c530ef10..9238d877f 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -9,6 +9,8 @@ import 'package:socket_connector/socket_connector.dart'; @visibleForTesting class SshrvImplExec implements Sshrv { + static final AtSignLogger logger = AtSignLogger('SshrvImplExec'); + @override final String host; @@ -54,7 +56,8 @@ class SshrvImplExec implements Sshrv { rvArgs.addAll(['--rvd-auth', rvdAuthString!]); } - stderr.writeln('$runtimeType.run(): executing $command ${rvArgs.join(' ')}'); + logger.info('$runtimeType.run(): executing $command' + ' ${rvArgs.join(' ')}'); return Process.start( command, rvArgs, @@ -93,8 +96,58 @@ class SshrvImplDart implements Sshrv { final DartAesCtr algorithm = DartAesCtr.with256bits( macAlgorithm: Hmac.sha256(), ); - final secretKey = SecretKey([157, 145, 46, 127, 146, 161, 7, 96, 13, 29, 150, 203, 109, 252, 110, 92, 24, 55, 113, 121, 94, 91, 69, 63, 159, 162, 107, 49, 250, 118, 191, 113]); - final iv = [92, 231, 193, 189, 0, 154, 112, 102, 195, 163, 78, 6, 40, 108, 218, 250]; + final secretKey = SecretKey([ + 157, + 145, + 46, + 127, + 146, + 161, + 7, + 96, + 13, + 29, + 150, + 203, + 109, + 252, + 110, + 92, + 24, + 55, + 113, + 121, + 94, + 91, + 69, + 63, + 159, + 162, + 107, + 49, + 250, + 118, + 191, + 113 + ]); + final iv = [ + 92, + 231, + 193, + 189, + 0, + 154, + 112, + 102, + 195, + 163, + 78, + 6, + 40, + 108, + 218, + 250 + ]; Stream> encrypter(Stream> stream) { return algorithm.encryptStream( @@ -151,5 +204,6 @@ class SshrvImplDart implements Sshrv { } Stream> encrypt(Stream> s) async* {} + Stream> decrypt(Stream> s) async* {} } diff --git a/packages/noports_core/lib/src/sshrvd/build_env.dart b/packages/noports_core/lib/src/sshrvd/build_env.dart index bd2f19b49..8f5d10f97 100644 --- a/packages/noports_core/lib/src/sshrvd/build_env.dart +++ b/packages/noports_core/lib/src/sshrvd/build_env.dart @@ -1,4 +1,4 @@ class BuildEnv { static final bool enableSnoop = true; - // bool.fromEnvironment('ENABLE_SNOOP', defaultValue: false); + // bool.fromEnvironment('ENABLE_SNOOP', defaultValue: false); } diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index 2fb91e6d9..3623bb18b 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -33,7 +33,12 @@ class SignatureAuthVerifier implements SocketAuthVerifier { /// a tag to help decipher logs String tag; - SignatureAuthVerifier(this.publicKey, this.dataToVerify, this.rvdNonce, this.tag); + SignatureAuthVerifier( + this.publicKey, + this.dataToVerify, + this.rvdNonce, + this.tag, + ); @override (bool authenticated, Uint8List? unused) onData( diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index c50fc50b8..10db50620 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -12,7 +12,7 @@ typedef ConnectorParams = ( int, int, String, - bool + bool, ); typedef PortPair = (int, int); @@ -27,10 +27,11 @@ void socketConnector(ConnectorParams connectorParams) async { portA, portB, sshrvdSessionParamsJsonString, - snoop + snoop, ) = connectorParams; - SshrvdSessionParams sshrvdSessionParams = SshrvdSessionParams.fromJson(jsonDecode(sshrvdSessionParamsJsonString)); + SshrvdSessionParams sshrvdSessionParams = + SshrvdSessionParams.fromJson(jsonDecode(sshrvdSessionParamsJsonString)); logger.info( 'Starting socket connector session for ${sshrvdSessionParams.toJson()}'); @@ -45,38 +46,40 @@ void socketConnector(ConnectorParams connectorParams) async { if (sshrvdSessionParams.authenticateSocketA) { String? pkAtSignA = sshrvdSessionParams.publicKeyA; if (pkAtSignA == null) { - logger.shout( - 'Cannot spawn socket connector.' - ' Authenticator for ${sshrvdSessionParams.atSignA}' - ' could not be created as PublicKey could not be' - ' fetched from the atServer.'); - throw Exception( - 'Failed to create SocketAuthenticator' - ' for ${sshrvdSessionParams.atSignA} due to failure to get public key for ${sshrvdSessionParams.atSignA}'); + logger.shout('Cannot spawn socket connector.' + ' Authenticator for ${sshrvdSessionParams.atSignA}' + ' could not be created as PublicKey could not be' + ' fetched from the atServer.'); + throw Exception('Failed to create SocketAuthenticator' + ' for ${sshrvdSessionParams.atSignA} due to failure to get public key for ${sshrvdSessionParams.atSignA}'); } socketAuthVerifierA = SignatureAuthVerifier( - pkAtSignA, jsonEncode(expectedPayloadForSignature), sshrvdSessionParams.rvdNonce!, sshrvdSessionParams.atSignA); + pkAtSignA, + jsonEncode(expectedPayloadForSignature), + sshrvdSessionParams.rvdNonce!, + sshrvdSessionParams.atSignA, + ); } SocketAuthVerifier? socketAuthVerifierB; if (sshrvdSessionParams.authenticateSocketB) { String? pkAtSignB = sshrvdSessionParams.publicKeyB; if (pkAtSignB == null) { - logger.shout( - 'Cannot spawn socket connector.' - ' Authenticator for ${sshrvdSessionParams.atSignB}' - ' could not be created as PublicKey could not be' - ' fetched from the atServer'); - throw Exception( - 'Failed to create SocketAuthenticator' - ' for ${sshrvdSessionParams.atSignB} due to failure to get public key for ${sshrvdSessionParams.atSignB}'); + logger.shout('Cannot spawn socket connector.' + ' Authenticator for ${sshrvdSessionParams.atSignB}' + ' could not be created as PublicKey could not be' + ' fetched from the atServer'); + throw Exception('Failed to create SocketAuthenticator' + ' for ${sshrvdSessionParams.atSignB} due to failure to get public key for ${sshrvdSessionParams.atSignB}'); } socketAuthVerifierB = SignatureAuthVerifier( - pkAtSignB, jsonEncode(expectedPayloadForSignature), sshrvdSessionParams.rvdNonce!, sshrvdSessionParams.atSignB!); + pkAtSignB, + jsonEncode(expectedPayloadForSignature), + sshrvdSessionParams.rvdNonce!, + sshrvdSessionParams.atSignB!, + ); } - - logger.shout('Calling serverToServer with authVerifiers A: $socketAuthVerifierA and B: $socketAuthVerifierB'); /// Create the socket connector SocketConnector socketStream = await SocketConnector.serverToServer( serverAddressA: InternetAddress.anyIPv4, @@ -103,10 +106,9 @@ void socketConnector(ConnectorParams connectorParams) async { closed = await socketStream.closed(); } - logger.warning( - 'Finished session ${sshrvdSessionParams.sessionId}' - ' for ${sshrvdSessionParams.atSignA} to ${sshrvdSessionParams.atSignB}' - ' using ports [$portA, $portB]'); + logger.info('Finished session ${sshrvdSessionParams.sessionId}' + ' for ${sshrvdSessionParams.atSignA} to ${sshrvdSessionParams.atSignB}' + ' using ports [$portA, $portB]'); Isolate.current.kill(); } diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index aab069a13..35d9cbccc 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -197,7 +197,8 @@ class SshrvdImpl implements Sshrvd { PortPair ports = await receivePort.first; - logger.info('Received ports $ports in main isolate for session ${sshrvdSessionParams.sessionId}'); + logger.info('Received ports $ports in main isolate' + ' for session ${sshrvdSessionParams.sessionId}'); return ports; } diff --git a/packages/noports_core/test/sshnp/sshnp_mocks.dart b/packages/noports_core/test/sshnp/sshnp_mocks.dart index 028d4c5d8..fdf023d52 100644 --- a/packages/noports_core/test/sshnp/sshnp_mocks.dart +++ b/packages/noports_core/test/sshnp/sshnp_mocks.dart @@ -12,7 +12,12 @@ abstract class FunctionCaller { class FunctionStub extends Mock implements FunctionCaller {} abstract class NotifyCaller { - Future call(AtKey key, String value); + Future call( + AtKey key, + String value, { + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + }); } class NotifyStub extends Mock implements NotifyCaller {} diff --git a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_mocks.dart b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_mocks.dart index e74fc6899..328cdee86 100644 --- a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_mocks.dart +++ b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_mocks.dart @@ -10,7 +10,12 @@ class HandleSshnpdPayloadStub extends Mock implements HandleSshnpdPayloadCaller {} class StubbedSshnpdChannel extends SshnpdChannel { - final Future Function(AtKey, String)? _notify; + final Future Function( + AtKey, + String, { + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + })? _notify; final Stream Function({String? regex, bool shouldDecrypt})? _subscribe; final Future Function(AtNotification notification)? @@ -21,7 +26,12 @@ class StubbedSshnpdChannel extends SshnpdChannel { required super.params, required super.sessionId, required super.namespace, - Future Function(AtKey, String)? notify, + Future Function( + AtKey, + String, { + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + })? notify, Stream Function({String? regex, bool shouldDecrypt})? subscribe, Future Function(AtNotification notification)? @@ -39,9 +49,16 @@ class StubbedSshnpdChannel extends SshnpdChannel { @override Future notify( AtKey atKey, - String value, - ) async { - return _notify?.call(atKey, value); + String value, { + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + }) async { + return _notify?.call( + atKey, + value, + checkForFinalDeliveryStatus: checkForFinalDeliveryStatus, + waitForFinalDeliveryStatus: waitForFinalDeliveryStatus, + ); } @override diff --git a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart index b20a5ed76..80f5e5077 100644 --- a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart +++ b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart @@ -26,7 +26,13 @@ void main() { // Invocation patterns as closures so they can be referred to by name // instead of explicitly writing these calls several times in the test - notifyInvocation() => notifyStub(any(), any()); + notifyInvocation() => notifyStub( + any(), + any(), + checkForFinalDeliveryStatus: + any(named: 'checkForFinalDeliveryStatus'), + waitForFinalDeliveryStatus: any(named: 'waitForFinalDeliveryStatus'), + ); subscribeInvocation() => subscribeStub( regex: any(named: 'regex'), shouldDecrypt: any(named: 'shouldDecrypt'), @@ -212,6 +218,10 @@ void main() { any( that: predicate((AtKey key) => key.key == 'sshpublickey')), any(), + checkForFinalDeliveryStatus: + any(named: 'checkForFinalDeliveryStatus'), + waitForFinalDeliveryStatus: + any(named: 'waitForFinalDeliveryStatus'), ), ).thenAnswer((_) async {}); @@ -227,6 +237,10 @@ void main() { any( that: predicate((AtKey key) => key.key == 'sshpublickey')), TestingKeyPair.public, + checkForFinalDeliveryStatus: + any(named: 'checkForFinalDeliveryStatus'), + waitForFinalDeliveryStatus: + any(named: 'waitForFinalDeliveryStatus'), ), ).called(1); }); // test sharePublicKeyIfRequired diff --git a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart index 207bcd0a2..dc12ab691 100644 --- a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart +++ b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart @@ -5,7 +5,13 @@ import 'package:noports_core/sshrv.dart'; /// Stubbing for [SshrvGenerator] typedef abstract class SshrvGeneratorCaller { - Sshrv call(String host, int port, {required int localPort, required bool bindLocalPort, String? rvdAuthString}); + Sshrv call( + String host, + int port, { + required int localPort, + required bool bindLocalPort, + String? rvdAuthString, + }); } class SshrvGeneratorStub extends Mock implements SshrvGeneratorCaller {} @@ -14,15 +20,26 @@ class MockSshrv extends Mock implements Sshrv {} /// Stubbed [SshrvdChannel] which we are testing class StubbedSshrvdChannel extends SshrvdChannel { - final Future Function(AtKey, String)? _notify; + final Future Function( + AtKey, + String, { + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + })? _notify; final Stream Function({String? regex, bool shouldDecrypt})? _subscribe; + StubbedSshrvdChannel({ required super.atClient, required super.params, required super.sessionId, required super.sshrvGenerator, - Future Function(AtKey, String)? notify, + Future Function( + AtKey, + String, { + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + })? notify, Stream Function({String? regex, bool shouldDecrypt})? subscribe, }) : _notify = notify, @@ -31,9 +48,16 @@ class StubbedSshrvdChannel extends SshrvdChannel { @override Future notify( AtKey atKey, - String value, - ) async { - return _notify?.call(atKey, value); + String value, { + required bool checkForFinalDeliveryStatus, + required bool waitForFinalDeliveryStatus, + }) async { + return _notify?.call( + atKey, + value, + checkForFinalDeliveryStatus: checkForFinalDeliveryStatus, + waitForFinalDeliveryStatus: waitForFinalDeliveryStatus, + ); } @override diff --git a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart index 78f118b9a..d75e7e2b7 100644 --- a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart +++ b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart @@ -27,18 +27,21 @@ void main() { // Invocation patterns as closures so they can be referred to by name // instead of explicitly writing these calls several times in the test - notifyInvocation() => notifyStub(any(), any()); + notifyInvocation() => notifyStub( + any(), + any(), + checkForFinalDeliveryStatus: + any(named: 'checkForFinalDeliveryStatus'), + waitForFinalDeliveryStatus: any(named: 'waitForFinalDeliveryStatus'), + ); subscribeInvocation() => subscribeStub( regex: any(named: 'regex'), shouldDecrypt: any(named: 'shouldDecrypt'), ); - sshrvGeneratorInvocation() => sshrvGeneratorStub( - any(), - any(), - localPort: any(named: 'localPort'), - bindLocalPort: any(named: 'bindLocalPort'), - rvdAuthString: any(named: 'rvdAuthString') - ); + sshrvGeneratorInvocation() => sshrvGeneratorStub(any(), any(), + localPort: any(named: 'localPort'), + bindLocalPort: any(named: 'bindLocalPort'), + rvdAuthString: any(named: 'rvdAuthString')); sshrvRunInvocation() => mockSshrv.run(); setUp(() { @@ -66,7 +69,7 @@ void main() { // Create an AtChops instance for testing AtEncryptionKeyPair encryptionKeyPair = - AtChopsUtil.generateAtEncryptionKeyPair(); + AtChopsUtil.generateAtEncryptionKeyPair(); AtChops atChops = AtChopsImpl( AtChopsKeys.create(encryptionKeyPair, null), @@ -88,7 +91,11 @@ void main() { expect(stubbedSshrvdChannel.logger, isA()); expect( stubbedSshrvdChannel.sshrvGenerator, - isA Function(String, int, {required int localPort, required bool bindLocalPort, String? rvdAuthString})>(), + isA< + Sshrv Function(String, int, + {required int localPort, + required bool bindLocalPort, + String? rvdAuthString})>(), ); expect(stubbedSshrvdChannel.atClient, mockAtClient); expect(stubbedSshrvdChannel.params, mockParams); @@ -145,7 +152,7 @@ void main() { that: predicate( // Predicate matching specifically the sshrvdIdKey format (AtKey key) => - key.key == 'mydevice.${Sshrvd.namespace}' && + key.key == 'mydevice.request_ports.${Sshrvd.namespace}' && key.sharedBy == '@client' && key.sharedWith == '@sshrvd' && key.metadata != null && @@ -154,6 +161,10 @@ void main() { ), ), any(), + checkForFinalDeliveryStatus: + any(named: 'checkForFinalDeliveryStatus'), + waitForFinalDeliveryStatus: + any(named: 'waitForFinalDeliveryStatus'), ), ]); diff --git a/packages/noports_core/test/sshrvd/notification_subscription_test.dart b/packages/noports_core/test/sshrvd/notification_subscription_test.dart index e14165a16..cc0ab502c 100644 --- a/packages/noports_core/test/sshrvd/notification_subscription_test.dart +++ b/packages/noports_core/test/sshrvd/notification_subscription_test.dart @@ -1,11 +1,19 @@ import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; import 'package:noports_core/sshrvd.dart'; import 'package:test/test.dart'; -void main() { +void main() { test('Test notification subscription regex', () { - expect(RegExp(SshrvdImpl.subscriptionRegex).hasMatch('jagan@test.${Sshrvd.namespace}@jagan'), true); - expect(RegExp(SshrvdImpl.subscriptionRegex).hasMatch('${Sshrvd.namespace}@'), true); - expect(RegExp(SshrvdImpl.subscriptionRegex).hasMatch('${Sshrvd.namespace}.test@'), false); + expect( + RegExp(SshrvdImpl.subscriptionRegex) + .hasMatch('jagan@test.${Sshrvd.namespace}@jagan'), + true); + expect( + RegExp(SshrvdImpl.subscriptionRegex).hasMatch('${Sshrvd.namespace}@'), + true); + expect( + RegExp(SshrvdImpl.subscriptionRegex) + .hasMatch('${Sshrvd.namespace}.test@'), + false); }); -} \ No newline at end of file +} diff --git a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart index 7adcc9fb8..413081e03 100644 --- a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart +++ b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart @@ -11,20 +11,22 @@ void main() { late AtChops atChops; setUpAll(() { - AtEncryptionKeyPair encryptionKeyPair = AtChopsUtil - .generateAtEncryptionKeyPair(keySize: 2048); + AtEncryptionKeyPair encryptionKeyPair = + AtChopsUtil.generateAtEncryptionKeyPair(keySize: 2048); atChops = AtChopsImpl(AtChopsKeys.create(encryptionKeyPair, null)); }); - test('SignatureVerifyingSocketAuthenticator signature verification success', () { + test('SignatureVerifyingSocketAuthenticator signature verification success', + () { String rvdSessionNonce = DateTime.now().toIso8601String(); - Map payload = {'sessionId':Uuid().v4(), 'rvdNonce': rvdSessionNonce}; + Map payload = {'sessionId': Uuid().v4(), 'rvdNonce': rvdSessionNonce}; String signedEnvelope = signPayload(atChops, payload); SignatureAuthVerifier sa = SignatureAuthVerifier( atChops.atChopsKeys.atEncryptionKeyPair!.atPublicKey.publicKey, jsonEncode(payload), // We'll verify the signature against this - rvdSessionNonce, 'test_for_success'); + rvdSessionNonce, + 'test_for_success'); List list = utf8.encode(signedEnvelope); Uint8List data = Uint8List.fromList(list); @@ -37,16 +39,21 @@ void main() { expect(unused, null); }); - test('SignatureVerifyingSocketAuthenticator signature verification failure', () { + test('SignatureVerifyingSocketAuthenticator signature verification failure', + () { String rvdSessionNonce = DateTime.now().toIso8601String(); - Map payload = {'sessionId':Uuid().v4().toString(), 'rvdNonce': rvdSessionNonce}; + Map payload = { + 'sessionId': Uuid().v4().toString(), + 'rvdNonce': rvdSessionNonce + }; String signedEnvelope = signPayload(atChops, payload); SignatureAuthVerifier sa = SignatureAuthVerifier( atChops.atChopsKeys.atEncryptionKeyPair!.atPublicKey.publicKey, // using a different payload; signature verification will fail 'some other payload', - rvdSessionNonce, 'test_for_failure'); + rvdSessionNonce, + 'test_for_failure'); List list = utf8.encode(signedEnvelope); Uint8List data = Uint8List.fromList(list); @@ -54,16 +61,19 @@ void main() { expect(() => sa.onData(data, MockSocket()), throwsException); }); - test('SignatureVerifyingSocketAuthenticator signature verification ok but mismatched nonce', () { + test( + 'SignatureVerifyingSocketAuthenticator signature verification ok but mismatched nonce', + () { final uuidString = Uuid().v4().toString(); String rvdSessionNonce = DateTime.now().toIso8601String(); - Map payload = {'sessionId':uuidString, 'rvdNonce': rvdSessionNonce}; + Map payload = {'sessionId': uuidString, 'rvdNonce': rvdSessionNonce}; String signedEnvelope = signPayload(atChops, payload); SignatureAuthVerifier sa = SignatureAuthVerifier( atChops.atChopsKeys.atEncryptionKeyPair!.atPublicKey.publicKey, jsonEncode(payload), - rvdSessionNonce, 'test_for_mismatch'); + rvdSessionNonce, + 'test_for_mismatch'); Map fakedEnvelope = jsonDecode(signedEnvelope); fakedEnvelope['payload']['rvdNonce'] = 'not the same nonce'; @@ -89,10 +99,10 @@ String signPayload(AtChops atChops, Map payload) { } bool verifySignature( - AtChops atChops, - String requestingAtsign, - Map envelope, - ) { + AtChops atChops, + String requestingAtsign, + Map envelope, +) { final String signature = envelope['signature']; Map payload = envelope['payload']; final hashingAlgo = HashingAlgoType.values.byName(envelope['hashingAlgo']); @@ -108,4 +118,4 @@ bool verifySignature( return svr.result; } -class MockSocket extends Mock implements Socket {} \ No newline at end of file +class MockSocket extends Mock implements Socket {} diff --git a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart index 3fd14241c..a45c0d83b 100644 --- a/packages/noports_core/test/sshrvd/sshrvdutil_test.dart +++ b/packages/noports_core/test/sshrvd/sshrvdutil_test.dart @@ -10,7 +10,6 @@ void main() { // Create a notification in rvd namespace AtNotification notification = AtNotification.empty(); notification.key = 'test.${Sshrvd.namespace}'; - }); test('sshrvd should accept notification in new request_ports format', () { @@ -20,7 +19,9 @@ void main() { expect(SshrvdUtil.accept(notification), true); }); - test('sshrvd backwards compatibility test - should handle both legacy and new messages in JSON format', () async { + test( + 'sshrvd backwards compatibility test - should handle both legacy and new messages in JSON format', + () async { Map m = {}; m['session'] = 'hello'; m['atSignA'] = '@4314sagittarius'; From a2b60f26a06ad4d054f56eeaaeb2404d642f88d8 Mon Sep 17 00:00:00 2001 From: gkc Date: Sat, 30 Dec 2023 13:31:28 +0000 Subject: [PATCH 22/60] fix: remove 9.7s of unnecessary delay from a unit test --- .../test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart index 80f5e5077..6d7a59490 100644 --- a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart +++ b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart @@ -173,7 +173,7 @@ void main() { when(payloadInvocation) .thenAnswer((_) async => SshnpdAck.notAcknowledged); - Future ack = stubbedSshnpdChannel.waitForDaemonResponse(); + Future ack = stubbedSshnpdChannel.waitForDaemonResponse(maxWaitMillis: 300); // manually add a notification to the stream final String notificationId = Uuid().v4(); From a4b8e32a7b51e821e59276e035b6ae54187fe167 Mon Sep 17 00:00:00 2001 From: gkc Date: Sat, 30 Dec 2023 15:49:34 +0000 Subject: [PATCH 23/60] feat: Client ability to ping daemon for info, including which features the daemon supports. This is then used to downgrade where the daemon does not support the feature --- .../lib/src/common/default_args.dart | 5 ++- .../noports_core/lib/src/common/features.dart | 8 ++++ .../lib/src/common/validation_utils.dart | 1 + .../lib/src/sshnp/models/sshnp_arg.dart | 15 +++++++- .../lib/src/sshnp/models/sshnp_params.dart | 19 ++++++++-- .../lib/src/sshnp/sshnp_core.dart | 21 ++++++++++- .../util/sshnpd_channel/sshnpd_channel.dart | 37 +++++++++++++++++++ .../noports_core/lib/src/sshnpd/sshnpd.dart | 8 ++++ .../lib/src/sshnpd/sshnpd_impl.dart | 21 +++++++++-- .../lib/src/sshrvd/sshrvd_impl.dart | 6 ++- packages/noports_core/lib/src/version.dart | 2 +- packages/noports_core/pubspec.yaml | 4 +- .../test/sshnp/models/sshnp_params_test.dart | 32 ++++++++-------- packages/sshnoports/pubspec.lock | 4 +- packages/sshnoports/pubspec.yaml | 4 +- 15 files changed, 150 insertions(+), 37 deletions(-) create mode 100644 packages/noports_core/lib/src/common/features.dart diff --git a/packages/noports_core/lib/src/common/default_args.dart b/packages/noports_core/lib/src/common/default_args.dart index 5f07fd6ff..4293c6abf 100644 --- a/packages/noports_core/lib/src/common/default_args.dart +++ b/packages/noports_core/lib/src/common/default_args.dart @@ -18,8 +18,9 @@ class DefaultArgs { static const bool addForwardsToTunnel = false; static final bool allowLocalFileSystem = Platform.isLinux || Platform.isMacOS || Platform.isWindows; - static const bool authenticateClientToRvd = false; - static const bool authenticateDeviceToRvd = false; + static const bool authenticateClientToRvd = true; + static const bool authenticateDeviceToRvd = true; + static const bool encryptRvdTraffic = true; } class DefaultSshnpArgs { diff --git a/packages/noports_core/lib/src/common/features.dart b/packages/noports_core/lib/src/common/features.dart new file mode 100644 index 000000000..3631ab4a3 --- /dev/null +++ b/packages/noports_core/lib/src/common/features.dart @@ -0,0 +1,8 @@ +/// Features which can be supported by the NoPorts Daemon +enum DaemonFeatures { + /// authenticate when connecting to the Socket Rendezvous (sr) + srAuth, + + /// End-to-end encrypt traffic sent via the SocketRendezvous (sr) + srE2ee, +} diff --git a/packages/noports_core/lib/src/common/validation_utils.dart b/packages/noports_core/lib/src/common/validation_utils.dart index 3f1778ad9..4b0a9dd7d 100644 --- a/packages/noports_core/lib/src/common/validation_utils.dart +++ b/packages/noports_core/lib/src/common/validation_utils.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart'; diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart index 30536c1fc..ea608622e 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart @@ -126,6 +126,7 @@ class SshnpArg { listDevicesArg, authenticateClientToRvdArg, authenticateDeviceToRvdArg, + encryptRvdTrafficArg, ]; @override @@ -350,7 +351,7 @@ class SshnpArg { ); static const authenticateClientToRvdArg = SshnpArg( name: 'authenticate-client', - help: 'When true, client needs to authenticate it self to rvd', + help: 'When false, client will not authenticate itself to rvd', defaultsTo: DefaultArgs.authenticateClientToRvd, format: ArgFormat.flag, parseWhen: ParseWhen.commandLine, @@ -358,10 +359,20 @@ class SshnpArg { ); static const authenticateDeviceToRvdArg = SshnpArg( name: 'authenticate-device', - help: 'When true, device needs to authenticate it self to rvd', + help: 'When false, device will not authenticate to the socket rendezvous', defaultsTo: DefaultArgs.authenticateDeviceToRvd, format: ArgFormat.flag, parseWhen: ParseWhen.commandLine, mandatory: false, ); + static const encryptRvdTrafficArg = SshnpArg( + name: 'encrypt-rvd-traffic', + help: 'When true, traffic via the socket rendezvous is encrypted,' + ' in addition to whatever encryption the traffic already has' + ' (e.g. an ssh session)', + defaultsTo: DefaultArgs.encryptRvdTraffic, + format: ArgFormat.flag, + parseWhen: ParseWhen.commandLine, + mandatory: false, + ); } diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index 9c6c89f91..1f779b166 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:at_utils/at_utils.dart'; import 'package:noports_core/src/common/types.dart'; import 'package:noports_core/src/sshnp/models/config_file_repository.dart'; import 'package:noports_core/src/sshnp/models/sshnp_arg.dart'; @@ -34,8 +35,9 @@ class SshnpParams { final bool addForwardsToTunnel; final String? atKeysFilePath; final SupportedSshAlgorithm sshAlgorithm; - final bool authenticateClientToRvd; - final bool authenticateDeviceToRvd; + bool authenticateClientToRvd; + bool authenticateDeviceToRvd; + bool encryptRvdTraffic; /// Special Arguments @@ -70,6 +72,7 @@ class SshnpParams { this.sshAlgorithm = DefaultArgs.sshAlgorithm, this.authenticateClientToRvd = DefaultArgs.authenticateClientToRvd, this.authenticateDeviceToRvd = DefaultArgs.authenticateDeviceToRvd, + this.encryptRvdTraffic = DefaultArgs.encryptRvdTraffic, }); factory SshnpParams.empty() { @@ -115,6 +118,7 @@ class SshnpParams { params2.authenticateClientToRvd ?? params1.authenticateClientToRvd, authenticateDeviceToRvd: params2.authenticateDeviceToRvd ?? params1.authenticateDeviceToRvd, + encryptRvdTraffic: params2.encryptRvdTraffic ?? params1.encryptRvdTraffic, ); } @@ -159,6 +163,8 @@ class SshnpParams { DefaultArgs.authenticateClientToRvd, authenticateDeviceToRvd: partial.authenticateDeviceToRvd ?? DefaultArgs.authenticateDeviceToRvd, + encryptRvdTraffic: + partial.encryptRvdTraffic ?? DefaultArgs.encryptRvdTraffic, ); } @@ -209,6 +215,7 @@ class SshnpParams { SshnpArg.sshAlgorithmArg.name: sshAlgorithm.toString(), SshnpArg.authenticateClientToRvdArg.name: authenticateClientToRvd, SshnpArg.authenticateDeviceToRvdArg.name: authenticateDeviceToRvd, + SshnpArg.encryptRvdTrafficArg.name: encryptRvdTraffic, }; args.removeWhere( (key, value) => !parserType.shouldParse(SshnpArg.fromName(key).parseWhen), @@ -249,6 +256,7 @@ class SshnpPartialParams { final SupportedSshAlgorithm? sshAlgorithm; final bool? authenticateClientToRvd; final bool? authenticateDeviceToRvd; + final bool? encryptRvdTraffic; /// Operation flags final bool? listDevices; @@ -278,6 +286,7 @@ class SshnpPartialParams { this.sshAlgorithm, this.authenticateClientToRvd, this.authenticateDeviceToRvd, + this.encryptRvdTraffic, }); factory SshnpPartialParams.empty() { @@ -318,6 +327,7 @@ class SshnpPartialParams { params2.authenticateClientToRvd ?? params1.authenticateClientToRvd, authenticateDeviceToRvd: params2.authenticateDeviceToRvd ?? params1.authenticateDeviceToRvd, + encryptRvdTraffic: params2.encryptRvdTraffic ?? params1.encryptRvdTraffic, ); } @@ -341,8 +351,8 @@ class SshnpPartialParams { factory SshnpPartialParams.fromArgMap(Map args) { return SshnpPartialParams( profileName: args[SshnpArg.profileNameArg.name], - clientAtSign: args[SshnpArg.fromArg.name], - sshnpdAtSign: args[SshnpArg.toArg.name], + clientAtSign: AtUtils.fixAtSign(args[SshnpArg.fromArg.name]), + sshnpdAtSign: AtUtils.fixAtSign(args[SshnpArg.toArg.name]), host: args[SshnpArg.hostArg.name], device: args[SshnpArg.deviceArg.name], port: args[SshnpArg.portArg.name], @@ -369,6 +379,7 @@ class SshnpPartialParams { args[SshnpArg.sshAlgorithmArg.name]), authenticateClientToRvd: args[SshnpArg.authenticateClientToRvdArg.name], authenticateDeviceToRvd: args[SshnpArg.authenticateDeviceToRvdArg.name], + encryptRvdTraffic: args[SshnpArg.encryptRvdTrafficArg.name], ); } diff --git a/packages/noports_core/lib/src/sshnp/sshnp_core.dart b/packages/noports_core/lib/src/sshnp/sshnp_core.dart index 0de4d3f25..fab929f64 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_core.dart +++ b/packages/noports_core/lib/src/sshnp/sshnp_core.dart @@ -3,9 +3,11 @@ import 'dart:async'; import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; +import 'package:noports_core/src/common/features.dart'; import 'package:noports_core/src/common/mixins/async_completion.dart'; import 'package:noports_core/src/common/mixins/async_initialization.dart'; import 'package:noports_core/src/common/mixins/at_client_bindings.dart'; +import 'package:noports_core/src/common/default_args.dart'; import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler/sshnp_ssh_key_handler.dart'; import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart'; import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; @@ -63,7 +65,7 @@ abstract class SshnpCore required this.atClient, required this.params, }) : sessionId = Uuid().v4(), - namespace = '${params.device}.sshnp', + namespace = '${params.device}.${DefaultArgs.namespace}', localPort = params.localPort { logger.level = params.verbose ? 'info' : 'shout'; @@ -83,6 +85,23 @@ abstract class SshnpCore /// Start the sshnpd payload handler await sshnpdChannel.callInitialization(); + late Map pingResponse; + try { + pingResponse = await sshnpdChannel.ping().timeout(Duration(seconds: 10)); + } catch (e) { + logger.severe( + 'No ping response from ${params.device}${params.sshnpdAtSign}'); + rethrow; + } + + final daemonFeatures = pingResponse['supportedFeatures']; + if (daemonFeatures[DaemonFeatures.srAuth.name] != true) { + params.authenticateDeviceToRvd = false; + } + if (daemonFeatures[DaemonFeatures.srE2ee.name] != true) { + params.encryptRvdTraffic = false; + } + /// Set the remote username to use for the ssh session remoteUsername = await sshnpdChannel.resolveRemoteUsername(); diff --git a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart index 80212e024..1ad7320e1 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart @@ -184,6 +184,43 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { } } + Future> ping() async { + Completer> completer = Completer(); + + subscribe( + regex: 'heartbeat' + '.${params.device}' + '.${DefaultArgs.namespace}', + shouldDecrypt: true, + ).listen((notification) { + logger.info( + 'Received ping response from ${notification.from} : ${notification.key} : ${notification.value}'); + if (notification.from == params.sshnpdAtSign) { + logger.info('Completing the future'); + completer.complete(jsonDecode(notification.value ?? '{}')); + } + }); + var pingKey = AtKey() + ..key = "ping.${params.device}" + ..sharedBy = params.clientAtSign + ..sharedWith = params.sshnpdAtSign + ..namespace = DefaultArgs.namespace + ..metadata = (Metadata() + ..isPublic = false + ..isEncrypted = true + ..namespaceAware = true); + + logger.info('Sending ping to sshnpd'); + await notify( + pingKey, + 'ping', + checkForFinalDeliveryStatus: false, + waitForFinalDeliveryStatus: false, + ); + + return completer.future; + } + /// List all available devices from the daemon. /// Returns a [SSHPNPDeviceList] object which contains a map of device names /// and corresponding info, and a list of active devices (devices which also diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd.dart b/packages/noports_core/lib/src/sshnpd/sshnpd.dart index a6ff10859..a5d64a5ee 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; @@ -69,6 +70,13 @@ abstract class Sshnpd { /// - [SupportedSshAlgorithm.rsa] abstract final SupportedSshAlgorithm sshAlgorithm; + /// An encryption keypair which should only ever reside in memory. + /// The public key is provided in responses to client 'pings', and is + /// used by clients to encrypt symmetric encryption keys intended for + /// one-time use in a NoPorts session, and share the encrypted details + /// as part of the session request payload. + abstract final AtEncryptionKeyPair ephemeralEncryptionKeyPair; + static Future fromCommandLineArgs(List args, {AtClient? atClient, FutureOr Function(SshnpdParams)? atClientGenerator, diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 5c2a78aea..48381ce4f 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -2,11 +2,13 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:dartssh2/dartssh2.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; +import 'package:noports_core/src/common/features.dart'; import 'package:noports_core/src/common/openssh_binary_path.dart'; import 'package:noports_core/src/sshrv/sshrv.dart'; import 'package:noports_core/sshnpd.dart'; @@ -55,6 +57,10 @@ class SshnpdImpl implements Sshnpd { @override final SupportedSshAlgorithm sshAlgorithm; + @override + final AtEncryptionKeyPair ephemeralEncryptionKeyPair = + AtChopsUtil.generateAtEncryptionKeyPair(keySize: 2048); + @override @visibleForTesting bool initialized = false; @@ -301,13 +307,20 @@ class SshnpdImpl implements Sshnpd { ..namespaceAware = true); /// send a heartbeat back + var pingResponse = { + 'devicename': device, + 'version': packageVersion, + 'ephemeralPK': ephemeralEncryptionKeyPair.atPublicKey.publicKey, + 'ephemeralPKType': 'rsa2048', + 'supportedFeatures': { + DaemonFeatures.srAuth.name: true, + DaemonFeatures.srE2ee.name: true, + }, + }; unawaited( _notify( atKey: atKey, - value: jsonEncode({ - 'devicename': device, - 'version': packageVersion, - }), + value: jsonEncode(pingResponse), checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, ), diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index 35d9cbccc..0d10109c0 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -132,7 +132,8 @@ class SshrvdImpl implements Sshrvd { return; } - logger.info('New session request: $sessionParams'); + logger + .info('New session request: $sessionParams from ${notification.from}'); (int, int) ports = await _spawnSocketConnector( 0, @@ -159,6 +160,9 @@ class SshrvdImpl implements Sshrvd { String data = '$ipAddress,$portA,$portB,${sessionParams.rvdNonce}'; + logger.info( + 'Sending response data for session ${sessionParams.sessionId} : [$data]'); + try { await atClient.notificationService.notify( NotificationParams.forUpdate(atKey, value: data), diff --git a/packages/noports_core/lib/src/version.dart b/packages/noports_core/lib/src/version.dart index d89252b2b..d603e0da8 100644 --- a/packages/noports_core/lib/src/version.dart +++ b/packages/noports_core/lib/src/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '5.0.2'; +const packageVersion = '5.1.0'; diff --git a/packages/noports_core/pubspec.yaml b/packages/noports_core/pubspec.yaml index 6d52007fb..c24cb62ca 100644 --- a/packages/noports_core/pubspec.yaml +++ b/packages/noports_core/pubspec.yaml @@ -2,7 +2,7 @@ name: noports_core description: Core library code for sshnoports homepage: https://docs.atsign.com/ -version: 5.0.2 +version: 5.1.0 environment: sdk: ">=3.0.0 <4.0.0" @@ -10,7 +10,7 @@ environment: dependencies: args: ^2.4.2 at_chops: ^1.0.4 - at_client: ^3.0.65 + at_client: ^3.0.70 at_commons: ^3.0.56 at_utils: ^3.0.15 cryptography: ^2.7.0 diff --git a/packages/noports_core/test/sshnp/models/sshnp_params_test.dart b/packages/noports_core/test/sshnp/models/sshnp_params_test.dart index 2737ffe7c..8fdbb1e7f 100644 --- a/packages/noports_core/test/sshnp/models/sshnp_params_test.dart +++ b/packages/noports_core/test/sshnp/models/sshnp_params_test.dart @@ -319,8 +319,8 @@ void main() { final params = SshnpParams.fromJson(json); expect(params.profileName, equals('myProfile')); - expect(params.clientAtSign, equals('@myClientAtSign')); - expect(params.sshnpdAtSign, equals('@mySshnpdAtSign')); + expect(params.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect(params.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(params.host, equals('@myHost')); expect(params.device, equals('myDeviceName')); expect(params.port, equals(1234)); @@ -378,8 +378,8 @@ void main() { ]; final params = SshnpParams.fromConfigLines('myProfile', configLines); expect(params.profileName, equals('myProfile')); - expect(params.clientAtSign, equals('@myClientAtSign')); - expect(params.sshnpdAtSign, equals('@mySshnpdAtSign')); + expect(params.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect(params.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(params.host, equals('@myHost')); expect(params.device, equals('myDeviceName')); expect(params.port, equals(1234)); @@ -426,8 +426,8 @@ void main() { final parsedParams = SshnpParams.fromConfigLines('myProfile', configLines); expect(parsedParams.profileName, equals('myProfile')); - expect(parsedParams.clientAtSign, equals('@myClientAtSign')); - expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign')); + expect(parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(parsedParams.host, equals('@myHost')); expect(parsedParams.device, equals('myDeviceName')); expect(parsedParams.port, equals(1234)); @@ -518,8 +518,8 @@ void main() { ); final json = params.toJson(); final parsedParams = SshnpParams.fromJson(json); - expect(parsedParams.clientAtSign, equals('@myClientAtSign')); - expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign')); + expect(parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(parsedParams.host, equals('@myHost')); expect(parsedParams.device, equals('myDeviceName')); expect(parsedParams.port, equals(1234)); @@ -822,8 +822,8 @@ void main() { final parsedParams = SshnpPartialParams.fromConfigLines('myProfile', configLines); expect(parsedParams.profileName, equals('myProfile')); - expect(parsedParams.clientAtSign, equals('@myClientAtSign')); - expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign')); + expect(parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(parsedParams.host, equals('@myHost')); expect(parsedParams.device, equals('myDeviceName')); expect(parsedParams.port, equals(1234)); @@ -865,8 +865,8 @@ void main() { final params = SshnpPartialParams.fromJson(json); expect(params.profileName, equals('myProfile')); - expect(params.clientAtSign, equals('@myClientAtSign')); - expect(params.sshnpdAtSign, equals('@mySshnpdAtSign')); + expect(params.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect(params.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(params.host, equals('@myHost')); expect(params.device, equals('myDeviceName')); expect(params.port, equals(1234)); @@ -913,8 +913,8 @@ void main() { SshnpArg.sshAlgorithmArg.name: SupportedSshAlgorithm.rsa.toString(), }); expect(params.profileName, equals('myProfile')); - expect(params.clientAtSign, equals('@myClientAtSign')); - expect(params.sshnpdAtSign, equals('@mySshnpdAtSign')); + expect(params.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect(params.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(params.host, equals('@myHost')); expect(params.device, equals('myDeviceName')); expect(params.port, equals(1234)); @@ -983,8 +983,8 @@ void main() { ]; final params = SshnpPartialParams.fromArgList(argList); expect(params.profileName, equals('myProfile')); - expect(params.clientAtSign, equals('@myClientAtSign')); - expect(params.sshnpdAtSign, equals('@mySshnpdAtSign')); + expect(params.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect(params.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(params.host, equals('@myHost')); expect(params.device, equals('myDeviceName')); expect(params.port, equals(1234)); diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index abae586e5..cd49dbf40 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -77,10 +77,10 @@ packages: dependency: "direct overridden" description: name: at_client - sha256: b25f991409efa860a6196399c20bbebb8e763695197996ea4013789b4b96f297 + sha256: "975da85cd85f6997569f562a198a46dc5eb1c51e74522cef9b432cceed436b59" url: "https://pub.dev" source: hosted - version: "3.0.68" + version: "3.0.70" at_commons: dependency: transitive description: diff --git a/packages/sshnoports/pubspec.yaml b/packages/sshnoports/pubspec.yaml index 76d7f30a4..760dfafe5 100644 --- a/packages/sshnoports/pubspec.yaml +++ b/packages/sshnoports/pubspec.yaml @@ -1,7 +1,7 @@ name: sshnoports publish_to: none -version: 4.0.4 +version: 4.1.0 environment: sdk: ">=3.0.0 <4.0.0" @@ -15,7 +15,7 @@ dependency_overrides: # Pin the dependencies of noports_core archive: 3.3.9 args: 2.4.2 - at_client: 3.0.68 + at_client: 3.0.70 at_lookup: 3.0.41 at_utils: 3.0.15 crypton: 2.1.0 From 968154f60b3640afceecee08f8e63145406bd43c Mon Sep 17 00:00:00 2001 From: gkc Date: Sat, 30 Dec 2023 15:50:22 +0000 Subject: [PATCH 24/60] fix: something weird about error handling in the sshnp client; commented out two 'return' statements for now, in the runZonedGuarded onError handler --- packages/sshnoports/bin/sshnp.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/sshnoports/bin/sshnp.dart b/packages/sshnoports/bin/sshnp.dart index d44ce6362..995770ee5 100644 --- a/packages/sshnoports/bin/sshnp.dart +++ b/packages/sshnoports/bin/sshnp.dart @@ -123,10 +123,14 @@ void main(List args) async { stderr.writeln('\nStack Trace: ${stackTrace.toString()}'); } exit(1); + } catch (error, stackTrace) { + stderr.writeln(error.toString()); + stderr.writeln('\nStack Trace: ${stackTrace.toString()}'); + exit(1); } }, (Object error, StackTrace stackTrace) async { - if (error is ArgumentError) return; - if (error is SshnpError) return; + // if (error is ArgumentError) return; + // if (error is SshnpError) return; stderr.writeln('Error: ${error.toString()}'); stderr.writeln('\nStack Trace: ${stackTrace.toString()}'); exit(1); From a874b49fe87fd1f9ef58600e432f3e33eba2a85c Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 31 Dec 2023 13:26:53 +0000 Subject: [PATCH 25/60] feat: add `--discover-daemon-features` to sshnp args --- .../lib/src/common/default_args.dart | 1 + .../lib/src/common/validation_utils.dart | 1 - .../lib/src/sshnp/models/sshnp_arg.dart | 19 ++++++++- .../lib/src/sshnp/models/sshnp_params.dart | 18 +++++++-- .../lib/src/sshnp/sshnp_core.dart | 39 ++++++++++++------- 5 files changed, 57 insertions(+), 21 deletions(-) diff --git a/packages/noports_core/lib/src/common/default_args.dart b/packages/noports_core/lib/src/common/default_args.dart index 4293c6abf..e322f90ca 100644 --- a/packages/noports_core/lib/src/common/default_args.dart +++ b/packages/noports_core/lib/src/common/default_args.dart @@ -21,6 +21,7 @@ class DefaultArgs { static const bool authenticateClientToRvd = true; static const bool authenticateDeviceToRvd = true; static const bool encryptRvdTraffic = true; + static const bool discoverDaemonFeatures = true; } class DefaultSshnpArgs { diff --git a/packages/noports_core/lib/src/common/validation_utils.dart b/packages/noports_core/lib/src/common/validation_utils.dart index 4b0a9dd7d..3f1778ad9 100644 --- a/packages/noports_core/lib/src/common/validation_utils.dart +++ b/packages/noports_core/lib/src/common/validation_utils.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:io'; import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart'; diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart index ea608622e..acf0519a9 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart @@ -127,6 +127,7 @@ class SshnpArg { authenticateClientToRvdArg, authenticateDeviceToRvdArg, encryptRvdTrafficArg, + discoverDaemonFeaturesArg, ]; @override @@ -350,7 +351,7 @@ class SshnpArg { parseWhen: ParseWhen.commandLine, ); static const authenticateClientToRvdArg = SshnpArg( - name: 'authenticate-client', + name: 'authenticate-client-to-rvd', help: 'When false, client will not authenticate itself to rvd', defaultsTo: DefaultArgs.authenticateClientToRvd, format: ArgFormat.flag, @@ -358,7 +359,7 @@ class SshnpArg { mandatory: false, ); static const authenticateDeviceToRvdArg = SshnpArg( - name: 'authenticate-device', + name: 'authenticate-device-to-rvd', help: 'When false, device will not authenticate to the socket rendezvous', defaultsTo: DefaultArgs.authenticateDeviceToRvd, format: ArgFormat.flag, @@ -375,4 +376,18 @@ class SshnpArg { parseWhen: ParseWhen.commandLine, mandatory: false, ); + static const discoverDaemonFeaturesArg = SshnpArg( + name: 'discover-daemon-features', + help: 'When this flag is set, this client starts by pinging the daemon to' + ' discover what features it supports, and exits if this client has ' + ' requested use of a feature which the daemon does not support.' + ' If you already know what features the daemon supports and are ' + ' setting other flags (--authenticate-device-to-rvd and' + ' --encrypt-rvd-traffic) based on that knowledge, then you should unset' + ' this flag to reduce total time-to-connection.', + defaultsTo: DefaultArgs.discoverDaemonFeatures, + format: ArgFormat.flag, + parseWhen: ParseWhen.commandLine, + mandatory: false, + ); } diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index 1f779b166..a019e9b91 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -35,9 +35,10 @@ class SshnpParams { final bool addForwardsToTunnel; final String? atKeysFilePath; final SupportedSshAlgorithm sshAlgorithm; - bool authenticateClientToRvd; - bool authenticateDeviceToRvd; - bool encryptRvdTraffic; + final bool authenticateClientToRvd; + final bool authenticateDeviceToRvd; + final bool encryptRvdTraffic; + final bool discoverDaemonFeatures; /// Special Arguments @@ -73,6 +74,7 @@ class SshnpParams { this.authenticateClientToRvd = DefaultArgs.authenticateClientToRvd, this.authenticateDeviceToRvd = DefaultArgs.authenticateDeviceToRvd, this.encryptRvdTraffic = DefaultArgs.encryptRvdTraffic, + this.discoverDaemonFeatures = DefaultArgs.discoverDaemonFeatures, }); factory SshnpParams.empty() { @@ -119,6 +121,8 @@ class SshnpParams { authenticateDeviceToRvd: params2.authenticateDeviceToRvd ?? params1.authenticateDeviceToRvd, encryptRvdTraffic: params2.encryptRvdTraffic ?? params1.encryptRvdTraffic, + discoverDaemonFeatures: + params2.discoverDaemonFeatures ?? params1.discoverDaemonFeatures, ); } @@ -165,6 +169,8 @@ class SshnpParams { DefaultArgs.authenticateDeviceToRvd, encryptRvdTraffic: partial.encryptRvdTraffic ?? DefaultArgs.encryptRvdTraffic, + discoverDaemonFeatures: + partial.discoverDaemonFeatures ?? DefaultArgs.discoverDaemonFeatures, ); } @@ -216,6 +222,7 @@ class SshnpParams { SshnpArg.authenticateClientToRvdArg.name: authenticateClientToRvd, SshnpArg.authenticateDeviceToRvdArg.name: authenticateDeviceToRvd, SshnpArg.encryptRvdTrafficArg.name: encryptRvdTraffic, + SshnpArg.discoverDaemonFeaturesArg.name: discoverDaemonFeatures, }; args.removeWhere( (key, value) => !parserType.shouldParse(SshnpArg.fromName(key).parseWhen), @@ -257,6 +264,7 @@ class SshnpPartialParams { final bool? authenticateClientToRvd; final bool? authenticateDeviceToRvd; final bool? encryptRvdTraffic; + final bool? discoverDaemonFeatures; /// Operation flags final bool? listDevices; @@ -287,6 +295,7 @@ class SshnpPartialParams { this.authenticateClientToRvd, this.authenticateDeviceToRvd, this.encryptRvdTraffic, + this.discoverDaemonFeatures, }); factory SshnpPartialParams.empty() { @@ -328,6 +337,8 @@ class SshnpPartialParams { authenticateDeviceToRvd: params2.authenticateDeviceToRvd ?? params1.authenticateDeviceToRvd, encryptRvdTraffic: params2.encryptRvdTraffic ?? params1.encryptRvdTraffic, + discoverDaemonFeatures: + params2.discoverDaemonFeatures ?? params1.discoverDaemonFeatures, ); } @@ -380,6 +391,7 @@ class SshnpPartialParams { authenticateClientToRvd: args[SshnpArg.authenticateClientToRvdArg.name], authenticateDeviceToRvd: args[SshnpArg.authenticateDeviceToRvdArg.name], encryptRvdTraffic: args[SshnpArg.encryptRvdTrafficArg.name], + discoverDaemonFeatures: args[SshnpArg.discoverDaemonFeaturesArg.name], ); } diff --git a/packages/noports_core/lib/src/sshnp/sshnp_core.dart b/packages/noports_core/lib/src/sshnp/sshnp_core.dart index fab929f64..eae394ca5 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_core.dart +++ b/packages/noports_core/lib/src/sshnp/sshnp_core.dart @@ -85,21 +85,30 @@ abstract class SshnpCore /// Start the sshnpd payload handler await sshnpdChannel.callInitialization(); - late Map pingResponse; - try { - pingResponse = await sshnpdChannel.ping().timeout(Duration(seconds: 10)); - } catch (e) { - logger.severe( - 'No ping response from ${params.device}${params.sshnpdAtSign}'); - rethrow; - } - - final daemonFeatures = pingResponse['supportedFeatures']; - if (daemonFeatures[DaemonFeatures.srAuth.name] != true) { - params.authenticateDeviceToRvd = false; - } - if (daemonFeatures[DaemonFeatures.srE2ee.name] != true) { - params.encryptRvdTraffic = false; + if (params.discoverDaemonFeatures) { + late Map pingResponse; + try { + pingResponse = + await sshnpdChannel.ping().timeout(Duration(seconds: 10)); + } catch (e) { + logger.severe( + 'No ping response from ${params.device}${params.sshnpdAtSign}'); + rethrow; + } + + final daemonFeatures = pingResponse['supportedFeatures']; + if ((daemonFeatures[DaemonFeatures.srAuth.name] != true) && + (params.authenticateDeviceToRvd == true)) { + throw ArgumentError('This device daemon does not support' + ' authentication to the socket rendezvous.' + ' Please set --no-authenticate-device'); + } + if ((daemonFeatures[DaemonFeatures.srE2ee.name] != true) && + (params.encryptRvdTraffic == true)) { + throw ArgumentError('This device daemon does not support' + ' encryption of traffic to the socket rendezvous.' + ' Please set --no-encrypt-rvd-traffic'); + } } /// Set the remote username to use for the ssh session From a464972501dfc3fcf50607619eac2a5e432f6150 Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 31 Dec 2023 13:35:23 +0000 Subject: [PATCH 26/60] chore: remove ephemeralEncryptionKeyPair from daemon; client will generate it instead, so that a ping to the daemon isn't required in order to share session encryption keys --- packages/noports_core/lib/src/sshnpd/sshnpd.dart | 8 -------- packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart | 7 ------- 2 files changed, 15 deletions(-) diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd.dart b/packages/noports_core/lib/src/sshnpd/sshnpd.dart index a5d64a5ee..a6ff10859 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; @@ -70,13 +69,6 @@ abstract class Sshnpd { /// - [SupportedSshAlgorithm.rsa] abstract final SupportedSshAlgorithm sshAlgorithm; - /// An encryption keypair which should only ever reside in memory. - /// The public key is provided in responses to client 'pings', and is - /// used by clients to encrypt symmetric encryption keys intended for - /// one-time use in a NoPorts session, and share the encrypted details - /// as part of the session request payload. - abstract final AtEncryptionKeyPair ephemeralEncryptionKeyPair; - static Future fromCommandLineArgs(List args, {AtClient? atClient, FutureOr Function(SshnpdParams)? atClientGenerator, diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 48381ce4f..3e09df71a 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:dartssh2/dartssh2.dart'; @@ -57,10 +56,6 @@ class SshnpdImpl implements Sshnpd { @override final SupportedSshAlgorithm sshAlgorithm; - @override - final AtEncryptionKeyPair ephemeralEncryptionKeyPair = - AtChopsUtil.generateAtEncryptionKeyPair(keySize: 2048); - @override @visibleForTesting bool initialized = false; @@ -310,8 +305,6 @@ class SshnpdImpl implements Sshnpd { var pingResponse = { 'devicename': device, 'version': packageVersion, - 'ephemeralPK': ephemeralEncryptionKeyPair.atPublicKey.publicKey, - 'ephemeralPKType': 'rsa2048', 'supportedFeatures': { DaemonFeatures.srAuth.name: true, DaemonFeatures.srE2ee.name: true, From 86be1736fdf69596b30c31fee8add7ac5025f752 Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 31 Dec 2023 13:49:44 +0000 Subject: [PATCH 27/60] feat: add encryptRvdTraffic flag and ephemeral public key to ssh session request message from client. --- .../src/sshnp/impl/notification_request_message.dart | 9 +++++++++ .../lib/src/sshnp/impl/sshnp_dart_pure_impl.dart | 3 +++ .../lib/src/sshnp/impl/sshnp_openssh_local_impl.dart | 3 +++ packages/noports_core/lib/src/sshnp/sshnp_core.dart | 11 +++++++++++ 4 files changed, 26 insertions(+) diff --git a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart index 021ee5072..e5ff735a3 100644 --- a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart @@ -6,6 +6,9 @@ class SshnpSessionRequest { final bool authenticateToRvd; final String clientNonce; final String rvdNonce; + final bool encryptRvdTraffic; + final String clientEphemeralPK; + final String clientEphemeralPKType; SshnpSessionRequest({ required this.direct, @@ -15,6 +18,9 @@ class SshnpSessionRequest { required this.authenticateToRvd, required this.clientNonce, required this.rvdNonce, + required this.encryptRvdTraffic, + required this.clientEphemeralPK, + required this.clientEphemeralPKType, }); Map toJson() => { @@ -25,5 +31,8 @@ class SshnpSessionRequest { 'authenticateToRvd': authenticateToRvd, 'clientNonce': clientNonce, 'rvdNonce': rvdNonce, + 'encryptRvdTraffic': encryptRvdTraffic, + 'clientEphemeralPK': clientEphemeralPK, + 'clientEphemeralPKType': clientEphemeralPKType, }; } diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index 3eee1749a..3978f2df0 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -72,6 +72,9 @@ class SshnpDartPureImpl extends SshnpCore authenticateToRvd: params.authenticateDeviceToRvd, clientNonce: sshrvdChannel.clientNonce, rvdNonce: sshrvdChannel.rvdNonce, + encryptRvdTraffic: params.encryptRvdTraffic, + clientEphemeralPK: clientEphemeralKeyPair.atPublicKey.publicKey, + clientEphemeralPKType: clientEphemeralKeyType.name, ).toJson()), checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index 1e5a29bf3..68cea067b 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -92,6 +92,9 @@ class SshnpOpensshLocalImpl extends SshnpCore authenticateToRvd: params.authenticateDeviceToRvd, clientNonce: sshrvdChannel.clientNonce, rvdNonce: sshrvdChannel.rvdNonce, + encryptRvdTraffic: params.encryptRvdTraffic, + clientEphemeralPK: clientEphemeralKeyPair.atPublicKey.publicKey, + clientEphemeralPKType: clientEphemeralKeyType.name, ).toJson()), checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, diff --git a/packages/noports_core/lib/src/sshnp/sshnp_core.dart b/packages/noports_core/lib/src/sshnp/sshnp_core.dart index eae394ca5..841433f2d 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_core.dart +++ b/packages/noports_core/lib/src/sshnp/sshnp_core.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; @@ -61,6 +62,16 @@ abstract class SshnpCore @protected SshnpdChannel get sshnpdChannel; + /// An encryption keypair which should only ever reside in memory. + /// The public key is provided in responses to client 'pings', and is + /// used by clients to encrypt symmetric encryption keys intended for + /// one-time use in a NoPorts session, and share the encrypted details + /// as part of the session request payload. + final AtEncryptionKeyPair clientEphemeralKeyPair = + AtChopsUtil.generateAtEncryptionKeyPair(keySize: 2048); + + final EncryptionKeyType clientEphemeralKeyType = EncryptionKeyType.rsa2048; + SshnpCore({ required this.atClient, required this.params, From 19eb1133ff48d8d3dd66ed998454e995f53c900d Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 31 Dec 2023 15:42:01 +0000 Subject: [PATCH 28/60] feat: sshrv use AES key and IV, when supplied, for en/decryption --- .../noports_core/lib/src/common/types.dart | 15 +- .../impl/notification_request_message.dart | 6 +- .../sshnp/impl/sshnp_openssh_local_impl.dart | 11 +- .../src/sshnp/impl/sshnp_unsigned_impl.dart | 6 +- .../lib/src/sshnp/models/sshnp_params.dart | 8 +- .../sshnpd_default_channel.dart | 12 +- .../util/sshrvd_channel/sshrvd_channel.dart | 14 +- .../lib/src/sshnpd/sshnpd_impl.dart | 42 ++++-- .../noports_core/lib/src/sshrv/sshrv.dart | 16 ++ .../lib/src/sshrv/sshrv_impl.dart | 141 ++++++++---------- .../test/sshnp/sshnp_core_test.dart | 2 + .../sshnp_local_ssh_key_handler_test.dart | 2 + .../sshnpd_default_channel_test.dart | 4 + .../sshrvd_channel/sshrvd_channel_mocks.dart | 2 + .../sshrvd_channel/sshrvd_channel_test.dart | 2 + packages/sshnoports/bin/sshrv.dart | 22 ++- 16 files changed, 196 insertions(+), 109 deletions(-) diff --git a/packages/noports_core/lib/src/common/types.dart b/packages/noports_core/lib/src/common/types.dart index 3eb1c0a77..0703c9356 100644 --- a/packages/noports_core/lib/src/common/types.dart +++ b/packages/noports_core/lib/src/common/types.dart @@ -1,15 +1,21 @@ import 'package:noports_core/sshrv.dart'; -typedef SshrvGenerator = Sshrv Function(String, int, - {required int localPort, - required bool bindLocalPort, - String? rvdAuthString}); +typedef SshrvGenerator = Sshrv Function( + String, + int, { + required int localPort, + required bool bindLocalPort, + String? rvdAuthString, + String? sessionAESKeyString, + String? sessionIVString, +}); enum SupportedSshClient { openssh(cliArg: 'openssh'), dart(cliArg: 'dart'); final String _cliArg; + const SupportedSshClient({required String cliArg}) : _cliArg = cliArg; factory SupportedSshClient.fromString(String cliArg) { @@ -28,6 +34,7 @@ enum SupportedSshAlgorithm { rsa(cliArg: 'ssh-rsa'); final String _cliArg; + const SupportedSshAlgorithm({required String cliArg}) : _cliArg = cliArg; factory SupportedSshAlgorithm.fromString(String cliArg) { diff --git a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart index e5ff735a3..ba1b38d9e 100644 --- a/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart +++ b/packages/noports_core/lib/src/sshnp/impl/notification_request_message.dart @@ -5,10 +5,10 @@ class SshnpSessionRequest { final int port; final bool authenticateToRvd; final String clientNonce; - final String rvdNonce; + final String? rvdNonce; final bool encryptRvdTraffic; - final String clientEphemeralPK; - final String clientEphemeralPKType; + final String? clientEphemeralPK; + final String? clientEphemeralPKType; SshnpSessionRequest({ required this.direct, diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index 68cea067b..aba3676bf 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -71,9 +71,6 @@ class SshnpOpensshLocalImpl extends SshnpCore int localRvPort = server.port; await server.close(); - /// Start sshrv - await sshrvdChannel.runSshrv(directSsh: true, localRvPort: localRvPort); - /// Send an ssh request to sshnpd await notify( AtKey() @@ -112,6 +109,14 @@ class SshnpOpensshLocalImpl extends SshnpCore ); } + /// Start sshrv + await sshrvdChannel.runSshrv( + directSsh: true, + localRvPort: localRvPort, + sessionAESKeyString: sshnpdChannel.sessionAESKeyString, + sessionIVString: sshnpdChannel.sessionIVString, + ); + /// Load the ephemeral private key into a key pair AtSshKeyPair ephemeralKeyPair = AtSshKeyPair.fromPem( sshnpdChannel.ephemeralPrivateKey!, diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart index 6b9c0745c..874ff0333 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart @@ -75,9 +75,6 @@ class SshnpUnsignedImpl extends SshnpCore with SshnpLocalSshKeyHandler { /// Ensure that sshnp is initialized await callInitialization(); - /// Start sshrv - var bean = await sshrvdChannel.runSshrv(directSsh: false); - /// Send an sshd request to sshnpd /// This will notify it that it can now connect to us await notify( @@ -98,6 +95,9 @@ class SshnpUnsignedImpl extends SshnpCore with SshnpLocalSshKeyHandler { throw SshnpError('sshnpd did not acknowledge the request'); } + /// Start sshrv + var bean = await sshrvdChannel.runSshrv(directSsh: false); + /// Ensure that we clean up after ourselves await callDisposal(); diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index a019e9b91..514f75e27 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -362,8 +362,12 @@ class SshnpPartialParams { factory SshnpPartialParams.fromArgMap(Map args) { return SshnpPartialParams( profileName: args[SshnpArg.profileNameArg.name], - clientAtSign: AtUtils.fixAtSign(args[SshnpArg.fromArg.name]), - sshnpdAtSign: AtUtils.fixAtSign(args[SshnpArg.toArg.name]), + clientAtSign: args[SshnpArg.fromArg.name] == null + ? null + : AtUtils.fixAtSign(args[SshnpArg.fromArg.name]), + sshnpdAtSign: args[SshnpArg.toArg.name] == null + ? null + : AtUtils.fixAtSign(args[SshnpArg.toArg.name]), host: args[SshnpArg.hostArg.name], device: args[SshnpArg.deviceArg.name], port: args[SshnpArg.portArg.name], diff --git a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart index 3444cc1e7..adeb24b97 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart @@ -18,6 +18,8 @@ class SshnpdDefaultChannel extends SshnpdChannel mixin SshnpdDefaultPayloadHandler on SshnpdChannel { String? ephemeralPrivateKey; + String? sessionAESKeyString; + String? sessionIVString; @visibleForTesting // disable publickey cache on windows @@ -66,8 +68,16 @@ mixin SshnpdDefaultPayloadHandler on SshnpdChannel { } logger.info('Verified signature of msg from ${params.sshnpdAtSign}'); - logger.info('Setting ephemeralPrivateKey'); + ephemeralPrivateKey = daemonResponse['ephemeralPrivateKey']; + logger.info('Received ephemeralPrivateKey: $ephemeralPrivateKey'); + + sessionAESKeyString = daemonResponse['sessionAESKey']; + logger.info('Received sessionAESKey: $sessionAESKeyString'); + + sessionIVString = daemonResponse['sessionIV']; + logger.info('Received sessionIV: $sessionIVString'); + return SshnpdAck.acknowledged; } return SshnpdAck.acknowledgedWithErrors; diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 08baae3d2..4fae14c7b 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -34,7 +34,6 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { final SshnpParams params; final String sessionId; final String clientNonce = DateTime.now().toIso8601String(); - late final String rvdNonce; // * Volatile fields which are set in [params] but may be overridden with // * values provided by sshrvd @@ -49,6 +48,10 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { // * Volatile fields set at runtime + String? rvdNonce; + String? sessionAESKeyString; + String? sessionIVString; + /// Whether sshrvd acknowledged our request @visibleForTesting SshrvdAck sshrvdAck = SshrvdAck.notAcknowledged; @@ -79,7 +82,12 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { completeInitialization(); } - Future runSshrv({required bool directSsh, int? localRvPort}) async { + Future runSshrv({ + required bool directSsh, + int? localRvPort, + String? sessionAESKeyString, + String? sessionIVString, + }) async { if (!directSsh && localRvPort != null) { throw Exception( 'localRvPort must be null when using reverseSsh (legacy)'); @@ -108,6 +116,8 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { 'rvdNonce': rvdNonce, }) : null, + sessionAESKeyString: sessionAESKeyString, + sessionIVString: sessionIVString, ); } else { // legacy behaviour diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 3e09df71a..abd29ed79 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:dartssh2/dartssh2.dart'; @@ -421,8 +422,6 @@ class SshnpdImpl implements Sshnpd { assertValidValue(params, 'remoteForwardPort', int); assertValidValue(params, 'privateKey', String); } - assertValidValue(params, 'clientNonce', String); - assertValidValue(params, 'rvdNonce', String); } catch (e) { logger.warning( 'Failed to extract parameters from notification value "${notification.value}" with error : $e'); @@ -449,6 +448,9 @@ class SshnpdImpl implements Sshnpd { authenticateToRvd: params['authenticateToRvd'], clientNonce: params['clientNonce'], rvdNonce: params['rvdNonce'], + encryptRvdTraffic: params['encryptRvdTraffic'], + clientEphemeralPK: params['clientEphemeralPK'], + clientEphemeralPKType: params['clientEphemeralPKType'], ); } else { // reverse ssh requested @@ -511,13 +513,18 @@ class SshnpdImpl implements Sshnpd { required String sessionId, required String host, required int port, - required bool authenticateToRvd, - required String clientNonce, - required String rvdNonce, + required bool? authenticateToRvd, + required String? clientNonce, + required String? rvdNonce, + required bool? encryptRvdTraffic, + required String? clientEphemeralPK, + required String? clientEphemeralPKType, }) async { logger.shout( 'Setting up ports for direct ssh session using ${sshClient.name} ($sshClient) from: $requestingAtsign session: $sessionId'); + authenticateToRvd ??= false; + encryptRvdTraffic ??= false; try { String? rvdAuthString; if (authenticateToRvd) { @@ -527,13 +534,26 @@ class SshnpdImpl implements Sshnpd { 'rvdNonce': rvdNonce, }); } + + String? sessionAESKey; + String? sessionIV; + if (encryptRvdTraffic) { + // 256-bit AES, 128-bit IV + sessionAESKey = + AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256).key; + sessionIV = base64Encode(AtChopsUtil.generateRandomIV(16).ivBytes); + } // Connect to rendezvous point using background process. // This program can then exit without causing an issue. - Process rv = await Sshrv.exec(host, port, - localPort: localSshdPort, - bindLocalPort: false, - rvdAuthString: rvdAuthString) - .run(); + Process rv = await Sshrv.exec( + host, + port, + localPort: localSshdPort, + bindLocalPort: false, + rvdAuthString: rvdAuthString, + sessionAESKeyString: sessionAESKey, + sessionIVString: sessionIV, + ).run(); logger.info('Started rv - pid is ${rv.pid}'); LocalSshKeyUtil keyUtil = LocalSshKeyUtil(); @@ -557,6 +577,8 @@ class SshnpdImpl implements Sshnpd { 'status': 'connected', 'sessionId': sessionId, 'ephemeralPrivateKey': keyPair.privateKeyContents, + 'sessionAESKey': sessionAESKey, + 'sessionIV': sessionIV, }), checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, diff --git a/packages/noports_core/lib/src/sshrv/sshrv.dart b/packages/noports_core/lib/src/sshrv/sshrv.dart index c93346bc4..56cb0750a 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv.dart @@ -14,8 +14,16 @@ abstract class Sshrv { /// Defaults to 22 abstract final int localPort; + /// A string which needs to be presented to the rvd before the rvd + /// will allow any further traffic on the socket abstract final String? rvdAuthString; + /// The AES key for encryption / decryption of the rv traffic + abstract final String? sessionAESKeyString; + + /// The IV to use with the [sessionAESKeyString] + abstract final String? sessionIVString; + abstract final bool bindLocalPort; Future run(); @@ -27,6 +35,8 @@ abstract class Sshrv { required int localPort, required bool bindLocalPort, String? rvdAuthString, + String? sessionAESKeyString, + String? sessionIVString, }) { return SshrvImplExec( host, @@ -34,6 +44,8 @@ abstract class Sshrv { localPort: localPort, bindLocalPort: bindLocalPort, rvdAuthString: rvdAuthString, + sessionAESKeyString: sessionAESKeyString, + sessionIVString: sessionIVString, ); } @@ -43,6 +55,8 @@ abstract class Sshrv { required int localPort, required bool bindLocalPort, String? rvdAuthString, + String? sessionAESKeyString, + String? sessionIVString, }) { return SshrvImplDart( host, @@ -50,6 +64,8 @@ abstract class Sshrv { localPort: localPort, bindLocalPort: bindLocalPort, rvdAuthString: rvdAuthString, + sessionAESKeyString: sessionAESKeyString, + sessionIVString: sessionIVString, ); } diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index 9238d877f..9518ad946 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'package:at_utils/at_utils.dart'; @@ -26,10 +27,26 @@ class SshrvImplExec implements Sshrv { @override final String? rvdAuthString; - SshrvImplExec(this.host, this.streamingPort, - {required this.localPort, - required this.bindLocalPort, - this.rvdAuthString}); + @override + final String? sessionAESKeyString; + + @override + final String? sessionIVString; + + SshrvImplExec( + this.host, + this.streamingPort, { + required this.localPort, + required this.bindLocalPort, + this.rvdAuthString, + this.sessionAESKeyString, + this.sessionIVString, + }) { + if ((sessionAESKeyString == null && sessionIVString != null) || + (sessionAESKeyString != null && sessionIVString == null)) { + throw ArgumentError('Both AES key and IV are required, or neither'); + } + } @override Future run() async { @@ -55,6 +72,12 @@ class SshrvImplExec implements Sshrv { if (rvdAuthString != null) { rvArgs.addAll(['--rvd-auth', rvdAuthString!]); } + if (sessionAESKeyString != null) { + rvArgs.addAll(['--aes-key', sessionAESKeyString!]); + } + if (sessionIVString != null) { + rvArgs.addAll(['--iv', sessionIVString!]); + } logger.info('$runtimeType.run(): executing $command' ' ${rvArgs.join(' ')}'); @@ -83,88 +106,56 @@ class SshrvImplDart implements Sshrv { @override final String? rvdAuthString; + @override + final String? sessionAESKeyString; + + @override + final String? sessionIVString; + SshrvImplDart( this.host, this.streamingPort, { required this.localPort, required this.bindLocalPort, this.rvdAuthString, - }); + this.sessionAESKeyString, + this.sessionIVString, + }) { + if ((sessionAESKeyString == null && sessionIVString != null) || + (sessionAESKeyString != null && sessionIVString == null)) { + throw ArgumentError('Both AES key and IV are required, or neither'); + } + } @override Future run() async { - final DartAesCtr algorithm = DartAesCtr.with256bits( - macAlgorithm: Hmac.sha256(), - ); - final secretKey = SecretKey([ - 157, - 145, - 46, - 127, - 146, - 161, - 7, - 96, - 13, - 29, - 150, - 203, - 109, - 252, - 110, - 92, - 24, - 55, - 113, - 121, - 94, - 91, - 69, - 63, - 159, - 162, - 107, - 49, - 250, - 118, - 191, - 113 - ]); - final iv = [ - 92, - 231, - 193, - 189, - 0, - 154, - 112, - 102, - 195, - 163, - 78, - 6, - 40, - 108, - 218, - 250 - ]; - - Stream> encrypter(Stream> stream) { - return algorithm.encryptStream( - stream, - secretKey: secretKey, - nonce: iv, - onMac: (mac) {}, - ); - } + DataTransformer? encrypter; + DataTransformer? decrypter; - Stream> decrypter(Stream> stream) { - return algorithm.decryptStream( - stream, - secretKey: secretKey, - nonce: iv, - mac: Mac.empty, + if (sessionAESKeyString != null && sessionIVString != null) { + final DartAesCtr algorithm = DartAesCtr.with256bits( + macAlgorithm: Hmac.sha256(), ); + final SecretKey sessionAESKey = + SecretKey(base64Decode(sessionAESKeyString!)); + final List sessionIV = base64Decode(sessionIVString!); + + encrypter = (Stream> stream) { + return algorithm.encryptStream( + stream, + secretKey: sessionAESKey, + nonce: sessionIV, + onMac: (mac) {}, + ); + }; + decrypter = (Stream> stream) { + return algorithm.decryptStream( + stream, + secretKey: sessionAESKey, + nonce: sessionIV, + mac: Mac.empty, + ); + }; } try { diff --git a/packages/noports_core/test/sshnp/sshnp_core_test.dart b/packages/noports_core/test/sshnp/sshnp_core_test.dart index a7726a33b..5804a6290 100644 --- a/packages/noports_core/test/sshnp/sshnp_core_test.dart +++ b/packages/noports_core/test/sshnp/sshnp_core_test.dart @@ -37,9 +37,11 @@ void main() { /// When declaration setup for the constructor of [StubbedSshnp] whenConstructor({bool verbose = false}) { + when(() => mockParams.sshnpdAtSign).thenReturn('@sshnpd'); when(() => mockParams.device).thenReturn('mydevice'); when(() => mockParams.localPort).thenReturn(0); when(() => mockParams.verbose).thenReturn(verbose); + when(() => mockParams.discoverDaemonFeatures).thenReturn(false); when(() => mockAtClient.getPreferences()).thenReturn(null); when(() => mockAtClient.setPreferences(any())).thenReturn(null); } diff --git a/packages/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart b/packages/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart index 95df64a6a..82f338948 100644 --- a/packages/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart +++ b/packages/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart @@ -28,9 +28,11 @@ void main() { }); whenConstructor() { + when(() => mockParams.sshnpdAtSign).thenReturn('@sshnpd'); when(() => mockParams.device).thenReturn('mydevice'); when(() => mockParams.localPort).thenReturn(0); when(() => mockParams.verbose).thenReturn(false); + when(() => mockParams.discoverDaemonFeatures).thenReturn(false); when(() => mockAtClient.getPreferences()).thenReturn(null); when(() => mockAtClient.setPreferences(any())).thenReturn(null); } diff --git a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_default_channel_test.dart b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_default_channel_test.dart index d342d7932..284009929 100644 --- a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_default_channel_test.dart +++ b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_default_channel_test.dart @@ -67,6 +67,10 @@ void main() { whenInitialization() { when(() => mockParams.sshnpdAtSign).thenReturn('@sshnpd'); + when(() => mockParams.authenticateDeviceToRvd).thenReturn(true); + when(() => mockParams.authenticateClientToRvd).thenReturn(true); + when(() => mockParams.encryptRvdTraffic).thenReturn(true); + when(() => mockParams.discoverDaemonFeatures).thenReturn(false); when(subscribeInvocation) .thenAnswer((_) => notificationStreamController.stream); } diff --git a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart index dc12ab691..66529e159 100644 --- a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart +++ b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart @@ -11,6 +11,8 @@ abstract class SshrvGeneratorCaller { required int localPort, required bool bindLocalPort, String? rvdAuthString, + String? sessionAESKeyString, + String? sessionIVString, }); } diff --git a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart index d75e7e2b7..deadaf08b 100644 --- a/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart +++ b/packages/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart @@ -109,6 +109,8 @@ void main() { when(() => mockParams.sshnpdAtSign).thenReturn('@sshnpd'); when(() => mockParams.authenticateDeviceToRvd).thenReturn(true); when(() => mockParams.authenticateClientToRvd).thenReturn(true); + when(() => mockParams.encryptRvdTraffic).thenReturn(true); + when(() => mockParams.discoverDaemonFeatures).thenReturn(false); when(subscribeInvocation) .thenAnswer((_) => notificationStreamController.stream); diff --git a/packages/sshnoports/bin/sshrv.dart b/packages/sshnoports/bin/sshrv.dart index 397795843..aa1946a73 100644 --- a/packages/sshnoports/bin/sshrv.dart +++ b/packages/sshnoports/bin/sshrv.dart @@ -13,7 +13,11 @@ Future main(List args) async { negatable: false, help: 'Set this flag when we are bridging from a local sender') ..addOption('rvd-auth', - mandatory: false, help: 'Auth string to provide to rvd'); + mandatory: false, help: 'Auth string to provide to rvd') + ..addOption('aes-key', + mandatory: false, help: 'AES key to use for session encryption') + ..addOption('iv', + mandatory: false, help: 'IV to use for session encryption'); final parsed = parser.parse(args); @@ -22,10 +26,16 @@ Future main(List args) async { final int localPort = int.parse(parsed['local-port']); final String? rvdAuthString = parsed['rvd-auth']; final bool bindLocalPort = parsed['bind-local-port']; + final String? sessionAESKeyString = parsed['aes-key']; + final String? sessionIVString = parsed['iv']; - await Sshrv.dart(host, streamingPort, - localPort: localPort, - bindLocalPort: bindLocalPort, - rvdAuthString: rvdAuthString) - .run(); + await Sshrv.dart( + host, + streamingPort, + localPort: localPort, + bindLocalPort: bindLocalPort, + rvdAuthString: rvdAuthString, + sessionAESKeyString: sessionAESKeyString, + sessionIVString: sessionIVString, + ).run(); } From 82baf8a7a3dfc7b8585c1b5f65cff100faedd44b Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 31 Dec 2023 16:16:33 +0000 Subject: [PATCH 29/60] fix: Need to give the sshrv process time to start and bind to its port before starting the initial tunnel which uses that port --- .../lib/src/sshnp/impl/sshnp_openssh_local_impl.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index aba3676bf..ac90ae8c7 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -117,6 +117,8 @@ class SshnpOpensshLocalImpl extends SshnpCore sessionIVString: sshnpdChannel.sessionIVString, ); + await Future.delayed(Duration(seconds: 1)); + /// Load the ephemeral private key into a key pair AtSshKeyPair ephemeralKeyPair = AtSshKeyPair.fromPem( sshnpdChannel.ephemeralPrivateKey!, From 0dc78ca21f787da856a4240589fff488bdc1e993 Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 31 Dec 2023 16:34:15 +0000 Subject: [PATCH 30/60] fix: Add defensive code to SshnpDartPureImpl to un-set the new flags, as SshnpDartPureImpl does not yet support them. --- .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 19 +++++++++++++++++++ .../lib/src/sshnp/models/sshnp_params.dart | 10 ++++++---- .../lib/src/sshrv/sshrv_impl.dart | 2 +- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index 3978f2df0..c4213ab9d 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -12,6 +12,25 @@ class SshnpDartPureImpl extends SshnpCore required super.params, required AtSshKeyPair? identityKeyPair, }) { + // TODO Defensive code to prevent use of rvd auth and rv traffic encryption + // TODO until they have been properly implemented with an in-memory RV. + // TODO At that time, make these four params "final" again + if (params.discoverDaemonFeatures) { + logger.shout('$runtimeType: disabling discoverDaemonFeatures flag'); + params.discoverDaemonFeatures = false; + } + if (params.encryptRvdTraffic) { + logger.shout('$runtimeType: disabling encryptRvdTraffic flag'); + params.encryptRvdTraffic = false; + } + if (params.authenticateDeviceToRvd) { + logger.shout('$runtimeType: disabling authenticateDeviceToRvd flag'); + params.authenticateDeviceToRvd = false; + } + if (params.authenticateClientToRvd) { + logger.shout('$runtimeType: disabling authenticateClientToRvd flag'); + params.authenticateClientToRvd = false; + } this.identityKeyPair = identityKeyPair; _sshnpdChannel = SshnpdDefaultChannel( atClient: atClient, diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index 514f75e27..9a5790fd1 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -35,10 +35,12 @@ class SshnpParams { final bool addForwardsToTunnel; final String? atKeysFilePath; final SupportedSshAlgorithm sshAlgorithm; - final bool authenticateClientToRvd; - final bool authenticateDeviceToRvd; - final bool encryptRvdTraffic; - final bool discoverDaemonFeatures; + // TODO Once pure dart impl supports these flags then they can be + // TODO made "final" again + bool authenticateClientToRvd; + bool authenticateDeviceToRvd; + bool encryptRvdTraffic; + bool discoverDaemonFeatures; /// Special Arguments diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index 9518ad946..61248748b 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -79,7 +79,7 @@ class SshrvImplExec implements Sshrv { rvArgs.addAll(['--iv', sessionIVString!]); } - logger.info('$runtimeType.run(): executing $command' + logger.shout('$runtimeType.run(): executing $command' ' ${rvArgs.join(' ')}'); return Process.start( command, From 7a5ab635e0d791e23b9608098d1d2b421d4a3d4e Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 31 Dec 2023 16:52:17 +0000 Subject: [PATCH 31/60] build: update pubspec to use git branch for socket_connector instead of local relative path --- packages/noports_core/pubspec.yaml | 7 +++---- packages/sshnoports/pubspec.lock | 11 ++++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/noports_core/pubspec.yaml b/packages/noports_core/pubspec.yaml index c24cb62ca..f66f6d87d 100644 --- a/packages/noports_core/pubspec.yaml +++ b/packages/noports_core/pubspec.yaml @@ -26,10 +26,9 @@ dependencies: dependency_overrides: socket_connector: - path: /Users/gary/dev/atsign/repos/socket_connector -# git: -# url: https://github.com/gkc/socket_connector.git -# ref: socket-authenticator-option + git: + url: https://github.com/gkc/socket_connector.git + ref: socket-authenticator-option dev_dependencies: build_runner: ^2.4.6 diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index cd49dbf40..7a5d8903b 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -575,7 +575,7 @@ packages: path: "../noports_core" relative: true source: path - version: "5.0.2" + version: "5.1.0" openssh_ed25519: dependency: transitive description: @@ -713,11 +713,12 @@ packages: source: hosted version: "1.0.4" socket_connector: - dependency: "direct overridden" + dependency: transitive description: - path: "/Users/gary/dev/atsign/repos/socket_connector" - relative: false - source: path + name: socket_connector + sha256: "8a5b67ae79e232186caa166ae640fac2247db390d4f5d11ed8975f95527fd355" + url: "https://pub.dev" + source: hosted version: "1.0.11" source_map_stack_trace: dependency: transitive From b5ff4a11ccdf2d1e6299a600ba18fff942242aca Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 1 Jan 2024 15:18:10 +0000 Subject: [PATCH 32/60] feat: encrypt/decrypt aes key and IV for rvd traffic using session ephemeral keypair --- .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 4 +-- .../sshnp/impl/sshnp_openssh_local_impl.dart | 4 +-- .../lib/src/sshnp/models/sshnp_params.dart | 15 ++++++++ .../lib/src/sshnp/sshnp_core.dart | 11 ------ .../sshnpd_default_channel.dart | 22 +++++++++--- .../lib/src/sshnpd/sshnpd_impl.dart | 35 ++++++++++++++++--- packages/sshnoports/pubspec.lock | 13 +++---- 7 files changed, 75 insertions(+), 29 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index c4213ab9d..5948420b7 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -92,8 +92,8 @@ class SshnpDartPureImpl extends SshnpCore clientNonce: sshrvdChannel.clientNonce, rvdNonce: sshrvdChannel.rvdNonce, encryptRvdTraffic: params.encryptRvdTraffic, - clientEphemeralPK: clientEphemeralKeyPair.atPublicKey.publicKey, - clientEphemeralPKType: clientEphemeralKeyType.name, + clientEphemeralPK: params.sessionKP.atPublicKey.publicKey, + clientEphemeralPKType: params.sessionKPType.name, ).toJson()), checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index ac90ae8c7..a6caa44dd 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -90,8 +90,8 @@ class SshnpOpensshLocalImpl extends SshnpCore clientNonce: sshrvdChannel.clientNonce, rvdNonce: sshrvdChannel.rvdNonce, encryptRvdTraffic: params.encryptRvdTraffic, - clientEphemeralPK: clientEphemeralKeyPair.atPublicKey.publicKey, - clientEphemeralPKType: clientEphemeralKeyType.name, + clientEphemeralPK: params.sessionKP.atPublicKey.publicKey, + clientEphemeralPKType: params.sessionKPType.name, ).toJson()), checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index 9a5790fd1..6bcf24ac8 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:at_chops/at_chops.dart'; import 'package:at_utils/at_utils.dart'; import 'package:noports_core/src/common/types.dart'; import 'package:noports_core/src/sshnp/models/config_file_repository.dart'; @@ -50,6 +51,20 @@ class SshnpParams { /// Operation flags final bool listDevices; + /// An encryption keypair which should only ever reside in memory. + /// The public key is provided in responses to client 'pings', and is + /// used by clients to encrypt symmetric encryption keys intended for + /// one-time use in a NoPorts session, and share the encrypted details + /// as part of the session request payload. + AtEncryptionKeyPair get sessionKP { + _sessionKP ??= AtChopsUtil.generateAtEncryptionKeyPair(keySize: 2048); + return _sessionKP!; + } + + /// Generate the ephemeralKeyPair only on demand + AtEncryptionKeyPair? _sessionKP; + final EncryptionKeyType sessionKPType = EncryptionKeyType.rsa2048; + SshnpParams({ required this.clientAtSign, required this.sshnpdAtSign, diff --git a/packages/noports_core/lib/src/sshnp/sshnp_core.dart b/packages/noports_core/lib/src/sshnp/sshnp_core.dart index 841433f2d..eae394ca5 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_core.dart +++ b/packages/noports_core/lib/src/sshnp/sshnp_core.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; @@ -62,16 +61,6 @@ abstract class SshnpCore @protected SshnpdChannel get sshnpdChannel; - /// An encryption keypair which should only ever reside in memory. - /// The public key is provided in responses to client 'pings', and is - /// used by clients to encrypt symmetric encryption keys intended for - /// one-time use in a NoPorts session, and share the encrypted details - /// as part of the session request payload. - final AtEncryptionKeyPair clientEphemeralKeyPair = - AtChopsUtil.generateAtEncryptionKeyPair(keySize: 2048); - - final EncryptionKeyType clientEphemeralKeyType = EncryptionKeyType.rsa2048; - SshnpCore({ required this.atClient, required this.params, diff --git a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart index adeb24b97..28ff5c2a4 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:at_chops/at_chops.dart'; import 'package:at_client/at_client.dart'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/io_types.dart'; @@ -72,11 +73,24 @@ mixin SshnpdDefaultPayloadHandler on SshnpdChannel { ephemeralPrivateKey = daemonResponse['ephemeralPrivateKey']; logger.info('Received ephemeralPrivateKey: $ephemeralPrivateKey'); - sessionAESKeyString = daemonResponse['sessionAESKey']; - logger.info('Received sessionAESKey: $sessionAESKeyString'); + String? sessionAESKeyStringEncrypted = daemonResponse['sessionAESKey']; + logger.info( + 'Received encrypted sessionAESKey: $sessionAESKeyStringEncrypted'); - sessionIVString = daemonResponse['sessionIV']; - logger.info('Received sessionIV: $sessionIVString'); + String? sessionIVStringEncrypted = daemonResponse['sessionIV']; + logger.info('Received encrypted sessionIV: $sessionIVStringEncrypted'); + + if (sessionAESKeyStringEncrypted != null && + sessionIVStringEncrypted != null) { + AtChops atChops = + AtChopsImpl(AtChopsKeys.create(params.sessionKP, null)); + sessionAESKeyString = atChops + .decryptString(sessionAESKeyStringEncrypted, params.sessionKPType) + .result; + sessionIVString = atChops + .decryptString(sessionIVStringEncrypted, params.sessionKPType) + .result; + } return SshnpdAck.acknowledged; } diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index abd29ed79..e6ea34a4c 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -535,13 +535,40 @@ class SshnpdImpl implements Sshnpd { }); } - String? sessionAESKey; - String? sessionIV; + String? sessionAESKey, sessionAESKeyEncrypted; + String? sessionIV, sessionIVEncrypted; if (encryptRvdTraffic) { + if (clientEphemeralPK == null || clientEphemeralPKType == null) { + throw Exception( + 'encryptRvdTraffic was requested, but no client ephemeral public key / key type was provided'); + } // 256-bit AES, 128-bit IV sessionAESKey = AtChopsUtil.generateSymmetricKey(EncryptionKeyType.aes256).key; sessionIV = base64Encode(AtChopsUtil.generateRandomIV(16).ivBytes); + late EncryptionKeyType ect; + try { + ect = EncryptionKeyType.values.byName(clientEphemeralPKType); + } catch (e) { + throw Exception('Unknown ephemeralPKType: $clientEphemeralPKType'); + } + switch (ect) { + case EncryptionKeyType.rsa2048: + AtChops ac = AtChopsImpl(AtChopsKeys.create( + AtEncryptionKeyPair.create(clientEphemeralPK, 'n/a'), null)); + sessionAESKeyEncrypted = ac + .encryptString(sessionAESKey, + EncryptionKeyType.values.byName(clientEphemeralPKType)) + .result; + sessionIVEncrypted = ac + .encryptString(sessionIV, + EncryptionKeyType.values.byName(clientEphemeralPKType)) + .result; + break; + default: + throw Exception( + 'No handling for ephemeralPKType $clientEphemeralPKType'); + } } // Connect to rendezvous point using background process. // This program can then exit without causing an issue. @@ -577,8 +604,8 @@ class SshnpdImpl implements Sshnpd { 'status': 'connected', 'sessionId': sessionId, 'ephemeralPrivateKey': keyPair.privateKeyContents, - 'sessionAESKey': sessionAESKey, - 'sessionIV': sessionIV, + 'sessionAESKey': sessionAESKeyEncrypted, + 'sessionIV': sessionIVEncrypted, }), checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index 7a5d8903b..61ea2ea82 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -713,13 +713,14 @@ packages: source: hosted version: "1.0.4" socket_connector: - dependency: transitive + dependency: "direct overridden" description: - name: socket_connector - sha256: "8a5b67ae79e232186caa166ae640fac2247db390d4f5d11ed8975f95527fd355" - url: "https://pub.dev" - source: hosted - version: "1.0.11" + path: "." + ref: socket-authenticator-option + resolved-ref: d7233335ccf0eee4721fc0eff9f1204c3188bba0 + url: "https://github.com/gkc/socket_connector.git" + source: git + version: "1.1.0" source_map_stack_trace: dependency: transitive description: From 469ce1d58acf93bc16b39d26a7143cadaaabe46b Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 1 Jan 2024 19:54:20 +0000 Subject: [PATCH 33/60] refactor: streamified socket_connector --- .../lib/src/sshnpd/sshnpd_params.dart | 3 +- .../lib/src/sshrv/sshrv_impl.dart | 19 +-- ...nature_verifying_socket_authenticator.dart | 113 ++++++++++-------- .../lib/src/sshrvd/socket_connector.dart | 9 +- .../test/sshnp/models/sshnp_params_test.dart | 18 ++- .../sshnpd_channel/sshnpd_channel_test.dart | 3 +- ...e_verifying_socket_authenticator_test.dart | 74 ++++++++++-- packages/sshnoports/bin/sshnp.dart | 3 + packages/sshnoports/bin/sshrv.dart | 12 +- packages/sshnoports/pubspec.lock | 10 +- packages/sshnoports/pubspec.yaml | 6 + 11 files changed, 188 insertions(+), 82 deletions(-) diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_params.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_params.dart index 1b46028e1..971fc5b1a 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_params.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_params.dart @@ -177,7 +177,8 @@ class SshnpdParams { parser.addOption( 'storage-path', mandatory: false, - help: r'Directory for local storage. Defaults to $HOME/.sshnp/${atSign}/storage', + help: + r'Directory for local storage. Defaults to $HOME/.sshnp/${atSign}/storage', ); return parser; diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index 61248748b..483e0897c 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -168,23 +168,28 @@ class SshrvImplDart implements Sshrv { receiverSocketAddress: hosts[0], receiverSocketPort: streamingPort, localServerPort: localPort, - verbose: true, + verbose: false, transformAtoB: encrypter, transformBtoA: decrypter); + if (rvdAuthString != null) { + stderr.writeln('authenticating socketB'); + socketConnector.authenticatedUnpairedReceivers.first.socket + .writeln(rvdAuthString); + } } else { socketConnector = await SocketConnector.socketToSocket( socketAddressA: InternetAddress.loopbackIPv4, socketPortA: localPort, socketAddressB: hosts[0], socketPortB: streamingPort, - verbose: true, + verbose: false, transformAtoB: encrypter, transformBtoA: decrypter); - } - - if (rvdAuthString != null) { - stderr.writeln('authenticating socketB'); - socketConnector.socketB?.writeln(rvdAuthString); + if (rvdAuthString != null) { + stderr.writeln('authenticating socketB'); + socketConnector.establishedConnections.first.sideB.socket + .writeln(rvdAuthString); + } } return socketConnector; diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index 3623bb18b..6426b1215 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -1,8 +1,10 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; +import 'package:noports_core/src/sshrvd/socket_connector.dart'; import 'package:socket_connector/socket_connector.dart'; /// @@ -40,60 +42,75 @@ class SignatureAuthVerifier implements SocketAuthVerifier { this.tag, ); + AtSigningResult _verifySignature(AtSigningVerificationInput input) { + AtChopsKeys atChopsKeys = AtChopsKeys(); + AtChops atChops = AtChopsImpl(atChopsKeys); + return atChops.verify(input); + } + @override - (bool authenticated, Uint8List? unused) onData( - Uint8List data, Socket socket) { - try { - final message = String.fromCharCodes(data); - // Expected message to be the JSON format with the below structure: - // { - // "signature":"", - // "hashingAlgo":"", - // "signingAlgo":"", - // "payload":{} - // } - var envelope = jsonDecode(message); + Future<(bool, Stream?)> authenticate(Socket socket) async { + Completer<(bool, Stream?)> completer = Completer(); + bool authenticated = false; + StreamController sc = StreamController(); + logger.info('SignatureAuthVerifier $tag: starting listen'); + socket.listen((Uint8List data) { + if (authenticated) { + sc.add(data); + } else { + try { + final message = String.fromCharCodes(data); + logger.info('SignatureAuthVerifier $tag received data: $message'); + // Expected message to be the JSON format with the below structure: + // { + // "signature":"", + // "hashingAlgo":"", + // "signingAlgo":"", + // "payload":{} + // } + var envelope = jsonDecode(message); - final hashingAlgo = - HashingAlgoType.values.byName(envelope['hashingAlgo']); - final signingAlgo = - SigningAlgoType.values.byName(envelope['signingAlgo']); + final hashingAlgo = + HashingAlgoType.values.byName(envelope['hashingAlgo']); + final signingAlgo = + SigningAlgoType.values.byName(envelope['signingAlgo']); - var payload = envelope['payload']; - if (payload == null || payload is! Map) { - throw Exception( - 'Received an auth signature which does not include the payload'); - } - if (payload['rvdNonce'] != rvdNonce) { - throw Exception( - 'Received rvdNonce which does not match what is expected'); - } + var payload = envelope['payload']; + if (payload == null || payload is! Map) { + completer.completeError( + 'Received an auth signature which does not include the payload'); + return; + } + if (payload['rvdNonce'] != rvdNonce) { + completer.completeError( + 'Received rvdNonce which does not match what is expected'); + return; + } - AtSigningVerificationInput input = AtSigningVerificationInput( - dataToVerify, base64Decode(envelope['signature']), publicKey) - ..signingAlgorithm = DefaultSigningAlgo(null, hashingAlgo) - ..signingMode = AtSigningMode.data - ..signingAlgoType = signingAlgo - ..hashingAlgoType = hashingAlgo; + AtSigningVerificationInput input = AtSigningVerificationInput( + dataToVerify, base64Decode(envelope['signature']), publicKey) + ..signingAlgorithm = DefaultSigningAlgo(null, hashingAlgo) + ..signingMode = AtSigningMode.data + ..signingAlgoType = signingAlgo + ..hashingAlgoType = hashingAlgo; - AtSigningResult atSigningResult = _verifySignature(input); - print('Signing verification outcome is: ${atSigningResult.result}'); - bool result = atSigningResult.result; + AtSigningResult atSigningResult = _verifySignature(input); + print('Signing verification outcome is: ${atSigningResult.result}'); + bool result = atSigningResult.result; - if (result == false) { - throw Exception( - 'Signature verification failed. Signatures did not match.'); - } - } catch (e) { - stderr.writeln('Error during socket authentication: $e'); - throw Exception(e); - } - return (true, null); - } + if (result == false) { + completer.completeError( + 'Signature verification failed. Signatures did not match.'); + return; + } - AtSigningResult _verifySignature(AtSigningVerificationInput input) { - AtChopsKeys atChopsKeys = AtChopsKeys(); - AtChops atChops = AtChopsImpl(atChopsKeys); - return atChops.verify(input); + authenticated = true; + completer.complete((true, sc.stream)); + } catch (e) { + completer.completeError('Error during socket authentication: $e'); + } + } + }); + return completer.future; } } diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index 10db50620..f582afa9c 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -81,7 +81,7 @@ void socketConnector(ConnectorParams connectorParams) async { } /// Create the socket connector - SocketConnector socketStream = await SocketConnector.serverToServer( + SocketConnector connector = await SocketConnector.serverToServer( serverAddressA: InternetAddress.anyIPv4, serverAddressB: InternetAddress.anyIPv4, serverPortA: portA, @@ -91,8 +91,8 @@ void socketConnector(ConnectorParams connectorParams) async { socketAuthVerifierB: socketAuthVerifierB); /// Get the assigned ports from the socket connector - portA = socketStream.senderPort()!; - portB = socketStream.receiverPort()!; + portA = connector.senderPort()!; + portB = connector.receiverPort()!; logger.info('Assigned ports [$portA, $portB]' ' for session ${sshrvdSessionParams.sessionId}'); @@ -103,7 +103,8 @@ void socketConnector(ConnectorParams connectorParams) async { /// Shut myself down once the socket connector closes bool closed = false; while (closed == false) { - closed = await socketStream.closed(); + logger.info('Waiting for connector to close'); + closed = await connector.closed(); } logger.info('Finished session ${sshrvdSessionParams.sessionId}' diff --git a/packages/noports_core/test/sshnp/models/sshnp_params_test.dart b/packages/noports_core/test/sshnp/models/sshnp_params_test.dart index 8fdbb1e7f..7382d09ac 100644 --- a/packages/noports_core/test/sshnp/models/sshnp_params_test.dart +++ b/packages/noports_core/test/sshnp/models/sshnp_params_test.dart @@ -426,8 +426,10 @@ void main() { final parsedParams = SshnpParams.fromConfigLines('myProfile', configLines); expect(parsedParams.profileName, equals('myProfile')); - expect(parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); - expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); + expect( + parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect( + parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(parsedParams.host, equals('@myHost')); expect(parsedParams.device, equals('myDeviceName')); expect(parsedParams.port, equals(1234)); @@ -518,8 +520,10 @@ void main() { ); final json = params.toJson(); final parsedParams = SshnpParams.fromJson(json); - expect(parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); - expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); + expect( + parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect( + parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(parsedParams.host, equals('@myHost')); expect(parsedParams.device, equals('myDeviceName')); expect(parsedParams.port, equals(1234)); @@ -822,8 +826,10 @@ void main() { final parsedParams = SshnpPartialParams.fromConfigLines('myProfile', configLines); expect(parsedParams.profileName, equals('myProfile')); - expect(parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); - expect(parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); + expect( + parsedParams.clientAtSign, equals('@myClientAtSign'.toLowerCase())); + expect( + parsedParams.sshnpdAtSign, equals('@mySshnpdAtSign'.toLowerCase())); expect(parsedParams.host, equals('@myHost')); expect(parsedParams.device, equals('myDeviceName')); expect(parsedParams.port, equals(1234)); diff --git a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart index 6d7a59490..9472f5759 100644 --- a/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart +++ b/packages/noports_core/test/sshnp/util/sshnpd_channel/sshnpd_channel_test.dart @@ -173,7 +173,8 @@ void main() { when(payloadInvocation) .thenAnswer((_) async => SshnpdAck.notAcknowledged); - Future ack = stubbedSshnpdChannel.waitForDaemonResponse(maxWaitMillis: 300); + Future ack = + stubbedSshnpdChannel.waitForDaemonResponse(maxWaitMillis: 300); // manually add a notification to the stream final String notificationId = Uuid().v4(); diff --git a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart index 413081e03..115bf31e9 100644 --- a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart +++ b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; @@ -17,10 +18,13 @@ void main() { atChops = AtChopsImpl(AtChopsKeys.create(encryptionKeyPair, null)); }); test('SignatureVerifyingSocketAuthenticator signature verification success', - () { + () async { String rvdSessionNonce = DateTime.now().toIso8601String(); Map payload = {'sessionId': Uuid().v4(), 'rvdNonce': rvdSessionNonce}; + late Function(Uint8List data) socketOnDataFn; + MockSocket mockSocket = MockSocket(); + String signedEnvelope = signPayload(atChops, payload); SignatureAuthVerifier sa = SignatureAuthVerifier( atChops.atChopsKeys.atEncryptionKeyPair!.atPublicKey.publicKey, @@ -31,16 +35,26 @@ void main() { List list = utf8.encode(signedEnvelope); Uint8List data = Uint8List.fromList(list); + when(() => mockSocket.listen(any(), + onError: any(named: "onError"), + onDone: any(named: "onDone"))).thenAnswer((Invocation invocation) { + socketOnDataFn = invocation.positionalArguments[0]; + + socketOnDataFn(data); + + return MockStreamSubscription(); + }); + bool authenticated; - Uint8List? unused; + Stream? stream; - (authenticated, unused) = sa.onData(data, MockSocket()); + (authenticated, stream) = await sa.authenticate(mockSocket); expect(authenticated, true); - expect(unused, null); + expect(stream, isNotNull); }); test('SignatureVerifyingSocketAuthenticator signature verification failure', - () { + () async { String rvdSessionNonce = DateTime.now().toIso8601String(); Map payload = { 'sessionId': Uuid().v4().toString(), @@ -58,12 +72,33 @@ void main() { List list = utf8.encode(signedEnvelope); Uint8List data = Uint8List.fromList(list); - expect(() => sa.onData(data, MockSocket()), throwsException); + late Function(Uint8List data) socketOnDataFn; + MockSocket mockSocket = MockSocket(); + + when(() => mockSocket.listen(any(), + onError: any(named: "onError"), + onDone: any(named: "onDone"))).thenAnswer((Invocation invocation) { + socketOnDataFn = invocation.positionalArguments[0]; + + socketOnDataFn(data); + + return MockStreamSubscription(); + }); + + bool authenticated; + Stream? stream; + bool somethingThrown = false; + try { + (authenticated, stream) = await sa.authenticate(mockSocket); + } catch (_) { + somethingThrown = true; + } + expect(somethingThrown, true); }); test( 'SignatureVerifyingSocketAuthenticator signature verification ok but mismatched nonce', - () { + () async { final uuidString = Uuid().v4().toString(); String rvdSessionNonce = DateTime.now().toIso8601String(); Map payload = {'sessionId': uuidString, 'rvdNonce': rvdSessionNonce}; @@ -80,7 +115,28 @@ void main() { List list = utf8.encode(jsonEncode(fakedEnvelope)); Uint8List data = Uint8List.fromList(list); - expect(() => sa.onData(data, MockSocket()), throwsException); + late Function(Uint8List data) socketOnDataFn; + MockSocket mockSocket = MockSocket(); + + when(() => mockSocket.listen(any(), + onError: any(named: "onError"), + onDone: any(named: "onDone"))).thenAnswer((Invocation invocation) { + socketOnDataFn = invocation.positionalArguments[0]; + + socketOnDataFn(data); + + return MockStreamSubscription(); + }); + + bool authenticated; + Stream? stream; + bool somethingThrown = false; + try { + (authenticated, stream) = await sa.authenticate(mockSocket); + } catch (_) { + somethingThrown = true; + } + expect(somethingThrown, true); }); } @@ -119,3 +175,5 @@ bool verifySignature( } class MockSocket extends Mock implements Socket {} + +class MockStreamSubscription extends Mock implements StreamSubscription {} diff --git a/packages/sshnoports/bin/sshnp.dart b/packages/sshnoports/bin/sshnp.dart index 995770ee5..10c38df44 100644 --- a/packages/sshnoports/bin/sshnp.dart +++ b/packages/sshnoports/bin/sshnp.dart @@ -117,6 +117,9 @@ void main(List args) async { } on ArgumentError catch (error) { printUsage(error: error); exit(1); + } on FormatException catch (error) { + printUsage(error: error); + exit(1); } on SshnpError catch (error, stackTrace) { stderr.writeln(error.toString()); if (params?.verbose ?? true) { diff --git a/packages/sshnoports/bin/sshrv.dart b/packages/sshnoports/bin/sshrv.dart index aa1946a73..c9ee574bb 100644 --- a/packages/sshnoports/bin/sshrv.dart +++ b/packages/sshnoports/bin/sshrv.dart @@ -1,5 +1,8 @@ +import 'dart:io'; + import 'package:args/args.dart'; import 'package:noports_core/sshrv.dart'; +import 'package:socket_connector/socket_connector.dart'; Future main(List args) async { final ArgParser parser = ArgParser() @@ -29,7 +32,7 @@ Future main(List args) async { final String? sessionAESKeyString = parsed['aes-key']; final String? sessionIVString = parsed['iv']; - await Sshrv.dart( + SocketConnector connector = await Sshrv.dart( host, streamingPort, localPort: localPort, @@ -38,4 +41,11 @@ Future main(List args) async { sessionAESKeyString: sessionAESKeyString, sessionIVString: sessionIVString, ).run(); + + /// Shut myself down once the socket connector closes + stderr.writeln('Waiting for connector to close'); + await connector.closed(); + + stderr.writeln('Closed - exiting'); + exit(0); } diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index 61ea2ea82..d4dae9e0f 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -713,13 +713,11 @@ packages: source: hosted version: "1.0.4" socket_connector: - dependency: "direct overridden" + dependency: "direct main" description: - path: "." - ref: socket-authenticator-option - resolved-ref: d7233335ccf0eee4721fc0eff9f1204c3188bba0 - url: "https://github.com/gkc/socket_connector.git" - source: git + path: "../../../socket_connector" + relative: true + source: path version: "1.1.0" source_map_stack_trace: dependency: transitive diff --git a/packages/sshnoports/pubspec.yaml b/packages/sshnoports/pubspec.yaml index 760dfafe5..885ec0a7a 100644 --- a/packages/sshnoports/pubspec.yaml +++ b/packages/sshnoports/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: noports_core: 5.0.2 at_onboarding_cli: 1.4.1 args: 2.4.2 + socket_connector: ^1.0.11 dependency_overrides: # Pin the dependencies of noports_core @@ -25,6 +26,11 @@ dependency_overrides: ssh_key: 0.8.0 uuid: 3.0.7 version: 3.0.2 + socket_connector: + git: + url: https://github.com/gkc/socket_connector.git + ref: socket-authenticator-option + dev_dependencies: lints: ^3.0.0 test: ^1.25.0 From da447e63e0a119f097d0ba59e56b3fc995cda532 Mon Sep 17 00:00:00 2001 From: gkc Date: Tue, 2 Jan 2024 10:41:57 +0000 Subject: [PATCH 34/60] feat: limited info leakage by moving sshrv params from command line to env vars --- .../lib/src/sshrv/sshrv_impl.dart | 17 ++++++++------ .../lib/src/sshrvd/build_env.dart | 5 ++-- ...e_verifying_socket_authenticator_test.dart | 8 ++----- packages/sshnoports/bin/sshrv.dart | 23 +++++++++++-------- packages/sshnoports/lib/src/create_sshnp.dart | 4 ++++ packages/sshnoports/pubspec.lock | 8 ++++--- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart index 483e0897c..cab3b7d5b 100644 --- a/packages/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -69,22 +69,25 @@ class SshrvImplExec implements Sshrv { if (bindLocalPort) { rvArgs.add('--bind-local-port'); } + Map environment = {}; if (rvdAuthString != null) { - rvArgs.addAll(['--rvd-auth', rvdAuthString!]); + rvArgs.addAll(['--rv-auth']); + environment['RV_AUTH'] = rvdAuthString!; } - if (sessionAESKeyString != null) { - rvArgs.addAll(['--aes-key', sessionAESKeyString!]); - } - if (sessionIVString != null) { - rvArgs.addAll(['--iv', sessionIVString!]); + if (sessionAESKeyString != null && sessionIVString != null) { + rvArgs.addAll(['--rv-e2ee']); + environment['RV_AES'] = sessionAESKeyString!; + environment['RV_IV'] = sessionIVString!; } - logger.shout('$runtimeType.run(): executing $command' + logger.info('$runtimeType.run(): executing $command' ' ${rvArgs.join(' ')}'); return Process.start( command, rvArgs, mode: ProcessStartMode.detached, + includeParentEnvironment: true, + environment: environment, ); } } diff --git a/packages/noports_core/lib/src/sshrvd/build_env.dart b/packages/noports_core/lib/src/sshrvd/build_env.dart index 8f5d10f97..8e23ed89f 100644 --- a/packages/noports_core/lib/src/sshrvd/build_env.dart +++ b/packages/noports_core/lib/src/sshrvd/build_env.dart @@ -1,4 +1,5 @@ +import 'dart:io'; + class BuildEnv { - static final bool enableSnoop = true; - // bool.fromEnvironment('ENABLE_SNOOP', defaultValue: false); + static final bool enableSnoop = (Platform.environment['ENABLE_SNOOP'] ?? "false").toLowerCase() == 'true'; } diff --git a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart index 115bf31e9..7e4d52208 100644 --- a/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart +++ b/packages/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart @@ -85,11 +85,9 @@ void main() { return MockStreamSubscription(); }); - bool authenticated; - Stream? stream; bool somethingThrown = false; try { - (authenticated, stream) = await sa.authenticate(mockSocket); + await sa.authenticate(mockSocket); } catch (_) { somethingThrown = true; } @@ -128,11 +126,9 @@ void main() { return MockStreamSubscription(); }); - bool authenticated; - Stream? stream; bool somethingThrown = false; try { - (authenticated, stream) = await sa.authenticate(mockSocket); + await sa.authenticate(mockSocket); } catch (_) { somethingThrown = true; } diff --git a/packages/sshnoports/bin/sshrv.dart b/packages/sshnoports/bin/sshrv.dart index c9ee574bb..3cff67353 100644 --- a/packages/sshnoports/bin/sshrv.dart +++ b/packages/sshnoports/bin/sshrv.dart @@ -15,22 +15,25 @@ Future main(List args) async { defaultsTo: false, negatable: false, help: 'Set this flag when we are bridging from a local sender') - ..addOption('rvd-auth', - mandatory: false, help: 'Auth string to provide to rvd') - ..addOption('aes-key', - mandatory: false, help: 'AES key to use for session encryption') - ..addOption('iv', - mandatory: false, help: 'IV to use for session encryption'); - + ..addFlag('rv-auth', + defaultsTo: false, + help: 'Whether this rv process will authenticate to rvd') + ..addFlag('rv-e2ee', + defaultsTo: false, + help: 'Whether this rv process will encrypt/decrypt' + ' all rvd socket traffic'); final parsed = parser.parse(args); final String host = parsed['host']; final int streamingPort = int.parse(parsed['port']); final int localPort = int.parse(parsed['local-port']); - final String? rvdAuthString = parsed['rvd-auth']; final bool bindLocalPort = parsed['bind-local-port']; - final String? sessionAESKeyString = parsed['aes-key']; - final String? sessionIVString = parsed['iv']; + final bool rvAuth = parsed['rv-auth']; + final bool rvE2ee = parsed['rv-e2ee']; + + String? rvdAuthString = rvAuth ? Platform.environment['RV_AUTH'] : null; + String? sessionAESKeyString = rvE2ee ? Platform.environment['RV_AES'] : null; + String? sessionIVString = rvE2ee ? Platform.environment['RV_IV'] : null; SocketConnector connector = await Sshrv.dart( host, diff --git a/packages/sshnoports/lib/src/create_sshnp.dart b/packages/sshnoports/lib/src/create_sshnp.dart index a90317316..c65eb2837 100644 --- a/packages/sshnoports/lib/src/create_sshnp.dart +++ b/packages/sshnoports/lib/src/create_sshnp.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:noports_core/sshnp_foundation.dart'; import 'package:at_client/at_client.dart'; import 'package:sshnoports/src/extended_arg_parser.dart'; +import 'package:at_utils/at_logger.dart'; typedef AtClientGenerator = Future Function(SshnpParams params); @@ -15,6 +16,9 @@ Future createSshnp( }) async { atClient ??= await atClientGenerator?.call(params); + if (params.verbose) { + AtSignLogger.root_level = 'INFO'; + } if (atClient == null) { throw ArgumentError( 'atClient must be provided or atClientGenerator must be provided'); diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index d4dae9e0f..60dacb6cc 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -715,9 +715,11 @@ packages: socket_connector: dependency: "direct main" description: - path: "../../../socket_connector" - relative: true - source: path + path: "." + ref: socket-authenticator-option + resolved-ref: c46f112a4593c2747f9fefda0562197ce09ac858 + url: "https://github.com/gkc/socket_connector.git" + source: git version: "1.1.0" source_map_stack_trace: dependency: transitive From 8c4368e6d466ccad5d57c2fee31304f7f21b1e42 Mon Sep 17 00:00:00 2001 From: gkc Date: Tue, 2 Jan 2024 11:55:16 +0000 Subject: [PATCH 35/60] chore: pinned some dependencies feat: increased timeout for responses from sshnpd and sshrvd --- .../lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart | 2 +- .../lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart | 2 +- .../sshrvd/signature_verifying_socket_authenticator.dart | 2 +- packages/sshnoports/pubspec.lock | 6 +++--- packages/sshnoports/pubspec.yaml | 2 ++ 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart index 1ad7320e1..aabf7723b 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart @@ -89,7 +89,7 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { /// Wait until we've received an acknowledgement from the daemon. /// Returns true if the daemon acknowledged our request. /// Returns false if a timeout occurred. - Future waitForDaemonResponse({int maxWaitMillis = 10000}) async { + Future waitForDaemonResponse({int maxWaitMillis = 15000}) async { // Timer to timeout after 10 Secs or after the Ack of connected/Errors for (int counter = 1; counter <= 100; counter++) { if (counter % 20 == 0) { diff --git a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 4fae14c7b..dc86f9199 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -178,7 +178,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } await Future.delayed(Duration(milliseconds: 100)); counter++; - if (counter > 100) { + if (counter > 150) { logger.warning('Timed out waiting for sshrvd response'); throw ('Connection timeout to sshrvd $host service\nhint: make sure host is valid and online'); } diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index 6426b1215..80b58be31 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -110,7 +110,7 @@ class SignatureAuthVerifier implements SocketAuthVerifier { completer.completeError('Error during socket authentication: $e'); } } - }); + }, onError: (error) => sc.addError(error), onDone: () => sc.close()); return completer.future; } } diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index 60dacb6cc..89faf852c 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -34,7 +34,7 @@ packages: source: hosted version: "2.4.2" asn1lib: - dependency: transitive + dependency: "direct overridden" description: name: asn1lib sha256: b74e3842a52c61f8819a1ec8444b4de5419b41a7465e69d4aa681445377398b0 @@ -370,7 +370,7 @@ packages: source: hosted version: "0.3.10" encrypt: - dependency: transitive + dependency: "direct overridden" description: name: encrypt sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" @@ -717,7 +717,7 @@ packages: description: path: "." ref: socket-authenticator-option - resolved-ref: c46f112a4593c2747f9fefda0562197ce09ac858 + resolved-ref: "3a816acfefb17f1e0eae8333364f1dde46f87267" url: "https://github.com/gkc/socket_connector.git" source: git version: "1.1.0" diff --git a/packages/sshnoports/pubspec.yaml b/packages/sshnoports/pubspec.yaml index 885ec0a7a..1fe1a6706 100644 --- a/packages/sshnoports/pubspec.yaml +++ b/packages/sshnoports/pubspec.yaml @@ -19,6 +19,8 @@ dependency_overrides: at_client: 3.0.70 at_lookup: 3.0.41 at_utils: 3.0.15 + asn1lib: 1.4.1 + encrypt: 5.0.1 crypton: 2.1.0 dartssh2: 2.8.2 logging: 1.2.0 From 7b554602fc767eb338f2ff0ff1b0f4f12bf33542 Mon Sep 17 00:00:00 2001 From: gkc Date: Tue, 2 Jan 2024 12:33:09 +0000 Subject: [PATCH 36/60] chore: more logging cleanup --- ...nature_verifying_socket_authenticator.dart | 4 +++- .../lib/src/sshrvd/socket_connector.dart | 20 +++++++++++++------ .../noports_core/lib/src/sshrvd/sshrvd.dart | 1 + .../lib/src/sshrvd/sshrvd_impl.dart | 7 +++++++ packages/sshnoports/pubspec.lock | 2 +- 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index 80b58be31..bd7a93a24 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; -import 'package:noports_core/src/sshrvd/socket_connector.dart'; +import 'package:at_utils/at_logger.dart'; import 'package:socket_connector/socket_connector.dart'; /// @@ -23,6 +23,8 @@ import 'package:socket_connector/socket_connector.dart'; /// /// class SignatureAuthVerifier implements SocketAuthVerifier { + static final AtSignLogger logger = AtSignLogger('SignatureAuthVerifier'); + /// Public key of the signing algorithm used to sign the data String publicKey; diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index f582afa9c..35b1a0777 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -9,15 +9,14 @@ import 'signature_verifying_socket_authenticator.dart'; typedef ConnectorParams = ( SendPort, - int, - int, - String, - bool, + int, // portA + int, // portB + String, // session params + bool, // snoop + bool, // verbose ); typedef PortPair = (int, int); -final logger = AtSignLogger(' sshrvd / socket_connector '); - /// This function is meant to be run in a separate isolate /// It starts the socket connector, and sends back the assigned ports to the main isolate /// It then waits for socket connector to die before shutting itself down @@ -28,8 +27,17 @@ void socketConnector(ConnectorParams connectorParams) async { portB, sshrvdSessionParamsJsonString, snoop, + verbose, ) = connectorParams; + if (verbose) { + AtSignLogger.root_level = 'INFO'; + } else { + AtSignLogger.root_level = 'WARNING'; + } + + final logger = AtSignLogger(' sshrvd / socket_connector '); + SshrvdSessionParams sshrvdSessionParams = SshrvdSessionParams.fromJson(jsonDecode(sshrvdSessionParamsJsonString)); logger.info( diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd.dart b/packages/noports_core/lib/src/sshrvd/sshrvd.dart index 33f204861..ec42e7330 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd.dart @@ -17,6 +17,7 @@ abstract class Sshrvd { abstract final String managerAtsign; abstract final String ipAddress; abstract final bool snoop; + bool verbose = false; /// true once [init] has completed @visibleForTesting diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index 0d10109c0..6f43874b4 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -31,6 +31,8 @@ class SshrvdImpl implements Sshrvd { final String ipAddress; @override final bool snoop; + @override + bool verbose = false; @override @visibleForTesting @@ -46,6 +48,7 @@ class SshrvdImpl implements Sshrvd { required this.managerAtsign, required this.ipAddress, required this.snoop, + required this.verbose, }) { logger.hierarchicalLoggingEnabled = true; logger.logger.level = Level.SHOUT; @@ -81,6 +84,7 @@ class SshrvdImpl implements Sshrvd { managerAtsign: p.managerAtsign, ipAddress: p.ipAddress, snoop: p.snoop, + verbose: p.verbose, ); if (p.verbose) { @@ -140,6 +144,7 @@ class SshrvdImpl implements Sshrvd { 0, sessionParams, snoop, + verbose, ); var (portA, portB) = ports; logger.warning( @@ -182,6 +187,7 @@ class SshrvdImpl implements Sshrvd { int portB, SshrvdSessionParams sshrvdSessionParams, bool snoop, + bool verbose, ) async { /// Spawn an isolate and wait for it to send back the issued port numbers ReceivePort receivePort = ReceivePort(sshrvdSessionParams.sessionId); @@ -192,6 +198,7 @@ class SshrvdImpl implements Sshrvd { portB, jsonEncode(sshrvdSessionParams), BuildEnv.enableSnoop && snoop, + verbose, ); logger diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index 89faf852c..7cde347fb 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -717,7 +717,7 @@ packages: description: path: "." ref: socket-authenticator-option - resolved-ref: "3a816acfefb17f1e0eae8333364f1dde46f87267" + resolved-ref: e651a93b5b42cd70679b6c0891ad21b1d2b0eb09 url: "https://github.com/gkc/socket_connector.git" source: git version: "1.1.0" From a4056760dd044626bd47be55f76d13fd7a14ba2c Mon Sep 17 00:00:00 2001 From: gkc Date: Tue, 2 Jan 2024 12:34:59 +0000 Subject: [PATCH 37/60] chore: more logging cleanup --- .../src/sshrvd/signature_verifying_socket_authenticator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index bd7a93a24..0b1ea12e7 100644 --- a/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -97,7 +97,7 @@ class SignatureAuthVerifier implements SocketAuthVerifier { ..hashingAlgoType = hashingAlgo; AtSigningResult atSigningResult = _verifySignature(input); - print('Signing verification outcome is: ${atSigningResult.result}'); + logger.info('Signing verification outcome is: ${atSigningResult.result}'); bool result = atSigningResult.result; if (result == false) { From 5f7e093fd08dd3e8f8acf82ce7ed9a8779bf75a8 Mon Sep 17 00:00:00 2001 From: gkc Date: Tue, 2 Jan 2024 13:33:06 +0000 Subject: [PATCH 38/60] feat: supply verbose and logTraffic parameters separately to `SocketConnector.serverToServer` --- packages/noports_core/lib/src/sshrvd/socket_connector.dart | 3 ++- packages/sshnoports/pubspec.lock | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index 35b1a0777..f93c3c62a 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -94,7 +94,8 @@ void socketConnector(ConnectorParams connectorParams) async { serverAddressB: InternetAddress.anyIPv4, serverPortA: portA, serverPortB: portB, - verbose: snoop, + verbose: verbose, + logTraffic: snoop, socketAuthVerifierA: socketAuthVerifierA, socketAuthVerifierB: socketAuthVerifierB); diff --git a/packages/sshnoports/pubspec.lock b/packages/sshnoports/pubspec.lock index 7cde347fb..2ccdde3b7 100644 --- a/packages/sshnoports/pubspec.lock +++ b/packages/sshnoports/pubspec.lock @@ -717,7 +717,7 @@ packages: description: path: "." ref: socket-authenticator-option - resolved-ref: e651a93b5b42cd70679b6c0891ad21b1d2b0eb09 + resolved-ref: acc28c31e9ce028dcb16948f2b3ce7c9fcec3b78 url: "https://github.com/gkc/socket_connector.git" source: git version: "1.1.0" From 05e74880a7085a36987458fdfc86b12489a4d4b7 Mon Sep 17 00:00:00 2001 From: gkc Date: Tue, 2 Jan 2024 13:36:05 +0000 Subject: [PATCH 39/60] refactor: changed variable names --- .../lib/src/sshrvd/socket_connector.dart | 6 +++--- packages/noports_core/lib/src/sshrvd/sshrvd.dart | 2 +- .../noports_core/lib/src/sshrvd/sshrvd_impl.dart | 12 ++++++------ .../noports_core/lib/src/sshrvd/sshrvd_params.dart | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/noports_core/lib/src/sshrvd/socket_connector.dart index f93c3c62a..1e2e5acfd 100644 --- a/packages/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/noports_core/lib/src/sshrvd/socket_connector.dart @@ -12,7 +12,7 @@ typedef ConnectorParams = ( int, // portA int, // portB String, // session params - bool, // snoop + bool, // logTraffic bool, // verbose ); typedef PortPair = (int, int); @@ -26,7 +26,7 @@ void socketConnector(ConnectorParams connectorParams) async { portA, portB, sshrvdSessionParamsJsonString, - snoop, + logTraffic, verbose, ) = connectorParams; @@ -95,7 +95,7 @@ void socketConnector(ConnectorParams connectorParams) async { serverPortA: portA, serverPortB: portB, verbose: verbose, - logTraffic: snoop, + logTraffic: logTraffic, socketAuthVerifierA: socketAuthVerifierA, socketAuthVerifierB: socketAuthVerifierB); diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd.dart b/packages/noports_core/lib/src/sshrvd/sshrvd.dart index ec42e7330..7e0cdb64b 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd.dart @@ -16,7 +16,7 @@ abstract class Sshrvd { abstract final String atKeysFilePath; abstract final String managerAtsign; abstract final String ipAddress; - abstract final bool snoop; + abstract final bool logTraffic; bool verbose = false; /// true once [init] has completed diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart index 6f43874b4..9524c3319 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -30,7 +30,7 @@ class SshrvdImpl implements Sshrvd { @override final String ipAddress; @override - final bool snoop; + final bool logTraffic; @override bool verbose = false; @@ -47,7 +47,7 @@ class SshrvdImpl implements Sshrvd { required this.atKeysFilePath, required this.managerAtsign, required this.ipAddress, - required this.snoop, + required this.logTraffic, required this.verbose, }) { logger.hierarchicalLoggingEnabled = true; @@ -83,7 +83,7 @@ class SshrvdImpl implements Sshrvd { atKeysFilePath: p.atKeysFilePath, managerAtsign: p.managerAtsign, ipAddress: p.ipAddress, - snoop: p.snoop, + logTraffic: p.logTraffic, verbose: p.verbose, ); @@ -143,7 +143,7 @@ class SshrvdImpl implements Sshrvd { 0, 0, sessionParams, - snoop, + logTraffic, verbose, ); var (portA, portB) = ports; @@ -186,7 +186,7 @@ class SshrvdImpl implements Sshrvd { int portA, int portB, SshrvdSessionParams sshrvdSessionParams, - bool snoop, + bool logTraffic, bool verbose, ) async { /// Spawn an isolate and wait for it to send back the issued port numbers @@ -197,7 +197,7 @@ class SshrvdImpl implements Sshrvd { portA, portB, jsonEncode(sshrvdSessionParams), - BuildEnv.enableSnoop && snoop, + BuildEnv.enableSnoop && logTraffic, verbose, ); diff --git a/packages/noports_core/lib/src/sshrvd/sshrvd_params.dart b/packages/noports_core/lib/src/sshrvd/sshrvd_params.dart index 8cd7ba27a..81a677058 100644 --- a/packages/noports_core/lib/src/sshrvd/sshrvd_params.dart +++ b/packages/noports_core/lib/src/sshrvd/sshrvd_params.dart @@ -10,7 +10,7 @@ class SshrvdParams { final String managerAtsign; final String ipAddress; final bool verbose; - final bool snoop; + final bool logTraffic; final String rootDomain; // Non param variables @@ -24,7 +24,7 @@ class SshrvdParams { required this.managerAtsign, required this.ipAddress, required this.verbose, - required this.snoop, + required this.logTraffic, required this.rootDomain, }); @@ -44,7 +44,7 @@ class SshrvdParams { managerAtsign: r['manager'], ipAddress: r['ip'], verbose: r['verbose'], - snoop: BuildEnv.enableSnoop && r['snoop'], + logTraffic: BuildEnv.enableSnoop && r['snoop'], rootDomain: r['root-domain'], ); } @@ -90,7 +90,7 @@ class SshrvdParams { 'snoop', abbr: 's', defaultsTo: false, - help: 'Snoop on traffic passing through service', + help: 'Log traffic passing through service', ); } parser.addOption( From 1f3de793817482b4f1d4dd0e6c3ee16550c5a0d1 Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 14 Jan 2024 12:14:34 +0000 Subject: [PATCH 40/60] feat: incorporated latest socket_connector changes --- .../sshnp/impl/sshnp_openssh_local_impl.dart | 1 - .../lib/src/sshrv/sshrv_impl.dart | 18 +++++------ ...nature_verifying_socket_authenticator.dart | 4 +-- .../lib/src/sshrvd/socket_connector.dart | 18 +++++------ packages/dart/sshnoports/bin/sshrv.dart | 2 +- packages/dart/sshnoports/pubspec.lock | 32 +++++++++---------- packages/dart/sshnoports/pubspec.yaml | 10 +++--- 7 files changed, 42 insertions(+), 43 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index f0a6f159f..cb3b0c9bb 100644 --- a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:at_client/at_client.dart'; -import 'package:meta/meta.dart'; import 'package:noports_core/src/common/io_types.dart'; import 'package:noports_core/src/sshnp/impl/notification_request_message.dart'; import 'package:noports_core/src/sshnp/util/ephemeral_port_binder.dart'; diff --git a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart index cab3b7d5b..9775174d8 100644 --- a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -168,29 +168,29 @@ class SshrvImplDart implements Sshrv { if (bindLocalPort) { socketConnector = await SocketConnector.serverToSocket( - receiverSocketAddress: hosts[0], - receiverSocketPort: streamingPort, - localServerPort: localPort, + portA: localPort, + addressB: hosts[0], + portB: streamingPort, verbose: false, transformAtoB: encrypter, transformBtoA: decrypter); if (rvdAuthString != null) { stderr.writeln('authenticating socketB'); - socketConnector.authenticatedUnpairedReceivers.first.socket + socketConnector.pendingB.first.socket .writeln(rvdAuthString); } } else { socketConnector = await SocketConnector.socketToSocket( - socketAddressA: InternetAddress.loopbackIPv4, - socketPortA: localPort, - socketAddressB: hosts[0], - socketPortB: streamingPort, + addressA: InternetAddress.loopbackIPv4, + portA: localPort, + addressB: hosts[0], + portB: streamingPort, verbose: false, transformAtoB: encrypter, transformBtoA: decrypter); if (rvdAuthString != null) { stderr.writeln('authenticating socketB'); - socketConnector.establishedConnections.first.sideB.socket + socketConnector.connections.first.sideB.socket .writeln(rvdAuthString); } } diff --git a/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index 0b1ea12e7..d221276dd 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -5,7 +5,6 @@ import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; import 'package:at_utils/at_logger.dart'; -import 'package:socket_connector/socket_connector.dart'; /// /// Verifies signature of the data received over the socket using the same signing algorithm used to sign the data @@ -22,7 +21,7 @@ import 'package:socket_connector/socket_connector.dart'; /// also expects signature to be base64 encoded /// /// -class SignatureAuthVerifier implements SocketAuthVerifier { +class SignatureAuthVerifier { static final AtSignLogger logger = AtSignLogger('SignatureAuthVerifier'); /// Public key of the signing algorithm used to sign the data @@ -50,7 +49,6 @@ class SignatureAuthVerifier implements SocketAuthVerifier { return atChops.verify(input); } - @override Future<(bool, Stream?)> authenticate(Socket socket) async { Completer<(bool, Stream?)> completer = Completer(); bool authenticated = false; diff --git a/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart index 1e2e5acfd..493934cd1 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart @@ -66,7 +66,7 @@ void socketConnector(ConnectorParams connectorParams) async { jsonEncode(expectedPayloadForSignature), sshrvdSessionParams.rvdNonce!, sshrvdSessionParams.atSignA, - ); + ).authenticate; } SocketAuthVerifier? socketAuthVerifierB; @@ -85,23 +85,23 @@ void socketConnector(ConnectorParams connectorParams) async { jsonEncode(expectedPayloadForSignature), sshrvdSessionParams.rvdNonce!, sshrvdSessionParams.atSignB!, - ); + ).authenticate; } /// Create the socket connector SocketConnector connector = await SocketConnector.serverToServer( - serverAddressA: InternetAddress.anyIPv4, - serverAddressB: InternetAddress.anyIPv4, - serverPortA: portA, - serverPortB: portB, + addressA: InternetAddress.anyIPv4, + addressB: InternetAddress.anyIPv4, + portA: portA, + portB: portB, verbose: verbose, logTraffic: logTraffic, socketAuthVerifierA: socketAuthVerifierA, socketAuthVerifierB: socketAuthVerifierB); /// Get the assigned ports from the socket connector - portA = connector.senderPort()!; - portB = connector.receiverPort()!; + portA = connector.sideAPort!; + portB = connector.sideBPort!; logger.info('Assigned ports [$portA, $portB]' ' for session ${sshrvdSessionParams.sessionId}'); @@ -113,7 +113,7 @@ void socketConnector(ConnectorParams connectorParams) async { bool closed = false; while (closed == false) { logger.info('Waiting for connector to close'); - closed = await connector.closed(); + closed = await connector.done; } logger.info('Finished session ${sshrvdSessionParams.sessionId}' diff --git a/packages/dart/sshnoports/bin/sshrv.dart b/packages/dart/sshnoports/bin/sshrv.dart index 3cff67353..cd4056059 100644 --- a/packages/dart/sshnoports/bin/sshrv.dart +++ b/packages/dart/sshnoports/bin/sshrv.dart @@ -47,7 +47,7 @@ Future main(List args) async { /// Shut myself down once the socket connector closes stderr.writeln('Waiting for connector to close'); - await connector.closed(); + await connector.done; stderr.writeln('Closed - exiting'); exit(0); diff --git a/packages/dart/sshnoports/pubspec.lock b/packages/dart/sshnoports/pubspec.lock index f2df466fc..0ef235349 100644 --- a/packages/dart/sshnoports/pubspec.lock +++ b/packages/dart/sshnoports/pubspec.lock @@ -26,7 +26,7 @@ packages: source: hosted version: "3.3.9" args: - dependency: "direct overridden" + dependency: "direct main" description: name: args sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 @@ -34,7 +34,7 @@ packages: source: hosted version: "2.4.2" asn1lib: - dependency: transitive + dependency: "direct overridden" description: name: asn1lib sha256: b74e3842a52c61f8819a1ec8444b4de5419b41a7465e69d4aa681445377398b0 @@ -77,10 +77,10 @@ packages: dependency: "direct overridden" description: name: at_client - sha256: b25f991409efa860a6196399c20bbebb8e763695197996ea4013789b4b96f297 + sha256: "975da85cd85f6997569f562a198a46dc5eb1c51e74522cef9b432cceed436b59" url: "https://pub.dev" source: hosted - version: "3.0.68" + version: "3.0.70" at_commons: dependency: transitive description: @@ -370,7 +370,7 @@ packages: source: hosted version: "0.3.10" encrypt: - dependency: transitive + dependency: "direct overridden" description: name: encrypt sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" @@ -572,11 +572,10 @@ packages: noports_core: dependency: "direct main" description: - name: noports_core - sha256: a74b987dde3f5781bab4a4646a3c825d63b6c8147b9f6eeedc4fa5dd7ddb18d2 - url: "https://pub.dev" - source: hosted - version: "5.0.4" + path: "../noports_core" + relative: true + source: path + version: "5.1.0" openssh_ed25519: dependency: transitive description: @@ -714,13 +713,14 @@ packages: source: hosted version: "1.0.4" socket_connector: - dependency: "direct overridden" + dependency: "direct main" description: - name: socket_connector - sha256: "8a5b67ae79e232186caa166ae640fac2247db390d4f5d11ed8975f95527fd355" - url: "https://pub.dev" - source: hosted - version: "1.0.11" + path: "." + ref: socket-authenticator-option + resolved-ref: "51832705146a6ca60f189e7c97a68dc56e06fb9e" + url: "https://github.com/gkc/socket_connector.git" + source: git + version: "2.0.0" source_map_stack_trace: dependency: transitive description: diff --git a/packages/dart/sshnoports/pubspec.yaml b/packages/dart/sshnoports/pubspec.yaml index 5628fb7e6..867c7931b 100644 --- a/packages/dart/sshnoports/pubspec.yaml +++ b/packages/dart/sshnoports/pubspec.yaml @@ -13,6 +13,12 @@ dependencies: socket_connector: ^2.0.0 dependency_overrides: + socket_connector: + git: + url: https://github.com/gkc/socket_connector.git + ref: socket-authenticator-option + noports_core: + path: ../noports_core # Pin the dependencies of noports_core archive: 3.3.9 args: 2.4.2 @@ -28,10 +34,6 @@ dependency_overrides: ssh_key: 0.8.0 uuid: 3.0.7 version: 3.0.2 - socket_connector: - git: - url: https://github.com/gkc/socket_connector.git - ref: socket-authenticator-option dev_dependencies: lints: ^3.0.0 From 38deebc751ea46a148d0f5f726e668b004c0dc01 Mon Sep 17 00:00:00 2001 From: gkc Date: Sun, 14 Jan 2024 12:17:55 +0000 Subject: [PATCH 41/60] style: ran dart format --- .../lib/src/sshnp/impl/sshnp_openssh_local_impl.dart | 6 +++++- packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart | 6 ++---- packages/dart/noports_core/lib/src/sshrvd/build_env.dart | 3 ++- .../sshrvd/signature_verifying_socket_authenticator.dart | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index cb3b0c9bb..cb39ef6d7 100644 --- a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -7,7 +7,10 @@ import 'package:noports_core/src/sshnp/util/ephemeral_port_binder.dart'; import 'package:noports_core/sshnp_foundation.dart'; class SshnpOpensshLocalImpl extends SshnpCore - with SshnpLocalSshKeyHandler, OpensshSshSessionHandler, EphemeralPortBinder { + with + SshnpLocalSshKeyHandler, + OpensshSshSessionHandler, + EphemeralPortBinder { SshnpOpensshLocalImpl({ required super.atClient, required super.params, @@ -40,6 +43,7 @@ class SshnpOpensshLocalImpl extends SshnpCore await super.initialize(); completeInitialization(); } + @override Future run() async { /// Ensure that sshnp is initialized diff --git a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart index 9775174d8..2921f3227 100644 --- a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -176,8 +176,7 @@ class SshrvImplDart implements Sshrv { transformBtoA: decrypter); if (rvdAuthString != null) { stderr.writeln('authenticating socketB'); - socketConnector.pendingB.first.socket - .writeln(rvdAuthString); + socketConnector.pendingB.first.socket.writeln(rvdAuthString); } } else { socketConnector = await SocketConnector.socketToSocket( @@ -190,8 +189,7 @@ class SshrvImplDart implements Sshrv { transformBtoA: decrypter); if (rvdAuthString != null) { stderr.writeln('authenticating socketB'); - socketConnector.connections.first.sideB.socket - .writeln(rvdAuthString); + socketConnector.connections.first.sideB.socket.writeln(rvdAuthString); } } diff --git a/packages/dart/noports_core/lib/src/sshrvd/build_env.dart b/packages/dart/noports_core/lib/src/sshrvd/build_env.dart index 8e23ed89f..77050b528 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/build_env.dart +++ b/packages/dart/noports_core/lib/src/sshrvd/build_env.dart @@ -1,5 +1,6 @@ import 'dart:io'; class BuildEnv { - static final bool enableSnoop = (Platform.environment['ENABLE_SNOOP'] ?? "false").toLowerCase() == 'true'; + static final bool enableSnoop = + (Platform.environment['ENABLE_SNOOP'] ?? "false").toLowerCase() == 'true'; } diff --git a/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index d221276dd..ec3616fb7 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -95,7 +95,8 @@ class SignatureAuthVerifier { ..hashingAlgoType = hashingAlgo; AtSigningResult atSigningResult = _verifySignature(input); - logger.info('Signing verification outcome is: ${atSigningResult.result}'); + logger.info('Signing verification outcome is:' + ' ${atSigningResult.result}'); bool result = atSigningResult.result; if (result == false) { From ea61d76eaab2b2b35bd0a80d1e657266dfc1bc24 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 11:22:17 +0000 Subject: [PATCH 42/60] fix: handle empty string for tunnelUserName parameter as if it had been presented as `null` --- .../lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart index aabf7723b..b28f71652 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart @@ -177,7 +177,8 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { /// Otherwise, the username will be set to [remoteUsername] Future resolveTunnelUsername( {required String? remoteUsername}) async { - if (params.tunnelUsername != null) { + if (params.tunnelUsername != null && + params.tunnelUsername!.trim().isNotEmpty) { return params.tunnelUsername!; } else { return remoteUsername; From 281fd22171b06276af7d2bf189d47e30a7239720 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 11:25:10 +0000 Subject: [PATCH 43/60] feat: performance improvements 1. have SshrvdImpl fetch the public key of another atSign via the Sshrvd's own atClient 2. instead of waiting 1 second for a forked sshrv process to have started up, instead listen to its stderr and wait for the message that says "rv is running" --- .../sshnp/impl/sshnp_openssh_local_impl.dart | 2 - .../lib/src/sshrv/sshrv_impl.dart | 27 +++++- .../lib/src/sshrvd/socket_connector.dart | 7 +- .../lib/src/sshrvd/sshrvd_impl.dart | 52 +++------- packages/dart/sshnoports/bin/sshrv.dart | 3 + packages/dart/sshnoports/pubspec.lock | 96 ++++++++++--------- 6 files changed, 97 insertions(+), 90 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index cb39ef6d7..673d263c9 100644 --- a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -101,8 +101,6 @@ class SshnpOpensshLocalImpl extends SshnpCore sessionIVString: sshnpdChannel.sessionIVString, ); - await Future.delayed(Duration(seconds: 1)); - /// Load the ephemeral private key into a key pair AtSshKeyPair ephemeralKeyPair = AtSshKeyPair.fromPem( sshnpdChannel.ephemeralPrivateKey!, diff --git a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart index 2921f3227..f28db2d70 100644 --- a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -82,13 +83,35 @@ class SshrvImplExec implements Sshrv { logger.info('$runtimeType.run(): executing $command' ' ${rvArgs.join(' ')}'); - return Process.start( + Process p = await Process.start( command, rvArgs, - mode: ProcessStartMode.detached, + mode: ProcessStartMode.detachedWithStdio, includeParentEnvironment: true, environment: environment, ); + Completer rvPortBound = Completer(); + p.stdout.listen((List l) { + var s = utf8.decode(l).trim(); + stderr.writeln('rv stdout: $s'); + }, onError: (e) {}); + p.stderr.listen((List l) { + var allLines = utf8.decode(l).trim(); + for (String s in allLines.split('\n')) { + stderr.writeln('rv stderr: [$s]'); + if (s.endsWith('is running') && !rvPortBound.isCompleted) { + rvPortBound.complete(); + } + } + }, onError: (e) { + if (! rvPortBound.isCompleted) { + rvPortBound.completeError(e); + } + }); + + await rvPortBound.future.timeout(Duration(seconds:2)); + + return p; } } diff --git a/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart index 493934cd1..86ce80d00 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart @@ -110,11 +110,8 @@ void socketConnector(ConnectorParams connectorParams) async { sendPort.send((portA, portB)); /// Shut myself down once the socket connector closes - bool closed = false; - while (closed == false) { - logger.info('Waiting for connector to close'); - closed = await connector.done; - } + logger.info('Waiting for connector to close'); + await connector.done; logger.info('Finished session ${sshrvdSessionParams.sessionId}' ' for ${sshrvdSessionParams.atSignA} to ${sshrvdSessionParams.atSignB}' diff --git a/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart index 9524c3319..dbcfbcdf6 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'dart:isolate'; import 'dart:convert'; import 'package:at_client/at_client.dart'; -import 'package:at_lookup/at_lookup.dart'; import 'package:at_utils/at_logger.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; @@ -40,6 +39,7 @@ class SshrvdImpl implements Sshrvd { static final String subscriptionRegex = '${Sshrvd.namespace}@'; + late final SshrvdUtil sshrvdUtil; SshrvdImpl({ required this.atClient, required this.atSign, @@ -49,7 +49,9 @@ class SshrvdImpl implements Sshrvd { required this.ipAddress, required this.logTraffic, required this.verbose, + SshrvdUtil? sshrvdUtil }) { + this.sshrvdUtil = sshrvdUtil ?? SshrvdUtil(atClient); logger.hierarchicalLoggingEnabled = true; logger.logger.level = Level.SHOUT; } @@ -119,12 +121,12 @@ class SshrvdImpl implements Sshrvd { } void _notificationHandler(AtNotification notification) async { - if (!SshrvdUtil.accept(notification)) { + if (!sshrvdUtil.accept(notification)) { return; } late SshrvdSessionParams sessionParams; try { - sessionParams = await SshrvdUtil.getParams(notification); + sessionParams = await sshrvdUtil.getParams(notification); if (managerAtsign != 'open' && managerAtsign != sessionParams.atSignA) { logger.shout( @@ -269,11 +271,14 @@ class SshrvdSessionParams { } class SshrvdUtil { - static bool accept(AtNotification notification) { + final AtClient atClient; + SshrvdUtil(this.atClient); + + bool accept(AtNotification notification) { return notification.key.contains(Sshrvd.namespace); } - static Future getParams( + Future getParams( AtNotification notification) async { if (notification.key.contains('.request_ports.${Sshrvd.namespace}')) { return await _processJSONRequest(notification); @@ -281,7 +286,7 @@ class SshrvdUtil { return _processLegacyRequest(notification); } - static SshrvdSessionParams _processLegacyRequest( + SshrvdSessionParams _processLegacyRequest( AtNotification notification) { return SshrvdSessionParams( sessionId: notification.value!, @@ -289,7 +294,7 @@ class SshrvdUtil { ); } - static Future _processJSONRequest( + Future _processJSONRequest( AtNotification notification) async { dynamic jsonValue = jsonDecode(notification.value ?? ''); @@ -330,35 +335,8 @@ class SshrvdUtil { ); } - static Future _fetchPublicKey(String atSign, - {int secondsToWait = 10}) async { - String? publicKey; - AtLookupImpl atLookupImpl = AtLookupImpl(atSign, 'root.atsign.org', 64); - SecondaryAddress secondaryAddress = - await atLookupImpl.secondaryAddressFinder.findSecondary(atSign); - - SecureSocket secureSocket = await SecureSocket.connect( - secondaryAddress.host, secondaryAddress.port); - - secureSocket.listen((event) { - String serverResponse = utf8.decode(event); - if (serverResponse == '@') { - secureSocket.write('lookup:publickey$atSign\n'); - } else if (serverResponse.startsWith('data:')) { - publicKey = serverResponse.replaceFirst('data:', ''); - publicKey = publicKey?.substring(0, publicKey?.indexOf('\n')).trim(); - } - }); - - int totalSecondsWaited = 0; - while (totalSecondsWaited < secondsToWait) { - await Future.delayed(Duration(seconds: 1)); - totalSecondsWaited = totalSecondsWaited + 1; - if (publicKey != null) { - break; - } - } - await secureSocket.close(); - return publicKey; + Future _fetchPublicKey(String atSign) async { + AtValue v = await atClient.get(AtKey.fromString('public:publickey$atSign')); + return v.value; } } diff --git a/packages/dart/sshnoports/bin/sshrv.dart b/packages/dart/sshnoports/bin/sshrv.dart index cd4056059..dc8d6dc10 100644 --- a/packages/dart/sshnoports/bin/sshrv.dart +++ b/packages/dart/sshnoports/bin/sshrv.dart @@ -45,6 +45,9 @@ Future main(List args) async { sessionIVString: sessionIVString, ).run(); + // Do not change this output + stderr.writeln('rv is running'); + /// Shut myself down once the socket connector closes stderr.writeln('Waiting for connector to close'); await connector.done; diff --git a/packages/dart/sshnoports/pubspec.lock b/packages/dart/sshnoports/pubspec.lock index 0ef235349..ac36e55c0 100644 --- a/packages/dart/sshnoports/pubspec.lock +++ b/packages/dart/sshnoports/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "65.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.3.0" archive: dependency: "direct overridden" description: @@ -77,10 +77,10 @@ packages: dependency: "direct overridden" description: name: at_client - sha256: "975da85cd85f6997569f562a198a46dc5eb1c51e74522cef9b432cceed436b59" + sha256: "4904549d31a41d893e0df39636acb867b604aef7f2a3b40d9bb20e30b2e248d1" url: "https://pub.dev" source: hosted - version: "3.0.70" + version: "3.0.71" at_commons: dependency: transitive description: @@ -181,18 +181,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_runner: dependency: "direct dev" description: @@ -229,18 +229,18 @@ packages: dependency: transitive description: name: built_value - sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 + sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 url: "https://pub.dev" source: hosted - version: "8.6.3" + version: "8.8.1" chalkdart: dependency: transitive description: name: chalkdart - sha256: "5fceaf0eb65cb3a0746423aad4b285b9ec123133b9435630ac027f305b21e8b5" + sha256: "0b7ec5c6a6bafd1445500632c00c573722bd7736e491675d4ac3fe560bbd9cfe" url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.2.1" charcode: dependency: transitive description: @@ -269,10 +269,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.10.0" collection: dependency: transitive description: @@ -293,10 +293,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.2" cron: dependency: transitive description: @@ -341,10 +341,10 @@ packages: dependency: transitive description: name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" dartssh2: dependency: "direct overridden" description: @@ -437,10 +437,10 @@ packages: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" http_multi_server: dependency: transitive description: @@ -461,10 +461,10 @@ packages: dependency: transitive description: name: image - sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" + sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" url: "https://pub.dev" source: hosted - version: "4.1.3" + version: "4.1.4" internet_connection_checker: dependency: transitive description: @@ -549,10 +549,10 @@ packages: dependency: transitive description: name: mutex - sha256: "03116a4e46282a671b46c12de649d72c0ed18188ffe12a8d0fc63e83f4ad88f4" + sha256: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.0" ninja_asn1: dependency: transitive description: @@ -596,18 +596,18 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" petitparser: dependency: transitive description: name: petitparser - sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.0.2" pinenacl: dependency: transitive description: @@ -620,18 +620,18 @@ packages: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.4" pointycastle: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.7.4" pool: dependency: transitive description: @@ -717,7 +717,7 @@ packages: description: path: "." ref: socket-authenticator-option - resolved-ref: "51832705146a6ca60f189e7c97a68dc56e06fb9e" + resolved-ref: ed751077eaa4fa081773720c69916deb6077cd09 url: "https://github.com/gkc/socket_connector.git" source: git version: "2.0.0" @@ -861,10 +861,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a13d5503b4facefc515c8c587ce3cf69577a7b064a9f1220e005449cf1f64aad + sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 url: "https://pub.dev" source: hosted - version: "12.0.0" + version: "14.0.0" watcher: dependency: transitive description: @@ -873,14 +873,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" + url: "https://pub.dev" + source: hosted + version: "0.4.2" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.3" webkit_inspection_protocol: dependency: transitive description: @@ -893,10 +901,10 @@ packages: dependency: transitive description: name: xml - sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.4.2" + version: "6.5.0" yaml: dependency: transitive description: @@ -909,9 +917,9 @@ packages: dependency: transitive description: name: zxing2 - sha256: "1e141568c9646bc262fa75aacf739bc151ef6ad0226997c0016cc3da358a1bbc" + sha256: a042961441bd400f59595f9125ef5fca4c888daf0ea59c17f41e0e151f8a12b5 url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0 <4.0.0" From 70d6664c6cd749f2a9ecf8361ece6c2912e91fc5 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 11:26:21 +0000 Subject: [PATCH 44/60] style: ran dart format --- .../dart/noports_core/lib/src/sshrv/sshrv_impl.dart | 4 ++-- .../dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart index f28db2d70..2f55f8163 100644 --- a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -104,12 +104,12 @@ class SshrvImplExec implements Sshrv { } } }, onError: (e) { - if (! rvPortBound.isCompleted) { + if (!rvPortBound.isCompleted) { rvPortBound.completeError(e); } }); - await rvPortBound.future.timeout(Duration(seconds:2)); + await rvPortBound.future.timeout(Duration(seconds: 2)); return p; } diff --git a/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart index dbcfbcdf6..61761cb53 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart @@ -40,6 +40,7 @@ class SshrvdImpl implements Sshrvd { static final String subscriptionRegex = '${Sshrvd.namespace}@'; late final SshrvdUtil sshrvdUtil; + SshrvdImpl({ required this.atClient, required this.atSign, @@ -49,7 +50,7 @@ class SshrvdImpl implements Sshrvd { required this.ipAddress, required this.logTraffic, required this.verbose, - SshrvdUtil? sshrvdUtil + SshrvdUtil? sshrvdUtil, }) { this.sshrvdUtil = sshrvdUtil ?? SshrvdUtil(atClient); logger.hierarchicalLoggingEnabled = true; @@ -272,22 +273,21 @@ class SshrvdSessionParams { class SshrvdUtil { final AtClient atClient; + SshrvdUtil(this.atClient); bool accept(AtNotification notification) { return notification.key.contains(Sshrvd.namespace); } - Future getParams( - AtNotification notification) async { + Future getParams(AtNotification notification) async { if (notification.key.contains('.request_ports.${Sshrvd.namespace}')) { return await _processJSONRequest(notification); } return _processLegacyRequest(notification); } - SshrvdSessionParams _processLegacyRequest( - AtNotification notification) { + SshrvdSessionParams _processLegacyRequest(AtNotification notification) { return SshrvdSessionParams( sessionId: notification.value!, atSignA: notification.from, From 91c601e02ca1b4a1978d92cb0fc5983d658d0ea5 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 12:04:29 +0000 Subject: [PATCH 45/60] chore: tidy up logging --- .../dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart | 2 +- .../dart/noports_core/lib/src/sshrv/sshrv_impl.dart | 10 +++++++--- packages/dart/sshnoports/bin/sshrv.dart | 3 --- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart index e6ea34a4c..e1908df29 100644 --- a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -520,7 +520,7 @@ class SshnpdImpl implements Sshnpd { required String? clientEphemeralPK, required String? clientEphemeralPKType, }) async { - logger.shout( + logger.info( 'Setting up ports for direct ssh session using ${sshClient.name} ($sshClient) from: $requestingAtsign session: $sessionId'); authenticateToRvd ??= false; diff --git a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart index 2f55f8163..017229b62 100644 --- a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart @@ -93,13 +93,13 @@ class SshrvImplExec implements Sshrv { Completer rvPortBound = Completer(); p.stdout.listen((List l) { var s = utf8.decode(l).trim(); - stderr.writeln('rv stdout: $s'); + logger.info('rv stdout | $s'); }, onError: (e) {}); p.stderr.listen((List l) { var allLines = utf8.decode(l).trim(); for (String s in allLines.split('\n')) { - stderr.writeln('rv stderr: [$s]'); - if (s.endsWith('is running') && !rvPortBound.isCompleted) { + logger.info('rv stderr | $s'); + if (s.endsWith('rv is running') && !rvPortBound.isCompleted) { rvPortBound.complete(); } } @@ -216,6 +216,10 @@ class SshrvImplDart implements Sshrv { } } + // Do not change this output; it is specifically looked for in + // SshrvImplExec.run + stderr.writeln('rv is running'); + return socketConnector; } catch (e) { AtSignLogger('sshrv').severe(e.toString()); diff --git a/packages/dart/sshnoports/bin/sshrv.dart b/packages/dart/sshnoports/bin/sshrv.dart index dc8d6dc10..cd4056059 100644 --- a/packages/dart/sshnoports/bin/sshrv.dart +++ b/packages/dart/sshnoports/bin/sshrv.dart @@ -45,9 +45,6 @@ Future main(List args) async { sessionIVString: sessionIVString, ).run(); - // Do not change this output - stderr.writeln('rv is running'); - /// Shut myself down once the socket connector closes stderr.writeln('Waiting for connector to close'); await connector.done; From bd17255fb69202dbb1487fc28d9b2fb716dba9e6 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 12:09:35 +0000 Subject: [PATCH 46/60] chore: adjusted SshrvdUtil test following refactoring of static methods to instance methods --- packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart index a45c0d83b..074e170e6 100644 --- a/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart +++ b/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart @@ -5,6 +5,8 @@ import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; import 'package:noports_core/sshrvd.dart'; import 'package:test/test.dart'; +import '../sshnp/sshnp_mocks.dart'; + void main() { test('test notification subscription regex', () { // Create a notification in rvd namespace @@ -16,7 +18,7 @@ void main() { // Create a notification in rvd namespace AtNotification notification = AtNotification.empty(); notification.key = 'request_ports.test.${Sshrvd.namespace}'; - expect(SshrvdUtil.accept(notification), true); + expect(SshrvdUtil(MockAtClient()).accept(notification), true); }); test( @@ -34,6 +36,6 @@ void main() { notification.key = 'request_ports.test.${Sshrvd.namespace}'; notification.value = jsonEncode(m); - expect(SshrvdUtil.accept(notification), true); + expect(SshrvdUtil(MockAtClient()).accept(notification), true); }); } From 4dc4ab31b5b4829257d44a0459dda3b196fa783f Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 12:12:12 +0000 Subject: [PATCH 47/60] feat: default new flags authenticateClientToRvd, authenticateDeviceToRvd, encryptRvdTraffic and discoverDaemonFeatures to false --- .../dart/noports_core/lib/src/common/default_args.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dart/noports_core/lib/src/common/default_args.dart b/packages/dart/noports_core/lib/src/common/default_args.dart index e322f90ca..1d348b88d 100644 --- a/packages/dart/noports_core/lib/src/common/default_args.dart +++ b/packages/dart/noports_core/lib/src/common/default_args.dart @@ -18,10 +18,10 @@ class DefaultArgs { static const bool addForwardsToTunnel = false; static final bool allowLocalFileSystem = Platform.isLinux || Platform.isMacOS || Platform.isWindows; - static const bool authenticateClientToRvd = true; - static const bool authenticateDeviceToRvd = true; - static const bool encryptRvdTraffic = true; - static const bool discoverDaemonFeatures = true; + static const bool authenticateClientToRvd = false; + static const bool authenticateDeviceToRvd = false; + static const bool encryptRvdTraffic = false; + static const bool discoverDaemonFeatures = false; } class DefaultSshnpArgs { From 122fec308f6c6b7ae1547699db70a9f6bd20f400 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 12:25:05 +0000 Subject: [PATCH 48/60] feat: compatibility with old sshrvd versions - if new rvd features (authentication) are NOT being used, send an old-style request - if new rvd features (authentication) ARE being used, send a new-style request --- .../util/sshrvd_channel/sshrvd_channel.dart | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index dc86f9199..39cac166f 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -149,24 +149,50 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { sshrvdAck = SshrvdAck.acknowledged; }); logger.info('Started listening for sshrvd response'); - AtKey ourSshrvdIdKey = AtKey() - ..key = '${params.device}.request_ports.${Sshrvd.namespace}' - ..sharedBy = params.clientAtSign // shared by us - ..sharedWith = host // shared with the sshrvd host - ..metadata = (Metadata() + + late AtKey rvdRequestKey; + late String rvdRequestValue; + + if (params.authenticateClientToRvd || params.authenticateDeviceToRvd) { + rvdRequestKey = AtKey() + ..key = '${params.device}.request_ports.${Sshrvd.namespace}' + ..sharedBy = params.clientAtSign // shared by us + ..sharedWith = host // shared with the sshrvd host + ..metadata = (Metadata() + // as we are sending a notification to the sshrvd namespace, + // we don't want to append our namespace + ..namespaceAware = false + ..ttl = 10000); + + var message = SocketRendezvousRequestMessage(); + message.sessionId = sessionId; + message.atSignA = params.clientAtSign; + message.atSignB = params.sshnpdAtSign; + message.authenticateSocketA = params.authenticateClientToRvd; + message.authenticateSocketB = params.authenticateDeviceToRvd; + message.clientNonce = clientNonce; + + rvdRequestValue = message.toString(); + } else { + // send a legacy message since no new rvd features are being used + rvdRequestKey = AtKey() + ..key = '${params.device}.${Sshrvd.namespace}' + ..sharedBy = params.clientAtSign // shared by us + ..sharedWith = host // shared with the sshrvd host + ..metadata = (Metadata() // as we are sending a notification to the sshrvd namespace, // we don't want to append our namespace - ..namespaceAware = false - ..ttl = 10000); - logger.info('Sending notification to sshrvd: $ourSshrvdIdKey'); - - String notificationValue = _prepareNotificationPayload(sessionId); - // We need to send not just the sessionId but other metaData - // especially, the atSign on the other end - // In the rvd we need to figure out backwards compatibility. + ..namespaceAware = false + ..ttl = 10000); + + rvdRequestValue = sessionId; + } + + logger.info( + 'Sending notification to sshrvd with key $rvdRequestKey and value $rvdRequestValue'); await notify( - ourSshrvdIdKey, - notificationValue, + rvdRequestKey, + rvdRequestValue, checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, ); @@ -184,15 +210,4 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } } } - - String _prepareNotificationPayload(String sessionId) { - var message = SocketRendezvousRequestMessage(); - message.sessionId = sessionId; - message.atSignA = params.clientAtSign; - message.atSignB = params.sshnpdAtSign; - message.authenticateSocketA = params.authenticateClientToRvd; - message.authenticateSocketB = params.authenticateDeviceToRvd; - message.clientNonce = clientNonce; - return message.toString(); - } } From e43804ec9925fd3394cce3ac33e1ac4e954163df Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 12:36:37 +0000 Subject: [PATCH 49/60] chore: fix pubspec.lock for melos build in github actions --- packages/dart/sshnoports/lib/src/version.dart | 2 +- packages/dart/sshnoports/pubspec.lock | 90 +++++++++---------- 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/packages/dart/sshnoports/lib/src/version.dart b/packages/dart/sshnoports/lib/src/version.dart index 2572e8013..2384f91ac 100644 --- a/packages/dart/sshnoports/lib/src/version.dart +++ b/packages/dart/sshnoports/lib/src/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '4.0.5'; +const packageVersion = '4.1.0'; diff --git a/packages/dart/sshnoports/pubspec.lock b/packages/dart/sshnoports/pubspec.lock index ac36e55c0..669dff789 100644 --- a/packages/dart/sshnoports/pubspec.lock +++ b/packages/dart/sshnoports/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "65.0.0" + version: "64.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.2.0" archive: dependency: "direct overridden" description: @@ -181,18 +181,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.0" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.1" build_runner: dependency: "direct dev" description: @@ -229,18 +229,18 @@ packages: dependency: transitive description: name: built_value - sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 + sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 url: "https://pub.dev" source: hosted - version: "8.8.1" + version: "8.6.3" chalkdart: dependency: transitive description: name: chalkdart - sha256: "0b7ec5c6a6bafd1445500632c00c573722bd7736e491675d4ac3fe560bbd9cfe" + sha256: "5fceaf0eb65cb3a0746423aad4b285b9ec123133b9435630ac027f305b21e8b5" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.0.9" charcode: dependency: transitive description: @@ -269,10 +269,10 @@ packages: dependency: transitive description: name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.7.0" collection: dependency: transitive description: @@ -293,10 +293,10 @@ packages: dependency: transitive description: name: coverage - sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" + sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" url: "https://pub.dev" source: hosted - version: "1.7.2" + version: "1.6.4" cron: dependency: transitive description: @@ -341,10 +341,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" + sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.3" dartssh2: dependency: "direct overridden" description: @@ -437,10 +437,10 @@ packages: dependency: transitive description: name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -461,10 +461,10 @@ packages: dependency: transitive description: name: image - sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" + sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "4.1.3" internet_connection_checker: dependency: transitive description: @@ -549,10 +549,10 @@ packages: dependency: transitive description: name: mutex - sha256: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2" + sha256: "03116a4e46282a671b46c12de649d72c0ed18188ffe12a8d0fc63e83f4ad88f4" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.0.1" ninja_asn1: dependency: transitive description: @@ -596,18 +596,18 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.8.3" petitparser: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "6.0.1" pinenacl: dependency: transitive description: @@ -620,18 +620,18 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.2" pointycastle: dependency: transitive description: name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.7.4" + version: "3.7.3" pool: dependency: transitive description: @@ -861,10 +861,10 @@ packages: dependency: transitive description: name: vm_service - sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + sha256: a13d5503b4facefc515c8c587ce3cf69577a7b064a9f1220e005449cf1f64aad url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "12.0.0" watcher: dependency: transitive description: @@ -873,22 +873,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" - url: "https://pub.dev" - source: hosted - version: "0.4.2" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.0" webkit_inspection_protocol: dependency: transitive description: @@ -901,10 +893,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.4.2" yaml: dependency: transitive description: @@ -917,9 +909,9 @@ packages: dependency: transitive description: name: zxing2 - sha256: a042961441bd400f59595f9125ef5fca4c888daf0ea59c17f41e0e151f8a12b5 + sha256: "1e141568c9646bc262fa75aacf739bc151ef6ad0226997c0016cc3da358a1bbc" url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "0.2.0" sdks: - dart: ">=3.2.0 <4.0.0" + dart: ">=3.1.0 <4.0.0" From 78b3e635a88068beeca728ba39afb26983af4c2b Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 12:47:32 +0000 Subject: [PATCH 50/60] chore: trying to find what the problem is with e2e local-local failing because of timeout waiting for sshrvd response --- .../lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 39cac166f..99c4d377f 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -138,6 +138,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { subscribe(regex: '$sessionId.${Sshrvd.namespace}@', shouldDecrypt: true) .listen((notification) async { String ipPorts = notification.value.toString(); + logger.info('Received from sshrvd: $ipPorts'); List results = ipPorts.split(','); _host = results[0]; _portA = int.parse(results[1]); From 3da8f27e83d8c71b32f433076843fea28ed6ec19 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 12:53:03 +0000 Subject: [PATCH 51/60] fix: make new sshrvdChannel handle responses from older versions of sshrvd --- .../src/sshnp/util/sshrvd_channel/sshrvd_channel.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 99c4d377f..7e239a443 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -143,9 +143,12 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { _host = results[0]; _portA = int.parse(results[1]); _portB = int.parse(results[2]); - rvdNonce = results[3]; - logger.info( - 'Received from sshrvd: host:port $host:$port and rvdNonce: $rvdNonce'); + if (results.length >= 4) { + rvdNonce = results[3]; + } + logger.info('Received from sshrvd:' + ' host:port $host:$port' + ' rvdNonce: $rvdNonce'); logger.info('Set sshrvdPort to: $_portB'); sshrvdAck = SshrvdAck.acknowledged; }); From 47429c89a83bc9485c591a8ae87bc2422e4eccb3 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 12:58:21 +0000 Subject: [PATCH 52/60] style: ran dart format --- .../lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index 7e239a443..160403079 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -163,8 +163,8 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ..sharedBy = params.clientAtSign // shared by us ..sharedWith = host // shared with the sshrvd host ..metadata = (Metadata() - // as we are sending a notification to the sshrvd namespace, - // we don't want to append our namespace + // as we are sending a notification to the sshrvd namespace, + // we don't want to append our namespace ..namespaceAware = false ..ttl = 10000); @@ -184,8 +184,8 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ..sharedBy = params.clientAtSign // shared by us ..sharedWith = host // shared with the sshrvd host ..metadata = (Metadata() - // as we are sending a notification to the sshrvd namespace, - // we don't want to append our namespace + // as we are sending a notification to the sshrvd namespace, + // we don't want to append our namespace ..namespaceAware = false ..ttl = 10000); From a2e5b44dd4fb95ef052a0a3bab3a8bc02f3898a8 Mon Sep 17 00:00:00 2001 From: gkc Date: Thu, 18 Jan 2024 14:36:31 +0000 Subject: [PATCH 53/60] feat: address review comments - in ping response, daemon says whether or not it accepts public keys from the client - added abbreviations in SshnpArg for the new flags - corrected some code comments and added to others --- .../noports_core/lib/src/common/features.dart | 4 ++++ .../lib/src/sshnp/models/sshnp_arg.dart | 4 ++++ .../util/sshnpd_channel/sshnpd_channel.dart | 5 ++--- .../lib/src/sshnpd/sshnpd_impl.dart | 1 + ...nature_verifying_socket_authenticator.dart | 20 ++++++++++++------- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/dart/noports_core/lib/src/common/features.dart b/packages/dart/noports_core/lib/src/common/features.dart index 3631ab4a3..40eac77e0 100644 --- a/packages/dart/noports_core/lib/src/common/features.dart +++ b/packages/dart/noports_core/lib/src/common/features.dart @@ -1,5 +1,9 @@ /// Features which can be supported by the NoPorts Daemon enum DaemonFeatures { + /// daemon will accept public keys sent by clients (i.e. daemon has been + /// started with the `--sshpublickey` or `-s` flag) + acceptsPublicKeys, + /// authenticate when connecting to the Socket Rendezvous (sr) srAuth, diff --git a/packages/dart/noports_core/lib/src/sshnp/models/sshnp_arg.dart b/packages/dart/noports_core/lib/src/sshnp/models/sshnp_arg.dart index acf0519a9..6e50cc96b 100644 --- a/packages/dart/noports_core/lib/src/sshnp/models/sshnp_arg.dart +++ b/packages/dart/noports_core/lib/src/sshnp/models/sshnp_arg.dart @@ -352,6 +352,7 @@ class SshnpArg { ); static const authenticateClientToRvdArg = SshnpArg( name: 'authenticate-client-to-rvd', + abbr: 'a', help: 'When false, client will not authenticate itself to rvd', defaultsTo: DefaultArgs.authenticateClientToRvd, format: ArgFormat.flag, @@ -360,6 +361,7 @@ class SshnpArg { ); static const authenticateDeviceToRvdArg = SshnpArg( name: 'authenticate-device-to-rvd', + abbr: 'A', help: 'When false, device will not authenticate to the socket rendezvous', defaultsTo: DefaultArgs.authenticateDeviceToRvd, format: ArgFormat.flag, @@ -368,6 +370,7 @@ class SshnpArg { ); static const encryptRvdTrafficArg = SshnpArg( name: 'encrypt-rvd-traffic', + abbr: 'E', help: 'When true, traffic via the socket rendezvous is encrypted,' ' in addition to whatever encryption the traffic already has' ' (e.g. an ssh session)', @@ -378,6 +381,7 @@ class SshnpArg { ); static const discoverDaemonFeaturesArg = SshnpArg( name: 'discover-daemon-features', + abbr: 'F', help: 'When this flag is set, this client starts by pinging the daemon to' ' discover what features it supports, and exits if this client has ' ' requested use of a feature which the daemon does not support.' diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart index b28f71652..f75eba87e 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart @@ -86,9 +86,8 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { @protected Future handleSshnpdPayload(AtNotification notification); - /// Wait until we've received an acknowledgement from the daemon. - /// Returns true if the daemon acknowledged our request. - /// Returns false if a timeout occurred. + /// Wait until we've received an acknowledgement from the daemon, or + /// have timed out while waiting. Future waitForDaemonResponse({int maxWaitMillis = 15000}) async { // Timer to timeout after 10 Secs or after the Ack of connected/Errors for (int counter = 1; counter <= 100; counter++) { diff --git a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart index e1908df29..55a0ed412 100644 --- a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -309,6 +309,7 @@ class SshnpdImpl implements Sshnpd { 'supportedFeatures': { DaemonFeatures.srAuth.name: true, DaemonFeatures.srE2ee.name: true, + DaemonFeatures.acceptsPublicKeys.name: addSshPublicKeys, }, }; unawaited( diff --git a/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart index ec3616fb7..0f9c2e6c0 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart +++ b/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart @@ -49,6 +49,19 @@ class SignatureAuthVerifier { return atChops.verify(input); } + /// We expect the authenticating client to send a JSON message with + /// this structure: + /// ```json + /// { + /// "signature":"<signature>", + /// "hashingAlgo":"<algo>", + /// "signingAlgo":"<algo>", + /// "payload":<the data which was signed> + /// } + /// ``` + /// The signature is verified against [dataToVerify] and, although not + /// strictly necessary, the rvdNonce is also checked in what the client + /// send in the payload Future<(bool, Stream?)> authenticate(Socket socket) async { Completer<(bool, Stream?)> completer = Completer(); bool authenticated = false; @@ -61,13 +74,6 @@ class SignatureAuthVerifier { try { final message = String.fromCharCodes(data); logger.info('SignatureAuthVerifier $tag received data: $message'); - // Expected message to be the JSON format with the below structure: - // { - // "signature":"", - // "hashingAlgo":"", - // "signingAlgo":"", - // "payload":{} - // } var envelope = jsonDecode(message); final hashingAlgo = From 33b113b50d5469d9650eb9413c67d609d927de4d Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 22 Jan 2024 14:05:06 +0800 Subject: [PATCH 54/60] chore: rename sshrv(d) to srv(d) in file names --- .../lib/src/common/default_args.dart | 2 +- .../noports_core/lib/src/common/types.dart | 2 +- .../src/{sshrv/sshrv.dart => srv/srv.dart} | 2 +- .../sshrv_impl.dart => srv/srv_impl.dart} | 2 +- .../lib/src/{sshrvd => srvd}/build_env.dart | 0 ...nature_verifying_socket_authenticator.dart | 0 .../{sshrvd => srvd}/socket_connector.dart | 2 +- .../{sshrvd/sshrvd.dart => srvd/srvd.dart} | 4 +- .../noports_core/lib/src/srvd/srvd_impl.dart | 342 ++++++++++++++++++ .../srvd_params.dart} | 2 +- .../lib/src/{sshrvd => srvd}/sshrvd_impl.dart | 8 +- .../lib/src/sshnp/sshnp_core.dart | 2 +- .../notification_request_message.dart | 0 .../srvd_channel.dart} | 6 +- .../srvd_dart_channel.dart} | 4 +- .../srvd_exec_channel.dart} | 4 +- .../lib/src/sshnpd/sshnpd_impl.dart | 2 +- packages/dart/noports_core/lib/srv.dart | 3 + packages/dart/noports_core/lib/srvd.dart | 4 + .../noports_core/lib/sshnp_foundation.dart | 6 +- packages/dart/noports_core/lib/sshrv.dart | 3 - packages/dart/noports_core/lib/sshrvd.dart | 4 - .../srvd_channel_mocks.dart} | 2 +- .../srvd_channel_test.dart} | 6 +- .../srvd_dart_channel_test.dart} | 0 .../srvd_exec_channel_test.dart} | 0 .../notification_subscription_test.dart | 4 +- ...e_verifying_socket_authenticator_test.dart | 2 +- .../test/sshrvd/sshrvdutil_test.dart | 4 +- .../sshnoports/bin/{sshrv.dart => srv.dart} | 2 +- .../sshnoports/bin/{sshrvd.dart => srvd.dart} | 2 +- packages/dart/sshnp_flutter/pubspec.lock | 39 +- 32 files changed, 403 insertions(+), 62 deletions(-) rename packages/dart/noports_core/lib/src/{sshrv/sshrv.dart => srv/srv.dart} (97%) rename packages/dart/noports_core/lib/src/{sshrv/sshrv_impl.dart => srv/srv_impl.dart} (99%) rename packages/dart/noports_core/lib/src/{sshrvd => srvd}/build_env.dart (100%) rename packages/dart/noports_core/lib/src/{sshrvd => srvd}/signature_verifying_socket_authenticator.dart (100%) rename packages/dart/noports_core/lib/src/{sshrvd => srvd}/socket_connector.dart (98%) rename packages/dart/noports_core/lib/src/{sshrvd/sshrvd.dart => srvd/srvd.dart} (89%) create mode 100644 packages/dart/noports_core/lib/src/srvd/srvd_impl.dart rename packages/dart/noports_core/lib/src/{sshrvd/sshrvd_params.dart => srvd/srvd_params.dart} (97%) rename packages/dart/noports_core/lib/src/{sshrvd => srvd}/sshrvd_impl.dart (97%) rename packages/dart/noports_core/lib/src/sshnp/util/{sshrvd_channel => srvd_channel}/notification_request_message.dart (100%) rename packages/dart/noports_core/lib/src/sshnp/util/{sshrvd_channel/sshrvd_channel.dart => srvd_channel/srvd_channel.dart} (97%) rename packages/dart/noports_core/lib/src/sshnp/util/{sshrvd_channel/sshrvd_dart_channel.dart => srvd_channel/srvd_dart_channel.dart} (69%) rename packages/dart/noports_core/lib/src/sshnp/util/{sshrvd_channel/sshrvd_exec_channel.dart => srvd_channel/srvd_exec_channel.dart} (68%) create mode 100644 packages/dart/noports_core/lib/srv.dart create mode 100644 packages/dart/noports_core/lib/srvd.dart delete mode 100644 packages/dart/noports_core/lib/sshrv.dart delete mode 100644 packages/dart/noports_core/lib/sshrvd.dart rename packages/dart/noports_core/test/sshnp/util/{sshrvd_channel/sshrvd_channel_mocks.dart => srvd_channel/srvd_channel_mocks.dart} (97%) rename packages/dart/noports_core/test/sshnp/util/{sshrvd_channel/sshrvd_channel_test.dart => srvd_channel/srvd_channel_test.dart} (98%) rename packages/dart/noports_core/test/sshnp/util/{sshrvd_channel/sshrvd_dart_channel_test.dart => srvd_channel/srvd_dart_channel_test.dart} (100%) rename packages/dart/noports_core/test/sshnp/util/{sshrvd_channel/ssrhvd_exec_channel_test.dart => srvd_channel/srvd_exec_channel_test.dart} (100%) rename packages/dart/sshnoports/bin/{sshrv.dart => srv.dart} (97%) rename packages/dart/sshnoports/bin/{sshrvd.dart => srvd.dart} (96%) diff --git a/packages/dart/noports_core/lib/src/common/default_args.dart b/packages/dart/noports_core/lib/src/common/default_args.dart index 1d348b88d..0f120bd72 100644 --- a/packages/dart/noports_core/lib/src/common/default_args.dart +++ b/packages/dart/noports_core/lib/src/common/default_args.dart @@ -1,6 +1,6 @@ import 'package:noports_core/src/common/io_types.dart'; import 'package:noports_core/src/common/types.dart'; -import 'package:noports_core/sshrv.dart'; +import 'package:noports_core/srv.dart'; class DefaultArgs { static const String namespace = 'sshnp'; diff --git a/packages/dart/noports_core/lib/src/common/types.dart b/packages/dart/noports_core/lib/src/common/types.dart index 0703c9356..b78b625ea 100644 --- a/packages/dart/noports_core/lib/src/common/types.dart +++ b/packages/dart/noports_core/lib/src/common/types.dart @@ -1,4 +1,4 @@ -import 'package:noports_core/sshrv.dart'; +import 'package:noports_core/srv.dart'; typedef SshrvGenerator = Sshrv Function( String, diff --git a/packages/dart/noports_core/lib/src/sshrv/sshrv.dart b/packages/dart/noports_core/lib/src/srv/srv.dart similarity index 97% rename from packages/dart/noports_core/lib/src/sshrv/sshrv.dart rename to packages/dart/noports_core/lib/src/srv/srv.dart index 56cb0750a..9ab5e52d7 100644 --- a/packages/dart/noports_core/lib/src/sshrv/sshrv.dart +++ b/packages/dart/noports_core/lib/src/srv/srv.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:noports_core/src/sshrv/sshrv_impl.dart'; +import 'package:noports_core/src/srv/srv_impl.dart'; import 'package:socket_connector/socket_connector.dart'; abstract class Sshrv { diff --git a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart b/packages/dart/noports_core/lib/src/srv/srv_impl.dart similarity index 99% rename from packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart rename to packages/dart/noports_core/lib/src/srv/srv_impl.dart index 017229b62..ece5013e8 100644 --- a/packages/dart/noports_core/lib/src/sshrv/sshrv_impl.dart +++ b/packages/dart/noports_core/lib/src/srv/srv_impl.dart @@ -6,7 +6,7 @@ import 'package:at_utils/at_utils.dart'; import 'package:cryptography/cryptography.dart'; import 'package:cryptography/dart.dart'; import 'package:meta/meta.dart'; -import 'package:noports_core/sshrv.dart'; +import 'package:noports_core/srv.dart'; import 'package:socket_connector/socket_connector.dart'; @visibleForTesting diff --git a/packages/dart/noports_core/lib/src/sshrvd/build_env.dart b/packages/dart/noports_core/lib/src/srvd/build_env.dart similarity index 100% rename from packages/dart/noports_core/lib/src/sshrvd/build_env.dart rename to packages/dart/noports_core/lib/src/srvd/build_env.dart diff --git a/packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart b/packages/dart/noports_core/lib/src/srvd/signature_verifying_socket_authenticator.dart similarity index 100% rename from packages/dart/noports_core/lib/src/sshrvd/signature_verifying_socket_authenticator.dart rename to packages/dart/noports_core/lib/src/srvd/signature_verifying_socket_authenticator.dart diff --git a/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart b/packages/dart/noports_core/lib/src/srvd/socket_connector.dart similarity index 98% rename from packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart rename to packages/dart/noports_core/lib/src/srvd/socket_connector.dart index 86ce80d00..fe704750b 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/socket_connector.dart +++ b/packages/dart/noports_core/lib/src/srvd/socket_connector.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'dart:isolate'; import 'package:at_utils/at_logger.dart'; -import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; +import 'package:noports_core/src/srvd/srvd_impl.dart'; import 'package:socket_connector/socket_connector.dart'; import 'signature_verifying_socket_authenticator.dart'; diff --git a/packages/dart/noports_core/lib/src/sshrvd/sshrvd.dart b/packages/dart/noports_core/lib/src/srvd/srvd.dart similarity index 89% rename from packages/dart/noports_core/lib/src/sshrvd/sshrvd.dart rename to packages/dart/noports_core/lib/src/srvd/srvd.dart index 7e0cdb64b..b154a6f61 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/sshrvd.dart +++ b/packages/dart/noports_core/lib/src/srvd/srvd.dart @@ -3,8 +3,8 @@ import 'dart:async'; import 'package:at_client/at_client.dart'; import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; -import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; -import 'package:noports_core/src/sshrvd/sshrvd_params.dart'; +import 'package:noports_core/src/srvd/srvd_impl.dart'; +import 'package:noports_core/src/srvd/srvd_params.dart'; abstract class Sshrvd { static const String namespace = 'sshrvd'; diff --git a/packages/dart/noports_core/lib/src/srvd/srvd_impl.dart b/packages/dart/noports_core/lib/src/srvd/srvd_impl.dart new file mode 100644 index 000000000..c51dfda92 --- /dev/null +++ b/packages/dart/noports_core/lib/src/srvd/srvd_impl.dart @@ -0,0 +1,342 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:isolate'; +import 'dart:convert'; +import 'package:at_client/at_client.dart'; +import 'package:at_utils/at_logger.dart'; +import 'package:logging/logging.dart'; +import 'package:meta/meta.dart'; +import 'package:noports_core/src/common/validation_utils.dart'; +import 'package:noports_core/src/srvd/build_env.dart'; +import 'package:noports_core/src/srvd/socket_connector.dart'; +import 'package:noports_core/src/srvd/srvd.dart'; +import 'package:noports_core/src/srvd/srvd_params.dart'; + +@protected +class SshrvdImpl implements Sshrvd { + @override + final AtSignLogger logger = AtSignLogger(' sshrvd '); + @override + AtClient atClient; + @override + final String atSign; + @override + final String homeDirectory; + @override + final String atKeysFilePath; + @override + final String managerAtsign; + @override + final String ipAddress; + @override + final bool logTraffic; + @override + bool verbose = false; + + @override + @visibleForTesting + bool initialized = false; + + static final String subscriptionRegex = '${Sshrvd.namespace}@'; + + late final SshrvdUtil sshrvdUtil; + + SshrvdImpl({ + required this.atClient, + required this.atSign, + required this.homeDirectory, + required this.atKeysFilePath, + required this.managerAtsign, + required this.ipAddress, + required this.logTraffic, + required this.verbose, + SshrvdUtil? sshrvdUtil, + }) { + this.sshrvdUtil = sshrvdUtil ?? SshrvdUtil(atClient); + logger.hierarchicalLoggingEnabled = true; + logger.logger.level = Level.SHOUT; + } + + static Future fromCommandLineArgs(List args, + {AtClient? atClient, + FutureOr Function(SshrvdParams)? atClientGenerator, + void Function(Object, StackTrace)? usageCallback}) async { + try { + var p = await SshrvdParams.fromArgs(args); + + if (!await File(p.atKeysFilePath).exists()) { + throw ('\n Unable to find .atKeys file : ${p.atKeysFilePath}'); + } + + AtSignLogger.root_level = 'SHOUT'; + if (p.verbose) { + AtSignLogger.root_level = 'INFO'; + } + + if (atClient == null && atClientGenerator == null) { + throw StateError('atClient and atClientGenerator are both null'); + } + + atClient ??= await atClientGenerator!(p); + + var sshrvd = SshrvdImpl( + atClient: atClient, + atSign: p.atSign, + homeDirectory: p.homeDirectory, + atKeysFilePath: p.atKeysFilePath, + managerAtsign: p.managerAtsign, + ipAddress: p.ipAddress, + logTraffic: p.logTraffic, + verbose: p.verbose, + ); + + if (p.verbose) { + sshrvd.logger.logger.level = Level.INFO; + } + return sshrvd; + } catch (e, s) { + usageCallback?.call(e, s); + rethrow; + } + } + + @override + Future init() async { + if (initialized) { + throw StateError('Cannot init() - already initialized'); + } + + initialized = true; + } + + @override + Future run() async { + if (!initialized) { + throw StateError('Cannot run() - not initialized'); + } + NotificationService notificationService = atClient.notificationService; + + notificationService + .subscribe(regex: subscriptionRegex, shouldDecrypt: true) + .listen(_notificationHandler); + } + + void _notificationHandler(AtNotification notification) async { + if (!sshrvdUtil.accept(notification)) { + return; + } + late SshrvdSessionParams sessionParams; + try { + sessionParams = await sshrvdUtil.getParams(notification); + + if (managerAtsign != 'open' && managerAtsign != sessionParams.atSignA) { + logger.shout( + 'Session ${sessionParams.sessionId} for ${sessionParams.atSignA} is denied'); + return; + } + } catch (e) { + logger.shout('Unable to provide the socket pair due to: $e'); + return; + } + + logger + .info('New session request: $sessionParams from ${notification.from}'); + + (int, int) ports = await _spawnSocketConnector( + 0, + 0, + sessionParams, + logTraffic, + verbose, + ); + var (portA, portB) = ports; + logger.warning( + 'Starting session ${sessionParams.sessionId} for ${sessionParams.atSignA} to ${sessionParams.atSignB} using ports $ports'); + + var metaData = Metadata() + ..isPublic = false + ..isEncrypted = true + ..ttl = 10000 + ..namespaceAware = true; + + var atKey = AtKey() + ..key = sessionParams.sessionId + ..sharedBy = atSign + ..sharedWith = notification.from + ..namespace = Sshrvd.namespace + ..metadata = metaData; + + String data = '$ipAddress,$portA,$portB,${sessionParams.rvdNonce}'; + + logger.info( + 'Sending response data for session ${sessionParams.sessionId} : [$data]'); + + try { + await atClient.notificationService.notify( + NotificationParams.forUpdate(atKey, value: data), + waitForFinalDeliveryStatus: false, + checkForFinalDeliveryStatus: false); + } catch (e) { + stderr.writeln("Error writing session ${notification.value} atKey"); + } + } + + /// This function spawns a new socketConnector in a background isolate + /// once the socketConnector has spawned and is ready to accept connections + /// it sends back the port numbers to the main isolate + /// then the port numbers are returned from this function + Future _spawnSocketConnector( + int portA, + int portB, + SshrvdSessionParams sshrvdSessionParams, + bool logTraffic, + bool verbose, + ) async { + /// Spawn an isolate and wait for it to send back the issued port numbers + ReceivePort receivePort = ReceivePort(sshrvdSessionParams.sessionId); + + ConnectorParams parameters = ( + receivePort.sendPort, + portA, + portB, + jsonEncode(sshrvdSessionParams), + BuildEnv.enableSnoop && logTraffic, + verbose, + ); + + logger + .info("Spawning socket connector isolate with parameters $parameters"); + + unawaited(Isolate.spawn(socketConnector, parameters)); + + PortPair ports = await receivePort.first; + + logger.info('Received ports $ports in main isolate' + ' for session ${sshrvdSessionParams.sessionId}'); + + return ports; + } +} + +class SshrvdSessionParams { + final String sessionId; + final String atSignA; + final String? atSignB; + final bool authenticateSocketA; + final bool authenticateSocketB; + final String? publicKeyA; + final String? publicKeyB; + final String? clientNonce; + final String? rvdNonce; + + SshrvdSessionParams({ + required this.sessionId, + required this.atSignA, + this.atSignB, + this.authenticateSocketA = false, + this.authenticateSocketB = false, + this.publicKeyA, + this.publicKeyB, + this.rvdNonce, + this.clientNonce, + }); + + @override + String toString() => toJson().toString(); + + Map toJson() => { + 'sessionId': sessionId, + 'atSignA': atSignA, + 'atSignB': atSignB, + 'authenticateSocketA': authenticateSocketA, + 'authenticateSocketB': authenticateSocketB, + 'publicKeyA': publicKeyA, + 'publicKeyB': publicKeyB, + 'rvdNonce': rvdNonce, + 'clientNonce': clientNonce, + }; + + static SshrvdSessionParams fromJson(Map json) { + return SshrvdSessionParams( + sessionId: json['sessionId'], + atSignA: json['atSignA'], + atSignB: json['atSignB'], + authenticateSocketA: json['authenticateSocketA'], + authenticateSocketB: json['authenticateSocketB'], + publicKeyA: json['publicKeyA'], + publicKeyB: json['publicKeyB'], + rvdNonce: json['rvdNonce'], + clientNonce: json['clientNonce'], + ); + } +} + +class SshrvdUtil { + final AtClient atClient; + + SshrvdUtil(this.atClient); + + bool accept(AtNotification notification) { + return notification.key.contains(Sshrvd.namespace); + } + + Future getParams(AtNotification notification) async { + if (notification.key.contains('.request_ports.${Sshrvd.namespace}')) { + return await _processJSONRequest(notification); + } + return _processLegacyRequest(notification); + } + + SshrvdSessionParams _processLegacyRequest(AtNotification notification) { + return SshrvdSessionParams( + sessionId: notification.value!, + atSignA: notification.from, + ); + } + + Future _processJSONRequest( + AtNotification notification) async { + dynamic jsonValue = jsonDecode(notification.value ?? ''); + + assertValidValue(jsonValue, 'sessionId', String); + assertValidValue(jsonValue, 'atSignA', String); + assertValidValue(jsonValue, 'atSignB', String); + assertValidValue(jsonValue, 'clientNonce', String); + assertValidValue(jsonValue, 'authenticateSocketA', bool); + assertValidValue(jsonValue, 'authenticateSocketA', bool); + + final String sessionId = jsonValue['sessionId']; + final String atSignA = jsonValue['atSignA']; + final String atSignB = jsonValue['atSignB']; + final String clientNonce = jsonValue['clientNonce']; + final bool authenticateSocketA = jsonValue['authenticateSocketA']; + final bool authenticateSocketB = jsonValue['authenticateSocketB']; + + String rvdSessionNonce = DateTime.now().toIso8601String(); + + String? publicKeyA; + String? publicKeyB; + if (authenticateSocketA) { + publicKeyA = await _fetchPublicKey(atSignA); + } + if (authenticateSocketB) { + publicKeyB = await _fetchPublicKey(atSignB); + } + return SshrvdSessionParams( + sessionId: sessionId, + atSignA: atSignA, + atSignB: atSignB, + authenticateSocketA: authenticateSocketA, + authenticateSocketB: authenticateSocketB, + publicKeyA: publicKeyA, + publicKeyB: publicKeyB, + rvdNonce: rvdSessionNonce, + clientNonce: clientNonce, + ); + } + + Future _fetchPublicKey(String atSign) async { + AtValue v = await atClient.get(AtKey.fromString('public:publickey$atSign')); + return v.value; + } +} diff --git a/packages/dart/noports_core/lib/src/sshrvd/sshrvd_params.dart b/packages/dart/noports_core/lib/src/srvd/srvd_params.dart similarity index 97% rename from packages/dart/noports_core/lib/src/sshrvd/sshrvd_params.dart rename to packages/dart/noports_core/lib/src/srvd/srvd_params.dart index 81a677058..b936dd72f 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/sshrvd_params.dart +++ b/packages/dart/noports_core/lib/src/srvd/srvd_params.dart @@ -1,6 +1,6 @@ import 'package:args/args.dart'; import 'package:noports_core/src/common/file_system_utils.dart'; -import 'package:noports_core/src/sshrvd/build_env.dart'; +import 'package:noports_core/src/srvd/build_env.dart'; class SshrvdParams { final String username; diff --git a/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart b/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart similarity index 97% rename from packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart rename to packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart index 61761cb53..c51dfda92 100644 --- a/packages/dart/noports_core/lib/src/sshrvd/sshrvd_impl.dart +++ b/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart @@ -7,10 +7,10 @@ import 'package:at_utils/at_logger.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/validation_utils.dart'; -import 'package:noports_core/src/sshrvd/build_env.dart'; -import 'package:noports_core/src/sshrvd/socket_connector.dart'; -import 'package:noports_core/src/sshrvd/sshrvd.dart'; -import 'package:noports_core/src/sshrvd/sshrvd_params.dart'; +import 'package:noports_core/src/srvd/build_env.dart'; +import 'package:noports_core/src/srvd/socket_connector.dart'; +import 'package:noports_core/src/srvd/srvd.dart'; +import 'package:noports_core/src/srvd/srvd_params.dart'; @protected class SshrvdImpl implements Sshrvd { diff --git a/packages/dart/noports_core/lib/src/sshnp/sshnp_core.dart b/packages/dart/noports_core/lib/src/sshnp/sshnp_core.dart index eae394ca5..3fcd93fb7 100644 --- a/packages/dart/noports_core/lib/src/sshnp/sshnp_core.dart +++ b/packages/dart/noports_core/lib/src/sshnp/sshnp_core.dart @@ -10,7 +10,7 @@ import 'package:noports_core/src/common/mixins/at_client_bindings.dart'; import 'package:noports_core/src/common/default_args.dart'; import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler/sshnp_ssh_key_handler.dart'; import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart'; -import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; +import 'package:noports_core/src/sshnp/util/srvd_channel/srvd_channel.dart'; import 'package:noports_core/sshnp.dart'; import 'package:uuid/uuid.dart'; diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/notification_request_message.dart similarity index 100% rename from packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/notification_request_message.dart rename to packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/notification_request_message.dart diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart similarity index 97% rename from packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart rename to packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart index 160403079..b59827b00 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart @@ -6,10 +6,10 @@ import 'package:meta/meta.dart'; import 'package:noports_core/src/common/mixins/async_initialization.dart'; import 'package:noports_core/src/common/mixins/at_client_bindings.dart'; import 'package:noports_core/src/common/validation_utils.dart'; -import 'package:noports_core/src/sshnp/util/sshrvd_channel/notification_request_message.dart'; +import 'package:noports_core/src/sshnp/util/srvd_channel/notification_request_message.dart'; import 'package:noports_core/sshnp.dart'; -import 'package:noports_core/sshrv.dart'; -import 'package:noports_core/sshrvd.dart'; +import 'package:noports_core/srv.dart'; +import 'package:noports_core/srvd.dart'; @visibleForTesting enum SshrvdAck { diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart similarity index 69% rename from packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart rename to packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart index 260b06e74..6c04fd0f9 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart @@ -1,5 +1,5 @@ -import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; -import 'package:noports_core/sshrv.dart'; +import 'package:noports_core/src/sshnp/util/srvd_channel/srvd_channel.dart'; +import 'package:noports_core/srv.dart'; import 'package:socket_connector/socket_connector.dart'; class SshrvdDartChannel extends SshrvdChannel { diff --git a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart similarity index 68% rename from packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart rename to packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart index 9f7932461..f8ffef2bf 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart @@ -1,6 +1,6 @@ import 'package:noports_core/src/common/io_types.dart'; -import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; -import 'package:noports_core/sshrv.dart'; +import 'package:noports_core/src/sshnp/util/srvd_channel/srvd_channel.dart'; +import 'package:noports_core/srv.dart'; class SshrvdExecChannel extends SshrvdChannel { SshrvdExecChannel({ diff --git a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 55a0ed412..87101beab 100644 --- a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -10,7 +10,7 @@ import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/features.dart'; import 'package:noports_core/src/common/openssh_binary_path.dart'; -import 'package:noports_core/src/sshrv/sshrv.dart'; +import 'package:noports_core/src/srv/srv.dart'; import 'package:noports_core/sshnpd.dart'; import 'package:noports_core/utils.dart'; import 'package:noports_core/src/version.dart'; diff --git a/packages/dart/noports_core/lib/srv.dart b/packages/dart/noports_core/lib/srv.dart new file mode 100644 index 000000000..702324128 --- /dev/null +++ b/packages/dart/noports_core/lib/srv.dart @@ -0,0 +1,3 @@ +library noports_core_sshrv; + +export 'src/srv/srv.dart'; diff --git a/packages/dart/noports_core/lib/srvd.dart b/packages/dart/noports_core/lib/srvd.dart new file mode 100644 index 000000000..f64ad7221 --- /dev/null +++ b/packages/dart/noports_core/lib/srvd.dart @@ -0,0 +1,4 @@ +library noports_core_sshrvd; + +export 'src/srvd/srvd.dart'; +export 'src/srvd/srvd_params.dart'; diff --git a/packages/dart/noports_core/lib/sshnp_foundation.dart b/packages/dart/noports_core/lib/sshnp_foundation.dart index 59bedf3d3..fcdb2afba 100644 --- a/packages/dart/noports_core/lib/sshnp_foundation.dart +++ b/packages/dart/noports_core/lib/sshnp_foundation.dart @@ -20,9 +20,9 @@ export 'src/sshnp/util/sshnpd_channel/sshnpd_channel.dart'; export 'src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart'; export 'src/sshnp/util/sshnpd_channel/sshnpd_unsigned_channel.dart'; -export 'src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; -export 'src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart'; -export 'src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart'; +export 'src/sshnp/util/srvd_channel/srvd_channel.dart'; +export 'src/sshnp/util/srvd_channel/srvd_dart_channel.dart'; +export 'src/sshnp/util/srvd_channel/srvd_exec_channel.dart'; export 'src/sshnp/util/ssh_session_handler/ssh_session_handler.dart'; export 'src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart'; diff --git a/packages/dart/noports_core/lib/sshrv.dart b/packages/dart/noports_core/lib/sshrv.dart deleted file mode 100644 index 3cfa5b3a9..000000000 --- a/packages/dart/noports_core/lib/sshrv.dart +++ /dev/null @@ -1,3 +0,0 @@ -library noports_core_sshrv; - -export 'src/sshrv/sshrv.dart'; diff --git a/packages/dart/noports_core/lib/sshrvd.dart b/packages/dart/noports_core/lib/sshrvd.dart deleted file mode 100644 index d31136bbb..000000000 --- a/packages/dart/noports_core/lib/sshrvd.dart +++ /dev/null @@ -1,4 +0,0 @@ -library noports_core_sshrvd; - -export 'src/sshrvd/sshrvd.dart'; -export 'src/sshrvd/sshrvd_params.dart'; diff --git a/packages/dart/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart similarity index 97% rename from packages/dart/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart rename to packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart index 66529e159..a59feac76 100644 --- a/packages/dart/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_mocks.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart @@ -1,7 +1,7 @@ import 'package:at_client/at_client.dart'; import 'package:mocktail/mocktail.dart'; import 'package:noports_core/sshnp_foundation.dart'; -import 'package:noports_core/sshrv.dart'; +import 'package:noports_core/srv.dart'; /// Stubbing for [SshrvGenerator] typedef abstract class SshrvGeneratorCaller { diff --git a/packages/dart/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart similarity index 98% rename from packages/dart/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart rename to packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart index deadaf08b..34d2bf665 100644 --- a/packages/dart/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_channel_test.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart @@ -5,13 +5,13 @@ import 'package:at_client/at_client.dart'; import 'package:at_utils/at_utils.dart'; import 'package:mocktail/mocktail.dart'; import 'package:noports_core/sshnp_foundation.dart'; -import 'package:noports_core/sshrv.dart'; -import 'package:noports_core/sshrvd.dart'; +import 'package:noports_core/srv.dart'; +import 'package:noports_core/srvd.dart'; import 'package:test/test.dart'; import 'package:uuid/uuid.dart'; import '../../sshnp_mocks.dart'; -import 'sshrvd_channel_mocks.dart'; +import 'srvd_channel_mocks.dart'; void main() { group('SshrvdChannel', () { diff --git a/packages/dart/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_dart_channel_test.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_dart_channel_test.dart similarity index 100% rename from packages/dart/noports_core/test/sshnp/util/sshrvd_channel/sshrvd_dart_channel_test.dart rename to packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_dart_channel_test.dart diff --git a/packages/dart/noports_core/test/sshnp/util/sshrvd_channel/ssrhvd_exec_channel_test.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_exec_channel_test.dart similarity index 100% rename from packages/dart/noports_core/test/sshnp/util/sshrvd_channel/ssrhvd_exec_channel_test.dart rename to packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_exec_channel_test.dart diff --git a/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart b/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart index cc0ab502c..b32c63f98 100644 --- a/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart +++ b/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart @@ -1,5 +1,5 @@ -import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; -import 'package:noports_core/sshrvd.dart'; +import 'package:noports_core/src/srvd/srvd_impl.dart'; +import 'package:noports_core/srvd.dart'; import 'package:test/test.dart'; void main() { diff --git a/packages/dart/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart b/packages/dart/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart index 7e4d52208..0292428c1 100644 --- a/packages/dart/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart +++ b/packages/dart/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:at_chops/at_chops.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:noports_core/src/sshrvd/signature_verifying_socket_authenticator.dart'; +import 'package:noports_core/src/srvd/signature_verifying_socket_authenticator.dart'; import 'package:test/test.dart'; import 'package:uuid/uuid.dart'; diff --git a/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart index 074e170e6..b7c3aeb85 100644 --- a/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart +++ b/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'package:at_client/at_client.dart'; -import 'package:noports_core/src/sshrvd/sshrvd_impl.dart'; -import 'package:noports_core/sshrvd.dart'; +import 'package:noports_core/src/srvd/srvd_impl.dart'; +import 'package:noports_core/srvd.dart'; import 'package:test/test.dart'; import '../sshnp/sshnp_mocks.dart'; diff --git a/packages/dart/sshnoports/bin/sshrv.dart b/packages/dart/sshnoports/bin/srv.dart similarity index 97% rename from packages/dart/sshnoports/bin/sshrv.dart rename to packages/dart/sshnoports/bin/srv.dart index cd4056059..9b459c585 100644 --- a/packages/dart/sshnoports/bin/sshrv.dart +++ b/packages/dart/sshnoports/bin/srv.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'package:args/args.dart'; -import 'package:noports_core/sshrv.dart'; +import 'package:noports_core/srv.dart'; import 'package:socket_connector/socket_connector.dart'; Future main(List args) async { diff --git a/packages/dart/sshnoports/bin/sshrvd.dart b/packages/dart/sshnoports/bin/srvd.dart similarity index 96% rename from packages/dart/sshnoports/bin/sshrvd.dart rename to packages/dart/sshnoports/bin/srvd.dart index af9341807..08e8a96f5 100644 --- a/packages/dart/sshnoports/bin/sshrvd.dart +++ b/packages/dart/sshnoports/bin/srvd.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:io'; import 'package:at_utils/at_logger.dart'; -import 'package:noports_core/sshrvd.dart'; +import 'package:noports_core/srvd.dart'; import 'package:sshnoports/src/create_at_client_cli.dart'; import 'package:sshnoports/src/print_version.dart'; diff --git a/packages/dart/sshnp_flutter/pubspec.lock b/packages/dart/sshnp_flutter/pubspec.lock index 4dedb685c..16b6149b6 100644 --- a/packages/dart/sshnp_flutter/pubspec.lock +++ b/packages/dart/sshnp_flutter/pubspec.lock @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: at_client - sha256: dbb841c6f26d47a77605606aa82c67570990b2c07baa3e630ed931efb8747331 + sha256: "41c5028179a1e765084ab85fcb6582640252b90421c4a4c37818d346e1d8ef9b" url: "https://pub.dev" source: hosted - version: "3.0.69" + version: "3.0.72" at_client_mobile: dependency: "direct main" description: @@ -261,10 +261,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: transitive description: @@ -705,10 +705,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: @@ -744,11 +744,10 @@ packages: noports_core: dependency: "direct main" description: - name: noports_core - sha256: "4b4e74403ee7cee3aacc449c2f20f10f98a1d223d0820c1dfff78c539067da5b" - url: "https://pub.dev" - source: hosted - version: "4.0.1" + path: "../noports_core" + relative: true + source: path + version: "5.1.0" openssh_ed25519: dependency: transitive description: @@ -1095,10 +1094,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" state_notifier: dependency: transitive description: @@ -1111,10 +1110,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -1135,10 +1134,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" tutorial_coach_mark: dependency: transitive description: @@ -1271,10 +1270,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" webview_flutter: dependency: transitive description: @@ -1364,5 +1363,5 @@ packages: source: hosted version: "0.2.1" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.13.0" From df981d3eea4255bdda12953a34bdd0d4dc4d8b12 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 22 Jan 2024 14:18:23 +0800 Subject: [PATCH 55/60] refactor: make srv look for old and new binary name on the path --- .../dart/noports_core/lib/src/srv/srv.dart | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/dart/noports_core/lib/src/srv/srv.dart b/packages/dart/noports_core/lib/src/srv/srv.dart index 9ab5e52d7..7b91a11a1 100644 --- a/packages/dart/noports_core/lib/src/srv/srv.dart +++ b/packages/dart/noports_core/lib/src/srv/srv.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:noports_core/src/srv/srv_impl.dart'; import 'package:socket_connector/socket_connector.dart'; -abstract class Sshrv { +abstract class Srv { /// The internet address of the host to connect to. abstract final String host; @@ -28,8 +28,8 @@ abstract class Sshrv { Future run(); - // Can't use factory functions since SSHRV contains a generic type - static Sshrv exec( + // Can't use factory functions since Srv contains a generic type + static Srv exec( String host, int streamingPort, { required int localPort, @@ -38,7 +38,7 @@ abstract class Sshrv { String? sessionAESKeyString, String? sessionIVString, }) { - return SshrvImplExec( + return SrvImplExec( host, streamingPort, localPort: localPort, @@ -49,7 +49,7 @@ abstract class Sshrv { ); } - static Sshrv dart( + static Srv dart( String host, int streamingPort, { required int localPort, @@ -58,7 +58,7 @@ abstract class Sshrv { String? sessionAESKeyString, String? sessionIVString, }) { - return SshrvImplDart( + return SrvImplDart( host, streamingPort, localPort: localPort, @@ -70,6 +70,15 @@ abstract class Sshrv { } static Future getLocalBinaryPath() async { + List binaryNames = ['srv', 'sshrv']; + for (var name in binaryNames) { + var binary = await _getBinaryPathByName(name); + if (binary != null) return binary; + } + return null; + } + + static Future _getBinaryPathByName(String name) async { String postfix = Platform.isWindows ? '.exe' : ''; List pathList = Platform.resolvedExecutable.split(Platform.pathSeparator); @@ -78,10 +87,10 @@ abstract class Sshrv { pathList ..removeLast() - ..add('sshrv$postfix'); + ..add('$name$postfix'); - File sshrvFile = File(pathList.join(Platform.pathSeparator)); - bool sshrvExists = await sshrvFile.exists(); - return (isExe && sshrvExists) ? sshrvFile.absolute.path : null; + File binaryName = File(pathList.join(Platform.pathSeparator)); + bool binaryExists = await binaryName.exists(); + return (isExe && binaryExists) ? binaryName.absolute.path : null; } } From 74fbd64f8e3e618d35848cb055b9725b5c3ad7a2 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 22 Jan 2024 14:18:59 +0800 Subject: [PATCH 56/60] chore: rename Sshrv and SshrvImpls --- .../noports_core/lib/src/common/default_args.dart | 2 +- .../dart/noports_core/lib/src/common/types.dart | 2 +- .../dart/noports_core/lib/src/srv/srv_impl.dart | 14 +++++++------- .../src/sshnp/util/srvd_channel/srvd_channel.dart | 2 +- .../sshnp/util/srvd_channel/srvd_dart_channel.dart | 2 +- .../sshnp/util/srvd_channel/srvd_exec_channel.dart | 2 +- .../noports_core/lib/src/sshnpd/sshnpd_impl.dart | 2 +- .../util/srvd_channel/srvd_channel_mocks.dart | 4 ++-- .../sshnp/util/srvd_channel/srvd_channel_test.dart | 2 +- packages/dart/sshnoports/bin/srv.dart | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/dart/noports_core/lib/src/common/default_args.dart b/packages/dart/noports_core/lib/src/common/default_args.dart index 0f120bd72..a7909b3fd 100644 --- a/packages/dart/noports_core/lib/src/common/default_args.dart +++ b/packages/dart/noports_core/lib/src/common/default_args.dart @@ -8,7 +8,7 @@ class DefaultArgs { SupportedSshAlgorithm.ed25519; static const bool verbose = false; static const String rootDomain = 'root.atsign.org'; - static const SshrvGenerator sshrvGenerator = Sshrv.exec; + static const SshrvGenerator sshrvGenerator = Srv.exec; static const int localSshdPort = 22; static const int remoteSshdPort = 22; diff --git a/packages/dart/noports_core/lib/src/common/types.dart b/packages/dart/noports_core/lib/src/common/types.dart index b78b625ea..0d4403972 100644 --- a/packages/dart/noports_core/lib/src/common/types.dart +++ b/packages/dart/noports_core/lib/src/common/types.dart @@ -1,6 +1,6 @@ import 'package:noports_core/srv.dart'; -typedef SshrvGenerator = Sshrv Function( +typedef SshrvGenerator = Srv Function( String, int, { required int localPort, diff --git a/packages/dart/noports_core/lib/src/srv/srv_impl.dart b/packages/dart/noports_core/lib/src/srv/srv_impl.dart index ece5013e8..ea1cda1c8 100644 --- a/packages/dart/noports_core/lib/src/srv/srv_impl.dart +++ b/packages/dart/noports_core/lib/src/srv/srv_impl.dart @@ -10,8 +10,8 @@ import 'package:noports_core/srv.dart'; import 'package:socket_connector/socket_connector.dart'; @visibleForTesting -class SshrvImplExec implements Sshrv { - static final AtSignLogger logger = AtSignLogger('SshrvImplExec'); +class SrvImplExec implements Srv { + static final AtSignLogger logger = AtSignLogger('SrvImplExec'); @override final String host; @@ -34,7 +34,7 @@ class SshrvImplExec implements Sshrv { @override final String? sessionIVString; - SshrvImplExec( + SrvImplExec( this.host, this.streamingPort, { required this.localPort, @@ -51,11 +51,11 @@ class SshrvImplExec implements Sshrv { @override Future run() async { - String? command = await Sshrv.getLocalBinaryPath(); + String? command = await Srv.getLocalBinaryPath(); String postfix = Platform.isWindows ? '.exe' : ''; if (command == null) { throw Exception( - 'Unable to locate sshrv$postfix binary.\n' + 'Unable to locate srv$postfix binary.\n' 'N.B. sshnp is expected to be compiled and run from source, not via the dart command.', ); } @@ -116,7 +116,7 @@ class SshrvImplExec implements Sshrv { } @visibleForTesting -class SshrvImplDart implements Sshrv { +class SrvImplDart implements Srv { @override final String host; @@ -138,7 +138,7 @@ class SshrvImplDart implements Sshrv { @override final String? sessionIVString; - SshrvImplDart( + SrvImplDart( this.host, this.streamingPort, { required this.localPort, diff --git a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart index b59827b00..5f538c538 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart @@ -102,7 +102,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { // Connect to rendezvous point using background process. // sshnp (this program) can then exit without issue. - late Sshrv sshrv; + late Srv sshrv; if (directSsh) { sshrv = sshrvGenerator( host, diff --git a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart index 6c04fd0f9..9fd815ce4 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart @@ -7,5 +7,5 @@ class SshrvdDartChannel extends SshrvdChannel { required super.atClient, required super.params, required super.sessionId, - }) : super(sshrvGenerator: Sshrv.dart); + }) : super(sshrvGenerator: Srv.dart); } diff --git a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart index f8ffef2bf..9a6d4fc2f 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart @@ -7,5 +7,5 @@ class SshrvdExecChannel extends SshrvdChannel { required super.atClient, required super.params, required super.sessionId, - }) : super(sshrvGenerator: Sshrv.exec); + }) : super(sshrvGenerator: Srv.exec); } diff --git a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 87101beab..0066528b4 100644 --- a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -573,7 +573,7 @@ class SshnpdImpl implements Sshnpd { } // Connect to rendezvous point using background process. // This program can then exit without causing an issue. - Process rv = await Sshrv.exec( + Process rv = await Srv.exec( host, port, localPort: localSshdPort, diff --git a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart index a59feac76..7c6f82b92 100644 --- a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart @@ -5,7 +5,7 @@ import 'package:noports_core/srv.dart'; /// Stubbing for [SshrvGenerator] typedef abstract class SshrvGeneratorCaller { - Sshrv call( + Srv call( String host, int port, { required int localPort, @@ -18,7 +18,7 @@ abstract class SshrvGeneratorCaller { class SshrvGeneratorStub extends Mock implements SshrvGeneratorCaller {} -class MockSshrv extends Mock implements Sshrv {} +class MockSshrv extends Mock implements Srv {} /// Stubbed [SshrvdChannel] which we are testing class StubbedSshrvdChannel extends SshrvdChannel { diff --git a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart index 34d2bf665..25ff5430f 100644 --- a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart @@ -92,7 +92,7 @@ void main() { expect( stubbedSshrvdChannel.sshrvGenerator, isA< - Sshrv Function(String, int, + Srv Function(String, int, {required int localPort, required bool bindLocalPort, String? rvdAuthString})>(), diff --git a/packages/dart/sshnoports/bin/srv.dart b/packages/dart/sshnoports/bin/srv.dart index 9b459c585..b3f2dd8cc 100644 --- a/packages/dart/sshnoports/bin/srv.dart +++ b/packages/dart/sshnoports/bin/srv.dart @@ -35,7 +35,7 @@ Future main(List args) async { String? sessionAESKeyString = rvE2ee ? Platform.environment['RV_AES'] : null; String? sessionIVString = rvE2ee ? Platform.environment['RV_IV'] : null; - SocketConnector connector = await Sshrv.dart( + SocketConnector connector = await Srv.dart( host, streamingPort, localPort: localPort, From 5580cb855f0e4aaed40edffdaf27f4bf793f860e Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 22 Jan 2024 14:30:22 +0800 Subject: [PATCH 57/60] fix: bug prone string matching --- .../dart/noports_core/lib/src/srv/srv_impl.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/dart/noports_core/lib/src/srv/srv_impl.dart b/packages/dart/noports_core/lib/src/srv/srv_impl.dart index ea1cda1c8..b9948ef7c 100644 --- a/packages/dart/noports_core/lib/src/srv/srv_impl.dart +++ b/packages/dart/noports_core/lib/src/srv/srv_impl.dart @@ -34,6 +34,9 @@ class SrvImplExec implements Srv { @override final String? sessionIVString; + @visibleForTesting + static const completionString = 'rv is running'; + SrvImplExec( this.host, this.streamingPort, { @@ -99,7 +102,7 @@ class SrvImplExec implements Srv { var allLines = utf8.decode(l).trim(); for (String s in allLines.split('\n')) { logger.info('rv stderr | $s'); - if (s.endsWith('rv is running') && !rvPortBound.isCompleted) { + if (s.endsWith(completionString) && !rvPortBound.isCompleted) { rvPortBound.complete(); } } @@ -216,13 +219,13 @@ class SrvImplDart implements Srv { } } - // Do not change this output; it is specifically looked for in - // SshrvImplExec.run - stderr.writeln('rv is running'); + // Do not remove this output; it is specifically looked for in + // SrvImplExec.run + stderr.writeln(SrvImplExec.completionString); return socketConnector; } catch (e) { - AtSignLogger('sshrv').severe(e.toString()); + AtSignLogger('srv').severe(e.toString()); rethrow; } } From 8475b4ac5e88abd0e4e675ce8f9c4e3505a14a37 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 22 Jan 2024 14:35:14 +0800 Subject: [PATCH 58/60] chore: rename the rest of sshrv(d) to srv(d) --- .../lib/src/common/default_args.dart | 2 +- .../noports_core/lib/src/common/types.dart | 2 +- .../lib/src/srvd/socket_connector.dart | 46 +++++------ .../dart/noports_core/lib/src/srvd/srvd.dart | 8 +- .../noports_core/lib/src/srvd/srvd_impl.dart | 68 ++++++++-------- .../lib/src/srvd/srvd_params.dart | 12 +-- .../lib/src/srvd/sshrvd_impl.dart | 68 ++++++++-------- .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 14 ++-- .../sshnp/impl/sshnp_openssh_local_impl.dart | 18 ++--- .../src/sshnp/impl/sshnp_unsigned_impl.dart | 12 +-- .../lib/src/sshnp/models/sshnp_arg.dart | 2 +- .../noports_core/lib/src/sshnp/sshnp.dart | 2 +- .../lib/src/sshnp/sshnp_core.dart | 8 +- .../sshnp/util/srvd_channel/srvd_channel.dart | 80 +++++++++---------- .../util/srvd_channel/srvd_dart_channel.dart | 6 +- .../util/srvd_channel/srvd_exec_channel.dart | 6 +- .../dart_ssh_session_handler.dart | 10 +-- .../openssh_ssh_session_handler.dart | 2 +- .../noports_core/lib/src/sshnpd/sshnpd.dart | 2 +- .../lib/src/sshnpd/sshnpd_impl.dart | 6 +- packages/dart/noports_core/lib/srv.dart | 2 +- packages/dart/noports_core/lib/srvd.dart | 2 +- .../util/srvd_channel/srvd_channel_mocks.dart | 2 +- .../util/srvd_channel/srvd_channel_test.dart | 6 +- .../notification_subscription_test.dart | 10 +-- .../test/sshrvd/sshrvdutil_test.dart | 10 +-- packages/dart/sshnoports/bin/srvd.dart | 6 +- 27 files changed, 205 insertions(+), 207 deletions(-) diff --git a/packages/dart/noports_core/lib/src/common/default_args.dart b/packages/dart/noports_core/lib/src/common/default_args.dart index a7909b3fd..f23afe475 100644 --- a/packages/dart/noports_core/lib/src/common/default_args.dart +++ b/packages/dart/noports_core/lib/src/common/default_args.dart @@ -8,7 +8,7 @@ class DefaultArgs { SupportedSshAlgorithm.ed25519; static const bool verbose = false; static const String rootDomain = 'root.atsign.org'; - static const SshrvGenerator sshrvGenerator = Srv.exec; + static const SrvGenerator srvGenerator = Srv.exec; static const int localSshdPort = 22; static const int remoteSshdPort = 22; diff --git a/packages/dart/noports_core/lib/src/common/types.dart b/packages/dart/noports_core/lib/src/common/types.dart index 0d4403972..c74bd2ce8 100644 --- a/packages/dart/noports_core/lib/src/common/types.dart +++ b/packages/dart/noports_core/lib/src/common/types.dart @@ -1,6 +1,6 @@ import 'package:noports_core/srv.dart'; -typedef SshrvGenerator = Srv Function( +typedef SrvGenerator = Srv Function( String, int, { required int localPort, diff --git a/packages/dart/noports_core/lib/src/srvd/socket_connector.dart b/packages/dart/noports_core/lib/src/srvd/socket_connector.dart index fe704750b..7f9636bf3 100644 --- a/packages/dart/noports_core/lib/src/srvd/socket_connector.dart +++ b/packages/dart/noports_core/lib/src/srvd/socket_connector.dart @@ -25,7 +25,7 @@ void socketConnector(ConnectorParams connectorParams) async { sendPort, portA, portB, - sshrvdSessionParamsJsonString, + srvdSessionParamsJsonString, logTraffic, verbose, ) = connectorParams; @@ -36,55 +36,55 @@ void socketConnector(ConnectorParams connectorParams) async { AtSignLogger.root_level = 'WARNING'; } - final logger = AtSignLogger(' sshrvd / socket_connector '); + final logger = AtSignLogger(' srvd / socket_connector '); - SshrvdSessionParams sshrvdSessionParams = - SshrvdSessionParams.fromJson(jsonDecode(sshrvdSessionParamsJsonString)); + SrvdSessionParams srvdSessionParams = + SrvdSessionParams.fromJson(jsonDecode(srvdSessionParamsJsonString)); logger.info( - 'Starting socket connector session for ${sshrvdSessionParams.toJson()}'); + 'Starting socket connector session for ${srvdSessionParams.toJson()}'); /// Create the socketAuthVerifiers as required Map expectedPayloadForSignature = { - 'sessionId': sshrvdSessionParams.sessionId, - 'clientNonce': sshrvdSessionParams.clientNonce, - 'rvdNonce': sshrvdSessionParams.rvdNonce, + 'sessionId': srvdSessionParams.sessionId, + 'clientNonce': srvdSessionParams.clientNonce, + 'rvdNonce': srvdSessionParams.rvdNonce, }; SocketAuthVerifier? socketAuthVerifierA; - if (sshrvdSessionParams.authenticateSocketA) { - String? pkAtSignA = sshrvdSessionParams.publicKeyA; + if (srvdSessionParams.authenticateSocketA) { + String? pkAtSignA = srvdSessionParams.publicKeyA; if (pkAtSignA == null) { logger.shout('Cannot spawn socket connector.' - ' Authenticator for ${sshrvdSessionParams.atSignA}' + ' Authenticator for ${srvdSessionParams.atSignA}' ' could not be created as PublicKey could not be' ' fetched from the atServer.'); throw Exception('Failed to create SocketAuthenticator' - ' for ${sshrvdSessionParams.atSignA} due to failure to get public key for ${sshrvdSessionParams.atSignA}'); + ' for ${srvdSessionParams.atSignA} due to failure to get public key for ${srvdSessionParams.atSignA}'); } socketAuthVerifierA = SignatureAuthVerifier( pkAtSignA, jsonEncode(expectedPayloadForSignature), - sshrvdSessionParams.rvdNonce!, - sshrvdSessionParams.atSignA, + srvdSessionParams.rvdNonce!, + srvdSessionParams.atSignA, ).authenticate; } SocketAuthVerifier? socketAuthVerifierB; - if (sshrvdSessionParams.authenticateSocketB) { - String? pkAtSignB = sshrvdSessionParams.publicKeyB; + if (srvdSessionParams.authenticateSocketB) { + String? pkAtSignB = srvdSessionParams.publicKeyB; if (pkAtSignB == null) { logger.shout('Cannot spawn socket connector.' - ' Authenticator for ${sshrvdSessionParams.atSignB}' + ' Authenticator for ${srvdSessionParams.atSignB}' ' could not be created as PublicKey could not be' ' fetched from the atServer'); throw Exception('Failed to create SocketAuthenticator' - ' for ${sshrvdSessionParams.atSignB} due to failure to get public key for ${sshrvdSessionParams.atSignB}'); + ' for ${srvdSessionParams.atSignB} due to failure to get public key for ${srvdSessionParams.atSignB}'); } socketAuthVerifierB = SignatureAuthVerifier( pkAtSignB, jsonEncode(expectedPayloadForSignature), - sshrvdSessionParams.rvdNonce!, - sshrvdSessionParams.atSignB!, + srvdSessionParams.rvdNonce!, + srvdSessionParams.atSignB!, ).authenticate; } @@ -104,7 +104,7 @@ void socketConnector(ConnectorParams connectorParams) async { portB = connector.sideBPort!; logger.info('Assigned ports [$portA, $portB]' - ' for session ${sshrvdSessionParams.sessionId}'); + ' for session ${srvdSessionParams.sessionId}'); /// Return the assigned ports to the main isolate sendPort.send((portA, portB)); @@ -113,8 +113,8 @@ void socketConnector(ConnectorParams connectorParams) async { logger.info('Waiting for connector to close'); await connector.done; - logger.info('Finished session ${sshrvdSessionParams.sessionId}' - ' for ${sshrvdSessionParams.atSignA} to ${sshrvdSessionParams.atSignB}' + logger.info('Finished session ${srvdSessionParams.sessionId}' + ' for ${srvdSessionParams.atSignA} to ${srvdSessionParams.atSignB}' ' using ports [$portA, $portB]'); Isolate.current.kill(); diff --git a/packages/dart/noports_core/lib/src/srvd/srvd.dart b/packages/dart/noports_core/lib/src/srvd/srvd.dart index b154a6f61..b2dae476c 100644 --- a/packages/dart/noports_core/lib/src/srvd/srvd.dart +++ b/packages/dart/noports_core/lib/src/srvd/srvd.dart @@ -6,7 +6,7 @@ import 'package:meta/meta.dart'; import 'package:noports_core/src/srvd/srvd_impl.dart'; import 'package:noports_core/src/srvd/srvd_params.dart'; -abstract class Sshrvd { +abstract class Srvd { static const String namespace = 'sshrvd'; abstract final AtSignLogger logger; @@ -23,11 +23,11 @@ abstract class Sshrvd { @visibleForTesting bool initialized = false; - static Future fromCommandLineArgs(List args, + static Future fromCommandLineArgs(List args, {AtClient? atClient, - FutureOr Function(SshrvdParams)? atClientGenerator, + FutureOr Function(SrvdParams)? atClientGenerator, void Function(Object, StackTrace)? usageCallback}) async { - return SshrvdImpl.fromCommandLineArgs(args, + return SrvdImpl.fromCommandLineArgs(args, atClient: atClient, atClientGenerator: atClientGenerator, usageCallback: usageCallback); diff --git a/packages/dart/noports_core/lib/src/srvd/srvd_impl.dart b/packages/dart/noports_core/lib/src/srvd/srvd_impl.dart index c51dfda92..95662a165 100644 --- a/packages/dart/noports_core/lib/src/srvd/srvd_impl.dart +++ b/packages/dart/noports_core/lib/src/srvd/srvd_impl.dart @@ -13,9 +13,9 @@ import 'package:noports_core/src/srvd/srvd.dart'; import 'package:noports_core/src/srvd/srvd_params.dart'; @protected -class SshrvdImpl implements Sshrvd { +class SrvdImpl implements Srvd { @override - final AtSignLogger logger = AtSignLogger(' sshrvd '); + final AtSignLogger logger = AtSignLogger(' srvd '); @override AtClient atClient; @override @@ -37,11 +37,11 @@ class SshrvdImpl implements Sshrvd { @visibleForTesting bool initialized = false; - static final String subscriptionRegex = '${Sshrvd.namespace}@'; + static final String subscriptionRegex = '${Srvd.namespace}@'; - late final SshrvdUtil sshrvdUtil; + late final SrvdUtil srvdUtil; - SshrvdImpl({ + SrvdImpl({ required this.atClient, required this.atSign, required this.homeDirectory, @@ -50,19 +50,19 @@ class SshrvdImpl implements Sshrvd { required this.ipAddress, required this.logTraffic, required this.verbose, - SshrvdUtil? sshrvdUtil, + SrvdUtil? srvdUtil, }) { - this.sshrvdUtil = sshrvdUtil ?? SshrvdUtil(atClient); + this.srvdUtil = srvdUtil ?? SrvdUtil(atClient); logger.hierarchicalLoggingEnabled = true; logger.logger.level = Level.SHOUT; } - static Future fromCommandLineArgs(List args, + static Future fromCommandLineArgs(List args, {AtClient? atClient, - FutureOr Function(SshrvdParams)? atClientGenerator, + FutureOr Function(SrvdParams)? atClientGenerator, void Function(Object, StackTrace)? usageCallback}) async { try { - var p = await SshrvdParams.fromArgs(args); + var p = await SrvdParams.fromArgs(args); if (!await File(p.atKeysFilePath).exists()) { throw ('\n Unable to find .atKeys file : ${p.atKeysFilePath}'); @@ -79,7 +79,7 @@ class SshrvdImpl implements Sshrvd { atClient ??= await atClientGenerator!(p); - var sshrvd = SshrvdImpl( + var srvd = SrvdImpl( atClient: atClient, atSign: p.atSign, homeDirectory: p.homeDirectory, @@ -91,9 +91,9 @@ class SshrvdImpl implements Sshrvd { ); if (p.verbose) { - sshrvd.logger.logger.level = Level.INFO; + srvd.logger.logger.level = Level.INFO; } - return sshrvd; + return srvd; } catch (e, s) { usageCallback?.call(e, s); rethrow; @@ -122,12 +122,12 @@ class SshrvdImpl implements Sshrvd { } void _notificationHandler(AtNotification notification) async { - if (!sshrvdUtil.accept(notification)) { + if (!srvdUtil.accept(notification)) { return; } - late SshrvdSessionParams sessionParams; + late SrvdSessionParams sessionParams; try { - sessionParams = await sshrvdUtil.getParams(notification); + sessionParams = await srvdUtil.getParams(notification); if (managerAtsign != 'open' && managerAtsign != sessionParams.atSignA) { logger.shout( @@ -163,7 +163,7 @@ class SshrvdImpl implements Sshrvd { ..key = sessionParams.sessionId ..sharedBy = atSign ..sharedWith = notification.from - ..namespace = Sshrvd.namespace + ..namespace = Srvd.namespace ..metadata = metaData; String data = '$ipAddress,$portA,$portB,${sessionParams.rvdNonce}'; @@ -188,18 +188,18 @@ class SshrvdImpl implements Sshrvd { Future _spawnSocketConnector( int portA, int portB, - SshrvdSessionParams sshrvdSessionParams, + SrvdSessionParams srvdSessionParams, bool logTraffic, bool verbose, ) async { /// Spawn an isolate and wait for it to send back the issued port numbers - ReceivePort receivePort = ReceivePort(sshrvdSessionParams.sessionId); + ReceivePort receivePort = ReceivePort(srvdSessionParams.sessionId); ConnectorParams parameters = ( receivePort.sendPort, portA, portB, - jsonEncode(sshrvdSessionParams), + jsonEncode(srvdSessionParams), BuildEnv.enableSnoop && logTraffic, verbose, ); @@ -212,13 +212,13 @@ class SshrvdImpl implements Sshrvd { PortPair ports = await receivePort.first; logger.info('Received ports $ports in main isolate' - ' for session ${sshrvdSessionParams.sessionId}'); + ' for session ${srvdSessionParams.sessionId}'); return ports; } } -class SshrvdSessionParams { +class SrvdSessionParams { final String sessionId; final String atSignA; final String? atSignB; @@ -229,7 +229,7 @@ class SshrvdSessionParams { final String? clientNonce; final String? rvdNonce; - SshrvdSessionParams({ + SrvdSessionParams({ required this.sessionId, required this.atSignA, this.atSignB, @@ -256,8 +256,8 @@ class SshrvdSessionParams { 'clientNonce': clientNonce, }; - static SshrvdSessionParams fromJson(Map json) { - return SshrvdSessionParams( + static SrvdSessionParams fromJson(Map json) { + return SrvdSessionParams( sessionId: json['sessionId'], atSignA: json['atSignA'], atSignB: json['atSignB'], @@ -271,30 +271,30 @@ class SshrvdSessionParams { } } -class SshrvdUtil { +class SrvdUtil { final AtClient atClient; - SshrvdUtil(this.atClient); + SrvdUtil(this.atClient); bool accept(AtNotification notification) { - return notification.key.contains(Sshrvd.namespace); + return notification.key.contains(Srvd.namespace); } - Future getParams(AtNotification notification) async { - if (notification.key.contains('.request_ports.${Sshrvd.namespace}')) { + Future getParams(AtNotification notification) async { + if (notification.key.contains('.request_ports.${Srvd.namespace}')) { return await _processJSONRequest(notification); } return _processLegacyRequest(notification); } - SshrvdSessionParams _processLegacyRequest(AtNotification notification) { - return SshrvdSessionParams( + SrvdSessionParams _processLegacyRequest(AtNotification notification) { + return SrvdSessionParams( sessionId: notification.value!, atSignA: notification.from, ); } - Future _processJSONRequest( + Future _processJSONRequest( AtNotification notification) async { dynamic jsonValue = jsonDecode(notification.value ?? ''); @@ -322,7 +322,7 @@ class SshrvdUtil { if (authenticateSocketB) { publicKeyB = await _fetchPublicKey(atSignB); } - return SshrvdSessionParams( + return SrvdSessionParams( sessionId: sessionId, atSignA: atSignA, atSignB: atSignB, diff --git a/packages/dart/noports_core/lib/src/srvd/srvd_params.dart b/packages/dart/noports_core/lib/src/srvd/srvd_params.dart index b936dd72f..8aa4bffe5 100644 --- a/packages/dart/noports_core/lib/src/srvd/srvd_params.dart +++ b/packages/dart/noports_core/lib/src/srvd/srvd_params.dart @@ -2,7 +2,7 @@ import 'package:args/args.dart'; import 'package:noports_core/src/common/file_system_utils.dart'; import 'package:noports_core/src/srvd/build_env.dart'; -class SshrvdParams { +class SrvdParams { final String username; final String atSign; final String homeDirectory; @@ -16,7 +16,7 @@ class SshrvdParams { // Non param variables static final ArgParser parser = _createArgParser(); - SshrvdParams({ + SrvdParams({ required this.username, required this.atSign, required this.homeDirectory, @@ -28,14 +28,14 @@ class SshrvdParams { required this.rootDomain, }); - static Future fromArgs(List args) async { + static Future fromArgs(List args) async { // Arg check ArgResults r = parser.parse(args); String atSign = r['atsign']; String homeDirectory = getHomeDirectory()!; - return SshrvdParams( + return SrvdParams( username: getUserName(throwIfNull: true)!, atSign: atSign, homeDirectory: homeDirectory, @@ -64,7 +64,7 @@ class SshrvdParams { 'atsign', abbr: 'a', mandatory: true, - help: 'atSign for sshrvd', + help: 'atSign for srvd', ); parser.addOption( 'manager', @@ -72,7 +72,7 @@ class SshrvdParams { defaultsTo: 'open', mandatory: false, help: - 'Managers atSign that sshrvd will accept requests from. Default is any atSign can use sshrvd', + 'Managers atSign that srvd will accept requests from. Default is any atSign can use srvd', ); parser.addOption( 'ip', diff --git a/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart b/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart index c51dfda92..95662a165 100644 --- a/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart +++ b/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart @@ -13,9 +13,9 @@ import 'package:noports_core/src/srvd/srvd.dart'; import 'package:noports_core/src/srvd/srvd_params.dart'; @protected -class SshrvdImpl implements Sshrvd { +class SrvdImpl implements Srvd { @override - final AtSignLogger logger = AtSignLogger(' sshrvd '); + final AtSignLogger logger = AtSignLogger(' srvd '); @override AtClient atClient; @override @@ -37,11 +37,11 @@ class SshrvdImpl implements Sshrvd { @visibleForTesting bool initialized = false; - static final String subscriptionRegex = '${Sshrvd.namespace}@'; + static final String subscriptionRegex = '${Srvd.namespace}@'; - late final SshrvdUtil sshrvdUtil; + late final SrvdUtil srvdUtil; - SshrvdImpl({ + SrvdImpl({ required this.atClient, required this.atSign, required this.homeDirectory, @@ -50,19 +50,19 @@ class SshrvdImpl implements Sshrvd { required this.ipAddress, required this.logTraffic, required this.verbose, - SshrvdUtil? sshrvdUtil, + SrvdUtil? srvdUtil, }) { - this.sshrvdUtil = sshrvdUtil ?? SshrvdUtil(atClient); + this.srvdUtil = srvdUtil ?? SrvdUtil(atClient); logger.hierarchicalLoggingEnabled = true; logger.logger.level = Level.SHOUT; } - static Future fromCommandLineArgs(List args, + static Future fromCommandLineArgs(List args, {AtClient? atClient, - FutureOr Function(SshrvdParams)? atClientGenerator, + FutureOr Function(SrvdParams)? atClientGenerator, void Function(Object, StackTrace)? usageCallback}) async { try { - var p = await SshrvdParams.fromArgs(args); + var p = await SrvdParams.fromArgs(args); if (!await File(p.atKeysFilePath).exists()) { throw ('\n Unable to find .atKeys file : ${p.atKeysFilePath}'); @@ -79,7 +79,7 @@ class SshrvdImpl implements Sshrvd { atClient ??= await atClientGenerator!(p); - var sshrvd = SshrvdImpl( + var srvd = SrvdImpl( atClient: atClient, atSign: p.atSign, homeDirectory: p.homeDirectory, @@ -91,9 +91,9 @@ class SshrvdImpl implements Sshrvd { ); if (p.verbose) { - sshrvd.logger.logger.level = Level.INFO; + srvd.logger.logger.level = Level.INFO; } - return sshrvd; + return srvd; } catch (e, s) { usageCallback?.call(e, s); rethrow; @@ -122,12 +122,12 @@ class SshrvdImpl implements Sshrvd { } void _notificationHandler(AtNotification notification) async { - if (!sshrvdUtil.accept(notification)) { + if (!srvdUtil.accept(notification)) { return; } - late SshrvdSessionParams sessionParams; + late SrvdSessionParams sessionParams; try { - sessionParams = await sshrvdUtil.getParams(notification); + sessionParams = await srvdUtil.getParams(notification); if (managerAtsign != 'open' && managerAtsign != sessionParams.atSignA) { logger.shout( @@ -163,7 +163,7 @@ class SshrvdImpl implements Sshrvd { ..key = sessionParams.sessionId ..sharedBy = atSign ..sharedWith = notification.from - ..namespace = Sshrvd.namespace + ..namespace = Srvd.namespace ..metadata = metaData; String data = '$ipAddress,$portA,$portB,${sessionParams.rvdNonce}'; @@ -188,18 +188,18 @@ class SshrvdImpl implements Sshrvd { Future _spawnSocketConnector( int portA, int portB, - SshrvdSessionParams sshrvdSessionParams, + SrvdSessionParams srvdSessionParams, bool logTraffic, bool verbose, ) async { /// Spawn an isolate and wait for it to send back the issued port numbers - ReceivePort receivePort = ReceivePort(sshrvdSessionParams.sessionId); + ReceivePort receivePort = ReceivePort(srvdSessionParams.sessionId); ConnectorParams parameters = ( receivePort.sendPort, portA, portB, - jsonEncode(sshrvdSessionParams), + jsonEncode(srvdSessionParams), BuildEnv.enableSnoop && logTraffic, verbose, ); @@ -212,13 +212,13 @@ class SshrvdImpl implements Sshrvd { PortPair ports = await receivePort.first; logger.info('Received ports $ports in main isolate' - ' for session ${sshrvdSessionParams.sessionId}'); + ' for session ${srvdSessionParams.sessionId}'); return ports; } } -class SshrvdSessionParams { +class SrvdSessionParams { final String sessionId; final String atSignA; final String? atSignB; @@ -229,7 +229,7 @@ class SshrvdSessionParams { final String? clientNonce; final String? rvdNonce; - SshrvdSessionParams({ + SrvdSessionParams({ required this.sessionId, required this.atSignA, this.atSignB, @@ -256,8 +256,8 @@ class SshrvdSessionParams { 'clientNonce': clientNonce, }; - static SshrvdSessionParams fromJson(Map json) { - return SshrvdSessionParams( + static SrvdSessionParams fromJson(Map json) { + return SrvdSessionParams( sessionId: json['sessionId'], atSignA: json['atSignA'], atSignB: json['atSignB'], @@ -271,30 +271,30 @@ class SshrvdSessionParams { } } -class SshrvdUtil { +class SrvdUtil { final AtClient atClient; - SshrvdUtil(this.atClient); + SrvdUtil(this.atClient); bool accept(AtNotification notification) { - return notification.key.contains(Sshrvd.namespace); + return notification.key.contains(Srvd.namespace); } - Future getParams(AtNotification notification) async { - if (notification.key.contains('.request_ports.${Sshrvd.namespace}')) { + Future getParams(AtNotification notification) async { + if (notification.key.contains('.request_ports.${Srvd.namespace}')) { return await _processJSONRequest(notification); } return _processLegacyRequest(notification); } - SshrvdSessionParams _processLegacyRequest(AtNotification notification) { - return SshrvdSessionParams( + SrvdSessionParams _processLegacyRequest(AtNotification notification) { + return SrvdSessionParams( sessionId: notification.value!, atSignA: notification.from, ); } - Future _processJSONRequest( + Future _processJSONRequest( AtNotification notification) async { dynamic jsonValue = jsonDecode(notification.value ?? ''); @@ -322,7 +322,7 @@ class SshrvdUtil { if (authenticateSocketB) { publicKeyB = await _fetchPublicKey(atSignB); } - return SshrvdSessionParams( + return SrvdSessionParams( sessionId: sessionId, atSignA: atSignA, atSignB: atSignB, diff --git a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index 5948420b7..ca76392e3 100644 --- a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -38,7 +38,7 @@ class SshnpDartPureImpl extends SshnpCore sessionId: sessionId, namespace: this.namespace, ); - _sshrvdChannel = SshrvdDartChannel( + _srvdChannel = SrvdDartChannel( atClient: atClient, params: params, sessionId: sessionId, @@ -50,8 +50,8 @@ class SshnpDartPureImpl extends SshnpCore late final SshnpdDefaultChannel _sshnpdChannel; @override - SshrvdDartChannel get sshrvdChannel => _sshrvdChannel; - late final SshrvdDartChannel _sshrvdChannel; + SrvdDartChannel get srvdChannel => _srvdChannel; + late final SrvdDartChannel _srvdChannel; @override Future initialize() async { @@ -86,11 +86,11 @@ class SshnpDartPureImpl extends SshnpCore SshnpSessionRequest( direct: true, sessionId: sessionId, - host: sshrvdChannel.host, - port: sshrvdChannel.port, + host: srvdChannel.host, + port: srvdChannel.port, authenticateToRvd: params.authenticateDeviceToRvd, - clientNonce: sshrvdChannel.clientNonce, - rvdNonce: sshrvdChannel.rvdNonce, + clientNonce: srvdChannel.clientNonce, + rvdNonce: srvdChannel.rvdNonce, encryptRvdTraffic: params.encryptRvdTraffic, clientEphemeralPK: params.sessionKP.atPublicKey.publicKey, clientEphemeralPKType: params.sessionKPType.name, diff --git a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart index 673d263c9..675891b96 100644 --- a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_openssh_local_impl.dart @@ -21,7 +21,7 @@ class SshnpOpensshLocalImpl extends SshnpCore sessionId: sessionId, namespace: this.namespace, ); - _sshrvdChannel = SshrvdExecChannel( + _srvdChannel = SrvdExecChannel( atClient: atClient, params: params, sessionId: sessionId, @@ -33,8 +33,8 @@ class SshnpOpensshLocalImpl extends SshnpCore late final SshnpdDefaultChannel _sshnpdChannel; @override - SshrvdExecChannel get sshrvdChannel => _sshrvdChannel; - late final SshrvdExecChannel _sshrvdChannel; + SrvdExecChannel get srvdChannel => _srvdChannel; + late final SrvdExecChannel _srvdChannel; @override Future initialize() async { @@ -68,11 +68,11 @@ class SshnpOpensshLocalImpl extends SshnpCore SshnpSessionRequest( direct: true, sessionId: sessionId, - host: sshrvdChannel.host, - port: sshrvdChannel.sshrvdPort!, + host: srvdChannel.host, + port: srvdChannel.srvdPort!, authenticateToRvd: params.authenticateDeviceToRvd, - clientNonce: sshrvdChannel.clientNonce, - rvdNonce: sshrvdChannel.rvdNonce, + clientNonce: srvdChannel.clientNonce, + rvdNonce: srvdChannel.rvdNonce, encryptRvdTraffic: params.encryptRvdTraffic, clientEphemeralPK: params.sessionKP.atPublicKey.publicKey, clientEphemeralPKType: params.sessionKPType.name, @@ -93,8 +93,8 @@ class SshnpOpensshLocalImpl extends SshnpCore ); } - /// Start sshrv - await sshrvdChannel.runSshrv( + /// Start srv + await srvdChannel.runSrv( directSsh: true, localRvPort: localRvPort, sessionAESKeyString: sshnpdChannel.sessionAESKeyString, diff --git a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart index e52f1a668..60b1af0bf 100644 --- a/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart @@ -22,7 +22,7 @@ class SshnpUnsignedImpl extends SshnpCore sessionId: sessionId, namespace: this.namespace, ); - _sshrvdChannel = SshrvdExecChannel( + _srvdChannel = SrvdExecChannel( atClient: atClient, params: params, sessionId: sessionId, @@ -34,8 +34,8 @@ class SshnpUnsignedImpl extends SshnpCore late final SshnpdUnsignedChannel _sshnpdChannel; @override - SshrvdExecChannel get sshrvdChannel => _sshrvdChannel; - late final SshrvdExecChannel _sshrvdChannel; + SrvdExecChannel get srvdChannel => _srvdChannel; + late final SrvdExecChannel _srvdChannel; @override Future initialize() async { @@ -87,7 +87,7 @@ class SshnpUnsignedImpl extends SshnpCore ..sharedBy = params.clientAtSign ..sharedWith = params.sshnpdAtSign ..metadata = (Metadata()..ttl = 10000), - '$localPort ${sshrvdChannel.port} ${keyUtil.username} ${sshrvdChannel.host} $sessionId', + '$localPort ${srvdChannel.port} ${keyUtil.username} ${srvdChannel.host} $sessionId', checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, ); @@ -98,8 +98,8 @@ class SshnpUnsignedImpl extends SshnpCore throw SshnpError('sshnpd did not acknowledge the request'); } - /// Start sshrv - var bean = await sshrvdChannel.runSshrv(directSsh: false); + /// Start srv + var bean = await srvdChannel.runSrv(directSsh: false); /// Ensure that we clean up after ourselves await callDisposal(); diff --git a/packages/dart/noports_core/lib/src/sshnp/models/sshnp_arg.dart b/packages/dart/noports_core/lib/src/sshnp/models/sshnp_arg.dart index 6e50cc96b..baf2bba4a 100644 --- a/packages/dart/noports_core/lib/src/sshnp/models/sshnp_arg.dart +++ b/packages/dart/noports_core/lib/src/sshnp/models/sshnp_arg.dart @@ -224,7 +224,7 @@ class SshnpArg { static const hostArg = SshnpArg( name: 'host', abbr: 'h', - help: 'atSign of sshrvd daemon or FQDN/IP address to connect back to', + help: 'atSign of srvd daemon or FQDN/IP address to connect back to', mandatory: true, ); static const portArg = SshnpArg( diff --git a/packages/dart/noports_core/lib/src/sshnp/sshnp.dart b/packages/dart/noports_core/lib/src/sshnp/sshnp.dart index 4ee1f996e..0cffdf3be 100644 --- a/packages/dart/noports_core/lib/src/sshnp/sshnp.dart +++ b/packages/dart/noports_core/lib/src/sshnp/sshnp.dart @@ -50,7 +50,7 @@ abstract interface class Sshnp { SshnpParams get params; /// May only be run after [initialize] has been run. - /// - Sends request to sshrvd if required + /// - Sends request to srvd if required /// - Sends request to sshnpd; the response listener was started by [initialize] /// - Waits for success or error response, or time out after 10 secs /// - Make ssh tunnel connection using ephemeral keys diff --git a/packages/dart/noports_core/lib/src/sshnp/sshnp_core.dart b/packages/dart/noports_core/lib/src/sshnp/sshnp_core.dart index 3fcd93fb7..b5e5acb0c 100644 --- a/packages/dart/noports_core/lib/src/sshnp/sshnp_core.dart +++ b/packages/dart/noports_core/lib/src/sshnp/sshnp_core.dart @@ -53,9 +53,9 @@ abstract class SshnpCore // * Communication Channels - /// The channel to communicate with the sshrvd (host) + /// The channel to communicate with the srvd (host) @protected - SshrvdChannel get sshrvdChannel; + SrvdChannel get srvdChannel; /// The channel to communicate with the sshnpd (daemon) @protected @@ -121,8 +121,8 @@ abstract class SshnpCore /// Shares the public key if required await sshnpdChannel.sharePublicKeyIfRequired(identityKeyPair); - /// Retrieve the sshrvd host and port pair - await sshrvdChannel.callInitialization(); + /// Retrieve the srvd host and port pair + await srvdChannel.callInitialization(); } @override diff --git a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart index 5f538c538..ef560bad9 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_channel.dart @@ -12,31 +12,31 @@ import 'package:noports_core/srv.dart'; import 'package:noports_core/srvd.dart'; @visibleForTesting -enum SshrvdAck { - /// sshrvd acknowledged our request +enum SrvdAck { + /// srvd acknowledged our request acknowledged, - /// sshrvd acknowledged our request and had errors + /// srvd acknowledged our request and had errors acknowledgedWithErrors, - /// sshrvd did not acknowledge our request + /// srvd did not acknowledge our request notAcknowledged, } -abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { +abstract class SrvdChannel with AsyncInitialization, AtClientBindings { @override - final logger = AtSignLogger(' SshrvdChannel '); + final logger = AtSignLogger(' SrvdChannel '); @override final AtClient atClient; - final SshrvGenerator sshrvGenerator; + final SrvGenerator srvGenerator; final SshnpParams params; final String sessionId; final String clientNonce = DateTime.now().toIso8601String(); // * Volatile fields which are set in [params] but may be overridden with - // * values provided by sshrvd + // * values provided by srvd String? _host; int? _portA; @@ -52,21 +52,21 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { String? sessionAESKeyString; String? sessionIVString; - /// Whether sshrvd acknowledged our request + /// Whether srvd acknowledged our request @visibleForTesting - SshrvdAck sshrvdAck = SshrvdAck.notAcknowledged; + SrvdAck srvdAck = SrvdAck.notAcknowledged; - /// The port sshrvd is listening on + /// The port srvd is listening on int? _portB; /// This is the port which the sshnp **daemon** will connect to - int? get sshrvdPort => _portB; + int? get srvdPort => _portB; - SshrvdChannel({ + SrvdChannel({ required this.atClient, required this.params, required this.sessionId, - required this.sshrvGenerator, + required this.srvGenerator, }) { logger.level = params.verbose ? 'info' : 'shout'; } @@ -74,7 +74,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { @override Future initialize() async { if (params.host.startsWith('@')) { - await getHostAndPortFromSshrvd(); + await getHostAndPortFromSrvd(); } else { _host = params.host; _portA = params.port; @@ -82,7 +82,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { completeInitialization(); } - Future runSshrv({ + Future runSrv({ required bool directSsh, int? localRvPort, String? sessionAESKeyString, @@ -97,14 +97,14 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { 'localRvPort must be non-null when using directSsh (default)'); } await callInitialization(); - if (_portB == null) throw Exception('sshrvdPort is null'); + if (_portB == null) throw Exception('srvdPort is null'); // Connect to rendezvous point using background process. // sshnp (this program) can then exit without issue. - late Srv sshrv; + late Srv srv; if (directSsh) { - sshrv = sshrvGenerator( + srv = srvGenerator( host, _portA!, localPort: localRvPort!, @@ -121,7 +121,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ); } else { // legacy behaviour - sshrv = sshrvGenerator( + srv = srvGenerator( host, _portB!, localPort: params.localSshdPort, @@ -129,16 +129,16 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ); } - return sshrv.run(); + return srv.run(); } @protected - Future getHostAndPortFromSshrvd() async { - sshrvdAck = SshrvdAck.notAcknowledged; - subscribe(regex: '$sessionId.${Sshrvd.namespace}@', shouldDecrypt: true) + Future getHostAndPortFromSrvd() async { + srvdAck = SrvdAck.notAcknowledged; + subscribe(regex: '$sessionId.${Srvd.namespace}@', shouldDecrypt: true) .listen((notification) async { String ipPorts = notification.value.toString(); - logger.info('Received from sshrvd: $ipPorts'); + logger.info('Received from srvd: $ipPorts'); List results = ipPorts.split(','); _host = results[0]; _portA = int.parse(results[1]); @@ -146,24 +146,24 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { if (results.length >= 4) { rvdNonce = results[3]; } - logger.info('Received from sshrvd:' + logger.info('Received from srvd:' ' host:port $host:$port' ' rvdNonce: $rvdNonce'); - logger.info('Set sshrvdPort to: $_portB'); - sshrvdAck = SshrvdAck.acknowledged; + logger.info('Set srvdPort to: $_portB'); + srvdAck = SrvdAck.acknowledged; }); - logger.info('Started listening for sshrvd response'); + logger.info('Started listening for srvd response'); late AtKey rvdRequestKey; late String rvdRequestValue; if (params.authenticateClientToRvd || params.authenticateDeviceToRvd) { rvdRequestKey = AtKey() - ..key = '${params.device}.request_ports.${Sshrvd.namespace}' + ..key = '${params.device}.request_ports.${Srvd.namespace}' ..sharedBy = params.clientAtSign // shared by us - ..sharedWith = host // shared with the sshrvd host + ..sharedWith = host // shared with the srvd host ..metadata = (Metadata() - // as we are sending a notification to the sshrvd namespace, + // as we are sending a notification to the srvd namespace, // we don't want to append our namespace ..namespaceAware = false ..ttl = 10000); @@ -180,11 +180,11 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } else { // send a legacy message since no new rvd features are being used rvdRequestKey = AtKey() - ..key = '${params.device}.${Sshrvd.namespace}' + ..key = '${params.device}.${Srvd.namespace}' ..sharedBy = params.clientAtSign // shared by us - ..sharedWith = host // shared with the sshrvd host + ..sharedWith = host // shared with the srvd host ..metadata = (Metadata() - // as we are sending a notification to the sshrvd namespace, + // as we are sending a notification to the srvd namespace, // we don't want to append our namespace ..namespaceAware = false ..ttl = 10000); @@ -193,7 +193,7 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { } logger.info( - 'Sending notification to sshrvd with key $rvdRequestKey and value $rvdRequestValue'); + 'Sending notification to srvd with key $rvdRequestKey and value $rvdRequestValue'); await notify( rvdRequestKey, rvdRequestValue, @@ -202,15 +202,15 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { ); int counter = 1; - while (sshrvdAck == SshrvdAck.notAcknowledged) { + while (srvdAck == SrvdAck.notAcknowledged) { if (counter % 20 == 0) { - logger.info('Still waiting for sshrvd response'); + logger.info('Still waiting for srvd response'); } await Future.delayed(Duration(milliseconds: 100)); counter++; if (counter > 150) { - logger.warning('Timed out waiting for sshrvd response'); - throw ('Connection timeout to sshrvd $host service\nhint: make sure host is valid and online'); + logger.warning('Timed out waiting for srvd response'); + throw ('Connection timeout to srvd $host service\nhint: make sure host is valid and online'); } } } diff --git a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart index 9fd815ce4..2d8a4512c 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_dart_channel.dart @@ -2,10 +2,10 @@ import 'package:noports_core/src/sshnp/util/srvd_channel/srvd_channel.dart'; import 'package:noports_core/srv.dart'; import 'package:socket_connector/socket_connector.dart'; -class SshrvdDartChannel extends SshrvdChannel { - SshrvdDartChannel({ +class SrvdDartChannel extends SrvdChannel { + SrvdDartChannel({ required super.atClient, required super.params, required super.sessionId, - }) : super(sshrvGenerator: Srv.dart); + }) : super(srvGenerator: Srv.dart); } diff --git a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart index 9a6d4fc2f..828cb87ea 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/srvd_channel/srvd_exec_channel.dart @@ -2,10 +2,10 @@ import 'package:noports_core/src/common/io_types.dart'; import 'package:noports_core/src/sshnp/util/srvd_channel/srvd_channel.dart'; import 'package:noports_core/srv.dart'; -class SshrvdExecChannel extends SshrvdChannel { - SshrvdExecChannel({ +class SrvdExecChannel extends SrvdChannel { + SrvdExecChannel({ required super.atClient, required super.params, required super.sessionId, - }) : super(sshrvGenerator: Srv.exec); + }) : super(srvGenerator: Srv.exec); } diff --git a/packages/dart/noports_core/lib/src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart b/packages/dart/noports_core/lib/src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart index 82bf67400..7078ac09d 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/ssh_session_handler/dart_ssh_session_handler.dart @@ -20,21 +20,21 @@ mixin DartSshSessionHandler on SshnpCore @override Future startInitialTunnelSession( {required String ephemeralKeyPairIdentifier, int? localRvPort}) async { - // If we are starting an initial tunnel, it should be to sshrvd, - // so it is safe to assume that sshrvdChannel is not null here + // If we are starting an initial tunnel, it should be to srvd, + // so it is safe to assume that srvdChannel is not null here var username = tunnelUsername ?? getUserName(throwIfNull: true)!; logger.info('Starting tunnel ssh session as $username' - ' to ${sshrvdChannel.host} on port ${sshrvdChannel.sshrvdPort!}'); + ' to ${srvdChannel.host} on port ${srvdChannel.srvdPort!}'); AtSshKeyPair keyPair = await keyUtil.getKeyPair(identifier: ephemeralKeyPairIdentifier); SshClientHelper helper = SshClientHelper(logger); SSHClient tunnelSshClient = await helper.createSshClient( - host: sshrvdChannel.host, - port: sshrvdChannel.sshrvdPort!, + host: srvdChannel.host, + port: srvdChannel.srvdPort!, username: username, keyPair: keyPair, ); diff --git a/packages/dart/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart b/packages/dart/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart index 020ef2aa4..80e8e144d 100644 --- a/packages/dart/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart +++ b/packages/dart/noports_core/lib/src/sshnp/util/ssh_session_handler/openssh_ssh_session_handler.dart @@ -15,7 +15,7 @@ mixin OpensshSshSessionHandler on SshnpCore @visibleForTesting ProcessStarter startProcess = Process.start, }) async { Process? process; - // If we are starting an initial tunnel, it should be to the local sshrv, + // If we are starting an initial tunnel, it should be to the local srv, // so it is safe to assume that localRvPort is non-null String argsString = '$tunnelUsername@localhost' ' -p ${localRvPort!}' diff --git a/packages/dart/noports_core/lib/src/sshnpd/sshnpd.dart b/packages/dart/noports_core/lib/src/sshnpd/sshnpd.dart index a6ff10859..6459987c6 100644 --- a/packages/dart/noports_core/lib/src/sshnpd/sshnpd.dart +++ b/packages/dart/noports_core/lib/src/sshnpd/sshnpd.dart @@ -10,7 +10,7 @@ import 'package:noports_core/src/sshnpd/sshnpd_params.dart'; abstract class Sshnpd { abstract final AtSignLogger logger; - /// The [AtClient] used to communicate with sshnpd and sshrvd + /// The [AtClient] used to communicate with sshnpd and srvd abstract AtClient atClient; // ==================================================================== diff --git a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart index 0066528b4..215f4b6f1 100644 --- a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -501,7 +501,7 @@ class SshnpdImpl implements Sshnpd { remoteForwardPort: int.parse(remoteForwardPort)); } - /// - Starts an sshrv process bridging the rvd to localhost:$localSshdPort + /// - Starts an srv process bridging the rvd to localhost:$localSshdPort /// - Generates an ephemeral keypair and adds its public key to the /// `authorized_keys` file, limiting permissions (e.g. hosts and ports /// which can be forwarded to) as per the `--ephemeral-permissions` option @@ -624,7 +624,7 @@ class SshnpdImpl implements Sshnpd { atKey: _createResponseAtKey( requestingAtsign: requestingAtsign, sessionId: sessionId), value: - 'Failed to start up the daemon side of the sshrv socket tunnel : $e', + 'Failed to start up the daemon side of the srv socket tunnel : $e', sessionId: sessionId, checkForFinalDeliveryStatus: false, waitForFinalDeliveryStatus: false, @@ -834,7 +834,7 @@ class SshnpdImpl implements Sshnpd { await Process.run('chmod', ['go-rwx', pemFile.absolute.path]); // When we receive notification 'sshd', WE are going to ssh to the host and port provided by sshnp - // which could be the host and port of a client machine, or the host and port of an sshrvd which is + // which could be the host and port of a client machine, or the host and port of an srvd which is // joined via socket connector to the client machine. Let's call it targetHostName/Port // // so: ssh username@targetHostName -p targetHostPort diff --git a/packages/dart/noports_core/lib/srv.dart b/packages/dart/noports_core/lib/srv.dart index 702324128..f2c30088c 100644 --- a/packages/dart/noports_core/lib/srv.dart +++ b/packages/dart/noports_core/lib/srv.dart @@ -1,3 +1,3 @@ -library noports_core_sshrv; +library noports_core_srv; export 'src/srv/srv.dart'; diff --git a/packages/dart/noports_core/lib/srvd.dart b/packages/dart/noports_core/lib/srvd.dart index f64ad7221..cec24ec15 100644 --- a/packages/dart/noports_core/lib/srvd.dart +++ b/packages/dart/noports_core/lib/srvd.dart @@ -1,4 +1,4 @@ -library noports_core_sshrvd; +library noports_core_srvd; export 'src/srvd/srvd.dart'; export 'src/srvd/srvd_params.dart'; diff --git a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart index 7c6f82b92..fbb752447 100644 --- a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart @@ -3,7 +3,7 @@ import 'package:mocktail/mocktail.dart'; import 'package:noports_core/sshnp_foundation.dart'; import 'package:noports_core/srv.dart'; -/// Stubbing for [SshrvGenerator] typedef +/// Stubbing for [SrvGenerator] typedef abstract class SshrvGeneratorCaller { Srv call( String host, diff --git a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart index 25ff5430f..d753dbf41 100644 --- a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart @@ -125,7 +125,7 @@ void main() { notificationStreamController.add( AtNotification.empty() ..id = Uuid().v4() - ..key = '$sessionId.${Sshrvd.namespace}' + ..key = '$sessionId.${Srvd.namespace}' ..from = '@sshrvd' ..to = '@client' ..epochMillis = DateTime.now().millisecondsSinceEpoch @@ -148,13 +148,13 @@ void main() { verifyInOrder([ () => subscribeStub( - regex: '$sessionId.${Sshrvd.namespace}@', shouldDecrypt: true), + regex: '$sessionId.${Srvd.namespace}@', shouldDecrypt: true), () => notifyStub( any( that: predicate( // Predicate matching specifically the sshrvdIdKey format (AtKey key) => - key.key == 'mydevice.request_ports.${Sshrvd.namespace}' && + key.key == 'mydevice.request_ports.${Srvd.namespace}' && key.sharedBy == '@client' && key.sharedWith == '@sshrvd' && key.metadata != null && diff --git a/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart b/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart index b32c63f98..39d3a9e31 100644 --- a/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart +++ b/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart @@ -5,15 +5,13 @@ import 'package:test/test.dart'; void main() { test('Test notification subscription regex', () { expect( - RegExp(SshrvdImpl.subscriptionRegex) - .hasMatch('jagan@test.${Sshrvd.namespace}@jagan'), + RegExp(SrvdImpl.subscriptionRegex) + .hasMatch('jagan@test.${Srvd.namespace}@jagan'), true); - expect( - RegExp(SshrvdImpl.subscriptionRegex).hasMatch('${Sshrvd.namespace}@'), + expect(RegExp(SrvdImpl.subscriptionRegex).hasMatch('${Srvd.namespace}@'), true); expect( - RegExp(SshrvdImpl.subscriptionRegex) - .hasMatch('${Sshrvd.namespace}.test@'), + RegExp(SrvdImpl.subscriptionRegex).hasMatch('${Srvd.namespace}.test@'), false); }); } diff --git a/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart index b7c3aeb85..af916cd92 100644 --- a/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart +++ b/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart @@ -11,14 +11,14 @@ void main() { test('test notification subscription regex', () { // Create a notification in rvd namespace AtNotification notification = AtNotification.empty(); - notification.key = 'test.${Sshrvd.namespace}'; + notification.key = 'test.${Srvd.namespace}'; }); test('sshrvd should accept notification in new request_ports format', () { // Create a notification in rvd namespace AtNotification notification = AtNotification.empty(); - notification.key = 'request_ports.test.${Sshrvd.namespace}'; - expect(SshrvdUtil(MockAtClient()).accept(notification), true); + notification.key = 'request_ports.test.${Srvd.namespace}'; + expect(SrvdUtil(MockAtClient()).accept(notification), true); }); test( @@ -33,9 +33,9 @@ void main() { // New message AtNotification notification = AtNotification.empty(); - notification.key = 'request_ports.test.${Sshrvd.namespace}'; + notification.key = 'request_ports.test.${Srvd.namespace}'; notification.value = jsonEncode(m); - expect(SshrvdUtil(MockAtClient()).accept(notification), true); + expect(SrvdUtil(MockAtClient()).accept(notification), true); }); } diff --git a/packages/dart/sshnoports/bin/srvd.dart b/packages/dart/sshnoports/bin/srvd.dart index 08e8a96f5..4f79a97a4 100644 --- a/packages/dart/sshnoports/bin/srvd.dart +++ b/packages/dart/sshnoports/bin/srvd.dart @@ -8,17 +8,17 @@ import 'package:sshnoports/src/print_version.dart'; void main(List args) async { AtSignLogger.root_level = 'SHOUT'; AtSignLogger.defaultLoggingHandler = AtSignLogger.stdErrLoggingHandler; - late final Sshrvd sshrvd; + late final Srvd sshrvd; try { - sshrvd = await Sshrvd.fromCommandLineArgs( + sshrvd = await Srvd.fromCommandLineArgs( args, atClientGenerator: (SshrvdParams p) => createAtClientCli( homeDirectory: p.homeDirectory, subDirectory: '.sshrvd', atsign: p.atSign, atKeysFilePath: p.atKeysFilePath, - namespace: Sshrvd.namespace, + namespace: Srvd.namespace, rootDomain: p.rootDomain, ), usageCallback: (e, s) { From 65bca5fc9594dfaacfe8025413a6393e94b20bb3 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 22 Jan 2024 14:41:38 +0800 Subject: [PATCH 59/60] chore: rename the remaining files from sshrv(d) to srv(d) --- .../composite/setup_entrypoints/action.yaml | 8 +- .github/workflows/end2end_tests.yaml | 10 +- .github/workflows/multibuild.yaml | 6 +- .github/workflows/prod_tests.yaml | 44 +++--- .../sshnp_client.py | 4 +- .../notification_subscription_test.dart | 0 ...e_verifying_socket_authenticator_test.dart | 0 .../srvdutil_test.dart} | 4 +- .../test/sshnp/sshnp_core_mocks.dart | 9 +- .../test/sshnp/sshnp_core_test.dart | 15 +- .../noports_core/test/sshnp/sshnp_mocks.dart | 2 +- .../util/srvd_channel/srvd_channel_mocks.dart | 14 +- .../util/srvd_channel/srvd_channel_test.dart | 128 +++++++++--------- .../srvd_channel/srvd_dart_channel_test.dart | 8 +- .../srvd_channel/srvd_exec_channel_test.dart | 8 +- .../openssh_ssh_session_handler_mocks.dart | 8 +- .../openssh_ssh_session_handler_test.dart | 8 +- .../sshnp_local_ssh_key_handler_test.dart | 13 +- .../sshnp_ssh_key_handler_mocks.dart | 9 +- packages/dart/sshnoports/README.md | 20 +-- packages/dart/sshnoports/bin/srvd.dart | 14 +- .../core/config/sshnp-config-template.env | 2 +- .../bundles/shell/headless/README.md | 14 +- .../bundles/shell/headless/root_sshrvd.sh | 2 +- .../bundles/shell/headless/sshrvd.sh | 2 +- .../dart/sshnoports/bundles/shell/install.sh | 52 +++---- .../bundles/shell/systemd/README.md | 18 +-- .../bundles/shell/systemd/sshrvd.service | 2 +- packages/dart/sshnoports/tools/Dockerfile | 4 +- .../dart/sshnoports/tools/Dockerfile.package | 14 +- .../contexts/_init_/setup-sshnp-entrypoint.sh | 6 +- .../_init_/setup-sshrvd-entrypoint.sh | 14 +- .../contexts/_init_/setup-sshrvd-keys.sh | 12 +- .../entrypoints/sshnp_entrypoint.sh | 2 +- .../entrypoints/sshnp_installer_entrypoint.sh | 2 +- .../entrypoints/sshrvd_entrypoint.sh | 2 +- tests/end2end_tests/image/Dockerfile | 10 +- .../tests/service-container-sshnpd.yaml | 2 +- .../tests/service-container-sshrvd.yaml | 8 +- tools/manual-docker/README.md | 4 +- tools/manual-docker/blank/docker-compose.yaml | 11 +- .../manual-docker/branch/docker-compose.yaml | 10 +- tools/manual-docker/local/docker-compose.yaml | 11 +- .../manual-docker/release/docker-compose.yaml | 4 +- tools/manual-docker/run-manual-docker.sh | 10 +- tools/notarize-macos.sh | 4 +- tools/package-macos-arm64.sh | 6 +- 47 files changed, 276 insertions(+), 284 deletions(-) rename packages/dart/noports_core/test/{sshrvd => srvd}/notification_subscription_test.dart (100%) rename packages/dart/noports_core/test/{sshrvd => srvd}/signature_verifying_socket_authenticator_test.dart (100%) rename packages/dart/noports_core/test/{sshrvd/sshrvdutil_test.dart => srvd/srvdutil_test.dart} (86%) diff --git a/.github/composite/setup_entrypoints/action.yaml b/.github/composite/setup_entrypoints/action.yaml index 46213cd89..4d4b07678 100644 --- a/.github/composite/setup_entrypoints/action.yaml +++ b/.github/composite/setup_entrypoints/action.yaml @@ -15,8 +15,8 @@ inputs: sshnpd_atsign: description: sshnpd atsign required: true - sshrvd_atsign: - description: sshrvd atsign + srvd_atsign: + description: srvd atsign required: true devicename: description: Unique sshnp devicename @@ -52,7 +52,7 @@ runs: esac ;; esac - ./setup-sshnp-entrypoint.sh ${{ inputs.devicename }} ${{ inputs.sshnp_atsign }} ${{ inputs.sshnpd_atsign }} ${{ inputs.sshrvd_atsign }} "$entrypoint_filename" "$args ${{ inputs.args }}" + ./setup-sshnp-entrypoint.sh ${{ inputs.devicename }} ${{ inputs.sshnp_atsign }} ${{ inputs.sshnpd_atsign }} ${{ inputs.srvd_atsign }} "$entrypoint_filename" "$args ${{ inputs.args }}" - name: Setup NPD entrypoint shell: bash @@ -73,4 +73,4 @@ runs: shell: bash working-directory: tests/end2end_tests/contexts/_init_ run: | - ./setup-sshrvd-entrypoint.sh ${{ inputs.sshrvd_atsign }} "sshrvd_entrypoint.sh" + ./setup-srvd-entrypoint.sh ${{ inputs.srvd_atsign }} "srvd_entrypoint.sh" diff --git a/.github/workflows/end2end_tests.yaml b/.github/workflows/end2end_tests.yaml index 6ee09e4ca..26f8c2ac1 100644 --- a/.github/workflows/end2end_tests.yaml +++ b/.github/workflows/end2end_tests.yaml @@ -16,7 +16,7 @@ on: env: SSHNP_ATSIGN: "@8incanteater" SSHNPD_ATSIGN: "@8052simple" - SSHRVD_ATSIGN: "@8485wealthy51" + SRVD_ATSIGN: "@8485wealthy51" PROD_AM_RVD_ATSIGN: "@rv_am" PROD_AP_RVD_ATSIGN: "@rv_ap" @@ -87,7 +87,7 @@ jobs: sshnp_atsign: ${{ env.SSHNP_ATSIGN }} sshnpd: ${{ matrix.npd }} sshnpd_atsign: ${{ env.SSHNPD_ATSIGN }} - sshrvd_atsign: ${{ env[env.PROD_RVD_ATSIGN] }} + srvd_atsign: ${{ env[env.PROD_RVD_ATSIGN] }} devicename: ${{ env.DEVICENAME }} - name: Ensure entrypoints exist @@ -95,7 +95,7 @@ jobs: run: | cat sshnp/entrypoint.sh cat sshnpd/entrypoint.sh - cat sshrvd/entrypoint.sh + cat srvd/entrypoint.sh - name: Create docker-compose.yaml working-directory: tests/end2end_tests/tests @@ -247,7 +247,7 @@ jobs: sshnp_atsign: ${{ env.SSHNP_ATSIGN }} sshnpd: ${{ matrix.npd }} sshnpd_atsign: ${{ env.SSHNPD_ATSIGN }} - sshrvd_atsign: ${{ env[env.PROD_RVD_ATSIGN] }} + srvd_atsign: ${{ env[env.PROD_RVD_ATSIGN] }} devicename: ${{ env.DEVICENAME }} args: "-P 55" @@ -256,7 +256,7 @@ jobs: run: | cat sshnp/entrypoint.sh cat sshnpd/entrypoint.sh - cat sshrvd/entrypoint.sh + cat srvd/entrypoint.sh - name: Create docker-compose.yaml working-directory: tests/end2end_tests/tests diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index 1d9491df0..30932ace9 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -55,11 +55,11 @@ jobs: - if: ${{ matrix.os != 'windows-latest' }} run: dart compile exe bin/sshnpd.dart -v -o sshnp/sshnpd${{ matrix.ext }} - if: ${{ matrix.os != 'windows-latest' }} - run: dart compile exe bin/sshrv.dart -v -o sshnp/sshrv${{ matrix.ext }} + run: dart compile exe bin/srv.dart -v -o sshnp/srv${{ matrix.ext }} - if: ${{ matrix.os != 'windows-latest' }} - run: dart compile exe bin/sshrvd.dart -v -o sshnp/sshrvd${{ matrix.ext }} + run: dart compile exe bin/srvd.dart -v -o sshnp/srvd${{ matrix.ext }} - if: ${{ matrix.os != 'windows-latest' }} - run: dart compile exe bin/sshrvd.dart -D ENABLE_SNOOP=true -v -o sshnp/debug/sshrvd${{ matrix.ext }} + run: dart compile exe bin/srvd.dart -D ENABLE_SNOOP=true -v -o sshnp/debug/srvd${{ matrix.ext }} - run: cp -r bundles/core/* sshnp/ - run: cp -r bundles/${{ matrix.bundle }}/* sshnp/ - run: cp LICENSE sshnp diff --git a/.github/workflows/prod_tests.yaml b/.github/workflows/prod_tests.yaml index 6cbff2587..88683e8d2 100644 --- a/.github/workflows/prod_tests.yaml +++ b/.github/workflows/prod_tests.yaml @@ -14,10 +14,10 @@ permissions: env: SSHNP_ATSIGN: "@8incanteater" SSHNPD_ATSIGN: "@8052simple" - SSHRVD_ATSIGN: "@8485wealthy51" - SSHRVD_AM_ATSIGN: "@rv_am" - SSHRVD_AP_ATSIGN: "@rv_ap" - SSHRVD_EU_ATSIGN: "@rv_eu" + SRVD_ATSIGN: "@8485wealthy51" + SRVD_AM_ATSIGN: "@rv_am" + SRVD_AP_ATSIGN: "@rv_ap" + SRVD_EU_ATSIGN: "@rv_eu" DOCKER_COMPOSE_BUILD_CMD: "docker compose build" DOCKER_COMPOSE_UP_CMD: "docker compose up --abort-on-container-exit" @@ -28,10 +28,10 @@ jobs: fail-fast: false # if one job fails, do not fail the others matrix: rvd: - # - ${{ env.SSHRVD_ATSIGN }} - # - ${{ env.SSHRVD_AM_ATSIGN }} - # - ${{ env.SSHRVD_AP_ATSIGN }} - # - ${{ env.SSHRVD_EU_ATSIGN }} + # - ${{ env.SRVD_ATSIGN }} + # - ${{ env.SRVD_AM_ATSIGN }} + # - ${{ env.SRVD_AP_ATSIGN }} + # - ${{ env.SRVD_EU_ATSIGN }} - "@8485wealthy51" - "@rv_am" - "@rv_ap" @@ -52,8 +52,8 @@ jobs: SSHNPD_ATKEYS="$(tr '[:lower:]' '[:upper:]' <<< '${{ env.SSHNPD_ATSIGN }}')" echo "SSHNPD_ATKEYS=ATKEYS_${SSHNPD_ATKEYS:1}" >> $GITHUB_ENV - SSHRVD_ATKEYS="$(tr '[:lower:]' '[:upper:]' <<< '${{ env.SSHRVD_ATSIGN }}')" - echo "SSHRVD_ATKEYS=ATKEYS_${SSHRVD_ATKEYS:1}" >> $GITHUB_ENV + SRVD_ATKEYS="$(tr '[:lower:]' '[:upper:]' <<< '${{ env.SRVD_ATSIGN }}')" + echo "SRVD_ATKEYS=ATKEYS_${SRVD_ATKEYS:1}" >> $GITHUB_ENV - name: Setup NP/NPD keys working-directory: tests/end2end_tests/contexts @@ -84,18 +84,18 @@ jobs: sshnpd_entrypoint.sh - name: Set up RVD keys and entrypoint - if: matrix.rvd == env.SSHRVD_ATSIGN + if: matrix.rvd == env.SRVD_ATSIGN working-directory: tests/end2end_tests run: | # setup keys - echo "${{ secrets[env.SSHRVD_ATKEYS] }}" > contexts/sshrvd/.atsign/keys/${{ env.SSHRVD_ATSIGN }}_key.atKeys + echo "${{ secrets[env.SRVD_ATKEYS] }}" > contexts/srvd/.atsign/keys/${{ env.SRVD_ATSIGN }}_key.atKeys - # set up sshrvd entrypoint + # set up srvd entrypoint cd contexts/_init_ - ./setup-sshrvd-entrypoint.sh \ + ./setup-srvd-entrypoint.sh \ ${{ matrix.rvd }} \ - sshrvd_entrypoint.sh - cd ../sshrvd + srvd_entrypoint.sh + cd ../srvd cat entrypoint.sh - name: Ensure entrypoints exist @@ -118,8 +118,8 @@ jobs: echo " condition: service_started" >> docker-compose.yaml echo " container-sshnpd:" >> docker-compose.yaml echo " condition: service_healthy" >> docker-compose.yaml - if [ "${{ matrix.rvd }}" == "${{ env.SSHRVD_ATSIGN }}" ]; then - echo " container-sshrvd:" >> docker-compose.yaml + if [ "${{ matrix.rvd }}" == "${{ env.SRVD_ATSIGN }}" ]; then + echo " container-srvd:" >> docker-compose.yaml echo " condition: service_healthy" >> docker-compose.yaml fi cat service-container-sshnpd.yaml >> docker-compose.yaml @@ -127,16 +127,16 @@ jobs: echo " depends_on:" >> docker-compose.yaml echo " image-runtime-release:" >> docker-compose.yaml echo " condition: service_started" >> docker-compose.yaml - if [ "${{ matrix.rvd }}" == "${{ env.SSHRVD_ATSIGN }}" ]; then - echo " container-sshrvd:" >> docker-compose.yaml + if [ "${{ matrix.rvd }}" == "${{ env.SRVD_ATSIGN }}" ]; then + echo " container-srvd:" >> docker-compose.yaml echo " condition: service_healthy" >> docker-compose.yaml fi - name: Add RVD service to docker-compose.yaml - if: matrix.rvd == env.SSHRVD_ATSIGN + if: matrix.rvd == env.SRVD_ATSIGN working-directory: tests/end2end_tests/tests run: | - cat service-container-sshrvd.yaml >> docker-compose.yaml + cat service-container-srvd.yaml >> docker-compose.yaml echo " image: atsigncompany/sshnp-e2e-runtime:latest" >> docker-compose.yaml echo " depends_on:" >> docker-compose.yaml echo " image-runtime-release:" >> docker-compose.yaml diff --git a/examples/automations/python/sshnoports_automation_python/sshnp_client.py b/examples/automations/python/sshnoports_automation_python/sshnp_client.py index 20a0eec41..2dfe0d4d9 100644 --- a/examples/automations/python/sshnoports_automation_python/sshnp_client.py +++ b/examples/automations/python/sshnoports_automation_python/sshnp_client.py @@ -252,7 +252,7 @@ def download_package_source(self, source: PackageSource) -> str: f"dart compile exe {target_path}/bin/sshnpd.dart -o {target_path}/sshnpd" ) self.client.run_command( - f"dart compile exe {target_path}/bin/sshrv.dart -o {target_path}/sshrv" + f"dart compile exe {target_path}/bin/srv.dart -o {target_path}/srv" ) self.client.run_command( f"dart compile exe {target_path}/bin/activate_cli.dart -o {target_path}/at_activate" @@ -280,7 +280,7 @@ def setup_main_binaries(self, source: str) -> None: if not self.is_connected(): raise Exception("SSHNPClient not connected to device") - binaries = "{" + ",".join(["sshnpd", "sshrv", "at_activate"]) + "}" + binaries = "{" + ",".join(["sshnpd", "srv", "at_activate"]) + "}" self.client.exec_command(f"cp -f {source}/{binaries} ~/.local/bin/") def update_sshnpd(self, source: PackageSource) -> None: diff --git a/packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart b/packages/dart/noports_core/test/srvd/notification_subscription_test.dart similarity index 100% rename from packages/dart/noports_core/test/sshrvd/notification_subscription_test.dart rename to packages/dart/noports_core/test/srvd/notification_subscription_test.dart diff --git a/packages/dart/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart b/packages/dart/noports_core/test/srvd/signature_verifying_socket_authenticator_test.dart similarity index 100% rename from packages/dart/noports_core/test/sshrvd/signature_verifying_socket_authenticator_test.dart rename to packages/dart/noports_core/test/srvd/signature_verifying_socket_authenticator_test.dart diff --git a/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart b/packages/dart/noports_core/test/srvd/srvdutil_test.dart similarity index 86% rename from packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart rename to packages/dart/noports_core/test/srvd/srvdutil_test.dart index af916cd92..1f0c53394 100644 --- a/packages/dart/noports_core/test/sshrvd/sshrvdutil_test.dart +++ b/packages/dart/noports_core/test/srvd/srvdutil_test.dart @@ -14,7 +14,7 @@ void main() { notification.key = 'test.${Srvd.namespace}'; }); - test('sshrvd should accept notification in new request_ports format', () { + test('srvd should accept notification in new request_ports format', () { // Create a notification in rvd namespace AtNotification notification = AtNotification.empty(); notification.key = 'request_ports.test.${Srvd.namespace}'; @@ -22,7 +22,7 @@ void main() { }); test( - 'sshrvd backwards compatibility test - should handle both legacy and new messages in JSON format', + 'srvd backwards compatibility test - should handle both legacy and new messages in JSON format', () async { Map m = {}; m['session'] = 'hello'; diff --git a/packages/dart/noports_core/test/sshnp/sshnp_core_mocks.dart b/packages/dart/noports_core/test/sshnp/sshnp_core_mocks.dart index 3fdded77b..36bdca7ef 100644 --- a/packages/dart/noports_core/test/sshnp/sshnp_core_mocks.dart +++ b/packages/dart/noports_core/test/sshnp/sshnp_core_mocks.dart @@ -10,9 +10,9 @@ class StubbedSshnp extends SshnpCore with StubbedAsyncInitializationMixin { required super.atClient, required super.params, SshnpdChannel? sshnpdChannel, - SshrvdChannel? sshrvdChannel, + SrvdChannel? srvdChannel, }) : _sshnpdChannel = sshnpdChannel, - _sshrvdChannel = sshrvdChannel; + _srvdChannel = srvdChannel; @override Future initialize() async { @@ -37,9 +37,8 @@ class StubbedSshnp extends SshnpCore with StubbedAsyncInitializationMixin { final SshnpdChannel? _sshnpdChannel; @override - SshrvdChannel get sshrvdChannel => - _sshrvdChannel ?? (throw UnimplementedError()); - final SshrvdChannel? _sshrvdChannel; + SrvdChannel get srvdChannel => _srvdChannel ?? (throw UnimplementedError()); + final SrvdChannel? _srvdChannel; @override bool get canRunShell => throw UnimplementedError(); diff --git a/packages/dart/noports_core/test/sshnp/sshnp_core_test.dart b/packages/dart/noports_core/test/sshnp/sshnp_core_test.dart index 5804a6290..ad1d4f05b 100644 --- a/packages/dart/noports_core/test/sshnp/sshnp_core_test.dart +++ b/packages/dart/noports_core/test/sshnp/sshnp_core_test.dart @@ -14,7 +14,7 @@ void main() { late AtClient mockAtClient; late SshnpParams mockParams; late SshnpdChannel mockSshnpdChannel; - late SshrvdChannel mockSshrvdChannel; + late SrvdChannel mockSrvdChannel; /// Initialization stubs late FunctionStub stubbedCallInitialization; @@ -26,7 +26,7 @@ void main() { mockAtClient = MockAtClient(); mockParams = MockSshnpParams(); mockSshnpdChannel = MockSshnpdChannel(); - mockSshrvdChannel = MockSshrvdChannel(); + mockSrvdChannel = MockSrvdChannel(); registerFallbackValue(AtClientPreference()); /// Initialization @@ -61,8 +61,7 @@ void main() { .thenAnswer((_) async => 'myTunnelUsername'); when(() => mockSshnpdChannel.sharePublicKeyIfRequired( identityKeyPair ?? any())).thenAnswer((_) async {}); - when(() => mockSshrvdChannel.callInitialization()) - .thenAnswer((_) async {}); + when(() => mockSrvdChannel.callInitialization()).thenAnswer((_) async {}); } group('Constructor', () { @@ -112,7 +111,7 @@ void main() { atClient: mockAtClient, params: mockParams, sshnpdChannel: mockSshnpdChannel, - sshrvdChannel: mockSshrvdChannel, + srvdChannel: mockSrvdChannel, ); /// Setup stubs for the mocks that are part of [MockAsyncInitializationMixin] @@ -142,7 +141,7 @@ void main() { remoteUsername: 'myRemoteUsername'), () => mockSshnpdChannel .sharePublicKeyIfRequired(sshnpCore.identityKeyPair), - () => mockSshrvdChannel.callInitialization(), + () => mockSrvdChannel.callInitialization(), () => stubbedCompleteInitialization(), ]); @@ -155,7 +154,7 @@ void main() { remoteUsername: 'myRemoteUsername')); verifyNever(() => mockSshnpdChannel .sharePublicKeyIfRequired(sshnpCore.identityKeyPair)); - verifyNever(() => mockSshrvdChannel.callInitialization()); + verifyNever(() => mockSrvdChannel.callInitialization()); verifyNever(() => stubbedCompleteInitialization()); /// Ensure [initialize()] is not ran a second time if we call @@ -164,7 +163,7 @@ void main() { verify(() => stubbedCallInitialization()).called(1); verifyNever(() => stubbedInitialize()); verifyNever(() => stubbedCompleteInitialization()); - verifyNever(() => mockSshrvdChannel.callInitialization()); + verifyNever(() => mockSrvdChannel.callInitialization()); }); test('tunnelUsername not supplied', () async { final params = SshnpParams( diff --git a/packages/dart/noports_core/test/sshnp/sshnp_mocks.dart b/packages/dart/noports_core/test/sshnp/sshnp_mocks.dart index fdf023d52..36e0b4330 100644 --- a/packages/dart/noports_core/test/sshnp/sshnp_mocks.dart +++ b/packages/dart/noports_core/test/sshnp/sshnp_mocks.dart @@ -38,7 +38,7 @@ class MockSshnpParams extends Mock implements SshnpParams {} class MockSshnpdChannel extends Mock implements SshnpdChannel {} -class MockSshrvdChannel extends Mock implements SshrvdChannel {} +class MockSrvdChannel extends Mock implements SrvdChannel {} /// [dart:io] Mocks class MockProcess extends Mock implements Process {} diff --git a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart index fbb752447..54711f253 100644 --- a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_mocks.dart @@ -4,7 +4,7 @@ import 'package:noports_core/sshnp_foundation.dart'; import 'package:noports_core/srv.dart'; /// Stubbing for [SrvGenerator] typedef -abstract class SshrvGeneratorCaller { +abstract class SrvGeneratorCaller { Srv call( String host, int port, { @@ -16,12 +16,12 @@ abstract class SshrvGeneratorCaller { }); } -class SshrvGeneratorStub extends Mock implements SshrvGeneratorCaller {} +class SrvGeneratorStub extends Mock implements SrvGeneratorCaller {} -class MockSshrv extends Mock implements Srv {} +class MockSrv extends Mock implements Srv {} -/// Stubbed [SshrvdChannel] which we are testing -class StubbedSshrvdChannel extends SshrvdChannel { +/// Stubbed [SrvdChannel] which we are testing +class StubbedSrvdChannel extends SrvdChannel { final Future Function( AtKey, String, { @@ -31,11 +31,11 @@ class StubbedSshrvdChannel extends SshrvdChannel { final Stream Function({String? regex, bool shouldDecrypt})? _subscribe; - StubbedSshrvdChannel({ + StubbedSrvdChannel({ required super.atClient, required super.params, required super.sessionId, - required super.sshrvGenerator, + required super.srvGenerator, Future Function( AtKey, String, { diff --git a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart index d753dbf41..20d6952c4 100644 --- a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_channel_test.dart @@ -14,16 +14,16 @@ import '../../sshnp_mocks.dart'; import 'srvd_channel_mocks.dart'; void main() { - group('SshrvdChannel', () { - late SshrvGeneratorStub sshrvGeneratorStub; + group('SrvdChannel', () { + late SrvGeneratorStub srvGeneratorStub; late MockAtClient mockAtClient; late StreamController notificationStreamController; late NotifyStub notifyStub; late SubscribeStub subscribeStub; late MockSshnpParams mockParams; late String sessionId; - late StubbedSshrvdChannel stubbedSshrvdChannel; - late MockSshrv mockSshrv; + late StubbedSrvdChannel stubbedSrvdChannel; + late MockSrv mockSrv; // Invocation patterns as closures so they can be referred to by name // instead of explicitly writing these calls several times in the test @@ -38,14 +38,14 @@ void main() { regex: any(named: 'regex'), shouldDecrypt: any(named: 'shouldDecrypt'), ); - sshrvGeneratorInvocation() => sshrvGeneratorStub(any(), any(), + srvGeneratorInvocation() => srvGeneratorStub(any(), any(), localPort: any(named: 'localPort'), bindLocalPort: any(named: 'bindLocalPort'), rvdAuthString: any(named: 'rvdAuthString')); - sshrvRunInvocation() => mockSshrv.run(); + srvRunInvocation() => mockSrv.run(); setUp(() { - sshrvGeneratorStub = SshrvGeneratorStub(); + srvGeneratorStub = SrvGeneratorStub(); mockAtClient = MockAtClient(); notificationStreamController = StreamController(); notifyStub = NotifyStub(); @@ -53,13 +53,13 @@ void main() { mockParams = MockSshnpParams(); when(() => mockParams.verbose).thenReturn(false); sessionId = Uuid().v4(); - mockSshrv = MockSshrv(); + mockSrv = MockSrv(); - stubbedSshrvdChannel = StubbedSshrvdChannel( + stubbedSrvdChannel = StubbedSrvdChannel( atClient: mockAtClient, params: mockParams, sessionId: sessionId, - sshrvGenerator: sshrvGeneratorStub, + srvGenerator: srvGeneratorStub, notify: notifyStub, subscribe: subscribeStub, ); @@ -83,27 +83,27 @@ void main() { // members which do not need further tests // Base type - expect(stubbedSshrvdChannel, isA>()); - expect(stubbedSshrvdChannel, isA()); - expect(stubbedSshrvdChannel, isA()); + expect(stubbedSrvdChannel, isA>()); + expect(stubbedSrvdChannel, isA()); + expect(stubbedSrvdChannel, isA()); // final params - expect(stubbedSshrvdChannel.logger, isA()); + expect(stubbedSrvdChannel.logger, isA()); expect( - stubbedSshrvdChannel.sshrvGenerator, + stubbedSrvdChannel.srvGenerator, isA< Srv Function(String, int, {required int localPort, required bool bindLocalPort, String? rvdAuthString})>(), ); - expect(stubbedSshrvdChannel.atClient, mockAtClient); - expect(stubbedSshrvdChannel.params, mockParams); - expect(stubbedSshrvdChannel.sessionId, sessionId); + expect(stubbedSrvdChannel.atClient, mockAtClient); + expect(stubbedSrvdChannel.params, mockParams); + expect(stubbedSrvdChannel.sessionId, sessionId); }); // test public API - whenInitializationWithSshrvdHost() { - when(() => mockParams.host).thenReturn('@sshrvd'); + whenInitializationWithSrvdHost() { + when(() => mockParams.host).thenReturn('@srvd'); when(() => mockParams.device).thenReturn('mydevice'); when(() => mockParams.clientAtSign).thenReturn('@client'); when(() => mockParams.sshnpdAtSign).thenReturn('@sshnpd'); @@ -126,7 +126,7 @@ void main() { AtNotification.empty() ..id = Uuid().v4() ..key = '$sessionId.${Srvd.namespace}' - ..from = '@sshrvd' + ..from = '@srvd' ..to = '@client' ..epochMillis = DateTime.now().millisecondsSinceEpoch ..value = '$testIp,$portA,$portB,$rvdSessionNonce', @@ -135,16 +135,16 @@ void main() { ); } - test('Initialization - sshrvd host', () async { + test('Initialization - srvd host', () async { /// Set the required parameters - whenInitializationWithSshrvdHost(); - expect(stubbedSshrvdChannel.sshrvdAck, SshrvdAck.notAcknowledged); - expect(stubbedSshrvdChannel.initializeStarted, false); + whenInitializationWithSrvdHost(); + expect(stubbedSrvdChannel.srvdAck, SrvdAck.notAcknowledged); + expect(stubbedSrvdChannel.initializeStarted, false); verifyNever(subscribeInvocation); verifyNever(notifyInvocation); - await expectLater(stubbedSshrvdChannel.initialize(), completes); + await expectLater(stubbedSrvdChannel.initialize(), completes); verifyInOrder([ () => subscribeStub( @@ -152,11 +152,11 @@ void main() { () => notifyStub( any( that: predicate( - // Predicate matching specifically the sshrvdIdKey format + // Predicate matching specifically the srvdIdKey format (AtKey key) => key.key == 'mydevice.request_ports.${Srvd.namespace}' && key.sharedBy == '@client' && - key.sharedWith == '@sshrvd' && + key.sharedWith == '@srvd' && key.metadata != null && key.metadata!.namespaceAware == false && key.metadata!.ttl == 10000, @@ -173,65 +173,65 @@ void main() { verifyNever(subscribeInvocation); verifyNever(notifyInvocation); - expect(stubbedSshrvdChannel.sshrvdAck, SshrvdAck.acknowledged); - expect(stubbedSshrvdChannel.host, '123.123.123.123'); - expect(stubbedSshrvdChannel.port, 10456); - expect(stubbedSshrvdChannel.sshrvdPort, 10789); - }); // test Initialization - sshrvd host + expect(stubbedSrvdChannel.srvdAck, SrvdAck.acknowledged); + expect(stubbedSrvdChannel.host, '123.123.123.123'); + expect(stubbedSrvdChannel.port, 10456); + expect(stubbedSrvdChannel.srvdPort, 10789); + }); // test Initialization - srvd host - test('Initialization - non-sshrvd host', () async { + test('Initialization - non-srvd host', () async { when(() => mockParams.host).thenReturn('234.234.234.234'); when(() => mockParams.port).thenReturn(135); - await expectLater(stubbedSshrvdChannel.initialize(), completes); + await expectLater(stubbedSrvdChannel.initialize(), completes); - expect(stubbedSshrvdChannel.host, '234.234.234.234'); - expect(stubbedSshrvdChannel.port, 135); - }); // test Initialization - non-sshrvd host + expect(stubbedSrvdChannel.host, '234.234.234.234'); + expect(stubbedSrvdChannel.port, 135); + }); // test Initialization - non-srvd host - test('Initialization completes - sshrvd host', () async { + test('Initialization completes - srvd host', () async { /// Set the required parameters - whenInitializationWithSshrvdHost(); - await expectLater(stubbedSshrvdChannel.callInitialization(), completes); - await expectLater(stubbedSshrvdChannel.initialized, completes); + whenInitializationWithSrvdHost(); + await expectLater(stubbedSrvdChannel.callInitialization(), completes); + await expectLater(stubbedSrvdChannel.initialized, completes); }); - test('Initialization completes - non-sshrvd host', () async { + test('Initialization completes - non-srvd host', () async { when(() => mockParams.host).thenReturn('234.234.234.234'); when(() => mockParams.port).thenReturn(135); - await expectLater(stubbedSshrvdChannel.callInitialization(), completes); - await expectLater(stubbedSshrvdChannel.initialized, completes); - }); // test Initialization - non-sshrvd host + await expectLater(stubbedSrvdChannel.callInitialization(), completes); + await expectLater(stubbedSrvdChannel.initialized, completes); + }); // test Initialization - non-srvd host - test('runSshrv', () async { - whenInitializationWithSshrvdHost(); + test('runSrv', () async { + whenInitializationWithSrvdHost(); - await expectLater(stubbedSshrvdChannel.callInitialization(), completes); - expect(stubbedSshrvdChannel.sshrvdAck, SshrvdAck.acknowledged); - await expectLater(stubbedSshrvdChannel.initialized, completes); + await expectLater(stubbedSrvdChannel.callInitialization(), completes); + expect(stubbedSrvdChannel.srvdAck, SrvdAck.acknowledged); + await expectLater(stubbedSrvdChannel.initialized, completes); // Initialization should be complete - // Begin test for [runSshrv()] + // Begin test for [runSrv()] when(() => mockParams.localSshdPort).thenReturn(23); - when(sshrvGeneratorInvocation).thenReturn(mockSshrv); - when(sshrvRunInvocation).thenAnswer((_) async => 'called sshrv run'); + when(srvGeneratorInvocation).thenReturn(mockSrv); + when(srvRunInvocation).thenAnswer((_) async => 'called srv run'); - verifyNever(sshrvGeneratorInvocation); - verifyNever(sshrvRunInvocation); + verifyNever(srvGeneratorInvocation); + verifyNever(srvRunInvocation); await expectLater( - await stubbedSshrvdChannel.runSshrv(directSsh: false), - 'called sshrv run', + await stubbedSrvdChannel.runSrv(directSsh: false), + 'called srv run', ); verifyInOrder([ - sshrvGeneratorInvocation, - sshrvRunInvocation, + srvGeneratorInvocation, + srvRunInvocation, ]); - verifyNever(sshrvGeneratorInvocation); - verifyNever(sshrvRunInvocation); - }); // test runSshrv - }); // group SshrvdChannel + verifyNever(srvGeneratorInvocation); + verifyNever(srvRunInvocation); + }); // test runSrv + }); // group SrvdChannel } diff --git a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_dart_channel_test.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_dart_channel_test.dart index a8d558bc2..1e7ad5662 100644 --- a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_dart_channel_test.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_dart_channel_test.dart @@ -7,7 +7,7 @@ import 'package:uuid/uuid.dart'; import '../../sshnp_mocks.dart'; void main() { - group('SshrvdDartChannel', () { + group('SrvdDartChannel', () { late MockAtClient mockAtClient; late MockSshnpParams mockSshnpParams; late String sessionId; @@ -20,13 +20,13 @@ void main() { }); test('public API', () { expect( - SshrvdDartChannel( + SrvdDartChannel( atClient: mockAtClient, params: mockSshnpParams, sessionId: sessionId, ), - isA>(), + isA>(), ); }); - }); // group SshrvdDartChannel + }); // group SrvdDartChannel } diff --git a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_exec_channel_test.dart b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_exec_channel_test.dart index 6fd0a1965..822c4dedc 100644 --- a/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_exec_channel_test.dart +++ b/packages/dart/noports_core/test/sshnp/util/srvd_channel/srvd_exec_channel_test.dart @@ -7,7 +7,7 @@ import 'package:uuid/uuid.dart'; import '../../sshnp_mocks.dart'; void main() { - group('SshrvdExecChannel', () { + group('SrvdExecChannel', () { late MockAtClient mockAtClient; late MockSshnpParams mockSshnpParams; late String sessionId; @@ -21,13 +21,13 @@ void main() { }); test('public API', () { expect( - SshrvdExecChannel( + SrvdExecChannel( atClient: mockAtClient, params: mockSshnpParams, sessionId: sessionId, ), - isA>(), + isA>(), ); }); - }); // group SshrvdDartChannel + }); // group SrvdDartChannel } diff --git a/packages/dart/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_mocks.dart b/packages/dart/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_mocks.dart index 56df84210..8e125c214 100644 --- a/packages/dart/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_mocks.dart +++ b/packages/dart/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_mocks.dart @@ -46,9 +46,9 @@ class StubbedSshnp extends SshnpCore required super.atClient, required super.params, required SshnpdChannel sshnpdChannel, - required SshrvdChannel sshrvdChannel, + required SrvdChannel srvdChannel, }) : _sshnpdChannel = sshnpdChannel, - _sshrvdChannel = sshrvdChannel; + _srvdChannel = srvdChannel; @override AtSshKeyPair? get identityKeyPair => throw UnimplementedError(); @@ -64,8 +64,8 @@ class StubbedSshnp extends SshnpCore final SshnpdChannel _sshnpdChannel; @override - SshrvdChannel get sshrvdChannel => _sshrvdChannel; - final SshrvdChannel _sshrvdChannel; + SrvdChannel get srvdChannel => _srvdChannel; + final SrvdChannel _srvdChannel; @override Future startUserSession({required Process? tunnelSession}) { diff --git a/packages/dart/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_test.dart b/packages/dart/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_test.dart index 4734e9bdd..0561788ee 100644 --- a/packages/dart/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_test.dart +++ b/packages/dart/noports_core/test/sshnp/util/ssh_session_handler/openssh_ssh_session_handler_test.dart @@ -13,14 +13,14 @@ void main() { late MockAtClient mockAtClient; late MockSshnpParams mockParams; late MockSshnpdChannel mockSshnpChannel; - late MockSshrvdChannel mockSshrvdChannel; + late MockSrvdChannel mockSrvdChannel; late StubbedSshnp stubbedSshnp; setUp(() { mockAtClient = MockAtClient(); mockParams = MockSshnpParams(); mockSshnpChannel = MockSshnpdChannel(); - mockSshrvdChannel = MockSshrvdChannel(); + mockSrvdChannel = MockSrvdChannel(); // Mocked SshnpCore Constructor calls registerFallbackValue(AtClientPreference()); @@ -34,11 +34,11 @@ void main() { atClient: mockAtClient, params: mockParams, sshnpdChannel: mockSshnpChannel, - sshrvdChannel: mockSshrvdChannel, + srvdChannel: mockSrvdChannel, ); // Mocked SshnpCore Initialization calls - // TODO sshrvd channel mock calls + // TODO srvd channel mock calls // TODO sshnpd channel mock calls }); diff --git a/packages/dart/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart b/packages/dart/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart index 82f338948..32383b860 100644 --- a/packages/dart/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart +++ b/packages/dart/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart @@ -14,7 +14,7 @@ void main() { late MockAtSshKeyPair keyPair; late MockSshnpdChannel mockSshnpdChannel; - late MockSshrvdChannel mockSshrvdChannel; + late MockSrvdChannel mockSrvdChannel; setUp(() { mockAtClient = MockAtClient(); @@ -23,7 +23,7 @@ void main() { keyPair = MockAtSshKeyPair(); mockSshnpdChannel = MockSshnpdChannel(); - mockSshrvdChannel = MockSshrvdChannel(); + mockSrvdChannel = MockSrvdChannel(); registerFallbackValue(AtClientPreference()); }); @@ -47,8 +47,7 @@ void main() { .thenAnswer((_) async => 'myTunnelUsername'); when(() => mockSshnpdChannel.sharePublicKeyIfRequired(identityKeyPair)) .thenAnswer((_) async {}); - when(() => mockSshrvdChannel.callInitialization()) - .thenAnswer((_) async {}); + when(() => mockSrvdChannel.callInitialization()).thenAnswer((_) async {}); } test('public API', () { @@ -68,7 +67,7 @@ void main() { params: mockParams, sshKeyUtil: keyUtil, sshnpdChannel: mockSshnpdChannel, - sshrvdChannel: mockSshrvdChannel, + srvdChannel: mockSrvdChannel, ); whenInitialization(identityKeyPair: keyPair); @@ -96,7 +95,7 @@ void main() { params: mockParams, sshKeyUtil: keyUtil, sshnpdChannel: mockSshnpdChannel, - sshrvdChannel: mockSshrvdChannel, + srvdChannel: mockSrvdChannel, ); whenInitialization(identityKeyPair: keyPair); @@ -125,7 +124,7 @@ void main() { params: mockParams, sshKeyUtil: keyUtil, sshnpdChannel: mockSshnpdChannel, - sshrvdChannel: mockSshrvdChannel, + srvdChannel: mockSrvdChannel, ); whenInitialization(); diff --git a/packages/dart/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_ssh_key_handler_mocks.dart b/packages/dart/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_ssh_key_handler_mocks.dart index d95f72988..16df1e958 100644 --- a/packages/dart/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_ssh_key_handler_mocks.dart +++ b/packages/dart/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_ssh_key_handler_mocks.dart @@ -19,10 +19,10 @@ class StubbedSshnp extends SshnpCore with SshnpLocalSshKeyHandler { required super.params, LocalSshKeyUtil? sshKeyUtil, SshnpdChannel? sshnpdChannel, - SshrvdChannel? sshrvdChannel, + SrvdChannel? srvdChannel, }) : _sshKeyUtil = sshKeyUtil, _sshnpdChannel = sshnpdChannel, - _sshrvdChannel = sshrvdChannel; + _srvdChannel = srvdChannel; @override Future run() => throw UnimplementedError(); @@ -33,9 +33,8 @@ class StubbedSshnp extends SshnpCore with SshnpLocalSshKeyHandler { final SshnpdChannel? _sshnpdChannel; @override - SshrvdChannel get sshrvdChannel => - _sshrvdChannel ?? (throw UnimplementedError()); - final SshrvdChannel? _sshrvdChannel; + SrvdChannel get srvdChannel => _srvdChannel ?? (throw UnimplementedError()); + final SrvdChannel? _srvdChannel; @override bool get canRunShell => false; diff --git a/packages/dart/sshnoports/README.md b/packages/dart/sshnoports/README.md index dd875a619..f3226aab0 100644 --- a/packages/dart/sshnoports/README.md +++ b/packages/dart/sshnoports/README.md @@ -24,11 +24,11 @@ There are five binaries:- `sshnp` : The client that sets up a connection to the device which you can then ssh to via your localhost interface -`sshrvd` : This daemon acts as a rendezvous service and provides Internet routable IP/Ports for sshnpd and sshrv to connect to +`srvd` : This daemon acts as a rendezvous service and provides Internet routable IP/Ports for sshnpd and srv to connect to -`sshrv` : This client is called by sshnp to connect the local sshd to the rendezvous point +`srv` : This client is called by sshnp to connect the local sshd to the rendezvous point -To get going you just need two (or three if you want to use your own sshrvd service) atSigns and their .atKeys files and the +To get going you just need two (or three if you want to use your own srvd service) atSigns and their .atKeys files and the binaries (from the [latest release](https://github.com/atsign-foundation/noports/releases)). Once you have the atSigns (free or paid atSigns from [atsign.com](https://atsign.com)), drop the binaries in place @@ -52,10 +52,10 @@ Once that has started up you can run the client code from another machine. The c ``` ./sshnp --from <@your_manager_atsign> --to <@your_devices_atsign> \ ---host --device -s <> +--host --device -s <> ``` -The --host specifies the atSign of the sshrvd or the DNS name of the openssh server of the client machine that the remote device can connect to. If everything goes to plan the client +The --host specifies the atSign of the srvd or the DNS name of the openssh server of the client machine that the remote device can connect to. If everything goes to plan the client will complete and tell you how to connect to the remote host for example. Example command would be:- @@ -76,11 +76,11 @@ If you want to do this in a single command use `$()` for example, note $(./sshnp -f @myclient -t @myserver -d mymachine -h @myrz -s id_ed25519.pub) ``` -Atsign provides a sshrvd service but if you want to run your own `sshrvd` you will need a machine that has an internet IP and all ports 1024-65535 unfirewalled and an atSign for the daemon to use. +Atsign provides a srvd service but if you want to run your own `srvd` you will need a machine that has an internet IP and all ports 1024-65535 unfirewalled and an atSign for the daemon to use. -To run your own rendezvous service, simply run the `sshrvd` binary. You may omit the manager atSign to allow all atSigns to use your rendezvous service. There are also flags like `-s` to snoop on traffic passing through the service. +To run your own rendezvous service, simply run the `srvd` binary. You may omit the manager atSign to allow all atSigns to use your rendezvous service. There are also flags like `-s` to snoop on traffic passing through the service. ``` -./sshrvd --atsign <@your_sshrvd_atsign> --manager <@manager_atsign> --ip +./srvd --atsign <@your_srvd_atsign> --manager <@manager_atsign> --ip ``` If you can now login using sshnp then you can now turn off sshd from listening on all external interfaces, and instead have ssh listen only on 127.0.0.1. @@ -130,7 +130,7 @@ after a reboot if for some reason the container crashes is all easily achieved. ## TWO Ways to run SSH! no ports daemons (root access NOT required) -### `sshnpd.sh` and `sshrvd.sh` - plain old shell scripts and log file +### `sshnpd.sh` and `srvd.sh` - plain old shell scripts and log file The scripts directory of this repo contains an example `sshnpd.sh` that can be run in a user's home directory (and assumes that the release has been @@ -148,7 +148,7 @@ You might also want to add a crontab entry to run the script on reboot: @reboot ~/sshnpd.sh > ~/sshnpd.log 2>&1 ``` -### `tmux-sshnpd.sh` and `tmux-sshrvd.sh` - the power of tmux, highly recommended if tmux is installed `sudo apt install tmux` +### `tmux-sshnpd.sh` and `tmux-srvd.sh` - the power of tmux, highly recommended if tmux is installed `sudo apt install tmux` This runs the daemon inside a tmux session, which can be connected to in order to see logs. diff --git a/packages/dart/sshnoports/bin/srvd.dart b/packages/dart/sshnoports/bin/srvd.dart index 4f79a97a4..d104520cd 100644 --- a/packages/dart/sshnoports/bin/srvd.dart +++ b/packages/dart/sshnoports/bin/srvd.dart @@ -8,14 +8,14 @@ import 'package:sshnoports/src/print_version.dart'; void main(List args) async { AtSignLogger.root_level = 'SHOUT'; AtSignLogger.defaultLoggingHandler = AtSignLogger.stdErrLoggingHandler; - late final Srvd sshrvd; + late final Srvd srvd; try { - sshrvd = await Srvd.fromCommandLineArgs( + srvd = await Srvd.fromCommandLineArgs( args, - atClientGenerator: (SshrvdParams p) => createAtClientCli( + atClientGenerator: (SrvdParams p) => createAtClientCli( homeDirectory: p.homeDirectory, - subDirectory: '.sshrvd', + subDirectory: '.srvd', atsign: p.atSign, atKeysFilePath: p.atKeysFilePath, namespace: Srvd.namespace, @@ -23,7 +23,7 @@ void main(List args) async { ), usageCallback: (e, s) { printVersion(); - stdout.writeln(SshrvdParams.parser.usage); + stdout.writeln(SrvdParams.parser.usage); stderr.writeln('\n$e'); }, ); @@ -32,8 +32,8 @@ void main(List args) async { } await runZonedGuarded(() async { - await sshrvd.init(); - await sshrvd.run(); + await srvd.init(); + await srvd.run(); }, (Object error, StackTrace stackTrace) async { stderr.writeln('Error: ${error.toString()}'); stderr.writeln('Stack Trace: ${stackTrace.toString()}'); diff --git a/packages/dart/sshnoports/bundles/core/config/sshnp-config-template.env b/packages/dart/sshnoports/bundles/core/config/sshnp-config-template.env index 0996c8a7b..963759a3c 100644 --- a/packages/dart/sshnoports/bundles/core/config/sshnp-config-template.env +++ b/packages/dart/sshnoports/bundles/core/config/sshnp-config-template.env @@ -14,7 +14,7 @@ TO= # Receiving (a.k.a. remote) device name DEVICE= -# atSign of sshrvd daemon or FQDN/IP address to connect back to +# atSign of srvd daemon or FQDN/IP address to connect back to HOST= # TCP port to connect back to (only required if --host specified a FQDN/IP) diff --git a/packages/dart/sshnoports/bundles/shell/headless/README.md b/packages/dart/sshnoports/bundles/shell/headless/README.md index 807a59450..92c1a8fa5 100644 --- a/packages/dart/sshnoports/bundles/shell/headless/README.md +++ b/packages/dart/sshnoports/bundles/shell/headless/README.md @@ -39,24 +39,24 @@ To edit the crontab: crontab -e ``` -## sshrvd +## srvd ### Installation -The `sshrvd.service` file should be placed in `/etc/systemd/system` (as root). +The `srvd.service` file should be placed in `/etc/systemd/system` (as root). -Modify the `sshrvd.service` unit to use the appropriate atSign, +Modify the `srvd.service` unit to use the appropriate atSign, (The boilerplate uses @atsign) as well as the internet address. -Also change the username and make sure that username running sshrvd has the +Also change the username and make sure that username running srvd has the .atkeys file in place at '~/.atsign/keys'. -Run the following command to view full usage information of the sshrvd binary: +Run the following command to view full usage information of the srvd binary: ```sh -/usr/local/bin/sshrvd +/usr/local/bin/srvd ``` or if you didn't install the binaries as root: ```sh -~/.local/bin/sshrvd +~/.local/bin/srvd ``` ### Usage diff --git a/packages/dart/sshnoports/bundles/shell/headless/root_sshrvd.sh b/packages/dart/sshnoports/bundles/shell/headless/root_sshrvd.sh index 041be6b80..83f3c212c 100755 --- a/packages/dart/sshnoports/bundles/shell/headless/root_sshrvd.sh +++ b/packages/dart/sshnoports/bundles/shell/headless/root_sshrvd.sh @@ -9,6 +9,6 @@ sleep 10; # allow machine to bring up network export USER="$user" while true; do - /usr/local/bin/sshrvd -a "$atsign" -i "$internet_address" + /usr/local/bin/srvd -a "$atsign" -i "$internet_address" sleep 10 done diff --git a/packages/dart/sshnoports/bundles/shell/headless/sshrvd.sh b/packages/dart/sshnoports/bundles/shell/headless/sshrvd.sh index 091c5eda3..4bf891027 100755 --- a/packages/dart/sshnoports/bundles/shell/headless/sshrvd.sh +++ b/packages/dart/sshnoports/bundles/shell/headless/sshrvd.sh @@ -9,6 +9,6 @@ sleep 10; # allow machine to bring up network export USER="$user" while true; do - "$HOME"/.local/bin/sshrvd -a "$atsign" -i "$internet_address" + "$HOME"/.local/bin/srvd -a "$atsign" -i "$internet_address" sleep 10 done diff --git a/packages/dart/sshnoports/bundles/shell/install.sh b/packages/dart/sshnoports/bundles/shell/install.sh index fa7ddc643..4d52ece8f 100755 --- a/packages/dart/sshnoports/bundles/shell/install.sh +++ b/packages/dart/sshnoports/bundles/shell/install.sh @@ -53,25 +53,25 @@ usage() { echo "at_activate - install at_activate" echo "sshnp - install sshnp" echo "sshnpd - install sshnpd" - echo "sshrv - install sshrv" - echo "sshrvd - install sshrvd" + echo "srv - install srv" + echo "srvd - install srvd" echo "binaries - install all base binaries" echo "" - echo "debug_sshrvd - install sshrvd with debugging enabled" + echo "debug_srvd - install srvd with debugging enabled" echo "debug - install all debug binaries" echo "" echo "all - install all binaries (base and debug)" if ! is_darwin; then echo "" echo "systemd - install a systemd unit" - echo " available units: [sshnpd, sshrvd]" + echo " available units: [sshnpd, srvd]" fi echo "" echo "headless - install a headless cron job" - echo " available jobs: [sshnpd, sshrvd]" + echo " available jobs: [sshnpd, srvd]" echo "" echo "tmux - install a service in a tmux session" - echo " available services: [sshnpd, sshrvd]" + echo " available services: [sshnpd, srvd]" } # SETUP AUTHORIZED KEYS # @@ -104,8 +104,8 @@ install_base_binaries() { install_single_binary "at_activate" install_single_binary "sshnp" install_single_binary "sshnpd" - install_single_binary "sshrv" - install_single_binary "sshrvd" + install_single_binary "srv" + install_single_binary "srvd" } install_debug_binary() { @@ -120,7 +120,7 @@ install_debug_binary() { } install_debug_binaries() { - install_debug_binary "sshrvd" + install_debug_binary "srvd" } install_all_binaries() { @@ -153,14 +153,14 @@ install_systemd_unit() { install_systemd_sshnpd() { root_only install_single_binary "sshnpd" - install_single_binary "sshrv" + install_single_binary "srv" install_systemd_unit "sshnpd.service" } -install_systemd_sshrvd() { +install_systemd_srvd() { root_only - install_single_binary "sshrvd" - install_systemd_unit "sshrvd.service" + install_single_binary "srvd" + install_systemd_unit "srvd.service" } systemd() { @@ -172,7 +172,7 @@ systemd() { case "$1" in --help) usage; exit 0;; sshnpd) install_systemd_sshnpd;; - sshrvd) install_systemd_sshrvd;; + srvd) install_systemd_srvd;; *) echo "Unknown systemd unit: $1"; usage; @@ -234,20 +234,20 @@ install_headless_job() { install_headless_sshnpd() { install_single_binary "sshnpd" - install_single_binary "sshrv" + install_single_binary "srv" install_headless_job "sshnpd" } -install_headless_sshrvd() { - install_single_binary "sshrvd" - install_headless_job "sshrvd" +install_headless_srvd() { + install_single_binary "srvd" + install_headless_job "srvd" } headless() { case "$1" in --help|'') usage; exit 0;; sshnpd) install_headless_sshnpd;; - sshrvd) install_headless_sshrvd;; + srvd) install_headless_srvd;; *) echo "Unknown headless job: $1"; usage; @@ -302,20 +302,20 @@ install_tmux_service() { install_tmux_sshnpd() { install_single_binary "sshnpd" - install_single_binary "sshrv" + install_single_binary "srv" install_tmux_service "sshnpd" } -install_tmux_sshrvd() { - install_single_binary "sshrvd" - install_tmux_service "sshrvd" +install_tmux_srvd() { + install_single_binary "srvd" + install_tmux_service "srvd" } tmux() { case "$1" in --help|'') usage; exit 0;; sshnpd) install_tmux_sshnpd;; - sshrvd) install_tmux_sshrvd;; + srvd) install_tmux_srvd;; *) echo "Unknown tmux service: $1"; usage; @@ -333,9 +333,9 @@ main() { case "$1" in --help|'') usage; exit 0;; - at_activate|sshnp|sshnpd|sshrv|sshrvd) install_single_binary "$1";; + at_activate|sshnp|sshnpd|srv|srvd) install_single_binary "$1";; binaries) install_base_binaries;; - debug_sshrvd) install_debug_sshrvd;; + debug_srvd) install_debug_srvd;; debug) install_debug_binaries;; all) install_all_binaries;; systemd|headless|tmux) diff --git a/packages/dart/sshnoports/bundles/shell/systemd/README.md b/packages/dart/sshnoports/bundles/shell/systemd/README.md index d09061005..94e347761 100644 --- a/packages/dart/sshnoports/bundles/shell/systemd/README.md +++ b/packages/dart/sshnoports/bundles/shell/systemd/README.md @@ -40,20 +40,20 @@ To view the realtime logs, use journalctl: sudo journalctl -u sshnpd.service ``` -## sshrvd +## srvd ### Installation -The `sshrvd.service` file should be placed in `/etc/systemd/system` (as root). +The `srvd.service` file should be placed in `/etc/systemd/system` (as root). -Modify the `sshrvd.service` unit to use the appropriate atSign, +Modify the `srvd.service` unit to use the appropriate atSign, (The boilerplate uses @atsign) as well as the internet address. -Also change the username and make sure that username running sshrvd has the +Also change the username and make sure that username running srvd has the .atkeys file in place at '~/.atsign/keys'. -Run the following command to view full usage information of the sshrvd binary: +Run the following command to view full usage information of the srvd binary: ```sh -/usr/local/bin/sshrvd +/usr/local/bin/srvd ``` ### Usage @@ -61,18 +61,18 @@ Run the following command to view full usage information of the sshrvd binary: To enable the service use: ```sh -sudo systemctl enable sshrvd.service +sudo systemctl enable srvd.service ``` The services will then start at the next reboot, or can be started immediately with: ```sh -sudo systemctl start sshrvd.service +sudo systemctl start srvd.service ``` To view the realtime logs, use journalctl: ```sh -sudo journalctl -u sshrvd.service +sudo journalctl -u srvd.service ``` \ No newline at end of file diff --git a/packages/dart/sshnoports/bundles/shell/systemd/sshrvd.service b/packages/dart/sshnoports/bundles/shell/systemd/sshrvd.service index 45aea48d4..0a196653b 100644 --- a/packages/dart/sshnoports/bundles/shell/systemd/sshrvd.service +++ b/packages/dart/sshnoports/bundles/shell/systemd/sshrvd.service @@ -13,7 +13,7 @@ RestartSec=3 # ExecStartPre=/bin/sleep 10 # TODO : set atsign, internet_address -ExecStart=/usr/local/bin/sshrvd -a <@atsign> -i +ExecStart=/usr/local/bin/srvd -a <@atsign> -i [Install] WantedBy=multi-user.target \ No newline at end of file diff --git a/packages/dart/sshnoports/tools/Dockerfile b/packages/dart/sshnoports/tools/Dockerfile index 6cc89d344..170558134 100644 --- a/packages/dart/sshnoports/tools/Dockerfile +++ b/packages/dart/sshnoports/tools/Dockerfile @@ -14,7 +14,7 @@ RUN \ dart pub get ; \ dart run build_runner build --delete-conflicting-outputs ; \ dart compile exe bin/sshnpd.dart -o ${BINARYDIR}/sshnpd ; \ - dart compile exe bin/sshrv.dart -o ${BINARYDIR}/sshrv + dart compile exe bin/srv.dart -o ${BINARYDIR}/srv # Second stage of build FROM debian-slim FROM debian:stable-20240110-slim@sha256:f7235f31d948d45b37de1faabc7e518859d2b9cf0508486d71c1772cfc9bed8a @@ -41,6 +41,6 @@ RUN \ chmod 755 /${USER}/.startup.sh COPY --from=buildimage --chown=${USER}:${USER} /usr/local/at/sshnpd /usr/local/at/ -COPY --from=buildimage --chown=${USER}:${USER} /usr/local/at/sshrv /usr/local/at/ +COPY --from=buildimage --chown=${USER}:${USER} /usr/local/at/srv /usr/local/at/ WORKDIR ${HOMEDIR} ENTRYPOINT ["/atsign/.startup.sh"] diff --git a/packages/dart/sshnoports/tools/Dockerfile.package b/packages/dart/sshnoports/tools/Dockerfile.package index 81cc09897..764eeb643 100644 --- a/packages/dart/sshnoports/tools/Dockerfile.package +++ b/packages/dart/sshnoports/tools/Dockerfile.package @@ -7,10 +7,10 @@ WORKDIR /sshnoports COPY . . RUN set -eux; \ case "$(dpkg --print-architecture)" in \ - amd64) ARCH="x64";; \ - armhf) ARCH="arm";; \ - arm64) ARCH="arm64";; \ - riscv64) ARCH="riscv64";; \ + amd64) ARCH="x64";; \ + armhf) ARCH="arm";; \ + arm64) ARCH="arm64";; \ + riscv64) ARCH="riscv64";; \ esac; \ mkdir -p sshnp/debug; \ mkdir tarball; \ @@ -19,9 +19,9 @@ RUN set -eux; \ dart compile exe bin/activate_cli.dart -v -o sshnp/at_activate; \ dart compile exe bin/sshnp.dart -v -o sshnp/sshnp; \ dart compile exe bin/sshnpd.dart -v -o sshnp/sshnpd; \ - dart compile exe bin/sshrv.dart -v -o sshnp/sshrv; \ - dart compile exe bin/sshrvd.dart -v -o sshnp/sshrvd; \ - dart compile exe bin/sshrvd.dart -D ENABLE_SNOOP=true -v -o sshnp/debug/sshrvd; \ + dart compile exe bin/srv.dart -v -o sshnp/srv; \ + dart compile exe bin/srvd.dart -v -o sshnp/srvd; \ + dart compile exe bin/srvd.dart -D ENABLE_SNOOP=true -v -o sshnp/debug/srvd; \ cp -r bundles/core/* sshnp/; \ cp -r bundles/shell/* sshnp/; \ cp LICENSE sshnp/; \ diff --git a/tests/end2end_tests/contexts/_init_/setup-sshnp-entrypoint.sh b/tests/end2end_tests/contexts/_init_/setup-sshnp-entrypoint.sh index 1ab50424a..57fad084d 100755 --- a/tests/end2end_tests/contexts/_init_/setup-sshnp-entrypoint.sh +++ b/tests/end2end_tests/contexts/_init_/setup-sshnp-entrypoint.sh @@ -1,13 +1,13 @@ #!/bin/bash # this script copies the template sshnp entrypoint to ../sshnp/entrypoint.sh -# then also replaces the device name, sshnp atSign, sshnpd atSign, and sshrvd atSign with the provided arguments +# then also replaces the device name, sshnp atSign, sshnpd atSign, and srvd atSign with the provided arguments # example usage: ./setup-sshnp-entrypoint.sh e2e @alice @alice @alice device=$1 # e.g. e2e sshnp=$2 # e.g. @alice sshnpd=$3 # e.g. @alice -sshrvd=$4 # e.g. @alice +srvd=$4 # e.g. @alice template_name=$5 # e.g. sshnp_entrypoint.sh args="$6" # e.g. "arg1 arg2 arg3" @@ -23,7 +23,7 @@ fi eval "$prefix" "s/@sshnpatsign/${sshnp}/g" ../sshnp/entrypoint.sh eval "$prefix" "s/@sshnpdatsign/${sshnpd}/g" ../sshnp/entrypoint.sh -eval "$prefix" "s/@sshrvdatsign/${sshrvd}/g" ../sshnp/entrypoint.sh +eval "$prefix" "s/@srvdatsign/${srvd}/g" ../sshnp/entrypoint.sh eval "$prefix" "s/deviceName/${device}/g" ../sshnp/entrypoint.sh # Don't use eval for this one, because it will try to evaluate the args stored in $args diff --git a/tests/end2end_tests/contexts/_init_/setup-sshrvd-entrypoint.sh b/tests/end2end_tests/contexts/_init_/setup-sshrvd-entrypoint.sh index 743c7cb9e..813e295fc 100755 --- a/tests/end2end_tests/contexts/_init_/setup-sshrvd-entrypoint.sh +++ b/tests/end2end_tests/contexts/_init_/setup-sshrvd-entrypoint.sh @@ -1,13 +1,13 @@ #!/bin/bash -# this script copies the template sshrvd entrypoint to ../sshrvd/entrypoint.sh -# then also replaces the @sshrvdatsign with the provided argument (e.g. @alice) -# example usage: ./setup-sshrvd-entrypoint.sh @alice +# this script copies the template srvd entrypoint to ../srvd/entrypoint.sh +# then also replaces the @srvdatsign with the provided argument (e.g. @alice) +# example usage: ./setup-srvd-entrypoint.sh @alice -sshrvd=$1 # e.g. @alice -template_name=$2 # e.g. "sshrvd_entrypoint.sh" +srvd=$1 # e.g. @alice +template_name=$2 # e.g. "srvd_entrypoint.sh" -cp ../../entrypoints/"$template_name" ../sshrvd/entrypoint.sh # copy template to the mounted folder +cp ../../entrypoints/"$template_name" ../srvd/entrypoint.sh # copy template to the mounted folder prefix="sed -i" @@ -17,4 +17,4 @@ then prefix="$prefix ''" fi -eval "$prefix" "s/@sshrvdatsign/${sshrvd}/g" ../sshrvd/entrypoint.sh \ No newline at end of file +eval "$prefix" "s/@srvdatsign/${srvd}/g" ../srvd/entrypoint.sh \ No newline at end of file diff --git a/tests/end2end_tests/contexts/_init_/setup-sshrvd-keys.sh b/tests/end2end_tests/contexts/_init_/setup-sshrvd-keys.sh index eb66a797c..6b5cdd4e5 100755 --- a/tests/end2end_tests/contexts/_init_/setup-sshrvd-keys.sh +++ b/tests/end2end_tests/contexts/_init_/setup-sshrvd-keys.sh @@ -1,14 +1,14 @@ #!/bin/bash -# this script copies the keys from ~/.atsign/keys to ../sshrvd/keys -# example usage: ./setup-sshrvd-keys.sh @alice +# this script copies the keys from ~/.atsign/keys to ../srvd/keys +# example usage: ./setup-srvd-keys.sh @alice -sshrvd=$1 +srvd=$1 -cp ~/.atsign/keys/"$sshrvd"_key.atKeys ../sshrvd/.atsign/keys/"$sshrvd"_key.atKeys # copy keys to the mounted folder +cp ~/.atsign/keys/"$srvd"_key.atKeys ../srvd/.atsign/keys/"$srvd"_key.atKeys # copy keys to the mounted folder -if [[ ! -f ../sshrvd/.atsign/keys/${sshrvd}_key.atKeys ]]; +if [[ ! -f ../srvd/.atsign/keys/${srvd}_key.atKeys ]]; then - echo "Could not copy ${sshrvd}_key.atKeys to ../sshrvd/.atsign/keys/${sshrvd}_key.atKeys" + echo "Could not copy ${srvd}_key.atKeys to ../srvd/.atsign/keys/${srvd}_key.atKeys" exit 1 fi \ No newline at end of file diff --git a/tests/end2end_tests/entrypoints/sshnp_entrypoint.sh b/tests/end2end_tests/entrypoints/sshnp_entrypoint.sh index 29bf9a4f3..6c970cfc7 100644 --- a/tests/end2end_tests/entrypoints/sshnp_entrypoint.sh +++ b/tests/end2end_tests/entrypoints/sshnp_entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash echo "SSHNP START ENTRY" -SSHNP_COMMAND="$HOME/.local/bin/sshnp -f @sshnpatsign -t @sshnpdatsign -d deviceName -h @sshrvdatsign args > sshnp.log" +SSHNP_COMMAND="$HOME/.local/bin/sshnp -f @sshnpatsign -t @sshnpdatsign -d deviceName -h @srvdatsign args > sshnp.log" run_test() { diff --git a/tests/end2end_tests/entrypoints/sshnp_installer_entrypoint.sh b/tests/end2end_tests/entrypoints/sshnp_installer_entrypoint.sh index 5c4114f01..de76b5a7a 100644 --- a/tests/end2end_tests/entrypoints/sshnp_installer_entrypoint.sh +++ b/tests/end2end_tests/entrypoints/sshnp_installer_entrypoint.sh @@ -1,5 +1,5 @@ #!/bin/bash -SSHNP_COMMAND="$HOME/.local/bin/sshnp@sshnpdatsign -d deviceName -h @sshrvdatsign args > sshnp.log" +SSHNP_COMMAND="$HOME/.local/bin/sshnp@sshnpdatsign -d deviceName -h @srvdatsign args > sshnp.log" echo "Running: $SSHNP_COMMAND" eval "$SSHNP_COMMAND" cat sshnp.log diff --git a/tests/end2end_tests/entrypoints/sshrvd_entrypoint.sh b/tests/end2end_tests/entrypoints/sshrvd_entrypoint.sh index c41a11bc8..82243b1b6 100644 --- a/tests/end2end_tests/entrypoints/sshrvd_entrypoint.sh +++ b/tests/end2end_tests/entrypoints/sshrvd_entrypoint.sh @@ -1,2 +1,2 @@ #!/bin/bash -"$HOME"/.local/bin/sshrvd -a @sshrvdatsign -i "$(hostname -i)" -v -s 2>&1 | tee -a sshrvd.log +"$HOME"/.local/bin/srvd -a @srvdatsign -i "$(hostname -i)" -v -s 2>&1 | tee -a srvd.log diff --git a/tests/end2end_tests/image/Dockerfile b/tests/end2end_tests/image/Dockerfile index 1f081bce5..1a3d9f773 100644 --- a/tests/end2end_tests/image/Dockerfile +++ b/tests/end2end_tests/image/Dockerfile @@ -47,8 +47,8 @@ RUN set -eux ; \ dart pub get -C ${PACKAGE_DIR}; \ dart compile exe ${PACKAGE_DIR}/bin/sshnp.dart -o ${OUTPUT_DIR}/sshnp ; \ dart compile exe ${PACKAGE_DIR}/bin/sshnpd.dart -o ${OUTPUT_DIR}/sshnpd ; \ - dart compile exe ${PACKAGE_DIR}/bin/sshrv.dart -o ${OUTPUT_DIR}/sshrv ; \ - dart compile exe ${PACKAGE_DIR}/bin/sshrvd.dart -o ${OUTPUT_DIR}/sshrvd ; \ + dart compile exe ${PACKAGE_DIR}/bin/srv.dart -o ${OUTPUT_DIR}/srv ; \ + dart compile exe ${PACKAGE_DIR}/bin/srvd.dart -o ${OUTPUT_DIR}/srvd ; \ dart compile exe ${PACKAGE_DIR}/bin/activate_cli.dart -o ${OUTPUT_DIR}/at_activate ; # RUNTIME BRANCH @@ -83,8 +83,8 @@ RUN set -eux ; \ dart pub get -C ${PACKAGE_DIR}; \ dart compile exe ${PACKAGE_DIR}/bin/sshnp.dart -o ${OUTPUT_DIR}/sshnp ; \ dart compile exe ${PACKAGE_DIR}/bin/sshnpd.dart -o ${OUTPUT_DIR}/sshnpd ; \ - dart compile exe ${PACKAGE_DIR}/bin/sshrv.dart -o ${OUTPUT_DIR}/sshrv ; \ - dart compile exe ${PACKAGE_DIR}/bin/sshrvd.dart -o ${OUTPUT_DIR}/sshrvd ; \ + dart compile exe ${PACKAGE_DIR}/bin/srv.dart -o ${OUTPUT_DIR}/srv ; \ + dart compile exe ${PACKAGE_DIR}/bin/srvd.dart -o ${OUTPUT_DIR}/srvd ; \ dart compile exe ${PACKAGE_DIR}/bin/activate_cli.dart -o ${OUTPUT_DIR}/at_activate ; # RUNTIME LOCAL @@ -134,7 +134,7 @@ RUN apt-get update ; \ tar -xvf sshnp-linux-${ARCH}.tgz ; \ rm sshnp-linux-${ARCH}.tgz ; \ cd sshnp ; \ - mv sshnp sshnpd sshrv sshrvd at_activate ${OUTPUT_DIR} ; + mv sshnp sshnpd srv srvd at_activate ${OUTPUT_DIR} ; # RUNTIME RELEASE FROM base AS runtime-release diff --git a/tests/end2end_tests/tests/service-container-sshnpd.yaml b/tests/end2end_tests/tests/service-container-sshnpd.yaml index 17f319fd9..ae79bc438 100644 --- a/tests/end2end_tests/tests/service-container-sshnpd.yaml +++ b/tests/end2end_tests/tests/service-container-sshnpd.yaml @@ -12,4 +12,4 @@ retries: 36 # Retry the check n times # auto added: # - image - # - depends_on: (sshrvd + runtime service) + # - depends_on: (srvd + runtime service) diff --git a/tests/end2end_tests/tests/service-container-sshrvd.yaml b/tests/end2end_tests/tests/service-container-sshrvd.yaml index ee3da03cf..a3ea127f2 100644 --- a/tests/end2end_tests/tests/service-container-sshrvd.yaml +++ b/tests/end2end_tests/tests/service-container-sshrvd.yaml @@ -1,10 +1,10 @@ - container-sshrvd: - container_name: sshrvd + container-srvd: + container_name: srvd volumes: - - ../contexts/sshrvd:/mount + - ../contexts/srvd:/mount network_mode: host healthcheck: - test: ["CMD", "grep", "-Eq", "monitor started for @", "/atsign/sshrvd.log"] + test: ["CMD", "grep", "-Eq", "monitor started for @", "/atsign/srvd.log"] start_period: 10s # Wait 10 seconds before checking interval: 5s # Check every 5 seconds timeout: 1s # If a check takes longer than a second, consider it a failed check diff --git a/tools/manual-docker/README.md b/tools/manual-docker/README.md index f4ecc7b4e..0373c8f24 100644 --- a/tools/manual-docker/README.md +++ b/tools/manual-docker/README.md @@ -49,7 +49,7 @@ $ ./run-manual-docker.sh usage: ./run-manual-docker.sh -h|--help - -t|--tag (required) - docker container tag + -t|--tag (required) - docker container tag --no-cache (optional) - docker build without cache --rm (optional) - remove container after exit ONE OF THE FOLLOWING (required) @@ -60,7 +60,7 @@ usage: ./run-manual-docker.sh example: ./run-manual-docker.sh -t sshnp -b trunk example: ./run-manual-docker.sh -t sshnpd -l - example: ./run-manual-docker.sh -t sshrvd -r v3.3.0 + example: ./run-manual-docker.sh -t srvd -r v3.3.0 example: ./run-manual-docker.sh -t sshnp --release example: ./run-manual-docker.sh -t sshnp --blank ``` diff --git a/tools/manual-docker/blank/docker-compose.yaml b/tools/manual-docker/blank/docker-compose.yaml index 2b90a9a31..cf1df788e 100644 --- a/tools/manual-docker/blank/docker-compose.yaml +++ b/tools/manual-docker/blank/docker-compose.yaml @@ -1,5 +1,4 @@ - -version: '3.8' +version: "3.8" services: image-manual-blank: @@ -29,11 +28,11 @@ services: - sshnpd depends_on: - image-manual-blank - container-sshrvd: + container-srvd: image: atsigncompany/sshnp-e2e-manual:blank - container_name: manual_blank_sshrvd + container_name: manual_blank_srvd volumes: - - ../../../tests/end2end_tests/contexts/sshrvd/.atsign/keys/:/atsign/.atsign/keys/ # mount keys + - ../../../tests/end2end_tests/contexts/srvd/.atsign/keys/:/atsign/.atsign/keys/ # mount keys network_mode: host depends_on: - image-manual-blank @@ -44,4 +43,4 @@ networks: driver: bridge sshnp: name: atsigncompany/sshnp-e2e-manual-network-sshnp - driver: bridge \ No newline at end of file + driver: bridge diff --git a/tools/manual-docker/branch/docker-compose.yaml b/tools/manual-docker/branch/docker-compose.yaml index 13c3953ce..830479cc8 100644 --- a/tools/manual-docker/branch/docker-compose.yaml +++ b/tools/manual-docker/branch/docker-compose.yaml @@ -1,5 +1,4 @@ - -version: '3.8' +version: "3.8" services: image-manual-branch: @@ -31,16 +30,15 @@ services: - sshnpd depends_on: - image-manual-branch - container-sshrvd: + container-srvd: image: atsigncompany/sshnp-e2e-manual:branch - container_name: manual_branch_sshrvd + container_name: manual_branch_srvd volumes: - - ../../../tests/end2end_tests/contexts/sshrvd/.atsign/keys/:/atsign/.atsign/keys/ # mount keys + - ../../../tests/end2end_tests/contexts/srvd/.atsign/keys/:/atsign/.atsign/keys/ # mount keys network_mode: host depends_on: - image-manual-branch - networks: sshnpd: name: atsigncompany/sshnp-e2e-manual-network-sshnpd diff --git a/tools/manual-docker/local/docker-compose.yaml b/tools/manual-docker/local/docker-compose.yaml index 37bebeecc..3a972978e 100644 --- a/tools/manual-docker/local/docker-compose.yaml +++ b/tools/manual-docker/local/docker-compose.yaml @@ -1,5 +1,4 @@ - -version: '3.8' +version: "3.8" services: image-manual-local: @@ -29,11 +28,11 @@ services: - sshnp depends_on: - image-manual-local - # container-sshrvd: + # container-srvd: # image: atsigncompany/sshnp-e2e-manual:local - # container_name: manual_local_sshrvd + # container_name: manual_local_srvd # volumes: - # - ../../../../contexts/sshrvd/.atsign/keys/:/atsign/.atsign/keys/ # mount keys + # - ../../../../contexts/srvd/.atsign/keys/:/atsign/.atsign/keys/ # mount keys # network_mode: host # depends_on: # - image-manual-local @@ -44,4 +43,4 @@ networks: driver: bridge sshnp: name: atsigncompany/sshnp-e2e-manual-network-sshnp - driver: bridge \ No newline at end of file + driver: bridge diff --git a/tools/manual-docker/release/docker-compose.yaml b/tools/manual-docker/release/docker-compose.yaml index a333f07cc..6a354479c 100644 --- a/tools/manual-docker/release/docker-compose.yaml +++ b/tools/manual-docker/release/docker-compose.yaml @@ -29,11 +29,11 @@ services: - sshnpd depends_on: - image-manual-release - container-sshrvd: + container-srvd: image: atsigncompany/sshnp-e2e-manual:release container_name: manual_release_sshnp volumes: - - ../../../tests/end2end_tests/contexts/sshrvd/.atsign/keys/:/atsign/.atsign/keys/ # mount keys + - ../../../tests/end2end_tests/contexts/srvd/.atsign/keys/:/atsign/.atsign/keys/ # mount keys network_mode: host depends_on: - image-manual-release diff --git a/tools/manual-docker/run-manual-docker.sh b/tools/manual-docker/run-manual-docker.sh index 2ba40d2ef..bd8ee9e7c 100755 --- a/tools/manual-docker/run-manual-docker.sh +++ b/tools/manual-docker/run-manual-docker.sh @@ -6,7 +6,7 @@ usage() { echo "" echo "usage: $0" echo " -h|--help" - echo " -t|--tag (required) - docker container tag" + echo " -t|--tag (required) - docker container tag" echo " --no-cache (optional) - docker build without cache" echo " --rm (optional) - remove container after exit" echo " ONE OF THE FOLLOWING (required)" @@ -17,7 +17,7 @@ usage() { echo "" echo " example: $0 -t sshnp -b trunk" echo " example: $0 -t sshnpd -l" - echo " example: $0 -t sshrvd -r v3.3.0" + echo " example: $0 -t srvd -r v3.3.0" echo " example: $0 -t sshnp --release" echo " example: $0 -t sshnp --blank" echo "" @@ -91,10 +91,10 @@ parse_args() { exit 1 fi - # check that tag is one of: sshnp/sshnpd/sshrvd - if [[ $tag != "sshnp" && $tag != "sshnpd" && $tag != "sshrvd" ]]; + # check that tag is one of: sshnp/sshnpd/srvd + if [[ $tag != "sshnp" && $tag != "sshnpd" && $tag != "srvd" ]]; then - echo "Invalid tag: $tag, must be one of: sshnp/sshnpd/sshrvd" + echo "Invalid tag: $tag, must be one of: sshnp/sshnpd/srvd" usage exit 1 fi diff --git a/tools/notarize-macos.sh b/tools/notarize-macos.sh index c9ca542ec..906ef2849 100755 --- a/tools/notarize-macos.sh +++ b/tools/notarize-macos.sh @@ -56,10 +56,10 @@ codesign \ --options=runtime \ -s "$SIGNING_IDENTITY" \ -v \ - "$WORKING_DIR"/sshnp/{ssh*,at_activate,debug/sshrvd}; + "$WORKING_DIR"/sshnp/{ssh*,at_activate,debug/srvd}; echo Verifying signatures: -codesign -vvv --deep --strict "$WORKING_DIR"/sshnp/{ssh*,at_activate,debug/sshrvd}; +codesign -vvv --deep --strict "$WORKING_DIR"/sshnp/{ssh*,at_activate,debug/srvd}; # Zip the signed binaries ditto -c -k --keepParent "$WORKING_DIR"/sshnp "$WORKING_DIR/$OUTPUT_FILE".zip diff --git a/tools/package-macos-arm64.sh b/tools/package-macos-arm64.sh index ba0591ab4..d39e3c3dc 100755 --- a/tools/package-macos-arm64.sh +++ b/tools/package-macos-arm64.sh @@ -47,10 +47,10 @@ mkdir -p "$OUTPUT_DIR/debug" eval "$DART compile exe -o $OUTPUT_DIR/sshnpd $SRC_DIR/bin/sshnpd.dart" eval "$DART compile exe -o $OUTPUT_DIR/sshnp $SRC_DIR/bin/sshnp.dart" -eval "$DART compile exe -o $OUTPUT_DIR/sshrvd $SRC_DIR/bin/sshrvd.dart" -eval "$DART compile exe -o $OUTPUT_DIR/sshrv $SRC_DIR/bin/sshrv.dart" +eval "$DART compile exe -o $OUTPUT_DIR/srvd $SRC_DIR/bin/srvd.dart" +eval "$DART compile exe -o $OUTPUT_DIR/srv $SRC_DIR/bin/srv.dart" eval "$DART compile exe -o $OUTPUT_DIR/at_activate $SRC_DIR/bin/activate_cli.dart" -eval "$DART compile exe -o $OUTPUT_DIR/debug/sshrvd -D ENABLE_SNOOP=true $SRC_DIR/bin/sshrvd.dart" +eval "$DART compile exe -o $OUTPUT_DIR/debug/srvd -D ENABLE_SNOOP=true $SRC_DIR/bin/srvd.dart" cp -r "$SRC_DIR/bundles/core"/* "$OUTPUT_DIR/"; cp -r "$SRC_DIR/bundles/shell"/* "$OUTPUT_DIR/"; From 796b59f8c98018fda53e9fd1403e6652720c750c Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 22 Jan 2024 14:58:12 +0800 Subject: [PATCH 60/60] chore: rename remaining sshrv files --- .../lib/src/srvd/sshrvd_impl.dart | 342 ------------------ .../headless/{root_sshrvd.sh => root_srvd.sh} | 0 .../shell/headless/{sshrvd.sh => srvd.sh} | 0 .../systemd/{sshrvd.service => srvd.service} | 0 ...entrypoint.sh => setup-srvd-entrypoint.sh} | 0 ...etup-sshrvd-keys.sh => setup-srvd-keys.sh} | 0 .../{sshrvd => srvd}/.atsign/keys/.gitkeep | 0 ...shrvd_entrypoint.sh => srvd_entrypoint.sh} | 0 ...shrvd.yaml => service-container-srvd.yaml} | 0 9 files changed, 342 deletions(-) delete mode 100644 packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart rename packages/dart/sshnoports/bundles/shell/headless/{root_sshrvd.sh => root_srvd.sh} (100%) rename packages/dart/sshnoports/bundles/shell/headless/{sshrvd.sh => srvd.sh} (100%) rename packages/dart/sshnoports/bundles/shell/systemd/{sshrvd.service => srvd.service} (100%) rename tests/end2end_tests/contexts/_init_/{setup-sshrvd-entrypoint.sh => setup-srvd-entrypoint.sh} (100%) rename tests/end2end_tests/contexts/_init_/{setup-sshrvd-keys.sh => setup-srvd-keys.sh} (100%) rename tests/end2end_tests/contexts/{sshrvd => srvd}/.atsign/keys/.gitkeep (100%) rename tests/end2end_tests/entrypoints/{sshrvd_entrypoint.sh => srvd_entrypoint.sh} (100%) rename tests/end2end_tests/tests/{service-container-sshrvd.yaml => service-container-srvd.yaml} (100%) diff --git a/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart b/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart deleted file mode 100644 index 95662a165..000000000 --- a/packages/dart/noports_core/lib/src/srvd/sshrvd_impl.dart +++ /dev/null @@ -1,342 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'dart:isolate'; -import 'dart:convert'; -import 'package:at_client/at_client.dart'; -import 'package:at_utils/at_logger.dart'; -import 'package:logging/logging.dart'; -import 'package:meta/meta.dart'; -import 'package:noports_core/src/common/validation_utils.dart'; -import 'package:noports_core/src/srvd/build_env.dart'; -import 'package:noports_core/src/srvd/socket_connector.dart'; -import 'package:noports_core/src/srvd/srvd.dart'; -import 'package:noports_core/src/srvd/srvd_params.dart'; - -@protected -class SrvdImpl implements Srvd { - @override - final AtSignLogger logger = AtSignLogger(' srvd '); - @override - AtClient atClient; - @override - final String atSign; - @override - final String homeDirectory; - @override - final String atKeysFilePath; - @override - final String managerAtsign; - @override - final String ipAddress; - @override - final bool logTraffic; - @override - bool verbose = false; - - @override - @visibleForTesting - bool initialized = false; - - static final String subscriptionRegex = '${Srvd.namespace}@'; - - late final SrvdUtil srvdUtil; - - SrvdImpl({ - required this.atClient, - required this.atSign, - required this.homeDirectory, - required this.atKeysFilePath, - required this.managerAtsign, - required this.ipAddress, - required this.logTraffic, - required this.verbose, - SrvdUtil? srvdUtil, - }) { - this.srvdUtil = srvdUtil ?? SrvdUtil(atClient); - logger.hierarchicalLoggingEnabled = true; - logger.logger.level = Level.SHOUT; - } - - static Future fromCommandLineArgs(List args, - {AtClient? atClient, - FutureOr Function(SrvdParams)? atClientGenerator, - void Function(Object, StackTrace)? usageCallback}) async { - try { - var p = await SrvdParams.fromArgs(args); - - if (!await File(p.atKeysFilePath).exists()) { - throw ('\n Unable to find .atKeys file : ${p.atKeysFilePath}'); - } - - AtSignLogger.root_level = 'SHOUT'; - if (p.verbose) { - AtSignLogger.root_level = 'INFO'; - } - - if (atClient == null && atClientGenerator == null) { - throw StateError('atClient and atClientGenerator are both null'); - } - - atClient ??= await atClientGenerator!(p); - - var srvd = SrvdImpl( - atClient: atClient, - atSign: p.atSign, - homeDirectory: p.homeDirectory, - atKeysFilePath: p.atKeysFilePath, - managerAtsign: p.managerAtsign, - ipAddress: p.ipAddress, - logTraffic: p.logTraffic, - verbose: p.verbose, - ); - - if (p.verbose) { - srvd.logger.logger.level = Level.INFO; - } - return srvd; - } catch (e, s) { - usageCallback?.call(e, s); - rethrow; - } - } - - @override - Future init() async { - if (initialized) { - throw StateError('Cannot init() - already initialized'); - } - - initialized = true; - } - - @override - Future run() async { - if (!initialized) { - throw StateError('Cannot run() - not initialized'); - } - NotificationService notificationService = atClient.notificationService; - - notificationService - .subscribe(regex: subscriptionRegex, shouldDecrypt: true) - .listen(_notificationHandler); - } - - void _notificationHandler(AtNotification notification) async { - if (!srvdUtil.accept(notification)) { - return; - } - late SrvdSessionParams sessionParams; - try { - sessionParams = await srvdUtil.getParams(notification); - - if (managerAtsign != 'open' && managerAtsign != sessionParams.atSignA) { - logger.shout( - 'Session ${sessionParams.sessionId} for ${sessionParams.atSignA} is denied'); - return; - } - } catch (e) { - logger.shout('Unable to provide the socket pair due to: $e'); - return; - } - - logger - .info('New session request: $sessionParams from ${notification.from}'); - - (int, int) ports = await _spawnSocketConnector( - 0, - 0, - sessionParams, - logTraffic, - verbose, - ); - var (portA, portB) = ports; - logger.warning( - 'Starting session ${sessionParams.sessionId} for ${sessionParams.atSignA} to ${sessionParams.atSignB} using ports $ports'); - - var metaData = Metadata() - ..isPublic = false - ..isEncrypted = true - ..ttl = 10000 - ..namespaceAware = true; - - var atKey = AtKey() - ..key = sessionParams.sessionId - ..sharedBy = atSign - ..sharedWith = notification.from - ..namespace = Srvd.namespace - ..metadata = metaData; - - String data = '$ipAddress,$portA,$portB,${sessionParams.rvdNonce}'; - - logger.info( - 'Sending response data for session ${sessionParams.sessionId} : [$data]'); - - try { - await atClient.notificationService.notify( - NotificationParams.forUpdate(atKey, value: data), - waitForFinalDeliveryStatus: false, - checkForFinalDeliveryStatus: false); - } catch (e) { - stderr.writeln("Error writing session ${notification.value} atKey"); - } - } - - /// This function spawns a new socketConnector in a background isolate - /// once the socketConnector has spawned and is ready to accept connections - /// it sends back the port numbers to the main isolate - /// then the port numbers are returned from this function - Future _spawnSocketConnector( - int portA, - int portB, - SrvdSessionParams srvdSessionParams, - bool logTraffic, - bool verbose, - ) async { - /// Spawn an isolate and wait for it to send back the issued port numbers - ReceivePort receivePort = ReceivePort(srvdSessionParams.sessionId); - - ConnectorParams parameters = ( - receivePort.sendPort, - portA, - portB, - jsonEncode(srvdSessionParams), - BuildEnv.enableSnoop && logTraffic, - verbose, - ); - - logger - .info("Spawning socket connector isolate with parameters $parameters"); - - unawaited(Isolate.spawn(socketConnector, parameters)); - - PortPair ports = await receivePort.first; - - logger.info('Received ports $ports in main isolate' - ' for session ${srvdSessionParams.sessionId}'); - - return ports; - } -} - -class SrvdSessionParams { - final String sessionId; - final String atSignA; - final String? atSignB; - final bool authenticateSocketA; - final bool authenticateSocketB; - final String? publicKeyA; - final String? publicKeyB; - final String? clientNonce; - final String? rvdNonce; - - SrvdSessionParams({ - required this.sessionId, - required this.atSignA, - this.atSignB, - this.authenticateSocketA = false, - this.authenticateSocketB = false, - this.publicKeyA, - this.publicKeyB, - this.rvdNonce, - this.clientNonce, - }); - - @override - String toString() => toJson().toString(); - - Map toJson() => { - 'sessionId': sessionId, - 'atSignA': atSignA, - 'atSignB': atSignB, - 'authenticateSocketA': authenticateSocketA, - 'authenticateSocketB': authenticateSocketB, - 'publicKeyA': publicKeyA, - 'publicKeyB': publicKeyB, - 'rvdNonce': rvdNonce, - 'clientNonce': clientNonce, - }; - - static SrvdSessionParams fromJson(Map json) { - return SrvdSessionParams( - sessionId: json['sessionId'], - atSignA: json['atSignA'], - atSignB: json['atSignB'], - authenticateSocketA: json['authenticateSocketA'], - authenticateSocketB: json['authenticateSocketB'], - publicKeyA: json['publicKeyA'], - publicKeyB: json['publicKeyB'], - rvdNonce: json['rvdNonce'], - clientNonce: json['clientNonce'], - ); - } -} - -class SrvdUtil { - final AtClient atClient; - - SrvdUtil(this.atClient); - - bool accept(AtNotification notification) { - return notification.key.contains(Srvd.namespace); - } - - Future getParams(AtNotification notification) async { - if (notification.key.contains('.request_ports.${Srvd.namespace}')) { - return await _processJSONRequest(notification); - } - return _processLegacyRequest(notification); - } - - SrvdSessionParams _processLegacyRequest(AtNotification notification) { - return SrvdSessionParams( - sessionId: notification.value!, - atSignA: notification.from, - ); - } - - Future _processJSONRequest( - AtNotification notification) async { - dynamic jsonValue = jsonDecode(notification.value ?? ''); - - assertValidValue(jsonValue, 'sessionId', String); - assertValidValue(jsonValue, 'atSignA', String); - assertValidValue(jsonValue, 'atSignB', String); - assertValidValue(jsonValue, 'clientNonce', String); - assertValidValue(jsonValue, 'authenticateSocketA', bool); - assertValidValue(jsonValue, 'authenticateSocketA', bool); - - final String sessionId = jsonValue['sessionId']; - final String atSignA = jsonValue['atSignA']; - final String atSignB = jsonValue['atSignB']; - final String clientNonce = jsonValue['clientNonce']; - final bool authenticateSocketA = jsonValue['authenticateSocketA']; - final bool authenticateSocketB = jsonValue['authenticateSocketB']; - - String rvdSessionNonce = DateTime.now().toIso8601String(); - - String? publicKeyA; - String? publicKeyB; - if (authenticateSocketA) { - publicKeyA = await _fetchPublicKey(atSignA); - } - if (authenticateSocketB) { - publicKeyB = await _fetchPublicKey(atSignB); - } - return SrvdSessionParams( - sessionId: sessionId, - atSignA: atSignA, - atSignB: atSignB, - authenticateSocketA: authenticateSocketA, - authenticateSocketB: authenticateSocketB, - publicKeyA: publicKeyA, - publicKeyB: publicKeyB, - rvdNonce: rvdSessionNonce, - clientNonce: clientNonce, - ); - } - - Future _fetchPublicKey(String atSign) async { - AtValue v = await atClient.get(AtKey.fromString('public:publickey$atSign')); - return v.value; - } -} diff --git a/packages/dart/sshnoports/bundles/shell/headless/root_sshrvd.sh b/packages/dart/sshnoports/bundles/shell/headless/root_srvd.sh similarity index 100% rename from packages/dart/sshnoports/bundles/shell/headless/root_sshrvd.sh rename to packages/dart/sshnoports/bundles/shell/headless/root_srvd.sh diff --git a/packages/dart/sshnoports/bundles/shell/headless/sshrvd.sh b/packages/dart/sshnoports/bundles/shell/headless/srvd.sh similarity index 100% rename from packages/dart/sshnoports/bundles/shell/headless/sshrvd.sh rename to packages/dart/sshnoports/bundles/shell/headless/srvd.sh diff --git a/packages/dart/sshnoports/bundles/shell/systemd/sshrvd.service b/packages/dart/sshnoports/bundles/shell/systemd/srvd.service similarity index 100% rename from packages/dart/sshnoports/bundles/shell/systemd/sshrvd.service rename to packages/dart/sshnoports/bundles/shell/systemd/srvd.service diff --git a/tests/end2end_tests/contexts/_init_/setup-sshrvd-entrypoint.sh b/tests/end2end_tests/contexts/_init_/setup-srvd-entrypoint.sh similarity index 100% rename from tests/end2end_tests/contexts/_init_/setup-sshrvd-entrypoint.sh rename to tests/end2end_tests/contexts/_init_/setup-srvd-entrypoint.sh diff --git a/tests/end2end_tests/contexts/_init_/setup-sshrvd-keys.sh b/tests/end2end_tests/contexts/_init_/setup-srvd-keys.sh similarity index 100% rename from tests/end2end_tests/contexts/_init_/setup-sshrvd-keys.sh rename to tests/end2end_tests/contexts/_init_/setup-srvd-keys.sh diff --git a/tests/end2end_tests/contexts/sshrvd/.atsign/keys/.gitkeep b/tests/end2end_tests/contexts/srvd/.atsign/keys/.gitkeep similarity index 100% rename from tests/end2end_tests/contexts/sshrvd/.atsign/keys/.gitkeep rename to tests/end2end_tests/contexts/srvd/.atsign/keys/.gitkeep diff --git a/tests/end2end_tests/entrypoints/sshrvd_entrypoint.sh b/tests/end2end_tests/entrypoints/srvd_entrypoint.sh similarity index 100% rename from tests/end2end_tests/entrypoints/sshrvd_entrypoint.sh rename to tests/end2end_tests/entrypoints/srvd_entrypoint.sh diff --git a/tests/end2end_tests/tests/service-container-sshrvd.yaml b/tests/end2end_tests/tests/service-container-srvd.yaml similarity index 100% rename from tests/end2end_tests/tests/service-container-sshrvd.yaml rename to tests/end2end_tests/tests/service-container-srvd.yaml