JKRhb / dtls2

A DTLS library for Dart based on OpenSSL.
MIT License
3 stars 0 forks source link

Unable to catch DtlsException: DTLS Handshake has failed. #80

Closed Ifilehk closed 1 year ago

Ifilehk commented 1 year ago

Hello, here an other point where I am struggling. Unable to catch DTLS Handshake has failed in this situation.

Client have a working dtls connection with the server. Server wants to ban the client. Client's presharedkey is then removed from the server. Client's tries to connect.

Result:

E/flutter (17787): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DtlsException: DTLS Handshake has failed. E/flutter (17787): #0 _DtlsClientConnection._performShutdown (package:dtls2/src/dtls_client.dart:496:7) E/flutter (17787): #1 _DtlsClientConnection._connectToPeer.<anonymous closure> (package:dtls2/src/dtls_client.dart:518:15) E/flutter (17787): #2 _DtlsClientConnection._handleError (package:dtls2/src/dtls_client.dart:482:21) E/flutter (17787): #3 _DtlsClientConnection._connectToPeer (package:dtls2/src/dtls_client.dart:516:7) E/flutter (17787): #4 _DtlsClientConnection._maintainState (package:dtls2/src/dtls_client.dart:602:7) E/flutter (17787): #5 _DtlsClientConnection._incoming (package:dtls2/src/dtls_client.dart:563:5) E/flutter (17787): #6 DtlsClient._startListening.<anonymous closure> (package:dtls2/src/dtls_client.dart:87:26)

Would like to be able to catch this exeption to redirect the client to the signing page.

Thank you for your support.

JKRhb commented 1 year ago

Hi @Ifilehk! Thanks for opening this issue :)

Simply wrapping the connect() in a try-catch block probably wouldn't work here, right? Would creating a custom exception that is only being thrown in the case of a failed handshake be an option here? Or do you maybe have a better suggestion?

JKRhb commented 1 year ago

Hmm, or is the problem rather that the client is trying to reuse a connection that has been cached before?

Ifilehk commented 1 year ago

No wrapping connect does not catch it. I suppose the exception is not transmitted up stream in the code.

Ifilehk commented 1 year ago

exception should come out in connection.listen((...) { ... }, on DtlsException { here }

JKRhb commented 1 year ago

No wrapping connect does not catch it. I suppose the exception is not transmitted up stream in the code.

Ah, I see! Hmm, maybe that is caused by the fact that the exception is thrown as part of a callback?

https://github.com/JKRhb/dtls2/blob/c25e3bdcb46ad8371132d8a354060d972443b771/lib/src/dtls_client.dart#L516-L520

Ifilehk commented 1 year ago

Possible ! thus not transmitted upstream. By the way if I remember well the Dtls Exception on send() works as expected.

JKRhb commented 1 year ago

Possible ! thus not transmitted upstream. By the way if I remember well the Dtls Exception on send() works as expected.

Here, the exception is actually being thrown directly:

https://github.com/JKRhb/dtls2/blob/c25e3bdcb46ad8371132d8a354060d972443b771/lib/src/dtls_client.dart#L523-L530

I guess we figured out how to solve this problem then :) I will try to come up with a fix in the next few hours.

JKRhb commented 1 year ago

@Ifilehk Could you try out if #81 fixes the issue? :)

Ifilehk commented 1 year ago

Unfortunately not yet fixed. Getting same Unhandled Exception.

E/flutter (12576): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DtlsHandshakeException: DTLS Handshake has failed.
E/flutter (12576): #0      _DtlsClientConnection._performShutdown (package:dtls2/src/dtls_client.dart:492:7)
E/flutter (12576): #1      _DtlsClientConnection._connectToPeer (package:dtls2/src/dtls_client.dart:513:9)
E/flutter (12576): #2      _DtlsClientConnection._maintainState (package:dtls2/src/dtls_client.dart:597:7)
E/flutter (12576): #3      _DtlsClientConnection._incoming (package:dtls2/src/dtls_client.dart:558:5)
E/flutter (12576): #4      DtlsClient._startListening.<anonymous closure> (package:dtls2/src/dtls_client.dart:87:26)
JKRhb commented 1 year ago

Hmm, could you post a code snippet reproducing the issue? Or how the exception should be caught?

Ifilehk commented 1 year ago

Here a test code against my dtls2 server.

Interesting to see also, not related to this bug but maybe to understand the flow, that is you use a correct identity and a wrong presharedkey, you get DTLS Connection ERROR: TIMEOUT EXCEPTION. I would expect in this case too a DTLS Exception or what do you think ?

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dtls2/dtls2.dart';

void main(List<String> arguments) {
  connect();
}

