IDE: enforce CRA to WAMP connector, using known PSK as credentials. TLS support is WIP.
--- a/connectors/WAMP/__init__.py Fri Feb 28 16:48:28 2025 +0100
+++ b/connectors/WAMP/__init__.py Fri Feb 28 16:53:33 2025 +0100
@@ -29,12 +29,16 @@
from threading import Thread, Event
from twisted.internet import reactor, threads
+from twisted.internet._sslverify import OpenSSLCertificateAuthorities
from autobahn.twisted import wamp
from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS
-from autobahn.wamp import types
+from autobahn.wamp import types, auth
from autobahn.wamp.exception import TransportLost
from autobahn.wamp.serializer import MsgPackSerializer
+from ProjectController import ToDoBeforeQuit
+from connectors.ConnectorBase import ConnectorBase
+import PSKManagement as PSK
_WampSession = None
_WampConnection = None
@@ -42,6 +46,29 @@
class WampSession(wamp.ApplicationSession):
+ def onConnect(self):
+ user = self.config.extra["ID"]
+ self.config.realm, user))
+ self.join(self.config.realm, ["wampcra"], user)
+
+ def onChallenge(self, challenge):
+ if challenge.method == "wampcra":
+ secret = self.config.extra["secret"]
+ if 'salt' in challenge.extra:
+ # salted secret
+ key = auth.derive_key(secret,
+ challenge.extra['salt'],
+ challenge.extra['iterations'],
+ challenge.extra['keylen'])
+ else:
+ # plain, unsalted secret
+ key = secret
+
+ signature = auth.compute_wcs(key, challenge.extra['challenge'])
+ return signature
+ else:
+ raise Exception("Invalid authmethod {}".format(challenge.method))
+
def onJoin(self, details):
global _WampSession
_WampSession = self
@@ -54,6 +81,19 @@
_WampSession = None
print('WAMP session left')
+def MakeSecureContextFactory(verifyHostname, trust_store=None):
+ if not verifyHostname:
+ return None
+ trustRoot=None
+ if trust_store:
+ if not os.path.exists(trust_store):
+ raise Exception("Wamp trust store not found")
+ cert = crypto.load_certificate(
+ crypto.FILETYPE_PEM,
+ open(trust_store, 'rb').read()
+ )
+ trustRoot=OpenSSLCertificateAuthorities([cert])
+ return optionsForClientTLS(_transportFactory.host, trustRoot=trustRoot)
def _WAMP_connector_factory(cls, uri, confnodesroot):
"""
@@ -66,6 +106,16 @@
"WAMPS": "wss"}[scheme]
url = urlprefix+"://"+urlpath
+ try:
+ secret = PSK.GetSecret(confnodesroot.ProjectPath, ID)
+ # TODO: add x509 certificate management together with PSK management.
+ trust_store = None
+ except Exception as e:
+ confnodesroot.logger.write_error(
+ _("Connection to {loc} failed with exception {ex}\n").format(
+ loc=uri, ex=str(e)))
+ return None
+
def RegisterWampClient():
# start logging to console
@@ -74,7 +124,10 @@
# create a WAMP application session factory
component_config = types.ComponentConfig(
realm=str(realm),
- extra={"ID": ID})
+ extra={
+ "ID": ID,
+ "secret": secret
+ })
session_factory = wamp.ApplicationSessionFactory(
config=component_config)
session_factory.session = cls
@@ -85,8 +138,14 @@
url=url,
serializers=[MsgPackSerializer()])
+ contextFactory=None
+ if transport_factory.isSecure:
+ contextFactory = MakeSecureContextFactory(
+ verifyHostname=True,
+ trust_store=trust_store)
+
# start the client from a Twisted endpoint
- conn = connectWS(transport_factory)
+ conn = connectWS(transport_factory, contextFactory)
confnodesroot.logger.write(_("WAMP connecting to URL : %s\n") % url)
return conn
@@ -97,7 +156,7 @@
ToDoBeforeQuit.append(reactor.stop)
reactor.run(installSignalHandlers=False)
- class WampPLCObjectProxy(object):
+ class WampPLCObjectProxy(ConnectorBase):
def __init__(self):
global _WampConnection
if not reactor.running: