1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 /**
12  * @file nx_azure_iot.h
13  *
14  */
15 
16 #ifndef NX_AZURE_IOT_H
17 #define NX_AZURE_IOT_H
18 
19 #ifdef __cplusplus
20 extern   "C" {
21 #endif
22 
23 #include "azure/core/az_log.h"
24 #include "azure/iot/az_iot_common.h"
25 #include "nx_api.h"
26 #include "nx_cloud.h"
27 #include "nxd_dns.h"
28 #include "nxd_mqtt_client.h"
29 
30 #ifndef NXD_MQTT_CLOUD_ENABLE
31 #error "NXD_MQTT_CLOUD_ENABLE must be defined"
32 #endif /* NXD_MQTT_CLOUD_ENABLE */
33 
34 #ifndef NX_SECURE_ENABLE
35 #error "NX_SECURE_ENABLE must be defined"
36 #endif /* NX_SECURE_ENABLE */
37 
38 /* Defined, certificate date is not validated. By default, it is enabled. */
39 /*
40 #define NX_AZURE_IOT_DISABLE_CERTIFICATE_DATE
41 */
42 
43 /* Define the LOG LEVEL.  */
44 #ifndef NX_AZURE_IOT_LOG_LEVEL
45 #define NX_AZURE_IOT_LOG_LEVEL    2
46 #endif /* NX_AZURE_IOT_LOG_LEVEL */
47 
48 /* Define maximum trusted certificates count.  */
49 #ifndef NX_AZURE_IOT_MAX_NUM_OF_TRUSTED_CERTS
50 #define NX_AZURE_IOT_MAX_NUM_OF_TRUSTED_CERTS 3
51 #endif /* NX_AZURE_IOT_MAX_NUM_OF_TRUSTED_CERTS */
52 
53 /* Define maximum device certificates count.  */
54 #ifndef NX_AZURE_IOT_MAX_NUM_OF_DEVICE_CERTS
55 #define NX_AZURE_IOT_MAX_NUM_OF_DEVICE_CERTS 2
56 #endif /* NX_AZURE_IOT_MAX_NUM_OF_DEVICE_CERTS */
57 
58 #define NX_AZURE_IOT_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
59 
60 /* Define the az iot log function. */
61 #define LogError(...)
62 #define LogInfo(...)
63 #define LogDebug(...)
64 #define LogLiteralArgs(s) (UCHAR *)s, sizeof(s) - 1
65 
66 /**
67 * Supports logging of AZ_LOG_IOT_AZURERTOS classification message. In the logged
68 * message, at-most one format specifier can be specifed at the end of message.
69 * Currently only two format specifier are supported:
70 *   - %d for integer
71 *   - %s for string, where first argument is length and second is pointer to string
72 */
73 UINT nx_azure_iot_log(UCHAR *type_ptr, UINT type_len, UCHAR *msg_ptr, UINT msg_len, ...);
74 
75 #if NX_AZURE_IOT_LOG_LEVEL > 0
76 #undef LogError
77 #define LogError(...) nx_azure_iot_log(LogLiteralArgs("[ERROR] "), __VA_ARGS__)
78 #endif /* NX_AZURE_IOT_LOG_LEVEL > 0 */
79 #if NX_AZURE_IOT_LOG_LEVEL > 1
80 #undef LogInfo
81 #define LogInfo(...) nx_azure_iot_log(LogLiteralArgs("[INFO] "), __VA_ARGS__)
82 #endif /* NX_AZURE_IOT_LOG_LEVEL > 1 */
83 #if NX_AZURE_IOT_LOG_LEVEL > 2
84 #undef LogDebug
85 #define LogDebug(...) nx_azure_iot_log(LogLiteralArgs("[DEBUG] "), __VA_ARGS__)
86 #endif /* NX_AZURE_IOT_LOG_LEVEL > 2 */
87 
88 #define NX_AZURE_IOT_MQTT_QOS_0                           0
89 #define NX_AZURE_IOT_MQTT_QOS_1                           1
90 
91 /* Define AZ IoT SDK event flags. These events are processed by the Cloud thread.  */
92 
93 /* Provisioning Client Connect event */
94 #define NX_AZURE_IOT_PROVISIONING_CLIENT_CONNECT_EVENT    ((ULONG)0x00000001)
95 
96 /* Provisioning Client Subscribe event */
97 #define NX_AZURE_IOT_PROVISIONING_CLIENT_SUBSCRIBE_EVENT  ((ULONG)0x00000002)
98 
99 /* Provisioning Client Request event */
100 #define NX_AZURE_IOT_PROVISIONING_CLIENT_REQUEST_EVENT    ((ULONG)0x00000004)
101 
102 /* Provisioning Client Response event */
103 #define NX_AZURE_IOT_PROVISIONING_CLIENT_RESPONSE_EVENT   ((ULONG)0x00000008)
104 
105 /* Provisioning Client Disconnect event */
106 #define NX_AZURE_IOT_PROVISIONING_CLIENT_DISCONNECT_EVENT ((ULONG)0x00000010)
107 
108 /* API return values.  */
109 /**< The operation was successful. */
110 #define NX_AZURE_IOT_SUCCESS                              0x0
111 
112 /**< The operation was unsuccessful. */
113 #define NX_AZURE_IOT_FAILURE                              0x20000
114 #define NX_AZURE_IOT_SDK_CORE_ERROR                       0x20001
115 #define NX_AZURE_IOT_INVALID_PARAMETER                    0x20002
116 #define NX_AZURE_IOT_INSUFFICIENT_BUFFER_SPACE            0x20003
117 #define NX_AZURE_IOT_INVALID_PACKET                       0x20004
118 #define NX_AZURE_IOT_NO_PACKET                            0x20005
119 
120 /**< If the item requested was not found */
121 #define NX_AZURE_IOT_NOT_FOUND                            0x20006
122 #define NX_AZURE_IOT_NOT_ENABLED                          0x20007
123 #define NX_AZURE_IOT_NOT_INITIALIZED                      0x20008
124 #define NX_AZURE_IOT_NOT_SUPPORTED                        0x20009
125 #define NX_AZURE_IOT_ALREADY_CONNECTED                    0x2000A
126 #define NX_AZURE_IOT_CONNECTING                           0x2000B
127 #define NX_AZURE_IOT_DISCONNECTED                         0x2000C
128 
129 /**< The operation is pending. */
130 #define NX_AZURE_IOT_PENDING                              0x2000D
131 #define NX_AZURE_IOT_SERVER_RESPONSE_ERROR                0x2000E
132 #define NX_AZURE_IOT_TOPIC_TOO_LONG                       0x2000F
133 #define NX_AZURE_IOT_MESSAGE_TOO_LONG                     0x20010
134 #define NX_AZURE_IOT_NO_AVAILABLE_CIPHER                  0x20011
135 #define NX_AZURE_IOT_WRONG_STATE                          0x20012
136 #define NX_AZURE_IOT_DELETE_ERROR                         0x20013
137 #define NX_AZURE_IOT_NO_SUBSCRIBE_ACK                     0x20014
138 #define NX_AZURE_IOT_THROTTLED                            0x20015
139 
140 #define NX_AZURE_IOT_EMPTY_JSON                           0x20016
141 #define NX_AZURE_IOT_SAS_TOKEN_EXPIRED                    0x20017
142 #define NX_AZURE_IOT_NO_MORE_ENTRIES                      0x20018
143 
144 /* Resource type managed by AZ_IOT.  */
145 #define NX_AZURE_IOT_RESOURCE_IOT_HUB                     0x1
146 #define NX_AZURE_IOT_RESOURCE_IOT_PROVISIONING            0x2
147 
148 /* Define the packet buffer for THREADX TLS.  */
149 #ifndef NX_AZURE_IOT_TLS_PACKET_BUFFER_SIZE
150 #define NX_AZURE_IOT_TLS_PACKET_BUFFER_SIZE               (1024 * 7)
151 #endif /* NX_AZURE_IOT_TLS_PACKET_BUFFER_SIZE  */
152 
153 /* Define MQTT keep alive in seconds. 0 means the keep alive is disabled.
154    By default, keep alive is 4 minutes. */
155 #ifndef NX_AZURE_IOT_MQTT_KEEP_ALIVE
156 #define NX_AZURE_IOT_MQTT_KEEP_ALIVE                      (60 * 4)
157 #endif /* NX_AZURE_IOT_MQTT_KEEP_ALIVE */
158 
159 /* MQTT Subscribe topic offset.  */
160 #define NX_AZURE_IOT_MQTT_SUBSCRIBE_TOPIC_OFFSET          6
161 
162 /* MQTT Publish offset.  */
163 #define NX_AZURE_IOT_PUBLISH_PACKET_START_OFFSET          7
164 
165 /**
166  * @brief Resource struct
167  *
168  */
169 typedef struct NX_AZURE_IOT_RESOURCE_STRUCT
170 {
171     UINT                                   resource_type;
172     VOID                                  *resource_data_ptr;
173     NXD_MQTT_CLIENT                        resource_mqtt;
174     UCHAR                                 *resource_mqtt_client_id;
175     UINT                                   resource_mqtt_client_id_length;
176     UCHAR                                 *resource_mqtt_user_name;
177     UINT                                   resource_mqtt_user_name_length;
178     UCHAR                                 *resource_mqtt_sas_token;
179     UINT                                   resource_mqtt_sas_token_length;
180     VOID                                  *resource_mqtt_buffer_context;
181     UINT                                   resource_mqtt_buffer_size;
182     UCHAR                                  resource_tls_packet_buffer[NX_AZURE_IOT_TLS_PACKET_BUFFER_SIZE];
183     const NX_CRYPTO_METHOD               **resource_crypto_array;
184     UINT                                   resource_crypto_array_size;
185     const NX_CRYPTO_CIPHERSUITE          **resource_cipher_map;
186     UINT                                   resource_cipher_map_size;
187     UCHAR                                 *resource_metadata_ptr;
188     UINT                                   resource_metadata_size;
189     NX_SECURE_X509_CERT                   *resource_trusted_certificates[NX_AZURE_IOT_MAX_NUM_OF_TRUSTED_CERTS];
190     NX_SECURE_X509_CERT                   *resource_device_certificates[NX_AZURE_IOT_MAX_NUM_OF_DEVICE_CERTS];
191     const UCHAR                           *resource_hostname;
192     UINT                                   resource_hostname_length;
193     struct NX_AZURE_IOT_RESOURCE_STRUCT   *resource_next;
194 
195 } NX_AZURE_IOT_RESOURCE;
196 
197 /**
198  * @brief Azure IoT Struct
199  *
200  */
201 typedef struct NX_AZURE_IOT_STRUCT
202 {
203     const UCHAR                           *nx_azure_iot_name;
204     NX_IP                                 *nx_azure_iot_ip_ptr;
205     NX_PACKET_POOL                        *nx_azure_iot_pool_ptr;
206     NX_DNS                                *nx_azure_iot_dns_ptr;
207     NX_CLOUD                               nx_azure_iot_cloud;
208     NX_CLOUD_MODULE                        nx_azure_iot_cloud_module;
209     TX_MUTEX                              *nx_azure_iot_mutex_ptr;
210     VOID                                 (*nx_azure_iot_provisioning_client_event_process)(
211                                           struct NX_AZURE_IOT_STRUCT *nx_azure_iot_ptr,
212                                           ULONG common_events, ULONG module_own_events);
213     struct NX_AZURE_IOT_RESOURCE_STRUCT   *nx_azure_iot_resource_list_header;
214     UINT                                 (*nx_azure_iot_unix_time_get)(ULONG *unix_time);
215 } NX_AZURE_IOT;
216 
217 typedef struct NX_AZURE_IOT_THREAD_STRUCT
218 {
219     TX_THREAD                           *thread_ptr;
220     struct NX_AZURE_IOT_THREAD_STRUCT   *thread_next;
221     UINT                                 thread_message_type;
222     UINT                                 thread_expected_id;     /* Used by device twin. */
223     NX_PACKET                           *thread_received_message;
224 } NX_AZURE_IOT_THREAD;
225 
226 /**
227  * @brief Create the Azure IoT subsystem
228  *
229  * @details This routine creates the Azure IoT subsystem. An internal thread is created to
230  *          manage activities related to Azure IoT services. Only one NX_AZURE_IOT instance
231  *          is needed to manage instances for Azure IoT hub, IoT Central, and Device Provisioning
232  *          Services (DPS).
233  *
234  * @param[in] nx_azure_iot_ptr A pointer to a #NX_AZURE_IOT
235  * @param[in] name_ptr A pointer to a NULL-terminated string indicating the name of the Azure IoT instance.
236  * @param[in] ip_ptr A pointer to a `NX_IP`, which is the IP stack used to connect to Azure IoT Services.
237  * @param[in] pool_ptr A pointer to a `NX_PACKET_POOL`, which is the packet pool used by Azure IoT Services.
238  * @param[in] dns_ptr A pointer to a `NX_DNS`.
239  * @param[in] stack_memory_ptr A pointer to memory to be used as a stack space for the internal thread.
240  * @param[in] stack_memory_size Size of stack memory area.
241  * @param[in] priority Priority of the internal thread.
242  * @param[in] unix_time_callback Callback to fetch unix time from platform.
243  * @return A `UINT` with the result of the API.
244  *   @retval #NX_AZURE_IOT_SUCCESS Successfully created the Azure IoT instance.
245  *   @retval #NX_AZURE_IOT_INVALID_PARAMETER Fail to create the Azure IoT instance due to invalid parameter.
246  *   @retval NX_OPTION_ERROR Fail to create the Azure IoT instance due to invalid priority.
247  *   @retval NX_PTR_ERROR Fail to create the Azure IoT instance due to invalid parameter.
248  *   @retval NX_SIZE_ERROR Fail to create the Azure IoT instance due to insufficient size of stack memory.
249  */
250 UINT nx_azure_iot_create(NX_AZURE_IOT *nx_azure_iot_ptr, const UCHAR *name_ptr,
251                          NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr, NX_DNS *dns_ptr,
252                          VOID *stack_memory_ptr, UINT stack_memory_size,
253                          UINT priority, UINT (*unix_time_callback)(ULONG *unix_time));
254 
255 /**
256  * @brief Shutdown and cleanup the Azure IoT subsystem.
257  * @details This routine stops all Azure services managed by this instance, and cleans up internal resources.
258  *
259  * @param[in] nx_azure_iot_ptr A pointer to a #NX_AZURE_IOT.
260  * @return A `UINT` with the result of the API.
261  *   @retval #NX_AZURE_IOT_SUCCESS Successfully stopped Azure IoT services and cleaned up internal
262  *                                    resources instance.
263  *   @retval #NX_AZURE_IOT_INVALID_PARAMETER Fail to delete the Azure IoT instance due to invalid parameter.
264  *   @retval #NX_AZURE_IOT_DELETE_ERROR Fail to delete the Azure IoT instance due to resource is still in use.
265  */
266 UINT nx_azure_iot_delete(NX_AZURE_IOT *nx_azure_iot_ptr);
267 
268 /**
269  * @brief Get unixtime
270  *
271  * @param[in] nx_azure_iot_ptr A pointer to a #NX_AZURE_IOT.
272  * @param[out] unix_time Pointer to `ULONG` where unixtime is returned.
273  * @return A `UINT` with the result of the API.
274  *   @retval #NX_AZURE_IOT_SUCCESS Successfully return unix time.
275  *   @retval #NX_AZURE_IOT_INVALID_PARAMETER Fail to get unix time due to invalid parameter.
276  */
277 UINT nx_azure_iot_unix_time_get(NX_AZURE_IOT *nx_azure_iot_ptr, ULONG *unix_time);
278 
279 /**
280  * @brief Initialize logging
281  *
282  * @details This routine initialized the logging with customer specific callback to output the logs for different
283  *          classifications:
284  *          - AZ_LOG_IOT_AZURERTOS,
285  *          - AZ_LOG_MQTT_RECEIVED_TOPIC,
286  *          - AZ_LOG_MQTT_RECEIVED_PAYLOAD,
287  *          - AZ_LOG_IOT_RETRY,
288  *          - AZ_LOG_IOT_SAS_TOKEN
289  *
290  * @param[in] log_callback A pointer to a callback.
291  *
292  */
293 VOID nx_azure_iot_log_init(VOID(*log_callback)(az_log_classification classification, UCHAR *msg, UINT msg_len));
294 
295 /* Internal APIs. */
296 UINT nx_azure_iot_buffer_allocate(NX_AZURE_IOT *nx_azure_iot_ptr, UCHAR **buffer_pptr,
297                                   UINT *buffer_size, VOID **buffer_context);
298 UINT nx_azure_iot_buffer_free(VOID *buffer_context);
299 UINT nx_azure_iot_resource_add(NX_AZURE_IOT *nx_azure_iot_ptr, NX_AZURE_IOT_RESOURCE *resource);
300 UINT nx_azure_iot_resource_remove(NX_AZURE_IOT *nx_azure_iot_ptr, NX_AZURE_IOT_RESOURCE *resource);
301 NX_AZURE_IOT_RESOURCE *nx_azure_iot_resource_search(NXD_MQTT_CLIENT *client_ptr);
302 UINT nx_azure_iot_publish_mqtt_packet(NXD_MQTT_CLIENT *client_ptr, NX_PACKET *packet_ptr,
303                                       UINT topic_len, UCHAR *packet_id, UINT qos, UINT wait_option);
304 UINT nx_azure_iot_publish_packet_get(NX_AZURE_IOT *nx_azure_iot_ptr, NXD_MQTT_CLIENT *client_ptr,
305                                      NX_PACKET **packet_pptr, UINT wait_option);
306 UINT nx_azure_iot_mqtt_packet_id_get(NXD_MQTT_CLIENT *client_ptr, UCHAR *packet_id);
307 VOID nx_azure_iot_mqtt_packet_adjust(NX_PACKET *packet_ptr);
308 UINT nx_azure_iot_mqtt_tls_setup(NXD_MQTT_CLIENT *client_ptr, NX_SECURE_TLS_SESSION *tls_session,
309                                  NX_SECURE_X509_CERT *certificate,
310                                  NX_SECURE_X509_CERT *trusted_certificate);
311 UINT nx_azure_iot_base64_hmac_sha256_calculate(NX_AZURE_IOT_RESOURCE *resource_ptr,
312                                                const UCHAR *key_ptr, UINT key_size,
313                                                const UCHAR *message_ptr, UINT message_size,
314                                                UCHAR *buffer_ptr, UINT buffer_len,
315                                                UCHAR **output_ptr, UINT *output_len_ptr);
316 
317 #ifdef __cplusplus
318 }
319 #endif
320 #endif /* NX_AZURE_IOT_H */
321