Codementor Events

MQTT sobre TLS-PSK con ESP32 y Mongoose-OS

Published Apr 11, 2022Last updated Apr 21, 2022

Este artículo es una extensión de este otro; te recomiendo que lo veas primero.

Para mayor información sobre el cliente MQTT y la operación sin TLS, existe este artículo. Para más información sobre TLS, éste.

TLS-PSK

Existe una solución un poco más simple a la que vimos en el primer artículo; consiste en utilizar, en vez de certificados, una clave pre-compartida entre el broker y el dispositivo que se conecta.

El proceso de conexión es similar al empleado con certificados, sólo que en vez de validarse éstos y luego generarse una clave derivada, se utiliza como punto de partida la clave pre-compartida. Según el esquema utilizado, se evitan las costosas operaciones de clave pública/privada (Public Key Cryptography), y la logística de manejo de claves suele ser más simple, además del hecho de no requerir una CA (Certification Authority).

Si bien existen al momento tres formas de operar, una de las cuales permite validar al broker mediante certificados y al dispositivo mediante clave pre-compartida (PSK). Sólo hemos probado la forma simple.

Configuración

Configuramos el ESP32 con Mongoose-OS para operar mediante TLS-PSK.

libs:
  - origin: https://github.com/mongoose-os-libs/mqtt # incorpora el cliente MQTT
config_schema:
  - ["mqtt.enable", true] # Habilita el cliente MQTT
  - ["mqtt.server", "address:port"] # Dirección IP del broker a utilizar
  - ["mqtt.ssl_psk_identity", "bob"] # identidad para la clave elegida
  - ["mqtt.ssl_psk_key", "000000000000000000000000deadbeef"] # clave AES-128 (ó 256)
  - ["mqtt.ssl_cipher_suites", "TLS-PSK-WITH-AES-128-CCM-8:TLS-PSK-WITH-AES-128-GCM-SHA256:TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256:TLS-PSK-WITH-AES-128-CBC-SHA"] # claves utilizables

El puerto comúnmente utilizado es 8883. El broker puede además solicitar que utilicemos nombre de usuario y password, aunque lo más común es configurarlo para aprovechar la identidad que se envía, como hicimos para estas pruebas (ejemplo más abajo).

Respecto a las cipher suites, se trata de indicar un conjunto de esquemas criptográficos que el sistema soporta y entre ellos debemos acertar a elegir uno que esté soportado por el broker. Este parámetro es obligatorio, sin él el dispositivo no anuncia ningún esquema criptográfico compatible con TLS-PSK y la conexión TLS no se establece. Lo habitual es acordarlo con el administrador del broker.

Al iniciar la conexión TLS, el dispositivo incluye los esquemas soportados en el mensaje ClientHello; el broker elige uno compatible y lo indica en el mensaje ServerHello. Dado que esta lista la obtuvimos analizando el código fuente, decidimos publicarla aquí para mayor utilidad. En nuestro caso en particular sólo uno de los esquemas coincidió con los disponibles en el broker: TLS-PSK-WITH-AES-128-CCM-8.

Por último, la clave pre-compartida (PSK, parámetro ssl_psk_key, debe tener una longitud de 128-bits (16-bits) para sets basados en AES-128 y de 256-bits (32-bytes) para sets basados en AES-256.

Operación

Al iniciar el procesador, observaremos en el log si todo funciona como debe, o los errores que se hayan producido. En este último caso, resulta bastante difícil determinar las causas sin un sniffer dado que las pistas de ambos lados no suelen ser muy precisas. Tanto el handshake como la operación MQTT dentro de TLS pueden observarse en un sniffer, ingresando dicha clave en el mismo (por ejemplo Wireshark, ejemplo más abajo).

[Mar 31 17:28:32.349] mgos_mqtt_conn.c:435 MQTT0 connecting to 192.168.5.3:8883
[Mar 31 17:28:32.368] mongoose.c:4906 0x3ffc76ac ciphersuite: TLS-PSK-WITH-AES-128-CBC-SHA
[Mar 31 17:28:32.398] mgos_mqtt_conn.c:188 MQTT0 TCP connect ok (0)
[Mar 31 17:28:32.411] mgos_mqtt_conn.c:235 MQTT0 CONNACK 0
[Mar 31 17:28:32.420] init.js:34 MQTT connected
[Mar 31 17:28:32.434] init.js:26 Published:OK topic:/this/test/esp32_807A98 msg:CONNECTED!

Detallamos, a modo instructivo y como ayuda rápida, las configuraciones mínimas necesarias para operar en Mosquitto. Los paths están en formato GNU/Linux y se espera que pongamos nuestros certificados allí.

listener 8883 192.168.5.1
log_dest syslog
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
**use\_identity\_as\_username true** # evitamos que requiera usuario en el header MQTT
**psk\_file /etc/mosquitto/pskfile**
 **psk\_hint** _cualquiera_ # cualquier nombre

El parámetro psk_hint, en el caso que el cliente se conecte a varios lugares, le da un indicio (hint) de qué identidad utilizar para identificarse. No tiene utilidad en nuestro caso.

El archivo pskfile, por su parte, debe contener las identidades y claves pre-compartidas; por ejemplo, para el usuario bob:

bob:000000000000000000000000deadbeef

Ejemplo

El código de ejemplo se encuentra en Github.

Sniffers: Wireshark

En Wireshark, poder decodificar el tráfico TLS-PSK es tan simple como ingresar la clave compartida en Edit->Preferences->Protocols->TLS->Pre-Shared Key. El sniffer es entonces capaz de descifrar el contenido y nos permite observar el tráfico MQTT.

Discover and read more posts from Sergio R. Caprile
get started
post commentsBe the first to share your opinion
Show more replies