Protocomm : Added component core for protocol communication

* 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>
This commit is contained in:
Amey Inamdar 2018-07-30 21:33:10 +05:30 committed by Anurag Kar
parent 3ba8eed60a
commit d0c777b2e1
14 changed files with 1595 additions and 0 deletions

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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 */
}
}

View file

@ -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 */
}
}

View file

@ -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 */
}
}

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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,
};

View file

@ -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,
};