MQTT: add SSL support
authorEdouard Tisserant <edouard@beremiz.fr>
Thu, 22 Aug 2024 12:15:31 +0200
changeset 4005 482452574fb4
parent 4004 19f8192b7d68
child 4006 e16c8443e877
MQTT: add SSL support
mqtt/client.py
mqtt/mqtt_client_gen.py
mqtt/mqtt_template.c
--- a/mqtt/client.py	Thu Aug 08 14:56:13 2024 +0200
+++ b/mqtt/client.py	Thu Aug 22 12:15:31 2024 +0200
@@ -3,6 +3,7 @@
 from __future__ import absolute_import
 
 import os
+import re
 
 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
 from PLCControler import LOCATION_CONFNODE, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT
@@ -41,8 +42,15 @@
                 <xsd:choice minOccurs="0">
                   <xsd:element name="x509">
                     <xsd:complexType>
-                      <xsd:attribute name="Certificate" type="xsd:string" use="optional" default="certificate.pem"/>
-                      <xsd:attribute name="PrivateKey" type="xsd:string" use="optional" default="private_key.pem"/>
+                      <xsd:attribute name="Client_certificate" type="xsd:string" use="optional" default="KeyStore.pem"/>
+                      <xsd:attribute name="Broker_certificate" type="xsd:string" use="optional" default="TrustStore.pem"/>
+                      <xsd:attribute name="Verify_hostname" type="xsd:boolean" use="optional" default="true"/>
+                    </xsd:complexType>
+                  </xsd:element>
+                  <xsd:element name="PSK">
+                    <xsd:complexType>
+                      <xsd:attribute name="Secret" type="xsd:string" use="optional" default=""/>
+                      <xsd:attribute name="ID" type="xsd:string" use="optional" default=""/>
                     </xsd:complexType>
                   </xsd:element>
                   <xsd:element name="UserPassword">
@@ -96,12 +104,23 @@
         paramList = authParams.get(AuthType, None)
         if paramList:
             for name,default in paramList:
-                value = cfg("AuthType."+name)
+
+                # Translate internal config naming into user config naming
+                displayed_name = {"KeyStore"   : "Client_certificate",
+                                  "TrustStore" : "Broker_certificate", 
+                                  "Verify"     : "Verify_hostname"}.get(name, name)
+
+                value = cfg("AuthType." + displayed_name)
                 if value == "" or value is None:
                     value = default
-                # cryptomaterial is expected to be in project's user provide file directory
-                if name in ["Certificate","PrivateKey"]:
-                    value = os.path.join(self.GetCTRoot()._getProjectFilesPath(), value)
+
+                if value is not None:
+                    # cryptomaterial is expected to be in project's user provided file directory
+
+                    # User input may contain char incompatible with C string literal escaping
+                    if name in ["User","Password","TrustStore","KeyStore","Broker_URI","Client_ID"]:
+                        value = re.sub(r'([\"\\])',  r'\\\1', value)
+
                 res[name] = value
 
         return res
@@ -123,7 +142,7 @@
 #include "beremiz.h"
 """
         config = self.GetConfig()
-        c_code += self.modeldata.GenerateC(c_path, locstr, self.GetConfig())
+        c_code += self.modeldata.GenerateC(c_path, locstr, config)
 
         with open(c_path, 'w') as c_file:
             c_file.write(c_code)
--- a/mqtt/mqtt_client_gen.py	Thu Aug 08 14:56:13 2024 +0200
+++ b/mqtt/mqtt_client_gen.py	Thu Aug 22 12:15:31 2024 +0200
@@ -66,10 +66,12 @@
 
 directions = ["input", "output"]
 
+# expected configuration entries with internal default value
 authParams = {
     "x509":[
-        ("Certificate", "certificate.der"),
-        ("PrivateKey", "private_key.pem")],
+        ("Verify", True),
+        ("KeyStore", None),
+        ("TrustStore", None)],
     "UserPassword":[
         ("User", None),
         ("Password", None)]}
@@ -350,9 +352,15 @@
 #define USE_MQTT_5""".format(**config)
 
         AuthType = config["AuthType"]
+        print(config)
         if AuthType == "x509":
+            for k in ["KeyStore","TrustStore"]:
+                config[k] = '"'+config[k]+'"' if config[k] else "NULL"
             formatdict["init"] += """
-    INIT_x509("{PrivateKey}", "{Certificate}")""".format(**config)
+    INIT_x509({Verify:d}, {KeyStore}, {TrustStore})""".format(**config)
+        if AuthType == "PSK":
+            formatdict["init"] += """
+    INIT_PSK("{Secret}", "{ID}")""".format(**config)
         elif AuthType == "UserPassword":
             formatdict["init"] += """
     INIT_UserPassword("{User}", "{Password}")""".format(**config)
--- a/mqtt/mqtt_template.c	Thu Aug 08 14:56:13 2024 +0200
+++ b/mqtt/mqtt_template.c	Thu Aug 22 12:15:31 2024 +0200
@@ -56,6 +56,8 @@
 static MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
 #endif
 
+MQTTClient_SSLOptions ssl_opts = MQTTClient_SSLOptions_initializer;
+
 /* condition to quit publish thread */
 static int MQTT_stop_thread = 0;
 
@@ -176,9 +178,19 @@
 #define INIT_NoAuth()                                                                             \
     LogInfo("MQTT Init no auth\n");
 
-#define INIT_x509(PrivateKey, Certificate)                                                        \
-    LogInfo("MQTT Init x509 %s,%s\n", PrivateKey, Certificate);
-    /* TODO */
+#define INIT_x509(Verify, KeyStore, TrustStore)                                                   \
+    LogInfo("MQTT Init x509 with %s,%s\n", KeyStore, TrustStore)                                  \
+    ssl_opts.verify = Verify;                                                                     \
+    ssl_opts.keyStore = KeyStore;                                                                 \
+    ssl_opts.trustStore = TrustStore;                                                             \
+    conn_opts.ssl = &ssl_opts;
+
+#define INIT_PSK(Secret, ID)                                                                      \
+    LogError("MQTT PSK NOT IMPLEMENTED\n")                                                        \
+    /* LogInfo("MQTT Init PSK for ID %s\n", ID) */                                                \
+    /* ssl_opts.ssl_psk_cb = TODO; */                                                             \
+    /* ssl_opts.ssl_psk_context = TODO; */                                                        \
+    conn_opts.ssl = &ssl_opts;
 
 #define INIT_UserPassword(User, Password)                                                         \
     LogInfo("MQTT Init UserPassword %s,%s\n", User, Password);                                    \