void connect() async {
  try {
    print("Connecting ...");

    const bindAddress = "0.0.0.0";
    DtlsClient dtlsClient = await DtlsClient.bind(bindAddress, 0);

    DtlsConnection dtlsConnection = await dtlsClient.connect(
      InternetAddress("XXX.XXX.XXX.XXX"),
      XXXX,
      DtlsClientContext(
        verify            : true,
        withTrustedRoots  : true,
        ciphers           : "PSK-AES128-CCM8",
        pskCredentialsCallback: (identityHint) {
          return PskCredentials(
            //identity      : ascii.encode('ID0'),  // Valid identity
            //preSharedKey  : ascii.encode('mAKW-X437USLUGkMZEcass5nwpgcLQbN'), // Valid key
            identity      : ascii.encode('ID1'),                            // Invalid identity
            preSharedKey  : ascii.encode('OAKW-X437USLUGkMZEcass5nwpgcLQbN'), // Invalid key
          );
        },
      ),
      timeout: const Duration(seconds: 10),
    );

    print('Connected');

    dtlsConnection.listen((datagram) async {
      print('Received datagram $datagram');

    }, onDone: () {
      print('DTLS Connection DONE');
    }, onError: (e) {
      print('DTLS Connection ERROR $e');
    },);

  } on DtlsException {
    print('DTLS Connection ERROR: DTLS EXCEPTION');

  } on SocketException {
    print('DTLS Connection ERROR: SOCKET EXCEPTION');

  } on TimeoutException {
    print('DTLS Connection ERROR: TIMEOUT EXCEPTION');

  } catch(e) {
    print('Unhandled exception $e');
  }
}
JKRhb commented 1 year ago

Hi @Ifilehk, thank you for the example! Unfortunately, I haven't been able to reproduce the issue using it :/ Could you maybe try using the altered example below (which uses the Californium test server) as a basis to replicate the issue (or integrate it with a server implementation)? This way I could try it out myself.

Good point regarding the TimeoutException by the way, how about a DtlsTimeoutException that extends the DtlsException class and implements TimeoutException? This way, we could have the best of both worlds, so to say. Edit: Done in #83.

// ignore_for_file: avoid_print

import "dart:async";
import "dart:convert";
import "dart:io";

import "package:dtls2/dtls2.dart";

void main(List<String> arguments) {
  connect();
}

const host = "californium.eclipseprojects.io";

Future<InternetAddress> lookupAddress() async {
  return (await InternetAddress.lookup(host, type: InternetAddressType.IPv6))
      .first;
}

Future<void> connect() async {
  final dtlsClient = await DtlsClient.bind(InternetAddress.anyIPv6, 0);

  final address = await lookupAddress();

  try {
    print("Connecting ...");

    final dtlsConnection = await dtlsClient.connect(
      address,
      5684,
      DtlsClientContext(
        withTrustedRoots: true,
        ciphers: "PSK-AES128-CCM8",
        pskCredentialsCallback: (identityHint) {
          return PskCredentials(
            identity: ascii.encode("Client_identity"),
            preSharedKey: ascii.encode("secretPSK"),
          );
        },
      ),
      timeout: const Duration(seconds: 10),
    );

    print("Connected");

    dtlsConnection.listen(
      (datagram) async {
        print("Received datagram $datagram");
      },
      onDone: () {
        print("DTLS Connection DONE");
      },
      onError: (e) {
        print("DTLS Connection ERROR $e");
      },
    );
  } on DtlsException {
    print("DTLS Connection ERROR: DTLS EXCEPTION");
  } on SocketException {
    print("DTLS Connection ERROR: SOCKET EXCEPTION");
  } on TimeoutException {
    print("DTLS Connection ERROR: TIMEOUT EXCEPTION");
  } catch (e) {
    print("Unhandled exception $e");
  }

  dtlsClient.close();
}
Ifilehk commented 1 year ago

OK will try it.

Regarding DtlsTimeoutException. I don't understand it in this context. Of course the DtlsConnection ends with not beeing established, but the real cause is that the identity / preshared key pair is invalid. For the upper layer it makes sense to know the reason. And actually what is a DtlsTimeoutException ? I think it lacks a definition. In a connection Timeout means peer did not respond after a certain time. Here we have a response and an error that could give information to the client. What do you think ?

Ifilehk commented 1 year ago

Cannot reproduce on your server. Could you try on mine ? 93.90.201.53 8888

Valid client: WERT PSK: P4OPmcvtHyZIhBjDSmPgGjM8V1Ykkp2D

JKRhb commented 1 year ago

Cannot reproduce on your server. Could you try on mine ? 93.90.201.53 8888

Valid client: WERT PSK: P4OPmcvtHyZIhBjDSmPgGjM8V1Ykkp2D

Thank you! That helped me a lot actually :) I think I now found the root cause of the problem and opened #85 as a potential fix. The bug was that an exception was thrown in the first place instead of using the Completer – this made it uncatchable from the outside.

JKRhb commented 1 year ago

Regarding DtlsTimeoutException. I don't understand it in this context. Of course the DtlsConnection ends with not beeing established, but the real cause is that the identity / preshared key pair is invalid. For the upper layer it makes sense to know the reason. And actually what is a DtlsTimeoutException ? I think it lacks a definition. In a connection Timeout means peer did not respond after a certain time. Here we have a response and an error that could give information to the client. What do you think ?

That's true, I think the TimeoutException should only be thrown in the case of an actual timeout, though. With the potential fix in #85, I think this should now rather be the case. For other scenarios, I think it should be possible to determine the reason why the Handshake has failed and create more fine-grained exceptions from that. I will have a look into it.

Ifilehk commented 1 year ago

85 does the trick !!! Now client says:

DTLS EXCEPTION at the right place.

Thank you !!!

JKRhb commented 1 year ago

85 does the trick !!! Now client says: DTLS EXCEPTION at the right place.

Thank you !!!

Awesome! Thank you for your error report! :) Then I will merge #85.