]> granicus.if.org Git - esp-idf/commitdiff
Protocomm : Added component core for protocol communication
authorAmey Inamdar <amey@espressif.com>
Mon, 30 Jul 2018 16:03:10 +0000 (21:33 +0530)
committerAnurag Kar <anurag.kar@espressif.com>
Tue, 2 Oct 2018 13:37:28 +0000 (19:07 +0530)
* This manages secure sessions and provides framework for multiple transports.
* The application can use protocomm layer directly to have application specific extensions for provisioning (or non-provisioning) use cases.
* Following features are available for provisioning :

  * Security - Security0 (no security), Security1 (curve25519 key exchange + AES-CTR encryption)
  * Proof-of-possession support for Security1

* Protocomm requires specific protocol buffer modules for compilation which can be generated from the `.proto` files in the `proto` directory using make.

Co-Authored-By: Amey Inamdar <amey@espressif.com>
Co-Authored-By: Anurag Kar <anurag.kar@espressif.com>
14 files changed:
components/protocomm/component.mk [new file with mode: 0644]
components/protocomm/include/common/protocomm.h [new file with mode: 0644]
components/protocomm/include/security/protocomm_security.h [new file with mode: 0644]
components/protocomm/include/security/protocomm_security0.h [new file with mode: 0644]
components/protocomm/include/security/protocomm_security1.h [new file with mode: 0644]
components/protocomm/proto/constants.proto [new file with mode: 0644]
components/protocomm/proto/makefile [new file with mode: 0644]
components/protocomm/proto/sec0.proto [new file with mode: 0644]
components/protocomm/proto/sec1.proto [new file with mode: 0644]
components/protocomm/proto/session.proto [new file with mode: 0644]
components/protocomm/src/common/protocomm.c [new file with mode: 0644]
components/protocomm/src/common/protocomm_priv.h [new file with mode: 0644]
components/protocomm/src/security/security0.c [new file with mode: 0644]
components/protocomm/src/security/security1.c [new file with mode: 0644]

