From 40c7b96f6b14739fce79df0d24145655c25d5b3d Mon Sep 17 00:00:00 2001 From: Jan Romann Date: Fri, 1 Apr 2022 12:16:45 +0200 Subject: [PATCH] feat: add (experimental) coaps support --- lib/src/binding_coap/coap_client.dart | 73 +++++++++++++++++-- lib/src/binding_coap/coap_client_factory.dart | 2 +- lib/src/binding_coap/coap_config.dart | 17 ++++- lib/src/core/consumed_thing.dart | 9 ++- 4 files changed, 89 insertions(+), 12 deletions(-) diff --git a/lib/src/binding_coap/coap_client.dart b/lib/src/binding_coap/coap_client.dart index 59026908..ad77e299 100644 --- a/lib/src/binding_coap/coap_client.dart +++ b/lib/src/binding_coap/coap_client.dart @@ -9,6 +9,7 @@ import 'dart:convert'; import 'package:coap/coap.dart' as coap; import 'package:coap/config/coap_config_default.dart'; import 'package:curie/curie.dart'; +import 'package:dart_wot/src/definitions/credentials/psk_credentials.dart'; import '../core/content.dart'; import '../core/operation_type.dart'; @@ -113,7 +114,30 @@ class _InternalCoapConfig extends CoapConfigDefault { @override int preferredBlockSize; - _InternalCoapConfig(this.preferredBlockSize); + @override + coap.DtlsBackend? dtlsBackend; + + final Form _form; + + _InternalCoapConfig(CoapConfig coapConfig, this._form) + : preferredBlockSize = + coapConfig.blocksize ?? coap.CoapConstants.preferredBlockSize { + if (!_dtlsNeeded) { + return; + } + + if (_hasPskCredentials(_form) && coapConfig.useTinyDtls) { + dtlsBackend = coap.DtlsBackend.TinyDtls; + } else if (coapConfig.useOpenSsl) { + dtlsBackend = coap.DtlsBackend.OpenSsl; + } + } + + bool get _dtlsNeeded => _form.href.startsWith("coaps"); +} + +bool _hasPskCredentials(Form form) { + return form.credentials.whereType().isNotEmpty; } class _CoapRequest { @@ -137,9 +161,11 @@ class _CoapRequest { _CoapRequest( this._form, this._requestMethod, - _InternalCoapConfig _coapConfig, [ + CoapConfig _coapConfig, [ this._subprotocol, - ]) : _coapClient = coap.CoapClient(Uri.parse(_form.href), _coapConfig), + ]) : _coapClient = coap.CoapClient( + Uri.parse(_form.href), _InternalCoapConfig(_coapConfig, _form), + pskCredentialsCallback: _createPskCallback(_form)), _requestUri = Uri.parse(_form.href); // TODO(JKRhb): blockwise parameters cannot be handled at the moment due to @@ -181,6 +207,41 @@ class _CoapRequest { return response; } + static coap.PskCredentials? _retrievePskCredentials(Form form) { + final pskCredentialsList = form.credentials.whereType(); + + for (final pskCredentials in pskCredentialsList) { + final identity = + pskCredentials.identity ?? pskCredentials.securityScheme?.identity; + + if (identity == null) { + continue; + } + + final preSharedKey = pskCredentials.preSharedKey; + + return coap.PskCredentials( + identity: identity, preSharedKey: preSharedKey); + } + + return null; + } + + static coap.PskCredentialsCallback? _createPskCallback(Form form) { + if (!_hasPskCredentials(form)) { + return null; + } + + final pskCredentials = _retrievePskCredentials(form); + + if (pskCredentials == null) { + throw CoapBindingException("No client Identity found for CoAPS request!"); + } + + // TODO(JKRhb): Should the identityHint be handled? + return (identityHint) => pskCredentials; + } + int _determineContentFormat(_ContentFormatType _contentFormatType) { final curieString = _coapPrefixMapping.expandCurieString(_contentFormatType.stringValue); @@ -291,10 +352,8 @@ class CoapClient extends ProtocolClient { final requestMethod = _getRequestMethod(form, operationType); final _Subprotocol? subprotocol = _determineSubprotocol(form, operationType); - final internalCoapConfig = _InternalCoapConfig( - _coapConfig?.blocksize ?? coap.CoapConstants.preferredBlockSize); - final request = - _CoapRequest(form, requestMethod, internalCoapConfig, subprotocol); + final coapConfig = _coapConfig ?? CoapConfig(); + final request = _CoapRequest(form, requestMethod, coapConfig, subprotocol); _pendingRequests.add(request); return request; } diff --git a/lib/src/binding_coap/coap_client_factory.dart b/lib/src/binding_coap/coap_client_factory.dart index 6bd7bc9f..a0895253 100644 --- a/lib/src/binding_coap/coap_client_factory.dart +++ b/lib/src/binding_coap/coap_client_factory.dart @@ -13,7 +13,7 @@ import 'coap_config.dart'; /// A [ProtocolClientFactory] that produces CoAP clients. class CoapClientFactory extends ProtocolClientFactory { @override - Set get schemes => {"coap"}; + Set get schemes => {"coap", "coaps"}; /// The [CoapConfig] used to configure new clients. final CoapConfig? coapConfig; diff --git a/lib/src/binding_coap/coap_config.dart b/lib/src/binding_coap/coap_config.dart index 0486ad2f..a56dd8ea 100644 --- a/lib/src/binding_coap/coap_config.dart +++ b/lib/src/binding_coap/coap_config.dart @@ -9,9 +9,24 @@ class CoapConfig { /// The port number used by a client or server. Defaults to 5683. final int port; + /// The coaps port number used by a client or server. Defaults to 5684. + final int securePort; + /// The preferred block size for blockwise transfer. final int? blocksize; + /// Indicates if tinydtls is available as a DTLS backend. + final bool useTinyDtls; + + /// Indicates if openSSL is available as a DTLS backend. + final bool useOpenSsl; + /// Creates a new [CoapConfig] object. - CoapConfig({this.port = 5683, this.blocksize}); + CoapConfig({ + this.port = 5683, + this.securePort = 5684, + this.blocksize, + this.useTinyDtls = false, + this.useOpenSsl = false, + }); } diff --git a/lib/src/core/consumed_thing.dart b/lib/src/core/consumed_thing.dart index 237827bc..ae3a9554 100644 --- a/lib/src/core/consumed_thing.dart +++ b/lib/src/core/consumed_thing.dart @@ -100,9 +100,12 @@ class ConsumedThing implements scripting_api.ConsumedThing { '$formIndex"'); } } else { - foundForm = forms.firstWhere((form) => - hasClientFor(Uri.parse(form.href).scheme) && - _supportsOperationType(form, affordanceType, operationType)); + foundForm = forms.firstWhere( + (form) => + hasClientFor(Uri.parse(form.href).scheme) && + _supportsOperationType(form, affordanceType, operationType), + // TODO(JKRhb): Add custom Exception + orElse: () => throw Exception("No matching form found!")); final scheme = Uri.parse(foundForm.href).scheme; client = servient.clientFor(scheme); }