mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
esp32s2/esp_ds: Added mqtt example for TLS using Digital Signature
This commit is contained in:
parent
24b88a7d9b
commit
b5c2fa632d
13
examples/protocols/mqtt/ssl_ds/CMakeLists.txt
Normal file
13
examples/protocols/mqtt/ssl_ds/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# The following four lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mqtt_ssl_ds)
|
||||
|
||||
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "main/client.crt" TEXT)
|
||||
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "main/mosquitto.org.crt" TEXT)
|
131
examples/protocols/mqtt/ssl_ds/README.md
Normal file
131
examples/protocols/mqtt/ssl_ds/README.md
Normal file
@ -0,0 +1,131 @@
|
||||
| Supported Targets | ESP32-S2 |
|
||||
# ESP-MQTT SSL Mutual Authentication with Digital Signature
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
Espressif's ESP32-S2 MCU has a built-in Digital Signature (DS) Peripheral, which provides hardware acceleration for RSA signature. More details can be found at [Digital Signature with ESP-TLS](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/protocols/esp_tls.html#digital-signature-with-esp-tls).
|
||||
|
||||
This example connects to the broker test.mosquitto.org using ssl transport with client certificate(RSA) and as a demonstration subscribes/unsubscribes and sends a message on certain topic.The RSA signature operation required in the ssl connection is performed with help of the Digital Signature (DS) peripheral.
|
||||
(Please note that the public broker is maintained by the community so may not be always available, for details please visit http://test.mosquitto.org)
|
||||
|
||||
It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker.
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example can be executed on any ESP32-S2 board (which has a built-in DS peripheral), the only required interface is WiFi and connection to internet.
|
||||
|
||||
### Configure the project
|
||||
|
||||
#### 1) Selecting the target
|
||||
As the project is to be built for the target ESP32-S2, it should be selected with the following command
|
||||
```
|
||||
idf.py set-target esp32s2
|
||||
```
|
||||
More detials can be found at [Selecting the target](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#selecting-the-target).
|
||||
|
||||
#### 2) Generate your client key and certificate
|
||||
|
||||
Navigate to the main directory
|
||||
|
||||
```
|
||||
cd main
|
||||
```
|
||||
|
||||
Generate a client key and a CSR. When you are generating the CSR, do not use the default values. At a minimum, the CSR must include the Country, Organisation and Common Name fields.
|
||||
|
||||
```
|
||||
openssl genrsa -out client.key
|
||||
openssl req -out client.csr -key client.key -new
|
||||
```
|
||||
|
||||
Paste the generated CSR in the [Mosquitto test certificate signer](https://test.mosquitto.org/ssl/index.php), click Submit and copy the downloaded `client.crt` in the `main` directory.
|
||||
|
||||
Please note, that the supplied file `client.crt` in the `main` directory is only a placeholder for your client certificate (i.e. the example "as is" would compile but would not connect to the broker)
|
||||
|
||||
#### 3) Configure the DS peripheral
|
||||
|
||||
* The DS peripheral can be configured with the python script [configure_ds.py](README.md#configure_ds-py) by executing the following command
|
||||
|
||||
```
|
||||
python configure_ds.py --port /* USB COM port */ --private_key /* RSA priv key */
|
||||
```
|
||||
|
||||
In the command USB COM port is nothing but the serial port to which the ESP32-S2 chip is connected. see
|
||||
[check serial port](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/establish-serial-connection.html#check-port-on-windows) for more details.
|
||||
RSA private key is nothing but the client private key ( RSA ) generated in Step 2.
|
||||
|
||||
#### 4) Connection cofiguration
|
||||
* Open the project configuration menu (`idf.py menuconfig`)
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
* When using Make build system, set `Default serial port` under `Serial flasher config`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (3714) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2
|
||||
I (3714) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
|
||||
I (3964) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000
|
||||
I (4164) MQTTS_EXAMPLE: MQTT_EVENT_CONNECTED
|
||||
I (4174) MQTTS_EXAMPLE: sent publish successful, msg_id=41464
|
||||
I (4174) MQTTS_EXAMPLE: sent subscribe successful, msg_id=17886
|
||||
I (4174) MQTTS_EXAMPLE: sent subscribe successful, msg_id=42970
|
||||
I (4184) MQTTS_EXAMPLE: sent unsubscribe successful, msg_id=50241
|
||||
I (4314) MQTTS_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=41464
|
||||
I (4484) MQTTS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=17886
|
||||
I (4484) MQTTS_EXAMPLE: sent publish successful, msg_id=0
|
||||
I (4684) MQTTS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=42970
|
||||
I (4684) MQTTS_EXAMPLE: sent publish successful, msg_id=0
|
||||
I (4884) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19
|
||||
I (4884) MQTTS_EXAMPLE: MQTT_EVENT_DATA
|
||||
TOPIC=/topic/qos0
|
||||
DATA=data
|
||||
I (5194) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19
|
||||
I (5194) MQTTS_EXAMPLE: MQTT_EVENT_DATA
|
||||
TOPIC=/topic/qos0
|
||||
DATA=data
|
||||
```
|
||||
|
||||
|
||||
### configure_ds.py
|
||||
The script [configure_ds.py](./configure_ds.py) is used for configuring the DS peripheral on the ESP32-S2 SoC. The steps in the script are based on technical details of certain operations in the Digital Signature calculation, which can be found at Digital Signature Section of [ESP32-S2 TRM](https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf)
|
||||
|
||||
The configuration script performs the following steps -
|
||||
|
||||
1. Take the client private key ( RSA key ) as input.
|
||||
(*required parameter for the script)
|
||||
can be provided with
|
||||
```
|
||||
python configure_ds.py --private-key /* path to client (rsa) prv key */
|
||||
```
|
||||
|
||||
2. Randomly Calculate the `HMAC_KEY` and the `initialization vector`(IV).Then calculate the encrypted private key parameters from client private key (step i) and newly generated parameters. These encrypted private key parameters are required for the DS peripheral to perform the Digital Signature operation.
|
||||
|
||||
3. Store `HMAC_KEY` in one of the efuse key blocks (in the hardware).
|
||||
The ID of the efuse key block ( should be in range 1-5) can be provided with the following option. ( default value of 1 is used if not provided),
|
||||
```
|
||||
python configure_ds.py --efuse_key_id /* key id in range 1-5 */ --burn_key
|
||||
```
|
||||
|
||||
Currently for development purposes, the `HMAC_KEY` is stored in the efuse key block without read protection so that read operation can be performed on the same key block.
|
||||
> You can burn (write) a key on an efuse key block only once.Please use a different block ID, if you want to use a different `HMAC_KEY` for the DS operation.
|
||||
|
||||
4. Create an NVS partition of the name `pre_prov.csv` (in `esp_ds_data` folder) which contains the required encrypted private key parameters. A bin file of the nvs partition (`pre_prov.bin`) is also created and is flashed on the device. As we have added a custom partition, the example is set to use the custom partition table by adding the required option in `sdkconfig.defaults`.
|
||||
|
||||
5. (optional) The script can be made to print the summary of the efuse on the chip by providing the following option.When this option is enabled, no other operations in the script are performed.
|
||||
```
|
||||
python configure_ds.py --summary
|
||||
```
|
||||
|
||||
> A list of all the supported options in the script can be obtained by executing `python configure_ds.py --help`.
|
314
examples/protocols/mqtt/ssl_ds/configure_ds.py
Normal file
314
examples/protocols/mqtt/ssl_ds/configure_ds.py
Normal file
@ -0,0 +1,314 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2020 Espressif Systems (Shanghai) Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import hashlib
|
||||
import hmac
|
||||
import struct
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.utils import int_to_bytes
|
||||
|
||||
try:
|
||||
import esptool
|
||||
import espefuse
|
||||
except ImportError:
|
||||
idf_path = os.getenv("IDF_PATH")
|
||||
if not idf_path or not os.path.exists(idf_path):
|
||||
raise Exception("IDF_PATH not found")
|
||||
sys.path.insert(0, os.path.join(idf_path, "components", "esptool_py", "esptool"))
|
||||
import esptool
|
||||
import espefuse
|
||||
try:
|
||||
import nvs_partition_gen as nvs_gen
|
||||
except ImportError:
|
||||
idf_path = os.getenv("IDF_PATH")
|
||||
if not idf_path or not os.path.exists(idf_path):
|
||||
raise Exception("IDF_PATH not found")
|
||||
sys.path.insert(0, os.path.join(idf_path, "components", "nvs_flash", "nvs_partition_generator"))
|
||||
import nvs_partition_gen as nvs_gen
|
||||
|
||||
|
||||
esp_ds_data_dir = 'esp_ds_data'
|
||||
# hmac_key_file is generated when HMAC_KEY is calculated, it is used when burning HMAC_KEY to efuse
|
||||
hmac_key_file = esp_ds_data_dir + '/hmac_key.bin'
|
||||
# csv and bin filenames are default filenames for nvs partition files created with this script
|
||||
csv_filename = esp_ds_data_dir + '/pre_prov.csv'
|
||||
bin_filename = esp_ds_data_dir + '/pre_prov.bin'
|
||||
|
||||
|
||||
def load_privatekey(key_file_path, password=None):
|
||||
key_file = open(key_file_path, 'rb')
|
||||
key = key_file.read()
|
||||
key_file.close()
|
||||
return serialization.load_pem_private_key(key, password=password, backend=default_backend())
|
||||
|
||||
|
||||
def number_as_bytes(number, pad_bits=None):
|
||||
"""
|
||||
Given a number, format as a little endian array of bytes
|
||||
"""
|
||||
result = int_to_bytes(number)[::-1]
|
||||
while pad_bits is not None and len(result) < (pad_bits // 8):
|
||||
result += b'\x00'
|
||||
return result
|
||||
|
||||
|
||||
def calculate_ds_parameters(privkey, priv_key_pass):
|
||||
private_key = load_privatekey(privkey, priv_key_pass)
|
||||
if not isinstance(private_key, rsa.RSAPrivateKey):
|
||||
print("Only RSA private keys are supported")
|
||||
sys.exit(-1)
|
||||
|
||||
priv_numbers = private_key.private_numbers()
|
||||
pub_numbers = private_key.public_key().public_numbers()
|
||||
Y = priv_numbers.d
|
||||
M = pub_numbers.n
|
||||
key_size = private_key.key_size
|
||||
supported_key_size = [1024, 2048, 3072, 4096]
|
||||
if key_size not in supported_key_size:
|
||||
print("Key size not supported, supported sizes are" + str(supported_key_size))
|
||||
sys.exit(-1)
|
||||
|
||||
hmac_key = os.urandom(32)
|
||||
with open(hmac_key_file, 'wb') as key_file:
|
||||
key_file.write(hmac_key)
|
||||
|
||||
iv = os.urandom(16)
|
||||
|
||||
rr = 1 << (key_size * 2)
|
||||
rinv = rr % pub_numbers.n
|
||||
mprime = - rsa._modinv(M, 1 << 32)
|
||||
mprime &= 0xFFFFFFFF
|
||||
length = key_size // 32 - 1
|
||||
|
||||
aes_key = hmac.HMAC(hmac_key, b"\xFF" * 32, hashlib.sha256).digest()
|
||||
|
||||
md_in = number_as_bytes(Y, 4096) + \
|
||||
number_as_bytes(M, 4096) + \
|
||||
number_as_bytes(rinv, 4096) + \
|
||||
struct.pack("<II", mprime, length) + \
|
||||
iv
|
||||
assert len(md_in) == 12480 / 8
|
||||
md = hashlib.sha256(md_in).digest()
|
||||
|
||||
# Y4096 || M4096 || Rb4096 || M_prime32 || LENGTH32 || MD256 || 0x08*8
|
||||
p = number_as_bytes(Y, 4096) + \
|
||||
number_as_bytes(M, 4096) + \
|
||||
number_as_bytes(rinv, 4096) + \
|
||||
md + \
|
||||
struct.pack("<II", mprime, length) + \
|
||||
b'\x08' * 8
|
||||
|
||||
assert len(p) == 12672 / 8
|
||||
|
||||
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend())
|
||||
encryptor = cipher.encryptor()
|
||||
c = encryptor.update(p) + encryptor.finalize()
|
||||
return c, iv, key_size
|
||||
|
||||
|
||||
class DefineArgs(object):
|
||||
def __init__(self, attributes):
|
||||
for key, value in attributes.items():
|
||||
self.__setattr__(key, value)
|
||||
|
||||
|
||||
def efuse_summary(esp,args):
|
||||
efuses, _efuse_operations = espefuse.get_efuses(esp, esp.CHIP_NAME, False, False, False)
|
||||
|
||||
summary_args = DefineArgs({
|
||||
'baud': 115200,
|
||||
'before': 'default_reset',
|
||||
'chip': esp.CHIP_NAME,
|
||||
'debug': False,
|
||||
'do_not_confirm': False,
|
||||
'file': sys.stdout,
|
||||
'mode':'w',
|
||||
'encding': 'utf-8',
|
||||
'format': 'summary',
|
||||
'operation': 'summary',
|
||||
'port':args.port,
|
||||
})
|
||||
|
||||
print("\n\n\n\t---SUMMARY START---\n")
|
||||
espefuse.summary(esp, efuses, summary_args)
|
||||
print("\n\t---SUMMARY END---\n\n")
|
||||
|
||||
|
||||
def efuse_burn_key(esp, args):
|
||||
|
||||
efuses, efuse_operations = espefuse.get_efuses(esp, esp.CHIP_NAME, False, False, False)
|
||||
|
||||
if args.efuse_key_id is None:
|
||||
print("efuse Key id cannot be None")
|
||||
sys.exit(-1)
|
||||
|
||||
key_file = open(hmac_key_file, 'rb')
|
||||
# First element of _KEYBLOCKS is config data so add offset of 1
|
||||
key_block = efuses._KEYBLOCKS[args.efuse_key_id + 1][0]
|
||||
burn_key_args = DefineArgs({
|
||||
'baud': 115200,
|
||||
'before': 'default_reset',
|
||||
'chip': esp.CHIP_NAME,
|
||||
'debug': False,
|
||||
'do_not_confirm': False,
|
||||
'block': [key_block],
|
||||
'keyfile': [key_file],
|
||||
'keypurpose': ['HMAC_DOWN_DIGITAL_SIGNATURE'],
|
||||
'operation': 'burn_key',
|
||||
'force_write_always': False,
|
||||
'no_read_protect': True,
|
||||
'no_write_protect': False,
|
||||
'port': args.port,
|
||||
|
||||
})
|
||||
|
||||
try:
|
||||
efuse_operations.burn_key(esp, efuses, burn_key_args, None)
|
||||
key_file.close()
|
||||
except esptool.FatalError:
|
||||
print("\nERROR: The provided key block already contains previously burned key, please use a different key block ID")
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
def generate_csv_file(c, iv, hmac_key_id, key_size, csv_file):
|
||||
|
||||
with open(csv_file, 'wt', encoding='utf8') as f:
|
||||
f.write("# This is a generated csv file containing required parameters for the Digital Signature operaiton\n")
|
||||
f.write("key,type,encoding,value\nesp_ds_ns,namespace,,\n")
|
||||
f.write("esp_ds_c,data,hex2bin,%s\n" % (c.hex()))
|
||||
f.write("esp_ds_iv,data,hex2bin,%s\n" % (iv.hex()))
|
||||
f.write("esp_ds_key_id,data,u8,%d\n" % (hmac_key_id))
|
||||
f.write("esp_ds_rsa_len,data,u16,%d\n" % (key_size))
|
||||
|
||||
|
||||
def generate_nvs_partition(input_filename, output_filename):
|
||||
|
||||
nvs_args = DefineArgs({
|
||||
'input': input_filename,
|
||||
'outdir': os.getcwd(),
|
||||
'output': output_filename,
|
||||
'size': hex(0x3000),
|
||||
'version': 2,
|
||||
'keyfile':None,
|
||||
})
|
||||
|
||||
nvs_gen.generate(nvs_args, is_encr_enabled=False, encr_key=None)
|
||||
|
||||
|
||||
def flash_nvs_partition(bin_path, addr, esp):
|
||||
esp.connect()
|
||||
print(bin_path)
|
||||
abs_bin_path = os.path.dirname(os.path.abspath(__file__)) + '/' + bin_path
|
||||
print(abs_bin_path)
|
||||
if (os.path.exists(abs_bin_path) is False):
|
||||
print("NVS partition not found")
|
||||
sys.exit(-1)
|
||||
|
||||
with open(bin_path, 'rb') as nvs_file:
|
||||
|
||||
flash_file = [(addr, nvs_file)]
|
||||
|
||||
flash_args = DefineArgs({
|
||||
'flash_size': '4MB',
|
||||
'flash_mode': 'qio',
|
||||
'flash_freq': '80m',
|
||||
'addr_filename': flash_file,
|
||||
'no_stub': False,
|
||||
'compress': False,
|
||||
'verify': False,
|
||||
'encrypt': False,
|
||||
'erase_all': False,
|
||||
})
|
||||
|
||||
esp.change_baud(baud=921600)
|
||||
esptool.write_flash(esp, flash_args)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='''Provision the ESPWROOM32SE device with
|
||||
device_certificate and signer_certificate required for TLS authentication''')
|
||||
|
||||
parser.add_argument(
|
||||
'--private-key',
|
||||
dest='privkey',
|
||||
default='main/client.key',
|
||||
metavar='relative/path/to/client-priv-key',
|
||||
help='relative path(from secure_cert_mfg.py) to signer certificate private key')
|
||||
|
||||
parser.add_argument(
|
||||
"--pwd", '--password',
|
||||
dest='priv_key_pass',
|
||||
metavar='[password]',
|
||||
help='the password associated with the private key')
|
||||
|
||||
parser.add_argument(
|
||||
'--summary',
|
||||
dest='summary',action='store_true',
|
||||
help='Provide this option to print efuse summary the chip')
|
||||
|
||||
parser.add_argument(
|
||||
'--efuse_key_id',
|
||||
dest='efuse_key_id', type=int, choices=range(1,6),
|
||||
metavar='[key_id] ',
|
||||
default=1,
|
||||
help='Provide the efuse key_id which contains/will contain HMAC_KEY, default is 1')
|
||||
|
||||
parser.add_argument(
|
||||
"--port", '-p',
|
||||
dest='port',
|
||||
metavar='[port]',
|
||||
required=True,
|
||||
help='UART com port to which ESP device is connected')
|
||||
|
||||
parser.add_argument(
|
||||
'--overwrite',
|
||||
dest='overwrite', action='store_true',
|
||||
help='Overwrite previously generated keys')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
esp = esptool.ESPLoader.detect_chip(args.port,baud=115200)
|
||||
if (esp.CHIP_NAME != 'ESP32-S2'):
|
||||
print("Only ESP32S2 chip is supported")
|
||||
sys.exit(-1)
|
||||
|
||||
if args.summary is not False:
|
||||
efuse_summary(esp, args)
|
||||
sys.exit(0)
|
||||
|
||||
if (os.path.exists(esp_ds_data_dir) is False):
|
||||
os.makedirs(esp_ds_data_dir)
|
||||
else:
|
||||
if (args.overwrite is False):
|
||||
print("WARNING: previous ecrypted private key data exists.\nIf you want to overwrite,"
|
||||
" please execute your command with providing \"--overwrite\" option")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("overwriting previous encrypted private key data, as you have provided \"--overwrite\" option")
|
||||
|
||||
c, iv, key_size = calculate_ds_parameters(args.privkey, args.priv_key_pass)
|
||||
efuse_burn_key(esp, args)
|
||||
|
||||
generate_csv_file(c, iv, args.efuse_key_id, key_size, csv_filename)
|
||||
generate_nvs_partition(csv_filename, bin_filename)
|
||||
flash_nvs_partition(bin_filename, 0x10000, esp)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
3
examples/protocols/mqtt/ssl_ds/main/CMakeLists.txt
Normal file
3
examples/protocols/mqtt/ssl_ds/main/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "app_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
212
examples/protocols/mqtt/ssl_ds/main/app_main.c
Normal file
212
examples/protocols/mqtt/ssl_ds/main/app_main.c
Normal file
@ -0,0 +1,212 @@
|
||||
/* MQTT Mutual Authentication Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/netdb.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "mqtt_client.h"
|
||||
|
||||
/* pre_prov - name of partition containing encrypted prv key parameters ( It is set as such to synchronize it with the pre provisioning service */
|
||||
#define NVS_PARTITION_NAME "pre_prov"
|
||||
/* esp_ds_ns - namespace used for defining values in esp_ds_nvs */
|
||||
#define NVS_NAMESPACE "esp_ds_ns"
|
||||
/* esp_ds_key_id - efuse key block id where 256 bit key is stored, which will be read by
|
||||
* DS module to perform DS operation */
|
||||
#define NVS_EFUSE_KEY_ID "esp_ds_key_id"
|
||||
/* esp_ds_rsa_len - length of RSA private key (in bits) which is encrypted */
|
||||
#define NVS_RSA_LEN "esp_ds_rsa_len"
|
||||
/* following entries denote key(ASCII string) for particular value in key-value pair of esp_ds_nvs (which are defined in esp_ds_ns) */
|
||||
/* ciphertext_c - encrypted RSA private key, see ESP32-S2 Techincal Reference Manual for more details */
|
||||
#define NVS_CIPHER_C "esp_ds_c"
|
||||
/* initialization vector (iv) - 256 bit value used to encrypt RSA private key (to generate ciphertext_c) */
|
||||
#define NVS_IV "esp_ds_iv"
|
||||
static const char *TAG = "MQTTS_EXAMPLE";
|
||||
|
||||
extern const uint8_t client_cert_pem_start[] asm("_binary_client_crt_start");
|
||||
extern const uint8_t client_cert_pem_end[] asm("_binary_client_crt_end");
|
||||
extern const uint8_t server_cert_pem_start[] asm("_binary_mosquitto_org_crt_start");
|
||||
extern const uint8_t server_cert_pem_end[] asm("_binary_mosquitto_org_crt_end");
|
||||
|
||||
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
|
||||
{
|
||||
esp_mqtt_client_handle_t client = event->client;
|
||||
int msg_id;
|
||||
// your_context_t *context = event->context;
|
||||
switch (event->event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
||||
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
|
||||
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
|
||||
|
||||
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
|
||||
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
|
||||
|
||||
msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
|
||||
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
|
||||
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
||||
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||
printf("DATA=%.*s\r\n", event->data_len, event->data);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void *esp_read_ds_data_from_nvs(void)
|
||||
{
|
||||
esp_ds_data_ctx_t *ds_data_ctx;
|
||||
ds_data_ctx = (esp_ds_data_ctx_t *)malloc(sizeof(esp_ds_data_ctx_t));
|
||||
if (ds_data_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Error in allocating memory for esp_ds_data_context");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ds_data_ctx->esp_ds_data = (esp_ds_data_t *)calloc(1, sizeof(esp_ds_data_t));
|
||||
if (ds_data_ctx->esp_ds_data == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate memory for DS data handle ");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
nvs_handle_t esp_ds_nvs_handle;
|
||||
esp_err_t esp_ret;
|
||||
esp_ret = nvs_flash_init_partition(NVS_PARTITION_NAME);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in esp_ds_nvs partition init, returned %02x", esp_ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
esp_ret = nvs_open_from_partition(NVS_PARTITION_NAME, NVS_NAMESPACE,
|
||||
NVS_READONLY, &esp_ds_nvs_handle);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in esp_ds_nvs partition open, returned %02x", esp_ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
esp_ret = nvs_get_u8(esp_ds_nvs_handle, NVS_EFUSE_KEY_ID, &ds_data_ctx->efuse_key_id);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in efuse_key_id value from nvs, returned %02x", esp_ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
esp_ret = nvs_get_u16(esp_ds_nvs_handle, NVS_RSA_LEN, &ds_data_ctx->rsa_length_bits);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading rsa key length value from nvs, returned %02x", esp_ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
size_t blob_length = ESP_DS_C_LEN;
|
||||
esp_ret = nvs_get_blob(esp_ds_nvs_handle, NVS_CIPHER_C, (void *)(ds_data_ctx->esp_ds_data->c), &blob_length);
|
||||
if ((esp_ret != ESP_OK) || (blob_length != ESP_DS_C_LEN)) {
|
||||
ESP_LOGE(TAG, "Error in reading initialization vector value from nvs,bytes_read = %d, returned %02x", blob_length, esp_ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blob_length = ESP_DS_IV_LEN;
|
||||
esp_ret = nvs_get_blob(esp_ds_nvs_handle, NVS_IV, (void *)(ds_data_ctx->esp_ds_data->iv), &blob_length);
|
||||
if ((esp_ret != ESP_OK) || (blob_length != ESP_DS_IV_LEN)) {
|
||||
ESP_LOGE(TAG, "Error in reading initialization vector value from nvs,bytes_read = %d, returned %02x", blob_length, esp_ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return (void *)ds_data_ctx;
|
||||
exit:
|
||||
if (ds_data_ctx != NULL) {
|
||||
free(ds_data_ctx->esp_ds_data);
|
||||
}
|
||||
free(ds_data_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void mqtt_app_start(void)
|
||||
{
|
||||
|
||||
/* The context is used by the DS peripheral, should not be freed */
|
||||
void *ds_data = esp_read_ds_data_from_nvs();
|
||||
if (ds_data == NULL) {
|
||||
ESP_LOGE(TAG, "Error in reading DS data from NVS");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
const esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.uri = "mqtts://test.mosquitto.org:8884",
|
||||
.event_handle = mqtt_event_handler,
|
||||
.cert_pem = (const char *)server_cert_pem_start,
|
||||
.client_cert_pem = (const char *)client_cert_pem_start,
|
||||
.client_key_pem = NULL,
|
||||
.ds_data = ds_data,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
|
||||
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
esp_mqtt_client_start(client);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "[APP] Startup..");
|
||||
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
|
||||
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
|
||||
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);
|
||||
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
mqtt_app_start();
|
||||
}
|
1
examples/protocols/mqtt/ssl_ds/main/client.crt
Normal file
1
examples/protocols/mqtt/ssl_ds/main/client.crt
Normal file
@ -0,0 +1 @@
|
||||
Please paste your client certificate here (follow instructions in README.md)
|
25
examples/protocols/mqtt/ssl_ds/main/mosquitto.org.crt
Normal file
25
examples/protocols/mqtt/ssl_ds/main/mosquitto.org.crt
Normal file
@ -0,0 +1,25 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
|
||||
BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG
|
||||
A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU
|
||||
BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv
|
||||
by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE
|
||||
BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES
|
||||
MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp
|
||||
dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg
|
||||
UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW
|
||||
Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA
|
||||
s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH
|
||||
3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo
|
||||
E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT
|
||||
MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV
|
||||
6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||
BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC
|
||||
6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf
|
||||
+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK
|
||||
sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839
|
||||
LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE
|
||||
m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
|
||||
-----END CERTIFICATE-----
|
||||
---
|
7
examples/protocols/mqtt/ssl_ds/partitions.csv
Normal file
7
examples/protocols/mqtt/ssl_ds/partitions.csv
Normal file
@ -0,0 +1,7 @@
|
||||
# ESP-IDF Partition Table
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs,data,nvs,0x9000,24K,
|
||||
phy_init,data,phy,0xf000,4K,
|
||||
pre_prov,data,nvs,0x10000,0x3000,
|
||||
factory,app,factory,0x20000,1M,
|
||||
|
|
2
examples/protocols/mqtt/ssl_ds/sdkconfig.defaults
Normal file
2
examples/protocols/mqtt/ssl_ds/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
|
Loading…
Reference in New Issue
Block a user