diff --git a/components/protocomm/component.mk b/components/protocomm/component.mk
new file mode 100644 (file)
index 0000000..ed8c4b7
--- /dev/null
@@ -0,0 +1,3 @@
+COMPONENT_ADD_INCLUDEDIRS := include/common include/security
+COMPONENT_PRIV_INCLUDEDIRS := proto-c src/common
+COMPONENT_SRCDIRS := src/common src/security proto-c
diff --git a/components/protocomm/include/common/protocomm.h b/components/protocomm/include/common/protocomm.h
new file mode 100644 (file)
index 0000000..f5f67da
--- /dev/null
@@ -0,0 +1,226 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
+
+#pragma once
+
+#include <protocomm_security.h>
+#include <esp_err.h>
+
+/**
+ * @brief Function prototype for protocomm endpoint handler
+ */
+typedef esp_err_t (*protocomm_req_handler_t)(
+    uint32_t        session_id, /*!< Session ID for identifying protocomm client */
+    const uint8_t  *inbuf,      /*!< Pointer to user provided input data buffer */
+    ssize_t         inlen,      /*!< Length o the input buffer */
+    uint8_t       **outbuf,     /*!< Pointer to output buffer allocated by handler */
+    ssize_t        *outlen,     /*!< Length of the allocated output buffer */
+    void           *priv_data   /*!< Private data passed to the handler (NULL if not used) */
+);
+
+/**
+ * @brief   This structure corresponds to a unique instance of protocomm
+ *          returned when the API `protocomm_new()` is called. The remaining
+ *          Protocomm APIs require this object as the first parameter.
+ *
+ * @note    Structure of the protocomm object is kept private
+ */
+typedef struct protocomm protocomm_t;
+
+/**
+ * @brief   Create a new protocomm instance
+ *
+ * This API will return a new dynamically allocated protocomm instance
+ * with all elements of the protocomm_t structure initialised to NULL.
+ *
+ * @return
+ *  - protocomm_t* : On success
+ *  - NULL : No memory for allocating new instance
+ */
+protocomm_t *protocomm_new();
+
+/**
+ * @brief   Delete a protocomm instance
+ *
+ * This API will deallocate a protocomm instance that was created
+ * using `protocomm_new()`.
+ *
+ * @param[in] pc    Pointer to the protocomm instance to be deleted
+ */
+void protocomm_delete(protocomm_t *pc);
+
+/**
+ * @brief   Add endpoint request handler for a protocomm instance
+ *
+ * This API will bind an endpoint handler function to the specified
+ * endpoint name, along with any private data that needs to be pass to
+ * the handler at the time of call.
+ *
+ * @note
+ *  - An endpoint must be bound to a valid protocomm instance,
+ *    created using `protocomm_new()`.
+ *  - This function internally calls the registered `add_endpoint()`
+ *    function which is a member of the protocomm_t instance structure.
+ *
+ * @param[in] pc        Pointer to the protocomm instance
+ * @param[in] ep_name   Endpoint identifier(name) string
+ * @param[in] h         Endpoint handler function
+ * @param[in] priv_data Pointer to private data to be passed as a
+ *                      parameter to the handler function on call.
+ *                      Pass NULL if not needed.
+ *
+ * @return
+ *  - ESP_OK : Added new endpoint succesfully
+ *  - ESP_FAIL : Error adding endpoint / Endpoint with this name already exists
+ *  - ESP_ERR_NO_MEM : Error allocating endpoint resource
+ *  - ESP_ERR_INVALID_ARG : Null instance/name/handler arguments
+ */
+esp_err_t protocomm_add_endpoint(protocomm_t *pc, const char *ep_name,
+                                 protocomm_req_handler_t h, void *priv_data);
+
+/**
+ * @brief   Remove endpoint request handler for a protocomm instance
+ *
+ * This API will remove a registered endpoint handler identified by
+ * an endpoint name.
+ *
+ * @note
+ *  - This function internally calls the registered `remove_endpoint()`
+ *    function which is a member of the protocomm_t instance structure.
+ *
+ * @param[in] pc        Pointer to the protocomm instance
+ * @param[in] ep_name   Endpoint identifier(name) string
+ *
+ * @return
+ *  - ESP_OK : Added new endpoint succesfully
+ *  - ESP_ERR_NOT_FOUND : Endpoint with specified name doesn't exist
+ *  - ESP_ERR_INVALID_ARG : Null instance/name arguments
+ */
+esp_err_t protocomm_remove_endpoint(protocomm_t *pc, const char *ep_name);
+
+/**
+ * @brief   Calls the registered handler of an endpoint session
+ *          for processing incoming data and giving the output
+ *
+ * @note
+ *  - An endpoint must be bound to a valid protocomm instance,
+ *    created using `protocomm_new()`.
+ *  - Resulting output buffer must be deallocated by the user.
+ *
+ * @param[in]  pc         Pointer to the protocomm instance
+ * @param[in]  ep_name    Endpoint identifier(name) string
+ * @param[in]  session_id Unique ID for a communication session
+ * @param[in]  inbuf      Input buffer contains input request data which is to be
+ *                        processed by the registered handler
+ * @param[in]  inlen      Length of the input buffer
+ * @param[out] outbuf     Pointer to internally allocated output buffer,
+ *                        where the resulting response data output from
+ *                        the registered handler is to be stored
+ * @param[out] outlen     Buffer length of the allocated output buffer
+ *
+ * @return
+ *  - ESP_OK : Request handled succesfully
+ *  - ESP_FAIL : Internal error in execution of registered handler
+ *  - ESP_ERR_NO_MEM : Error allocating internal resource
+ *  - ESP_ERR_NOT_FOUND : Endpoint with specified name doesn't exist
+ *  - ESP_ERR_INVALID_ARG : Null instance/name arguments
+ */
+esp_err_t protocomm_req_handle(protocomm_t *pc, const char *ep_name, uint32_t session_id,
+                               const uint8_t *inbuf, ssize_t inlen,
+                               uint8_t **outbuf, ssize_t *outlen);
+
+/**
+ * @brief   Add endpoint security for a protocomm instance
+ *
+ * This API will bind a security session establisher to the specified
+ * endpoint name, along with any proof of possession that may be required
+ * for authenticating a session client.
+ *
+ * @note
+ *  - An endpoint must be bound to a valid protocomm instance,
+ *    created using `protocomm_new()`.
+ *  - The choice of security can be any `protocomm_security_t` instance.
+ *    Choices `protocomm_security0` and `protocomm_security1` are readily available.
+ *
+ * @param[in] pc        Pointer to the protocomm instance
+ * @param[in] ep_name   Endpoint identifier(name) string
+ * @param[in] sec       Pointer to endpoint security instance
+ * @param[in] pop       Pointer to proof of possession for authenticating a client
+ *
+ * @return
+ *  - ESP_OK : Added new security endpoint succesfully
+ *  - ESP_FAIL : Error adding endpoint / Endpoint with this name already exists
+ *  - ESP_ERR_INVALID_STATE : Security endpoint already set
+ *  - ESP_ERR_NO_MEM : Error allocating endpoint resource
+ *  - ESP_ERR_INVALID_ARG : Null instance/name/handler arguments
+ */
+esp_err_t protocomm_set_security(protocomm_t *pc, const char *ep_name,
+                                 const protocomm_security_t *sec,
+                                 const protocomm_security_pop_t *pop);
+
+/**
+ * @brief   Remove endpoint security for a protocomm instance
+ *
+ * This API will remove a registered security endpoint identified by
+ * an endpoint name.
+ *
+ * @param[in] pc        Pointer to the protocomm instance
+ * @param[in] ep_name   Endpoint identifier(name) string
+ *
+ * @return
+ *  - ESP_OK : Added new endpoint succesfully
+ *  - ESP_ERR_NOT_FOUND : Endpoint with specified name doesn't exist
+ *  - ESP_ERR_INVALID_ARG : Null instance/name arguments
+ */
+esp_err_t protocomm_unset_security(protocomm_t *pc, const char *ep_name);
+
+/**
+ * @brief   Set endpoint for version verification
+ *
+ * This API can be used for setting an application specific protocol
+ * version which can be verfied by clients through the endpoint.
+ *
+ * @note
+ *  - An endpoint must be bound to a valid protocomm instance,
+ *    created using `protocomm_new()`.
+
+ * @param[in] pc        Pointer to the protocomm instance
+ * @param[in] ep_name   Endpoint identifier(name) string
+ * @param[in] version   Version identifier(name) string
+ *
+ * @return
+ *  - ESP_OK : Added new security endpoint succesfully
+ *  - ESP_FAIL : Error adding endpoint / Endpoint with this name already exists
+ *  - ESP_ERR_INVALID_STATE : Version endpoint already set
+ *  - ESP_ERR_NO_MEM : Error allocating endpoint resource
+ *  - ESP_ERR_INVALID_ARG : Null instance/name/handler arguments
+ */
+esp_err_t protocomm_set_version(protocomm_t *pc, const char *ep_name,
+                                const char *version);
+
+/**
+ * @brief   Remove version verification endpoint from a protocomm instance
+ *
+ * This API will remove a registered version endpoint identified by
+ * an endpoint name.
+ *
+ * @param[in] pc        Pointer to the protocomm instance
+ * @param[in] ep_name   Endpoint identifier(name) string
+ *
+ * @return
+ *  - ESP_OK : Added new endpoint succesfully
+ *  - ESP_ERR_NOT_FOUND : Endpoint with specified name doesn't exist
+ *  - ESP_ERR_INVALID_ARG : Null instance/name arguments
+ */
+esp_err_t protocomm_unset_version(protocomm_t *pc, const char *ep_name);
diff --git a/components/protocomm/include/security/protocomm_security.h b/components/protocomm/include/security/protocomm_security.h
new file mode 100644 (file)
index 0000000..7eeecc1
--- /dev/null
@@ -0,0 +1,93 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
+
+#pragma once
+
+#include <esp_err.h>
+
+/**
+ * @brief   Proof Of Possession for authenticating a secure session
+ */
+typedef struct protocomm_security_pop {
+    /**
+     * Pointer to buffer containing the proof of possession data
+     */
+    const uint8_t *data;
+
+    /**
+     * Length (in bytes) of the proof of possession data
+     */
+    uint16_t len;
+} protocomm_security_pop_t;
+
+/**
+ * @brief   Protocomm security object structure.
+ *
+ * The member functions are used for implementing secure
+ * protocomm sessions.
+ *
+ * @note    This structure should not have any dynamic
+ *          members to allow re-entrancy
+ */
+typedef struct protocomm_security {
+    /**
+     * Unique version number of security implmentation
+     */
+    int ver;
+
+    /**
+     * Function for initialising/allocating security
+     * infrastructure
+     */
+    esp_err_t (*init)();
+
+    /**
+     * Function for deallocating security infrastructure
+     */
+    esp_err_t (*cleanup)();
+
+    /**
+     * Starts new secure transport session with specified ID
+     */
+    esp_err_t (*new_transport_session)(uint32_t session_id);
+
+    /**
+     * Closes a secure transport session with specified ID
+     */
+    esp_err_t (*close_transport_session)(uint32_t session_id);
+
+    /**
+     * Handler function for authenticating connection
+     * request and establishing secure session
+     */
+    esp_err_t (*security_req_handler)(const protocomm_security_pop_t *pop,
+                                      uint32_t session_id,
+                                      const uint8_t *inbuf, ssize_t inlen,
+                                      uint8_t **outbuf, ssize_t *outlen,
+                                      void *priv_data);
+
+    /**
+     * Function which implements the encryption algorithm
+     */
+    esp_err_t (*encrypt)(uint32_t session_id,
+                         const uint8_t *inbuf, ssize_t inlen,
+                         uint8_t *outbuf, ssize_t *outlen);
+
+    /**
+     * Function which implements the decryption algorithm
+     */
+    esp_err_t (*decrypt)(uint32_t session_id,
+                         const uint8_t *inbuf, ssize_t inlen,
+                         uint8_t *outbuf, ssize_t *outlen);
+} protocomm_security_t;
diff --git a/components/protocomm/include/security/protocomm_security0.h b/components/protocomm/include/security/protocomm_security0.h
new file mode 100644 (file)
index 0000000..7d0462d
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
+
+#pragma once
+
+#include <protocomm_security.h>
+
+/**
+ * @brief   Protocomm security version 0 implementation
+ *
+ * This is a simple implementation to be used when no
+ * security is required for the protocomm instance
+ */
+extern const protocomm_security_t protocomm_security0;
diff --git a/components/protocomm/include/security/protocomm_security1.h b/components/protocomm/include/security/protocomm_security1.h
new file mode 100644 (file)
index 0000000..098d61e
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
+
+#pragma once
+
+#include <protocomm_security.h>
+
+/**
+ * @brief   Protocomm security version 1 implementation
+ *
+ * This is a full fledged security implmentation using
+ * Curve25519 key exchange and AES-256-CTR encryption
+ */
+extern const protocomm_security_t protocomm_security1;
diff --git a/components/protocomm/proto/constants.proto b/components/protocomm/proto/constants.proto
new file mode 100644 (file)
index 0000000..c80d250
--- /dev/null
@@ -0,0 +1,14 @@
+syntax = "proto3";
+
+/* Allowed values for the status
+ * of a protocomm instance */
+enum Status {
+    Success = 0;
+    InvalidSecScheme = 1;
+    InvalidProto = 2;
+    TooManySessions = 3;
+    InvalidArgument = 4;
+    InternalError = 5;
+    CryptoError = 6;
+    InvalidSession = 7;
+}
diff --git a/components/protocomm/proto/makefile b/components/protocomm/proto/makefile
new file mode 100644 (file)
index 0000000..154e591
--- /dev/null
@@ -0,0 +1,7 @@
+all: c_proto python_proto
+
+c_proto: *.proto
+       @protoc-c --c_out=../proto-c/ -I . *.proto
+
+python_proto: *.proto
+       @protoc --python_out=../python/ -I . *.proto
diff --git a/components/protocomm/proto/sec0.proto b/components/protocomm/proto/sec0.proto
new file mode 100644 (file)
index 0000000..299fce7
--- /dev/null
@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+import "constants.proto";
+
+/* Data structure of Session command/request packet */
+message S0SessionCmd {
+
+}
+
+/* Data structure of Session response packet */
+message S0SessionResp {
+    Status status = 1;
+}
+
+/* A message must be of type Cmd or Resp */
+enum Sec0MsgType {
+    S0_Session_Command = 0;
+    S0_Session_Response = 1;
+}
+
+/* Payload structure of session data */
+message Sec0Payload {
+    Sec0MsgType msg = 1;        /*!< Type of message */
+    oneof payload {
+        S0SessionCmd sc = 20;   /*!< Payload data interpreted as Cmd */
+        S0SessionResp sr = 21;  /*!< Payload data interpreted as Resp */
+    }
+}
diff --git a/components/protocomm/proto/sec1.proto b/components/protocomm/proto/sec1.proto
new file mode 100644 (file)
index 0000000..97e05ea
--- /dev/null
@@ -0,0 +1,45 @@
+syntax = "proto3";
+
+import "constants.proto";
+
+/* Data structure of Session command1 packet */
+message SessionCmd1 {
+    bytes client_verify_data = 2;
+}
+
+/* Data structure of Session response1 packet */
+message SessionResp1 {
+    Status status = 1;
+    bytes device_verify_data = 3;
+}
+
+/* Data structure of Session command0 packet */
+message SessionCmd0 {
+    bytes client_pubkey = 1;
+}
+
+/* Data structure of Session response0 packet */
+message SessionResp0 {
+    Status status = 1;
+    bytes device_pubkey = 2;
+    bytes device_random = 3;
+}
+
+/* A message must be of type Cmd0 / Cmd1 / Resp0 / Resp1 */
+enum Sec1MsgType {
+    Session_Command0 = 0;
+    Session_Response0 = 1;
+    Session_Command1 = 2;
+    Session_Response1 = 3;
+}
+
+/* Payload structure of session data */
+message Sec1Payload {
+    Sec1MsgType msg = 1;        /*!< Type of message */
+    oneof payload {
+        SessionCmd0 sc0 = 20;   /*!< Payload data interpreted as Cmd0 */
+        SessionResp0 sr0 = 21;  /*!< Payload data interpreted as Resp0 */
+        SessionCmd1 sc1 = 22;   /*!< Payload data interpreted as Cmd1 */
+        SessionResp1 sr1 = 23;  /*!< Payload data interpreted as Resp1 */
+    }
+}
diff --git a/components/protocomm/proto/session.proto b/components/protocomm/proto/session.proto
new file mode 100644 (file)
index 0000000..de267b1
--- /dev/null
@@ -0,0 +1,21 @@
+syntax = "proto3";
+
+import "sec0.proto";
+import "sec1.proto";
+
+/* Allowed values for the type of security
+ * being used in a protocomm session */
+enum SecSchemeVersion {
+    SecScheme0 = 0; /*!< Unsecured - plaintext communication */
+    SecScheme1 = 1; /*!< Security scheme 1 - Curve25519 + AES-256-CTR*/
+}
+
+/* Data structure exchanged when establishing
+ * secure session between Host and Client */
+message SessionData {
+    SecSchemeVersion sec_ver = 2;   /*!< Type of security */
+    oneof proto {
+        Sec0Payload sec0 = 10;      /*!< Payload data in case of security 0 */
+        Sec1Payload sec1 = 11;      /*!< Payload data in case of security 1 */
+    }
+}
diff --git a/components/protocomm/src/common/protocomm.c b/components/protocomm/src/common/protocomm.c
new file mode 100644 (file)
index 0000000..94ebc8e
--- /dev/null
@@ -0,0 +1,383 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <esp_err.h>
+#include <esp_log.h>
+#include <rom/queue.h>
+
+#include <protocomm.h>
+#include <protocomm_security.h>
+
+#include "protocomm_priv.h"
+
+static const char *TAG = "protocomm";
+
+protocomm_t *protocomm_new()
+{
+    protocomm_t *pc;
+
+    pc = (protocomm_t *) calloc(1, sizeof(protocomm_t));
+    if (!pc) {
+       ESP_LOGE(TAG, "Error allocating protocomm");
+       return NULL;
+    }
+    SLIST_INIT(&pc->endpoints);
+
+    return pc;
+}
+
+void protocomm_delete(protocomm_t *pc)
+{
+    if (pc == NULL) {
+        return;
+    }
+
+    protocomm_ep_t *it, *tmp;
+    /* Remove endpoints first */
+    SLIST_FOREACH_SAFE(it, &pc->endpoints, next, tmp) {
+        free(it);
+    }
+
+    free(pc);
+}
+
+static protocomm_ep_t *search_endpoint(protocomm_t *pc, const char *ep_name)
+{
+    protocomm_ep_t *it;
+    SLIST_FOREACH(it, &pc->endpoints, next) {
+        if (strcmp(it->ep_name, ep_name) == 0) {
+            return it;
+        }
+    }
+    return NULL;
+}
+
+static esp_err_t protocomm_add_endpoint_internal(protocomm_t *pc, const char *ep_name,
+                                                 protocomm_req_handler_t h, void *priv_data,
+                                                 uint32_t flag)
+{
+    if ((pc == NULL) || (ep_name == NULL) || (h == NULL)) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    protocomm_ep_t *ep;
+    esp_err_t ret;
+
+    ep = search_endpoint(pc, ep_name);
+    if (ep) {
+        ESP_LOGE(TAG, "Endpoint with this name already exists");
+        return ESP_FAIL;
+    }
+
+    if (pc->add_endpoint) {
+        ret = pc->add_endpoint(ep_name, h, priv_data);
+        if (ret != ESP_OK) {
+            ESP_LOGE(TAG, "Error adding endpoint");
+            return ret;
+        }
+    }
+
+    ep = (protocomm_ep_t *) calloc(1, sizeof(protocomm_ep_t));
+    if (!ep) {
+        ESP_LOGE(TAG, "Error allocating endpoint resource");
+        return ESP_ERR_NO_MEM;
+    }
+
+    /* Initialize ep handler */
+    ep->ep_name = ep_name;
+    ep->req_handler = h;
+    ep->priv_data = priv_data;
+    ep->flag = flag;
+
+    /* Add endpoint to the head of the singly linked list */
+    SLIST_INSERT_HEAD(&pc->endpoints, ep, next);
+
+    return ESP_OK;
+}
+
+esp_err_t protocomm_add_endpoint(protocomm_t *pc, const char *ep_name,
+                                 protocomm_req_handler_t h, void *priv_data)
+{
+    return protocomm_add_endpoint_internal(pc, ep_name, h, priv_data, REQ_EP);
+}
+
+esp_err_t protocomm_remove_endpoint(protocomm_t *pc, const char *ep_name)
+{
+    if ((pc == NULL) || (ep_name == NULL)) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    if (pc->remove_endpoint) {
+        pc->remove_endpoint(ep_name);
+    }
+
+    protocomm_ep_t *it, *tmp;
+    SLIST_FOREACH_SAFE(it, &pc->endpoints, next, tmp) {
+        if (strcmp(ep_name, it->ep_name) == 0) {
+            SLIST_REMOVE(&pc->endpoints, it, protocomm_ep, next);
+            free(it);
+            return ESP_OK;
+        }
+    }
+    return ESP_ERR_NOT_FOUND;
+}
+
+esp_err_t protocomm_req_handle(protocomm_t *pc, const char *ep_name, uint32_t session_id,
+                               const uint8_t *inbuf, ssize_t inlen,
+                               uint8_t **outbuf, ssize_t *outlen)
+{
+    if ((pc == NULL) || (ep_name == NULL)) {
+        ESP_LOGE(TAG, "Invalid params %p %p", pc, ep_name);
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    protocomm_ep_t *ep = search_endpoint(pc, ep_name);
+    if (!ep) {
+        ESP_LOGE(TAG, "No registered endpoint for %s", ep_name);
+        return ESP_ERR_NOT_FOUND;
+    }
+
+    esp_err_t ret = ESP_FAIL;
+    if (ep->flag & SEC_EP) {
+        /* Call the registered endpoint handler for establishing secure session */
+        ret = ep->req_handler(session_id, inbuf, inlen, outbuf, outlen, ep->priv_data);
+        ESP_LOGD(TAG, "SEC_EP Req handler returned %d", ret);
+    } else if (ep->flag & REQ_EP) {
+        if (pc->sec && pc->sec->decrypt) {
+            /* Decrypt the data first */
+            uint8_t *dec_inbuf = (uint8_t *) malloc(inlen);
+            if (!dec_inbuf) {
+                ESP_LOGE(TAG, "Failed to allocate decrypt buf len %d", inlen);
+                return ESP_ERR_NO_MEM;
+            }
+
+            ssize_t dec_inbuf_len = inlen;
+            pc->sec->decrypt(session_id, inbuf, inlen, dec_inbuf, &dec_inbuf_len);
+
+            /* Invoke the request handler */
+            uint8_t *plaintext_resp;
+            ssize_t plaintext_resp_len;
+            ret = ep->req_handler(session_id,
+                                  dec_inbuf, dec_inbuf_len,
+                                  &plaintext_resp, &plaintext_resp_len,
+                                  ep->priv_data);
+            if (ret != ESP_OK) {
+                ESP_LOGE(TAG, "Request handler for %s failed", ep_name);
+                *outbuf = NULL;
+                *outlen = 0;
+                free(dec_inbuf);
+                return ret;
+            }
+            /* We don't need decrypted data anymore */
+            free(dec_inbuf);
+
+            /* Encrypt response to be sent back */
+            uint8_t *enc_resp = (uint8_t *) malloc(plaintext_resp_len);
+            if (!enc_resp) {
+                ESP_LOGE(TAG, "Failed to allocate decrypt buf len %d", inlen);
+                return ESP_ERR_NO_MEM;
+            }
+
+            ssize_t enc_resp_len = plaintext_resp_len;
+            pc->sec->encrypt(session_id, plaintext_resp, plaintext_resp_len,
+                             enc_resp, &enc_resp_len);
+
+            /* We no more need plaintext response */
+            free(plaintext_resp);
+
+            /* Set outbuf and outlen appropriately */
+            *outbuf = enc_resp;
+            *outlen = enc_resp_len;
+        } else {
+            /* No encryption */
+            ret = ep->req_handler(session_id,
+                                  inbuf, inlen,
+                                  outbuf, outlen,
+                                  ep->priv_data);
+            ESP_LOGD(TAG, "No encrypt ret %d", ret);
+        }
+    } else if (ep->flag & VER_EP) {
+        ret = ep->req_handler(session_id, inbuf, inlen, outbuf, outlen, ep->priv_data);
+        ESP_LOGD(TAG, "VER_EP Req handler returned %d", ret);
+    }
+    return ret;
+}
+
+static int protocomm_common_security_handler(uint32_t session_id,
+                                             const uint8_t *inbuf, ssize_t inlen,
+                                             uint8_t **outbuf, ssize_t *outlen,
+                                             void *priv_data)
+{
+    protocomm_t *pc = (protocomm_t *) priv_data;
+
+    if (pc->sec && pc->sec->security_req_handler) {
+        return pc->sec->security_req_handler(pc->pop, session_id,
+                                             inbuf, inlen,
+                                             outbuf, outlen,
+                                             priv_data);
+    }
+
+    return ESP_OK;
+}
+
+esp_err_t protocomm_set_security(protocomm_t *pc, const char *ep_name,
+                                 const protocomm_security_t *sec,
+                                 const protocomm_security_pop_t *pop)
+{
+    if ((pc == NULL) || (ep_name == NULL) || (sec == NULL)) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    if (pc->sec) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    esp_err_t ret = protocomm_add_endpoint_internal(pc, ep_name,
+                                                    protocomm_common_security_handler,
+                                                    (void *) pc, SEC_EP);
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "Error adding security endpoint");
+        return ret;
+    }
+
+    if (sec->init) {
+        ret = sec->init();
+        if (ret != ESP_OK) {
+            ESP_LOGE(TAG, "Error initialising security");
+            protocomm_remove_endpoint(pc, ep_name);
+            return ret;
+        }
+    }
+    pc->sec = sec;
+
+    if (pop) {
+        pc->pop = malloc(sizeof(protocomm_security_pop_t));
+        if (pc->pop == NULL) {
+            ESP_LOGE(TAG, "Error allocating Proof of Possession");
+            if (pc->sec && pc->sec->cleanup) {
+                pc->sec->cleanup();
+                pc->sec = NULL;
+            }
+
+            protocomm_remove_endpoint(pc, ep_name);
+            return ESP_ERR_NO_MEM;
+        }
+        memcpy((void *)pc->pop, pop, sizeof(protocomm_security_pop_t));
+    }
+    return ESP_OK;
+}
+
+esp_err_t protocomm_unset_security(protocomm_t *pc, const char *ep_name)
+{
+    if ((pc == NULL) || (ep_name == NULL)) {
+        return ESP_FAIL;
+    }
+
+    if (pc->sec && pc->sec->cleanup) {
+        pc->sec->cleanup();
+        pc->sec = NULL;
+    }
+
+    if (pc->pop) {
+        free(pc->pop);
+        pc->pop = NULL;
+    }
+
+    return protocomm_remove_endpoint(pc, ep_name);
+}
+
+static int protocomm_version_handler(uint32_t session_id,
+                                     const uint8_t *inbuf, ssize_t inlen,
+                                     uint8_t **outbuf, ssize_t *outlen,
+                                     void *priv_data)
+{
+    const char *resp_match = "SUCCESS";
+    const char *resp_fail  = "FAIL";
+    bool match = false;
+    char *version = strndup((const char *)inbuf, inlen);
+    protocomm_t *pc = (protocomm_t *) priv_data;
+    *outbuf = NULL;
+    *outlen = 0;
+
+    if ((pc->ver != NULL) && (version != NULL)) {
+        ESP_LOGV(TAG, "Protocol version of device : %s", pc->ver);
+        ESP_LOGV(TAG, "Protocol version of client : %s", version);
+        if (strcmp(pc->ver, version) == 0) {
+            match = true;
+        }
+    } else if ((pc->ver == NULL) && (version == NULL)) {
+        match = true;
+    }
+    free(version);
+
+    if (!match) {
+        ESP_LOGE(TAG, "Protocol version mismatch");
+    }
+
+    const char *result_msg = match ? resp_match : resp_fail;
+
+    /* Output is a non null terminated string with length specified */
+    *outlen = strlen(result_msg);
+    *outbuf = malloc(strlen(result_msg));
+    if (outbuf == NULL) {
+        ESP_LOGE(TAG, "Failed to allocate memory for version check response");
+        return ESP_ERR_NO_MEM;
+    }
+
+    memcpy(*outbuf, result_msg, *outlen);
+    return ESP_OK;
+}
+
+esp_err_t protocomm_set_version(protocomm_t *pc, const char *ep_name, const char *version)
+{
+    if ((pc == NULL) || (ep_name == NULL) || (version == NULL)) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    if (pc->ver) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    pc->ver = strdup(version);
+    if (pc->ver == NULL) {
+        ESP_LOGE(TAG, "Error allocating version string");
+        return ESP_ERR_NO_MEM;
+    }
+
+    esp_err_t ret = protocomm_add_endpoint_internal(pc, ep_name,
+                                                    protocomm_version_handler,
+                                                    (void *) pc, VER_EP);
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "Error adding version endpoint");
+        return ret;
+    }
+    return ESP_OK;
+}
+
+esp_err_t protocomm_unset_version(protocomm_t *pc, const char *ep_name)
+{
+    if ((pc == NULL) || (ep_name == NULL)) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    if (pc->ver) {
+        free((char *)pc->ver);
+        pc->ver = NULL;
+    }
+
+    return protocomm_remove_endpoint(pc, ep_name);
+}
diff --git a/components/protocomm/src/common/protocomm_priv.h b/components/protocomm/src/common/protocomm_priv.h
new file mode 100644 (file)
index 0000000..e956c3f
--- /dev/null
@@ -0,0 +1,79 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
+
+#pragma once
+
+#include <rom/queue.h>
+#include <protocomm_security.h>
+#include <esp_err.h>
+
+#define PROTOCOMM_NO_SESSION_ID UINT32_MAX
+
+/* Bit Flags for indicating intended functionality of handler to either
+ * process request or establish secure session */
+#define REQ_EP      (1 << 0)    /*!< Flag indicating request  handling endpoint */
+#define SEC_EP      (1 << 1)    /*!< Flag indicating security handling endpoint */
+#define VER_EP      (1 << 2)    /*!< Flag indicating version  handling endpoint */
+
+/**
+ * @brief   Protocomm endpoint table entry prototype
+ *
+ * The structure of an entry stored in the endpoint table.
+ */
+typedef struct protocomm_ep {
+    const char              *ep_name;       /*!< Unique endpoint name */
+    protocomm_req_handler_t  req_handler;   /*!< Request handler function */
+
+    /* Pointer to private data to be passed as a parameter to the handler
+     * function at the time of call. Set to NULL if not used. */
+    void            *priv_data;
+
+    uint32_t         flag;          /*!< Flag indicating endpoint functionality */
+
+    /* Next endpoint entry in the singly linked list for storing endpoint handlers */
+    SLIST_ENTRY(protocomm_ep) next;
+} protocomm_ep_t;
+
+/**
+ * @brief   Prototype structure of a Protocomm instance
+ *
+ * This structure corresponds to a unique instance of protocomm returned,
+ * when the API protocomm_new() is called. The remaining Protocomm
+ * APIs require this object as the first parameter.
+ */
+struct protocomm {
+    /* Function Prototype of transport specific function, which is called
+     * internally when protocomm_add_endpoint() is invoked. */
+    int (*add_endpoint)(const char *ep_name, protocomm_req_handler_t h, void *priv_data);
+
+    /* Function Prototype of transport specific function, which is called
+     * internally when protocomm_remove_endpoint() is invoked. */
+    int (*remove_endpoint)(const char *ep_name);
+
+    /* Pointer to security layer instance to be used internally for
+     * establising secure sessions */
+    const protocomm_security_t *sec;
+
+    /* Pointer to proof of possession object */
+    protocomm_security_pop_t *pop;
+
+    /* Head of the singly linked list for storing endpoint handlers */
+    SLIST_HEAD(eptable_t, protocomm_ep) endpoints;
+
+    /* Private data to be used internally by the protocomm instance */
+    void* priv;
+
+    /* Application specific version string */
+    const char* ver;
+};
diff --git a/components/protocomm/src/security/security0.c b/components/protocomm/src/security/security0.c
new file mode 100644 (file)
index 0000000..bca4d18
--- /dev/null
@@ -0,0 +1,111 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <esp_err.h>
+#include <esp_log.h>
+
+#include <protocomm_security.h>
+#include <protocomm_security0.h>
+
+#include "session.pb-c.h"
+#include "sec0.pb-c.h"
+#include "constants.pb-c.h"
+
+static const char* TAG = "security0";
+
+static esp_err_t sec0_session_setup(uint32_t session_id,
+                                    SessionData *req, SessionData *resp,
+                                    const protocomm_security_pop_t *pop)
+{
+    Sec0Payload *out = (Sec0Payload *) malloc(sizeof(Sec0Payload));
+    S0SessionResp *s0resp = (S0SessionResp *) malloc(sizeof(S0SessionResp));
+    if (!out || !s0resp) {
+        ESP_LOGE(TAG, "Error allocating response");
+        return ESP_ERR_NO_MEM;
+    }
+    sec0_payload__init(out);
+    s0_session_resp__init(s0resp);
+    s0resp->status = STATUS__Success;
+
+    out->msg = SEC0_MSG_TYPE__S0_Session_Response;
+    out->payload_case = SEC0_PAYLOAD__PAYLOAD_SR;
+    out->sr = s0resp;
+
+    resp->proto_case = SESSION_DATA__PROTO_SEC0;
+    resp->sec0 = out;
+
+    return ESP_OK;
+}
+
+static void sec0_session_setup_cleanup(uint32_t session_id, SessionData *resp)
+{
+    if (!resp) {
+        return;
+    }
+
+    free(resp->sec0->sr);
+    free(resp->sec0);
+    return;
+}
+
+static esp_err_t sec0_req_handler(const protocomm_security_pop_t *pop, uint32_t session_id,
+                                  const uint8_t *inbuf, ssize_t inlen,
+                                  uint8_t **outbuf, ssize_t *outlen,
+                                  void *priv_data)
+{
+    SessionData *req;
+    SessionData resp;
+    esp_err_t ret;
+
+    req = session_data__unpack(NULL, inlen, inbuf);
+    if (!req) {
+        ESP_LOGE(TAG, "Unable to unpack setup_req");
+        return ESP_ERR_INVALID_ARG;
+    }
+    if (req->sec_ver != protocomm_security0.ver) {
+        ESP_LOGE(TAG, "Security version mismatch. Closing connection");
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    session_data__init(&resp);
+    ret = sec0_session_setup(session_id, req, &resp, pop);
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "Session setup error %d", ret);
+        return ESP_FAIL;
+    }
+
+    session_data__free_unpacked(req, NULL);
+
+    resp.sec_ver = req->sec_ver;
+
+    *outlen = session_data__get_packed_size(&resp);
+    *outbuf = (uint8_t *) malloc(*outlen);
+    if (!*outbuf) {
+        ESP_LOGE(TAG, "System out of memory");
+        return ESP_ERR_NO_MEM;
+    }
+    session_data__pack(&resp, *outbuf);
+
+    sec0_session_setup_cleanup(session_id, &resp);
+    return ESP_OK;
+}
+
+const protocomm_security_t protocomm_security0 = {
+    .ver = 0,
+    .security_req_handler = sec0_req_handler,
+};
diff --git a/components/protocomm/src/security/security1.c b/components/protocomm/src/security/security1.c
new file mode 100644 (file)
index 0000000..76b9a25
--- /dev/null
@@ -0,0 +1,535 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE 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.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <esp_err.h>
+#include <esp_log.h>
+
+#include <mbedtls/aes.h>
+#include <mbedtls/sha256.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/ecdh.h>
+#include <mbedtls/error.h>
+#include <mbedtls/ssl_internal.h>
+
+#include <protocomm_security.h>
+#include <protocomm_security1.h>
+
+#include "session.pb-c.h"
+#include "sec1.pb-c.h"
+#include "constants.pb-c.h"
+
+static const char* TAG = "security1";
+
+#define PUBLIC_KEY_LEN  32
+#define SZ_RANDOM       16
+
+#define SESSION_STATE_1     1 /* Session in state 1 */
+#define SESSION_STATE_SETUP 2 /* Session setup successful */
+
+typedef struct session {
+    /* Session data */
+    uint32_t id;
+    uint8_t state;
+    uint8_t device_pubkey[PUBLIC_KEY_LEN];
+    uint8_t client_pubkey[PUBLIC_KEY_LEN];
+    uint8_t sym_key[PUBLIC_KEY_LEN];
+    uint8_t rand[SZ_RANDOM];
+
+    /* mbedtls context data for AES */
+    mbedtls_aes_context ctx_aes;
+    unsigned char stb[16];
+    size_t nc_off;
+} session_t;
+
+static session_t *cur_session;
+
+static void flip_endian(uint8_t *data, size_t len)
+{
+    uint8_t swp_buf;
+    for (int i = 0; i < len/2; i++) {
+        swp_buf = data[i];
+        data[i] = data[len - i - 1];
+        data[len - i - 1] = swp_buf;
+    }
+}
+
+static void hexdump(const char *msg, uint8_t *buf, int len)
+{
+    ESP_LOGD(TAG, "%s:", msg);
+    ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, len, ESP_LOG_DEBUG);
+}
+
+static esp_err_t handle_session_command1(uint32_t session_id,
+                                         SessionData *req, SessionData *resp)
+{
+    ESP_LOGD(TAG, "Request to handle setup1_command");
+    Sec1Payload *in = (Sec1Payload *) req->sec1;
+    uint8_t check_buf[PUBLIC_KEY_LEN];
+    int mbed_err;
+
+    if (!cur_session) {
+        ESP_LOGE(TAG, "Data on session endpoint without session establishment");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if (session_id != cur_session->id) {
+        ESP_LOGE(TAG, "Invalid session");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if (!in) {
+        ESP_LOGE(TAG, "Empty session data");
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    /* Initialise crypto context */
+    mbedtls_aes_init(&cur_session->ctx_aes);
+    memset(cur_session->stb, 0, sizeof(cur_session->stb));
+    cur_session->nc_off = 0;
+
+    hexdump("Client verifier", in->sc1->client_verify_data.data,
+            in->sc1->client_verify_data.len);
+
+    mbed_err = mbedtls_aes_setkey_enc(&cur_session->ctx_aes, cur_session->sym_key,
+                                      sizeof(cur_session->sym_key)*8);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failure at mbedtls_aes_setkey_enc with error code : -0x%x", -mbed_err);
+        return ESP_FAIL;
+    }
+
+    mbed_err = mbedtls_aes_crypt_ctr(&cur_session->ctx_aes,
+                                     PUBLIC_KEY_LEN, &cur_session->nc_off,
+                                     cur_session->rand, cur_session->stb,
+                                     in->sc1->client_verify_data.data, check_buf);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failure at mbedtls_aes_crypt_ctr with error code : -0x%x", -mbed_err);
+        return ESP_FAIL;
+    }
+
+    hexdump("Dec Client verifier", check_buf, sizeof(check_buf));
+
+    /* constant time memcmp */
+    if (mbedtls_ssl_safer_memcmp(check_buf, cur_session->device_pubkey,
+                                 sizeof(cur_session->device_pubkey)) != 0) {
+        ESP_LOGE(TAG, "Key mismatch. Close connection");
+        return ESP_FAIL;
+    }
+
+    Sec1Payload *out = (Sec1Payload *) malloc(sizeof(Sec1Payload));
+    sec1_payload__init(out);
+    SessionResp1 *out_resp = (SessionResp1 *) malloc(sizeof(SessionResp1));
+    session_resp1__init(out_resp);
+
+    out_resp->status = STATUS__Success;
+
+    uint8_t *outbuf = (uint8_t *) malloc(PUBLIC_KEY_LEN);
+    if (!outbuf) {
+        ESP_LOGE(TAG, "Error allocating ciphertext buffer");
+        return ESP_ERR_NO_MEM;
+    }
+
+    mbed_err = mbedtls_aes_crypt_ctr(&cur_session->ctx_aes,
+                                     PUBLIC_KEY_LEN, &cur_session->nc_off,
+                                     cur_session->rand, cur_session->stb,
+                                     cur_session->client_pubkey, outbuf);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failure at mbedtls_aes_crypt_ctr with error code : -0x%x", -mbed_err);
+        return ESP_FAIL;
+    }
+
+    out_resp->device_verify_data.data = outbuf;
+    out_resp->device_verify_data.len = PUBLIC_KEY_LEN;
+    hexdump("Device verify data", outbuf, PUBLIC_KEY_LEN);
+
+    out->msg = SEC1_MSG_TYPE__Session_Response1;
+    out->payload_case = SEC1_PAYLOAD__PAYLOAD_SR1;
+    out->sr1 = out_resp;
+
+    resp->proto_case = SESSION_DATA__PROTO_SEC1;
+    resp->sec1 = out;
+
+    ESP_LOGD(TAG, "Session successful");
+
+    return ESP_OK;
+}
+
+static esp_err_t handle_session_command0(uint32_t session_id,
+                                         SessionData *req, SessionData *resp,
+                                         const protocomm_security_pop_t *pop)
+{
+    Sec1Payload *in = (Sec1Payload *) req->sec1;
+    esp_err_t ret;
+    int mbed_err;
+
+    if (!cur_session) {
+        ESP_LOGE(TAG, "Data on session endpoint without session establishment");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if (session_id != cur_session->id) {
+        ESP_LOGE(TAG, "Invalid session");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if (!in) {
+        ESP_LOGE(TAG, "Empty session data");
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    if (in->sc0->client_pubkey.len != PUBLIC_KEY_LEN) {
+        ESP_LOGE(TAG, "Invalid public key length");
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    cur_session->state = SESSION_STATE_1;
+
+    mbedtls_ecdh_context     *ctx_server = malloc(sizeof(mbedtls_ecdh_context));
+    mbedtls_entropy_context  *entropy    = malloc(sizeof(mbedtls_entropy_context));
+    mbedtls_ctr_drbg_context *ctr_drbg   = malloc(sizeof(mbedtls_ctr_drbg_context));
+    if (!ctx_server || !ctx_server || !ctr_drbg) {
+        ESP_LOGE(TAG, "Failed to allocate memory for mbedtls context");
+        free(ctx_server);
+        free(entropy);
+        free(ctr_drbg);
+        return ESP_ERR_NO_MEM;
+    }
+
+    mbedtls_ecdh_init(ctx_server);
+    mbedtls_ctr_drbg_init(ctr_drbg);
+    mbedtls_entropy_init(entropy);
+
+    mbed_err = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func,
+                                     entropy, NULL, 0);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_ctr_drbg_seed with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+
+    mbed_err = mbedtls_ecp_group_load(&ctx_server->grp, MBEDTLS_ECP_DP_CURVE25519);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_ecp_group_load with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+
+    mbed_err = mbedtls_ecdh_gen_public(&ctx_server->grp, &ctx_server->d, &ctx_server->Q,
+                                       mbedtls_ctr_drbg_random, ctr_drbg);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_ecdh_gen_public with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+
+    mbed_err = mbedtls_mpi_write_binary(&ctx_server->Q.X,
+                                        cur_session->device_pubkey,
+                                        PUBLIC_KEY_LEN);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+    flip_endian(cur_session->device_pubkey, PUBLIC_KEY_LEN);
+
+    memcpy(cur_session->client_pubkey, in->sc0->client_pubkey.data, PUBLIC_KEY_LEN);
+
+    uint8_t *dev_pubkey = cur_session->device_pubkey;
+    uint8_t *cli_pubkey = cur_session->client_pubkey;
+    hexdump("Device pubkey", dev_pubkey, PUBLIC_KEY_LEN);
+    hexdump("Client pubkey", cli_pubkey, PUBLIC_KEY_LEN);
+
+    mbed_err = mbedtls_mpi_lset(&ctx_server->Qp.Z, 1);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_mpi_lset with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+
+    flip_endian(cur_session->client_pubkey, PUBLIC_KEY_LEN);
+    mbed_err = mbedtls_mpi_read_binary(&ctx_server->Qp.X, cli_pubkey, PUBLIC_KEY_LEN);
+    flip_endian(cur_session->client_pubkey, PUBLIC_KEY_LEN);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_mpi_read_binary with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+
+    mbed_err = mbedtls_ecdh_compute_shared(&ctx_server->grp, &ctx_server->z, &ctx_server->Qp,
+                                           &ctx_server->d, mbedtls_ctr_drbg_random, ctr_drbg);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_ecdh_compute_shared with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+
+    mbed_err = mbedtls_mpi_write_binary(&ctx_server->z, cur_session->sym_key, PUBLIC_KEY_LEN);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+    flip_endian(cur_session->sym_key, PUBLIC_KEY_LEN);
+
+    if (pop != NULL && pop->data != NULL && pop->len != 0) {
+        ESP_LOGD(TAG, "Adding proof of possession");
+        uint8_t sha_out[PUBLIC_KEY_LEN];
+
+        mbed_err = mbedtls_sha256_ret((const unsigned char *) pop->data, pop->len, sha_out, 0);
+        if (mbed_err != 0) {
+            ESP_LOGE(TAG, "Failed at mbedtls_sha256_ret with error code : -0x%x", -mbed_err);
+            ret = ESP_FAIL;
+            goto exit_cmd0;
+        }
+
+        for (int i = 0; i < PUBLIC_KEY_LEN; i++) {
+            cur_session->sym_key[i] ^= sha_out[i];
+        }
+    }
+
+    hexdump("Shared key", cur_session->sym_key, PUBLIC_KEY_LEN);
+
+    mbed_err = mbedtls_ctr_drbg_random(ctr_drbg, cur_session->rand, SZ_RANDOM);
+    if (mbed_err != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_ctr_drbg_random with error code : -0x%x", -mbed_err);
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+
+    hexdump("Device random", cur_session->rand, SZ_RANDOM);
+
+    Sec1Payload *out = (Sec1Payload *) malloc(sizeof(Sec1Payload));
+    SessionResp0 *out_resp = (SessionResp0 *) malloc(sizeof(SessionResp0));
+    if (!out || !out_resp) {
+        ESP_LOGE(TAG, "Error allocating memory for response");
+        ret = ESP_FAIL;
+        goto exit_cmd0;
+    }
+
+    sec1_payload__init(out);
+    session_resp0__init(out_resp);
+
+    out_resp->status = STATUS__Success;
+
+    out_resp->device_pubkey.data = dev_pubkey;
+    out_resp->device_pubkey.len = PUBLIC_KEY_LEN;
+
+    out_resp->device_random.data = (uint8_t *) cur_session->rand;
+    out_resp->device_random.len = SZ_RANDOM;
+
+    out->msg = SEC1_MSG_TYPE__Session_Response0;
+    out->payload_case = SEC1_PAYLOAD__PAYLOAD_SR0;
+    out->sr0 = out_resp;
+
+    resp->proto_case = SESSION_DATA__PROTO_SEC1;
+    resp->sec1 = out;
+
+    ESP_LOGD(TAG, "Session setup phase1 done");
+    ret = ESP_OK;
+
+exit_cmd0:
+    mbedtls_ecdh_free(ctx_server);
+    free(ctx_server);
+
+    mbedtls_ctr_drbg_free(ctr_drbg);
+    free(ctr_drbg);
+
+    mbedtls_entropy_free(entropy);
+    free(entropy);
+
+    return ret;
+}
+
+static esp_err_t sec1_session_setup(uint32_t session_id,
+                                    SessionData *req, SessionData *resp,
+                                    const protocomm_security_pop_t *pop)
+{
+    Sec1Payload *in = (Sec1Payload *) req->sec1;
+    esp_err_t ret;
+
+    switch (in->msg) {
+        case SEC1_MSG_TYPE__Session_Command0:
+            ret = handle_session_command0(session_id, req, resp, pop);
+            break;
+        case SEC1_MSG_TYPE__Session_Command1:
+            ret = handle_session_command1(session_id, req, resp);
+            break;
+        default:
+            ESP_LOGE(TAG, "Invalid security message type");
+            ret = ESP_ERR_INVALID_ARG;
+    }
+
+    return ret;
+
+}
+
+static void sec1_session_setup_cleanup(uint32_t session_id, SessionData *resp)
+{
+    Sec1Payload *out = resp->sec1;
+
+    if (!out) {
+        return;
+    }
+
+    switch (out->msg) {
+        case SEC1_MSG_TYPE__Session_Response0:
+            {
+                SessionResp0 *out_resp0 = out->sr0;
+                if (out_resp0) {
+                    free(out_resp0);
+                }
+                break;
+            }
+        case SEC1_MSG_TYPE__Session_Response1:
+            {
+                SessionResp1 *out_resp1 = out->sr1;
+                if (out_resp1) {
+                    free(out_resp1->device_verify_data.data);
+                    free(out_resp1);
+                }
+                break;
+            }
+        default:
+            break;
+    }
+    free(out);
+
+    return;
+}
+
+static esp_err_t sec1_init()
+{
+    return ESP_OK;
+}
+
+static esp_err_t sec1_cleanup()
+{
+    if (cur_session) {
+        ESP_LOGD(TAG, "Closing current session with id %u", cur_session->id);
+        mbedtls_aes_free(&cur_session->ctx_aes);
+        bzero(cur_session, sizeof(session_t));
+        free(cur_session);
+        cur_session = NULL;
+    }
+    return ESP_OK;
+}
+
+static esp_err_t sec1_new_session(uint32_t session_id)
+{
+    if (cur_session && cur_session->id != session_id) {
+        ESP_LOGE(TAG, "Closing old session with id %u", cur_session->id);
+        sec1_cleanup();
+    } else if (cur_session && cur_session->id == session_id) {
+        return ESP_OK;
+    }
+
+    cur_session = (session_t *) calloc(1, sizeof(session_t));
+    if (!cur_session) {
+        ESP_LOGE(TAG, "Error allocating session structure");
+        return ESP_ERR_NO_MEM;
+    }
+
+    cur_session->id = session_id;
+    return ESP_OK;
+}
+
+static esp_err_t sec1_close_session(uint32_t session_id)
+{
+    if (!cur_session || cur_session->id != session_id) {
+        ESP_LOGE(TAG, "Attempt to close invalid session");
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    bzero(cur_session, sizeof(session_t));
+    free(cur_session);
+    cur_session = NULL;
+    return ESP_OK;
+}
+
+static esp_err_t sec1_decrypt(uint32_t session_id,
+                              const uint8_t *inbuf, ssize_t inlen,
+                              uint8_t *outbuf, ssize_t *outlen)
+{
+    if (*outlen < inlen) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    if (!cur_session || cur_session->id != session_id) {
+        return ESP_ERR_INVALID_STATE;
+    }
+    *outlen = inlen;
+    int ret = mbedtls_aes_crypt_ctr(&cur_session->ctx_aes, inlen, &cur_session->nc_off,
+                                    cur_session->rand, cur_session->stb, inbuf, outbuf);
+    if (ret != 0) {
+        ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with error code : %d", ret);
+        return ESP_FAIL;
+    }
+    return ESP_OK;
+}
+
+static esp_err_t sec1_req_handler(const protocomm_security_pop_t *pop, uint32_t session_id,
+                                  const uint8_t *inbuf, ssize_t inlen,
+                                  uint8_t **outbuf, ssize_t *outlen,
+                                  void *priv_data)
+{
+    SessionData *req;
+    SessionData resp;
+    esp_err_t ret;
+
+    req = session_data__unpack(NULL, inlen, inbuf);
+    if (!req) {
+        ESP_LOGE(TAG, "Unable to unpack setup_req");
+        return ESP_ERR_INVALID_ARG;
+    }
+    if (req->sec_ver != protocomm_security1.ver) {
+        ESP_LOGE(TAG, "Security version mismatch. Closing connection");
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    session_data__init(&resp);
+    ret = sec1_session_setup(session_id, req, &resp, pop);
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "Session setup error %d", ret);
+        return ESP_FAIL;
+    }
+
+    session_data__free_unpacked(req, NULL);
+
+    resp.sec_ver = req->sec_ver;
+
+    *outlen = session_data__get_packed_size(&resp);
+    *outbuf = (uint8_t *) malloc(*outlen);
+    if (!*outbuf) {
+        ESP_LOGE(TAG, "System out of memory");
+        return ESP_ERR_NO_MEM;
+    }
+    session_data__pack(&resp, *outbuf);
+
+    sec1_session_setup_cleanup(session_id, &resp);
+    return ESP_OK;
+}
+
+const protocomm_security_t protocomm_security1 = {
+    .ver = 1,
+    .init = sec1_init,
+    .cleanup = sec1_cleanup,
+    .new_transport_session = sec1_new_session,
+    .close_transport_session = sec1_close_session,
+    .security_req_handler = sec1_req_handler,
+    .encrypt = sec1_decrypt, /* Encrypt == decrypt for AES-CTR */
+    .decrypt = sec1_decrypt,
+};