mirror of https://github.com/arendst/Tasmota.git
support for apple homekit on esp32
This commit is contained in:
parent
0750dfb1c3
commit
9d64f0cfba
|
@ -0,0 +1,2 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
|
@ -0,0 +1,32 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Mixiaoxiao (Wang Bin)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,31 @@
|
|||
# Arduino-HomeKit-ESP
|
||||
Arduino library version of espressif's official [esp-homekit-sdk](https://github.com/espressif/esp-homekit-sdk).
|
||||
|
||||
Currently, only for ESP32 with hardware acceleration. ESP32-S2, ESP32-C3 and future IDF based chips are all on the way.
|
||||
|
||||
Tested on my ESP32 board, works fine.
|
||||
|
||||
The performance is awesome!!!
|
||||
|
||||
The serial log is [here](https://raw.github.com/Mixiaoxiao/Arduino-HomeKit-ESP/master/extras/SerialLog.txt)
|
||||
|
||||
## Setup code
|
||||
|
||||
``111-11-111``
|
||||
|
||||
## Manual Installation
|
||||
|
||||
Refer to the official guide: [Manual installation](https://www.arduino.cc/en/guide/libraries#toc5)
|
||||
Note: this library will not publish the release version for Arduino IDE.
|
||||
|
||||
|
||||
#### Manual Installation for Windows
|
||||
|
||||
1. Click on _"Clone or Download"_ button, then click _"[Download ZIP](https://github.com/Mixiaoxiao/Arduino-HomeKit-ESP/archive/master.zip)"_ on the page.
|
||||
1. Extract the contents of the downloaded zip file.
|
||||
1. Rename the extracted folder to _"Arduino-HomeKit-ESP"_.
|
||||
1. Move this folder to your libraries directory. (under windows: `C:\Users\<USERNAME>\Documents\Arduino\libraries\`)
|
||||
1. Restart your Arduino IDE.
|
||||
1. Check out the examples.
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
Boot OK
|
||||
WiFi connecting...
|
||||
...............
|
||||
WiFi connected, IP: 192.168.6.134
|
||||
[ 1657] Keystore initialised
|
||||
[ 1662] Accessory is not Paired with any controller
|
||||
[ 1664] Database initialised. Accessory Device ID: 90:60:09:AB:F0:1D
|
||||
[ 1664] HAP Initialization succeeded. Version : 4.0
|
||||
[ 1670] MFi auth not supported. Falling back to HAP_MFI_AUTH_NONE
|
||||
[ 1675] Setup ID: ES32
|
||||
[ 1681] HAP Main Loop Started
|
||||
[ 1684] mDNS initialised
|
||||
[ 1684] Registering HomeKit web handlers
|
||||
[ 1687] Announcing _hap._tcp mDNS service
|
||||
[ 10032] ######## Starting Pair Setup ########
|
||||
[ 10032] Pair Setup M1 Received
|
||||
[ 12073] Pair Setup M2 Successful
|
||||
[ 12501] Pair Setup M3 Received
|
||||
[ 14540] Using pair-setup without MFi.
|
||||
[ 14540] Pair Setup M4 Successful
|
||||
[ 14929] Pair Setup M5 Received
|
||||
[ 15023] Pair Setup Successful for CF87DA6B-7078-44BB-AC04-27B6F2B26D37
|
||||
[ 15027] Updated state number to 11
|
||||
[ 15028] Cleaning Pair Setup Context
|
||||
[ 15032] Re-announcing _hap._tcp mDNS service
|
||||
[ 16205] ######## Starting Pair Verify ########
|
||||
[ 16205] Pair Verify M1 Received
|
||||
[ 16327] Pair Verify M2 Successful
|
||||
[ 16385] Pair Verify M3 Received
|
||||
[ 16444] HomeKit Session active
|
||||
[ 16445] Pair Verify Successful for CF87DA6B-7078-44BB-AC04-27B6F2B26D37
|
||||
[ 16781] Events Enabled for aid=1 iid=12
|
||||
[ 16781] Events Enabled for aid=1 iid=13
|
||||
[ 17080] Events Enabled for aid=1 iid=12
|
||||
[ 17080] Events Enabled for aid=1 iid=13
|
||||
[ 22311] ######## Starting Pair Verify ########
|
||||
[ 22311] Pair Verify M1 Received
|
||||
[ 22435] Pair Verify M2 Successful
|
||||
[ 22470] Pair Verify M3 Received
|
||||
[ 22528] HomeKit Session active
|
||||
[ 22528] Pair Verify Successful for CF87DA6B-7078-44BB-AC04-27B6F2B26D37
|
||||
[ 25069] Value Changed
|
||||
[ 26605] Value Changed
|
|
@ -0,0 +1,20 @@
|
|||
#include "Arduino.h"
|
||||
#include "wifi_info.h"
|
||||
#include "hap.h"
|
||||
|
||||
extern "C" void homekit_main();
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Boot OK");
|
||||
wifi_connect();
|
||||
// Useful apis: (see hap.h)
|
||||
// hap_reset_to_factory();
|
||||
// hap_reset_homekit_data();
|
||||
// hap_reset_pairings();
|
||||
homekit_main();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
/* HomeKit Smart Outlet Example
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#include <hap.h>
|
||||
|
||||
#include <hap_apple_servs.h>
|
||||
#include <hap_apple_chars.h>
|
||||
|
||||
//#include <app_wifi.h>
|
||||
//#include <app_hap_setup_payload.h>
|
||||
|
||||
static const char *TAG = "HAP outlet";
|
||||
|
||||
#define SMART_OUTLET_TASK_PRIORITY 1
|
||||
#define SMART_OUTLET_TASK_STACKSIZE 4 * 1024
|
||||
#define SMART_OUTLET_TASK_NAME "hap_outlet"
|
||||
|
||||
//#define OUTLET_IN_USE_GPIO GPIO_NUM_0
|
||||
#define OUTLET_IN_USE_GPIO -1
|
||||
|
||||
#define ESP_INTR_FLAG_DEFAULT 0
|
||||
|
||||
static xQueueHandle s_esp_evt_queue = NULL;
|
||||
/**
|
||||
* @brief the recover outlet in use gpio interrupt function
|
||||
*/
|
||||
static void IRAM_ATTR outlet_in_use_isr(void* arg)
|
||||
{
|
||||
uint32_t gpio_num = (uint32_t) arg;
|
||||
xQueueSendFromISR(s_esp_evt_queue, &gpio_num, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a GPIO Pin for Outlet in Use Detection
|
||||
*/
|
||||
static void outlet_in_use_key_init(uint32_t key_gpio_pin)
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
/* Interrupt for both the edges */
|
||||
io_conf.intr_type = GPIO_INTR_ANYEDGE;
|
||||
/* Bit mask of the pins */
|
||||
io_conf.pin_bit_mask = 1 << key_gpio_pin;
|
||||
/* Set as input mode */
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
/* Enable internal pull-up */
|
||||
io_conf.pull_up_en = 1;
|
||||
/* Disable internal pull-down */
|
||||
io_conf.pull_down_en = 0;
|
||||
/* Set the GPIO configuration */
|
||||
gpio_config(&io_conf);
|
||||
|
||||
/* Install gpio isr service */
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
|
||||
/* Hook isr handler for specified gpio pin */
|
||||
gpio_isr_handler_add(key_gpio_pin, outlet_in_use_isr, (void*)key_gpio_pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Smart Outlet Hardware.Here, we just enebale the Outlet-In-Use detection.
|
||||
*/
|
||||
void smart_outlet_hardware_init(gpio_num_t gpio_num)
|
||||
{
|
||||
s_esp_evt_queue = xQueueCreate(2, sizeof(uint32_t));
|
||||
if (s_esp_evt_queue != NULL) {
|
||||
outlet_in_use_key_init(gpio_num);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mandatory identify routine for the accessory.
|
||||
* In a real accessory, something like LED blink should be implemented
|
||||
* got visual identification
|
||||
*/
|
||||
static int outlet_identify(hap_acc_t *ha)
|
||||
{
|
||||
ESP_LOGI(TAG, "Accessory identified");
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
/* A dummy callback for handling a write on the "On" characteristic of Outlet.
|
||||
* In an actual accessory, this should control the hardware
|
||||
*/
|
||||
static int outlet_write(hap_write_data_t write_data[], int count,
|
||||
void *serv_priv, void *write_priv)
|
||||
{
|
||||
int i, ret = HAP_SUCCESS;
|
||||
hap_write_data_t *write;
|
||||
for (i = 0; i < count; i++) {
|
||||
write = &write_data[i];
|
||||
if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ON)) {
|
||||
//ESP_LOGI(TAG, "Received Write. Outlet %s", write->val.b ? "On" : "Off");
|
||||
ESP_LOG_LEVEL(ESP_LOG_INFO, TAG, "Received Write. Outlet %s", write->val.b ? "On" : "Off");
|
||||
/* TODO: Control Actual Hardware */
|
||||
hap_char_update_val(write->hc, &(write->val));
|
||||
*(write->status) = HAP_STATUS_SUCCESS;
|
||||
} else {
|
||||
*(write->status) = HAP_STATUS_RES_ABSENT;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*The main thread for handling the Smart Outlet Accessory */
|
||||
static void smart_outlet_thread_entry(void *p)
|
||||
{
|
||||
hap_acc_t *accessory;
|
||||
hap_serv_t *service;
|
||||
|
||||
/* Initialize the HAP core */
|
||||
hap_init(HAP_TRANSPORT_WIFI);
|
||||
|
||||
/* Initialise the mandatory parameters for Accessory which will be added as
|
||||
* the mandatory services internally
|
||||
*/
|
||||
hap_acc_cfg_t cfg = {
|
||||
.name = "Esp-Smart-Outlet",
|
||||
.manufacturer = "Espressif",
|
||||
.model = "EspSmartOutlet01",
|
||||
.serial_num = "001122334455",
|
||||
.fw_rev = "0.9.0",
|
||||
.hw_rev = NULL,
|
||||
.pv = "1.1.0",
|
||||
.identify_routine = outlet_identify,
|
||||
.cid = HAP_CID_OUTLET,
|
||||
};
|
||||
/* Create accessory object */
|
||||
accessory = hap_acc_create(&cfg);
|
||||
|
||||
/* Add a dummy Product Data */
|
||||
uint8_t product_data[] = {'E','S','P','3','2','H','A','P'};
|
||||
hap_acc_add_product_data(accessory, product_data, sizeof(product_data));
|
||||
|
||||
/* Create the Outlet Service. Include the "name" since this is a user visible service */
|
||||
service = hap_serv_outlet_create(false, false);
|
||||
hap_serv_add_char(service, hap_char_name_create("My Smart Outlet"));
|
||||
|
||||
/* Get pointer to the outlet in use characteristic which we need to monitor for state changes */
|
||||
hap_char_t *outlet_in_use = hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_OUTLET_IN_USE);
|
||||
|
||||
/* Set the write callback for the service */
|
||||
hap_serv_set_write_cb(service, outlet_write);
|
||||
|
||||
/* Add the Outlet Service to the Accessory Object */
|
||||
hap_acc_add_serv(accessory, service);
|
||||
|
||||
/* Add the Accessory to the HomeKit Database */
|
||||
hap_add_accessory(accessory);
|
||||
|
||||
/* Initialize the appliance specific hardware. This enables out-in-use detection */
|
||||
smart_outlet_hardware_init(OUTLET_IN_USE_GPIO);
|
||||
|
||||
/* For production accessories, the setup code shouldn't be programmed on to
|
||||
* the device. Instead, the setup info, derived from the setup code must
|
||||
* be used. Use the factory_nvs_gen utility to generate this data and then
|
||||
* flash it into the factory NVS partition.
|
||||
*
|
||||
* By default, the setup ID and setup info will be read from the factory_nvs
|
||||
* Flash partition and so, is not required to set here explicitly.
|
||||
*
|
||||
* However, for testing purpose, this can be overridden by using hap_set_setup_code()
|
||||
* and hap_set_setup_id() APIs, as has been done here.
|
||||
*/
|
||||
hap_set_setup_code("111-11-111");
|
||||
hap_set_setup_id("ES32");
|
||||
#ifdef CONFIG_EXAMPLE_USE_HARDCODED_SETUP_CODE
|
||||
/* Unique Setup code of the format xxx-xx-xxx. Default: 111-22-333 */
|
||||
hap_set_setup_code(CONFIG_EXAMPLE_SETUP_CODE);
|
||||
/* Unique four character Setup Id. Default: ES32 */
|
||||
hap_set_setup_id(CONFIG_EXAMPLE_SETUP_ID);
|
||||
#ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING
|
||||
app_hap_setup_payload(CONFIG_EXAMPLE_SETUP_CODE, CONFIG_EXAMPLE_SETUP_ID, true, cfg.cid);
|
||||
#else
|
||||
app_hap_setup_payload(CONFIG_EXAMPLE_SETUP_CODE, CONFIG_EXAMPLE_SETUP_ID, false, cfg.cid);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Enable Hardware MFi authentication (applicable only for MFi variant of SDK) */
|
||||
hap_enable_mfi_auth(HAP_MFI_AUTH_HW);
|
||||
|
||||
/* Initialize Wi-Fi */
|
||||
//app_wifi_init();
|
||||
|
||||
/* After all the initializations are done, start the HAP core */
|
||||
hap_start();
|
||||
/* Start Wi-Fi */
|
||||
//app_wifi_start(portMAX_DELAY);
|
||||
|
||||
uint32_t io_num = OUTLET_IN_USE_GPIO;
|
||||
hap_val_t appliance_value = {
|
||||
.b = true,
|
||||
};
|
||||
/* Listen for Outlet-In-Use state change events. Other read/write functionality will be handled
|
||||
* by the HAP Core.
|
||||
* When the Outlet in Use GPIO goes low, it means Outlet is not in use.
|
||||
* When the Outlet in Use GPIO goes high, it means Outlet is in use.
|
||||
* Applications can define own logic as per their hardware.
|
||||
*/
|
||||
while (1) {
|
||||
if (xQueueReceive(s_esp_evt_queue, &io_num, portMAX_DELAY) == pdFALSE) {
|
||||
ESP_LOGI(TAG, "Outlet-In-Use trigger FAIL");
|
||||
} else {
|
||||
appliance_value.b = gpio_get_level(io_num);
|
||||
/* If any state change is detected, update the Outlet In Use characteristic value */
|
||||
hap_char_update_val(outlet_in_use, &appliance_value);
|
||||
ESP_LOGI(TAG, "Outlet-In-Use triggered [%d]", appliance_value.b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void homekit_main()
|
||||
{
|
||||
/* Create the application thread */
|
||||
xTaskCreate(smart_outlet_thread_entry, SMART_OUTLET_TASK_NAME, SMART_OUTLET_TASK_STACKSIZE,
|
||||
NULL, SMART_OUTLET_TASK_PRIORITY, NULL);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* wifi_info.h
|
||||
*
|
||||
* Created on: 2020-05-15
|
||||
* Author: Mixiaoxiao (Wang Bin)
|
||||
*/
|
||||
|
||||
#ifndef WIFI_INFO_H_
|
||||
#define WIFI_INFO_H_
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#elif defined(ESP32)
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
const char *ssid = "your-ssid";
|
||||
const char *password = "your-password";
|
||||
|
||||
void wifi_connect() {
|
||||
WiFi.persistent(false);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi connecting...");
|
||||
while (!WiFi.isConnected()) {
|
||||
delay(100);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.print("\n");
|
||||
Serial.printf("WiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
}
|
||||
|
||||
#endif /* WIFI_INFO_H_ */
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* _esp_hap_config.h
|
||||
*
|
||||
* Created on: 2020-11-23
|
||||
* Author: Mixiaoxiao
|
||||
*/
|
||||
|
||||
#ifndef ESP_HAP_CONFIG_H_
|
||||
#define ESP_HAP_CONFIG_H_
|
||||
|
||||
#define HAP_SDK_VER "4.0"
|
||||
#define MFI_VER HAP_SDK_VER
|
||||
|
||||
#define CONFIG_HAP_HTTP_STACK_SIZE 12288
|
||||
#define CONFIG_HAP_HTTP_SERVER_PORT 5556 // 80 for normal webserver
|
||||
#define CONFIG_HAP_HTTP_CONTROL_PORT 32859
|
||||
#define CONFIG_HAP_HTTP_MAX_OPEN_SOCKETS 6
|
||||
#define CONFIG_HAP_HTTP_MAX_URI_HANDLERS 16
|
||||
|
||||
#endif /* ESP_HAP_CONFIG_H_ */
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Base36 PostgreSQL input/output function for bigint
|
||||
*
|
||||
* Author: Dimitri Fontaine <dimitri@2ndQuadrant.fr>
|
||||
*
|
||||
* Taken from https://github.com/dimitri/base36/blob/master/base36.c
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define BASE36_LENGTH 13
|
||||
|
||||
typedef long long int base36;
|
||||
|
||||
static int base36_digits[36] =
|
||||
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
||||
'U', 'V', 'W', 'X', 'Y', 'Z'
|
||||
};
|
||||
|
||||
static base36 base36_powers[BASE36_LENGTH] =
|
||||
{
|
||||
1ULL,
|
||||
36ULL,
|
||||
1296ULL,
|
||||
46656ULL,
|
||||
1679616ULL,
|
||||
60466176ULL,
|
||||
2176782336ULL,
|
||||
78364164096ULL,
|
||||
2821109907456ULL,
|
||||
101559956668416ULL,
|
||||
3656158440062976ULL,
|
||||
131621703842267136ULL,
|
||||
4738381338321616896ULL
|
||||
};
|
||||
|
||||
static inline
|
||||
char *base36_to_str(base36 c)
|
||||
{
|
||||
int i, d, p = 0;
|
||||
base36 m = c;
|
||||
bool discard = true;
|
||||
char *str = calloc((BASE36_LENGTH + 1), sizeof(char));
|
||||
|
||||
for(i=BASE36_LENGTH-1; i>=0; i--)
|
||||
{
|
||||
d = m / base36_powers[i];
|
||||
m = m - base36_powers[i] * d;
|
||||
|
||||
discard = discard && (d == 0 && i >0);
|
||||
|
||||
if( !discard )
|
||||
str[p++] = base36_digits[d];
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
// Copyright 2020 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 <stdint.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* Functions to convert Little Endian byte stream to
|
||||
* uint16, uint32 and uint64
|
||||
*/
|
||||
uint16_t get_u16_le(const void *val_ptr)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)val_ptr;
|
||||
uint16_t val;
|
||||
|
||||
val = (uint16_t)p[0];
|
||||
val |= (uint16_t)p[1] << 8;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t get_u32_le(const void *val_ptr)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)val_ptr;
|
||||
uint32_t val;
|
||||
|
||||
val = (uint32_t)p[0];
|
||||
val |= (uint32_t)p[1] << 8;
|
||||
val |= (uint32_t)p[2] << 16;
|
||||
val |= (uint32_t)p[3] << 24;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
uint64_t get_u64_le(const void *val_ptr)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)val_ptr;
|
||||
uint64_t val;
|
||||
|
||||
val = (uint64_t)p[0];
|
||||
val |= (uint64_t)p[1] << 8;
|
||||
val |= (uint64_t)p[2] << 16;
|
||||
val |= (uint64_t)p[3] << 24;
|
||||
val |= (uint64_t)p[4] << 32;
|
||||
val |= (uint64_t)p[5] << 40;
|
||||
val |= (uint64_t)p[6] << 48;
|
||||
val |= (uint64_t)p[7] << 56;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Functions to convert Big Endian byte stream to
|
||||
* uint16, uint32 and uint64
|
||||
*/
|
||||
uint16_t get_u16_be(const void *val_ptr)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)val_ptr;
|
||||
uint16_t val;
|
||||
|
||||
val = (uint16_t)p[0] << 8;
|
||||
val |= (uint16_t)p[1];
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t get_u32_be(const void *val_ptr)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)val_ptr;
|
||||
uint32_t val;
|
||||
|
||||
val = (uint32_t)p[0] << 24;
|
||||
val |= (uint32_t)p[1] << 16;
|
||||
val |= (uint32_t)p[2] << 8;
|
||||
val |= (uint32_t)p[3];
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
uint64_t get_u64_be(const void *val_ptr)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)val_ptr;
|
||||
uint64_t val;
|
||||
|
||||
val = (uint64_t)p[0] << 56;
|
||||
val |= (uint64_t)p[1] << 48;
|
||||
val |= (uint64_t)p[2] << 40;
|
||||
val |= (uint64_t)p[3] << 32;
|
||||
val |= (uint64_t)p[4] << 24;
|
||||
val |= (uint64_t)p[5] << 16;
|
||||
val |= (uint64_t)p[6] << 8;
|
||||
val |= (uint64_t)p[7];
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Functions to convert uint16, uint32 and uint64
|
||||
* to Little Endian
|
||||
*/
|
||||
void put_u16_le(void *val_ptr, const uint16_t val)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)val_ptr;
|
||||
|
||||
p[0] = (uint8_t)val & 0xff;
|
||||
p[1] = (uint8_t)(val >> 8) & 0xff;
|
||||
}
|
||||
|
||||
void put_u32_le(void *val_ptr, const uint32_t val)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)val_ptr;
|
||||
|
||||
p[0] = (uint8_t)val & 0xff;
|
||||
p[1] = (uint8_t)(val >> 8) & 0xff;
|
||||
p[2] = (uint8_t)(val >> 16) & 0xff;
|
||||
p[3] = (uint8_t)(val >> 24) & 0xff;
|
||||
}
|
||||
|
||||
void put_u64_le(void *val_ptr, const uint64_t val)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)val_ptr;
|
||||
|
||||
p[0] = (uint8_t)val & 0xff;
|
||||
p[1] = (uint8_t)(val >> 8) & 0xff;
|
||||
p[2] = (uint8_t)(val >> 16) & 0xff;
|
||||
p[3] = (uint8_t)(val >> 24) & 0xff;
|
||||
p[4] = (uint8_t)(val >> 32) & 0xff;
|
||||
p[5] = (uint8_t)(val >> 40) & 0xff;
|
||||
p[6] = (uint8_t)(val >> 48) & 0xff;
|
||||
p[7] = (uint8_t)(val >> 56) & 0xff;
|
||||
}
|
||||
|
||||
/* Functions to convert uint16, uint32 and uint64
|
||||
* to Big Endian
|
||||
*/
|
||||
void put_u16_be(void *val_ptr, const uint16_t val)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)val_ptr;
|
||||
|
||||
p[0] = (uint8_t)(val >> 8) & 0xff;
|
||||
p[1] = (uint8_t)val & 0xff;
|
||||
}
|
||||
|
||||
void put_u32_be(void *val_ptr, const uint32_t val)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)val_ptr;
|
||||
|
||||
p[0] = (uint8_t)((val >> 24) & 0xff);
|
||||
p[1] = (uint8_t)((val >> 16) & 0xff);
|
||||
p[2] = (uint8_t)((val >> 8) & 0xff);
|
||||
p[3] = (uint8_t)(val & 0xff);
|
||||
}
|
||||
|
||||
void put_u64_be(void *val_ptr, const uint64_t val)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)val_ptr;
|
||||
|
||||
p[0] = (uint8_t)(val >> 56) & 0xff;
|
||||
p[1] = (uint8_t)(val >> 48) & 0xff;
|
||||
p[2] = (uint8_t)(val >> 40) & 0xff;
|
||||
p[3] = (uint8_t)(val >> 32) & 0xff;
|
||||
p[4] = (uint8_t)(val >> 24) & 0xff;
|
||||
p[5] = (uint8_t)(val >> 16) & 0xff;
|
||||
p[6] = (uint8_t)(val >> 8) & 0xff;
|
||||
p[7] = (uint8_t)val & 0xff;
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2020 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.
|
||||
/**
|
||||
* \file byte_convert.h
|
||||
* \brief Conversion between integers and byte streams
|
||||
*
|
||||
* This module offers APIs to convert 16bit, 32bit and 64 bit unsigned integers
|
||||
* into Little/Big Endian byte streams
|
||||
*/
|
||||
#ifndef _BYTE_CONVERT_H
|
||||
#define _BYTE_CONVERT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** Little Endian to uint16 Conversion
|
||||
* Get a uint16 integer from a little Endian byte stream
|
||||
*
|
||||
* \param[in] val_ptr Pointer to the 2 byte stream
|
||||
*
|
||||
* \return The converted uint16 integer
|
||||
*/
|
||||
uint16_t get_u16_le(const void *val_ptr);
|
||||
|
||||
/** Little Endian to uint32 Conversion
|
||||
* Get a uint32 integer from a little Endian byte stream
|
||||
*
|
||||
* \param[in] val_ptr Pointer to the 4 byte stream
|
||||
*
|
||||
* \return The converted uint32 integer
|
||||
*/
|
||||
uint32_t get_u32_le(const void *val_ptr);
|
||||
|
||||
/** Little Endian to uint64 Conversion
|
||||
* Get a uint64 integer from a little Endian byte stream
|
||||
*
|
||||
* \param[in] val_ptr Pointer to the 8 byte stream
|
||||
*
|
||||
* \return The converted uint32 integer
|
||||
*/
|
||||
uint64_t get_u64_le(const void *val_ptr);
|
||||
|
||||
/** Big Endian to uint16 Conversion
|
||||
* Get a uint16 integer from a Big Endian byte stream
|
||||
*
|
||||
* \param[in] val_ptr Pointer to the 2 byte stream
|
||||
*
|
||||
* \return The converted uint16 integer
|
||||
*/
|
||||
uint16_t get_u16_be(const void *val_ptr);
|
||||
|
||||
/** Big Endian to uint32 Conversion
|
||||
* Get a uint32 integer from a Big Endian byte stream
|
||||
*
|
||||
* \param[in] val_ptr Pointer to the 4 byte stream
|
||||
*
|
||||
* \return The converted uint32 integer
|
||||
*/
|
||||
uint32_t get_u32_be(const void *val_ptr);
|
||||
|
||||
/** Big Endian to uint64 Conversion
|
||||
* Get a uint64 integer from a Big Endian byte stream
|
||||
*
|
||||
* \param[in] val_ptr Pointer to the 8 byte stream
|
||||
*
|
||||
* \return The converted uint16 integer
|
||||
*/
|
||||
uint64_t get_u64_be(const void *val_ptr);
|
||||
|
||||
/** Uint16 to Little Endian Conversion
|
||||
*
|
||||
* Put a uint16 integer into a Little Endian byte stream
|
||||
*
|
||||
* \param[out] Pointer to a 2 byte stream that will be filled with the value
|
||||
*
|
||||
* \param[in] The uint16 integer
|
||||
*/
|
||||
void put_u16_le(void *val_ptr, const uint16_t val);
|
||||
|
||||
/** Uint32 to Little Endian Conversion
|
||||
*
|
||||
* Put a uint32 integer into a Little Endian byte stream
|
||||
*
|
||||
* \param[out] Pointer to a 4 byte stream that will be filled with the value
|
||||
*
|
||||
* \param[in] The uint32 integer
|
||||
*/
|
||||
void put_u32_le(void *val_ptr, const uint32_t val);
|
||||
|
||||
/** Uint64 to Little Endian Conversion
|
||||
*
|
||||
* Put a uint64 integer into a Little Endian byte stream
|
||||
*
|
||||
* \param[out] Pointer to a 8 byte stream that will be filled with the value
|
||||
*
|
||||
* \param[in] The uint64 integer
|
||||
*/
|
||||
void put_u64_le(void *val_ptr, const uint64_t val);
|
||||
|
||||
/** Uint16 to Big Endian Conversion
|
||||
*
|
||||
* Put a uint16 integer into a Big Endian byte stream
|
||||
*
|
||||
* \param[out] Pointer to a 2 byte stream that will be filled with the value
|
||||
*
|
||||
* \param[in] The uint16 integer
|
||||
*/
|
||||
void put_u16_be(void *val_ptr, const uint16_t val);
|
||||
|
||||
/** Uint32 to Big Endian Conversion
|
||||
*
|
||||
* Put a uint32 integer into a Big Endian byte stream
|
||||
*
|
||||
* \param[out] Pointer to a 4 byte stream that will be filled with the value
|
||||
*
|
||||
* \param[in] The uint32 integer
|
||||
*/
|
||||
void put_u32_be(void *val_ptr, const uint32_t val);
|
||||
|
||||
/** Uint64 to Big Endian Conversion
|
||||
*
|
||||
* Put a uint64 integer into a Big Endian byte stream
|
||||
*
|
||||
* \param[out] Pointer to a 8 byte stream that will be filled with the value
|
||||
*
|
||||
* \param[in] The uint64 integer
|
||||
*/
|
||||
void put_u64_be(void *val_ptr, const uint64_t val);
|
||||
#endif /* _BYTE_CONVERT_H */
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <hap_platform_memory.h>
|
||||
#include <esp_hap_acc.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
#include <hap_apple_servs.h>
|
||||
#include <hap_apple_chars.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_hap_keystore.h>
|
||||
#include <esp_hap_main.h>
|
||||
|
||||
/* Primary Accessory Pointer */
|
||||
static __hap_acc_t *primary_acc;
|
||||
|
||||
/*****************************************************************************************************/
|
||||
|
||||
hap_acc_t *hap_get_first_acc()
|
||||
{
|
||||
return (hap_acc_t *)primary_acc;
|
||||
}
|
||||
|
||||
hap_acc_t *hap_acc_get_next(hap_acc_t *ha)
|
||||
{
|
||||
if (!ha)
|
||||
return NULL;
|
||||
|
||||
__hap_acc_t *_ha = (__hap_acc_t *)ha;
|
||||
return (hap_acc_t *)_ha->next;
|
||||
}
|
||||
/* Service write callback to handle "Identify" */
|
||||
static int hap_acc_info_write(hap_write_data_t write_data[], int count,
|
||||
void *serv_priv, void *write_priv)
|
||||
{
|
||||
int i;
|
||||
__hap_char_t *_hc;
|
||||
for (i = 0; i < count; i++) {
|
||||
_hc = (__hap_char_t *)write_data[i].hc;
|
||||
if (!strcmp(_hc->type_uuid, HAP_CHAR_UUID_IDENTIFY)) {
|
||||
__hap_acc_t *_ha = (__hap_acc_t *)serv_priv;
|
||||
if (_ha) {
|
||||
_ha->identify_routine((hap_acc_t *)_ha);
|
||||
*(write_data->status) = HAP_STATUS_SUCCESS;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*(write_data->status) = HAP_STATUS_VAL_INVALID;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP create an accessory
|
||||
*/
|
||||
hap_acc_t *hap_acc_create(hap_acc_cfg_t *acc_cfg)
|
||||
{
|
||||
static bool first = true;
|
||||
int ret = 0;
|
||||
__hap_acc_t *_ha = hap_platform_memory_calloc(1, sizeof(__hap_acc_t));
|
||||
if (!_ha) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_ha->identify_routine = acc_cfg->identify_routine;
|
||||
_ha->next_iid = 1;
|
||||
|
||||
/* Add the Accessory Information Service internally */
|
||||
hap_serv_t *hs = hap_serv_create("3E");
|
||||
if (!hs) {
|
||||
goto acc_create_fail;
|
||||
}
|
||||
ret = hap_serv_add_char(hs, hap_char_bool_create(HAP_CHAR_UUID_IDENTIFY, HAP_CHAR_PERM_PW, false));
|
||||
ret |= hap_serv_add_char(hs, hap_char_string_create(HAP_CHAR_UUID_MANUFACTURER, HAP_CHAR_PERM_PR, acc_cfg->manufacturer));
|
||||
ret |= hap_serv_add_char(hs, hap_char_string_create(HAP_CHAR_UUID_MODEL, HAP_CHAR_PERM_PR, acc_cfg->model));
|
||||
ret |= hap_serv_add_char(hs, hap_char_string_create(HAP_CHAR_UUID_NAME, HAP_CHAR_PERM_PR, acc_cfg->name));
|
||||
ret |= hap_serv_add_char(hs, hap_char_string_create(HAP_CHAR_UUID_SERIAL_NUMBER, HAP_CHAR_PERM_PR, acc_cfg->serial_num));
|
||||
ret |= hap_serv_add_char(hs, hap_char_string_create(HAP_CHAR_UUID_FIRMWARE_REVISION, HAP_CHAR_PERM_PR, acc_cfg->fw_rev));
|
||||
if (acc_cfg->hw_rev) {
|
||||
ret |= hap_serv_add_char(hs, hap_char_string_create(HAP_CHAR_UUID_HARDWARE_REVISION, HAP_CHAR_PERM_PR, acc_cfg->hw_rev));
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
goto acc_create_fail;
|
||||
}
|
||||
|
||||
hap_serv_set_write_cb(hs, hap_acc_info_write);
|
||||
hap_serv_set_priv(hs,(void *)_ha);
|
||||
hap_acc_add_serv((hap_acc_t *)_ha, hs);
|
||||
|
||||
if (first) {
|
||||
/* Add the Procol Information Service Internally */
|
||||
hs = hap_serv_create("A2");
|
||||
if (!hs) {
|
||||
goto acc_create_fail;
|
||||
}
|
||||
ret = hap_serv_add_char(hs, hap_char_string_create("37", HAP_CHAR_PERM_PR, "1.1.0"));
|
||||
|
||||
if (ret) {
|
||||
goto acc_create_fail;
|
||||
}
|
||||
hap_acc_add_serv((hap_acc_t *)_ha, hs);
|
||||
hap_priv.cid = acc_cfg->cid;
|
||||
first = false;
|
||||
}
|
||||
|
||||
return (hap_acc_t *)_ha;
|
||||
|
||||
acc_create_fail:
|
||||
hap_acc_delete((hap_acc_t *)_ha);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hap_acc_add_accessory_flags(hap_acc_t *ha, uint32_t flags)
|
||||
{
|
||||
if (!ha) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_serv_t *hs = hap_acc_get_serv_by_uuid(ha, HAP_SERV_UUID_ACCESSORY_INFORMATION);
|
||||
if (!hs) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
return hap_serv_add_char(hs, hap_char_accessory_flags_create(flags));
|
||||
|
||||
}
|
||||
|
||||
int hap_acc_update_accessory_flags(hap_acc_t *ha, uint32_t flags)
|
||||
{
|
||||
if (!ha) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_serv_t *hs = hap_acc_get_serv_by_uuid(ha, HAP_SERV_UUID_ACCESSORY_INFORMATION);
|
||||
if (!hs) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_char_t *hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_ACCESSORY_FLAGS);
|
||||
if (!hc) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_val_t val = {
|
||||
.u = flags,
|
||||
};
|
||||
return hap_char_update_val(hc, &val);
|
||||
}
|
||||
|
||||
int hap_acc_add_product_data(hap_acc_t *ha, uint8_t *product_data, size_t data_size)
|
||||
{
|
||||
if (!ha) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_serv_t *hs = hap_acc_get_serv_by_uuid(ha, HAP_SERV_UUID_ACCESSORY_INFORMATION);
|
||||
if (!hs) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if (data_size != 8) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Product data size is not 8");
|
||||
}
|
||||
uint8_t *buf = calloc(1, data_size);
|
||||
if (!buf) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
memcpy(buf, product_data, data_size);
|
||||
hap_data_val_t data_val = {
|
||||
.buf = buf,
|
||||
.buflen = data_size
|
||||
};
|
||||
return hap_serv_add_char(hs, hap_char_product_data_create(&data_val));
|
||||
}
|
||||
|
||||
const hap_val_t *hap_get_product_data()
|
||||
{
|
||||
hap_char_t *acc_info = hap_acc_get_serv_by_uuid(hap_get_first_acc(), HAP_SERV_UUID_ACCESSORY_INFORMATION);
|
||||
if (acc_info) {
|
||||
hap_char_t *product_data = hap_serv_get_char_by_uuid(acc_info, HAP_CHAR_UUID_PRODUCT_DATA);
|
||||
if (product_data) {
|
||||
return hap_char_get_val(product_data);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* @brief check if accessory's AID matches the target AID
|
||||
*/
|
||||
bool hap_check_aid(__hap_acc_t *accessory, int32_t aid)
|
||||
{
|
||||
return accessory->aid == aid ? true : false;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_acc_get_first_serv(hap_acc_t *ha)
|
||||
{
|
||||
return ((__hap_acc_t *)ha)->servs;
|
||||
}
|
||||
/**
|
||||
* @brief get target service by it's type description string
|
||||
*/
|
||||
hap_serv_t *hap_acc_get_serv_by_iid(hap_acc_t *ha, int32_t iid)
|
||||
{
|
||||
if (!ha)
|
||||
return NULL;
|
||||
|
||||
hap_serv_t *hs;
|
||||
for (hs = hap_acc_get_first_serv(ha); hs; hs = hap_serv_get_next(hs)) {
|
||||
if (((__hap_serv_t *)hs)->iid == iid)
|
||||
return hs;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_acc_get_serv_by_uuid(hap_acc_t *ha, const char *uuid)
|
||||
{
|
||||
if (!ha)
|
||||
return NULL;
|
||||
|
||||
hap_serv_t *hs;
|
||||
for (hs = hap_acc_get_first_serv(ha); hs; hs = hap_serv_get_next(hs)) {
|
||||
if (!strcmp(((__hap_serv_t *)hs)->type_uuid, uuid))
|
||||
return hs;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get target characteristics by it's IID
|
||||
*/
|
||||
hap_char_t *hap_acc_get_char_by_iid(hap_acc_t *ha, int32_t iid)
|
||||
{
|
||||
if (!ha)
|
||||
return NULL;
|
||||
hap_serv_t *hs;
|
||||
hap_char_t *hc;
|
||||
for (hs = hap_acc_get_first_serv(ha); hs; hs = hap_serv_get_next(hs)) {
|
||||
hc = hap_serv_get_char_by_iid(hs, iid);
|
||||
if (hc)
|
||||
return hc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hap_acc_get_info(hap_acc_cfg_t *acc_cfg)
|
||||
{
|
||||
ESP_MFI_ASSERT(acc_cfg);
|
||||
hap_acc_t *ha = hap_get_first_acc();
|
||||
|
||||
ESP_MFI_ASSERT(ha);
|
||||
|
||||
hap_serv_t *hs = hap_acc_get_serv_by_uuid(ha, HAP_SERV_UUID_ACCESSORY_INFORMATION);
|
||||
|
||||
hap_char_t *hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_NAME);
|
||||
acc_cfg->name = ((__hap_char_t *)hc)->val.s;
|
||||
|
||||
hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_MODEL);
|
||||
acc_cfg->model = ((__hap_char_t *)hc)->val.s;
|
||||
|
||||
hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_MANUFACTURER);
|
||||
acc_cfg->manufacturer = ((__hap_char_t *)hc)->val.s;
|
||||
|
||||
hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_SERIAL_NUMBER);
|
||||
acc_cfg->serial_num = ((__hap_char_t *)hc)->val.s;
|
||||
|
||||
hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_FIRMWARE_REVISION);
|
||||
acc_cfg->fw_rev = ((__hap_char_t *)hc)->val.s;
|
||||
|
||||
hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_HARDWARE_REVISION);
|
||||
if (hc) {
|
||||
acc_cfg->hw_rev = ((__hap_char_t *)hc)->val.s;
|
||||
} else {
|
||||
acc_cfg->hw_rev = NULL;
|
||||
}
|
||||
|
||||
hs = hap_acc_get_serv_by_uuid(ha, HAP_SERV_UUID_PROTOCOL_INFORMATION);
|
||||
|
||||
hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_VERSION);
|
||||
acc_cfg->pv = ((__hap_char_t *)hc)->val.s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add a characteristics to a service
|
||||
*/
|
||||
int hap_acc_add_serv(hap_acc_t *ha, hap_serv_t *hs)
|
||||
{
|
||||
ESP_MFI_ASSERT(ha);
|
||||
ESP_MFI_ASSERT(hs);
|
||||
__hap_acc_t *_ha = (__hap_acc_t *)ha;
|
||||
__hap_serv_t *_hs = (__hap_serv_t *)hs;
|
||||
|
||||
if (_hs->parent) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Service already added");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/* If the accessory has no services, add this as the first */
|
||||
if (!_ha->servs) {
|
||||
_ha->servs = hs;
|
||||
} else {
|
||||
/* Else loop through the services to get to the last one,
|
||||
* and add this at the end
|
||||
*/
|
||||
__hap_serv_t *temp = (__hap_serv_t *)_ha->servs;
|
||||
while (temp->next_serv)
|
||||
temp = (__hap_serv_t *)temp->next_serv;
|
||||
temp->next_serv = hs;
|
||||
}
|
||||
_hs->iid = _ha->next_iid++;
|
||||
__hap_char_t *_hc = (__hap_char_t *)_hs->chars;
|
||||
while(_hc) {
|
||||
_hc->iid = _ha->next_iid++;
|
||||
_hc = (__hap_char_t *)_hc->next_char;
|
||||
}
|
||||
_hs->parent = ha;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hap_add_acc_to_list(__hap_acc_t *primary, __hap_acc_t *new)
|
||||
{
|
||||
__hap_acc_t *cur = primary;
|
||||
while (cur->next) {
|
||||
cur = cur->next;
|
||||
}
|
||||
cur->next = new;
|
||||
}
|
||||
|
||||
static void hap_remove_acc_from_list(__hap_acc_t *primary, __hap_acc_t *old)
|
||||
{
|
||||
__hap_acc_t *cur = primary;
|
||||
while (cur->next != old) {
|
||||
cur = cur->next;
|
||||
}
|
||||
cur->next = cur->next->next;
|
||||
}
|
||||
|
||||
#define HAP_BRIDGE_KEYSTORE "hap_bridge"
|
||||
|
||||
int hap_get_unique_aid(const char *id)
|
||||
{
|
||||
if (!id) {
|
||||
return -1;
|
||||
}
|
||||
int aid = 0;
|
||||
size_t aid_size = sizeof(aid);
|
||||
if (hap_keystore_get(HAP_KEYSTORE_NAMESPACE_HAPMAIN, id, (uint8_t *)&aid, &aid_size) != HAP_SUCCESS) {
|
||||
aid = hap_get_next_aid();
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Assigning aid = %d for Bridged accessory %s", aid, id);
|
||||
hap_keystore_set(HAP_KEYSTORE_NAMESPACE_HAPMAIN, id, (uint8_t *)&aid, sizeof(aid));
|
||||
} else {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Using aid = %d for Bridged accessory %s", aid, id);
|
||||
}
|
||||
return aid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP add accessory to HAP kernel
|
||||
*/
|
||||
void hap_add_accessory(hap_acc_t *ha)
|
||||
{
|
||||
if (!ha) {
|
||||
return;
|
||||
}
|
||||
if (primary_acc) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Primary Accessory already added. Use hap_add_bridged_accessory() instead");
|
||||
return;
|
||||
}
|
||||
__hap_acc_t *_ha = (__hap_acc_t *)ha;
|
||||
_ha->aid = 1;
|
||||
primary_acc = _ha;
|
||||
if (hap_priv.cfg.unique_param >= UNIQUE_NAME) {
|
||||
char name[74];
|
||||
uint8_t eth_mac[6];
|
||||
esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
|
||||
hap_serv_t *hs = hap_acc_get_serv_by_uuid(ha, HAP_SERV_UUID_ACCESSORY_INFORMATION);
|
||||
hap_char_t *hc = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_NAME);
|
||||
snprintf(name, sizeof(name), "%s-%02X%02X%02X", ((__hap_char_t *)hc)->val.s,
|
||||
eth_mac[3], eth_mac[4], eth_mac[5]);
|
||||
hap_platform_memory_free(((__hap_char_t *)hc)->val.s);
|
||||
((__hap_char_t *)hc)->val.s = strdup(name);
|
||||
}
|
||||
hap_acc_get_info(&hap_priv.primary_acc);
|
||||
}
|
||||
|
||||
void hap_add_bridged_accessory(hap_acc_t *ha, int aid)
|
||||
{
|
||||
if (!ha) {
|
||||
return;
|
||||
}
|
||||
__hap_acc_t *_ha = (__hap_acc_t *)ha;
|
||||
if (aid) {
|
||||
_ha->aid = aid;
|
||||
} else {
|
||||
_ha->aid = hap_get_next_aid();
|
||||
}
|
||||
|
||||
hap_add_acc_to_list(primary_acc, _ha);
|
||||
if (!hap_priv.cfg.disable_config_num_update) {
|
||||
hap_update_config_number();
|
||||
}
|
||||
}
|
||||
|
||||
void hap_remove_bridged_accessory(hap_acc_t *ha)
|
||||
{
|
||||
if ((__hap_acc_t *)ha == primary_acc) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Cannot remove primary accessory");
|
||||
} else {
|
||||
if (ha) {
|
||||
hap_remove_acc_from_list(primary_acc, (__hap_acc_t *)ha);
|
||||
if (!hap_priv.cfg.disable_config_num_update) {
|
||||
hap_update_config_number();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief HAP delete target accessory
|
||||
*/
|
||||
void hap_acc_delete(hap_acc_t *ha)
|
||||
{
|
||||
/* Returning success even if pointer is NULL, because it means
|
||||
* that the accessory is absent and as good as deleted
|
||||
*/
|
||||
if (!ha)
|
||||
return;
|
||||
__hap_acc_t *_ha = (__hap_acc_t *)ha;
|
||||
__hap_serv_t *_hs = (__hap_serv_t *)_ha->servs;
|
||||
while (_hs) {
|
||||
_ha->servs = _hs->next_serv;
|
||||
hap_serv_delete((hap_serv_t *)_hs);
|
||||
_hs = (__hap_serv_t *)_ha->servs;
|
||||
}
|
||||
hap_platform_memory_free(_ha);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP get target accessory AID
|
||||
*/
|
||||
uint32_t hap_acc_get_aid(hap_acc_t *ha)
|
||||
{
|
||||
ESP_MFI_ASSERT(ha);
|
||||
__hap_acc_t *_ha = (__hap_acc_t *)ha;
|
||||
|
||||
return _ha->aid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief delete all accessories
|
||||
*/
|
||||
void hap_delete_all_accessories(void)
|
||||
{
|
||||
__hap_acc_t *next, *ha = primary_acc;
|
||||
while (ha) {
|
||||
next = ha->next;
|
||||
hap_acc_delete((hap_acc_t *)ha);
|
||||
ha = next;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief get target accessory by AID
|
||||
*/
|
||||
hap_acc_t *hap_acc_get_by_aid(int32_t aid)
|
||||
{
|
||||
hap_acc_t *ha;
|
||||
for (ha = hap_get_first_acc(); ha; ha = hap_acc_get_next(ha)) {
|
||||
if (((__hap_acc_t *)ha)->aid == aid) {
|
||||
return ha;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HAP_ACC_H_
|
||||
#define _HAP_ACC_H_
|
||||
|
||||
#include <hap.h>
|
||||
#include <esp_hap_serv.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
typedef struct esp_mfi_accessory {
|
||||
struct esp_mfi_accessory *next;
|
||||
uint32_t aid; /* accessory AID */
|
||||
hap_serv_t *servs; /* service list */
|
||||
bool power_off;
|
||||
uint32_t next_iid;
|
||||
hap_identify_routine_t identify_routine;
|
||||
} __hap_acc_t;
|
||||
hap_char_t *hap_acc_get_char_by_iid(hap_acc_t *ha, int32_t iid);
|
||||
hap_acc_t *hap_acc_get_by_aid(int32_t aid);
|
||||
int hap_acc_get_info(hap_acc_cfg_t *acc_cfg);
|
||||
const hap_val_t *hap_get_product_data();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _HAP_ACC_H_ */
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <hap_platform_memory.h>
|
||||
#include <esp_hap_main.h>
|
||||
#include <esp_hap_mdns.h>
|
||||
#include <esp_hap_wifi.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
static char *new_name;
|
||||
|
||||
void hap_bct_change_name(const char *name)
|
||||
{
|
||||
if (new_name) {
|
||||
hap_platform_memory_free(new_name);
|
||||
}
|
||||
new_name = strdup(name);
|
||||
hap_send_event(HAP_INTERNAL_EVENT_BCT_CHANGE_NAME);
|
||||
}
|
||||
|
||||
void hap_bct_hot_plug()
|
||||
{
|
||||
hap_send_event(HAP_INTERNAL_EVENT_BCT_HOT_PLUG);
|
||||
}
|
||||
|
||||
void hap_handle_bct_change_name()
|
||||
{
|
||||
if (!new_name) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "No BCT name specified");
|
||||
return;
|
||||
} else {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Changing BCT Name to %s", new_name);
|
||||
}
|
||||
if (hap_mdns_serv_name_change(&hap_priv.hap_mdns_handle, new_name) != HAP_SUCCESS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Failed to change BCT name");
|
||||
}
|
||||
hap_platform_memory_free(new_name);
|
||||
new_name = NULL;
|
||||
}
|
||||
|
||||
void hap_handle_hot_plug()
|
||||
{
|
||||
esp_wifi_stop();
|
||||
vTaskDelay((10 * 1000) / portTICK_PERIOD_MS); /* Wait for 10 seconds */
|
||||
esp_wifi_start();
|
||||
esp_wifi_connect();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_BCT_PRIV_H_
|
||||
#define _HAP_BCT_PRIV_H_
|
||||
void hap_handle_bct_change_name();
|
||||
void hap_handle_hot_plug();
|
||||
#endif /* _HAP_BCT_PRIV_H_ */
|
|
@ -0,0 +1,601 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <hap_platform_memory.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "esp_mfi_debug.h"
|
||||
|
||||
#include <esp_hap_main.h>
|
||||
#include <esp_hap_acc.h>
|
||||
#include <esp_hap_char.h>
|
||||
#include <esp_hap_ip_services.h>
|
||||
#include <esp_hap_database.h>
|
||||
|
||||
static QueueHandle_t hap_event_queue;
|
||||
|
||||
/**
|
||||
* @brief get characteristics's value
|
||||
*/
|
||||
hap_val_t *esp_mfi_characteristics_get_value(__hap_char_t *_hc)
|
||||
{
|
||||
return &_hc->val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief check if characteristics has the specific permission
|
||||
*/
|
||||
bool hap_char_has_permission(__hap_char_t *_hc, uint16_t permission)
|
||||
{
|
||||
return _hc->permission & permission ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief compute the mod of a and b: a % b
|
||||
*/
|
||||
static double esp_mfi_fmod(double a, double b)
|
||||
{
|
||||
if (a < 1e-06 || b < 1e-06)
|
||||
return 0;
|
||||
|
||||
while (!((long long)a) || !((long long)b)) {
|
||||
a *= 10.0;
|
||||
b *= 10.0;
|
||||
}
|
||||
|
||||
return fmod(a, b);
|
||||
}
|
||||
|
||||
int hap_event_queue_init()
|
||||
{
|
||||
hap_event_queue = xQueueCreate( hap_priv.cfg.max_event_notif_chars,
|
||||
sizeof(hap_char_t *) );
|
||||
if (hap_event_queue) {
|
||||
return HAP_SUCCESS;
|
||||
} else {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
hap_char_t * hap_get_pending_notif_char()
|
||||
{
|
||||
hap_char_t *hc;
|
||||
if (xQueueReceive(hap_event_queue, &hc, 0) != pdTRUE) {
|
||||
return NULL;
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
static int hap_queue_event(hap_char_t *hc)
|
||||
{
|
||||
int ret;
|
||||
if (!hap_event_queue) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if (xPortInIsrContext() == pdTRUE) {
|
||||
ret = xQueueSendFromISR(hap_event_queue, &hc, NULL);
|
||||
} else {
|
||||
ret = xQueueSend(hap_event_queue, &hc, 0);
|
||||
}
|
||||
if (ret == pdTRUE) {
|
||||
hap_send_event(HAP_INTERNAL_EVENT_TRIGGER_NOTIF);
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief check if characteristics value is at the range
|
||||
*/
|
||||
int hap_char_check_val_constraints(__hap_char_t *_hc, hap_val_t *val)
|
||||
{
|
||||
if (!_hc->constraint_flags)
|
||||
return HAP_SUCCESS;
|
||||
|
||||
if (_hc->format == HAP_CHAR_FORMAT_INT) {
|
||||
int value = val->i;
|
||||
int remainder;
|
||||
|
||||
if (value > _hc->max.i
|
||||
|| value < _hc->min.i)
|
||||
return HAP_FAIL;
|
||||
|
||||
if (!_hc->step.i)
|
||||
return HAP_SUCCESS;
|
||||
|
||||
remainder = (value - _hc->min.i) % _hc->step.i;
|
||||
if (remainder)
|
||||
return HAP_FAIL;
|
||||
} else if (_hc->format == HAP_CHAR_FORMAT_FLOAT) {
|
||||
float value = val->f;
|
||||
|
||||
if (value > _hc->max.f
|
||||
|| value < _hc->min.f)
|
||||
return HAP_FAIL;
|
||||
# if 0
|
||||
/* Check for step value for floats has a high chance of failure,
|
||||
* because of precision issues. Hence, better to skip it.
|
||||
*/
|
||||
double remainder;
|
||||
if (_hc->step.f == 0.0)
|
||||
return HAP_SUCCESS;
|
||||
|
||||
remainder = esp_mfi_fmod(value - _hc->min.f, _hc->step.f);
|
||||
if (remainder != 0.0)
|
||||
return HAP_FAIL;
|
||||
#endif
|
||||
} else if (_hc->format == HAP_CHAR_FORMAT_UINT8
|
||||
|| _hc->format == HAP_CHAR_FORMAT_UINT16
|
||||
|| _hc->format == HAP_CHAR_FORMAT_UINT32) {
|
||||
uint32_t value = val->u;
|
||||
uint32_t remainder;
|
||||
|
||||
|
||||
if (value > _hc->max.u
|
||||
|| value < _hc->min.u)
|
||||
return HAP_FAIL;
|
||||
|
||||
if (!_hc->step.u)
|
||||
return HAP_SUCCESS;
|
||||
|
||||
remainder = (value - _hc->min.u) % _hc->step.u;
|
||||
if (remainder)
|
||||
return HAP_FAIL;
|
||||
} else if (_hc->format == HAP_CHAR_FORMAT_UINT64) {
|
||||
/* TODO: Add support. Currently, there is no particular 64-bit characteristic */
|
||||
}
|
||||
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief user update characteristics value, preparing for notification
|
||||
*/
|
||||
int hap_char_update_val(hap_char_t *hc, hap_val_t *val)
|
||||
{
|
||||
if (!hc || !val) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
_hc->update_called = true;
|
||||
if (hap_char_check_val_constraints(_hc, val) != HAP_SUCCESS)
|
||||
return HAP_FAIL;
|
||||
/* Boolean to track if the value has changed.
|
||||
* This will be later used to decide if an event notification
|
||||
* is required or not. If the new and old values are same,
|
||||
* there is no need of a notification
|
||||
*/
|
||||
bool value_changed = false;
|
||||
|
||||
switch (_hc->format) {
|
||||
case HAP_CHAR_FORMAT_BOOL:
|
||||
if (_hc->val.b != val->b) {
|
||||
_hc->val.b = val->b;
|
||||
value_changed = true;
|
||||
}
|
||||
break;
|
||||
case HAP_CHAR_FORMAT_INT:
|
||||
case HAP_CHAR_FORMAT_UINT8:
|
||||
case HAP_CHAR_FORMAT_UINT16:
|
||||
case HAP_CHAR_FORMAT_UINT32:
|
||||
if (_hc->val.i != val->i) {
|
||||
_hc->val.i = val->i;
|
||||
value_changed = true;
|
||||
}
|
||||
break;
|
||||
case HAP_CHAR_FORMAT_FLOAT:
|
||||
if (_hc->val.f != val->f) {
|
||||
_hc->val.f = val->f;
|
||||
value_changed = true;
|
||||
}
|
||||
break;
|
||||
case HAP_CHAR_FORMAT_STRING:
|
||||
/* Not checking all combinations to find if value has changed,
|
||||
* since generally we do not expect dynamic string values
|
||||
*
|
||||
* Eg.
|
||||
* Both (old and new) values being NULL
|
||||
* Old value NULL, New non-NULL
|
||||
* Old value non-NULL, new NULL
|
||||
*/
|
||||
if (_hc->val.s && val->s && !strcmp(_hc->val.s, val->s))
|
||||
value_changed = false;
|
||||
else
|
||||
value_changed = true;
|
||||
|
||||
if (_hc->val.s) {
|
||||
hap_platform_memory_free(_hc->val.s);
|
||||
_hc->val.s = NULL;
|
||||
}
|
||||
|
||||
if (val->s) {
|
||||
_hc->val.s = strdup(val->s);
|
||||
if (!_hc->val.s)
|
||||
return HAP_FAIL;
|
||||
}
|
||||
break;
|
||||
case HAP_CHAR_FORMAT_DATA:
|
||||
case HAP_CHAR_FORMAT_TLV8: {
|
||||
_hc->val.d.buf = val->d.buf;
|
||||
_hc->val.d.buflen = val->d.buflen;
|
||||
value_changed = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (value_changed || (_hc->permission & HAP_CHAR_PERM_SPECIAL_READ)) {
|
||||
ESP_MFI_DEBUG_INTR(ESP_MFI_DEBUG_INFO, "Value Changed");
|
||||
hap_queue_event(hc);
|
||||
} else {
|
||||
/* If there is no value change, reset the owner flag here itself, as no notification
|
||||
* is being sent anyways. In the absence of this, if there is a GET /characteristics
|
||||
* followed by some value change from hardware, the owner_ctrl stays assigned to a
|
||||
* stale value, and so the controller misses a notification.
|
||||
*/
|
||||
_hc->owner_ctrl = 0;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
const hap_val_t *hap_char_get_val(hap_char_t *hc)
|
||||
{
|
||||
if (!hc)
|
||||
return NULL;
|
||||
return &((__hap_char_t *)hc)->val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the minimum value of characteristic
|
||||
*/
|
||||
const hap_val_t *hap_char_get_min_val(hap_char_t *hc)
|
||||
{
|
||||
if (hc) {
|
||||
if(((__hap_char_t *)hc)->constraint_flags & HAP_CHAR_MIN_FLAG) {
|
||||
return &((__hap_char_t *)hc)->min;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the maximum value of characteristic
|
||||
*/
|
||||
const hap_val_t *hap_char_get_max_val(hap_char_t *hc)
|
||||
{
|
||||
if (hc) {
|
||||
if(((__hap_char_t *)hc)->constraint_flags & HAP_CHAR_MAX_FLAG || ((__hap_char_t *)hc)->constraint_flags & HAP_CHAR_MAXLEN_FLAG) {
|
||||
return &((__hap_char_t *)hc)->max;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the step value of characteristic
|
||||
*/
|
||||
const hap_val_t *hap_char_get_step_val(hap_char_t *hc)
|
||||
{
|
||||
if (hc) {
|
||||
if (((__hap_char_t *)hc)->constraint_flags & HAP_CHAR_STEP_FLAG) {
|
||||
return &((__hap_char_t *)hc)->step;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP create a characteristics
|
||||
*/
|
||||
static hap_char_t *hap_char_create(char *type_uuid, uint32_t permission, hap_char_format_t format, hap_val_t val)
|
||||
{
|
||||
__hap_char_t *new_ch;
|
||||
|
||||
ESP_MFI_ASSERT(type_uuid);
|
||||
|
||||
if (HAP_CHAR_FORMAT_STRING == format && val.s) {
|
||||
if (strlen(val.s) > HAP_CHAR_STRING_MAX_LEN)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_ch = hap_platform_memory_calloc(1, sizeof(__hap_char_t));
|
||||
if (!new_ch) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_ch->val = val;
|
||||
new_ch->type_uuid = type_uuid;
|
||||
new_ch->format = format;
|
||||
new_ch->permission = permission;
|
||||
|
||||
return (hap_char_t *) new_ch;
|
||||
}
|
||||
|
||||
hap_char_t *hap_char_bool_create(char *type_uuid, uint16_t perms, bool b)
|
||||
{
|
||||
hap_val_t val = {.b = b};
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_BOOL, val);
|
||||
}
|
||||
hap_char_t *hap_char_uint8_create(char *type_uuid, uint16_t perms, uint8_t u8)
|
||||
{
|
||||
hap_val_t val = {.u = u8};
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_UINT8, val);
|
||||
}
|
||||
hap_char_t *hap_char_uint16_create(char *type_uuid, uint16_t perms, uint16_t u16)
|
||||
{
|
||||
hap_val_t val = {.u = u16};
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_UINT16, val);
|
||||
}
|
||||
hap_char_t *hap_char_uint32_create(char *type_uuid, uint16_t perms, uint32_t u32)
|
||||
{
|
||||
hap_val_t val = {.u = u32};
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_UINT32, val);
|
||||
}
|
||||
hap_char_t *hap_char_uint64_create(char *type_uuid, uint16_t perms, uint64_t u64)
|
||||
{
|
||||
hap_val_t val = {.i64 = u64};
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_UINT64, val);
|
||||
}
|
||||
hap_char_t *hap_char_int_create(char *type_uuid, uint16_t perms, int i32)
|
||||
{
|
||||
hap_val_t val = {.i = i32};
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_INT, val);
|
||||
}
|
||||
hap_char_t *hap_char_float_create(char *type_uuid, uint16_t perms, float f)
|
||||
{
|
||||
hap_val_t val = {.f = f};
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_FLOAT, val);
|
||||
}
|
||||
hap_char_t *hap_char_string_create(char *type_uuid, uint16_t perms, char *s)
|
||||
{
|
||||
hap_val_t val;
|
||||
if (s)
|
||||
val.s = strdup(s);
|
||||
else
|
||||
val.s = NULL;
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_STRING, val);
|
||||
}
|
||||
|
||||
hap_char_t *hap_char_data_create(char *type_uuid, uint16_t perms, hap_data_val_t *d)
|
||||
{
|
||||
hap_val_t val = {0};
|
||||
if (d) {
|
||||
val.d = *d;
|
||||
}
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_DATA, val);
|
||||
}
|
||||
|
||||
hap_char_t *hap_char_tlv8_create(char *type_uuid, uint16_t perms, hap_tlv8_val_t *t)
|
||||
{
|
||||
hap_val_t val = {0};
|
||||
if (t) {
|
||||
val.t = *t;
|
||||
}
|
||||
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_TLV8, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP get target characteristics IID
|
||||
*/
|
||||
uint32_t hap_char_get_iid(hap_char_t *hc)
|
||||
{
|
||||
ESP_MFI_ASSERT(hc);
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
|
||||
return tmp->iid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP get target characteristics type UUID
|
||||
*/
|
||||
const char * hap_char_get_type_uuid(hap_char_t *hc)
|
||||
{
|
||||
ESP_MFI_ASSERT(hc);
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
|
||||
return tmp->type_uuid;
|
||||
}
|
||||
|
||||
uint16_t hap_char_get_perm(hap_char_t *hc)
|
||||
{
|
||||
if (!hc)
|
||||
return 0;
|
||||
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
return tmp->permission;
|
||||
}
|
||||
|
||||
hap_char_format_t hap_char_get_format(hap_char_t *hc)
|
||||
{
|
||||
if (!hc)
|
||||
return 0;
|
||||
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
return tmp->format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP delete target characteristics
|
||||
*/
|
||||
void hap_char_delete(hap_char_t *hc)
|
||||
{
|
||||
ESP_MFI_ASSERT(hc);
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
if (_hc->format == HAP_CHAR_FORMAT_STRING) {
|
||||
if (_hc->val.s) {
|
||||
hap_platform_memory_free(_hc->val.s);
|
||||
}
|
||||
}
|
||||
if (_hc->valid_vals) {
|
||||
hap_platform_memory_free(_hc->valid_vals);
|
||||
}
|
||||
if (_hc->valid_vals_range) {
|
||||
hap_platform_memory_free(_hc->valid_vals_range);
|
||||
}
|
||||
hap_platform_memory_free(_hc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP configure the characteristics's value description
|
||||
*/
|
||||
void hap_char_int_set_constraints(hap_char_t *hc, int min, int max, int step)
|
||||
{
|
||||
ESP_MFI_ASSERT(hc);
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
tmp->min.i = min;
|
||||
tmp->max.i = max;
|
||||
tmp->step.i = step;
|
||||
if (step) {
|
||||
tmp->constraint_flags |= (HAP_CHAR_MIN_FLAG | HAP_CHAR_MAX_FLAG | HAP_CHAR_STEP_FLAG);
|
||||
} else {
|
||||
tmp->constraint_flags |= (HAP_CHAR_MIN_FLAG | HAP_CHAR_MAX_FLAG);
|
||||
}
|
||||
}
|
||||
void hap_char_float_set_constraints(hap_char_t *hc, float min, float max, float step)
|
||||
{
|
||||
ESP_MFI_ASSERT(hc);
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
tmp->min.f = min;
|
||||
tmp->max.f = max;
|
||||
tmp->step.f = step;
|
||||
if (step) {
|
||||
tmp->constraint_flags |= (HAP_CHAR_MIN_FLAG | HAP_CHAR_MAX_FLAG | HAP_CHAR_STEP_FLAG);
|
||||
} else {
|
||||
tmp->constraint_flags |= (HAP_CHAR_MIN_FLAG | HAP_CHAR_MAX_FLAG);
|
||||
}
|
||||
}
|
||||
|
||||
void hap_char_string_set_maxlen(hap_char_t *hc, int maxlen)
|
||||
{
|
||||
ESP_MFI_ASSERT(hc);
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
if (maxlen > HAP_CHAR_STRING_MAX_LEN) {
|
||||
maxlen = HAP_CHAR_STRING_MAX_LEN;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_WARN, "Characteristic string length larger than maximum value(%d), falling back to the maximum value.", HAP_CHAR_STRING_MAX_LEN);
|
||||
}
|
||||
tmp->max.i = maxlen;
|
||||
tmp->constraint_flags |= HAP_CHAR_MAXLEN_FLAG;
|
||||
}
|
||||
|
||||
void hap_char_add_description(hap_char_t *hc, const char *description)
|
||||
{
|
||||
ESP_MFI_ASSERT(hc);
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
tmp->description = (char *)description;
|
||||
}
|
||||
void hap_char_add_unit(hap_char_t *hc, const char *unit)
|
||||
{
|
||||
ESP_MFI_ASSERT(hc);
|
||||
__hap_char_t *tmp = (__hap_char_t *)hc;
|
||||
tmp->unit = (char *)unit;
|
||||
}
|
||||
hap_char_t *hap_char_get_next(hap_char_t *hc)
|
||||
{
|
||||
return ((__hap_char_t *)hc)->next_char;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_char_get_parent(hap_char_t *hc)
|
||||
{
|
||||
return ((__hap_char_t *)hc)->parent;
|
||||
|
||||
}
|
||||
|
||||
#define set_bit(val, index) ((val) |= (1 << index))
|
||||
#define reset_bit(val, index) ((val) &= ~(1 << index))
|
||||
void hap_char_manage_notification(hap_char_t *hc, int index, bool ev)
|
||||
{
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
if (ev)
|
||||
set_bit(_hc->ev_ctrls, index);
|
||||
else
|
||||
reset_bit(_hc->ev_ctrls, index);
|
||||
}
|
||||
|
||||
bool hap_char_is_ctrl_subscribed(hap_char_t *hc, int index)
|
||||
{
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
return (_hc->ev_ctrls & (1 << index)) ? true : false;
|
||||
}
|
||||
|
||||
void hap_char_set_owner_ctrl(hap_char_t *hc, int index)
|
||||
{
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
_hc->owner_ctrl = 0;
|
||||
set_bit(_hc->owner_ctrl, index);
|
||||
}
|
||||
|
||||
bool hap_char_is_ctrl_owner(hap_char_t *hc, int index)
|
||||
{
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
return (_hc->owner_ctrl & (1 << index)) ? true : false;
|
||||
}
|
||||
|
||||
void hap_char_set_iid(hap_char_t *hc, int32_t iid)
|
||||
{
|
||||
if (hc) {
|
||||
((__hap_char_t *)hc)->iid = iid;
|
||||
}
|
||||
}
|
||||
|
||||
void hap_disable_all_char_notif(int index)
|
||||
{
|
||||
/* Just loop through all characteristic objects and reset the
|
||||
* bit indicating event notifications. This is the simplest way
|
||||
*/
|
||||
hap_acc_t *ha;
|
||||
hap_serv_t *hs;
|
||||
hap_char_t *hc;
|
||||
for (ha = hap_get_first_acc(); ha; ha = hap_acc_get_next(ha)) {
|
||||
for (hs = hap_acc_get_first_serv(ha); hs; hs = hap_serv_get_next(hs)) {
|
||||
for (hc = hap_serv_get_first_char(hs); hc; hc = hap_char_get_next(hc)) {
|
||||
reset_bit(((__hap_char_t *)hc)->ev_ctrls, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hap_char_add_valid_vals(hap_char_t *hc, const uint8_t *valid_vals, size_t valid_val_cnt)
|
||||
{
|
||||
if (!hc)
|
||||
return;
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
_hc->valid_vals = hap_platform_memory_malloc(valid_val_cnt);
|
||||
if (_hc->valid_vals) {
|
||||
memcpy(_hc->valid_vals, valid_vals, valid_val_cnt);
|
||||
_hc->valid_vals_cnt = valid_val_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
void hap_char_add_valid_vals_range(hap_char_t *hc, uint8_t start_val, uint8_t end_val)
|
||||
{
|
||||
if (!hc)
|
||||
return;
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
_hc->valid_vals_range = hap_platform_memory_malloc(sizeof(uint8_t));
|
||||
if (_hc->valid_vals_range) {
|
||||
_hc->valid_vals_range[0] = start_val;
|
||||
_hc->valid_vals_range[1] = end_val;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HAP_CHAR_H_
|
||||
#define _HAP_CHAR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include <hap.h>
|
||||
#include <esp_hap_serv.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define HAP_CHAR_MIN_FLAG (1 << 0)
|
||||
#define HAP_CHAR_MAX_FLAG (1 << 1)
|
||||
#define HAP_CHAR_STEP_FLAG (1 << 2)
|
||||
#define HAP_CHAR_MAXLEN_FLAG (1 << 3)
|
||||
#define HAP_CHAR_MAXDATALEN_FLAG (1 << 4)
|
||||
|
||||
/**
|
||||
* @brief characteristics object information
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t iid; /* Characteristic instance ID */
|
||||
const char *type_uuid; /* Apple's characteristic UUID */
|
||||
uint16_t permission; /* Characteristic permission */
|
||||
hap_char_format_t format; /* data type of the value */
|
||||
hap_val_t val;
|
||||
bool ev; /* check if characteristics supports event */
|
||||
char *description; /* characteristics's description */
|
||||
char *unit;
|
||||
|
||||
/* Characteristics's father subsystem */
|
||||
hap_serv_t *parent;
|
||||
|
||||
uint8_t constraint_flags;
|
||||
|
||||
hap_val_t max; /* maximum value, maxlen, max data len*/
|
||||
hap_val_t min; /* minimum value */
|
||||
hap_val_t step; /* step value */
|
||||
|
||||
hap_char_t *next_char;
|
||||
/* Bitmap to indicate which controllers have enabled notifications
|
||||
*/
|
||||
uint16_t ev_ctrls;
|
||||
|
||||
/* Bitmap indicating the last controller that modified the value.
|
||||
* No notification should be sent to the owner
|
||||
*/
|
||||
uint16_t owner_ctrl;
|
||||
|
||||
/* Pointer to a valid values range. It will be a 2 byte array, if set from application */
|
||||
uint8_t *valid_vals_range;
|
||||
/* Since a list of valid values can have any length, using a pointer here,
|
||||
* which will be allocated if valid values are set for a characteristic
|
||||
*/
|
||||
uint8_t *valid_vals;
|
||||
size_t valid_vals_cnt;
|
||||
bool update_called;
|
||||
} __hap_char_t;
|
||||
|
||||
void hap_char_manage_notification(hap_char_t *hc, int index, bool ev);
|
||||
bool hap_char_is_ctrl_subscribed(hap_char_t *hc, int index);
|
||||
void hap_char_set_owner_ctrl(hap_char_t *hc, int index);
|
||||
bool hap_char_is_ctrl_owner(hap_char_t *hc, int index);
|
||||
void hap_disable_all_char_notif(int index);
|
||||
int hap_char_check_val_constraints(__hap_char_t *_hc, hap_val_t *val);
|
||||
int hap_event_queue_init();
|
||||
hap_char_t * hap_get_pending_notif_char();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _HAP_CHAR_H_ */
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <hap.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
#include <esp_hap_main.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_hap_controllers.h>
|
||||
#include <esp_hap_keystore.h>
|
||||
|
||||
#define HAP_KEYSTORE_NAMESPACE_CTRL "hap_ctrl"
|
||||
|
||||
int hap_controllers_init()
|
||||
{
|
||||
memset(hap_priv.controllers, 0, sizeof(hap_priv.controllers));
|
||||
char index_str[4];
|
||||
uint8_t i;
|
||||
size_t info_size;
|
||||
bool acc_paired = false;
|
||||
for (i = 0; i < HAP_MAX_CONTROLLERS; i++) {
|
||||
snprintf(index_str, sizeof(index_str), "%d", i);
|
||||
info_size = sizeof(hap_ctrl_info_t);
|
||||
if (hap_keystore_get(HAP_KEYSTORE_NAMESPACE_CTRL, index_str,
|
||||
(uint8_t *)&hap_priv.controllers[i].info, &info_size) == HAP_SUCCESS) {
|
||||
if (info_size == sizeof(hap_ctrl_info_t)) {
|
||||
hap_priv.controllers[i].index = i;
|
||||
hap_priv.controllers[i].valid = true;
|
||||
acc_paired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (acc_paired) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Accessory is Paired with atleast one controller");
|
||||
} else {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Accessory is not Paired with any controller");
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
hap_ctrl_data_t *hap_controller_get_empty_loc()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_CONTROLLERS; i++) {
|
||||
if (!hap_priv.controllers[i].valid) {
|
||||
hap_priv.controllers[i].index = i;
|
||||
return &hap_priv.controllers[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hap_get_paired_controller_count()
|
||||
{
|
||||
int i, cnt = 0;
|
||||
for (i = 0; i < HAP_MAX_CONTROLLERS; i++) {
|
||||
if (hap_priv.controllers[i].valid) {
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
bool is_accessory_paired()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_CONTROLLERS; i++) {
|
||||
if (hap_priv.controllers[i].valid)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_admin_paired()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_CONTROLLERS; i++) {
|
||||
if (hap_priv.controllers[i].valid && hap_priv.controllers[i].info.perms)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int hap_controller_save(hap_ctrl_data_t *ctrl_data)
|
||||
{
|
||||
ctrl_data->valid = true;
|
||||
char index_str[4];
|
||||
snprintf(index_str, sizeof(index_str), "%d", ctrl_data->index);
|
||||
int ret = hap_keystore_set(HAP_KEYSTORE_NAMESPACE_CTRL, index_str,
|
||||
(const uint8_t *)&ctrl_data->info, (size_t)sizeof(hap_ctrl_info_t));
|
||||
|
||||
if (ret != HAP_SUCCESS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Failed to store controller %d", ctrl_data->index);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_report_event(HAP_EVENT_CTRL_PAIRED, ctrl_data->info.id, sizeof(ctrl_data->info.id));
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
void hap_controller_remove(hap_ctrl_data_t *ctrl_data)
|
||||
{
|
||||
if (!ctrl_data)
|
||||
return;
|
||||
char index_str[4];
|
||||
snprintf(index_str, sizeof(index_str), "%d", ctrl_data->index);
|
||||
char id[HAP_CTRL_ID_LEN];
|
||||
strncpy(id, ctrl_data->info.id, sizeof(id));
|
||||
hap_keystore_delete(HAP_KEYSTORE_NAMESPACE_CTRL, index_str);
|
||||
memset(ctrl_data, 0, sizeof(hap_ctrl_data_t));
|
||||
hap_report_event(HAP_EVENT_CTRL_UNPAIRED, id, sizeof(id));
|
||||
}
|
||||
|
||||
hap_ctrl_data_t *hap_get_controller(char *ctrl_id)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_CONTROLLERS; i++) {
|
||||
if (hap_priv.controllers[i].valid
|
||||
&& (!strcmp(hap_priv.controllers[i].info.id, ctrl_id)))
|
||||
return &hap_priv.controllers[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void hap_erase_controller_info()
|
||||
{
|
||||
hap_keystore_delete_namespace(HAP_KEYSTORE_NAMESPACE_CTRL);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_CONTROLLERS_H_
|
||||
#define _HAP_CONTROLLERS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HAP_MAX_CONTROLLERS 16
|
||||
#define HAP_CTRL_ID_LEN 64
|
||||
#define ED_KEY_LEN 32
|
||||
|
||||
|
||||
typedef struct {
|
||||
char id[HAP_CTRL_ID_LEN];
|
||||
uint8_t ltpk[ED_KEY_LEN];
|
||||
uint8_t perms;
|
||||
} __attribute__((packed)) hap_ctrl_info_t;
|
||||
|
||||
typedef struct {
|
||||
hap_ctrl_info_t info;
|
||||
/* If "valid" is false, it means that the entry is invalid,
|
||||
* irrespective of the values of other members, and can be
|
||||
* used to store new controller info
|
||||
*/
|
||||
bool valid;
|
||||
/* Index is used just for better managing the keystore data */
|
||||
uint8_t index;
|
||||
} hap_ctrl_data_t;
|
||||
|
||||
int hap_controllers_init();
|
||||
bool is_accessory_paired();
|
||||
bool is_admin_paired();
|
||||
hap_ctrl_data_t *hap_controller_get_empty_loc();
|
||||
int hap_controller_save(hap_ctrl_data_t *ctrl_data);
|
||||
void hap_controller_remove(hap_ctrl_data_t *ctrl_data);
|
||||
hap_ctrl_data_t *hap_get_controller(char *ctrl_id);
|
||||
void hap_erase_controller_info();
|
||||
|
||||
#endif /* _HAP_CONTROLLERS_H_ */
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sodium/crypto_sign_ed25519.h>
|
||||
#include <string.h>
|
||||
#include <hap_platform_memory.h>
|
||||
|
||||
#include <esp_mfi_rand.h>
|
||||
#include <esp_mfi_sha.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
#include <esp_hap_main.h>
|
||||
#include <esp_hap_keystore.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_hap_controllers.h>
|
||||
|
||||
#include <esp_mfi_base64.h>
|
||||
|
||||
#define HAP_KEY_ACC_ID "acc_id"
|
||||
#define HAP_KEY_LTSKA "ltska"
|
||||
#define HAP_KEY_LTPKA "ltpka"
|
||||
#define HAP_KEY_CONFIG_NUM "config_num"
|
||||
#define HAP_KEY_FW_REV "fw_rev"
|
||||
#define HAP_KEY_CUR_AID "cur_aid"
|
||||
#define HAP_KEY_STATE_NUM "state_num"
|
||||
|
||||
#define HAP_KEY_SETUP_ID "setup_id"
|
||||
#define HAP_KEY_SETUP_SALT "setup_salt"
|
||||
#define HAP_KEY_SETUP_VERIFIER "setup_verifier"
|
||||
|
||||
#define HAP_LOOP_STACK (4 * 1024)
|
||||
#define HAP_MAIN_THREAD_PRIORITY 7
|
||||
#define HAP_MAX_NOTIF_CHARS 8
|
||||
#define HAP_SOCK_RECV_TIMEOUT 10
|
||||
#define HAP_SOCK_SEND_TIMEOUT 10
|
||||
|
||||
hap_priv_t hap_priv = {
|
||||
.cfg = {
|
||||
.task_stack_size = HAP_LOOP_STACK,
|
||||
.task_priority = HAP_MAIN_THREAD_PRIORITY,
|
||||
.max_event_notif_chars = HAP_MAX_NOTIF_CHARS,
|
||||
.unique_param = UNIQUE_SSID,
|
||||
.recv_timeout = HAP_SOCK_RECV_TIMEOUT,
|
||||
.send_timeout = HAP_SOCK_SEND_TIMEOUT,
|
||||
.sw_token_max_len = HAP_SW_TOKEN_MAX_LEN,
|
||||
}
|
||||
};
|
||||
|
||||
static void hap_save_config_number()
|
||||
{
|
||||
|
||||
hap_keystore_set(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_CONFIG_NUM,
|
||||
(uint8_t *)&hap_priv.config_num, sizeof(hap_priv.config_num));
|
||||
}
|
||||
|
||||
static void hap_get_config_number()
|
||||
{
|
||||
size_t config_num_len = sizeof(hap_priv.config_num);
|
||||
if (hap_keystore_get(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_CONFIG_NUM,
|
||||
(uint8_t *)&hap_priv.config_num, &config_num_len) != HAP_SUCCESS) {
|
||||
hap_priv.config_num = 1;
|
||||
hap_save_config_number();
|
||||
}
|
||||
if (hap_priv.config_num > 65535) {
|
||||
hap_priv.config_num = 1;
|
||||
hap_save_config_number();
|
||||
}
|
||||
}
|
||||
|
||||
void hap_increment_and_save_config_num()
|
||||
{
|
||||
hap_priv.config_num++;
|
||||
if (hap_priv.config_num > 65535) {
|
||||
hap_priv.config_num = 1;
|
||||
}
|
||||
hap_save_config_number();
|
||||
}
|
||||
|
||||
|
||||
static void hap_save_state_number()
|
||||
{
|
||||
hap_keystore_set(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_STATE_NUM,
|
||||
(uint8_t *)&hap_priv.state_num, sizeof(hap_priv.state_num));
|
||||
}
|
||||
|
||||
void hap_increment_and_save_state_num()
|
||||
{
|
||||
if (is_accessory_paired()) {
|
||||
hap_priv.state_num++;
|
||||
/* If value becomes 0 after incrementing, it means that it has wrapped around.
|
||||
* So, reset to 1
|
||||
*/
|
||||
if (hap_priv.state_num == 0) {
|
||||
hap_priv.state_num = 1;
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Updated state number to %d", hap_priv.state_num);
|
||||
hap_save_state_number();
|
||||
}
|
||||
}
|
||||
|
||||
static void hap_init_state_number()
|
||||
{
|
||||
size_t state_num_len = sizeof(hap_priv.state_num);
|
||||
if (hap_keystore_get(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_STATE_NUM,
|
||||
(uint8_t *)&hap_priv.state_num, &state_num_len) != HAP_SUCCESS) {
|
||||
/* If state number is not found, initialise with 1 and store.
|
||||
*/
|
||||
hap_priv.state_num = 1;
|
||||
hap_save_state_number();
|
||||
} else {
|
||||
hap_increment_and_save_state_num();
|
||||
}
|
||||
}
|
||||
|
||||
static void hap_save_cur_aid()
|
||||
{
|
||||
hap_keystore_set(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_CUR_AID,
|
||||
(uint8_t *)&hap_priv.cur_aid, sizeof(hap_priv.cur_aid));
|
||||
}
|
||||
|
||||
static void hap_get_cur_aid()
|
||||
{
|
||||
size_t aid_len = sizeof(hap_priv.cur_aid);
|
||||
if (hap_keystore_get(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_CUR_AID,
|
||||
(uint8_t *)&hap_priv.cur_aid, &aid_len) != HAP_SUCCESS) {
|
||||
/* AID = 1 is reserved for Primary Accessory. So, we set the initial
|
||||
* value to 1, so that the bridged accessories get assigned aid from
|
||||
* 2 onwards
|
||||
*/
|
||||
hap_priv.cur_aid = 1;
|
||||
hap_save_cur_aid();
|
||||
}
|
||||
}
|
||||
|
||||
static int hap_get_setup_id()
|
||||
{
|
||||
/* Read setup id from NVS, only if it is not already set from the accessory code */
|
||||
if (!strlen(hap_priv.setup_id)) {
|
||||
size_t setup_id_len = sizeof(hap_priv.setup_id);
|
||||
if (hap_factory_keystore_get(HAP_FACTORY_NAMESPACE_HAP_SETUP, HAP_KEY_SETUP_ID,
|
||||
(uint8_t *)hap_priv.setup_id, &setup_id_len) != HAP_SUCCESS) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
static int hap_get_setup_info()
|
||||
{
|
||||
/* If the setup code has been set directly, no need to check for setup info */
|
||||
if (hap_priv.setup_code) {
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
/* If the setup info has been set externally, directly from the accessory code,
|
||||
* no need to check in the NVS
|
||||
*/
|
||||
if (!hap_priv.setup_info) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Getting setup info from factory NVS");
|
||||
hap_priv.setup_info = hap_platform_memory_calloc(1, sizeof(hap_setup_info_t));
|
||||
if (!hap_priv.setup_info)
|
||||
return HAP_FAIL;
|
||||
size_t salt_len = sizeof(hap_priv.setup_info->salt);
|
||||
size_t verifier_len = sizeof(hap_priv.setup_info->verifier);
|
||||
int ret = hap_factory_keystore_get(HAP_FACTORY_NAMESPACE_HAP_SETUP, HAP_KEY_SETUP_SALT,
|
||||
hap_priv.setup_info->salt, &salt_len);
|
||||
ret |= hap_factory_keystore_get(HAP_FACTORY_NAMESPACE_HAP_SETUP, HAP_KEY_SETUP_VERIFIER,
|
||||
hap_priv.setup_info->verifier, &verifier_len);
|
||||
if (ret != HAP_SUCCESS) {
|
||||
hap_platform_memory_free(hap_priv.setup_info);
|
||||
hap_priv.setup_info = NULL;
|
||||
return HAP_FAIL;
|
||||
}
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
static void hap_check_fw_version()
|
||||
{
|
||||
char fw_rev[64] = {0};
|
||||
size_t fw_rev_len = sizeof(fw_rev);
|
||||
/* Check if the firmware revision is stored in NVS */
|
||||
if (hap_keystore_get(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_FW_REV,
|
||||
(uint8_t *)fw_rev, &fw_rev_len) == HAP_SUCCESS) {
|
||||
/* If the firmware revision is found, compare with the current revision.
|
||||
* If it is the same, no need to do anything. So, just return
|
||||
*/
|
||||
if (strncmp(fw_rev, hap_priv.primary_acc.fw_rev, sizeof(fw_rev)) == 0) {
|
||||
return;
|
||||
} else {
|
||||
/* If there is a version mismatch, it means that the firmware was upgraded.
|
||||
* Update config number in that case
|
||||
*/
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "FW Update detected. Incrementing config number");
|
||||
hap_increment_and_save_config_num();
|
||||
}
|
||||
}
|
||||
/* Save the new firmare revision to NVS */
|
||||
hap_keystore_set(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_FW_REV,
|
||||
(uint8_t *)hap_priv.primary_acc.fw_rev,
|
||||
strlen(hap_priv.primary_acc.fw_rev));
|
||||
}
|
||||
|
||||
int hap_acc_setup_init()
|
||||
{
|
||||
if (hap_get_setup_id() != HAP_SUCCESS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Setup ID absent");
|
||||
return HAP_FAIL;
|
||||
} else {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Setup ID: %s", hap_priv.setup_id);
|
||||
}
|
||||
|
||||
if (hap_get_setup_info() != HAP_SUCCESS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Setup Info absent");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t digest[MFI_SHA512_SIZE] = {0};
|
||||
esp_mfi_sha_ctx_t ctx = 0;
|
||||
ctx = esp_mfi_sha512_new();
|
||||
if (!ctx) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Out of Memory");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
/* Compute setup hash by taking a SHA512 hash of the setup id and device id */
|
||||
esp_mfi_sha512_init(ctx);
|
||||
esp_mfi_sha512_update(ctx, (const uint8_t *)hap_priv.setup_id, strlen(hap_priv.setup_id));
|
||||
esp_mfi_sha512_update(ctx, (const uint8_t *)hap_priv.acc_id, strlen(hap_priv.acc_id));
|
||||
esp_mfi_sha512_final(ctx, digest);
|
||||
/* Copy only the first 4 bytes as the setup hash */
|
||||
memcpy(hap_priv.setup_hash, digest, SETUP_HASH_LEN);
|
||||
esp_mfi_sha512_free(ctx);
|
||||
|
||||
int hash_size = sizeof(hap_priv.setup_hash_str);
|
||||
esp_mfi_base64_encode((const char *)hap_priv.setup_hash, SETUP_HASH_LEN,
|
||||
hap_priv.setup_hash_str, hash_size, &hash_size);
|
||||
|
||||
hap_check_fw_version();
|
||||
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_database_init(void)
|
||||
{
|
||||
uint8_t id[6];
|
||||
size_t val_size = sizeof(id);
|
||||
if (hap_keystore_get(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_ACC_ID, id, &val_size) == HAP_SUCCESS) {
|
||||
val_size = sizeof(hap_priv.ltska);
|
||||
hap_keystore_get(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_LTSKA, hap_priv.ltska, &val_size);
|
||||
val_size = sizeof(hap_priv.ltpka);
|
||||
hap_keystore_get(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_LTPKA, hap_priv.ltpka, &val_size);
|
||||
} else {
|
||||
/* If the accessory ID is not found in keystore, create and store a new random ID */
|
||||
esp_mfi_get_random(id, sizeof(id));
|
||||
hap_keystore_set(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_ACC_ID, id, sizeof(id));
|
||||
/* Also create a new ED25519 key pair */
|
||||
esp_mfi_get_random(hap_priv.ltska, sizeof(hap_priv.ltska));
|
||||
crypto_sign_ed25519_keypair(hap_priv.ltpka, hap_priv.ltska);
|
||||
hap_keystore_set(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_LTSKA, hap_priv.ltska, sizeof(hap_priv.ltska));
|
||||
hap_keystore_set(HAP_KEYSTORE_NAMESPACE_HAPMAIN, HAP_KEY_LTPKA, hap_priv.ltpka, sizeof(hap_priv.ltpka));
|
||||
}
|
||||
|
||||
memcpy(hap_priv.raw_acc_id, id, sizeof(hap_priv.raw_acc_id));
|
||||
snprintf(hap_priv.acc_id, sizeof(hap_priv.acc_id), "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
id[0], id[1], id[2], id[3], id[4], id[5]);
|
||||
|
||||
hap_controllers_init();
|
||||
hap_get_config_number();
|
||||
hap_get_cur_aid();
|
||||
hap_init_state_number();
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Database initialised. Accessory Device ID: %s", hap_priv.acc_id);
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
char *hap_get_acc_id()
|
||||
{
|
||||
return hap_priv.acc_id;
|
||||
}
|
||||
|
||||
int hap_get_next_aid(char *id)
|
||||
{
|
||||
hap_priv.cur_aid++;
|
||||
hap_save_cur_aid();
|
||||
return hap_priv.cur_aid;
|
||||
}
|
||||
|
||||
void hap_erase_accessory_info()
|
||||
{
|
||||
hap_keystore_delete_namespace(HAP_KEYSTORE_NAMESPACE_HAPMAIN);
|
||||
}
|
||||
|
||||
void hap_configure_unique_param(hap_unique_param_t param)
|
||||
{
|
||||
hap_priv.cfg.unique_param = param;
|
||||
}
|
||||
|
||||
int hap_get_config(hap_cfg_t *cfg)
|
||||
{
|
||||
if (!cfg) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
*cfg = hap_priv.cfg;
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_set_config(const hap_cfg_t *cfg)
|
||||
{
|
||||
if (!cfg) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_priv.cfg = *cfg;
|
||||
return HAP_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_DATABASE_H_
|
||||
#define _HAP_DATABASE_H_
|
||||
|
||||
#include <hap.h>
|
||||
#include <esp_hap_controllers.h>
|
||||
#include <esp_hap_pair_common.h>
|
||||
#include <esp_hap_mdns.h>
|
||||
#include <esp_hap_secure_message.h>
|
||||
#include <esp_http_server.h>
|
||||
|
||||
#define HAP_KEYSTORE_NAMESPACE_HAPMAIN "hap_main"
|
||||
#define HAP_FACTORY_NAMESPACE_HAP_SETUP "hap_setup"
|
||||
|
||||
#define HAP_MAX_SESSIONS 8
|
||||
#define SETUP_ID_LEN 4
|
||||
#define SETUP_HASH_LEN 4
|
||||
|
||||
#define HAP_ACC_ID_LEN 18 /* AA:BB:CC:XX:YY:ZZ\0 */
|
||||
#define ED_KEY_LEN 32
|
||||
#define HAP_SW_TOKEN_MAX_LEN 1200
|
||||
|
||||
|
||||
typedef struct {
|
||||
hap_acc_cfg_t primary_acc;
|
||||
uint32_t config_num;
|
||||
uint32_t cur_aid;
|
||||
uint8_t raw_acc_id[6];
|
||||
char acc_id[HAP_ACC_ID_LEN];
|
||||
char setup_id[SETUP_ID_LEN + 1];
|
||||
uint8_t setup_hash[SETUP_HASH_LEN];
|
||||
char setup_hash_str[SETUP_HASH_LEN * 2 + 1];
|
||||
uint8_t ltska[ED_KEY_LEN];
|
||||
uint8_t ltpka[ED_KEY_LEN];
|
||||
hap_cid_t cid;
|
||||
hap_ctrl_data_t controllers[HAP_MAX_CONTROLLERS];
|
||||
hap_secure_session_t *sessions[HAP_MAX_SESSIONS];
|
||||
uint8_t pair_attempts;
|
||||
hap_mdns_handle_t wac_mdns_handle;
|
||||
hap_mdns_handle_t hap_mdns_handle;
|
||||
hap_setup_info_t *setup_info;
|
||||
char *setup_code;
|
||||
char *ssid;
|
||||
char *password;
|
||||
hap_software_token_info_t *token_info;
|
||||
uint8_t features;
|
||||
hap_event_handler_t hap_event_handler;
|
||||
char *softap_ssid;
|
||||
void (*ext_nw_prov_start)(void *data, const char *name);
|
||||
void (*ext_nw_prov_stop)(void *data);
|
||||
void *ext_nw_prov_data;
|
||||
hap_cfg_t cfg;
|
||||
hap_transport_t transport;
|
||||
uint32_t pairing_flags;
|
||||
httpd_handle_t server;
|
||||
uint8_t *product_data;
|
||||
uint16_t state_num;
|
||||
bool disconnected_event_sent;
|
||||
hap_mfi_auth_type_t auth_type;
|
||||
} hap_priv_t;
|
||||
|
||||
extern hap_priv_t hap_priv;
|
||||
int hap_database_init(void);
|
||||
char *hap_get_acc_id();
|
||||
int hap_get_next_aid();
|
||||
int hap_acc_setup_init();
|
||||
void hap_erase_accessory_info();
|
||||
void hap_increment_and_save_config_num();
|
||||
void hap_increment_and_save_state_num();
|
||||
#endif /* _HAP_DATABASE_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_IP_SERVICES_H_
|
||||
#define _HAP_IP_SERVICES_H_
|
||||
#include <stdbool.h>
|
||||
#include <esp_http_server.h>
|
||||
int hap_http_session_not_authorized(httpd_req_t *req);
|
||||
int hap_httpd_get_data(httpd_req_t *req, char *buffer, int len);
|
||||
int hap_httpd_start();
|
||||
int hap_ip_services_start();
|
||||
int hap_mdns_announce(bool first);
|
||||
int hap_mdns_deannounce();
|
||||
void hap_http_send_notif();
|
||||
#endif /* _HAP_IP_SERVICES_H_ */
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <hap.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
#include <hap_platform_keystore.h>
|
||||
|
||||
static bool keystore_init_done;
|
||||
static char *hap_platform_nvs_partition;
|
||||
static char *hap_platform_factory_nvs_partition;
|
||||
|
||||
int hap_keystore_init()
|
||||
{
|
||||
if (keystore_init_done) {
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
hap_platform_nvs_partition = hap_platform_keystore_get_nvs_partition_name();
|
||||
|
||||
// hap_platfrom_keystore_erase_partition(hap_platform_nvs_partition);
|
||||
|
||||
int err = hap_platform_keystore_init_partition(hap_platform_nvs_partition, false);
|
||||
if (err != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Error (%d) NVS init failed", err);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
/* Not cheking the return value, as this partition may be absent */
|
||||
hap_platform_factory_nvs_partition = hap_platform_keystore_get_factory_nvs_partition_name();
|
||||
hap_platform_keystore_init_partition(hap_platform_factory_nvs_partition, true);
|
||||
|
||||
keystore_init_done = true;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Keystore initialised");
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int __hap_keystore_get(const char *part_name, const char *name_space, const char *key, uint8_t *val, size_t *val_size)
|
||||
{
|
||||
if (!keystore_init_done) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int err = hap_platform_keystore_get(part_name, name_space, key, val, val_size);
|
||||
if (err != 0) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
int hap_keystore_get(const char *name_space, const char *key, uint8_t *val, size_t *val_size)
|
||||
{
|
||||
|
||||
return __hap_keystore_get(hap_platform_nvs_partition, name_space, key, val, val_size);
|
||||
}
|
||||
int hap_factory_keystore_get(const char *name_space, const char *key, uint8_t *val, size_t *val_size)
|
||||
{
|
||||
return __hap_keystore_get(hap_platform_factory_nvs_partition, name_space, key, val, val_size);
|
||||
}
|
||||
|
||||
int __hap_keystore_set(const char *part_name, const char *name_space, const char *key, const uint8_t *val, const size_t val_len)
|
||||
|
||||
{
|
||||
if (!keystore_init_done) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int err = hap_platform_keystore_set(part_name, name_space, key, val, val_len);
|
||||
if (err != 0) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_keystore_set(const char *name_space, const char *key, const uint8_t *val, const size_t val_len)
|
||||
{
|
||||
return __hap_keystore_set(hap_platform_nvs_partition, name_space, key, val, val_len);
|
||||
}
|
||||
|
||||
int hap_factory_keystore_set(const char *name_space, const char *key, const uint8_t *val, const size_t val_len)
|
||||
{
|
||||
return __hap_keystore_set(hap_platform_factory_nvs_partition, name_space, key, val, val_len);
|
||||
}
|
||||
|
||||
int hap_keystore_delete(const char *name_space, const char *key)
|
||||
{
|
||||
if (!keystore_init_done) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int err = hap_platform_keystore_delete(hap_platform_nvs_partition, name_space, key);
|
||||
if (err != 0) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_keystore_delete_namespace(const char *name_space)
|
||||
{
|
||||
if (!keystore_init_done) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int err = hap_platform_keystore_delete_namespace(hap_platform_nvs_partition, name_space);
|
||||
if (err != 0) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
void hap_keystore_erase_all_data()
|
||||
{
|
||||
hap_platfrom_keystore_erase_partition(hap_platform_nvs_partition);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_KEYSTORE_H_
|
||||
#define _HAP_KEYSTORE_H_
|
||||
#include <hap.h>
|
||||
int hap_keystore_init();
|
||||
int hap_keystore_get(const char *name_space, const char *key, uint8_t *val, size_t *val_size);
|
||||
int hap_keystore_set(const char *name_space, const char *key, const uint8_t *val, const size_t val_len);
|
||||
int hap_keystore_delete(const char *name_space, const char *key);
|
||||
int hap_keystore_delete_namespace(const char *name_space);
|
||||
int hap_factory_keystore_set(const char *name_space, const char *key, const uint8_t *val, const size_t val_len);
|
||||
void hap_keystore_erase_all_data();
|
||||
#endif /* _HAP_KEYSTORE_H_ */
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_wifi.h>
|
||||
|
||||
#include <esp_mfi_debug.h>
|
||||
#include <esp_hap_acc.h>
|
||||
#include <esp_hap_char.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_hap_ip_services.h>
|
||||
#include <esp_hap_wifi.h>
|
||||
#include <esp_hap_mdns.h>
|
||||
#include <esp_hap_keystore.h>
|
||||
#include <esp_hap_main.h>
|
||||
#include <esp_hap_wac.h>
|
||||
#include <esp_hap_bct_priv.h>
|
||||
#include <esp_hap_pair_verify.h>
|
||||
#include <hap_platform_os.h>
|
||||
#include <_esp_hap_config.h>
|
||||
|
||||
static QueueHandle_t xQueue;
|
||||
ESP_EVENT_DEFINE_BASE(HAP_EVENT);
|
||||
|
||||
const char * hap_get_version(void)
|
||||
{
|
||||
return MFI_VER;
|
||||
}
|
||||
static void hap_nw_configured_sm(hap_internal_event_t event, hap_state_t *state)
|
||||
{
|
||||
switch (event) {
|
||||
case HAP_INTERNAL_EVENT_ACC_PAIRED:
|
||||
hap_mdns_announce(false);
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_ACC_UNPAIRED:
|
||||
hap_mdns_announce(false);
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_CONFIG_NUM_UPDATED:
|
||||
hap_increment_and_save_config_num();
|
||||
hap_mdns_announce(false);
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_BCT_CHANGE_NAME:
|
||||
/* Waiting for sometime to allow the response to reach the host */
|
||||
vTaskDelay(1000 / hap_platform_os_get_msec_per_tick());
|
||||
hap_handle_bct_change_name();
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_BCT_HOT_PLUG:
|
||||
/* Waiting for sometime to allow the response to reach the host */
|
||||
vTaskDelay(1000 / hap_platform_os_get_msec_per_tick());
|
||||
hap_handle_hot_plug();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void hap_common_sm(hap_internal_event_t event)
|
||||
{
|
||||
char *reboot_reason = HAP_REBOOT_REASON_UNKNOWN;
|
||||
switch (event) {
|
||||
case HAP_INTERNAL_EVENT_RESET_PAIRINGS:
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Resetting all Pairing Information");
|
||||
/* Wait for some time before erasing the information, so that the callee
|
||||
* gets some time for any additional operations
|
||||
*/
|
||||
vTaskDelay(1000 / hap_platform_os_get_msec_per_tick());
|
||||
hap_close_all_sessions();
|
||||
hap_mdns_deannounce();
|
||||
hap_erase_controller_info();
|
||||
hap_erase_accessory_info();
|
||||
reboot_reason = HAP_REBOOT_REASON_RESET_PAIRINGS;
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_RESET_TO_FACTORY:
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Resetting to Factory Defaults");
|
||||
/* Wait for some time before erasing the information, so that the callee
|
||||
* gets some time for any additional operations
|
||||
*/
|
||||
vTaskDelay(1000 / hap_platform_os_get_msec_per_tick());
|
||||
hap_close_all_sessions();
|
||||
hap_mdns_deannounce();
|
||||
hap_keystore_erase_all_data();
|
||||
reboot_reason = HAP_REBOOT_REASON_RESET_TO_FACTORY;
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_RESET_HOMEKIT_DATA:
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Resetting all HomeKit Data");
|
||||
/* Wait for some time before erasing the information, so that the callee
|
||||
* gets some time for any additional operations
|
||||
*/
|
||||
vTaskDelay(1000 / hap_platform_os_get_msec_per_tick());
|
||||
hap_close_all_sessions();
|
||||
hap_mdns_deannounce();
|
||||
hap_erase_controller_info();
|
||||
hap_erase_network_info();
|
||||
hap_erase_accessory_info();
|
||||
reboot_reason = HAP_REBOOT_REASON_RESET_HOMEKIT_DATA;
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_REBOOT:
|
||||
vTaskDelay(1000 / hap_platform_os_get_msec_per_tick());
|
||||
/* Wait for some time and then close all the active sessions
|
||||
*/
|
||||
hap_close_all_sessions();
|
||||
hap_mdns_deannounce();
|
||||
reboot_reason = HAP_REBOOT_REASON_REBOOT_ACC;
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_RESET_NETWORK:
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Resetting Network Credentials");
|
||||
/* Wait for some time, close all the active sessions and then
|
||||
* erase network info.
|
||||
*/
|
||||
vTaskDelay(1000 / hap_platform_os_get_msec_per_tick());
|
||||
hap_close_all_sessions();
|
||||
hap_mdns_deannounce();
|
||||
hap_erase_network_info();
|
||||
reboot_reason = HAP_REBOOT_REASON_RESET_NETWORK;
|
||||
break;
|
||||
case HAP_INTERNAL_EVENT_TRIGGER_NOTIF:
|
||||
/* TODO: Avoid direct http function. Notification could be even for iCloud or BLE.
|
||||
*/
|
||||
hap_http_send_notif();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait for some time after peeforming the operations and then reboot */
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Rebooting...");
|
||||
hap_report_event(HAP_EVENT_ACC_REBOOTING, reboot_reason, strlen(reboot_reason) + 1);
|
||||
vTaskDelay(1000 / hap_platform_os_get_msec_per_tick());
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
static void hap_loop_task(void *param)
|
||||
{
|
||||
hap_state_t cur_state = HAP_STATE_NONE;
|
||||
xQueue = xQueueCreate( 10, sizeof(hap_event_ctx_t) );
|
||||
hap_event_ctx_t hap_event;
|
||||
bool loop_continue = true;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "HAP Main Loop Started");
|
||||
while (loop_continue) {
|
||||
if (xQueueReceive(xQueue, &hap_event, portMAX_DELAY) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
if (hap_event.event == HAP_INTERNAL_EVENT_LOOP_STOP) {
|
||||
loop_continue = false;
|
||||
continue;
|
||||
}
|
||||
hap_common_sm(hap_event.event);
|
||||
hap_nw_configured_sm(hap_event.event, &cur_state);
|
||||
}
|
||||
vQueueDelete(xQueue);
|
||||
xQueue = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static bool loop_started;
|
||||
int hap_loop_start()
|
||||
{
|
||||
if (!loop_started) {
|
||||
loop_started = true;
|
||||
xTaskCreate(hap_loop_task, "hap-loop", hap_priv.cfg.task_stack_size, NULL,
|
||||
hap_priv.cfg.task_priority, NULL);
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
bool is_hap_loop_started()
|
||||
{
|
||||
return loop_started;
|
||||
}
|
||||
int hap_send_event(hap_internal_event_t event)
|
||||
{
|
||||
if (!is_hap_loop_started()) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if (!xQueue) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_event_ctx_t hap_event = {
|
||||
.event = event,
|
||||
};
|
||||
BaseType_t ret;
|
||||
if (xPortInIsrContext() == pdTRUE) {
|
||||
ret = xQueueSendFromISR(xQueue, &hap_event, NULL);
|
||||
} else {
|
||||
ret = xQueueSend(xQueue, &hap_event, 0);
|
||||
}
|
||||
if (ret == pdTRUE) {
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int hap_update_config_number()
|
||||
{
|
||||
return hap_send_event(HAP_INTERNAL_EVENT_CONFIG_NUM_UPDATED);
|
||||
}
|
||||
|
||||
int hap_loop_stop()
|
||||
{
|
||||
return hap_send_event(HAP_INTERNAL_EVENT_LOOP_STOP);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void hap_network_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int event_id, void* event_data)
|
||||
{
|
||||
if (((event_base == WIFI_EVENT) && (event_id == WIFI_EVENT_STA_CONNECTED)) ||
|
||||
((event_base == ETH_EVENT) && (event_id == ETHERNET_EVENT_CONNECTED))){
|
||||
hap_send_event(HAP_INTERNAL_EVENT_NETWORK_CONNECTED);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
int hap_init(hap_transport_t method)
|
||||
{
|
||||
int ret = HAP_SUCCESS;
|
||||
if (!(method & (HAP_TRANSPORT_WIFI | HAP_TRANSPORT_ETHERNET))) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid Transport");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
hap_priv.transport = method;
|
||||
|
||||
ret = hap_keystore_init();
|
||||
if (ret != 0 ) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "HAP Key Store Init failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hap_database_init();
|
||||
if (ret != 0 ) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "HAP Database Init failed");
|
||||
return ret;
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "HAP Initialization succeeded. Version : %s", hap_get_version());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hap_deinit(void)
|
||||
{
|
||||
int ret = HAP_SUCCESS;
|
||||
//todo
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hap_reset_to_factory()
|
||||
{
|
||||
return hap_send_event(HAP_INTERNAL_EVENT_RESET_TO_FACTORY);
|
||||
}
|
||||
|
||||
int hap_reset_homekit_data()
|
||||
{
|
||||
return hap_send_event(HAP_INTERNAL_EVENT_RESET_HOMEKIT_DATA);
|
||||
}
|
||||
|
||||
int hap_reset_pairings()
|
||||
{
|
||||
return hap_send_event(HAP_INTERNAL_EVENT_RESET_PAIRINGS);
|
||||
}
|
||||
|
||||
int hap_reboot_accessory()
|
||||
{
|
||||
return hap_send_event(HAP_INTERNAL_EVENT_REBOOT);
|
||||
}
|
||||
|
||||
int hap_reset_network()
|
||||
{
|
||||
return hap_send_event(HAP_INTERNAL_EVENT_RESET_NETWORK);
|
||||
}
|
||||
|
||||
int hap_start(void)
|
||||
{
|
||||
int ret = 0;
|
||||
#ifdef CONFIG_HAP_MFI_ENABLE
|
||||
if (hap_priv.auth_type == HAP_MFI_AUTH_HW) {
|
||||
ret = hap_enable_hw_auth();
|
||||
} else if (hap_priv.auth_type == HAP_MFI_AUTH_SW) {
|
||||
ret = hap_enable_sw_auth();
|
||||
}
|
||||
#endif /* CONFIG_HAP_MFI_ENABLE */
|
||||
if (ret != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Failed to enable MFi %s authentication",
|
||||
hap_priv.auth_type == HAP_MFI_AUTH_HW ? "HW" : "SW");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
if (!hap_get_first_acc()) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Failed to start HAP. Please add an Accessory before hap_start()");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
ret = hap_acc_setup_init();
|
||||
if (ret != HAP_SUCCESS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Accessory Setup init failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hap_httpd_start();
|
||||
if (ret != HAP_SUCCESS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "HTTPD START Failed [%d]", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hap_event_queue_init();
|
||||
if (ret != HAP_SUCCESS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Queue Initialisation for Event Notifications Failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hap_loop_start();
|
||||
if (ret != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "HAP Loop Failed: [%d]", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = hap_mdns_init();
|
||||
if (ret != 0 ) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "HAP mDNS Init failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hap_ip_services_start();
|
||||
if (ret != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "HAP IP Services Start Failed [%d]", ret);
|
||||
return ret;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_stop(void)
|
||||
{
|
||||
int ret = HAP_SUCCESS;
|
||||
//todo
|
||||
return ret;
|
||||
}
|
||||
|
||||
void hap_report_event(hap_event_t event, void *data, size_t data_size)
|
||||
{
|
||||
if (hap_priv.hap_event_handler) {
|
||||
hap_priv.hap_event_handler(event, data);
|
||||
}
|
||||
esp_event_post(HAP_EVENT, event, data, data_size, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void hap_register_event_handler(hap_event_handler_t handler)
|
||||
{
|
||||
hap_priv.hap_event_handler = handler;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HAP_MAIN_LOOP_H_
|
||||
#define _HAP_MAIN_LOOP_H_
|
||||
#include <stdbool.h>
|
||||
#include <hap.h>
|
||||
|
||||
#define HAP_FF_HARDWARE_AUTH 0x01
|
||||
#define HAP_FF_SW_TOKEN_AUTH 0x02
|
||||
|
||||
|
||||
#define HAP_SF_ACC_UNPAIRED 0x01
|
||||
#define HAP_SF_ACC_UNCONFIGURED 0x02
|
||||
#define HAP_SF_PROBLEM_DETECTED 0x04
|
||||
|
||||
typedef enum {
|
||||
HAP_INTERNAL_EVENT_LOOP_STOP = 1,
|
||||
HAP_INTERNAL_EVENT_ACC_PAIRED,
|
||||
HAP_INTERNAL_EVENT_ACC_UNPAIRED,
|
||||
HAP_INTERNAL_EVENT_CONFIG_NUM_UPDATED,
|
||||
HAP_INTERNAL_EVENT_BCT_CHANGE_NAME,
|
||||
HAP_INTERNAL_EVENT_BCT_HOT_PLUG,
|
||||
HAP_INTERNAL_EVENT_RESET_PAIRINGS,
|
||||
HAP_INTERNAL_EVENT_RESET_TO_FACTORY,
|
||||
HAP_INTERNAL_EVENT_REBOOT,
|
||||
HAP_INTERNAL_EVENT_RESET_NETWORK,
|
||||
HAP_INTERNAL_EVENT_TRIGGER_NOTIF,
|
||||
HAP_INTERNAL_EVENT_RESET_HOMEKIT_DATA,
|
||||
} hap_internal_event_t;
|
||||
|
||||
typedef struct {
|
||||
hap_internal_event_t event;
|
||||
} hap_event_ctx_t;
|
||||
|
||||
typedef enum {
|
||||
HAP_STATE_NONE = 0,
|
||||
HAP_STATE_NW_UNCONFIGURED,
|
||||
HAP_STATE_NW_CONFIGURED,
|
||||
} hap_state_t;
|
||||
|
||||
int hap_loop_start();
|
||||
int hap_loop_stop();
|
||||
int hap_send_event(hap_internal_event_t event);
|
||||
int hap_update_config_number();
|
||||
bool is_hap_loop_started();
|
||||
void hap_report_event(hap_event_t event, void *data, size_t data_size);
|
||||
int hap_enable_hw_auth(void);
|
||||
int hap_enable_sw_auth(void);
|
||||
|
||||
#endif /* _HAP_MAIN_LOOP_H_ */
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <esp_hap_mdns.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
static bool mdns_init_done;
|
||||
|
||||
int hap_mdns_serv_start(hap_mdns_handle_t *handle, const char *name, const char *type,
|
||||
const char *protocol, int port, mdns_txt_item_t *txt_records, size_t num_txt)
|
||||
{
|
||||
strcpy(handle->type, type);
|
||||
strcpy(handle->proto, protocol);
|
||||
if (mdns_service_add(name, type, protocol, port, txt_records, num_txt) != 0) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_mdns_serv_update_txt(hap_mdns_handle_t *handle, mdns_txt_item_t *txt_records, size_t num_txt)
|
||||
{
|
||||
if (mdns_service_txt_set(handle->type, handle->proto, txt_records, num_txt) != 0) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_mdns_serv_name_change(hap_mdns_handle_t *handle, const char * instance_name)
|
||||
{
|
||||
if (mdns_service_instance_name_set(handle->type, handle->proto, instance_name) == ESP_OK) {
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int hap_mdns_serv_stop(hap_mdns_handle_t *handle)
|
||||
{
|
||||
if (mdns_service_remove(handle->type, handle->proto) == ESP_OK) {
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int hap_mdns_init()
|
||||
{
|
||||
int ret = HAP_SUCCESS;
|
||||
if (!mdns_init_done) {
|
||||
ret = mdns_init();
|
||||
if (ret == ESP_OK) {
|
||||
mdns_hostname_set("MyHost");
|
||||
mdns_init_done = true;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "mDNS initialised");
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
}
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int hap_mdns_deinit()
|
||||
{
|
||||
mdns_free();
|
||||
mdns_init_done = false;
|
||||
return HAP_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_MDNS_H_
|
||||
#define _HAP_MDNS_H_
|
||||
|
||||
#include <mdns.h>
|
||||
#include <hap.h>
|
||||
|
||||
typedef struct {
|
||||
char type[32];
|
||||
char proto[32];
|
||||
} hap_mdns_handle_t;
|
||||
|
||||
int hap_mdns_serv_start(hap_mdns_handle_t *handle, const char *name, const char *type,
|
||||
const char *protocol, int port, mdns_txt_item_t *txt_records, size_t num_txt);
|
||||
int hap_mdns_serv_update_txt(hap_mdns_handle_t *handle, mdns_txt_item_t *txt_records, size_t num_txt);
|
||||
int hap_mdns_serv_name_change(hap_mdns_handle_t *handle, const char * instance_name);
|
||||
int hap_mdns_serv_stop(hap_mdns_handle_t *handle);
|
||||
int hap_mdns_init();
|
||||
int hap_mdns_deinit();
|
||||
|
||||
#endif /* _HAP_MDNS_H_ */
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <sodium/crypto_aead_chacha20poly1305.h>
|
||||
#include <byte_convert.h>
|
||||
|
||||
#include <esp_mfi_debug.h>
|
||||
#include <hap.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_hap_pair_common.h>
|
||||
#include <esp_hap_pair_verify.h>
|
||||
|
||||
#define HAP_MAX_NW_FRAME_SIZE 1024 /* As per HAP Specifications */
|
||||
#define AUTH_TAG_LEN 16
|
||||
typedef struct {
|
||||
uint8_t pkt_size[2];
|
||||
uint8_t data[HAP_MAX_NW_FRAME_SIZE];
|
||||
/* The Poly auth tag buffer will get used only if the data length is
|
||||
* greater than HAP_MAX_NW_FRAME_SIZE - 16.
|
||||
* Else, the auth tag would be included in the data buffer itself
|
||||
*/
|
||||
uint8_t poly_auth_tag[AUTH_TAG_LEN];
|
||||
} hap_encrypt_frame_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t pkt_size;
|
||||
uint16_t bytes_read;
|
||||
uint8_t data[HAP_MAX_NW_FRAME_SIZE + AUTH_TAG_LEN];
|
||||
hap_secure_session_t *session;
|
||||
} hap_decrypt_frame_t;
|
||||
|
||||
typedef int (*hap_decrypt_read_fn_t) (uint8_t *buf, int buf_size, void *context);
|
||||
static int min(int val1, int val2)
|
||||
{
|
||||
if (val1 < val2)
|
||||
return val1;
|
||||
return val2;
|
||||
}
|
||||
static int hap_httpd_raw_recv(uint8_t *buf, int buf_size, void *context)
|
||||
{
|
||||
int sock = *((int *)context);
|
||||
return recv(sock, buf, buf_size, 0);
|
||||
}
|
||||
|
||||
/* Frame format as per HAP Specifications:
|
||||
* <2: AAD for Little Endian length of encrypted data (n) in bytes>
|
||||
* <n: Encrypted data according to AEAD algorithm, upto 1024 bytes>
|
||||
* <16: authTag according to AEAD algorithm>
|
||||
*/
|
||||
int hap_encrypt_data(hap_encrypt_frame_t *frame, hap_secure_session_t *session,
|
||||
uint8_t *buf, int buflen)
|
||||
{
|
||||
if (!session)
|
||||
return HAP_FAIL;
|
||||
put_u16_le(frame->pkt_size, buflen);
|
||||
/* Encrypt the received data as per Chacha20-Poly1305 AEAD algorithm.
|
||||
* The authTag will be appended at the end of data. Hence, pointer given as
|
||||
* frame->data + nlen
|
||||
*/
|
||||
unsigned long long mlen = 16;
|
||||
uint8_t newnonce[12];
|
||||
memset(newnonce, 0, sizeof newnonce);
|
||||
memcpy(newnonce+4, session->encrypt_nonce, 8);
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt_detached(frame->data, frame->data + buflen, &mlen,
|
||||
buf, buflen, frame->pkt_size, 2, NULL, newnonce, session->encrypt_key);
|
||||
|
||||
/* Increment nonce after every frame */
|
||||
uint64_t int_nonce = get_u64_le(session->encrypt_nonce);
|
||||
int_nonce++;
|
||||
put_u64_le(session->encrypt_nonce, int_nonce);
|
||||
return 2 + buflen + 16; /* Total length of the encrypted data */
|
||||
}
|
||||
|
||||
#include <esp_hap_main.h>
|
||||
void hap_close_ctrl_sessions_fix(hap_secure_session_t *session)
|
||||
{
|
||||
if (!session)
|
||||
return;
|
||||
int i;
|
||||
printf("---- hap_close_ctrl_sessions_fix begin -----\n");
|
||||
for (i = 0; i < HAP_MAX_SESSIONS; i++) {
|
||||
if (!hap_priv.sessions[i])
|
||||
continue;
|
||||
if (hap_priv.sessions[i] == session) {
|
||||
hap_report_event(HAP_EVENT_CTRL_DISCONNECTED, (session->ctrl->info.id),
|
||||
sizeof((session->ctrl->info.id)));
|
||||
/* TODO: Use some generic function and not a direct HTTPD function
|
||||
*/
|
||||
printf("---- trigger_close fd: %d\n", hap_priv.sessions[i]->conn_identifier);
|
||||
httpd_sess_trigger_close(hap_priv.server, hap_priv.sessions[i]->conn_identifier);
|
||||
}
|
||||
}
|
||||
printf("---- hap_close_ctrl_sessions_fix end ----\n");
|
||||
}
|
||||
|
||||
int hap_decrypt_error(hap_secure_session_t *session)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Decryption error/Connection lost. Marking session as invalid");
|
||||
if (session) {
|
||||
session->state = STATE_INVALID;
|
||||
//hap_close_ctrl_sessions(session->ctrl);
|
||||
hap_close_ctrl_sessions_fix(session);
|
||||
}
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int hap_decrypt_data(hap_decrypt_frame_t *frame, hap_secure_session_t *session,
|
||||
void *buf, int buf_size, hap_decrypt_read_fn_t read_fn, void *context)
|
||||
{
|
||||
if (!frame || !session)
|
||||
return -1;
|
||||
if (frame->session != session) {
|
||||
memset(frame, 0, sizeof(hap_decrypt_frame_t));
|
||||
frame->session = session;
|
||||
}
|
||||
if ((frame->pkt_size - frame->bytes_read) == 0) {
|
||||
int len = read_fn(frame->data, 2, context);
|
||||
if (len == 0) { //nothing received, but we should NOT consider this is a 'decrypt_error'
|
||||
return 0;
|
||||
}
|
||||
if (len < 2) {
|
||||
//len is -1 or 1
|
||||
//len = -1: socket disconnected
|
||||
//len = 1: try receiving 2 bytes timeout (SO_RCVTIMEO is set in esp_hap_ip_services.c)
|
||||
printf("---- error 1 ----, len: %d\n", len);
|
||||
//Decryption error/Connection lost on ESP32 with multiple Apple devices running Home
|
||||
//https://github.com/espressif/esp-homekit-sdk/issues/14
|
||||
//---- error 1 ----, len: 0
|
||||
return hap_decrypt_error(session);
|
||||
}
|
||||
|
||||
frame->pkt_size = get_u16_le(frame->data);
|
||||
frame->bytes_read = 0;
|
||||
uint16_t bytes_to_read = frame->pkt_size + AUTH_TAG_LEN; /* +AUTH_TAG_LEN as the receivedd packet will also have the auth Tag */
|
||||
while (bytes_to_read) {
|
||||
int num_bytes = read_fn(&frame->data[frame->bytes_read],
|
||||
bytes_to_read, context);
|
||||
if (num_bytes <= 0) {
|
||||
printf("---- error 2 ----\n");
|
||||
return hap_decrypt_error(session);
|
||||
}
|
||||
bytes_to_read -= num_bytes;
|
||||
frame->bytes_read += num_bytes;
|
||||
}
|
||||
frame->bytes_read -= AUTH_TAG_LEN; /* -AUTH_TAG_LEN to get only the data length */
|
||||
uint8_t aad[2];
|
||||
int ret;
|
||||
put_u16_le(aad, frame->pkt_size); /* Packet size is the AAD for AEAD */
|
||||
uint8_t newnonce[12];
|
||||
memset(newnonce, 0, sizeof newnonce);
|
||||
memcpy(newnonce+4, session->decrypt_nonce, 8);
|
||||
ret = crypto_aead_chacha20poly1305_ietf_decrypt_detached(frame->data, NULL, frame->data, frame->pkt_size,
|
||||
&frame->data[frame->bytes_read], aad, 2, newnonce, session->decrypt_key);
|
||||
if (ret != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "AEAD decryption failure");
|
||||
printf("---- error 3 ----\n");
|
||||
return hap_decrypt_error(session);
|
||||
}
|
||||
frame->bytes_read = 0;
|
||||
/* Increment nonce after every frame */
|
||||
int64_t int_nonce = get_u64_le(session->decrypt_nonce);
|
||||
int_nonce++;
|
||||
put_u64_le(session->decrypt_nonce, int_nonce);
|
||||
}
|
||||
int bytes = min(frame->pkt_size - frame->bytes_read, buf_size);
|
||||
memcpy(buf, &frame->data[frame->bytes_read], bytes);
|
||||
frame->bytes_read += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int hap_httpd_send(httpd_handle_t hd, int sockfd, const char *buf, unsigned buf_len, int flags)
|
||||
{
|
||||
hap_secure_session_t *session = httpd_sess_get_ctx(hap_priv.server, sockfd);
|
||||
if (session && (session->state == STATE_VERIFIED)) {
|
||||
uint8_t *buf_ptr = (uint8_t *)buf;
|
||||
int tmp_buf_len = buf_len;
|
||||
while (tmp_buf_len) {
|
||||
hap_encrypt_frame_t encrypt_frame;
|
||||
memset(&encrypt_frame, 0, sizeof(encrypt_frame));
|
||||
int len = min(tmp_buf_len, HAP_MAX_NW_FRAME_SIZE);
|
||||
int send_len = hap_encrypt_data(&encrypt_frame, session, buf_ptr, len);
|
||||
if (send(sockfd, (uint8_t *)&encrypt_frame, send_len, flags) <= 0)
|
||||
return HAP_FAIL;
|
||||
tmp_buf_len -= len;
|
||||
buf_ptr += len;
|
||||
}
|
||||
/* Return the total length at the end since this API expects so
|
||||
*/
|
||||
return buf_len;
|
||||
}
|
||||
return send(sockfd, buf, buf_len, flags);
|
||||
}
|
||||
|
||||
int hap_httpd_recv(httpd_handle_t hd, int sockfd, char *buf, unsigned buf_len, int flags)
|
||||
{
|
||||
static hap_decrypt_frame_t decrypt_frame;
|
||||
hap_secure_session_t *session = httpd_sess_get_ctx(hap_priv.server, sockfd);
|
||||
if (session) {
|
||||
if (session->state == STATE_VERIFIED) {
|
||||
return hap_decrypt_data(&decrypt_frame, session, buf, buf_len,
|
||||
hap_httpd_raw_recv, &sockfd);
|
||||
} else {
|
||||
/* If the session state is invalid, we return an error.
|
||||
* The errno is set here explicitly, so that even if the higher layers
|
||||
* query for it, they do not get a stale value.
|
||||
* EACCES means permission denied, which indeed is the case here.
|
||||
*/
|
||||
errno = EACCES;
|
||||
return HAP_FAIL;
|
||||
}
|
||||
}
|
||||
return recv(sockfd, buf, buf_len, sockfd);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_NETWORK_IO_H_
|
||||
#define _HAP_NETWORK_IO_H_
|
||||
#include <stdint.h>
|
||||
#include <hap_platform_httpd.h>
|
||||
int hap_httpd_send(httpd_handle_t hd, int sockfd, const char *buf, unsigned buf_len, int flags);
|
||||
int hap_httpd_recv(httpd_handle_t hd, int sockfd, char *buf, unsigned buf_len, int flags);
|
||||
|
||||
#endif /* _HAP_NETWORK_IO_H_ */
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <esp_hap_pair_common.h>
|
||||
|
||||
void hap_tlv_data_init(hap_tlv_data_t *tlv_data, uint8_t *buf, int buf_size)
|
||||
{
|
||||
tlv_data->bufptr = buf;
|
||||
tlv_data->bufsize = buf_size;
|
||||
tlv_data->curlen = 0;
|
||||
}
|
||||
|
||||
int get_tlv_length(uint8_t *buf, int buflen, uint8_t type)
|
||||
{
|
||||
if (!buf )
|
||||
return -1;
|
||||
int curlen = 0;
|
||||
int val_len = 0;
|
||||
bool found = false;
|
||||
while (buflen > 0) {
|
||||
if (buf[curlen] == type) {
|
||||
uint8_t len = buf[curlen + 1];
|
||||
if ((buflen - len) < 2)
|
||||
return -1;
|
||||
val_len += len;
|
||||
if (len < 255)
|
||||
return val_len;
|
||||
else
|
||||
found = true;
|
||||
|
||||
} else if (found)
|
||||
return val_len;
|
||||
|
||||
/* buf[curlen +1] will give the Length */
|
||||
buflen -= (2 + buf[curlen + 1]);
|
||||
curlen += (2 + buf[curlen + 1]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int get_value_from_tlv(uint8_t *buf, int buflen, uint8_t type, void *val, int val_size)
|
||||
{
|
||||
if (!buf || !val)
|
||||
return -1;
|
||||
int curlen = 0;
|
||||
int val_len = 0;
|
||||
bool found = false;
|
||||
while (buflen > 0) {
|
||||
if (buf[curlen] == type) {
|
||||
uint8_t len = buf[curlen + 1];
|
||||
if ((val_size < len) || ((buflen - len) < 2))
|
||||
return -1;
|
||||
memcpy(val + val_len, &buf[curlen + 2], len);
|
||||
val_len += len;
|
||||
val_size -= len;
|
||||
if (len < 255)
|
||||
return val_len;
|
||||
else
|
||||
found = true;
|
||||
|
||||
} else if (found)
|
||||
return val_len;
|
||||
|
||||
/* buf[curlen +1] will give the Length */
|
||||
buflen -= (2 + buf[curlen + 1]);
|
||||
curlen += (2 + buf[curlen + 1]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int add_tlv(hap_tlv_data_t *tlv_data, uint8_t type, int len, void *val)
|
||||
{
|
||||
if(!tlv_data->bufptr || ((len + 2) > (tlv_data->bufsize - tlv_data->curlen)))
|
||||
return -1;
|
||||
uint8_t *buf_ptr = (uint8_t *)val;
|
||||
int orig_len = tlv_data->curlen;
|
||||
do {
|
||||
tlv_data->bufptr[tlv_data->curlen++] = type;
|
||||
int tmp_len;
|
||||
if (len > 255)
|
||||
tmp_len = 255;
|
||||
else
|
||||
tmp_len = len;
|
||||
tlv_data->bufptr[tlv_data->curlen++] = tmp_len;
|
||||
memcpy(&tlv_data->bufptr[tlv_data->curlen], buf_ptr, tmp_len);
|
||||
tlv_data->curlen += tmp_len;
|
||||
buf_ptr += tmp_len;
|
||||
len -= tmp_len;
|
||||
} while (len);
|
||||
return tlv_data->curlen - orig_len;
|
||||
}
|
||||
void hap_prepare_error_tlv(uint8_t state, uint8_t error, void *buf, int bufsize, int *outlen)
|
||||
{
|
||||
hap_tlv_data_t tlv_data;
|
||||
tlv_data.bufptr = buf;
|
||||
tlv_data.bufsize = bufsize;
|
||||
tlv_data.curlen = 0;
|
||||
/* Not doing any error handling because the size required for "state" and "error" will
|
||||
* be too small to cause any error, and we anyways dont have any specific action to
|
||||
* do in case if error in add_tlv()
|
||||
*/
|
||||
add_tlv(&tlv_data, kTLVType_State, sizeof(state), &state);
|
||||
add_tlv(&tlv_data, kTLVType_Error, sizeof(error), &error);
|
||||
*outlen = tlv_data.curlen;
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_PAIR_COMMON_H_
|
||||
#define _HAP_PAIR_COMMON_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_hap_controllers.h>
|
||||
#define ENCRYPT_KEY_LEN 32
|
||||
#define POLY_AUTHTAG_LEN 16
|
||||
#define CURVE_KEY_LEN 32
|
||||
#define ED_SIGN_LEN 64
|
||||
#define NONCE_LEN 8
|
||||
|
||||
#define STATE_M0 0
|
||||
#define STATE_M1 1
|
||||
#define STATE_M2 2
|
||||
#define STATE_M3 3
|
||||
#define STATE_M4 4
|
||||
#define STATE_M5 5
|
||||
#define STATE_M6 6
|
||||
#define STATE_VERIFIED 0x55
|
||||
#define STATE_INVALID 0xaa
|
||||
|
||||
typedef enum {
|
||||
HAP_METHOD_RESERVED = 0,
|
||||
HAP_METHOD_PAIR_SETUP = 1,
|
||||
HAP_METHOD_PAIR_VERIFY = 2,
|
||||
HAP_METHOD_ADD_PAIRING = 3,
|
||||
HAP_METHOD_REMOVE_PAIRING = 4,
|
||||
HAP_METHOD_LIST_PAIRINGS = 5,
|
||||
} hap_pairing_methods_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
kTLVError_Unknown = 0x01,
|
||||
kTLVError_Authentication = 0x02,
|
||||
kTLVError_Backoff = 0x03,
|
||||
kTLVError_MaxPeers = 0x04,
|
||||
kTLVError_MaxTries = 0x05,
|
||||
kTLVError_Unavailable = 0x06,
|
||||
kTLVError_Busy = 0x07,
|
||||
} hap_tlv_error_t;
|
||||
|
||||
typedef enum {
|
||||
kTLVType_Method = 0x00,
|
||||
kTLVType_Identifier = 0x01,
|
||||
kTLVType_Salt = 0x02,
|
||||
kTLVType_PublicKey = 0x03,
|
||||
kTLVType_Proof = 0x04,
|
||||
kTLVType_EncryptedData = 0x05,
|
||||
kTLVType_State = 0x06,
|
||||
kTLVType_Error = 0x07,
|
||||
kTLVType_RetryDelay = 0x08,
|
||||
kTLVType_Certificate = 0x09,
|
||||
kTLVType_Signature = 0x0a,
|
||||
kTLVType_Permissions = 0x0b,
|
||||
kTLVType_FragmentedData = 0x0c,
|
||||
kTLVType_FragmentLast = 0x0d,
|
||||
kTLVType_Flags = 0x13,
|
||||
kTLVType_OwnershipProofToken = 0x1A,
|
||||
kTLVType_ProductData = 0x1C,
|
||||
kTLVType_Separator = 0xff,
|
||||
} hap_tlv_type_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *bufptr;
|
||||
int bufsize;
|
||||
int curlen;
|
||||
} hap_tlv_data_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t state;
|
||||
uint8_t encrypt_key[ENCRYPT_KEY_LEN];
|
||||
uint8_t decrypt_key[ENCRYPT_KEY_LEN];
|
||||
uint8_t encrypt_nonce[NONCE_LEN];
|
||||
uint8_t decrypt_nonce[NONCE_LEN];
|
||||
hap_ctrl_data_t *ctrl;
|
||||
uint64_t pid;
|
||||
int64_t ttl;
|
||||
int64_t prepare_time;
|
||||
/* TODO: As of now, this identifier will be the socket
|
||||
* number, since only http is supported.
|
||||
* Need to make this generic later.
|
||||
*/
|
||||
int conn_identifier;
|
||||
} hap_secure_session_t;
|
||||
|
||||
void hap_tlv_data_init(hap_tlv_data_t *tlv_data, uint8_t *buf, int buf_size);
|
||||
int get_value_from_tlv(uint8_t *buf, int buf_len, uint8_t type, void *val, int val_size);
|
||||
int get_tlv_length(uint8_t *buf, int buflen, uint8_t type);
|
||||
int add_tlv(hap_tlv_data_t *tlv_data, uint8_t type, int len, void *val);
|
||||
void hap_prepare_error_tlv(uint8_t state, uint8_t error, void *buf, int buf_size, int *out_len);
|
||||
#endif /* _HAP_PAIR_COMMON_H_ */
|
|
@ -0,0 +1,563 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <mu_srp.h>
|
||||
#include <sodium/crypto_aead_chacha20poly1305.h>
|
||||
#include <hkdf-sha.h>
|
||||
#include <sodium/crypto_sign_ed25519.h>
|
||||
#include <hexdump.h>
|
||||
#include <hap_platform_memory.h>
|
||||
#include <hap_platform_os.h>
|
||||
|
||||
#include <esp_hap_pair_common.h>
|
||||
#include <esp_hap_pair_setup.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_hap_main.h>
|
||||
#include <esp_hap_acc.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
/* Maximum attempts allowed for Pair Setup, as per HAP Specifications */
|
||||
#define HAP_PAIR_SETUP_MAX_ATTEMPTS 100
|
||||
|
||||
#define PAIR_SETUP_ENCRYPT_SALT "Pair-Setup-Encrypt-Salt"
|
||||
#define PAIR_SETUP_ENCRYPT_INFO "Pair-Setup-Encrypt-Info"
|
||||
#define PAIR_SETUP_CTRL_SIGN_SALT "Pair-Setup-Controller-Sign-Salt"
|
||||
#define PAIR_SETUP_CTRL_SIGN_INFO "Pair-Setup-Controller-Sign-Info"
|
||||
#define PAIR_SETUP_ACC_SIGN_SALT "Pair-Setup-Accessory-Sign-Salt"
|
||||
#define PAIR_SETUP_ACC_SIGN_INFO "Pair-Setup-Accessory-Sign-Info"
|
||||
|
||||
#define PS_CTX_INIT 1
|
||||
#define PS_CTX_DEINIT 2
|
||||
|
||||
/* Timeout if pair setup not completed in 1 minute (60sec)*/
|
||||
#define HAP_SETUP_TIMEOUT_IN_TICKS ((40 * 1000) / hap_platform_os_get_msec_per_tick())
|
||||
|
||||
static int hap_pair_setup_process_srp_start(pair_setup_ctx_t *ps_ctx, uint8_t *buf, int inlen,
|
||||
int bufsize, int *outlen)
|
||||
{
|
||||
/* Pair setup is not allowed if the accessory is already paired */
|
||||
if (is_accessory_paired()) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Accessory is already paired. "
|
||||
"Please use \"Add Pairing\" to add more controllers");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unavailable, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/* Pair Setup is not allowed if the failed attempts have exceeded the
|
||||
* maximum allowed attempts.
|
||||
*/
|
||||
if (hap_priv.pair_attempts >= HAP_PAIR_SETUP_MAX_ATTEMPTS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Too many attempts. Aborting");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_MaxTries, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
ps_ctx->ctrl = hap_controller_get_empty_loc();
|
||||
if (!ps_ctx->ctrl) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "No empty controller slot. Aborting");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t state;
|
||||
if ((get_value_from_tlv(buf, inlen, kTLVType_State, &state, sizeof(state)) < 0) ||
|
||||
(get_value_from_tlv(buf, inlen, kTLVType_Method,
|
||||
&ps_ctx->method, sizeof(ps_ctx->method)) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid TLVs received");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if (state != STATE_M1) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Incorrect State received");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Setup M1 Received");
|
||||
|
||||
int flags_len;
|
||||
if ((flags_len = get_value_from_tlv(buf, inlen, kTLVType_Flags, &ps_ctx->pairing_flags, sizeof(ps_ctx->pairing_flags))) > 0) {
|
||||
ps_ctx->pairing_flags_len = flags_len;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Got pairing flags %x", ps_ctx->pairing_flags);
|
||||
|
||||
/* If the Split pairing flag is not set, it is an error */
|
||||
if (!(ps_ctx->pairing_flags & PAIR_FLAG_SPLIT)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Pairing Flags received, but the Split flag is not set");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/* If either the current or the previous request didn't have transient flag set, return authentication error */
|
||||
if (!((ps_ctx->pairing_flags | hap_priv.pairing_flags) & PAIR_FLAG_TRANSIENT)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Split pairing received before preceding Transient Pairing");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
}
|
||||
hap_priv.pairing_flags = ps_ctx->pairing_flags;
|
||||
|
||||
int len_B = 0;
|
||||
char *bytes_B;
|
||||
|
||||
/* Create SRP Salt and Verifier for the provided pairing PIN */
|
||||
mu_srp_init(&ps_ctx->srp_hd, MU_NG_3072);
|
||||
|
||||
/* If a setup code is explicitly set, use it */
|
||||
if (hap_priv.setup_code) {
|
||||
ps_ctx->len_s = 16;
|
||||
mu_srp_srv_pubkey(&ps_ctx->srp_hd, "Pair-Setup", (const char*)hap_priv.setup_code, strlen(hap_priv.setup_code),
|
||||
ps_ctx->len_s, &bytes_B, &len_B, &ps_ctx->bytes_s);
|
||||
} else {
|
||||
/* Else, use the salt and verifier for SRP. This should be the default production case
|
||||
*/
|
||||
if (mu_srp_set_salt_verifier(&ps_ctx->srp_hd, (char *)hap_priv.setup_info->salt, sizeof(hap_priv.setup_info->salt),
|
||||
(char *)hap_priv.setup_info->verifier, sizeof(hap_priv.setup_info->verifier)) < 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "SRP-6a Salt-Verifier Init Failed");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
ps_ctx->bytes_s = (char *)hap_priv.setup_info->salt;
|
||||
ps_ctx->len_s = sizeof(hap_priv.setup_info->salt);
|
||||
mu_srp_srv_pubkey_from_salt_verifier(&ps_ctx->srp_hd, &bytes_B, &len_B);
|
||||
}
|
||||
if (!ps_ctx->bytes_s || !bytes_B) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "SRP-6a Verifier Creation Failed");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
hex_dbg_with_name("salt", (uint8_t *)ps_ctx->bytes_s, ps_ctx->len_s);
|
||||
hex_dbg_with_name("acc_srp_public_key", (uint8_t *)bytes_B, len_B);
|
||||
|
||||
/* Construct the response M2 */
|
||||
hap_tlv_data_t tlv_data;
|
||||
tlv_data.bufptr = buf;
|
||||
tlv_data.bufsize = bufsize;
|
||||
tlv_data.curlen = 0;
|
||||
state = STATE_M2;
|
||||
if ((add_tlv(&tlv_data, kTLVType_State, 1, &state) < 0) ||
|
||||
(add_tlv(&tlv_data, kTLVType_PublicKey, len_B, (void *)bytes_B) < 0) ||
|
||||
(add_tlv(&tlv_data, kTLVType_Salt, ps_ctx->len_s, ps_ctx->bytes_s) < 0 )) {
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
/* Not adding any error check here, because without the pairing flags, the pairing will
|
||||
* anyways fail later.
|
||||
*/
|
||||
if (ps_ctx->pairing_flags_len) {
|
||||
add_tlv(&tlv_data, kTLVType_Flags, ps_ctx->pairing_flags_len, &ps_ctx->pairing_flags);
|
||||
}
|
||||
*outlen = tlv_data.curlen;
|
||||
ps_ctx->state = state;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Setup M2 Successful");
|
||||
hap_report_event(HAP_EVENT_PAIRING_STARTED, NULL, 0);
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int hap_pair_setup_process_srp_verify(pair_setup_ctx_t *ps_ctx, uint8_t *buf, int inlen,
|
||||
int bufsize, int *outlen)
|
||||
{
|
||||
uint8_t state;
|
||||
char ctrl_public_key[384];
|
||||
int ctrl_public_key_len;
|
||||
char ctrl_proof[64];
|
||||
int ctrl_proof_len;
|
||||
|
||||
if ((get_value_from_tlv(buf, inlen, kTLVType_State, &state, sizeof(state)) < 0) ||
|
||||
((ctrl_public_key_len = get_value_from_tlv(buf, inlen, kTLVType_PublicKey,
|
||||
ctrl_public_key, sizeof(ctrl_public_key))) < 0) ||
|
||||
((ctrl_proof_len = get_value_from_tlv(buf, inlen, kTLVType_Proof,
|
||||
ctrl_proof, sizeof(ctrl_proof))) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid TLVs received");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if (state != STATE_M3) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Incorrect State received");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Setup M3 Received");
|
||||
|
||||
hex_dbg_with_name("ctrl_srp_public_key", (uint8_t *)ctrl_public_key, ctrl_public_key_len);
|
||||
hex_dbg_with_name("ctrl_proof", (uint8_t *)ctrl_proof, ctrl_proof_len);
|
||||
mu_srp_get_session_key(&ps_ctx->srp_hd, ctrl_public_key, ctrl_public_key_len, &ps_ctx->shared_secret, &ps_ctx->secret_len);
|
||||
char host_proof[SHA512HashSize];
|
||||
int ret = mu_srp_exchange_proofs(&ps_ctx->srp_hd, "Pair-Setup", ctrl_proof, host_proof);
|
||||
if (ret != 1) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "SRP Verify: Controller Authentication failed");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
hap_report_event(HAP_EVENT_PAIRING_ABORTED, NULL, 0);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
int acc_proof_length = SHA512HashSize;
|
||||
|
||||
hkdf(SHA512, (uint8_t *) PAIR_SETUP_ENCRYPT_SALT, strlen(PAIR_SETUP_ENCRYPT_SALT),
|
||||
(uint8_t *)ps_ctx->shared_secret, ps_ctx->secret_len,
|
||||
(uint8_t *) PAIR_SETUP_ENCRYPT_INFO, strlen(PAIR_SETUP_ENCRYPT_INFO),
|
||||
ps_ctx->session_key, sizeof(ps_ctx->session_key));
|
||||
|
||||
/* Construct the response M4 */
|
||||
hap_tlv_data_t tlv_data;
|
||||
tlv_data.bufptr = buf;
|
||||
tlv_data.bufsize = bufsize;
|
||||
tlv_data.curlen = 0;
|
||||
state = STATE_M4;
|
||||
if ((add_tlv(&tlv_data, kTLVType_State, 1, &state) < 0) ||
|
||||
(add_tlv(&tlv_data, kTLVType_Proof, acc_proof_length,
|
||||
(void *)host_proof) < 0)) {
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "TLV creation failed");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hap_tlv_error_t tlv_error = 0;
|
||||
if (hap_pair_setup_manage_mfi_auth(ps_ctx, &tlv_data, &tlv_error) != ESP_OK) {
|
||||
hap_prepare_error_tlv(STATE_M4, tlv_error, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
*outlen = tlv_data.curlen;
|
||||
ps_ctx->state = state;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Setup M4 Successful");
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
static int hap_pair_setup_process_exchange(pair_setup_ctx_t *ps_ctx, uint8_t *buf, int inlen,
|
||||
int bufsize, int *outlen)
|
||||
{
|
||||
uint8_t state;
|
||||
uint8_t edata[220];
|
||||
int edata_len;
|
||||
int ret;
|
||||
|
||||
if ((get_value_from_tlv(buf, inlen, kTLVType_State, &state, sizeof(state)) < 0) ||
|
||||
((edata_len = get_value_from_tlv(buf, inlen, kTLVType_EncryptedData,
|
||||
edata, sizeof(edata))) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid TLVs received");
|
||||
hap_prepare_error_tlv(STATE_M6, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hex_dbg_with_name("recv_encrypted_data", edata, edata_len);
|
||||
if (state != STATE_M5) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Incorrect State received");
|
||||
hap_prepare_error_tlv(STATE_M6, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Setup M5 Received");
|
||||
edata_len -= 16; /* 16 bytes for the authTag */
|
||||
uint8_t newnonce[12];
|
||||
memset(newnonce, 0, sizeof newnonce);
|
||||
memcpy(newnonce+4, (uint8_t *)PS_NONCE2, 8);
|
||||
ret = crypto_aead_chacha20poly1305_ietf_decrypt_detached(edata, NULL, edata, edata_len,
|
||||
&edata[edata_len], NULL, 0, newnonce, ps_ctx->session_key);
|
||||
if (ret != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Decryption Failed");
|
||||
hap_prepare_error_tlv(STATE_M6, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hex_dbg_with_name("subtlv", edata, edata_len);
|
||||
int ctrl_id_len;
|
||||
unsigned char ed_sign[64];
|
||||
unsigned long long ed_sign_len;
|
||||
if (((ctrl_id_len = get_value_from_tlv(edata, edata_len, kTLVType_Identifier,
|
||||
ps_ctx->ctrl->info.id, sizeof(ps_ctx->ctrl->info.id))) < 0) ||
|
||||
(get_value_from_tlv(edata, edata_len, kTLVType_PublicKey,
|
||||
ps_ctx->ctrl->info.ltpk, ED_KEY_LEN) != ED_KEY_LEN) ||
|
||||
(get_value_from_tlv(edata, edata_len, kTLVType_Signature,
|
||||
ed_sign, sizeof(ed_sign)) != sizeof(ed_sign))) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid subTLV received");
|
||||
hap_prepare_error_tlv(STATE_M6, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
ps_ctx->ctrl->info.id[ctrl_id_len] = 0; /* NULL termination */
|
||||
hex_dbg_with_name("ctrl_id", (uint8_t *)ps_ctx->ctrl->info.id, ctrl_id_len);
|
||||
hex_dbg_with_name("ltpkc", ps_ctx->ctrl->info.ltpk, ED_KEY_LEN);
|
||||
hex_dbg_with_name("ctrl_sign", ed_sign, sizeof(ed_sign));
|
||||
|
||||
/* Derive iOSDeviceX from SRP shared secret using HKDF-SHA512 */
|
||||
uint8_t ios_device_x[32];
|
||||
hkdf(SHA512, (unsigned char *) PAIR_SETUP_CTRL_SIGN_SALT,
|
||||
strlen(PAIR_SETUP_CTRL_SIGN_SALT), (uint8_t *)ps_ctx->shared_secret, ps_ctx->secret_len,
|
||||
(unsigned char *) PAIR_SETUP_CTRL_SIGN_INFO,
|
||||
strlen(PAIR_SETUP_CTRL_SIGN_INFO),
|
||||
ios_device_x, sizeof(ios_device_x));
|
||||
/* Construct iOSDeviceInfo by concatenating
|
||||
* iOSDeviceX
|
||||
* iOSDevicePairingID (ctrl_id)
|
||||
* iOSDeviceLTPK (ltpkc)
|
||||
*/
|
||||
uint8_t info_buf[HAP_CTRL_ID_LEN + 32 + ED_KEY_LEN];
|
||||
uint8_t *ios_dev_info = info_buf;
|
||||
int ios_dev_info_len = 0;
|
||||
memcpy(ios_dev_info, ios_device_x, sizeof(ios_device_x));
|
||||
ios_dev_info_len += sizeof(ios_device_x);
|
||||
memcpy(&ios_dev_info[ios_dev_info_len], ps_ctx->ctrl->info.id, ctrl_id_len);
|
||||
ios_dev_info_len += ctrl_id_len;
|
||||
memcpy(&ios_dev_info[ios_dev_info_len], ps_ctx->ctrl->info.ltpk, ED_KEY_LEN);
|
||||
ios_dev_info_len += ED_KEY_LEN;
|
||||
|
||||
ret = crypto_sign_ed25519_verify_detached(ed_sign, ios_dev_info, ios_dev_info_len, ps_ctx->ctrl->info.ltpk);
|
||||
/* Verify Signature of constructed iOSDeviceInfo using the iOSDeviceLTPK */
|
||||
if (ret != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid Signature");
|
||||
hap_prepare_error_tlv(STATE_M6, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
hex_dbg_with_name("ltpka", hap_priv.ltpka, sizeof(hap_priv.ltpka));
|
||||
|
||||
/* Derive AccessoryX from the SRP shared secret using HKDF-SHA512 */
|
||||
uint8_t acc_x[32];
|
||||
hkdf(SHA512, (unsigned char *) PAIR_SETUP_ACC_SIGN_SALT,
|
||||
strlen(PAIR_SETUP_ACC_SIGN_SALT), (uint8_t *)ps_ctx->shared_secret, ps_ctx->secret_len,
|
||||
(unsigned char *) PAIR_SETUP_ACC_SIGN_INFO,
|
||||
strlen(PAIR_SETUP_ACC_SIGN_INFO),
|
||||
acc_x, sizeof(acc_x));
|
||||
/* Construct AccessoryInfo by concatenating
|
||||
* AccessoryX
|
||||
* AccessoryPairingID (acc_id)
|
||||
* AccessoryLTPK (ltpka)
|
||||
*/
|
||||
uint8_t *acc_info = info_buf;
|
||||
int acc_info_len = 0;
|
||||
memcpy(acc_info, acc_x, sizeof(acc_x));
|
||||
acc_info_len += sizeof(acc_x);
|
||||
memcpy(&ios_dev_info[acc_info_len], hap_priv.acc_id, strlen(hap_priv.acc_id));
|
||||
acc_info_len += strlen(hap_priv.acc_id);
|
||||
memcpy(&ios_dev_info[acc_info_len], hap_priv.ltpka, sizeof(hap_priv.ltpka));
|
||||
acc_info_len += sizeof(hap_priv.ltpka);
|
||||
|
||||
/* Generate AccessorySignature by signing AccessoryInfo with AccessoryLTSK
|
||||
*/
|
||||
crypto_sign_ed25519_detached(ed_sign, &ed_sign_len, acc_info, acc_info_len, hap_priv.ltska);
|
||||
hex_dbg_with_name("acc_sign", ed_sign, sizeof(ed_sign));
|
||||
|
||||
/* Create subTLV with:
|
||||
* kTLVType_Identifier : Accessory ID (acc_id)
|
||||
* kTLVType_PublicKey : Accessory LTPK
|
||||
* kTLVType_Signature : AccessorySignature
|
||||
*/
|
||||
|
||||
uint8_t subtlv[6 + HAP_ACC_ID_LEN + ED_KEY_LEN + ED_SIGN_LEN + POLY_AUTHTAG_LEN];
|
||||
|
||||
hap_tlv_data_t tlv_data;
|
||||
tlv_data.bufptr = subtlv;
|
||||
tlv_data.bufsize = sizeof(subtlv);
|
||||
tlv_data.curlen = 0;
|
||||
add_tlv(&tlv_data, kTLVType_Identifier, strlen(hap_priv.acc_id), hap_priv.acc_id);
|
||||
add_tlv(&tlv_data, kTLVType_PublicKey, sizeof(hap_priv.ltpka), hap_priv.ltpka);
|
||||
add_tlv(&tlv_data, kTLVType_Signature, sizeof(ed_sign), ed_sign);
|
||||
int subtlv_len = tlv_data.curlen;
|
||||
hex_dbg_with_name("subtlv", subtlv, subtlv_len);
|
||||
|
||||
/* Encrypt the subTLV using the session key */
|
||||
|
||||
unsigned long long mlen = 16;
|
||||
memset(newnonce, 0, sizeof newnonce);
|
||||
memcpy(newnonce+4, (uint8_t *) PS_NONCE3, 8);
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt_detached(subtlv, &subtlv[subtlv_len], &mlen, subtlv,
|
||||
subtlv_len, NULL, 0, NULL, newnonce, ps_ctx->session_key);
|
||||
hex_dbg_with_name("send_encrypt_data", subtlv, subtlv_len + 16);
|
||||
|
||||
/* Construct the response M6 */
|
||||
tlv_data.bufptr = buf;
|
||||
tlv_data.bufsize = bufsize;
|
||||
tlv_data.curlen = 0;
|
||||
state = STATE_M6;
|
||||
if ((add_tlv(&tlv_data, kTLVType_State, 1, &state) < 0) ||
|
||||
(add_tlv(&tlv_data, kTLVType_EncryptedData, subtlv_len + 16,
|
||||
subtlv) < 0)) {
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
*outlen = tlv_data.curlen;
|
||||
ps_ctx->state = state;
|
||||
ps_ctx->ctrl->info.perms = 1; /* Controller added using pair setup is always an admin */
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Setup Successful for %s", ps_ctx->ctrl->info.id);
|
||||
/* Reset the Pairing Attempts count */
|
||||
hap_priv.pair_attempts = 0;
|
||||
hap_controller_save(ps_ctx->ctrl);
|
||||
hap_send_event(HAP_INTERNAL_EVENT_ACC_PAIRED);
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
static uint8_t hap_pair_setup_get_received_state(uint8_t *buf, int inlen)
|
||||
{
|
||||
uint8_t state = 0;
|
||||
get_value_from_tlv(buf, inlen, kTLVType_State, &state, sizeof(state));
|
||||
return state;
|
||||
}
|
||||
|
||||
static void hap_pair_setup_timeout(TimerHandle_t handle)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Clearing Pair Setup Context due to inactivity");
|
||||
pair_setup_ctx_t *ps_ctx = pvTimerGetTimerID(handle);
|
||||
if (ps_ctx) {
|
||||
httpd_sess_trigger_close(hap_priv.server, ps_ctx->sock_fd);
|
||||
hap_report_event(HAP_EVENT_PAIRING_ABORTED, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pair Setup can have only a single context at a time as the specification does not allow
|
||||
* multiple controllers to perform Pair Setup Simultaneously.
|
||||
*
|
||||
* The purpose of this function is to manage allocation and resetting of the context by
|
||||
* using a static variable rather than a global variable.
|
||||
*/
|
||||
static pair_setup_ctx_t *hap_pair_setup_ctx_action(int action)
|
||||
{
|
||||
static pair_setup_ctx_t *ps_ctx;
|
||||
/* If the call is to initialise the context, check if it is already
|
||||
* initialised.
|
||||
*/
|
||||
if (action == PS_CTX_INIT) {
|
||||
/* If the context is already initialised, it means that pair setup
|
||||
* is already in progress. So, return NULL.
|
||||
* Else, allocate the context.
|
||||
*/
|
||||
if (ps_ctx)
|
||||
return NULL;
|
||||
else {
|
||||
ps_ctx = hap_platform_memory_calloc(sizeof(pair_setup_ctx_t), 1);
|
||||
if (ps_ctx) {
|
||||
ps_ctx->timer = xTimerCreate("hap_setup_timer", HAP_SETUP_TIMEOUT_IN_TICKS,
|
||||
pdFALSE, (void *) ps_ctx, hap_pair_setup_timeout);
|
||||
xTimerStart(ps_ctx->timer, 0);
|
||||
}
|
||||
}
|
||||
} else if (action == PS_CTX_DEINIT) {
|
||||
/* If the call is to de-initialise the context, clean it up
|
||||
* and set the pointer to NULL
|
||||
*/
|
||||
if (ps_ctx) {
|
||||
if (ps_ctx->timer) {
|
||||
xTimerStop(ps_ctx->timer, 0);
|
||||
xTimerDelete(ps_ctx->timer, 100);
|
||||
}
|
||||
mu_srp_free(&ps_ctx->srp_hd);
|
||||
hap_platform_memory_free(ps_ctx);
|
||||
}
|
||||
ps_ctx = NULL;
|
||||
}
|
||||
return ps_ctx;
|
||||
}
|
||||
|
||||
|
||||
void hap_pair_setup_ctx_clean(void *sess_ctx)
|
||||
{
|
||||
if (sess_ctx) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Cleaning Pair Setup Context");
|
||||
hap_pair_setup_ctx_action(PS_CTX_DEINIT);
|
||||
}
|
||||
}
|
||||
|
||||
int hap_pair_setup_context_init(int sock_fd, void **ctx, uint8_t *buf, int bufsize, int *outlen)
|
||||
{
|
||||
pair_setup_ctx_t *ps_ctx = hap_pair_setup_ctx_action(PS_CTX_INIT);
|
||||
|
||||
if (!ps_ctx) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR,"######## Aborted! Pair Setup in Progress with another controller ########");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Busy, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
ps_ctx->sock_fd = sock_fd;
|
||||
*ctx = ps_ctx;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "######## Starting Pair Setup ########");
|
||||
return HAP_SUCCESS;
|
||||
|
||||
}
|
||||
int hap_pair_setup_process(void **ctx, uint8_t *buf, int inlen, int bufsize, int *outlen)
|
||||
{
|
||||
pair_setup_ctx_t *ps_ctx = (pair_setup_ctx_t *)(*ctx);
|
||||
|
||||
uint8_t recv_state = hap_pair_setup_get_received_state(buf, inlen);
|
||||
if (!ps_ctx) {
|
||||
hap_prepare_error_tlv(recv_state + 1, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
/* Receiving STATE_M1 in the message while the pair-setup was already in progress means
|
||||
* that pair setup was restarted. Handle it accordingly
|
||||
*/
|
||||
if ((recv_state == STATE_M1) && (ps_ctx->state != STATE_M0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Restarting Pair Setup");
|
||||
int sock_fd = ps_ctx->sock_fd;
|
||||
hap_pair_setup_ctx_clean(ps_ctx);
|
||||
int ret = hap_pair_setup_context_init(sock_fd, ctx, buf, bufsize, outlen);
|
||||
if (ret != HAP_SUCCESS)
|
||||
return ret;
|
||||
ps_ctx = (pair_setup_ctx_t *)(*ctx);
|
||||
}
|
||||
if (ps_ctx->state == STATE_M0) {
|
||||
return hap_pair_setup_process_srp_start(ps_ctx, buf, inlen, bufsize, outlen);
|
||||
} else if (ps_ctx->state == STATE_M2) {
|
||||
hap_priv.pair_attempts++;
|
||||
int ret = hap_pair_setup_process_srp_verify(ps_ctx, buf, inlen, bufsize, outlen);
|
||||
if (ps_ctx->session) {
|
||||
*ctx = ps_ctx->session;
|
||||
hap_pair_setup_ctx_clean(ps_ctx);
|
||||
}
|
||||
return ret;
|
||||
} else if (ps_ctx->state == STATE_M4) {
|
||||
int ret = hap_pair_setup_process_exchange(ps_ctx, buf, inlen, bufsize, outlen);
|
||||
/* If last step of pair setup is successful, it means that the context would
|
||||
* be no more required. Hence, clear it.
|
||||
*/
|
||||
if (ret == HAP_SUCCESS) {
|
||||
hap_pair_setup_ctx_clean(ps_ctx);
|
||||
*ctx = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
void hap_set_setup_code(const char *setup_code)
|
||||
{
|
||||
if (hap_priv.setup_code)
|
||||
hap_platform_memory_free(hap_priv.setup_code);
|
||||
hap_priv.setup_code = strdup(setup_code);
|
||||
}
|
||||
|
||||
int hap_set_setup_info(const hap_setup_info_t *setup_info)
|
||||
{
|
||||
if (!setup_info)
|
||||
return HAP_FAIL;
|
||||
if (hap_priv.setup_info)
|
||||
hap_platform_memory_free(hap_priv.setup_info);
|
||||
hap_priv.setup_info = hap_platform_memory_calloc(1, sizeof(hap_setup_info_t));
|
||||
if (!hap_priv.setup_info)
|
||||
return HAP_FAIL;
|
||||
memcpy(hap_priv.setup_info, setup_info, sizeof(hap_setup_info_t));
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_set_setup_id(const char *setup_id)
|
||||
{
|
||||
if (setup_id == NULL || strlen(setup_id) != SETUP_ID_LEN)
|
||||
return HAP_FAIL;
|
||||
strcpy(hap_priv.setup_id, setup_id);
|
||||
return HAP_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_PAIR_SETUP_H_
|
||||
#define _HAP_PAIR_SETUP_H_
|
||||
#include <stdint.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <mu_srp.h>
|
||||
#include <esp_hap_pair_common.h>
|
||||
|
||||
#define PS_NONCE1 "PS-Msg04"
|
||||
#define PS_NONCE2 "PS-Msg05"
|
||||
#define PS_NONCE3 "PS-Msg06"
|
||||
|
||||
#define PAIR_FLAG_TRANSIENT 0x00000010
|
||||
#define PAIR_FLAG_SPLIT 0x01000000
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t state;
|
||||
uint8_t method;
|
||||
uint32_t pairing_flags;
|
||||
int8_t pairing_flags_len;
|
||||
int len_s;
|
||||
char *bytes_s;
|
||||
int secret_len;
|
||||
char *shared_secret;
|
||||
mu_srp_handle_t srp_hd;
|
||||
hap_ctrl_data_t *ctrl;
|
||||
uint8_t session_key[32];
|
||||
TimerHandle_t timer;
|
||||
hap_secure_session_t *session;
|
||||
int sock_fd;
|
||||
} pair_setup_ctx_t;
|
||||
int hap_pair_setup_context_init(int sock_fd, void **ctx, uint8_t *buf, int bufsize, int *outlen);
|
||||
int hap_pair_setup_process(void **ctx, uint8_t *buf, int inlen, int bufsize, int *outlen);
|
||||
void hap_pair_setup_ctx_clean(void *sess_ctx);
|
||||
int hap_pair_setup_manage_mfi_auth(pair_setup_ctx_t *ps_ctx, hap_tlv_data_t *tlv_data, hap_tlv_error_t *tlv_error);
|
||||
#endif /* _HAP_PAIR_SETUP_H_ */
|
|
@ -0,0 +1,464 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sodium/crypto_scalarmult_curve25519.h>
|
||||
#include <sodium/crypto_sign_ed25519.h>
|
||||
#include <hkdf-sha.h>
|
||||
#include <sodium/crypto_aead_chacha20poly1305.h>
|
||||
#include <esp_http_server.h>
|
||||
#include <hap_platform_memory.h>
|
||||
|
||||
#include <esp_hap_main.h>
|
||||
#include <esp_hap_pair_common.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_hap_char.h>
|
||||
#include <hexdump.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
#include <esp_mfi_rand.h>
|
||||
|
||||
#define PAIR_VERIFY_ENCRYPT_SALT "Pair-Verify-Encrypt-Salt"
|
||||
#define PAIR_VERIFY_ENCRYPT_INFO "Pair-Verify-Encrypt-Info"
|
||||
#define PV_NONCE1 "PV-Msg02"
|
||||
#define PV_NONCE2 "PV-Msg03"
|
||||
#define CONTROL_SALT "Control-Salt"
|
||||
#define CONTROL_READ_INFO "Control-Read-Encryption-Key"
|
||||
#define CONTROL_WRITE_INFO "Control-Write-Encryption-Key"
|
||||
|
||||
typedef struct {
|
||||
/* It is important that "state" should be the first element of the structure.
|
||||
* It will be the first element, even in hap_secure_session_t.
|
||||
* This will be useful while checking the state from the context, irrespective
|
||||
* of whether the context points to pair_verify_ctx_t or hap_secure_session_t.
|
||||
*/
|
||||
uint8_t state;
|
||||
uint8_t ctrl_curve_pk[CURVE_KEY_LEN];
|
||||
uint8_t acc_curve_pk[CURVE_KEY_LEN];
|
||||
uint8_t hkdf_key[ENCRYPT_KEY_LEN];
|
||||
uint8_t shared_secret[CURVE_KEY_LEN];
|
||||
hap_secure_session_t *session;
|
||||
} pair_verify_ctx_t;
|
||||
|
||||
void hap_close_ctrl_sessions(hap_ctrl_data_t *ctrl)
|
||||
{
|
||||
if (!ctrl)
|
||||
return;
|
||||
int i;
|
||||
printf("---- hap_close_ctrl_sessions begin -----\n");
|
||||
for (i = 0; i < HAP_MAX_SESSIONS; i++) {
|
||||
if (!hap_priv.sessions[i])
|
||||
continue;
|
||||
if (hap_priv.sessions[i]->ctrl == ctrl) {
|
||||
hap_report_event(HAP_EVENT_CTRL_DISCONNECTED, (ctrl->info.id),
|
||||
sizeof((ctrl->info.id)));
|
||||
/* TODO: Use some generic function and not a direct HTTPD function
|
||||
*/
|
||||
printf("---- trigger_close fd: %d\n", hap_priv.sessions[i]->conn_identifier);
|
||||
httpd_sess_trigger_close(hap_priv.server, hap_priv.sessions[i]->conn_identifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("----hap_close_ctrl_sessions end ----\n");
|
||||
}
|
||||
|
||||
int hap_get_ctrl_session_index(hap_secure_session_t *session)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_SESSIONS; i++) {
|
||||
if (hap_priv.sessions[i] == session)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void hap_close_all_sessions()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_SESSIONS; i++) {
|
||||
if (hap_priv.sessions[i]) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Closing Session");
|
||||
hap_close_ctrl_sessions(hap_priv.sessions[i]->ctrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hap_add_secure_session(hap_secure_session_t *session)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_SESSIONS; i++) {
|
||||
if (hap_priv.sessions[i] == NULL) {
|
||||
hap_priv.sessions[i] = session;
|
||||
hap_report_event(HAP_EVENT_CTRL_CONNECTED, session->ctrl->info.id,
|
||||
sizeof(session->ctrl->info.id));
|
||||
/* Set the disconnected_event_sent flag here to false so that an
|
||||
* event can be sent later for a state change, when no controller
|
||||
* is connected.
|
||||
* HAP Spec R15 say that the state number should change only once
|
||||
* between accessory disconneted (from all controllers) to connected
|
||||
* state.
|
||||
*/
|
||||
hap_priv.disconnected_event_sent = false;
|
||||
//ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "HomeKit Session active");
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "HomeKit Session active, fd: %d", session->conn_identifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hap_free_session(void *session)
|
||||
{
|
||||
if (!session)
|
||||
return;
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_SESSIONS; i++) {
|
||||
if (hap_priv.sessions[i] == session) {
|
||||
/* Disable all characteristic notifications on this session */
|
||||
int fd = (hap_priv.sessions[i])->conn_identifier;
|
||||
hap_disable_all_char_notif(i);
|
||||
hap_priv.sessions[i] = NULL;
|
||||
//ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "HomeKit Session terminated");
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "HomeKit Session terminated, fd: %d", fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
hap_platform_memory_free(session);
|
||||
}
|
||||
|
||||
static int hap_pair_verify_process_start(pair_verify_ctx_t *pv_ctx, uint8_t *buf, int inlen,
|
||||
int bufsize, int *outlen)
|
||||
{
|
||||
/* Parse the data received from the controller */
|
||||
uint8_t state;
|
||||
if (!pv_ctx) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "No context");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if ((get_value_from_tlv(buf, inlen, kTLVType_State, &state, sizeof(state)) < 0) ||
|
||||
(get_value_from_tlv(buf, inlen, kTLVType_PublicKey, pv_ctx->ctrl_curve_pk,
|
||||
CURVE_KEY_LEN) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid TLVs received");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if (state != STATE_M1) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Incorrect State received");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Verify M1 Received");
|
||||
hex_dbg_with_name("ctrl curve pk", pv_ctx->ctrl_curve_pk, 32);
|
||||
|
||||
/* Generate a new Curve25519 Key Pair */
|
||||
uint8_t acc_curve_sk[CURVE_KEY_LEN];
|
||||
esp_mfi_get_random(acc_curve_sk, CURVE_KEY_LEN);
|
||||
/* This particular value of basepoint is required to generate the public key
|
||||
* from secret key
|
||||
*/
|
||||
uint8_t basepoint[32] = {9};
|
||||
if (crypto_scalarmult_curve25519(pv_ctx->acc_curve_pk, acc_curve_sk, basepoint) == -1) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Curve25519 Error");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
hex_dbg_with_name("acc curve sk", acc_curve_sk, 32);
|
||||
hex_dbg_with_name("acc curve pk", pv_ctx->acc_curve_pk, 32);
|
||||
if (crypto_scalarmult_curve25519(pv_ctx->shared_secret, acc_curve_sk, pv_ctx->ctrl_curve_pk) == -1) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Curve25519 Error");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
hex_dbg_with_name("shared secret", pv_ctx->shared_secret, 32);
|
||||
|
||||
/* Construct AccessoryInfo by concatenating
|
||||
* a. Accessory's Curve25519 Public Key
|
||||
* b. Accessory ID
|
||||
* c. Controller's Curve25519 Public Key received in M1
|
||||
*/
|
||||
uint8_t acc_info[2 * CURVE_KEY_LEN + HAP_ACC_ID_LEN];
|
||||
int acc_info_len = 0;
|
||||
memcpy(acc_info, pv_ctx->acc_curve_pk, CURVE_KEY_LEN);
|
||||
acc_info_len += CURVE_KEY_LEN;
|
||||
memcpy(acc_info + acc_info_len, hap_priv.acc_id, strlen(hap_priv.acc_id));
|
||||
acc_info_len += strlen(hap_priv.acc_id);
|
||||
memcpy(acc_info + acc_info_len, pv_ctx->ctrl_curve_pk, CURVE_KEY_LEN);
|
||||
acc_info_len += CURVE_KEY_LEN;
|
||||
|
||||
hex_dbg_with_name("acc_info", acc_info, acc_info_len);
|
||||
|
||||
/* Use Ed25519 to generate AccessorySignature by signing AccessoryInfo
|
||||
* with its Long Term Secret Key AccessoryLTSK
|
||||
*/
|
||||
unsigned char ed_sign[64];
|
||||
unsigned long long ed_sign_len;
|
||||
crypto_sign_ed25519_detached(ed_sign, &ed_sign_len, acc_info, acc_info_len, hap_priv.ltska);
|
||||
hex_dbg_with_name("sign", ed_sign, 64);
|
||||
|
||||
/* Construct a subTLV with
|
||||
* kTLVType_Identifier : Accessory Identifier
|
||||
* kTLVType_Signature : AccessorySignature generated above
|
||||
*/
|
||||
uint8_t subtlv[4 + HAP_ACC_ID_LEN + ED_SIGN_LEN + POLY_AUTHTAG_LEN];
|
||||
hap_tlv_data_t tlv_data;
|
||||
tlv_data.bufptr = subtlv;
|
||||
tlv_data.bufsize = sizeof(subtlv);
|
||||
tlv_data.curlen = 0;
|
||||
add_tlv(&tlv_data, kTLVType_Identifier, strlen(hap_priv.acc_id), hap_priv.acc_id);
|
||||
add_tlv(&tlv_data, kTLVType_Signature, sizeof(ed_sign), ed_sign);
|
||||
int subtlv_len = tlv_data.curlen;
|
||||
hex_dbg_with_name("subtlv", subtlv, subtlv_len);
|
||||
|
||||
/* Derive Symmetric Session encryption key SessionKey from the curve
|
||||
* shared secret using HKDF-SHA-512
|
||||
*/
|
||||
hkdf(SHA512, (unsigned char *) PAIR_VERIFY_ENCRYPT_SALT,
|
||||
strlen(PAIR_VERIFY_ENCRYPT_SALT),
|
||||
pv_ctx->shared_secret, sizeof(pv_ctx->shared_secret),
|
||||
(unsigned char *) PAIR_VERIFY_ENCRYPT_INFO,
|
||||
strlen(PAIR_VERIFY_ENCRYPT_INFO),
|
||||
pv_ctx->hkdf_key, sizeof(pv_ctx->hkdf_key));
|
||||
/* Encrypt the sub TLV to get encryptedData and an authTag using
|
||||
* Chacha20-Poly1305 AEAD Algorithm
|
||||
*/
|
||||
uint8_t edata[4 + HAP_ACC_ID_LEN + ED_SIGN_LEN + POLY_AUTHTAG_LEN];
|
||||
unsigned long long mlen = 16;
|
||||
uint8_t newnonce[12];
|
||||
memset(newnonce, 0, sizeof newnonce);
|
||||
memcpy(newnonce+4, PV_NONCE1, 8);
|
||||
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt_detached(edata, edata + subtlv_len, &mlen, subtlv, subtlv_len, NULL, 0, NULL, newnonce, pv_ctx->hkdf_key);
|
||||
|
||||
int edata_len = subtlv_len + POLY_AUTHTAG_LEN;
|
||||
|
||||
hex_dbg_with_name("encrypt_data", edata, edata_len);
|
||||
|
||||
/* Construct the response M2 */
|
||||
tlv_data.bufptr = buf;
|
||||
tlv_data.bufsize = bufsize;
|
||||
tlv_data.curlen = 0;
|
||||
state = STATE_M2;
|
||||
if ((add_tlv(&tlv_data, kTLVType_State, 1, &state) < 0) ||
|
||||
(add_tlv(&tlv_data, kTLVType_PublicKey, CURVE_KEY_LEN,
|
||||
pv_ctx->acc_curve_pk) < 0) ||
|
||||
(add_tlv(&tlv_data, kTLVType_EncryptedData, edata_len, edata) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "TLV creation failed");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
*outlen = tlv_data.curlen;
|
||||
hex_dbg_with_name("M2", buf, *outlen);
|
||||
pv_ctx->state = STATE_M2;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Verify M2 Successful");
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
static int hap_pair_verify_process_finish(pair_verify_ctx_t *pv_ctx, uint8_t *buf, int inlen,
|
||||
int bufsize, int *outlen)
|
||||
{
|
||||
/* Parse the data received from the controller */
|
||||
uint8_t state;
|
||||
if (!pv_ctx) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "No context");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
uint8_t edata[4 + HAP_CTRL_ID_LEN + ED_SIGN_LEN + POLY_AUTHTAG_LEN];
|
||||
int edata_len;
|
||||
if ((get_value_from_tlv(buf, inlen, kTLVType_State, &state, sizeof(state)) < 0) ||
|
||||
((edata_len = get_value_from_tlv(buf, inlen, kTLVType_EncryptedData,
|
||||
edata, sizeof(edata))) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid TLVs received");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if (state != STATE_M3) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Incorrect State received");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Verify M3 Received");
|
||||
|
||||
/* Decrypt the received data to get the subTLV */
|
||||
uint8_t newnonce[12];
|
||||
memset(newnonce, 0, sizeof newnonce);
|
||||
memcpy(newnonce+4, PV_NONCE2, 8);
|
||||
int ret;
|
||||
ret = crypto_aead_chacha20poly1305_ietf_decrypt_detached(edata, NULL, edata, edata_len - POLY_AUTHTAG_LEN, edata + edata_len - POLY_AUTHTAG_LEN, NULL, 0, newnonce, pv_ctx->hkdf_key);
|
||||
if (ret != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Decryption error");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/* Parse the subTLV to get the iOSDevicePairingID and iOSDeviceSignature
|
||||
*/
|
||||
edata_len = edata_len - POLY_AUTHTAG_LEN;
|
||||
unsigned char ed_sign[64];
|
||||
char ctrl_id[HAP_CTRL_ID_LEN];
|
||||
memset(ctrl_id, 0, sizeof(ctrl_id));
|
||||
if ((get_value_from_tlv(edata, edata_len, kTLVType_Identifier,
|
||||
ctrl_id, sizeof(ctrl_id)) < 0) ||
|
||||
(get_value_from_tlv(edata, edata_len, kTLVType_Signature,
|
||||
ed_sign, sizeof(ed_sign)) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Wrong subTLV received");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/* Check if the controller is present in the database i.e. check
|
||||
* if the controller was paired with the accessory
|
||||
*/
|
||||
hap_ctrl_data_t *ctrl = hap_get_controller(ctrl_id);
|
||||
if (!ctrl) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "No ctrl details found");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/* Construct iOSDeviceInfo by concatenating
|
||||
* a. Controllers's Curve25519 Public Key (received in M1)
|
||||
* b. Controller ID
|
||||
* c. Accessory's Curve25519 Public Key (sent in M2)
|
||||
*/
|
||||
uint8_t ios_dev_info[2 * CURVE_KEY_LEN + HAP_CTRL_ID_LEN];
|
||||
int ios_dev_info_len = 0;
|
||||
memcpy(ios_dev_info, pv_ctx->ctrl_curve_pk, CURVE_KEY_LEN);
|
||||
ios_dev_info_len += CURVE_KEY_LEN;
|
||||
memcpy(ios_dev_info + ios_dev_info_len, ctrl_id, strlen(ctrl_id));
|
||||
ios_dev_info_len += strlen(ctrl_id);
|
||||
memcpy(ios_dev_info + ios_dev_info_len, pv_ctx->acc_curve_pk,
|
||||
CURVE_KEY_LEN);
|
||||
ios_dev_info_len += CURVE_KEY_LEN;
|
||||
|
||||
/* Validate the signature with the received iOSDeviceSignature */
|
||||
if (crypto_sign_ed25519_verify_detached(ed_sign, ios_dev_info, ios_dev_info_len, ctrl->info.ltpk) != 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Signature mismatch");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/* Allocate memory for the secure session information */
|
||||
hap_secure_session_t *session = hap_platform_memory_calloc(sizeof(hap_secure_session_t), 1);
|
||||
if (!session) {
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Memory allocation failed");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
/* Construct the response M4 */
|
||||
hap_tlv_data_t tlv_data;
|
||||
tlv_data.bufptr = buf;
|
||||
tlv_data.bufsize = bufsize;
|
||||
tlv_data.curlen = 0;
|
||||
state = STATE_M4;
|
||||
if (add_tlv(&tlv_data, kTLVType_State, 1, &state) < 0) {
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
hap_platform_memory_free(session);
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "TLV creation failed");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
*outlen = tlv_data.curlen;
|
||||
|
||||
/* Generate the Encryption and Decryption Keys.
|
||||
* Since, read and write are from the controller's point of view,
|
||||
* encryption key uses READ_INFO and decryption key uses WRITE_INFO
|
||||
*
|
||||
* Also, set the nonce to zero
|
||||
*/
|
||||
hkdf(SHA512, (unsigned char *) CONTROL_SALT, strlen(CONTROL_SALT),
|
||||
pv_ctx->shared_secret, sizeof(pv_ctx->shared_secret),
|
||||
(unsigned char *) CONTROL_READ_INFO, strlen(CONTROL_READ_INFO),
|
||||
session->encrypt_key, sizeof(session->encrypt_key));
|
||||
|
||||
hkdf(SHA512, (unsigned char *) CONTROL_SALT, strlen(CONTROL_SALT),
|
||||
pv_ctx->shared_secret, sizeof(pv_ctx->shared_secret),
|
||||
(unsigned char *) CONTROL_WRITE_INFO, strlen(CONTROL_WRITE_INFO),
|
||||
session->decrypt_key, sizeof(session->decrypt_key));
|
||||
|
||||
session->state = STATE_VERIFIED;
|
||||
pv_ctx->state = STATE_VERIFIED;
|
||||
|
||||
memset(session->encrypt_nonce, 0, sizeof(session->encrypt_nonce));
|
||||
memset(session->decrypt_nonce, 0, sizeof(session->decrypt_nonce));
|
||||
session->ctrl = ctrl;
|
||||
|
||||
pv_ctx->session = session;
|
||||
|
||||
/* Add the session information to database */
|
||||
hap_add_secure_session(session);
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Pair Verify Successful for %s", ctrl_id);
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_pair_verify_process(void **ctx, uint8_t *buf, int inlen, int bufsize, int *outlen)
|
||||
{
|
||||
pair_verify_ctx_t *pv_ctx = (pair_verify_ctx_t *)(*ctx);
|
||||
if (pv_ctx) {
|
||||
if (pv_ctx->state == STATE_M0)
|
||||
return hap_pair_verify_process_start(pv_ctx, buf, inlen,
|
||||
bufsize, outlen);
|
||||
else if (pv_ctx->state == STATE_M2) {
|
||||
int ret = hap_pair_verify_process_finish(pv_ctx, buf, inlen,
|
||||
bufsize, outlen);
|
||||
/* Successful finish means that the pair verify was successful.
|
||||
* So, we clear the old context and assign the secure_session
|
||||
* as the new context
|
||||
*/
|
||||
if (ret == HAP_SUCCESS) {
|
||||
hap_secure_session_t *session = pv_ctx->session;
|
||||
hap_platform_memory_free(pv_ctx);
|
||||
*ctx = session;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Error processing Pair Verify Data");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
int hap_pair_verify_context_init(void **ctx, uint8_t *buf, int bufsize, int *outlen)
|
||||
{
|
||||
pair_verify_ctx_t *pv_ctx;
|
||||
|
||||
pv_ctx = (pair_verify_ctx_t *) hap_platform_memory_calloc(sizeof(pair_verify_ctx_t), 1);
|
||||
if (!pv_ctx) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Failed to create Pair Verify Context");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
*ctx = pv_ctx;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "######## Starting Pair Verify ########");
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t hap_pair_verify_get_state(void *ctx)
|
||||
{
|
||||
pair_verify_ctx_t *pv_ctx = (pair_verify_ctx_t *)ctx;
|
||||
if (pv_ctx)
|
||||
return pv_ctx->state;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_PAIR_VERIFY_H_
|
||||
#define _HAP_PAIR_VERIFY_H_
|
||||
#include <esp_hap_pair_common.h>
|
||||
#include <esp_hap_controllers.h>
|
||||
int hap_pair_verify_context_init(void **ctx, uint8_t *buf, int bufsize, int *outlen);
|
||||
int hap_pair_verify_process(void **ctx, uint8_t *buf, int inlen, int bufsize, int *outlen);
|
||||
uint8_t hap_pair_verify_get_state(void *ctx);
|
||||
void hap_free_session(void *session);
|
||||
int hap_get_ctrl_session_index(hap_secure_session_t *session);
|
||||
void hap_close_ctrl_sessions(hap_ctrl_data_t *ctrl);
|
||||
void hap_close_all_sessions();
|
||||
#endif /* _HAP_PAIR_VERIFY_H_ */
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <esp_hap_database.h>
|
||||
#include <esp_hap_controllers.h>
|
||||
#include <esp_hap_pair_common.h>
|
||||
#include <esp_hap_pair_verify.h>
|
||||
#include <esp_hap_main.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
bool hap_is_req_admin(void *__session)
|
||||
{
|
||||
hap_secure_session_t *session = (hap_secure_session_t *)__session;
|
||||
if (!session) {
|
||||
return false;
|
||||
}
|
||||
if ((session->state == STATE_VERIFIED) && (session->ctrl->info.perms == 1)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char *hap_req_get_ctrl_id(void *__session)
|
||||
{
|
||||
hap_secure_session_t *session = (hap_secure_session_t *)__session;
|
||||
if (!session)
|
||||
return NULL;
|
||||
|
||||
if (session->state == STATE_VERIFIED) {
|
||||
return session->ctrl->info.id;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool hap_is_req_secure(hap_secure_session_t *session)
|
||||
{
|
||||
if (!session) {
|
||||
return false;
|
||||
}
|
||||
if (session->state == STATE_VERIFIED) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void hap_remove_all_controllers()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < HAP_MAX_CONTROLLERS; i++) {
|
||||
if (hap_priv.controllers[i].valid) {
|
||||
hap_close_ctrl_sessions(&hap_priv.controllers[i]);
|
||||
hap_controller_remove(&hap_priv.controllers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
static int hap_process_pair_remove(uint8_t *buf, int inlen, int bufsize, int *outlen)
|
||||
{
|
||||
bool acc_unpaired = false;
|
||||
char ctrl_id[HAP_CTRL_ID_LEN];
|
||||
memset(ctrl_id, 0, HAP_CTRL_ID_LEN);
|
||||
if (get_value_from_tlv(buf, inlen, kTLVType_Identifier,
|
||||
ctrl_id, sizeof(ctrl_id)) < 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Identifier not found");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Removing Controller %s", ctrl_id);
|
||||
hap_ctrl_data_t *ctrl = hap_get_controller(ctrl_id);
|
||||
hap_close_ctrl_sessions(ctrl);
|
||||
hap_controller_remove(ctrl);
|
||||
|
||||
if (!is_admin_paired()) {
|
||||
acc_unpaired = true;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Last Admin controller removed. Removing all other controllers.");
|
||||
hap_remove_all_controllers();
|
||||
}
|
||||
|
||||
hap_tlv_data_t tlv_data = {
|
||||
.bufptr = buf,
|
||||
.bufsize = bufsize,
|
||||
.curlen = 0,
|
||||
};
|
||||
uint8_t state = STATE_M2;
|
||||
if (add_tlv(&tlv_data, kTLVType_State, sizeof(state), &state) < 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "TLV creation failed");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
*outlen = tlv_data.curlen;
|
||||
if (acc_unpaired) {
|
||||
hap_send_event(HAP_INTERNAL_EVENT_ACC_UNPAIRED);
|
||||
}
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
static int hap_process_pair_add(uint8_t *buf, int inlen, int bufsize, int *outlen)
|
||||
{
|
||||
char ctrl_id[HAP_CTRL_ID_LEN];
|
||||
uint8_t ltpkc[ED_KEY_LEN];
|
||||
uint8_t perms;
|
||||
memset(ctrl_id, 0, HAP_CTRL_ID_LEN);
|
||||
if ((get_value_from_tlv(buf, inlen, kTLVType_Identifier,
|
||||
ctrl_id, sizeof(ctrl_id)) < 0) ||
|
||||
(get_value_from_tlv(buf, inlen, kTLVType_PublicKey,
|
||||
ltpkc, sizeof(ltpkc)) < 0) ||
|
||||
(get_value_from_tlv(buf, inlen, kTLVType_Permissions,
|
||||
&perms, sizeof(perms)) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid TLVs received");
|
||||
hap_prepare_error_tlv(STATE_M4, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
hap_ctrl_data_t *ctrl = hap_get_controller(ctrl_id);
|
||||
if (ctrl) {
|
||||
/* If controller already exists, but the key does not match, return error */
|
||||
if (memcmp(ltpkc, ctrl->info.ltpk, ED_KEY_LEN)) {
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
} else {
|
||||
/* Else, just change the permissions */
|
||||
ctrl->info.perms = perms;
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Modifying Existing Controller %s", ctrl_id);
|
||||
hap_controller_save(ctrl);
|
||||
}
|
||||
} else {
|
||||
/* Check if there is space available for a new controller */
|
||||
ctrl = hap_controller_get_empty_loc();
|
||||
if (!ctrl) {
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_MaxPeers, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
strcpy(ctrl->info.id, ctrl_id);
|
||||
memcpy(ctrl->info.ltpk, ltpkc, ED_KEY_LEN);
|
||||
ctrl->info.perms = perms;
|
||||
hap_controller_save(ctrl);
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Added Controller %s", ctrl_id);
|
||||
}
|
||||
hap_tlv_data_t tlv_data = {
|
||||
.bufptr = buf,
|
||||
.bufsize = bufsize,
|
||||
.curlen = 0,
|
||||
};
|
||||
uint8_t state = STATE_M2;
|
||||
if (add_tlv(&tlv_data, kTLVType_State, sizeof(state), &state) < 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "TLV creation failed");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
*outlen = tlv_data.curlen;
|
||||
return HAP_SUCCESS;
|
||||
|
||||
}
|
||||
static int hap_process_pair_list(uint8_t *buf, int inlen, int bufsize, int *outlen)
|
||||
{
|
||||
hap_tlv_data_t tlv_data = {
|
||||
.bufptr = buf,
|
||||
.bufsize = bufsize,
|
||||
.curlen = 0,
|
||||
};
|
||||
int i;
|
||||
hap_ctrl_data_t *ctrl;
|
||||
hap_ctrl_info_t *ctrl_info;
|
||||
bool entry_added = false;
|
||||
for (i = 0; i < HAP_MAX_CONTROLLERS; i++) {
|
||||
ctrl = &hap_priv.controllers[i];
|
||||
if (!ctrl->valid)
|
||||
continue;
|
||||
/* If an entry is already added, include a separator */
|
||||
if (entry_added) {
|
||||
if (add_tlv(&tlv_data, kTLVType_Separator, 0, NULL) < 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "TLV creation failed");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown,
|
||||
buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
} else {
|
||||
/* Else, since this is the first entry, add the state */
|
||||
uint8_t state = STATE_M2;
|
||||
if (add_tlv(&tlv_data, kTLVType_State, sizeof(state), &state) < 0) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "TLV creation failed");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown,
|
||||
buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
}
|
||||
ctrl_info = &ctrl->info;
|
||||
if ((add_tlv(&tlv_data, kTLVType_Identifier,
|
||||
strlen(ctrl_info->id), ctrl_info->id) < 0) ||
|
||||
(add_tlv(&tlv_data, kTLVType_PublicKey,
|
||||
ED_KEY_LEN, ctrl_info->ltpk) < 0) ||
|
||||
(add_tlv(&tlv_data, kTLVType_Permissions,
|
||||
1, &ctrl_info->perms) < 0)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "TLV creation failed");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
entry_added = true;
|
||||
}
|
||||
*outlen = tlv_data.curlen;
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int hap_pairings_process(void *ctx, uint8_t *buf, int inlen, int bufsize, int *outlen)
|
||||
{
|
||||
if (!hap_is_req_admin(ctx)) {
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Authentication, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
uint8_t state, method;
|
||||
if ((get_value_from_tlv(buf, inlen, kTLVType_State, &state, sizeof(state)) < 0) ||
|
||||
(get_value_from_tlv(buf, inlen, kTLVType_Method,
|
||||
&method, sizeof(method)) < 0) ||
|
||||
(state != STATE_M1)) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Invalid TLVs received");
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
||||
if (method == HAP_METHOD_ADD_PAIRING) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Add Pairing received");
|
||||
return hap_process_pair_add(buf, inlen, bufsize, outlen);
|
||||
} else if (method == HAP_METHOD_REMOVE_PAIRING) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Remove Pairing received");
|
||||
return hap_process_pair_remove(buf, inlen, bufsize, outlen);
|
||||
} else if (method == HAP_METHOD_LIST_PAIRINGS) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "List Pairings received");
|
||||
return hap_process_pair_list(buf, inlen, bufsize, outlen);
|
||||
}
|
||||
hap_prepare_error_tlv(STATE_M2, kTLVError_Unknown, buf, bufsize, outlen);
|
||||
return HAP_FAIL;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_PAIRINGS_H_
|
||||
#define _HAP_PAIRINGS_H_
|
||||
#include <esp_hap_pair_common.h>
|
||||
int hap_pairings_process(void *ctx, uint8_t *buf, int inlen, int bufsize, int *outlen);
|
||||
bool hap_is_req_secure(hap_secure_session_t *session);
|
||||
#endif /* _HAP_PAIRINGS_H_ */
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_SECURE_MESSAGE_H_
|
||||
#define _HAP_SECURE_MESSAGE_H_
|
||||
#include <stdint.h>
|
||||
#include <esp_http_server.h>
|
||||
/** Information for Software Token based authentication.
|
||||
* To be used only if MFi chip is not present on the accessory
|
||||
*/
|
||||
typedef struct {
|
||||
/** UUID for the accessory */
|
||||
uint8_t uuid[16];
|
||||
/** Token associated with the UUID */
|
||||
uint8_t *token;
|
||||
/* Length of the above token */
|
||||
size_t token_len;
|
||||
} hap_software_token_info_t;
|
||||
|
||||
int hap_register_secure_message_handler(httpd_handle_t handle);
|
||||
int hap_unregister_secure_message_handler(httpd_handle_t handle);
|
||||
#endif /* _HAP_SECURE_MESSAGE_H_ */
|
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <hap_platform_memory.h>
|
||||
|
||||
#include <esp_hap_serv.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
void hap_serv_mark_primary(hap_serv_t *hs)
|
||||
{
|
||||
if (hs) {
|
||||
((__hap_serv_t *)hs)->primary = true;
|
||||
}
|
||||
}
|
||||
|
||||
void hap_serv_mark_hidden(hap_serv_t *hs)
|
||||
{
|
||||
if (hs) {
|
||||
((__hap_serv_t *)hs)->hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get service hidden attribute
|
||||
*/
|
||||
bool hap_serv_get_hidden(hap_serv_t *hs)
|
||||
{
|
||||
if (hs) {
|
||||
return ((__hap_serv_t *)hs)->hidden;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void hap_serv_set_iid(hap_serv_t *hs, int32_t iid)
|
||||
{
|
||||
if (hs) {
|
||||
((__hap_serv_t *)hs)->iid = iid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get service "primary" attribute
|
||||
*/
|
||||
bool hap_serv_get_primary(hap_serv_t *hs)
|
||||
{
|
||||
if (hs) {
|
||||
return ((__hap_serv_t *)hs)->primary;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
hap_char_t *hap_serv_get_first_char(hap_serv_t *hs)
|
||||
{
|
||||
return ((__hap_serv_t *)hs)->chars;
|
||||
}
|
||||
/**
|
||||
* @brief get target characteristics by it's IID
|
||||
*/
|
||||
hap_char_t *hap_serv_get_char_by_iid(hap_serv_t *hs, int32_t iid)
|
||||
{
|
||||
if (!hs)
|
||||
return NULL;
|
||||
|
||||
hap_char_t *hc;
|
||||
for (hc = hap_serv_get_first_char(hs); hc; hc = hap_char_get_next(hc)) {
|
||||
if (((__hap_char_t *)hc)->iid == iid)
|
||||
return hc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get target characteristics by it's UUID
|
||||
*/
|
||||
hap_char_t *hap_serv_get_char_by_uuid(hap_serv_t *hs, const char *uuid)
|
||||
{
|
||||
if (!hs | !uuid)
|
||||
return NULL;
|
||||
|
||||
hap_char_t *hc;
|
||||
for (hc = hap_serv_get_first_char(hs); hc; hc = hap_char_get_next(hc)) {
|
||||
if (!strcmp(((__hap_char_t *)hc)->type_uuid, uuid))
|
||||
return hc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get characteristics UUID prefix number
|
||||
*/
|
||||
char *hap_serv_get_uuid(hap_serv_t *hs)
|
||||
{
|
||||
return ((__hap_serv_t *)hs)->type_uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default service bulk read callback, which will be registered while creating a service.
|
||||
* It will internally call the actual read routines
|
||||
*/
|
||||
static int hap_serv_def_bulk_read_cb(hap_read_data_t read_data[], int count,
|
||||
void *serv_priv, void *read_priv)
|
||||
{
|
||||
int i;
|
||||
if (!count) {
|
||||
return HAP_FAIL;
|
||||
}
|
||||
__hap_serv_t *hs = (__hap_serv_t *)hap_char_get_parent(read_data[0].hc);
|
||||
/* If no read routine is registered, just return success so that the cached values
|
||||
* will be used
|
||||
*/
|
||||
if (!hs->read_cb) {
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
|
||||
int ret = HAP_SUCCESS;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (hs->read_cb(read_data[i].hc, read_data[i].status, serv_priv, read_priv) != HAP_SUCCESS) {
|
||||
ret = HAP_FAIL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* @brief HAP create a service
|
||||
*/
|
||||
hap_serv_t *hap_serv_create(char *type_uuid)
|
||||
{
|
||||
ESP_MFI_ASSERT(type_uuid);
|
||||
__hap_serv_t *_hs = hap_platform_memory_calloc(1, sizeof(__hap_serv_t));
|
||||
if (!_hs) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_hs->type_uuid = type_uuid;
|
||||
_hs->bulk_read = hap_serv_def_bulk_read_cb;
|
||||
|
||||
return (hap_serv_t *)_hs;
|
||||
}
|
||||
|
||||
int hap_serv_link_serv(hap_serv_t *hs, hap_serv_t *linked_serv)
|
||||
{
|
||||
if (!hs || !linked_serv)
|
||||
return HAP_FAIL;
|
||||
|
||||
hap_linked_serv_t *cur = hap_platform_memory_calloc(1, sizeof(hap_linked_serv_t));
|
||||
if (!cur)
|
||||
return HAP_FAIL;
|
||||
cur->hs = linked_serv;
|
||||
|
||||
__hap_serv_t *_hs = (__hap_serv_t *)hs;
|
||||
hap_linked_serv_t *linked = _hs->linked_servs;
|
||||
|
||||
if (!linked) {
|
||||
_hs->linked_servs = cur;
|
||||
return HAP_SUCCESS;
|
||||
} else {
|
||||
while(linked->next) {
|
||||
linked = linked->next;
|
||||
}
|
||||
linked->next = cur;
|
||||
return HAP_SUCCESS;
|
||||
}
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP get target service IID
|
||||
*/
|
||||
uint32_t hap_serv_get_iid(hap_serv_t *hs)
|
||||
{
|
||||
if (!hs)
|
||||
return 0;
|
||||
|
||||
__hap_serv_t *tmp = (__hap_serv_t *)hs;
|
||||
|
||||
return tmp->iid;
|
||||
}
|
||||
|
||||
char *hap_serv_get_type_uuid(hap_serv_t *hs)
|
||||
{
|
||||
if (!hs)
|
||||
return 0;
|
||||
|
||||
__hap_serv_t *tmp = (__hap_serv_t *)hs;
|
||||
|
||||
return tmp->type_uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAP delete target service
|
||||
*/
|
||||
void hap_serv_delete(hap_serv_t *hs)
|
||||
{
|
||||
/* Returning success even if pointer is NULL, because it means
|
||||
* that the service is absent and as good as deleted
|
||||
*/
|
||||
if (!hs)
|
||||
return;
|
||||
__hap_serv_t *_hs = (__hap_serv_t *)hs;
|
||||
__hap_char_t *_hc = (__hap_char_t *)_hs->chars;
|
||||
while (_hc) {
|
||||
_hs->chars = _hc->next_char;
|
||||
hap_char_delete((hap_char_t *)_hc);
|
||||
_hc = (__hap_char_t *)_hs->chars;
|
||||
}
|
||||
if (_hs->linked_servs) {
|
||||
hap_linked_serv_t *cur = _hs->linked_servs;
|
||||
hap_linked_serv_t *next = cur->next;
|
||||
while (next) {
|
||||
hap_platform_memory_free(cur);
|
||||
cur = next;
|
||||
next = cur->next;
|
||||
}
|
||||
hap_platform_memory_free(cur);
|
||||
}
|
||||
hap_platform_memory_free(hs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add a characteristics to a service
|
||||
*/
|
||||
int hap_serv_add_char(hap_serv_t *hs, hap_char_t *hc)
|
||||
{
|
||||
if (!hs || !hc)
|
||||
return -1;
|
||||
__hap_serv_t *_hs = (__hap_serv_t *)hs;
|
||||
__hap_char_t *_hc = (__hap_char_t *)hc;
|
||||
|
||||
if (_hc->parent) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Characteristic already added");
|
||||
return HAP_FAIL;
|
||||
}
|
||||
|
||||
/* If the service has no characteristics, add this as the first */
|
||||
if (!_hs->chars) {
|
||||
_hs->chars = hc;
|
||||
} else {
|
||||
/* Else loop through the characteristics to get to the last one,
|
||||
* and add this at the end
|
||||
*/
|
||||
__hap_char_t *temp = (__hap_char_t *)_hs->chars;
|
||||
while (temp->next_char)
|
||||
temp = (__hap_char_t *)temp->next_char;
|
||||
temp->next_char = hc;
|
||||
}
|
||||
if (_hs->parent) {
|
||||
_hc->iid = ((__hap_acc_t *)(_hs->parent))->next_iid++;
|
||||
}
|
||||
_hc->parent = hs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hap_serv_set_write_cb(hap_serv_t *hs, hap_serv_write_t write)
|
||||
{
|
||||
if (hs) {
|
||||
((__hap_serv_t *)hs)->write_cb = write;
|
||||
}
|
||||
}
|
||||
|
||||
void hap_serv_set_read_cb(hap_serv_t *hs, hap_serv_read_t read)
|
||||
{
|
||||
if (hs) {
|
||||
((__hap_serv_t *)hs)->read_cb = read;
|
||||
}
|
||||
}
|
||||
|
||||
void hap_serv_set_bulk_read_cb(hap_serv_t *hs, hap_serv_bulk_read_t read)
|
||||
{
|
||||
if (hs) {
|
||||
((__hap_serv_t *)hs)->bulk_read = read;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hap_serv_t *hap_serv_get_next(hap_serv_t *hs)
|
||||
{
|
||||
if (hs) {
|
||||
return ((__hap_serv_t *)hs)->next_serv;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
hap_acc_t *hap_serv_get_parent(hap_serv_t *hs)
|
||||
{
|
||||
if (hs) {
|
||||
return ((__hap_serv_t *)hs)->parent;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void hap_serv_set_priv(hap_serv_t *hs, void *priv)
|
||||
{
|
||||
if (hs) {
|
||||
((__hap_serv_t *)hs)->priv = priv;
|
||||
}
|
||||
}
|
||||
|
||||
void *hap_serv_get_priv(hap_serv_t *hs)
|
||||
{
|
||||
if (hs) {
|
||||
return ((__hap_serv_t *)hs)->priv;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HAP_SERV_H_
|
||||
#define _HAP_SERV_H_
|
||||
|
||||
#include <hap.h>
|
||||
#include <esp_hap_char.h>
|
||||
#include <esp_hap_acc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct hap_linked_serv {
|
||||
hap_serv_t *hs;
|
||||
struct hap_linked_serv *next;
|
||||
} hap_linked_serv_t;
|
||||
|
||||
typedef struct hap_linked_serv hap_linked_serv_t;
|
||||
|
||||
/**
|
||||
* HAP service information
|
||||
*/
|
||||
typedef struct {
|
||||
char *type_uuid; /* String that defines the type of the service. */
|
||||
|
||||
uint32_t iid; /* service instance ID */
|
||||
|
||||
bool hidden; /* If set it to be True, the service is not visible to user. */
|
||||
bool primary; /* If set it to be True, this is the primary service of the accessory. */
|
||||
|
||||
/**
|
||||
* List of Characteristic objects. Must not be empty. The maximum number of characteristics
|
||||
* allowed is 100, and each characteristic in the array must have a unique type.
|
||||
*/
|
||||
hap_char_t *chars;
|
||||
hap_acc_t *parent;
|
||||
hap_serv_t *next_serv;
|
||||
|
||||
hap_serv_write_t write_cb;
|
||||
hap_serv_read_t read_cb;
|
||||
hap_serv_bulk_read_t bulk_read;
|
||||
hap_linked_serv_t *linked_servs;
|
||||
void *priv;
|
||||
} __hap_serv_t;
|
||||
|
||||
bool hap_serv_get_hidden(hap_serv_t *hs);
|
||||
bool hap_serv_get_primary(hap_serv_t *hs);
|
||||
hap_char_t *hap_serv_get_char_by_iid(hap_serv_t *hs, int32_t iid);
|
||||
char *hap_serv_get_uuid(hap_serv_t *hs);
|
||||
hap_serv_t *hap_serv_create(char *type_uuid);
|
||||
void hap_serv_delete(hap_serv_t *hs);
|
||||
int hap_serv_add_char(hap_serv_t *hs, hap_char_t *hc);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _HAP_SERV_H_ */
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <hap.h>
|
||||
|
||||
#include <base36.h>
|
||||
|
||||
static const char *TAG = "esp_hap_setup_payload";
|
||||
|
||||
#define SETUP_CODE_MASK 0x0000000007ffffff
|
||||
#define HAP_OVER_IP_MASK 0x0000000010000000
|
||||
#define WAC_MASK 0x0000000040000000
|
||||
#define SETUP_PAYLOAD_PREFIX "X-HM://00"
|
||||
|
||||
static void remove_chars(char *str, char c)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
while(str[i]) {
|
||||
if (str[i] != c) {
|
||||
str[j] = str[i];
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
str[j] = 0;
|
||||
}
|
||||
|
||||
char *esp_hap_get_setup_payload(char *setup_code, char *setup_id, bool wac_support, hap_cid_t cid)
|
||||
{
|
||||
if (!setup_code || !setup_id) {
|
||||
ESP_LOGE(TAG, "Setup code or Setup ID cannot be NULL");
|
||||
return NULL;
|
||||
}
|
||||
uint64_t payload = 0;
|
||||
if (strlen(setup_code) != 10 || strlen(setup_id) != 4) {
|
||||
ESP_LOGE(TAG, "Setup code or Setup ID not correct. Eg. 111-22-333, ES32");
|
||||
return NULL;
|
||||
}
|
||||
char setup_code_copy[11];
|
||||
strcpy(setup_code_copy, setup_code);
|
||||
remove_chars(setup_code_copy, '-');
|
||||
int64_t code = atoi(setup_code_copy);
|
||||
int64_t category = cid;
|
||||
category <<= 31;
|
||||
|
||||
payload |= code;
|
||||
payload |= category;
|
||||
payload |= HAP_OVER_IP_MASK;
|
||||
if (wac_support) {
|
||||
payload |= WAC_MASK;
|
||||
}
|
||||
char *base36_str = base36_to_str(payload);
|
||||
char setup_payload[24];
|
||||
snprintf(setup_payload, sizeof(setup_payload), "%s%s%s", SETUP_PAYLOAD_PREFIX, base36_str, setup_id);
|
||||
free(base36_str);
|
||||
return strdup(setup_payload);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HAP_WAC_H_
|
||||
#define _HAP_WAC_H_
|
||||
int hap_wac_start(void);
|
||||
int hap_wac2_network_switch(void);
|
||||
int hap_wac2_finish(void);
|
||||
int hap_wac2_stop(void);
|
||||
bool hap_is_mfi_auth_enabled();
|
||||
#endif /* _HAP_WAC_H_ */
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <esp_log.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
#include <string.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_hap_database.h>
|
||||
|
||||
esp_err_t hap_wifi_is_provisioned(bool *provisioned)
|
||||
{
|
||||
if (!provisioned) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
*provisioned = false;
|
||||
|
||||
/* Get Wi-Fi Station configuration */
|
||||
wifi_config_t wifi_cfg;
|
||||
if (esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_cfg) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (strlen((const char *) wifi_cfg.sta.ssid)) {
|
||||
*provisioned = true;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
bool hap_is_network_configured(void)
|
||||
{
|
||||
/* If only the Ethernet is enabled, return true */
|
||||
if (hap_priv.transport == HAP_TRANSPORT_ETHERNET) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool provisioned = false;
|
||||
hap_wifi_is_provisioned(&provisioned);
|
||||
return provisioned;
|
||||
}
|
||||
|
||||
void hap_erase_network_info(void)
|
||||
{
|
||||
esp_wifi_restore();
|
||||
}
|
||||
|
||||
esp_err_t hap_wifi_softap_start(char *ssid)
|
||||
{
|
||||
if (!ssid) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "SSID cannot be NULL");
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Starting SoftAP with SSID: %s", ssid);
|
||||
wifi_config_t wifi_config = {
|
||||
.ap = {
|
||||
.ssid = "",
|
||||
.ssid_len = 0,
|
||||
.max_connection = 4,
|
||||
.password = "",
|
||||
.authmode = WIFI_AUTH_OPEN
|
||||
},
|
||||
};
|
||||
size_t ssid_len = strnlen(ssid, sizeof(wifi_config.ap.ssid));
|
||||
memcpy(wifi_config.ap.ssid, ssid, ssid_len);
|
||||
wifi_mode_t mode;
|
||||
esp_wifi_get_mode(&mode);
|
||||
if (mode == WIFI_MODE_STA) {
|
||||
esp_wifi_set_mode(WIFI_MODE_APSTA);
|
||||
} else {
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
}
|
||||
esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config);
|
||||
esp_wifi_start();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t hap_wifi_softap_stop(void)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Stopping SoftAP.");
|
||||
wifi_mode_t mode;
|
||||
esp_wifi_get_mode(&mode);
|
||||
if (mode == WIFI_MODE_AP) {
|
||||
esp_wifi_stop();
|
||||
}
|
||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t hap_wifi_sta_connect(wifi_config_t *config)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_INFO, "Connecting to Wi-Fi.");
|
||||
wifi_mode_t mode;
|
||||
esp_wifi_get_mode(&mode);
|
||||
if (mode == WIFI_MODE_AP || mode == WIFI_MODE_APSTA) {
|
||||
esp_wifi_set_mode(WIFI_MODE_APSTA);
|
||||
} else {
|
||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
}
|
||||
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
|
||||
esp_wifi_start();
|
||||
esp_wifi_connect();
|
||||
return ESP_OK;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_WIFI_H_
|
||||
#define _HAP_WIFI_H_
|
||||
#include <hap.h>
|
||||
bool hap_is_network_configured();
|
||||
void hap_wifi_restart();
|
||||
void hap_erase_network_info();
|
||||
#endif /* _HAP_WIFI_H_ */
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include "mbedtls/aes.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_mfi_aes.h"
|
||||
|
||||
static const char* TAG = "mfi_aes_adapter";
|
||||
|
||||
/*!
|
||||
* @group AES 128-bit Counter Mode API
|
||||
|
||||
* @abstract API to encrypt or decrypt using AES-128 in counter mode.
|
||||
*
|
||||
* Call esp_mfi_aes_ctr_init to initialize the context. Don't use the context until it has been initialized.
|
||||
* Call esp_mfi_aes_ctr_update to encrypt or decrypt N bytes of input and generate N bytes of output.
|
||||
* Call esp_mfi_aes_ctr_final to finalize the context. After finalizing, you must call AES_CTR_Init to use it again.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint8_t key[MFI_AES_CTR_SIZE];
|
||||
uint8_t nonce[MFI_AES_CTR_SIZE];
|
||||
} aes_ctr_context_t;
|
||||
|
||||
/**
|
||||
* @bref Create AES context
|
||||
*
|
||||
* @param none
|
||||
*
|
||||
* @return the AES context point
|
||||
*/
|
||||
esp_mfi_aes_ctr_t esp_mfi_aes_ctr_new(void)
|
||||
{
|
||||
aes_ctr_context_t *context = NULL;
|
||||
context = (aes_ctr_context_t *) malloc(sizeof(aes_ctr_context_t));
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Initialize AES context, include initialize the key and nonce
|
||||
*
|
||||
* @param incontext AES context point
|
||||
* inkey AES key
|
||||
* innonce AES nonce
|
||||
*
|
||||
* @return the result
|
||||
* 0 : sucessful
|
||||
* others : failed
|
||||
*/
|
||||
int esp_mfi_aes_ctr_init(esp_mfi_aes_ctr_t incontext, const uint8_t inkey[MFI_AES_CTR_SIZE], const uint8_t innonce[MFI_AES_CTR_SIZE])
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (incontext == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
aes_ctr_context_t *context = incontext;
|
||||
|
||||
memcpy(context->key, inkey, MFI_AES_CTR_SIZE);
|
||||
memcpy(context->nonce, innonce, MFI_AES_CTR_SIZE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Update AES context
|
||||
*
|
||||
* @param incontext AES context point
|
||||
* insrc the data point of update
|
||||
* insrclen the data length
|
||||
* indst the data pint of output
|
||||
*
|
||||
* @return the result
|
||||
* 0 : sucessful
|
||||
* others : failed
|
||||
*/
|
||||
int esp_mfi_aes_ctr_update(esp_mfi_aes_ctr_t incontext, const void *insrc, uint16_t insrclen, void *indst)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (incontext == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
aes_ctr_context_t *context = incontext;
|
||||
size_t offset = 0;
|
||||
uint8_t stream_block[MFI_AES_CTR_SIZE];
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
|
||||
ret = mbedtls_aes_setkey_enc( &ctx, context->key, 128);
|
||||
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "mfi aes setkey[%d]", ret);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = mbedtls_aes_crypt_ctr(&ctx, insrclen, &offset, context->nonce, stream_block, (uint8_t *) insrc, indst);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "mfi aes crypt[%d]", ret);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Destory AES context point
|
||||
*
|
||||
* @param incontext AES context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_aes_ctr_final(esp_mfi_aes_ctr_t incontext)
|
||||
{
|
||||
if (incontext) {
|
||||
memset((aes_ctr_context_t *) incontext, 0, sizeof(aes_ctr_context_t));
|
||||
free(incontext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef ESP_MFI_AES_H_
|
||||
#define ESP_MFI_AES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MFI_AES_CTR_SIZE 16
|
||||
|
||||
typedef void* esp_mfi_aes_ctr_t;
|
||||
|
||||
/**
|
||||
* @brief Create AES context
|
||||
*
|
||||
* @return pointer of the AES context
|
||||
*/
|
||||
esp_mfi_aes_ctr_t esp_mfi_aes_ctr_new(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize AES context, including the key and nonce
|
||||
*
|
||||
* @param incontext pointer of the AES context
|
||||
* @param inkey AES key
|
||||
* @param innonce AES nonce
|
||||
*
|
||||
* @return
|
||||
* - 0 : succeed
|
||||
* - others : fail
|
||||
*/
|
||||
int esp_mfi_aes_ctr_init(esp_mfi_aes_ctr_t incontext, const uint8_t inkey[MFI_AES_CTR_SIZE], const uint8_t innonce[MFI_AES_CTR_SIZE]);
|
||||
|
||||
/**
|
||||
* @brief Update AES context
|
||||
*
|
||||
* @param incontext AES context point
|
||||
* @param insrc pointer of the source data to be updated
|
||||
* @param insrclen source data length
|
||||
* @param indst pointer of the updated data
|
||||
*
|
||||
* @return
|
||||
* - 0 : succeed
|
||||
* - others : fail
|
||||
*/
|
||||
int esp_mfi_aes_ctr_update(esp_mfi_aes_ctr_t incontext, const void *insrc, uint16_t insrclen, void *indst);
|
||||
|
||||
/**
|
||||
* @brief Destroy AES context pointer
|
||||
*
|
||||
* @param incontext pointer of the AES context
|
||||
*/
|
||||
void esp_mfi_aes_ctr_final(esp_mfi_aes_ctr_t incontext);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ESP_MFI_AES_H_ */
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include "mbedtls/base64.h"
|
||||
|
||||
/**
|
||||
* @brief transform bin data to base64 data
|
||||
*/
|
||||
int esp_mfi_base64_encode(const char *src, int len, char *dest, int dest_len, int *out_len)
|
||||
{
|
||||
int ret = mbedtls_base64_encode((unsigned char *)dest, dest_len, (size_t *)out_len, (unsigned char *)src, len);
|
||||
if (ret != 0){
|
||||
return -EINVAL;
|
||||
} else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief transform base64 data to bin data
|
||||
*/
|
||||
int esp_mfi_base64_decode(const char *src, int len, char *dest, int dest_len, int *out_len)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = mbedtls_base64_decode((unsigned char *)dest, dest_len, (size_t *)out_len, (unsigned char *)src, len);
|
||||
if (ret != 0) {
|
||||
return -EINVAL;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef ESP_MFI_BASE64_H_
|
||||
#define ESP_MFI_BASE64_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief transform bin data to base64 data
|
||||
*
|
||||
* @param src input data point
|
||||
* @param len input data length
|
||||
* @param dest output data point
|
||||
* @param dest_len output data buffer length
|
||||
* @param out_len output data length
|
||||
*
|
||||
* @return
|
||||
* - 0 : succeed
|
||||
* - others : fail
|
||||
*/
|
||||
int esp_mfi_base64_encode(const char *src, int len, char *dest, int dest_len, int *out_len);
|
||||
|
||||
/**
|
||||
* @brief transform base64 data to bin data
|
||||
*
|
||||
* @param src input data point
|
||||
* @param len input data length
|
||||
* @param dest output data point
|
||||
* @param dest_len output data buffer length
|
||||
* @param out_len output data length
|
||||
*
|
||||
* @return
|
||||
* - 0 : succeed
|
||||
* - others : fail
|
||||
*/
|
||||
int esp_mfi_base64_decode(const char *src, int len, char *dest, int dest_len, int *out_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ESP_MFI_BASE64_H_ */
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <hap.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
|
||||
#define RED_CHAR 31
|
||||
#define GREEN_CHAR 32
|
||||
#define YELLOW_CHAR 33
|
||||
#define BLUE_CHAR 34
|
||||
#define CYAN_CHAR 36
|
||||
#define WHITE_CHAR 37
|
||||
|
||||
#ifdef CONFIG_MFI_DEBUG_LEVEL_INIT
|
||||
#define MFI_DEBUG_LEVEL_INIT CONFIG_MFI_DEBUG_LEVEL_INIT
|
||||
#else /* CONFIG_MFI_DEBUG_LEVEL_INIT */
|
||||
#define MFI_DEBUG_LEVEL_INIT 0
|
||||
#endif /* CONFIG_MFI_DEBUG_LEVEL_INIT */
|
||||
|
||||
static uint32_t mfi_debug_level = MFI_DEBUG_LEVEL_INIT;
|
||||
|
||||
/**
|
||||
* @bref set the MFI debugging level
|
||||
*/
|
||||
int esp_mfi_set_debug_level(uint32_t level)
|
||||
{
|
||||
mfi_debug_level = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref get the MFI debugging level and output data color
|
||||
*/
|
||||
uint32_t esp_mfi_get_debug_level(uint32_t level, uint32_t *color)
|
||||
{
|
||||
const uint32_t prospect_table[] = {
|
||||
0, /* no */
|
||||
WHITE_CHAR, /* information: 1, color(37: white) */
|
||||
GREEN_CHAR, /* warn : 3, color(32: green) */
|
||||
RED_CHAR, /* error : 3, color(31: red) */
|
||||
YELLOW_CHAR, /* assert : 4, color(33: yellow) */
|
||||
CYAN_CHAR /* block : 5, color(36: cyan) */
|
||||
/* others : n, color(34: blue) */
|
||||
};
|
||||
const uint32_t prospect_table_size = sizeof(prospect_table) / sizeof(prospect_table[0]);
|
||||
|
||||
if (level < prospect_table_size)
|
||||
*color = prospect_table[level];
|
||||
else
|
||||
*color = BLUE_CHAR;
|
||||
|
||||
return mfi_debug_level;
|
||||
}
|
||||
|
||||
void hap_set_debug_level(hap_debug_level_t level)
|
||||
{
|
||||
esp_mfi_set_debug_level(level);
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ESP_MFI_DEBUG_H_
|
||||
#define ESP_MFI_DEBUG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#define ESP_MFI_DEBUG_FL "\n"
|
||||
#define CONFIG_ESP_MFI_DEBUG_ENABLE
|
||||
#ifdef CONFIG_ESP_MFI_DEBUG_ENABLE
|
||||
#define ESP_MFI_DEBUG_ENABLE
|
||||
#endif /* CONFIG_ESP_MFI_DEBUG_ENABLE */
|
||||
|
||||
#define CONFIG_ESP_MFI_ASSERT
|
||||
#ifdef CONFIG_ESP_MFI_ASSERT
|
||||
#define ESP_MFI_ASSERT_ENABLE
|
||||
#endif /* CONFIG_ESP_MFI_ASSERT */
|
||||
|
||||
#define CONFIG_MFI_DEBUG_LEVEL_INIT 0
|
||||
#define CONFIG_ESP_MFI_ASSERT_BLOCK
|
||||
#ifdef CONFIG_ESP_MFI_ASSERT_BLOCK
|
||||
#define ESP_MFI_ASSERT_BLOCK() while (1)
|
||||
#else /* CONFIG_ESP_MFI_ASSERT_BLOCK */
|
||||
#define ESP_MFI_ASSERT_BLOCK()
|
||||
#endif /* CONFIG_ESP_MFI_ASSERT_BLOCK */
|
||||
|
||||
/* debug level with different color */
|
||||
#define ESP_MFI_DEBUG_INFO 1
|
||||
#define ESP_MFI_DEBUG_WARN 2
|
||||
#define ESP_MFI_DEBUG_ERR 3
|
||||
#define ESP_MFI_DEBUG_ASSERT 4
|
||||
#define ESP_MFI_DEBUG_BLOCK 5
|
||||
|
||||
/**
|
||||
* @bref set the MFI debugging level
|
||||
*
|
||||
* @param level debugging level
|
||||
*
|
||||
* @return the result
|
||||
* = 0 : OK
|
||||
* others : failed
|
||||
*/
|
||||
int esp_mfi_set_debug_level(uint32_t level);
|
||||
|
||||
/**
|
||||
* @bref get the MFI debugging level and output data color
|
||||
*
|
||||
* @param level input debug level
|
||||
* @param color data color
|
||||
*
|
||||
* @return MFI debugging level
|
||||
*/
|
||||
uint32_t esp_mfi_get_debug_level(uint32_t level, uint32_t *color);
|
||||
|
||||
/**
|
||||
* @bref format the string and data, then output it
|
||||
*
|
||||
* @param level if level > MFI_DEBUG_LEVEL then output it
|
||||
* @param fmt format string
|
||||
* @param ... parameters of format string
|
||||
*
|
||||
* @return none
|
||||
*
|
||||
* void ESP_MFI_DEBUG(unsigned int level, const char *fmt, ...);
|
||||
*/
|
||||
#ifdef ESP_MFI_DEBUG_ENABLE
|
||||
/*
|
||||
#define ESP_MFI_DEBUG(l, fmt, ...) \
|
||||
{ \
|
||||
uint32_t __color_LINE; \
|
||||
if (l > esp_mfi_get_debug_level(l, &__color_LINE)) { \
|
||||
printf("\e[1;%dm" fmt "\e[0m" ESP_MFI_DEBUG_FL, \
|
||||
__color_LINE, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
#define ESP_MFI_DEBUG_INTR(l, fmt, ...) \
|
||||
{ \
|
||||
uint32_t __color_LINE; \
|
||||
if (l > esp_mfi_get_debug_level(l, &__color_LINE)) { \
|
||||
ets_printf("\e[1;%dm" fmt "\e[0m" ESP_MFI_DEBUG_FL, \
|
||||
__color_LINE, ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ESP_MFI_DEBUG(l, fmt, ...) \
|
||||
{ \
|
||||
uint32_t __color_LINE; \
|
||||
if (l > esp_mfi_get_debug_level(l, &__color_LINE)) { \
|
||||
printf("[%7lu] " fmt "\n", ((unsigned long) (esp_timer_get_time() / 1000ULL)), ##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
#define ESP_MFI_DEBUG_INTR(l, fmt, ...) \
|
||||
{ \
|
||||
uint32_t __color_LINE; \
|
||||
if (l > esp_mfi_get_debug_level(l, &__color_LINE)) { \
|
||||
printf("[%7lu] " fmt "\n", ((unsigned long) (esp_timer_get_time() / 1000ULL)), ##__VA_ARGS__); \
|
||||
} \
|
||||
}*/
|
||||
|
||||
#define ESP_MFI_DEBUG(l, fmt, ...) \
|
||||
{ \
|
||||
printf("[%7lu] " fmt "\n", ((unsigned long) (esp_timer_get_time() / 1000ULL)), ##__VA_ARGS__); \
|
||||
}
|
||||
#define ESP_MFI_DEBUG_INTR(l, fmt, ...) \
|
||||
{ \
|
||||
printf("[%7lu] " fmt "\n", ((unsigned long) (esp_timer_get_time() / 1000ULL)), ##__VA_ARGS__); \
|
||||
}
|
||||
#else /* ESP_MFI_DEBUG_ENABLE */
|
||||
#define ESP_MFI_DEBUG(l, fmt, ...)
|
||||
#define ESP_MFI_DEBUG_INTR(l, fmt, ...)
|
||||
#endif /* ESP_MFI_DEBUG_ENABLE */
|
||||
|
||||
/**
|
||||
* @bref if the signal is false(0) do something depended on configuration
|
||||
*
|
||||
* @param conditions checking conditions
|
||||
*
|
||||
* @return none
|
||||
*
|
||||
* void ESP_MFI_ASSERT(int conditions);
|
||||
*/
|
||||
#ifdef ESP_MFI_ASSERT_ENABLE
|
||||
#define ESP_MFI_ASSERT(cond) \
|
||||
{ \
|
||||
if (!(cond)) { \
|
||||
printf("\e[1;33m" "ESP_MFI assert file %s line %d" ESP_MFI_DEBUG_FL, \
|
||||
__FILE__, __LINE__); \
|
||||
ESP_MFI_ASSERT_BLOCK(); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define ESP_MFI_ASSERT(cond)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @bref check if data length matches given length
|
||||
*
|
||||
* @param optlen data length
|
||||
* @param opttype data
|
||||
*
|
||||
* @return none
|
||||
*
|
||||
* void ESP_MFI_CHECK_OPTLEN(int conditions, (all type) data);
|
||||
*/
|
||||
#define ESP_MFI_CHECK_OPTLEN(optlen, opttype) \
|
||||
do { \
|
||||
if ((optlen) > sizeof(opttype)) { \
|
||||
return -1; \
|
||||
} \
|
||||
} while(0) \
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ESP_MFI_DEBUG_H_ */
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <esp_http_server.h>
|
||||
#include <esp_mfi_debug.h>
|
||||
#include <esp_hap_pair_common.h>
|
||||
#include <esp_hap_pair_setup.h>
|
||||
#include <esp_hap_database.h>
|
||||
#include <hap.h>
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(HAP_WAC_EVENT);
|
||||
|
||||
int hap_pair_setup_manage_mfi_auth(pair_setup_ctx_t *ps_ctx, hap_tlv_data_t *tlv_data, hap_tlv_error_t *tlv_error)
|
||||
{
|
||||
|
||||
if (ps_ctx->method == HAP_METHOD_PAIR_SETUP) {
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "Secure pair setup not supported. Please use MFi variant of the SDK.");
|
||||
*tlv_error = kTLVError_Unknown;
|
||||
return HAP_FAIL;
|
||||
}
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_WARN, "Using pair-setup without MFi.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int hap_enable_mfi_auth(hap_mfi_auth_type_t auth_type)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_WARN, "MFi auth not supported. Falling back to HAP_MFI_AUTH_NONE");
|
||||
hap_priv.auth_type = HAP_MFI_AUTH_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hap_wac_start(void)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "WAC not supported. Please use MFi variant of the SDK.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int hap_wac_stop(void)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "WAC not supported. Please use MFi variant of the SDK.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int hap_enable_hw_auth(void)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "MFi HW Auth not supported. Please use MFi variant of the SDK.");
|
||||
return ESP_FAIL;
|
||||
|
||||
}
|
||||
int hap_enable_sw_auth(void)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "MFi SW Auth not supported. Please use MFi variant of the SDK.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int hap_register_secure_message_handler(httpd_handle_t handle)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "MFi SW Auth not supported. Please use MFi variant of the SDK.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int hap_unregister_secure_message_handler(httpd_handle_t handle)
|
||||
{
|
||||
ESP_MFI_DEBUG(ESP_MFI_DEBUG_ERR, "MFi SW Auth not supported. Please use MFi variant of the SDK.");
|
||||
return ESP_FAIL;
|
||||
}
|
|
@ -0,0 +1,689 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sdkconfig.h>
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#else
|
||||
#include "esp32/rom/ets_sys.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_I2C_SOFTWARE
|
||||
#include "driver/gpio.h"
|
||||
#else
|
||||
#include "driver/i2c.h"
|
||||
#endif
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_mfi_i2c.h"
|
||||
|
||||
static const char *TAG = "mfi_i2c";
|
||||
|
||||
#ifndef CONFIG_I2C_SOFTWARE
|
||||
|
||||
#define I2C_MASTER_NUM I2C_NUM_0
|
||||
#define I2C_MASTER_FREQ_HZ CONFIG_IC2_SPEED
|
||||
#define I2C_MASTER_RX_BUF_DISABLE 0
|
||||
#define I2C_MASTER_TX_BUF_DISABLE 0
|
||||
#define ACK_CHECK_EN 1
|
||||
#define ACK_VAL 0
|
||||
#define NACK_VAL 1
|
||||
|
||||
/* To use GPIOs 17/18 for the Hardware I2C, replace the numbers 26/27 below
|
||||
*/
|
||||
#define I2C_MASTER_SDA_GPIO CONFIG_SDA_GPIO
|
||||
#define I2C_MASTER_SCL_GPIO CONFIG_SCL_GPIO
|
||||
|
||||
#define I2C_MASTER_MAX_READ CONFIG_I2C_MAX_READ_COUNT
|
||||
#define I2C_MASTER_RETRY_TIMES 500
|
||||
#define I2C_MASTER_TICKS_TIMES 2 * I2C_MASTER_RETRY_TIMES
|
||||
#define I2C_MASTER_MAX_RETRY 10
|
||||
#define I2C_MASTER_INTERNAL_TIMES 8 * I2C_MASTER_RETRY_TIMES
|
||||
/**
|
||||
* @brief Initialize I2C information
|
||||
*/
|
||||
int esp_mfi_i2c_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
i2c_config_t conf;
|
||||
int i2c_master_port = I2C_MASTER_NUM;
|
||||
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
conf.sda_io_num = I2C_MASTER_SDA_GPIO;
|
||||
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.scl_io_num = I2C_MASTER_SCL_GPIO;
|
||||
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
|
||||
|
||||
ret = i2c_param_config(i2c_master_port, &conf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "I2C parameter initial fail %d", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "I2C driver initial fail %d", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* @brief write data buffer to slave
|
||||
*/
|
||||
int esp_mfi_i2c_write(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
if (!buff)
|
||||
return -EINVAL;
|
||||
|
||||
ESP_LOGI(TAG, "Writing to HW I2C");
|
||||
|
||||
int ret = 0;
|
||||
i2c_cmd_handle_t cmd = NULL;
|
||||
i = 0;
|
||||
|
||||
do {
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
|
||||
// Send write address of the CP
|
||||
i2c_master_write_byte(cmd, slvaddr, ACK_CHECK_EN);
|
||||
// Send register address to slave.
|
||||
i2c_master_write_byte(cmd, regaddr, ACK_CHECK_EN);
|
||||
// Send data out.
|
||||
i2c_master_write(cmd, buff, len, ACK_CHECK_EN);
|
||||
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TICKS_TIMES / portTICK_RATE_MS);
|
||||
i ++;
|
||||
i2c_cmd_link_delete(cmd);
|
||||
ets_delay_us(I2C_MASTER_RETRY_TIMES);
|
||||
} while(ret != ESP_OK && i < I2C_MASTER_MAX_RETRY);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Write data to slave fail %d.", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief read data form slave
|
||||
*/
|
||||
int esp_mfi_i2c_read(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len)
|
||||
{
|
||||
uint16_t i, j = 0;
|
||||
|
||||
if (!buff)
|
||||
return -EINVAL;
|
||||
|
||||
ESP_LOGI(TAG, "Reading from HW I2C");
|
||||
|
||||
int ret = 0;
|
||||
i2c_cmd_handle_t cmd = NULL;
|
||||
i = 0;
|
||||
|
||||
do {
|
||||
for (j = 0; j < I2C_MASTER_MAX_READ; j++) {
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
|
||||
// Send write address of the CP
|
||||
i2c_master_write_byte(cmd, slvaddr, ACK_CHECK_EN);
|
||||
// Send register address to slave.
|
||||
i2c_master_write_byte(cmd, regaddr, ACK_CHECK_EN);
|
||||
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TICKS_TIMES / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if (ret == ESP_OK) {
|
||||
break;
|
||||
} else {
|
||||
ets_delay_us(I2C_MASTER_INTERNAL_TIMES);
|
||||
}
|
||||
}
|
||||
|
||||
ets_delay_us(I2C_MASTER_INTERNAL_TIMES);
|
||||
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
|
||||
i2c_master_write_byte(cmd, slvaddr + 1, ACK_CHECK_EN);
|
||||
if (len == 1)
|
||||
i2c_master_read_byte(cmd, buff, NACK_VAL);
|
||||
else {
|
||||
i2c_master_read(cmd, buff, len - 1, ACK_VAL);
|
||||
i2c_master_read_byte(cmd, buff + len - 1, NACK_VAL);
|
||||
}
|
||||
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TICKS_TIMES / portTICK_RATE_MS);
|
||||
i ++;
|
||||
i2c_cmd_link_delete(cmd);
|
||||
} while (ret != ESP_OK && i < I2C_MASTER_MAX_RETRY);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Read data from slave fail %d.", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define MFI_CP_BUSY_RETRY_TIMES CONFIG_I2C_RETRY_COUNT
|
||||
|
||||
#define GPIO_Pin_26 GPIO_SEL_26
|
||||
#define GPIO_Pin_27 GPIO_SEL_27
|
||||
|
||||
#define gpio_set_output_level(a, b) gpio_set_level(a, b)
|
||||
#define gpio_get_input_level(a) gpio_get_level(a)
|
||||
|
||||
#define GPIO_Mode_Input_OutOD GPIO_MODE_INPUT_OUTPUT_OD
|
||||
#define GPIO_PullDown_DIS GPIO_PULLDOWN_DISABLE
|
||||
#define GPIO_PullUp_EN GPIO_PULLUP_ENABLE
|
||||
#define GPIO_Mode_Out_OD GPIO_MODE_OUTPUT_OD
|
||||
|
||||
#define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO26_U
|
||||
#define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_GPIO27_U
|
||||
#define I2C_MASTER_SDA_GPIO 26
|
||||
#define I2C_MASTER_SDA_PIN GPIO_Pin_26
|
||||
#define I2C_MASTER_SDA_FUNC FUNC_GPIO26_GPIO26
|
||||
#define I2C_MASTER_SCL_GPIO 27
|
||||
#define I2C_MASTER_SCL_PIN GPIO_Pin_27
|
||||
#define I2C_MASTER_SCL_FUNC FUNC_GPIO27_GPIO27
|
||||
|
||||
#define I2C_MASTER_SDA_HIGH_SCL_HIGH() \
|
||||
gpio_set_output_level(I2C_MASTER_SDA_GPIO,1); \
|
||||
gpio_set_output_level(I2C_MASTER_SCL_GPIO,1);
|
||||
|
||||
#define I2C_MASTER_SDA_HIGH_SCL_LOW() \
|
||||
gpio_set_output_level(I2C_MASTER_SDA_GPIO,1); \
|
||||
gpio_set_output_level(I2C_MASTER_SCL_GPIO,0);
|
||||
|
||||
#define I2C_MASTER_SDA_LOW_SCL_HIGH() \
|
||||
gpio_set_output_level(I2C_MASTER_SDA_GPIO,0); \
|
||||
gpio_set_output_level(I2C_MASTER_SCL_GPIO,1);
|
||||
|
||||
#define I2C_MASTER_SDA_LOW_SCL_LOW() \
|
||||
gpio_set_output_level(I2C_MASTER_SDA_GPIO,0); \
|
||||
gpio_set_output_level(I2C_MASTER_SCL_GPIO,0);
|
||||
|
||||
#define i2c_master_wait ets_delay_us
|
||||
|
||||
static void i2c_master_init(void);
|
||||
static void i2c_master_stop(void);
|
||||
|
||||
static uint8_t s_last_sda;
|
||||
static uint8_t s_last_scl;
|
||||
|
||||
/**
|
||||
* @brief set I2C SDA and SCL bit value for half CLK cycle
|
||||
*/
|
||||
static void i2c_master_setDC(uint8_t SDA, uint8_t SCL)
|
||||
{
|
||||
SDA &= 0x01;
|
||||
SCL &= 0x01;
|
||||
s_last_sda = SDA;
|
||||
s_last_scl = SCL;
|
||||
|
||||
if ((0 == SDA) && (0 == SCL)) {
|
||||
I2C_MASTER_SDA_LOW_SCL_LOW();
|
||||
} else if ((0 == SDA) && (1 == SCL)) {
|
||||
I2C_MASTER_SDA_LOW_SCL_HIGH();
|
||||
} else if ((1 == SDA) && (0 == SCL)) {
|
||||
I2C_MASTER_SDA_HIGH_SCL_LOW();
|
||||
} else {
|
||||
I2C_MASTER_SDA_HIGH_SCL_HIGH();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get I2C SDA bit value
|
||||
*/
|
||||
static uint8_t i2c_master_getDC(void)
|
||||
{
|
||||
uint8_t sda_out;
|
||||
|
||||
sda_out = gpio_get_input_level(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO));
|
||||
|
||||
return sda_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief initialize I2C bus to enable i2c operations
|
||||
*/
|
||||
static void i2c_master_init(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5);
|
||||
|
||||
// when SCL = 0, toggle SDA to clear up
|
||||
i2c_master_setDC(0, 0) ;
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 0) ;
|
||||
i2c_master_wait(5);
|
||||
|
||||
// set data_cnt to max value
|
||||
for (i = 0; i < 28; i++) {
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5); // sda 1, scl 0
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5); // sda 1, scl 1
|
||||
}
|
||||
|
||||
// reset all
|
||||
i2c_master_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief configure SDA and SCL GPIO to open-drain output mode
|
||||
*/
|
||||
static void i2c_master_gpio_init(void)
|
||||
{
|
||||
|
||||
gpio_config_t io_config;
|
||||
io_config.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
io_config.mode = GPIO_MODE_INPUT_OUTPUT_OD;
|
||||
io_config.pin_bit_mask = I2C_MASTER_SDA_PIN;
|
||||
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_config.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
|
||||
gpio_config(&io_config);
|
||||
|
||||
io_config.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
io_config.mode = GPIO_MODE_OUTPUT_OD;
|
||||
io_config.pin_bit_mask = I2C_MASTER_SCL_PIN;
|
||||
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_config.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
|
||||
|
||||
gpio_config(&io_config);
|
||||
|
||||
gpio_pullup_en(I2C_MASTER_SDA_GPIO);
|
||||
gpio_pullup_en(I2C_MASTER_SCL_GPIO);
|
||||
gpio_pulldown_dis(I2C_MASTER_SDA_GPIO);
|
||||
gpio_pulldown_dis(I2C_MASTER_SCL_GPIO);
|
||||
|
||||
i2c_master_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set I2C to start operation
|
||||
*/
|
||||
static void i2c_master_start(void)
|
||||
{
|
||||
i2c_master_setDC(1, s_last_scl);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5); // sda 1, scl 1
|
||||
i2c_master_setDC(0, 1);
|
||||
i2c_master_wait(5); // sda 0, scl 1
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set i2c to stop sending state
|
||||
*/
|
||||
static void i2c_master_stop(void)
|
||||
{
|
||||
i2c_master_wait(5);
|
||||
|
||||
i2c_master_setDC(0, s_last_scl);
|
||||
i2c_master_wait(5); // sda 0
|
||||
i2c_master_setDC(0, 1);
|
||||
i2c_master_wait(5); // sda 0, scl 1
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5); // sda 1, scl 1
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set ack to i2c bus as level value
|
||||
*/
|
||||
static void i2c_master_setAck(uint8_t level)
|
||||
{
|
||||
i2c_master_setDC(s_last_sda, 0);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(level, 0);
|
||||
i2c_master_wait(5); // sda level, scl 0
|
||||
i2c_master_setDC(level, 1);
|
||||
i2c_master_wait(8); // sda level, scl 1
|
||||
i2c_master_setDC(level, 0);
|
||||
i2c_master_wait(5); // sda level, scl 0
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief confirm if peer send ack
|
||||
*/
|
||||
static uint8_t i2c_master_getAck(void)
|
||||
{
|
||||
uint8_t retVal;
|
||||
|
||||
i2c_master_setDC(s_last_sda, 0);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5);
|
||||
|
||||
retVal = i2c_master_getDC();
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get I2C response
|
||||
*/
|
||||
static bool i2c_master_checkAck(void)
|
||||
{
|
||||
if (i2c_master_getAck()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief send I2C ack signal
|
||||
*/
|
||||
static void i2c_master_send_ack(void)
|
||||
{
|
||||
i2c_master_setAck(0x0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief send I2C not ack signal
|
||||
*/
|
||||
static void i2c_master_send_nack(void)
|
||||
{
|
||||
i2c_master_setAck(0x1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief read byte from i2c bus
|
||||
*/
|
||||
static uint8_t i2c_master_readByte(void)
|
||||
{
|
||||
uint8_t retVal = 0;
|
||||
uint8_t k, i;
|
||||
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(s_last_sda, 0);
|
||||
i2c_master_wait(5); // sda 1, scl 0
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5); // sda 1, scl 0
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5); // sda 1, scl 1
|
||||
|
||||
k = i2c_master_getDC();
|
||||
i2c_master_wait(5);
|
||||
|
||||
if (i == 7) {
|
||||
i2c_master_wait(3);
|
||||
}
|
||||
|
||||
k <<= (7 - i);
|
||||
retVal |= k;
|
||||
}
|
||||
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5); // sda 1, scl 0
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write byte to i2c
|
||||
*/
|
||||
static void i2c_master_writeByte(uint8_t wrdata)
|
||||
{
|
||||
uint8_t dat;
|
||||
int8_t i;
|
||||
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(s_last_sda, 0);
|
||||
i2c_master_wait(5);
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
dat = wrdata >> i;
|
||||
i2c_master_setDC(dat, 0);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(dat, 1);
|
||||
i2c_master_wait(5);
|
||||
|
||||
if (i == 0) {
|
||||
i2c_master_wait(3); ////
|
||||
}
|
||||
|
||||
i2c_master_setDC(dat, 0);
|
||||
i2c_master_wait(5);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write byte to i2c and get check ack
|
||||
*/
|
||||
static bool i2c_write_byte(uint8_t data, uint8_t iter)
|
||||
{
|
||||
if (iter == 0) {
|
||||
iter = 1;
|
||||
}
|
||||
|
||||
while (iter--) {
|
||||
i2c_master_writeByte(data);
|
||||
|
||||
if (i2c_master_getAck()) {
|
||||
i2c_master_stop();
|
||||
ets_delay_us(500); // Wait 500us and retry.
|
||||
i2c_master_start();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
i2c_master_stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write byte to target register of target I2C slave
|
||||
*/
|
||||
static void i2c_write_reg(uint8_t slave_add, uint8_t reg_add, uint16_t data)
|
||||
{
|
||||
i2c_master_start();
|
||||
if (false == i2c_write_byte(slave_add, 30)) {
|
||||
ESP_LOGE(TAG, "1Slave is busy, [%02x]TIME OUT\n", slave_add);
|
||||
}
|
||||
if (false == i2c_write_byte(reg_add, 30)) {
|
||||
ESP_LOGE(TAG, "2Slave is busy, [%02x]TIME OUT\n", reg_add);
|
||||
}
|
||||
if (false == i2c_write_byte((data >> 8), 30)) {
|
||||
ESP_LOGE(TAG, "3Slave is busy, [%02x]TIME OUT\n", data);
|
||||
}
|
||||
if (false == i2c_write_byte((data & 0xFF), 30)) {
|
||||
ESP_LOGE(TAG, "3Slave is busy, [%02x]TIME OUT\n", data);
|
||||
}
|
||||
i2c_master_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief a block of data to I2C
|
||||
*/
|
||||
static void i2s_write_arr(const uint8_t *data, size_t len)
|
||||
{
|
||||
if (data == NULL || len <= 0) return;
|
||||
|
||||
i2c_master_start();
|
||||
|
||||
int i = 0;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (false == i2c_write_byte(data[i], 30)) {
|
||||
ESP_LOGE(TAG, "Slave is busy, [%02x]TIME OUT\n", data[i]);
|
||||
}
|
||||
}
|
||||
i2c_master_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize I2C information
|
||||
*/
|
||||
int esp_mfi_i2c_init(void)
|
||||
{
|
||||
i2c_master_gpio_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finish I2C information
|
||||
*/
|
||||
int esp_mfi_i2c_end(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write one byte data to slave
|
||||
*/
|
||||
static int esp_mfi_i2c_write_byte(uint8_t data, uint16_t retrytimes)
|
||||
{
|
||||
uint16_t times = retrytimes;
|
||||
if (0 == times) {
|
||||
times = 1;
|
||||
}
|
||||
|
||||
while (times--) {
|
||||
i2c_master_writeByte(data);
|
||||
if (i2c_master_getAck()) {
|
||||
i2c_master_wait(500); /**< Wait 500us and retry */
|
||||
i2c_master_stop();
|
||||
i2c_master_wait(500); /**< Wait 500us and retry */
|
||||
i2c_master_start();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_master_stop();
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write data buffer to slave
|
||||
*/
|
||||
int esp_mfi_i2c_write(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
if (!buff)
|
||||
return -EINVAL;
|
||||
|
||||
ESP_LOGI(TAG, "Writing to SW I2C");
|
||||
|
||||
i2c_master_start();
|
||||
|
||||
/**< Send write address of the CP */
|
||||
if (esp_mfi_i2c_write_byte(slvaddr, MFI_CP_BUSY_RETRY_TIMES) != 0) {
|
||||
ESP_LOGE(TAG, "Slave is busy, time out.");
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/**< Send register address to slave */
|
||||
if (esp_mfi_i2c_write_byte(regaddr, 0) != 0) {
|
||||
ESP_LOGE(TAG, "Write register address[0x%02x] to slave.", regaddr);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/**< Send data out */
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (esp_mfi_i2c_write_byte(*buff++, 0) != 0) {
|
||||
ESP_LOGE(TAG, "Write data[0x%02x] to slave.", *buff);
|
||||
return -ENODATA;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_master_stop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief read data form slave
|
||||
*/
|
||||
int esp_mfi_i2c_read(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
if (!buff)
|
||||
return -EINVAL;
|
||||
|
||||
ESP_LOGI(TAG, "Reading from SW I2C");
|
||||
|
||||
i2c_master_start();
|
||||
|
||||
/**< Send write address of the CP */
|
||||
if (esp_mfi_i2c_write_byte(slvaddr, MFI_CP_BUSY_RETRY_TIMES) != 0) {
|
||||
ESP_LOGE(TAG, "Slave is busy, time out.");
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/**< Send register address to slave */
|
||||
if (esp_mfi_i2c_write_byte(regaddr, 0) != 0) {
|
||||
ESP_LOGE(TAG, "Write register address[0x%02x] to slave.", regaddr);
|
||||
return -ENODATA;
|
||||
}
|
||||
i2c_master_stop();
|
||||
|
||||
i2c_master_wait(4000);
|
||||
|
||||
/**< Send read address of the CP */
|
||||
if (esp_mfi_i2c_write_byte((slvaddr + 1), MFI_CP_BUSY_RETRY_TIMES) != 0) {
|
||||
ESP_LOGE(TAG, "Slave is busy, time out.");
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
buff[i] = i2c_master_readByte();
|
||||
|
||||
i2c_master_setAck((i == (len - 1)) ? 1 : 0);
|
||||
}
|
||||
|
||||
i2c_master_stop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ESP_MFI_I2C_H_
|
||||
#define ESP_MFI_I2C_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize I2C
|
||||
*
|
||||
* @return
|
||||
* - 0 : succeed
|
||||
* - others : fail
|
||||
*/
|
||||
int esp_mfi_i2c_init(void);
|
||||
|
||||
/**
|
||||
* @brief I2C operation end
|
||||
*
|
||||
* @return
|
||||
* - 0 : succeed
|
||||
* - others : fail
|
||||
*/
|
||||
int esp_mfi_i2c_end(void);
|
||||
|
||||
/**
|
||||
* @brief Write data to I2C slave
|
||||
*
|
||||
* @param slvaddr address of slave
|
||||
* @param regaddr address of register
|
||||
* @param buff pointer of data to write
|
||||
* @param len the data length
|
||||
*
|
||||
* @return
|
||||
* - 0 : succeed
|
||||
* - others : fail
|
||||
*/
|
||||
int esp_mfi_i2c_write(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Read data from I2C slave
|
||||
*
|
||||
* @param slvaddr address of slave
|
||||
* @param regaddr address of register
|
||||
* @param buff pointer of data read
|
||||
* @param len the data length
|
||||
*
|
||||
* @return
|
||||
* - 0 : succeed
|
||||
* - others : fail
|
||||
*/
|
||||
int esp_mfi_i2c_read(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ESP_MFI_I2C_H_ */
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_system.h>
|
||||
|
||||
/**
|
||||
* @bref Obtain a series of random bytes
|
||||
*
|
||||
* @param buf the random bytes were copied point
|
||||
* len the number of bytes requested
|
||||
*
|
||||
* @return the result
|
||||
* > 0 : the number of bytes that were copied to the buffer
|
||||
* others : failed
|
||||
*/
|
||||
int esp_mfi_get_random(uint8_t *buf, uint16_t len)
|
||||
{
|
||||
int i, j;
|
||||
unsigned long tmp;
|
||||
|
||||
for (i = 0; i < ((len + 3) & ~3) / 4; i++) {
|
||||
tmp = esp_random();
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
if ((i * 4 + j) < len) {
|
||||
buf[i * 4 + j] = (uint8_t) (tmp >> (j * 8));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ESP_MFI_RAND_H_
|
||||
#define ESP_MFI_RAND_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get a random number of bytes specified.
|
||||
*
|
||||
* @param buf pointer of the random number it gets
|
||||
* @param len specified bytes of the random number
|
||||
*
|
||||
* @return
|
||||
* - > 0 : the actual length(bytes) of the random number
|
||||
* - others : failure
|
||||
*/
|
||||
int esp_mfi_get_random(uint8_t *buf, uint16_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ESP_MFI_RAND_H_ */
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mbedtls/sha1.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "mbedtls/sha512.h"
|
||||
|
||||
#include "esp_mfi_sha.h"
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP8266
|
||||
#define mbedtls_sha512_starts mbedtls_sha512_starts_ret
|
||||
#define mbedtls_sha512_update mbedtls_sha512_update_ret
|
||||
#define mbedtls_sha512_finish mbedtls_sha512_finish_ret
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @bref Create SHA1 context
|
||||
*
|
||||
* @param none
|
||||
*
|
||||
* @return the SHA1 context point
|
||||
*/
|
||||
esp_mfi_sha_ctx_t esp_mfi_sha1_new(void)
|
||||
{
|
||||
mbedtls_sha1_context *context = NULL;
|
||||
context = (mbedtls_sha1_context *) malloc(sizeof(mbedtls_sha1_context));
|
||||
mbedtls_sha1_init(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Free SHA1 context
|
||||
*
|
||||
* @param ctx the SHA1 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha1_free(esp_mfi_sha_ctx_t ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
mbedtls_sha1_free( (mbedtls_sha1_context *)ctx);
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Init SHA1 context
|
||||
*
|
||||
* @param ctx the SHA1 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha1_init(esp_mfi_sha_ctx_t ctx)
|
||||
{
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha1_starts((mbedtls_sha1_context *) ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Update SHA1 context
|
||||
*
|
||||
* @param ctx the SHA1 context point
|
||||
* msg input data point
|
||||
* len input data length
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha1_update(esp_mfi_sha_ctx_t ctx, const uint8_t * msg, int len)
|
||||
{
|
||||
if (ctx == NULL || msg == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha1_update((mbedtls_sha1_context *) ctx, msg, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Final SHA1 context
|
||||
*
|
||||
* @param digest output data point
|
||||
* ctx the SHA1 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha1_final(esp_mfi_sha_ctx_t ctx, uint8_t *digest)
|
||||
{
|
||||
if (ctx == NULL || digest == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha1_finish((mbedtls_sha1_context *) ctx, digest);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA256 declarations
|
||||
*/
|
||||
|
||||
/**
|
||||
* @bref Create SHA256 context
|
||||
*
|
||||
* @param none
|
||||
*
|
||||
* @return the SHA256 context point
|
||||
*/
|
||||
esp_mfi_sha_ctx_t esp_mfi_sha256_new(void)
|
||||
{
|
||||
mbedtls_sha256_context *context = NULL;
|
||||
context = (mbedtls_sha256_context *) malloc(sizeof(mbedtls_sha256_context));
|
||||
mbedtls_sha256_init(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Free SHA256 context
|
||||
*
|
||||
* @param ctx the SHA256 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha256_free(esp_mfi_sha_ctx_t ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
mbedtls_sha256_free((mbedtls_sha256_context *)ctx);
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Init SHA256 context
|
||||
*
|
||||
* @param ctx the SHA256 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha256_init(esp_mfi_sha_ctx_t ctx)
|
||||
{
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha256_starts((mbedtls_sha256_context *) ctx, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Update SHA256 context
|
||||
*
|
||||
* @param ctx the SHA256 context point
|
||||
* msg input data point
|
||||
* len input data length
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha256_update(esp_mfi_sha_ctx_t ctx, const uint8_t *input, int len)
|
||||
{
|
||||
if (ctx == NULL || input == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha256_update((mbedtls_sha256_context *) ctx, input, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Final SHA256 context
|
||||
*
|
||||
* @param digest output data point
|
||||
* ctx the SHA256 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha256_final(esp_mfi_sha_ctx_t ctx, uint8_t *digest)
|
||||
{
|
||||
if (ctx == NULL || digest == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha256_finish((mbedtls_sha256_context *) ctx, digest);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA512 declarations
|
||||
*/
|
||||
|
||||
/**
|
||||
* @bref Create SHA512 context
|
||||
*
|
||||
* @param none
|
||||
*
|
||||
* @return the SHA512 context point
|
||||
*/
|
||||
esp_mfi_sha_ctx_t esp_mfi_sha512_new(void)
|
||||
{
|
||||
mbedtls_sha512_context *context = NULL;
|
||||
context = (mbedtls_sha512_context *) malloc(sizeof(mbedtls_sha512_context));
|
||||
mbedtls_sha512_init(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Free SHA512 context
|
||||
*
|
||||
* @param ctx the SHA512 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha512_free(esp_mfi_sha_ctx_t ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
mbedtls_sha512_free((mbedtls_sha512_context *)ctx);
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Init SHA512 context
|
||||
*
|
||||
* @param ctx the SHA512 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha512_init(esp_mfi_sha_ctx_t ctx)
|
||||
{
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha512_starts((mbedtls_sha512_context *) ctx, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Update SHA512 context
|
||||
*
|
||||
* @param ctx the SHA512 context point
|
||||
* msg input data point
|
||||
* len input data length
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha512_update(esp_mfi_sha_ctx_t ctx, const uint8_t *input, int len)
|
||||
{
|
||||
if (ctx == NULL || input == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha512_update((mbedtls_sha512_context *) ctx, input, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @bref Final SHA512 context
|
||||
*
|
||||
* @param digest output data point
|
||||
* ctx the SHA512 context point
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
void esp_mfi_sha512_final(esp_mfi_sha_ctx_t ctx, uint8_t *digest)
|
||||
{
|
||||
if (ctx == NULL || digest == NULL)
|
||||
return;
|
||||
|
||||
mbedtls_sha512_finish((mbedtls_sha512_context *) ctx, digest);
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ESP_MFI_SHA_H_
|
||||
#define ESP_MFI_SHA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void* esp_mfi_sha_ctx_t;
|
||||
|
||||
/*
|
||||
* SHA1 declarations
|
||||
*/
|
||||
|
||||
#define MFI_SHA1_SIZE 20
|
||||
|
||||
/**
|
||||
* @brief Create SHA1 context
|
||||
*
|
||||
* @return pointer of the SHA1 context
|
||||
*/
|
||||
esp_mfi_sha_ctx_t esp_mfi_sha1_new(void);
|
||||
|
||||
/**
|
||||
* @brief Init SHA1 context
|
||||
*
|
||||
* @param ctx pointer of the SHA1 context
|
||||
*/
|
||||
void esp_mfi_sha1_init(esp_mfi_sha_ctx_t ctx);
|
||||
|
||||
/**
|
||||
* @brief Update SHA1 context
|
||||
*
|
||||
* @param ctx pointer of the SHA1 context
|
||||
* @param msg pointer of the input data
|
||||
* @param len input data length
|
||||
*/
|
||||
void esp_mfi_sha1_update(esp_mfi_sha_ctx_t ctx, const uint8_t *msg, int len);
|
||||
|
||||
/**
|
||||
* @brief Final SHA1 context
|
||||
*
|
||||
* @param digest pointer of output data
|
||||
* @param ctx pointer of the SHA1 context
|
||||
*/
|
||||
void esp_mfi_sha1_final(esp_mfi_sha_ctx_t ctx, uint8_t *digest);
|
||||
|
||||
/**
|
||||
* @brief Free SHA1 context
|
||||
*
|
||||
* @param ctx pointer of the SHA1 context
|
||||
*/
|
||||
void esp_mfi_sha1_free(esp_mfi_sha_ctx_t ctx);
|
||||
|
||||
/*
|
||||
* SHA256 declarations
|
||||
*/
|
||||
|
||||
#define MFI_SHA256_SIZE 32
|
||||
|
||||
/**
|
||||
* @brief Create SHA256 context
|
||||
*
|
||||
* @return pointer of the SHA256 context
|
||||
*/
|
||||
esp_mfi_sha_ctx_t esp_mfi_sha256_new(void);
|
||||
|
||||
/**
|
||||
* @brief Init SHA256 context
|
||||
*
|
||||
* @param ctx pointer of the SHA256 context
|
||||
*/
|
||||
void esp_mfi_sha256_init(esp_mfi_sha_ctx_t ctx);
|
||||
|
||||
/**
|
||||
* @brief Update SHA256 context
|
||||
*
|
||||
* @param ctx pointer of the SHA256 context
|
||||
* @param msg pointer of input data
|
||||
* @param len input data length
|
||||
*/
|
||||
void esp_mfi_sha256_update(esp_mfi_sha_ctx_t ctx, const uint8_t *input, int len);
|
||||
|
||||
/**
|
||||
* @brief Final SHA256 context
|
||||
*
|
||||
* @param digest pointer of output data
|
||||
* @param ctx pointer of the SHA256 context
|
||||
*/
|
||||
void esp_mfi_sha256_final(esp_mfi_sha_ctx_t ctx, uint8_t *digest);
|
||||
|
||||
/**
|
||||
* @brief Free SHA256 context
|
||||
*
|
||||
* @param ctx pointer of the SHA256 context
|
||||
*/
|
||||
void esp_mfi_sha256_free(esp_mfi_sha_ctx_t ctx);
|
||||
|
||||
/*
|
||||
* SHA512 declarations
|
||||
*/
|
||||
|
||||
#define MFI_SHA512_SIZE 64
|
||||
|
||||
/**
|
||||
* @brief Create SHA512 context
|
||||
*
|
||||
* @return pointer of the SHA512 context
|
||||
*/
|
||||
esp_mfi_sha_ctx_t esp_mfi_sha512_new(void);
|
||||
|
||||
/**
|
||||
* @brief Init SHA512 context
|
||||
*
|
||||
* @param ctx pointer of the SHA512 context
|
||||
*/
|
||||
void esp_mfi_sha512_init(esp_mfi_sha_ctx_t ctx);
|
||||
|
||||
/**
|
||||
* @brief Update SHA512 context
|
||||
*
|
||||
* @param ctx pointer of the SHA512 context
|
||||
* @param msg pointer of input data
|
||||
* @param len input data length
|
||||
*/
|
||||
void esp_mfi_sha512_update(esp_mfi_sha_ctx_t ctx, const uint8_t *input, int len);
|
||||
|
||||
/**
|
||||
* @brief Final SHA512 context
|
||||
*
|
||||
* @param digest pointer of output data
|
||||
* @param ctx pointer of the SHA512 context
|
||||
*/
|
||||
void esp_mfi_sha512_final(esp_mfi_sha_ctx_t ctx, uint8_t *digest);
|
||||
|
||||
/**
|
||||
* @brief Free SHA512 context
|
||||
*
|
||||
* @param ctx pointer of the SHA512 context
|
||||
*/
|
||||
void esp_mfi_sha512_free(esp_mfi_sha_ctx_t ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ESP_MFI_SHA_H_ */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,706 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <hap.h>
|
||||
#include <hap_apple_servs.h>
|
||||
#include <hap_apple_chars.h>
|
||||
|
||||
hap_serv_t *hap_serv_accessory_information_create(hap_acc_cfg_t *cfg)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_ACCESSORY_INFORMATION);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_name_create(cfg->name)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (hap_serv_add_char(hs, hap_char_model_create(cfg->model)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (hap_serv_add_char(hs, hap_char_manufacturer_create(cfg->manufacturer)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (hap_serv_add_char(hs, hap_char_serial_number_create(cfg->serial_num)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (hap_serv_add_char(hs, hap_char_firmware_revision_create(cfg->fw_rev)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (hap_serv_add_char(hs, hap_char_identify_create()) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (cfg->hw_rev) {
|
||||
if (hap_serv_add_char(hs, hap_char_hardware_revision_create(cfg->hw_rev)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_protocol_information_create(char *version)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_PROTOCOL_INFORMATION);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_version_create(version)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_fan_create(bool on)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_FAN);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_on_create(on)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_garage_door_opener_create(uint8_t curr_door_state, uint8_t targ_door_state, bool obstr_detect)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_GARAGE_DOOR_OPENER);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_door_state_create(curr_door_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_door_state_create(targ_door_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_obstruction_detect_create(obstr_detect)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_lightbulb_create(bool on)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_LIGHTBULB);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_on_create(on)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_lock_management_create(hap_tlv8_val_t *lock_control_point, char * version)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_LOCK_MANAGEMENT);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_lock_control_point_create(lock_control_point)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_version_create(version)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_lock_mechanism_create(uint8_t lock_curr_state, uint8_t lock_targ_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_LOCK_MECHANISM);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_lock_current_state_create(lock_curr_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_lock_target_state_create(lock_targ_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_outlet_create(bool on, bool outlet_in_use)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_OUTLET);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_on_create(on)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_outlet_in_use_create(outlet_in_use)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_switch_create(bool on)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_SWITCH);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_on_create(on)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_thermostat_create(uint8_t curr_heating_cooling_state, uint8_t targ_heating_cooling_state, float curr_temp,
|
||||
float targ_temp, uint8_t temp_disp_units)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_THERMOSTAT);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_heating_cooling_state_create(curr_heating_cooling_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_heating_cooling_state_create(targ_heating_cooling_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_temperature_create(curr_temp)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_temperature_create(targ_temp)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_temperature_display_units_create(temp_disp_units)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_air_quality_sensor_create(uint8_t air_quality)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_AIR_QUALITY_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_air_quality_create(air_quality)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_security_system_create(uint8_t security_sys_curr_state, uint8_t security_sys_targ_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_SECURITY_SYSTEM);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_security_system_current_state_create(security_sys_curr_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_security_system_target_state_create(security_sys_targ_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_carbon_monoxide_sensor_create(uint8_t carbon_monoxide_detected)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_CARBON_MONOXIDE_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_carbon_monoxide_detected_create(carbon_monoxide_detected)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_contact_sensor_create(uint8_t contact_sensor_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_CONTACT_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_contact_sensor_state_create(contact_sensor_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_door_create(uint8_t curr_pos, uint8_t targ_pos, uint8_t pos_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_DOOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_position_create(curr_pos)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_position_create(targ_pos)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_position_state_create(pos_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_humidity_sensor_create(float curr_rel_humidity)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_HUMIDITY_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_relative_humidity_create(curr_rel_humidity)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_leak_sensor_create(uint8_t leak_detected)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_LEAK_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_leak_detected_create(leak_detected)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_light_sensor_create(float curr_ambient_light_level)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_LIGHT_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_ambient_light_level_create(curr_ambient_light_level)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_motion_sensor_create(bool motion_detected)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_MOTION_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_motion_detected_create(motion_detected)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_occupancy_sensor_create(uint8_t occupancy_detected)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_OCCUPANCY_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_occupancy_detected_create(occupancy_detected)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_smoke_sensor_create(uint8_t smoke_detected)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_SMOKE_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_smoke_detected_create(smoke_detected)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_stateless_programmable_switch_create(uint8_t programmable_switch_event)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_STATLESS_PROGRAMMABLE_SWITCH);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_programmable_switch_event_create(programmable_switch_event)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_temperature_sensor_create(float curr_temp)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_TEMPERATURE_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_temperature_create(curr_temp)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_window_create(uint8_t curr_pos, uint8_t targ_pos, uint8_t pos_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_WINDOW);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_position_create(curr_pos)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_position_create(targ_pos)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_position_state_create(pos_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_window_covering_create(uint8_t targ_pos, uint8_t curr_pos, uint8_t pos_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_WINDOW_COVERING);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_position_create(targ_pos)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_position_create(curr_pos)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_position_state_create(pos_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_battery_service_create(uint8_t battery_level, uint8_t charging_state, uint8_t status_low_battery)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_BATTERY_SERVICE);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_battery_level_create(battery_level)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_charging_state_create(charging_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_status_low_battery_create(status_low_battery)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_carbon_dioxide_sensor_create(uint8_t carbon_dioxide_detected)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_CARBON_DIOXIDE_SENSOR);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_carbon_dioxide_detected_create(carbon_dioxide_detected)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_fan_v2_create(uint8_t active)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_FAN_V2);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_active_create(active)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_slat_create(uint8_t curr_slat_state, uint8_t slat_type)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_SLAT);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_slat_state_create(curr_slat_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_slat_type_create(slat_type)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_filter_maintenance_create(uint8_t filter_change_indication)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_FILTER_MAINTENANCE);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_filter_change_indication_create(filter_change_indication)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_air_purifier_create(uint8_t active, uint8_t curr_air_purifier_state, uint8_t
|
||||
targ_air_purifier_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_AIR_PURIFIER);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_active_create(active)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_air_purifier_state_create(curr_air_purifier_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_air_purifier_state_create(targ_air_purifier_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_heater_cooler_create(uint8_t active, float curr_temp, uint8_t curr_heater_cooler_state, uint8_t targ_heater_cooler_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_HEATER_COOLER);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_active_create(active)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_temperature_create(curr_temp)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_heater_cooler_state_create(curr_heater_cooler_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_heater_cooler_state_create(targ_heater_cooler_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_humidifier_dehumidifier_create(uint8_t active, float curr_rel_humidity, uint8_t
|
||||
curr_humid_dehumid_state, uint8_t targ_humid_dehumid_state)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_HUMIDIFIER_DEHUMIDIFIER);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_active_create(active)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_relative_humidity_create(curr_rel_humidity)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_current_humidifier_dehumidifier_state_create(curr_humid_dehumid_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_target_humidifier_dehumidifier_state_create(targ_humid_dehumid_state)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_service_label_create(uint8_t service_label_namespace)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_SERVICE_LABEL);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_service_label_namespace_create(service_label_namespace)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_irrigation_system_create(uint8_t active, uint8_t prog_mode, uint8_t in_use)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_IRRIGATION_SYSTEM);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_active_create(active)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_program_mode_create(prog_mode)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_in_use_create(in_use)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_valve_create(uint8_t active, uint8_t in_use, uint8_t valve_type)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_VALVE);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_active_create(active)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_in_use_create(in_use)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_valve_type_create(valve_type)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hap_serv_t *hap_serv_faucet_create(uint8_t active)
|
||||
{
|
||||
hap_serv_t *hs = hap_serv_create(HAP_SERV_UUID_FAUCET);
|
||||
if (!hs) {
|
||||
return NULL;
|
||||
}
|
||||
if (hap_serv_add_char(hs, hap_char_active_create(active)) != HAP_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
return hs;
|
||||
err:
|
||||
hap_serv_delete(hs);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -0,0 +1,560 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
/** HAP Apple Services
|
||||
*
|
||||
* This offers helper APIs for all the standard HomeKit Services defined by Apple
|
||||
*/
|
||||
#ifndef _HAP_APPLE_SERVS_H_
|
||||
#define _HAP_APPLE_SERVS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <hap.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define HAP_SERV_UUID_ACCESSORY_INFORMATION "3E"
|
||||
#define HAP_SERV_UUID_PROTOCOL_INFORMATION "A2"
|
||||
#define HAP_SERV_UUID_FAN "40"
|
||||
#define HAP_SERV_UUID_GARAGE_DOOR_OPENER "41"
|
||||
#define HAP_SERV_UUID_LIGHTBULB "43"
|
||||
#define HAP_SERV_UUID_LOCK_MANAGEMENT "44"
|
||||
#define HAP_SERV_UUID_LOCK_MECHANISM "45"
|
||||
#define HAP_SERV_UUID_SWITCH "49"
|
||||
#define HAP_SERV_UUID_OUTLET "47"
|
||||
#define HAP_SERV_UUID_THERMOSTAT "4A"
|
||||
#define HAP_SERV_UUID_AIR_QUALITY_SENSOR "8D"
|
||||
#define HAP_SERV_UUID_SECURITY_SYSTEM "7E"
|
||||
#define HAP_SERV_UUID_CARBON_MONOXIDE_SENSOR "7F"
|
||||
#define HAP_SERV_UUID_CONTACT_SENSOR "80"
|
||||
#define HAP_SERV_UUID_DOOR "81"
|
||||
#define HAP_SERV_UUID_HUMIDITY_SENSOR "82"
|
||||
#define HAP_SERV_UUID_LEAK_SENSOR "83"
|
||||
#define HAP_SERV_UUID_LIGHT_SENSOR "84"
|
||||
#define HAP_SERV_UUID_MOTION_SENSOR "85"
|
||||
#define HAP_SERV_UUID_OCCUPANCY_SENSOR "86"
|
||||
#define HAP_SERV_UUID_SMOKE_SENSOR "87"
|
||||
#define HAP_SERV_UUID_STATLESS_PROGRAMMABLE_SWITCH "89"
|
||||
#define HAP_SERV_UUID_TEMPERATURE_SENSOR "8A"
|
||||
#define HAP_SERV_UUID_WINDOW "8B"
|
||||
#define HAP_SERV_UUID_WINDOW_COVERING "8C"
|
||||
#define HAP_SERV_UUID_BATTERY_SERVICE "96"
|
||||
#define HAP_SERV_UUID_CARBON_DIOXIDE_SENSOR "97"
|
||||
#define HAP_SERV_UUID_FAN_V2 "B7"
|
||||
#define HAP_SERV_UUID_SLAT "B9"
|
||||
#define HAP_SERV_UUID_FILTER_MAINTENANCE "BA"
|
||||
#define HAP_SERV_UUID_AIR_PURIFIER "BB"
|
||||
#define HAP_SERV_UUID_HEATER_COOLER "BC"
|
||||
#define HAP_SERV_UUID_HUMIDIFIER_DEHUMIDIFIER "BD"
|
||||
#define HAP_SERV_UUID_SERVICE_LABEL "CC"
|
||||
#define HAP_SERV_UUID_IRRIGATION_SYSTEM "CF"
|
||||
#define HAP_SERV_UUID_VALVE "D0"
|
||||
#define HAP_SERV_UUID_FAUCET "D7"
|
||||
|
||||
/** Create Accessory Information Service
|
||||
*
|
||||
* This API will create the Accessory Information Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] cfg The accessory configuration to be used to create the service
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_accessory_information_create(hap_acc_cfg_t *cfg);
|
||||
|
||||
/** Create Protocol Information Service
|
||||
*
|
||||
* This API will create the Protocol Information Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] version The Protocol Version string
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_protocol_information_create(char *version);
|
||||
|
||||
/** Create Fan Service
|
||||
*
|
||||
* This API will create the Fan Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] on Initial "On" state of the service
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_fan_create(bool on);
|
||||
|
||||
/** Create Garage Door Opener Service
|
||||
*
|
||||
* This API will create the Garage Door Opener Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] curr_door_state Initial value of the current door state characteristic
|
||||
* @param[in] targ_door_state Value of the target door state characteristic
|
||||
* @param[in] obstr_detect Value of the obstruction detected characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_garage_door_opener_create(uint8_t curr_door_state, uint8_t targ_door_state, bool obstr_detect)
|
||||
;
|
||||
|
||||
/** Create Light Bulb Service
|
||||
*
|
||||
* This API will create the Light Bulb Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] on Initial "On" state of the service
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_lightbulb_create(bool on);
|
||||
|
||||
/** Create Lock Management Service
|
||||
*
|
||||
* This API will create the Lock Management Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] lock_control_point Accepts data from TLV8 commands
|
||||
* @param[in] version The Protocol Version string
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_lock_management_create(hap_tlv8_val_t *lock_control_point, char * version);
|
||||
|
||||
/** Create Lock Mechanism Service
|
||||
*
|
||||
* This API will create the Lock Mechanism Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] lock_curr_state Current lock state of the service
|
||||
* @param[in] lock_targ_state Target lock state of the service
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_lock_mechanism_create(uint8_t lock_curr_state, uint8_t lock_targ_state);
|
||||
|
||||
/** Create Outlet Service
|
||||
*
|
||||
* This API will create the Outlet Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] on Initial "On" state of the service
|
||||
* @param[in] outlet_in_use Initial value of the outlet in use characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_outlet_create(bool on, bool outlet_in_use);
|
||||
|
||||
/** Create Switch Service
|
||||
*
|
||||
* This API will create the Switch Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] on Initial "On" state of the service
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_switch_create(bool on);
|
||||
|
||||
/** Create Thermostat Service
|
||||
*
|
||||
* This API will create the Thermostat Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] curr_heating_cooling_state Initial value of Current Heating Cooling State characteristic
|
||||
* @param[in] targ_heating_cooling_state Initial value of Target Heating Cooling State characteristic
|
||||
* @param[in] curr_temp Initial value of Current Temperature characteristic
|
||||
* @param[in] targ_temp Initial value of Target Temperature characteristic
|
||||
* @param[in] temp_disp_units Initial value of Temperature Display Units characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_thermostat_create(uint8_t curr_heating_cooling_state, uint8_t targ_heating_cooling_state, float curr_temp, float targ_temp, uint8_t temp_disp_units);
|
||||
|
||||
/** Create Air Quality Sensor Service
|
||||
*
|
||||
* This API will create the Air Quality Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] air_quality Initial value of Air Quality characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_air_quality_sensor_create(uint8_t air_quality);
|
||||
|
||||
/** Create Security System Current State Service
|
||||
*
|
||||
* This API will create the Security System Current State Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] security_sys_curr_state Initial value of Security System Current State characteristic
|
||||
* @param[in] security_sys_targ_state Initial value of Security System Target State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_security_system_create(uint8_t security_sys_curr_state, uint8_t security_sys_targ_state);
|
||||
|
||||
/** Create Carbon Monoxide Sensor Service
|
||||
*
|
||||
* This API will create the Carbon Monoxide Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] carbon_monoxide_detected Initial value of Carbon Monoxide Sensor characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_carbon_monoxide_sensor_create(uint8_t carbon_monoxide_detected);
|
||||
|
||||
/** Create Contact Sensor Service
|
||||
*
|
||||
* This API will create the Contact Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] contact_sensor_state Initial value of Contact Sensor State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_contact_sensor_create(uint8_t contact_sensor_state);
|
||||
|
||||
/** Create Door Service
|
||||
*
|
||||
* This API will create the Door Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] curr_pos Initial value of Current Position characteristic
|
||||
* @param[in] targ_pos Initial value of Target Position characteristic
|
||||
* @param[in] pos_state Initial value of Position State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_door_create(uint8_t curr_pos, uint8_t targ_pos, uint8_t pos_state);
|
||||
|
||||
/** Create Humidity Sensor Service
|
||||
*
|
||||
* This API will create the Humidity Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] curr_relative_humidity Initial value of Humidity Sensor State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_humidity_sensor_create(float curr_relative_humidity);
|
||||
|
||||
/** Leak Sensor Service
|
||||
*
|
||||
* This API will create the Leak Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] leak_detected Initial value of Leak Detected State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_leak_sensor_create(uint8_t leak_detected);
|
||||
|
||||
/** Light Sensor Service
|
||||
*
|
||||
* This API will create the Light Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] curr_ambient_light_level Initial value of Current Ambient Light Level characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_light_sensor_create(float curr_ambient_light_level);
|
||||
|
||||
/** Motion Sensor Service
|
||||
*
|
||||
* This API will create the Motion Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] motion_detected Initial value of Motion Detected characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_motion_sensor_create(bool motion_detected);
|
||||
|
||||
/** Occupancy Sensor Service
|
||||
*
|
||||
* This API will create the Occupancy Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] occupancy_detected Initial value of Occupancy Detected characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_occupancy_sensor_create(uint8_t occupancy_detected);
|
||||
|
||||
/** Smoke Sensor Service
|
||||
*
|
||||
* This API will create the Smoke Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] smoke_detected Initial value of Smoke Detected characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_smoke_sensor_create(uint8_t smoke_detected);
|
||||
|
||||
/** Stateless Programmable Switch Service
|
||||
*
|
||||
* This API will create the Stateless Programmable Switch Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] programmable_switch_event Initial value of Programmable Switch Event characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_stateless_programmable_switch_create(uint8_t programmable_switch_event);
|
||||
|
||||
/** Temperature Sensor Service
|
||||
*
|
||||
* This API will create the Temperature Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] curr_temp Initial value of Current Temprature characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_temperature_sensor_create(float curr_temp);
|
||||
|
||||
/** Window Service
|
||||
*
|
||||
* This API will create the Window Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] curr_pos Initial value of Current Position characteristic
|
||||
* @param[in] targ_pos Initial value of Target Position characteristic
|
||||
* @param[in] pos_state Initial value of Position State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_window_create(uint8_t curr_pos, uint8_t targ_pos, uint8_t pos_state);
|
||||
|
||||
/** Window Covering Service
|
||||
*
|
||||
* This API will create the Window Covering Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] targ_pos Initial value of Target Position characteristic
|
||||
* @param[in] curr_pos Initial value of Current Position characteristic
|
||||
* @param[in] pos_state Initial value of Position State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_window_covering_create(uint8_t targ_pos, uint8_t curr_pos, uint8_t pos_state);
|
||||
|
||||
/** Battery Service
|
||||
*
|
||||
* This API will create the Battery Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] battery_level Initial value of Battery Level characteristic
|
||||
* @param[in] charging_state Initial value of Charging State characteristic
|
||||
* @param[in] status_low_battery Initial value of Status Low Battery characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_battery_service_create(uint8_t battery_level, uint8_t charging_state, uint8_t status_low_battery);
|
||||
|
||||
/** Create Carbon Dioxide Sensor Service
|
||||
*
|
||||
* This API will create the Carbon Dioxide Sensor Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] carbon_dioxide_detected Initial value of Carbon Dioxide Sensor characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_carbon_dioxide_sensor_create(uint8_t carbon_dioxide_detected);
|
||||
|
||||
/** Fan v2 Service
|
||||
*
|
||||
* This API will create the Fan v2 Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] active Value of Active characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_fan_v2_create(uint8_t active);
|
||||
|
||||
/** Slat Service
|
||||
*
|
||||
* This API will create the Slat Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] curr_slat_state Value of Current Slat State characteristic
|
||||
* @param[in] slat_type Value of Slat Type characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_slat_create(uint8_t curr_slat_state, uint8_t slat_type);
|
||||
|
||||
/** Filter Maintenance Service
|
||||
*
|
||||
* This API will create the Filter Maintenance Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] filter_change_indication Value of Filter Change Indication characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_filter_maintenance_create(uint8_t filter_change_indication);
|
||||
|
||||
/** Air Purifier Service
|
||||
*
|
||||
* This API will create the Air Purifier Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] active Value of Active characteristic
|
||||
* @param[in] curr_air_purifier_state Value of Current Air Purifier State characteristic
|
||||
* @param[in] targ_air_purifier_state Value of Target Air Purifier State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_air_purifier_create(uint8_t active, uint8_t curr_air_purifier_state, uint8_t
|
||||
targ_air_purifier_state);
|
||||
|
||||
/** Heater Cooler Service
|
||||
*
|
||||
* This API will create the Heater Cooler Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] active Value of Active characteristic
|
||||
* @param[in] curr_temp Value of Current Temperature characteristic
|
||||
* @param[in] curr_heater_cooler_state Value of Current Heater Cooler State characteristic
|
||||
* @param[in] targ_heater_cooler_state Value of Target Heater Cooler State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_heater_cooler_create(uint8_t active, float curr_temp, uint8_t curr_heater_cooler_state, uint8_t targ_heater_cooler_state);
|
||||
|
||||
/** Create Humidifer Dehumidfier Service
|
||||
*
|
||||
* This API will create the Humidifer Dehumidfier Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] active Initial value of Active characteristic
|
||||
* @param[in] curr_rel_humid Initial value of Current Relative Humidity characteristic
|
||||
* @param[in] curr_humid_dehumid_state Initial value of Current Humidifier Dehumidifier State characteristic
|
||||
* @param[in] targ_humid_dehumid_state Initial value of Target Humidifier Dehumidifier State characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_humidifier_dehumidifier_create(uint8_t active, float curr_rel_humid, uint8_t
|
||||
curr_humid_dehumid_state, uint8_t targ_humid_dehumid_state);
|
||||
|
||||
/** Create Service Label Service
|
||||
*
|
||||
* This API will create the Service Label Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] service_label_namespace Initial value of Service Label Service characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_service_label_create(uint8_t service_label_namespace);
|
||||
|
||||
/** Create Irrigation System Service
|
||||
*
|
||||
* This API will create the Irrigation System Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] active Initial value of active characteristic
|
||||
* @param[in] prog_mode Initial value of Program Mode characteristic
|
||||
* @param[in] in_use Initial value of In Use characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_irrigation_system_create(uint8_t active, uint8_t prog_mode, uint8_t in_use);
|
||||
|
||||
/** Create Valve Service
|
||||
*
|
||||
* This API will create the Valve Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] active Initial value of active characteristic
|
||||
* @param[in] in_use Initial value of In Use characteristic
|
||||
* @param[in] valve_type Initial value of Valve Type characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_valve_create(uint8_t active, uint8_t in_use, uint8_t valve_type);
|
||||
|
||||
/** Create Faucet Service
|
||||
*
|
||||
* This API will create the Faucet Service with the mandatory
|
||||
* characteristics as per the HAP Specs.
|
||||
*
|
||||
* @param[in] active Initial value of active characteristic
|
||||
*
|
||||
* @return Pointer to the service object on success
|
||||
* @return NULL on failure
|
||||
*/
|
||||
hap_serv_t *hap_serv_faucet_create(uint8_t active);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _HAP_APPLE_SERVS_H_ */
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_BCT_H_
|
||||
#define _HAP_BCT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Change the name of the Bonjour Service for BCT
|
||||
*
|
||||
* This is required as per the "Manual Name Change" step of "Tester Interaction"
|
||||
* section of Bonjour Conformance Test.
|
||||
*
|
||||
* @param[in] name The desired new name for the service. For BCT 1.4 or earlier: "New - Bonjour Service Name".
|
||||
* For BCT 1.5 or later: "New-BCT"
|
||||
*/
|
||||
void hap_bct_change_name(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Trigger a Hot plug of the network interface for BCT
|
||||
*
|
||||
* This is required as per the "Cable Change Handling" and "Hot Plugging" steps
|
||||
* of "Tester Interaction" section of Bonjout Conformance Test
|
||||
*/
|
||||
void hap_bct_hot_plug();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _HAP_BCT_H_ */
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <esp_http_server.h>
|
||||
#include <_esp_hap_config.h>
|
||||
|
||||
httpd_handle_t *int_handle;
|
||||
int hap_platform_httpd_start(httpd_handle_t *handle)
|
||||
{
|
||||
httpd_config_t config = {
|
||||
.task_priority = tskIDLE_PRIORITY+5,
|
||||
.stack_size = CONFIG_HAP_HTTP_STACK_SIZE,
|
||||
.server_port = CONFIG_HAP_HTTP_SERVER_PORT,
|
||||
.ctrl_port = CONFIG_HAP_HTTP_CONTROL_PORT,
|
||||
.max_open_sockets = CONFIG_HAP_HTTP_MAX_OPEN_SOCKETS,
|
||||
.max_uri_handlers = CONFIG_HAP_HTTP_MAX_URI_HANDLERS,
|
||||
.max_resp_headers = 8,
|
||||
.backlog_conn = 5,
|
||||
.lru_purge_enable = true,
|
||||
.recv_wait_timeout = 5,
|
||||
.send_wait_timeout = 5,
|
||||
};
|
||||
esp_err_t err = httpd_start(handle, &config);
|
||||
if (err == ESP_OK) {
|
||||
int_handle = handle;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int hap_platform_httpd_stop(httpd_handle_t *handle)
|
||||
{
|
||||
esp_err_t err = httpd_stop(*handle);
|
||||
if (err == ESP_OK) {
|
||||
int_handle = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int hap_platform_httpd_get_port()
|
||||
{
|
||||
return CONFIG_HAP_HTTP_SERVER_PORT;
|
||||
}
|
||||
|
||||
void * hap_platform_httpd_get_sess_ctx(httpd_req_t *req)
|
||||
{
|
||||
if (req) {
|
||||
return req->sess_ctx;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t hap_platform_httpd_set_sess_ctx(httpd_req_t *req, void *ctx, httpd_free_ctx_fn_t free_ctx, bool ignore_ctx_changes)
|
||||
{
|
||||
if (req) {
|
||||
req->sess_ctx = ctx;
|
||||
req->free_ctx = free_ctx;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP8266
|
||||
req->ignore_sess_ctx_changes = ignore_ctx_changes;
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static char * hap_platform_httpd_rqtype_to_string(int method)
|
||||
{
|
||||
switch (method) {
|
||||
case HTTP_GET:
|
||||
return "GET";
|
||||
case HTTP_POST:
|
||||
return "POST";
|
||||
case HTTP_PUT:
|
||||
return "PUT";
|
||||
default:
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
const char *hap_platform_httpd_get_req_method(httpd_req_t *req)
|
||||
{
|
||||
if (req) {
|
||||
return hap_platform_httpd_rqtype_to_string(req->method);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *hap_platform_httpd_get_req_uri(httpd_req_t *req)
|
||||
{
|
||||
if (req) {
|
||||
return req->uri;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hap_platform_httpd_get_content_len(httpd_req_t *req)
|
||||
{
|
||||
if (req) {
|
||||
return req->content_len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
httpd_handle_t *hap_platform_httpd_get_handle()
|
||||
{
|
||||
return int_handle;
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_PLATFORM_HTTPD_H_
|
||||
#define _HAP_PLATFORM_HTTPD_H_
|
||||
#include <esp_http_server.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Start the webserver
|
||||
*
|
||||
* This API will be called by the HAP Core to start the webserver.
|
||||
*
|
||||
* @param[out] handle Pointer to the handle that should be populated by this
|
||||
* function on a success.
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error code otherwise
|
||||
*/
|
||||
int hap_platform_httpd_start(httpd_handle_t *handle);
|
||||
|
||||
/** Stop the webserver
|
||||
*
|
||||
* This API will be called by the HAP Core to stop the webserver.
|
||||
*
|
||||
* @param[in] handle Pointer to the handle created in hap_platform_httpd_start
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* @return error code otherwise
|
||||
*/
|
||||
int hap_platform_httpd_stop(httpd_handle_t *handle);
|
||||
|
||||
/** Get the current HTTP Port
|
||||
*
|
||||
* This API will be called by the HAP Core to get the HTTP Port being used. This will
|
||||
* be used for the mDNS announcement
|
||||
*
|
||||
* @return Configured HTTP Port
|
||||
*/
|
||||
int hap_platform_httpd_get_port();
|
||||
|
||||
/** Get the HTTPD session context
|
||||
*
|
||||
* This API will be called by the HAP Core to get the context associated with a given
|
||||
* session. It is generally used to maintain pairing information.
|
||||
*
|
||||
* @param[in] req HTTPD Request structure.
|
||||
*
|
||||
* @return pointer to the session context.
|
||||
*/
|
||||
void * hap_platform_httpd_get_sess_ctx(httpd_req_t *req);
|
||||
|
||||
/** Set the HTTPD session context
|
||||
*
|
||||
* This API will be called by the HAP Core to set the context associated with a given
|
||||
* session. It is generally used to maintain pairing information.
|
||||
*
|
||||
* @param[in] req HTTPD Request structure.
|
||||
* @param[in] ctx The context to be set for the given session.
|
||||
* @param[in] free_fn Pointer to a function that will be used by the HTTP Server to clear the context when the session closes
|
||||
* @param[in] ignore_ctx_changes Flag to indicate if the HTTP Server should ignore changes to the context across different requests.
|
||||
* If set to false, the server will try clearing the old context, if it detects any change.
|
||||
*
|
||||
* @return ESP_OK in success
|
||||
* @return ESP_FAIL if req is nULL
|
||||
*/
|
||||
esp_err_t hap_platform_httpd_set_sess_ctx(httpd_req_t *req, void *ctx, httpd_free_ctx_fn_t free_ctx, bool ignore_ctx_changes);
|
||||
|
||||
/** Get the HTTP request method
|
||||
*
|
||||
* This API will be called by the HAP Core to get the HTTP request method
|
||||
* in string format (Eg. GET, POST, etc.)
|
||||
*
|
||||
* @param[in] HTTPD Request structure.
|
||||
*
|
||||
* @return Pointer to a string indicating the HTTP method.
|
||||
*/
|
||||
const char *hap_platform_httpd_get_req_method(httpd_req_t *req);
|
||||
|
||||
/** Get the HTTP request URI
|
||||
*
|
||||
* This API will be called by the HAP Core to get the request URI string.
|
||||
*
|
||||
* @param[in] HTTPD Request structure.
|
||||
*
|
||||
* @return Pointer to the URI string.
|
||||
*/
|
||||
const char *hap_platform_httpd_get_req_uri(httpd_req_t *req);
|
||||
|
||||
/** Get the HTTP request content length
|
||||
*
|
||||
* This API will be called by the HAP Core to get the content length of the request.
|
||||
*
|
||||
* @param[in] HTTPD Request structure.
|
||||
*
|
||||
* @return Content length.
|
||||
*/
|
||||
int hap_platform_httpd_get_content_len(httpd_req_t *req);
|
||||
|
||||
/**
|
||||
* Get the HAP HTTPD Handle
|
||||
*
|
||||
* If an application wants to register additional HTTPD endpoints on the same server
|
||||
* instance being used by HomeKit, this API can be used to get the handle. This
|
||||
* should be used only after hap_start().
|
||||
*
|
||||
* @return HomeKit HTTPD Handle
|
||||
*/
|
||||
httpd_handle_t *hap_platform_httpd_get_handle();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _HAP_PLATFORM_HTTPD_H_ */
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <esp_log.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <string.h>
|
||||
|
||||
#define HAP_PLATFORM_DEF_NVS_PARTITION "nvs"
|
||||
#define HAP_PLATFORM_DEF_FACTORY_NVS_PARTITION "factory_nvs"
|
||||
|
||||
static const char *TAG = "hap_platform_keystore";
|
||||
|
||||
char * hap_platform_keystore_get_nvs_partition_name()
|
||||
{
|
||||
return HAP_PLATFORM_DEF_NVS_PARTITION;
|
||||
}
|
||||
|
||||
char * hap_platform_keystore_get_factory_nvs_partition_name()
|
||||
{
|
||||
return HAP_PLATFORM_DEF_FACTORY_NVS_PARTITION;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NVS_ENCRYPTION
|
||||
int hap_platform_keystore_init_partition(const char *part_name, bool read_only)
|
||||
{
|
||||
esp_err_t err;
|
||||
nvs_sec_cfg_t *cfg = NULL;
|
||||
nvs_sec_cfg_t sec_cfg;
|
||||
esp_partition_iterator_t iterator = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL);
|
||||
if (iterator) {
|
||||
const esp_partition_t *partition = esp_partition_get(iterator);
|
||||
err = nvs_flash_read_security_cfg(partition, &sec_cfg);
|
||||
if (err == ESP_OK) {
|
||||
cfg = &sec_cfg;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No NVS keys found");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No NVS keys partition found");
|
||||
}
|
||||
if (!cfg) {
|
||||
ESP_LOGE(TAG, "NVS partition '%s' not encrypted", part_name);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "NVS partition '%s' is encrypted", part_name);
|
||||
}
|
||||
if (read_only) {
|
||||
err = nvs_flash_secure_init_partition(part_name, cfg);
|
||||
} else {
|
||||
err = nvs_flash_secure_init_partition(part_name, cfg);
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase_partition(part_name));
|
||||
err = nvs_flash_secure_init_partition(part_name, cfg);
|
||||
}
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
int hap_platform_keystore_init_partition(const char *part_name, bool read_only)
|
||||
{
|
||||
esp_err_t err;
|
||||
if (read_only) {
|
||||
err = nvs_flash_init_partition(part_name);
|
||||
} else {
|
||||
err = nvs_flash_init_partition(part_name);
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase_partition(part_name));
|
||||
err = nvs_flash_init_partition(part_name);
|
||||
}
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_NVS_ENCRYPTION */
|
||||
|
||||
int hap_platform_keystore_get(const char *part_name, const char *name_space, const char *key, uint8_t *val, size_t *val_size)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(part_name, name_space, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return -1;
|
||||
} else {
|
||||
err = nvs_get_blob(handle, key, val, val_size);
|
||||
nvs_close(handle);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hap_platform_keystore_set(const char *part_name, const char *name_space, const char *key, const uint8_t *val, const size_t val_len)
|
||||
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(part_name, name_space, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error (%d) opening NVS handle!", err);
|
||||
} else {
|
||||
err = nvs_set_blob(handle, key, val, val_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write %s", key);
|
||||
} else {
|
||||
nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hap_platform_keystore_delete(const char *part_name, const char *name_space, const char *key)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(part_name, name_space, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error (%d) opening NVS handle!", err);
|
||||
} else {
|
||||
err = nvs_erase_key(handle, key);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to delete %s", key);
|
||||
} else {
|
||||
nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hap_platform_keystore_delete_namespace(const char *part_name, const char *name_space)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err = nvs_open_from_partition(part_name, name_space, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error (%d) opening NVS handle!", err);
|
||||
} else {
|
||||
err = nvs_erase_all(handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to delete %s", name_space);
|
||||
} else {
|
||||
nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hap_platfrom_keystore_erase_partition(const char *part_name)
|
||||
{
|
||||
esp_err_t err = nvs_flash_erase_partition(part_name);
|
||||
if (err == ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HAP_PLATFORM_KEYSTORE_H_
|
||||
#define _HAP_PLATFORM_KEYSTORE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Get NVS Partition Name
|
||||
*
|
||||
* @return pointer to an allocated string indicating NVS partition Name
|
||||
*/
|
||||
char * hap_platform_keystore_get_nvs_partition_name();
|
||||
|
||||
|
||||
/** Get Factory NVS Partition Name
|
||||
*
|
||||
* @return pointer to an allocated string indicating Factory NVS partition Name
|
||||
*/
|
||||
char * hap_platform_keystore_get_factory_nvs_partition_name();
|
||||
|
||||
/** Initialise Key Store Partition
|
||||
*
|
||||
* @param[in] part_name Name of Partition
|
||||
* @param[in] read_only True for Read-Only, False for Read-Write
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
*/
|
||||
int hap_platform_keystore_init_partition(const char *part_name, bool read_only);
|
||||
|
||||
/** Get Value from Key Store
|
||||
*
|
||||
* @param[in] part_name Name of Partition
|
||||
* @param[in] name_space Name space for the key
|
||||
* @param[in] key Name of the key
|
||||
* @param[out] val Allocated buffer into which the value will be read
|
||||
* @param[in,out] val_size Size of the allocated value buffer. Will hold the size of value read on success
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
*/
|
||||
int hap_platform_keystore_get(const char *part_name, const char *name_space, const char *key, uint8_t *val, size_t *val_size);
|
||||
|
||||
/** Set Value in Key Store
|
||||
*
|
||||
* @param[in] part_name Name of Partition
|
||||
* @param[in] name_space Name space for the key
|
||||
* @param[in] key Name of the key
|
||||
* @param[in] val Pointer to the value buffer
|
||||
* @param[in] val_len Length of the value buffer
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
*/
|
||||
int hap_platform_keystore_set(const char *part_name, const char *name_space, const char *key, const uint8_t *val, const size_t val_len);
|
||||
|
||||
/** Delete Entry from Key Store
|
||||
*
|
||||
* @param[in] part_name Name of Partition
|
||||
* @param[in] name_space Name space for the key
|
||||
* @param[in] key Name of the key
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
*/
|
||||
int hap_platform_keystore_delete(const char *part_name, const char *name_space, const char *key);
|
||||
|
||||
/** Delete Name space from Key Store
|
||||
*
|
||||
* @param[in] part_name Name of Partition
|
||||
* @param[in] name_space Name space for the key
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
*/
|
||||
int hap_platform_keystore_delete_namespace(const char *part_name, const char *name_space);
|
||||
|
||||
/** Erase a Key Store partition
|
||||
*
|
||||
* @param[in] part_name Name of Partition
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
*/
|
||||
int hap_platfrom_keystore_erase_partition(const char *part_name);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _HAP_PLATFORM_KEYSTORE_H_ */
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
void * hap_platform_memory_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void * hap_platform_memory_calloc(size_t count, size_t size)
|
||||
{
|
||||
return calloc(count, size);
|
||||
}
|
||||
|
||||
void hap_platform_memory_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_PLATFORM_MEMORY_H_
|
||||
#define _HAP_PLATFORM_MEMORY_H_
|
||||
#include <stdlib.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** Allocate memory
|
||||
*
|
||||
* This API allocates "size" bytes of memory and returns a pointer to the allocated memory.
|
||||
*
|
||||
* @param[in] size Number of bytes to be allocated
|
||||
*
|
||||
* @return pointer to the allocated memory
|
||||
* @return NULL on failure
|
||||
*/
|
||||
void * hap_platform_memory_malloc(size_t size);
|
||||
|
||||
/** Allocate contiguous memory for items
|
||||
*
|
||||
* This API contiguously allocates enough space for "count" objects that are "size" bytes of memory each
|
||||
* and returns a pointer to the allocated memory. The allocated memory is filled with bytes of value zero.
|
||||
*
|
||||
* @param[in] count Number of items
|
||||
* @param[in] size Size of each item
|
||||
*
|
||||
* @return pointer to the allocated memory
|
||||
* @return NULL on failure
|
||||
*/
|
||||
void * hap_platform_memory_calloc(size_t count, size_t size);
|
||||
|
||||
/** Free allocate memory
|
||||
*
|
||||
* This API frees the memory allocated by hap_platform_memory_malloc() or hap_platform_memory_calloc()
|
||||
*
|
||||
* @param[in] ptr Pointer to the allocated memory
|
||||
*/
|
||||
void hap_platform_memory_free(void *ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _HAP_PLATFORM_MEMORY_H_ */
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/FreeRTOSConfig.h>
|
||||
#include <freertos/portmacro.h>
|
||||
|
||||
uint16_t hap_platform_os_get_msec_per_tick()
|
||||
{
|
||||
return portTICK_PERIOD_MS;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef _HAP_PLATFORM_OS_H_
|
||||
#define _HAP_PLATFORM_OS_H_
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** Return the OS tick period in milliseconds
|
||||
*
|
||||
* @return the milliseconds per tick as configured for the OS
|
||||
*/
|
||||
uint16_t hap_platform_os_get_msec_per_tick();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _HAP_PLATFORM_OS_H_ */
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HAP_WAC_H_
|
||||
#define _HAP_WAC_H_
|
||||
#include <stdint.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_err.h>
|
||||
/** HomeKit Event Base */
|
||||
ESP_EVENT_DECLARE_BASE(HAP_WAC_EVENT);
|
||||
/** HomeKit Events */
|
||||
typedef enum {
|
||||
/* WAC has started. Associated data is NULL */
|
||||
HAP_WAC_EVENT_STARTED,
|
||||
/** WAC has timed out because of inactivity. Associated data is NULL.
|
||||
* Appropriate indications should be given to the user, so that the accessory can be rebooted
|
||||
* for restarting WAC.
|
||||
*/
|
||||
HAP_WAC_EVENT_TIMEOUT,
|
||||
/** WAC was successful and will stop after some time. Associated data is NULL.
|
||||
*/
|
||||
HAP_WAC_EVENT_SUCCESS,
|
||||
/** WAC is requesting to start SoftAP. Associated data is a pointer to a NULL terminated SSID.
|
||||
* Using this SSID isn't required, but recommended. The network security should be "Open" for
|
||||
* WAC to work. If SoftAP has already been started, nothing needs to be done for this event.
|
||||
* The helper function hap_wifi_softap_start() can be used for this.
|
||||
*/
|
||||
HAP_WAC_EVENT_REQ_SOFTAP_START,
|
||||
/** WAC is requesting to stop SoftAP. Associated data is NULL.
|
||||
* The helper function hap_wifi_softap_stop() can be used for this.
|
||||
*/
|
||||
HAP_WAC_EVENT_REQ_SOFTAP_STOP,
|
||||
/** WAC has received the Wi-Fi credentials. Associated data is a pointer to wifi_config_t
|
||||
* structure. This should be used to connect to the Wi-Fi network.
|
||||
* The helper function hap_wifi_sta_connect() can be used for this.
|
||||
*/
|
||||
HAP_WAC_EVENT_RECV_CRED,
|
||||
/** WAC has stopped */
|
||||
HAP_WAC_EVENT_STOPPED
|
||||
} hap_wac_event_t;
|
||||
|
||||
/** Start WAC Provisioning.
|
||||
*
|
||||
* This should be called when the accessory boots up in an unprovisioned mode.
|
||||
* The helper function hap_wifi_is_provisioned() function can be used to find
|
||||
* if the accessory is provisioned or not.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -1 on failure.
|
||||
*/
|
||||
int hap_wac_start(void);
|
||||
|
||||
/** Stop WAC Provisioning.
|
||||
*
|
||||
* This should be used only if WAC needs to be stopped explicitly (say, after
|
||||
* provisioning was done using some other means). Else, WAC stops automatically,
|
||||
* either after the timeout or after successful provisioning.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return -1 on failure.
|
||||
*/
|
||||
int hap_wac_stop(void);
|
||||
|
||||
/** Check is accessory is provisioned
|
||||
*
|
||||
* @param[out] provisioned Pointer to an allocated boolean variable. Will
|
||||
* be set to true if accessory is provisioned, else false.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t hap_wifi_is_provisioned(bool *provisioned);
|
||||
|
||||
/** Start SoftAP
|
||||
*
|
||||
* @param[in] ssid NULL terminated ssid string.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t hap_wifi_softap_start(char *ssid);
|
||||
|
||||
/** Stop Soft AP
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t hap_wifi_softap_stop(void);
|
||||
|
||||
/** Connect to Wi-Fi network with given configuration
|
||||
*
|
||||
* @param[in] config Pointer to \ref wifi_config_t structure.
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error on failure.
|
||||
*/
|
||||
esp_err_t hap_wifi_sta_connect(wifi_config_t *config);
|
||||
|
||||
#endif /* _HAP_WAC_H_ */
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2020 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 <hexbin.h>
|
||||
static int hex2bin_byte(uint8_t *byte)
|
||||
{
|
||||
if (*byte >= '0' && *byte <= '9')
|
||||
*byte -= '0';
|
||||
else if (*byte >= 'a' && *byte <= 'f')
|
||||
*byte -= ('a' - 0x0a);
|
||||
else if (*byte >= 'A' && *byte <= 'F')
|
||||
*byte -= ('A' - 0x0a);
|
||||
else
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hex2bin(const char *ihex, size_t ilen, uint8_t *obin, size_t *olen)
|
||||
{
|
||||
if (ilen > (2 * (*olen))) {
|
||||
return -1;
|
||||
} else if (ilen % 2) {
|
||||
return -1;
|
||||
}
|
||||
int i, j;
|
||||
uint8_t byte;
|
||||
for (i = 0, j = 0; i < ilen / 2; i++) {
|
||||
byte = ihex[j++];
|
||||
if (hex2bin_byte(&byte) < 0) {
|
||||
return -1;
|
||||
}
|
||||
obin[i] = (byte << 4);
|
||||
byte = ihex[j++];
|
||||
if (hex2bin_byte(&byte) < 0) {
|
||||
return -1;
|
||||
}
|
||||
obin[i] |= byte;
|
||||
}
|
||||
*olen = i;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2020 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.
|
||||
#ifndef _HEXBIN_H_
|
||||
#define _HEXBIN_H_
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
int hex2bin(const char *ihex, size_t ilen, uint8_t *obin, size_t *olen);
|
||||
#endif /* _HEXBIN_H_ */
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2020 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>
|
||||
|
||||
//#define HEX_DBG_ENABLE
|
||||
|
||||
#ifdef HEX_DBG_ENABLE
|
||||
void hex_dbg_with_name(char *name, unsigned char *buf, int buf_len)
|
||||
{
|
||||
int i;
|
||||
printf("%s: ", name);
|
||||
for (i = 0; i < buf_len; i++) {
|
||||
if (i % 16 == 0)
|
||||
printf("\r\n");
|
||||
printf("%02x ", buf[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
#else
|
||||
void hex_dbg_with_name(char *name, unsigned char *buf, int buf_len)
|
||||
{
|
||||
}
|
||||
#endif /* HEX_DBG_ENABLED */
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2020 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.
|
||||
#ifndef _HEXDUMP_H_
|
||||
#define _HEXDUMP_H_
|
||||
void hex_dbg_with_name(char *name, unsigned char *buf, int buf_len);
|
||||
|
||||
#endif /* _HEXDUMP_H_ */
|
|
@ -0,0 +1,358 @@
|
|||
/**************************** sha.h ****************************/
|
||||
/***************** See RFC 6234 for details. *******************/
|
||||
/*
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as
|
||||
authors of the code. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and
|
||||
the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor
|
||||
the names of specific contributors, may be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _SHA_H_
|
||||
#define _SHA_H_
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* This file implements the Secure Hash Algorithms
|
||||
* as defined in the U.S. National Institute of Standards
|
||||
* and Technology Federal Information Processing Standards
|
||||
* Publication (FIPS PUB) 180-3 published in October 2008
|
||||
* and formerly defined in its predecessors, FIPS PUB 180-1
|
||||
* and FIP PUB 180-2.
|
||||
*
|
||||
* A combined document showing all algorithms is available at
|
||||
* http://csrc.nist.gov/publications/fips/
|
||||
* fips180-3/fips180-3_final.pdf
|
||||
*
|
||||
* The five hashes are defined in these sizes:
|
||||
* SHA-1 20 byte / 160 bit
|
||||
* SHA-224 28 byte / 224 bit
|
||||
* SHA-256 32 byte / 256 bit
|
||||
* SHA-384 48 byte / 384 bit
|
||||
* SHA-512 64 byte / 512 bit
|
||||
*
|
||||
* Compilation Note:
|
||||
* These files may be compiled with two options:
|
||||
* USE_32BIT_ONLY - use 32-bit arithmetic only, for systems
|
||||
* without 64-bit integers
|
||||
*
|
||||
* USE_MODIFIED_MACROS - use alternate form of the SHA_Ch()
|
||||
* and SHA_Maj() macros that are equivalent
|
||||
* and potentially faster on many systems
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
/*
|
||||
* If you do not have the ISO standard stdint.h header file, then you
|
||||
* must typedef the following:
|
||||
* name meaning
|
||||
* uint64_t unsigned 64-bit integer
|
||||
* uint32_t unsigned 32-bit integer
|
||||
* uint8_t unsigned 8-bit integer (i.e., unsigned char)
|
||||
* int_least16_t integer of >= 16 bits
|
||||
*
|
||||
* See stdint-example.h
|
||||
*/
|
||||
|
||||
#ifndef _SHA_enum_
|
||||
#define _SHA_enum_
|
||||
/*
|
||||
* All SHA functions return one of these values.
|
||||
*/
|
||||
enum {
|
||||
shaSuccess = 0,
|
||||
shaNull, /* Null pointer parameter */
|
||||
shaInputTooLong, /* input data too long */
|
||||
shaStateError, /* called Input after FinalBits or Result */
|
||||
shaBadParam /* passed a bad parameter */
|
||||
};
|
||||
#endif /* _SHA_enum_ */
|
||||
|
||||
/*
|
||||
* These constants hold size information for each of the SHA
|
||||
* hashing operations
|
||||
*/
|
||||
enum {
|
||||
SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64,
|
||||
SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128,
|
||||
SHA512_Message_Block_Size = 128,
|
||||
USHA_Max_Message_Block_Size = SHA512_Message_Block_Size,
|
||||
|
||||
SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32,
|
||||
SHA384HashSize = 48, SHA512HashSize = 64,
|
||||
USHAMaxHashSize = SHA512HashSize,
|
||||
|
||||
SHA1HashSizeBits = 160, SHA224HashSizeBits = 224,
|
||||
SHA256HashSizeBits = 256, SHA384HashSizeBits = 384,
|
||||
SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits
|
||||
};
|
||||
|
||||
/*
|
||||
* These constants are used in the USHA (Unified SHA) functions.
|
||||
*/
|
||||
typedef enum SHAversion {
|
||||
SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
} SHAversion;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-1
|
||||
* hashing operation.
|
||||
*/
|
||||
typedef struct SHA1Context {
|
||||
uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
|
||||
|
||||
uint32_t Length_High; /* Message length in bits */
|
||||
uint32_t Length_Low; /* Message length in bits */
|
||||
|
||||
int_least16_t Message_Block_Index; /* Message_Block array index */
|
||||
/* 512-bit message blocks */
|
||||
uint8_t Message_Block[SHA1_Message_Block_Size];
|
||||
|
||||
int Computed; /* Is the hash computed? */
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
} SHA1Context;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-256
|
||||
* hashing operation.
|
||||
*/
|
||||
typedef struct SHA256Context {
|
||||
uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */
|
||||
|
||||
uint32_t Length_High; /* Message length in bits */
|
||||
uint32_t Length_Low; /* Message length in bits */
|
||||
|
||||
int_least16_t Message_Block_Index; /* Message_Block array index */
|
||||
/* 512-bit message blocks */
|
||||
uint8_t Message_Block[SHA256_Message_Block_Size];
|
||||
|
||||
int Computed; /* Is the hash computed? */
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
} SHA256Context;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-512
|
||||
* hashing operation.
|
||||
*/
|
||||
typedef struct SHA512Context {
|
||||
#ifdef USE_32BIT_ONLY
|
||||
uint32_t Intermediate_Hash[SHA512HashSize/4]; /* Message Digest */
|
||||
uint32_t Length[4]; /* Message length in bits */
|
||||
#else /* !USE_32BIT_ONLY */
|
||||
uint64_t Intermediate_Hash[SHA512HashSize/8]; /* Message Digest */
|
||||
uint64_t Length_High, Length_Low; /* Message length in bits */
|
||||
#endif /* USE_32BIT_ONLY */
|
||||
|
||||
int_least16_t Message_Block_Index; /* Message_Block array index */
|
||||
/* 1024-bit message blocks */
|
||||
uint8_t Message_Block[SHA512_Message_Block_Size];
|
||||
|
||||
int Computed; /* Is the hash computed?*/
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
} SHA512Context;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-224
|
||||
* hashing operation. It uses the SHA-256 structure for computation.
|
||||
*/
|
||||
typedef struct SHA256Context SHA224Context;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-384
|
||||
* hashing operation. It uses the SHA-512 structure for computation.
|
||||
*/
|
||||
typedef struct SHA512Context SHA384Context;
|
||||
|
||||
/*
|
||||
* This structure holds context information for all SHA
|
||||
* hashing operations.
|
||||
*/
|
||||
typedef struct USHAContext {
|
||||
int whichSha; /* which SHA is being used */
|
||||
union {
|
||||
SHA1Context sha1Context;
|
||||
SHA224Context sha224Context; SHA256Context sha256Context;
|
||||
SHA384Context sha384Context; SHA512Context sha512Context;
|
||||
} ctx;
|
||||
|
||||
} USHAContext;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the HMAC
|
||||
* keyed-hashing operation.
|
||||
*/
|
||||
typedef struct HMACContext {
|
||||
int whichSha; /* which SHA is being used */
|
||||
int hashSize; /* hash size of SHA being used */
|
||||
int blockSize; /* block size of SHA being used */
|
||||
USHAContext shaContext; /* SHA context */
|
||||
unsigned char k_opad[USHA_Max_Message_Block_Size];
|
||||
/* outer padding - key XORd with opad */
|
||||
int Computed; /* Is the MAC computed? */
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
|
||||
} HMACContext;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the HKDF
|
||||
* extract-and-expand Key Derivation Functions.
|
||||
*/
|
||||
typedef struct HKDFContext {
|
||||
int whichSha; /* which SHA is being used */
|
||||
HMACContext hmacContext;
|
||||
int hashSize; /* hash size of SHA being used */
|
||||
unsigned char prk[USHAMaxHashSize];
|
||||
/* pseudo-random key - output of hkdfInput */
|
||||
int Computed; /* Is the key material computed? */
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
} HKDFContext;
|
||||
|
||||
/*
|
||||
* Function Prototypes
|
||||
*/
|
||||
|
||||
/* SHA-1 */
|
||||
extern int SHA1Reset(SHA1Context *);
|
||||
extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA1FinalBits(SHA1Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA1Result(SHA1Context *,
|
||||
uint8_t Message_Digest[SHA1HashSize]);
|
||||
|
||||
/* SHA-224 */
|
||||
extern int SHA224Reset(SHA224Context *);
|
||||
extern int SHA224Input(SHA224Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA224FinalBits(SHA224Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA224Result(SHA224Context *,
|
||||
uint8_t Message_Digest[SHA224HashSize]);
|
||||
|
||||
/* SHA-256 */
|
||||
extern int SHA256Reset(SHA256Context *);
|
||||
extern int SHA256Input(SHA256Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA256FinalBits(SHA256Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA256Result(SHA256Context *,
|
||||
uint8_t Message_Digest[SHA256HashSize]);
|
||||
|
||||
/* SHA-384 */
|
||||
extern int SHA384Reset(SHA384Context *);
|
||||
extern int SHA384Input(SHA384Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA384FinalBits(SHA384Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA384Result(SHA384Context *,
|
||||
uint8_t Message_Digest[SHA384HashSize]);
|
||||
|
||||
/* SHA-512 */
|
||||
extern int SHA512Reset(SHA512Context *);
|
||||
extern int SHA512Input(SHA512Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA512FinalBits(SHA512Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA512Result(SHA512Context *,
|
||||
uint8_t Message_Digest[SHA512HashSize]);
|
||||
|
||||
/* Unified SHA functions, chosen by whichSha */
|
||||
extern int USHAReset(USHAContext *context, SHAversion whichSha);
|
||||
extern int USHAInput(USHAContext *context,
|
||||
const uint8_t *bytes, unsigned int bytecount);
|
||||
extern int USHAFinalBits(USHAContext *context,
|
||||
uint8_t bits, unsigned int bit_count);
|
||||
extern int USHAResult(USHAContext *context,
|
||||
uint8_t Message_Digest[USHAMaxHashSize]);
|
||||
extern int USHABlockSize(enum SHAversion whichSha);
|
||||
extern int USHAHashSize(enum SHAversion whichSha);
|
||||
extern int USHAHashSizeBits(enum SHAversion whichSha);
|
||||
extern const char *USHAHashName(enum SHAversion whichSha);
|
||||
|
||||
/*
|
||||
* HMAC Keyed-Hashing for Message Authentication, RFC 2104,
|
||||
* for all SHAs.
|
||||
* This interface allows a fixed-length text input to be used.
|
||||
*/
|
||||
extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */
|
||||
const unsigned char *text, /* pointer to data stream */
|
||||
int text_len, /* length of data stream */
|
||||
const unsigned char *key, /* pointer to authentication key */
|
||||
int key_len, /* length of authentication key */
|
||||
uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */
|
||||
|
||||
/*
|
||||
* HMAC Keyed-Hashing for Message Authentication, RFC 2104,
|
||||
* for all SHAs.
|
||||
* This interface allows any length of text input to be used.
|
||||
*/
|
||||
extern int hmacReset(HMACContext *context, enum SHAversion whichSha,
|
||||
const unsigned char *key, int key_len);
|
||||
extern int hmacInput(HMACContext *context, const unsigned char *text,
|
||||
int text_len);
|
||||
extern int hmacFinalBits(HMACContext *context, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int hmacResult(HMACContext *context,
|
||||
uint8_t digest[USHAMaxHashSize]);
|
||||
|
||||
/*
|
||||
* HKDF HMAC-based Extract-and-Expand Key Derivation Function,
|
||||
* RFC 5869, for all SHAs.
|
||||
*/
|
||||
extern int hkdf(SHAversion whichSha, const unsigned char *salt,
|
||||
int salt_len, const unsigned char *ikm, int ikm_len,
|
||||
const unsigned char *info, int info_len,
|
||||
uint8_t okm[ ], int okm_len);
|
||||
extern int hkdfExtract(SHAversion whichSha, const unsigned char *salt,
|
||||
int salt_len, const unsigned char *ikm,
|
||||
int ikm_len, uint8_t prk[USHAMaxHashSize]);
|
||||
extern int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ],
|
||||
int prk_len, const unsigned char *info,
|
||||
int info_len, uint8_t okm[ ], int okm_len);
|
||||
|
||||
/*
|
||||
* HKDF HMAC-based Extract-and-Expand Key Derivation Function,
|
||||
* RFC 5869, for all SHAs.
|
||||
* This interface allows any length of text input to be used.
|
||||
*/
|
||||
extern int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
|
||||
const unsigned char *salt, int salt_len);
|
||||
extern int hkdfInput(HKDFContext *context, const unsigned char *ikm,
|
||||
int ikm_len);
|
||||
extern int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
|
||||
unsigned int ikm_bit_count);
|
||||
extern int hkdfResult(HKDFContext *context,
|
||||
uint8_t prk[USHAMaxHashSize],
|
||||
const unsigned char *info, int info_len,
|
||||
uint8_t okm[USHAMaxHashSize], int okm_len);
|
||||
#endif /* _SHA_H_ */
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
/**************************** hkdf.c ***************************/
|
||||
/***************** See RFC 6234 for details. *******************/
|
||||
/* Copyright (c) 2011 IETF Trust and the persons identified as */
|
||||
/* authors of the code. All rights reserved. */
|
||||
/* See sha.h for terms of use and redistribution. */
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* This file implements the HKDF algorithm (HMAC-based
|
||||
* Extract-and-Expand Key Derivation Function, RFC 5869),
|
||||
* expressed in terms of the various SHA algorithms.
|
||||
*/
|
||||
|
||||
#include "sha.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* hkdf
|
||||
*
|
||||
* Description:
|
||||
* This function will generate keying material using HKDF.
|
||||
*
|
||||
* Parameters:
|
||||
* whichSha: [in]
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
* salt[ ]: [in]
|
||||
* The optional salt value (a non-secret random value);
|
||||
* if not provided (salt == NULL), it is set internally
|
||||
* to a string of HashLen(whichSha) zeros.
|
||||
* salt_len: [in]
|
||||
* The length of the salt value. (Ignored if salt == NULL.)
|
||||
* ikm[ ]: [in]
|
||||
* Input keying material.
|
||||
* ikm_len: [in]
|
||||
* The length of the input keying material.
|
||||
* info[ ]: [in]
|
||||
* The optional context and application specific information.
|
||||
* If info == NULL or a zero-length string, it is ignored.
|
||||
* info_len: [in]
|
||||
* The length of the optional context and application specific
|
||||
* information. (Ignored if info == NULL.)
|
||||
* okm[ ]: [out]
|
||||
* Where the HKDF is to be stored.
|
||||
* okm_len: [in]
|
||||
* The length of the buffer to hold okm.
|
||||
* okm_len must be <= 255 * USHABlockSize(whichSha)
|
||||
*
|
||||
* Notes:
|
||||
* Calls hkdfExtract() and hkdfExpand().
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hkdf(SHAversion whichSha,
|
||||
const unsigned char *salt, int salt_len,
|
||||
const unsigned char *ikm, int ikm_len,
|
||||
const unsigned char *info, int info_len,
|
||||
uint8_t okm[ ], int okm_len)
|
||||
{
|
||||
uint8_t prk[USHAMaxHashSize];
|
||||
return hkdfExtract(whichSha, salt, salt_len, ikm, ikm_len, prk) ||
|
||||
hkdfExpand(whichSha, prk, USHAHashSize(whichSha), info,
|
||||
info_len, okm, okm_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* hkdfExtract
|
||||
*
|
||||
* Description:
|
||||
* This function will perform HKDF extraction.
|
||||
*
|
||||
* Parameters:
|
||||
* whichSha: [in]
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
* salt[ ]: [in]
|
||||
* The optional salt value (a non-secret random value);
|
||||
* if not provided (salt == NULL), it is set internally
|
||||
* to a string of HashLen(whichSha) zeros.
|
||||
* salt_len: [in]
|
||||
* The length of the salt value. (Ignored if salt == NULL.)
|
||||
* ikm[ ]: [in]
|
||||
* Input keying material.
|
||||
* ikm_len: [in]
|
||||
* The length of the input keying material.
|
||||
* prk[ ]: [out]
|
||||
* Array where the HKDF extraction is to be stored.
|
||||
* Must be larger than USHAHashSize(whichSha);
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hkdfExtract(SHAversion whichSha,
|
||||
const unsigned char *salt, int salt_len,
|
||||
const unsigned char *ikm, int ikm_len,
|
||||
uint8_t prk[USHAMaxHashSize])
|
||||
{
|
||||
unsigned char nullSalt[USHAMaxHashSize];
|
||||
if (salt == 0) {
|
||||
salt = nullSalt;
|
||||
salt_len = USHAHashSize(whichSha);
|
||||
memset(nullSalt, '\0', salt_len);
|
||||
} else if (salt_len < 0) {
|
||||
return shaBadParam;
|
||||
}
|
||||
return hmac(whichSha, ikm, ikm_len, salt, salt_len, prk);
|
||||
}
|
||||
|
||||
/*
|
||||
* hkdfExpand
|
||||
*
|
||||
* Description:
|
||||
* This function will perform HKDF expansion.
|
||||
*
|
||||
* Parameters:
|
||||
* whichSha: [in]
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
* prk[ ]: [in]
|
||||
* The pseudo-random key to be expanded; either obtained
|
||||
* directly from a cryptographically strong, uniformly
|
||||
* distributed pseudo-random number generator, or as the
|
||||
* output from hkdfExtract().
|
||||
* prk_len: [in]
|
||||
* The length of the pseudo-random key in prk;
|
||||
* should at least be equal to USHAHashSize(whichSHA).
|
||||
* info[ ]: [in]
|
||||
* The optional context and application specific information.
|
||||
* If info == NULL or a zero-length string, it is ignored.
|
||||
* info_len: [in]
|
||||
* The length of the optional context and application specific
|
||||
* information. (Ignored if info == NULL.)
|
||||
* okm[ ]: [out]
|
||||
* Where the HKDF is to be stored.
|
||||
* okm_len: [in]
|
||||
* The length of the buffer to hold okm.
|
||||
* okm_len must be <= 255 * USHABlockSize(whichSha)
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ], int prk_len,
|
||||
const unsigned char *info, int info_len,
|
||||
uint8_t okm[ ], int okm_len)
|
||||
{
|
||||
int hash_len, N;
|
||||
unsigned char T[USHAMaxHashSize];
|
||||
int Tlen, where, i;
|
||||
|
||||
if (info == 0) {
|
||||
info = (const unsigned char *)"";
|
||||
info_len = 0;
|
||||
} else if (info_len < 0) {
|
||||
return shaBadParam;
|
||||
}
|
||||
if (okm_len <= 0) return shaBadParam;
|
||||
if (!okm) return shaBadParam;
|
||||
|
||||
hash_len = USHAHashSize(whichSha);
|
||||
if (prk_len < hash_len) return shaBadParam;
|
||||
N = okm_len / hash_len;
|
||||
if ((okm_len % hash_len) != 0) N++;
|
||||
if (N > 255) return shaBadParam;
|
||||
|
||||
Tlen = 0;
|
||||
where = 0;
|
||||
for (i = 1; i <= N; i++) {
|
||||
HMACContext context;
|
||||
unsigned char c = i;
|
||||
int ret = hmacReset(&context, whichSha, prk, prk_len) ||
|
||||
hmacInput(&context, T, Tlen) ||
|
||||
hmacInput(&context, info, info_len) ||
|
||||
hmacInput(&context, &c, 1) ||
|
||||
hmacResult(&context, T);
|
||||
if (ret != shaSuccess) return ret;
|
||||
memcpy(okm + where, T,
|
||||
(i != N) ? hash_len : (okm_len - where));
|
||||
where += hash_len;
|
||||
Tlen = hash_len;
|
||||
}
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* hkdfReset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the hkdfContext in preparation
|
||||
* for key derivation using the modular HKDF interface for
|
||||
* arbitrary length inputs.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
* whichSha: [in]
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
* salt[ ]: [in]
|
||||
* The optional salt value (a non-secret random value);
|
||||
* if not provided (salt == NULL), it is set internally
|
||||
* to a string of HashLen(whichSha) zeros.
|
||||
* salt_len: [in]
|
||||
* The length of the salt value. (Ignored if salt == NULL.)
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
|
||||
const unsigned char *salt, int salt_len)
|
||||
{
|
||||
unsigned char nullSalt[USHAMaxHashSize];
|
||||
if (!context) return shaNull;
|
||||
|
||||
context->whichSha = whichSha;
|
||||
context->hashSize = USHAHashSize(whichSha);
|
||||
if (salt == 0) {
|
||||
salt = nullSalt;
|
||||
salt_len = context->hashSize;
|
||||
memset(nullSalt, '\0', salt_len);
|
||||
}
|
||||
|
||||
return hmacReset(&context->hmacContext, whichSha, salt, salt_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* hkdfInput
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion
|
||||
* of the input keying material. It may be called multiple times.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The HKDF context to update.
|
||||
* ikm[ ]: [in]
|
||||
* An array of octets representing the next portion of
|
||||
* the input keying material.
|
||||
* ikm_len: [in]
|
||||
* The length of ikm.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hkdfInput(HKDFContext *context, const unsigned char *ikm,
|
||||
int ikm_len)
|
||||
{
|
||||
if (!context) return shaNull;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
return hmacInput(&context->hmacContext, ikm, ikm_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* hkdfFinalBits
|
||||
*
|
||||
* Description:
|
||||
* This function will add in any final bits of the
|
||||
* input keying material.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The HKDF context to update
|
||||
* ikm_bits: [in]
|
||||
* The final bits of the input keying material, in the upper
|
||||
* portion of the byte. (Use 0b###00000 instead of 0b00000###
|
||||
* to input the three bits ###.)
|
||||
* ikm_bit_count: [in]
|
||||
* The number of bits in message_bits, between 1 and 7.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
|
||||
unsigned int ikm_bit_count)
|
||||
{
|
||||
if (!context) return shaNull;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
return hmacFinalBits(&context->hmacContext, ikm_bits, ikm_bit_count);
|
||||
}
|
||||
|
||||
/*
|
||||
* hkdfResult
|
||||
*
|
||||
* Description:
|
||||
* This function will finish the HKDF extraction and perform the
|
||||
* final HKDF expansion.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The HKDF context to use to calculate the HKDF hash.
|
||||
* prk[ ]: [out]
|
||||
* An optional location to store the HKDF extraction.
|
||||
* Either NULL, or pointer to a buffer that must be
|
||||
* larger than USHAHashSize(whichSha);
|
||||
* info[ ]: [in]
|
||||
* The optional context and application specific information.
|
||||
* If info == NULL or a zero-length string, it is ignored.
|
||||
* info_len: [in]
|
||||
* The length of the optional context and application specific
|
||||
* information. (Ignored if info == NULL.)
|
||||
* okm[ ]: [out]
|
||||
* Where the HKDF is to be stored.
|
||||
* okm_len: [in]
|
||||
* The length of the buffer to hold okm.
|
||||
* okm_len must be <= 255 * USHABlockSize(whichSha)
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hkdfResult(HKDFContext *context,
|
||||
uint8_t prk[USHAMaxHashSize],
|
||||
const unsigned char *info, int info_len,
|
||||
uint8_t okm[ ], int okm_len)
|
||||
{
|
||||
uint8_t prkbuf[USHAMaxHashSize];
|
||||
int ret;
|
||||
|
||||
if (!context) return shaNull;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
if (!okm) return context->Corrupted = shaBadParam;
|
||||
if (!prk) prk = prkbuf;
|
||||
|
||||
ret = hmacResult(&context->hmacContext, prk) ||
|
||||
hkdfExpand(context->whichSha, prk, context->hashSize, info,
|
||||
info_len, okm, okm_len);
|
||||
context->Computed = 1;
|
||||
return context->Corrupted = ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
/**************************** hmac.c ***************************/
|
||||
/***************** See RFC 6234 for details. *******************/
|
||||
/* Copyright (c) 2011 IETF Trust and the persons identified as */
|
||||
/* authors of the code. All rights reserved. */
|
||||
/* See sha.h for terms of use and redistribution. */
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* This file implements the HMAC algorithm (Keyed-Hashing for
|
||||
* Message Authentication, [RFC 2104]), expressed in terms of
|
||||
* the various SHA algorithms.
|
||||
*/
|
||||
|
||||
#include "sha.h"
|
||||
|
||||
/*
|
||||
* hmac
|
||||
*
|
||||
* Description:
|
||||
* This function will compute an HMAC message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* whichSha: [in]
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
* message_array[ ]: [in]
|
||||
* An array of octets representing the message.
|
||||
* Note: in RFC 2104, this parameter is known
|
||||
* as 'text'.
|
||||
* length: [in]
|
||||
* The length of the message in message_array.
|
||||
* key[ ]: [in]
|
||||
* The secret shared key.
|
||||
* key_len: [in]
|
||||
* The length of the secret shared key.
|
||||
* digest[ ]: [out]
|
||||
* Where the digest is to be returned.
|
||||
* NOTE: The length of the digest is determined by
|
||||
* the value of whichSha.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
|
||||
int hmac(SHAversion whichSha,
|
||||
const unsigned char *message_array, int length,
|
||||
const unsigned char *key, int key_len,
|
||||
uint8_t digest[USHAMaxHashSize])
|
||||
{
|
||||
HMACContext context;
|
||||
return hmacReset(&context, whichSha, key, key_len) ||
|
||||
hmacInput(&context, message_array, length) ||
|
||||
hmacResult(&context, digest);
|
||||
}
|
||||
|
||||
/*
|
||||
* hmacReset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the hmacContext in preparation
|
||||
* for computing a new HMAC message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
* whichSha: [in]
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
* key[ ]: [in]
|
||||
* The secret shared key.
|
||||
* key_len: [in]
|
||||
* The length of the secret shared key.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hmacReset(HMACContext *context, enum SHAversion whichSha,
|
||||
const unsigned char *key, int key_len)
|
||||
{
|
||||
int i, blocksize, hashsize, ret;
|
||||
|
||||
/* inner padding - key XORd with ipad */
|
||||
unsigned char k_ipad[USHA_Max_Message_Block_Size];
|
||||
|
||||
/* temporary buffer when keylen > blocksize */
|
||||
unsigned char tempkey[USHAMaxHashSize];
|
||||
|
||||
if (!context) return shaNull;
|
||||
context->Computed = 0;
|
||||
context->Corrupted = shaSuccess;
|
||||
|
||||
blocksize = context->blockSize = USHABlockSize(whichSha);
|
||||
hashsize = context->hashSize = USHAHashSize(whichSha);
|
||||
context->whichSha = whichSha;
|
||||
|
||||
/*
|
||||
* If key is longer than the hash blocksize,
|
||||
* reset it to key = HASH(key).
|
||||
*/
|
||||
if (key_len > blocksize) {
|
||||
USHAContext tcontext;
|
||||
int err = USHAReset(&tcontext, whichSha) ||
|
||||
USHAInput(&tcontext, key, key_len) ||
|
||||
USHAResult(&tcontext, tempkey);
|
||||
if (err != shaSuccess) return err;
|
||||
|
||||
key = tempkey;
|
||||
key_len = hashsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* The HMAC transform looks like:
|
||||
*
|
||||
* SHA(K XOR opad, SHA(K XOR ipad, text))
|
||||
*
|
||||
* where K is an n byte key, 0-padded to a total of blocksize bytes,
|
||||
* ipad is the byte 0x36 repeated blocksize times,
|
||||
* opad is the byte 0x5c repeated blocksize times,
|
||||
* and text is the data being protected.
|
||||
*/
|
||||
|
||||
/* store key into the pads, XOR'd with ipad and opad values */
|
||||
for (i = 0; i < key_len; i++) {
|
||||
k_ipad[i] = key[i] ^ 0x36;
|
||||
context->k_opad[i] = key[i] ^ 0x5c;
|
||||
}
|
||||
/* remaining pad bytes are '\0' XOR'd with ipad and opad values */
|
||||
for ( ; i < blocksize; i++) {
|
||||
k_ipad[i] = 0x36;
|
||||
context->k_opad[i] = 0x5c;
|
||||
}
|
||||
|
||||
/* perform inner hash */
|
||||
/* init context for 1st pass */
|
||||
ret = USHAReset(&context->shaContext, whichSha) ||
|
||||
/* and start with inner pad */
|
||||
USHAInput(&context->shaContext, k_ipad, blocksize);
|
||||
return context->Corrupted = ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* hmacInput
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion
|
||||
* of the message. It may be called multiple times.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The HMAC context to update.
|
||||
* text[ ]: [in]
|
||||
* An array of octets representing the next portion of
|
||||
* the message.
|
||||
* text_len: [in]
|
||||
* The length of the message in text.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hmacInput(HMACContext *context, const unsigned char *text,
|
||||
int text_len)
|
||||
{
|
||||
if (!context) return shaNull;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
/* then text of datagram */
|
||||
return context->Corrupted =
|
||||
USHAInput(&context->shaContext, text, text_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* hmacFinalBits
|
||||
*
|
||||
* Description:
|
||||
* This function will add in any final bits of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The HMAC context to update.
|
||||
* message_bits: [in]
|
||||
* The final bits of the message, in the upper portion of the
|
||||
* byte. (Use 0b###00000 instead of 0b00000### to input the
|
||||
* three bits ###.)
|
||||
* length: [in]
|
||||
* The number of bits in message_bits, between 1 and 7.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int hmacFinalBits(HMACContext *context,
|
||||
uint8_t bits, unsigned int bit_count)
|
||||
{
|
||||
if (!context) return shaNull;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
/* then final bits of datagram */
|
||||
return context->Corrupted =
|
||||
USHAFinalBits(&context->shaContext, bits, bit_count);
|
||||
}
|
||||
|
||||
/*
|
||||
* hmacResult
|
||||
*
|
||||
* Description:
|
||||
* This function will return the N-byte message digest into the
|
||||
* Message_Digest array provided by the caller.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to use to calculate the HMAC hash.
|
||||
* digest[ ]: [out]
|
||||
* Where the digest is returned.
|
||||
* NOTE 2: The length of the hash is determined by the value of
|
||||
* whichSha that was passed to hmacReset().
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int hmacResult(HMACContext *context, uint8_t *digest)
|
||||
{
|
||||
int ret;
|
||||
if (!context) return shaNull;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
|
||||
/* finish up 1st pass */
|
||||
/* (Use digest here as a temporary buffer.) */
|
||||
ret =
|
||||
USHAResult(&context->shaContext, digest) ||
|
||||
|
||||
/* perform outer SHA */
|
||||
/* init context for 2nd pass */
|
||||
USHAReset(&context->shaContext, context->whichSha) ||
|
||||
|
||||
/* start with outer pad */
|
||||
USHAInput(&context->shaContext, context->k_opad,
|
||||
context->blockSize) ||
|
||||
|
||||
/* then results of 1st hash */
|
||||
USHAInput(&context->shaContext, digest, context->hashSize) ||
|
||||
/* finish up 2nd pass */
|
||||
USHAResult(&context->shaContext, digest);
|
||||
|
||||
context->Computed = 1;
|
||||
return context->Corrupted = ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Serge Zaitsev
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef JSMN_H
|
||||
#define JSMN_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef JSMN_STATIC
|
||||
#define JSMN_API static
|
||||
#else
|
||||
#define JSMN_API extern
|
||||
#endif
|
||||
|
||||
/**
|
||||
* JSON type identifier. Basic types are:
|
||||
* o Object
|
||||
* o Array
|
||||
* o String
|
||||
* o Other primitive: number, boolean (true/false) or null
|
||||
*/
|
||||
typedef enum {
|
||||
JSMN_UNDEFINED = 0,
|
||||
JSMN_OBJECT = 1,
|
||||
JSMN_ARRAY = 2,
|
||||
JSMN_STRING = 3,
|
||||
JSMN_PRIMITIVE = 4
|
||||
} jsmntype_t;
|
||||
|
||||
enum jsmnerr {
|
||||
/* Not enough tokens were provided */
|
||||
JSMN_ERROR_NOMEM = -1,
|
||||
/* Invalid character inside JSON string */
|
||||
JSMN_ERROR_INVAL = -2,
|
||||
/* The string is not a full JSON packet, more bytes expected */
|
||||
JSMN_ERROR_PART = -3
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON token description.
|
||||
* type type (object, array, string etc.)
|
||||
* start start position in JSON data string
|
||||
* end end position in JSON data string
|
||||
*/
|
||||
typedef struct jsmntok {
|
||||
jsmntype_t type;
|
||||
int start;
|
||||
int end;
|
||||
int size;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
int parent;
|
||||
#endif
|
||||
} jsmntok_t;
|
||||
|
||||
/**
|
||||
* JSON parser. Contains an array of token blocks available. Also stores
|
||||
* the string being parsed now and current position in that string.
|
||||
*/
|
||||
typedef struct jsmn_parser {
|
||||
unsigned int pos; /* offset in the JSON string */
|
||||
unsigned int toknext; /* next token to allocate */
|
||||
int toksuper; /* superior token node, e.g. parent object or array */
|
||||
} jsmn_parser;
|
||||
|
||||
/**
|
||||
* Create JSON parser over an array of tokens
|
||||
*/
|
||||
JSMN_API void jsmn_init(jsmn_parser *parser);
|
||||
|
||||
/**
|
||||
* Run JSON parser. It parses a JSON data string into and array of tokens, each
|
||||
* describing
|
||||
* a single JSON object.
|
||||
*/
|
||||
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
|
||||
jsmntok_t *tokens, const unsigned int num_tokens);
|
||||
|
||||
#ifndef JSMN_HEADER
|
||||
/**
|
||||
* Allocates a fresh unused token from the token pool.
|
||||
*/
|
||||
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
|
||||
const size_t num_tokens) {
|
||||
jsmntok_t *tok;
|
||||
if (parser->toknext >= num_tokens) {
|
||||
return NULL;
|
||||
}
|
||||
tok = &tokens[parser->toknext++];
|
||||
tok->start = tok->end = -1;
|
||||
tok->size = 0;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
tok->parent = -1;
|
||||
#endif
|
||||
return tok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills token type and boundaries.
|
||||
*/
|
||||
static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
|
||||
const int start, const int end) {
|
||||
token->type = type;
|
||||
token->start = start;
|
||||
token->end = end;
|
||||
token->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next available token with JSON primitive.
|
||||
*/
|
||||
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
|
||||
const size_t len, jsmntok_t *tokens,
|
||||
const size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
int start;
|
||||
|
||||
start = parser->pos;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
switch (js[parser->pos]) {
|
||||
#ifndef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by "," or "}" or "]" */
|
||||
case ':':
|
||||
#endif
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case ' ':
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
goto found;
|
||||
default:
|
||||
/* to quiet a warning from gcc*/
|
||||
break;
|
||||
}
|
||||
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by a comma/object/array */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
#endif
|
||||
|
||||
found:
|
||||
if (tokens == NULL) {
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next token with JSON string.
|
||||
*/
|
||||
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
|
||||
const size_t len, jsmntok_t *tokens,
|
||||
const size_t num_tokens) {
|
||||
jsmntok_t *token;
|
||||
|
||||
int start = parser->pos;
|
||||
|
||||
parser->pos++;
|
||||
|
||||
/* Skip starting quote */
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c = js[parser->pos];
|
||||
|
||||
/* Quote: end of string */
|
||||
if (c == '\"') {
|
||||
if (tokens == NULL) {
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backslash: Quoted symbol expected */
|
||||
if (c == '\\' && parser->pos + 1 < len) {
|
||||
int i;
|
||||
parser->pos++;
|
||||
switch (js[parser->pos]) {
|
||||
/* Allowed escaped symbols */
|
||||
case '\"':
|
||||
case '/':
|
||||
case '\\':
|
||||
case 'b':
|
||||
case 'f':
|
||||
case 'r':
|
||||
case 'n':
|
||||
case 't':
|
||||
break;
|
||||
/* Allows escaped symbol \uXXXX */
|
||||
case 'u':
|
||||
parser->pos++;
|
||||
for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
|
||||
i++) {
|
||||
/* If it isn't a hex character we have an error */
|
||||
if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
|
||||
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
|
||||
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->pos++;
|
||||
}
|
||||
parser->pos--;
|
||||
break;
|
||||
/* Unexpected symbol */
|
||||
default:
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON string and fill tokens.
|
||||
*/
|
||||
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
|
||||
jsmntok_t *tokens, const unsigned int num_tokens) {
|
||||
int r;
|
||||
int i;
|
||||
jsmntok_t *token;
|
||||
int count = parser->toknext;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
||||
char c;
|
||||
jsmntype_t type;
|
||||
|
||||
c = js[parser->pos];
|
||||
switch (c) {
|
||||
case '{':
|
||||
case '[':
|
||||
count++;
|
||||
if (tokens == NULL) {
|
||||
break;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL) {
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
if (parser->toksuper != -1) {
|
||||
jsmntok_t *t = &tokens[parser->toksuper];
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode an object or array can't become a key */
|
||||
if (t->type == JSMN_OBJECT) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
#endif
|
||||
t->size++;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
}
|
||||
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
token->start = parser->pos;
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
if (tokens == NULL) {
|
||||
break;
|
||||
}
|
||||
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
if (parser->toknext < 1) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token = &tokens[parser->toknext - 1];
|
||||
for (;;) {
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token->end = parser->pos + 1;
|
||||
parser->toksuper = token->parent;
|
||||
break;
|
||||
}
|
||||
if (token->parent == -1) {
|
||||
if (token->type != type || parser->toksuper == -1) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
token = &tokens[token->parent];
|
||||
}
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
if (token->type != type) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->toksuper = -1;
|
||||
token->end = parser->pos + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Error if unmatched closing bracket */
|
||||
if (i == -1) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
for (; i >= 0; i--) {
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1) {
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case '\"':
|
||||
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL) {
|
||||
tokens[parser->toksuper].size++;
|
||||
}
|
||||
break;
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case ' ':
|
||||
break;
|
||||
case ':':
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case ',':
|
||||
if (tokens != NULL && parser->toksuper != -1 &&
|
||||
tokens[parser->toksuper].type != JSMN_ARRAY &&
|
||||
tokens[parser->toksuper].type != JSMN_OBJECT) {
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
parser->toksuper = tokens[parser->toksuper].parent;
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitives are: numbers and booleans */
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 't':
|
||||
case 'f':
|
||||
case 'n':
|
||||
/* And they must not be keys of the object */
|
||||
if (tokens != NULL && parser->toksuper != -1) {
|
||||
const jsmntok_t *t = &tokens[parser->toksuper];
|
||||
if (t->type == JSMN_OBJECT ||
|
||||
(t->type == JSMN_STRING && t->size != 0)) {
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* In non-strict mode every unquoted value is a primitive */
|
||||
default:
|
||||
#endif
|
||||
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL) {
|
||||
tokens[parser->toksuper].size++;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef JSMN_STRICT
|
||||
/* Unexpected char in strict mode */
|
||||
default:
|
||||
return JSMN_ERROR_INVAL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens != NULL) {
|
||||
for (i = parser->toknext - 1; i >= 0; i--) {
|
||||
/* Unmatched opened object or array */
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new parser based over a given buffer with an array of tokens
|
||||
* available.
|
||||
*/
|
||||
JSMN_API void jsmn_init(jsmn_parser *parser) {
|
||||
parser->pos = 0;
|
||||
parser->toknext = 0;
|
||||
parser->toksuper = -1;
|
||||
}
|
||||
|
||||
#endif /* JSMN_HEADER */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* JSMN_H */
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* Copyright 2020 Piyush Shah <shahpiyushv@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <json_generator.h>
|
||||
|
||||
#define MAX_INT_IN_STR 24
|
||||
#define MAX_FLOAT_IN_STR 30
|
||||
|
||||
static inline int json_gen_get_empty_len(json_gen_str_t *jstr)
|
||||
{
|
||||
return (jstr->buf_size - (jstr->free_ptr - jstr->buf) - 1);
|
||||
}
|
||||
|
||||
/* This will add the incoming string to the JSON string buffer
|
||||
* and flush it out if the buffer is full. Note that the data being
|
||||
* flushed out will always be equal to the size of the buffer unless
|
||||
* this is the last chunk being flushed out on json_gen_end_str()
|
||||
*/
|
||||
static int json_gen_add_to_str(json_gen_str_t *jstr, char *str)
|
||||
{
|
||||
if (!str) {
|
||||
return 0;
|
||||
}
|
||||
int len = strlen(str);
|
||||
char *cur_ptr = str;
|
||||
while (1) {
|
||||
int len_remaining = json_gen_get_empty_len(jstr);
|
||||
int copy_len = len_remaining > len ? len : len_remaining;
|
||||
memmove(jstr->free_ptr, cur_ptr, copy_len);
|
||||
cur_ptr += copy_len;
|
||||
jstr->free_ptr += copy_len;
|
||||
len -= copy_len;
|
||||
if (len) {
|
||||
*jstr->free_ptr = '\0';
|
||||
/* Report error if the buffer is full and no flush callback
|
||||
* is registered
|
||||
*/
|
||||
if (!jstr->flush_cb) {
|
||||
return -1;
|
||||
}
|
||||
jstr->flush_cb(jstr->buf, jstr->priv);
|
||||
jstr->free_ptr = jstr->buf;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void json_gen_str_start(json_gen_str_t *jstr, char *buf, int buf_size,
|
||||
json_gen_flush_cb_t flush_cb, void *priv)
|
||||
{
|
||||
memset(jstr, 0, sizeof(json_gen_str_t));
|
||||
jstr->buf = buf;
|
||||
jstr->buf_size = buf_size;
|
||||
jstr->flush_cb = flush_cb;
|
||||
jstr->free_ptr = buf;
|
||||
jstr->priv = priv;
|
||||
}
|
||||
|
||||
void json_gen_str_end(json_gen_str_t *jstr)
|
||||
{
|
||||
*jstr->free_ptr = '\0';
|
||||
if (jstr->flush_cb)
|
||||
jstr->flush_cb(jstr->buf, jstr->priv);
|
||||
memset(jstr, 0, sizeof(json_gen_str_t));
|
||||
}
|
||||
|
||||
static inline void json_gen_handle_comma(json_gen_str_t *jstr)
|
||||
{
|
||||
if (jstr->comma_req)
|
||||
json_gen_add_to_str(jstr, ",");
|
||||
}
|
||||
|
||||
|
||||
static int json_gen_handle_name(json_gen_str_t *jstr, char *name)
|
||||
{
|
||||
json_gen_add_to_str(jstr, "\"");
|
||||
json_gen_add_to_str(jstr, name);
|
||||
return json_gen_add_to_str(jstr, "\":");
|
||||
}
|
||||
|
||||
|
||||
int json_gen_start_object(json_gen_str_t *jstr)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
jstr->comma_req = false;
|
||||
return json_gen_add_to_str(jstr, "{");
|
||||
}
|
||||
|
||||
int json_gen_end_object(json_gen_str_t *jstr)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
return json_gen_add_to_str(jstr, "}");
|
||||
}
|
||||
|
||||
|
||||
int json_gen_start_array(json_gen_str_t *jstr)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
jstr->comma_req = false;
|
||||
return json_gen_add_to_str(jstr, "[");
|
||||
}
|
||||
|
||||
int json_gen_end_array(json_gen_str_t *jstr)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
return json_gen_add_to_str(jstr, "]");
|
||||
}
|
||||
|
||||
int json_gen_push_object(json_gen_str_t *jstr, char *name)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
jstr->comma_req = false;
|
||||
return json_gen_add_to_str(jstr, "{");
|
||||
}
|
||||
|
||||
int json_gen_pop_object(json_gen_str_t *jstr)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
return json_gen_add_to_str(jstr, "}");
|
||||
}
|
||||
|
||||
int json_gen_push_object_str(json_gen_str_t *jstr, char *name, char *object_str)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
jstr->comma_req = true;
|
||||
return json_gen_add_to_str(jstr, object_str);
|
||||
}
|
||||
|
||||
int json_gen_push_array(json_gen_str_t *jstr, char *name)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
jstr->comma_req = false;
|
||||
return json_gen_add_to_str(jstr, "[");
|
||||
}
|
||||
int json_gen_pop_array(json_gen_str_t *jstr)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
return json_gen_add_to_str(jstr, "]");
|
||||
}
|
||||
|
||||
int json_gen_push_array_str(json_gen_str_t *jstr, char *name, char *array_str)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
jstr->comma_req = true;
|
||||
return json_gen_add_to_str(jstr, array_str);
|
||||
}
|
||||
|
||||
static int json_gen_set_bool(json_gen_str_t *jstr, bool val)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
if (val)
|
||||
return json_gen_add_to_str(jstr, "true");
|
||||
else
|
||||
return json_gen_add_to_str(jstr, "false");
|
||||
}
|
||||
int json_gen_obj_set_bool(json_gen_str_t *jstr, char *name, bool val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
return json_gen_set_bool(jstr, val);
|
||||
}
|
||||
|
||||
int json_gen_arr_set_bool(json_gen_str_t *jstr, bool val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
return json_gen_set_bool(jstr, val);
|
||||
}
|
||||
|
||||
static int json_gen_set_int(json_gen_str_t *jstr, int val)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
char str[MAX_INT_IN_STR];
|
||||
snprintf(str, MAX_INT_IN_STR, "%d", val);
|
||||
return json_gen_add_to_str(jstr, str);
|
||||
}
|
||||
|
||||
int json_gen_obj_set_int(json_gen_str_t *jstr, char *name, int val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
return json_gen_set_int(jstr, val);
|
||||
}
|
||||
|
||||
int json_gen_arr_set_int(json_gen_str_t *jstr, int val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
return json_gen_set_int(jstr, val);
|
||||
}
|
||||
|
||||
|
||||
static int json_gen_set_float(json_gen_str_t *jstr, float val)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
char str[MAX_FLOAT_IN_STR];
|
||||
snprintf(str, MAX_FLOAT_IN_STR, "%.*f", JSON_FLOAT_PRECISION, val);
|
||||
return json_gen_add_to_str(jstr, str);
|
||||
}
|
||||
int json_gen_obj_set_float(json_gen_str_t *jstr, char *name, float val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
return json_gen_set_float(jstr, val);
|
||||
}
|
||||
int json_gen_arr_set_float(json_gen_str_t *jstr, float val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
return json_gen_set_float(jstr, val);
|
||||
}
|
||||
|
||||
static int json_gen_set_string(json_gen_str_t *jstr, char *val)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
json_gen_add_to_str(jstr, "\"");
|
||||
json_gen_add_to_str(jstr, val);
|
||||
return json_gen_add_to_str(jstr, "\"");
|
||||
}
|
||||
|
||||
int json_gen_obj_set_string(json_gen_str_t *jstr, char *name, char *val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
return json_gen_set_string(jstr, val);
|
||||
}
|
||||
|
||||
int json_gen_arr_set_string(json_gen_str_t *jstr, char *val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
return json_gen_set_string(jstr, val);
|
||||
}
|
||||
|
||||
static int json_gen_set_long_string(json_gen_str_t *jstr, char *val)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
json_gen_add_to_str(jstr, "\"");
|
||||
return json_gen_add_to_str(jstr, val);
|
||||
}
|
||||
|
||||
int json_gen_obj_start_long_string(json_gen_str_t *jstr, char *name, char *val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
return json_gen_set_long_string(jstr, val);
|
||||
}
|
||||
|
||||
int json_gen_arr_start_long_string(json_gen_str_t *jstr, char *val)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
return json_gen_set_long_string(jstr, val);
|
||||
}
|
||||
|
||||
int json_gen_add_to_long_string(json_gen_str_t *jstr, char *val)
|
||||
{
|
||||
return json_gen_add_to_str(jstr, val);
|
||||
}
|
||||
|
||||
int json_gen_end_long_string(json_gen_str_t *jstr)
|
||||
{
|
||||
return json_gen_add_to_str(jstr, "\"");
|
||||
}
|
||||
static int json_gen_set_null(json_gen_str_t *jstr)
|
||||
{
|
||||
jstr->comma_req = true;
|
||||
return json_gen_add_to_str(jstr, "null");
|
||||
}
|
||||
int json_gen_obj_set_null(json_gen_str_t *jstr, char *name)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
json_gen_handle_name(jstr, name);
|
||||
return json_gen_set_null(jstr);
|
||||
}
|
||||
|
||||
int json_gen_arr_set_null(json_gen_str_t *jstr)
|
||||
{
|
||||
json_gen_handle_comma(jstr);
|
||||
return json_gen_set_null(jstr);
|
||||
}
|
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* Copyright 2020 Piyush Shah <shahpiyushv@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* JSON String Generator
|
||||
*
|
||||
* This module can be used to create JSON strings with a facility
|
||||
* to flush out data if the destination buffer is full. All commas
|
||||
* and colons as required are automatically added by the APIs
|
||||
*
|
||||
*/
|
||||
#ifndef _JSON_GENERATOR_H
|
||||
#define _JSON_GENERATOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** Float precision i.e. number of digits after decimal point */
|
||||
#ifndef JSON_FLOAT_PRECISION
|
||||
#define JSON_FLOAT_PRECISION 5
|
||||
#endif
|
||||
|
||||
/** JSON string flush callback prototype
|
||||
*
|
||||
* This is a prototype of the function that needs to be passed to
|
||||
* json_gen_str_start() and which will be invoked by the JSON generator
|
||||
* module either when the buffer is full or json_gen_str_end() ins invoked.
|
||||
*
|
||||
* \param[in] buf Pointer to a NULL terminated JSON string
|
||||
* \param[in] priv Private data to be passed to the flush callback. Will
|
||||
* be the same as the one passed to json_gen_str_start()
|
||||
*/
|
||||
typedef void (*json_gen_flush_cb_t) (char *buf, void *priv);
|
||||
|
||||
/** JSON String structure
|
||||
*
|
||||
* Please do not set/modify any elements.
|
||||
* Just define this structure and pass a pointer to it in the APIs below
|
||||
*/
|
||||
typedef struct {
|
||||
/** Pointer to the JSON buffer provided by the calling function */
|
||||
char *buf;
|
||||
/** Size of the above buffer */
|
||||
int buf_size;
|
||||
/** (Optional) callback function to invoke when the buffer gets full */
|
||||
json_gen_flush_cb_t flush_cb;
|
||||
/** (Optional) Private data to pass to the callback function */
|
||||
void *priv;
|
||||
/** (For Internal use only) */
|
||||
bool comma_req;
|
||||
/** (For Internal use only) */
|
||||
char *free_ptr;
|
||||
} json_gen_str_t;
|
||||
|
||||
/** Start a JSON String
|
||||
*
|
||||
* This is the first function to be called for creating a JSON string.
|
||||
* It initializes the internal data structures. After the JSON string
|
||||
* generation is over, the json_gen_str_end() function should be called.
|
||||
*
|
||||
* \param[out] jstr Pointer to an allocated \ref json_gen_str_t structure.
|
||||
* This will be initialised internally and needs to be passed to all
|
||||
* subsequent function calls
|
||||
* \param[out] buf Pointer to an allocated buffer into which the JSON
|
||||
* string will be written
|
||||
* \param[in] buf_size Size of the buffer
|
||||
* \param[in] flush_cb Pointer to the flushing function of type \ref json_gen_flush_cb_t
|
||||
* which will be invoked either when the buffer is full or when json_gen_str_end()
|
||||
* is invoked. Can be left NULL.
|
||||
* \param[in] priv Private data to be passed to the flushing function callback.
|
||||
* Can be something like a session identifier (Eg. socket). Can be left NULL.
|
||||
*/
|
||||
void json_gen_str_start(json_gen_str_t *jstr, char *buf, int buf_size,
|
||||
json_gen_flush_cb_t flush_cb, void *priv);
|
||||
|
||||
/** End JSON string
|
||||
*
|
||||
* This should be the last function to be called after the entire JSON string
|
||||
* has been generated.
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
*/
|
||||
void json_gen_str_end(json_gen_str_t *jstr);
|
||||
|
||||
/** Start a JSON object
|
||||
*
|
||||
* This starts a JSON object by adding a '{'
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_start_object(json_gen_str_t *jstr);
|
||||
|
||||
/** End a JSON object
|
||||
*
|
||||
* This ends a JSON object by adding a '}'
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_end_object(json_gen_str_t *jstr);
|
||||
|
||||
/** Start a JSON array
|
||||
*
|
||||
* This starts a JSON object by adding a '['
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_start_array(json_gen_str_t *jstr);
|
||||
|
||||
/** End a JSON object
|
||||
*
|
||||
* This ends a JSON object by adding a ']'
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_end_array(json_gen_str_t *jstr);
|
||||
|
||||
/** Push a named JSON object
|
||||
*
|
||||
* This adds a JSON object like "name":{
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the object
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_push_object(json_gen_str_t *jstr, char *name);
|
||||
|
||||
/** Pop a named JSON object
|
||||
*
|
||||
* This ends a JSON object by adding a '}'. This is basically same as
|
||||
* json_gen_end_object() but included so as to complement json_gen_push_object()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_pop_object(json_gen_str_t *jstr);
|
||||
|
||||
/** Push a JSON object string
|
||||
*
|
||||
* This adds a complete pre-formatted JSON object string to the JSON object.
|
||||
*
|
||||
* Eg. json_gen_push_object_str(jstr, "pre-formatted", "{\"a\":1,\"b\":2}");
|
||||
* This will add "pre-formatted":{"a":1,"b":2}
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the JSON object string
|
||||
* \param[in] object_str The pre-formatted JSON object string
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that.
|
||||
*/
|
||||
int json_gen_push_object_str(json_gen_str_t *jstr, char *name, char *object_str);
|
||||
|
||||
/** Push a named JSON array
|
||||
*
|
||||
* This adds a JSON array like "name":[
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the array
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_push_array(json_gen_str_t *jstr, char *name);
|
||||
|
||||
/** Pop a named JSON array
|
||||
*
|
||||
* This ends a JSON array by adding a ']'. This is basically same as
|
||||
* json_gen_end_array() but included so as to complement json_gen_push_array()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_pop_array(json_gen_str_t *jstr);
|
||||
|
||||
/** Push a JSON array string
|
||||
*
|
||||
* This adds a complete pre-formatted JSON array string to the JSON object.
|
||||
*
|
||||
* Eg. json_gen_push_object_str(jstr, "pre-formatted", "[1,2,3]");
|
||||
* This will add "pre-formatted":[1,2,3]
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the JSON array string
|
||||
* \param[in] array_str The pre-formatted JSON array string
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that.
|
||||
*/
|
||||
int json_gen_push_array_str(json_gen_str_t *jstr, char *name, char *array_str);
|
||||
|
||||
/** Add a boolean element to an object
|
||||
*
|
||||
* This adds a boolean element to an object. Eg. "bool_val":true
|
||||
*
|
||||
* \note This must be called between json_gen_start_object()/json_gen_push_object()
|
||||
* and json_gen_end_object()/json_gen_pop_object()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the element
|
||||
* \param[in] val Boolean value of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_obj_set_bool(json_gen_str_t *jstr, char *name, bool val);
|
||||
|
||||
/** Add an integer element to an object
|
||||
*
|
||||
* This adds an integer element to an object. Eg. "int_val":28
|
||||
*
|
||||
* \note This must be called between json_gen_start_object()/json_gen_push_object()
|
||||
* and json_gen_end_object()/json_gen_pop_object()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the element
|
||||
* \param[in] val Integer value of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_obj_set_int(json_gen_str_t *jstr, char *name, int val);
|
||||
|
||||
/** Add a float element to an object
|
||||
*
|
||||
* This adds a float element to an object. Eg. "float_val":23.8
|
||||
*
|
||||
* \note This must be called between json_gen_start_object()/json_gen_push_object()
|
||||
* and json_gen_end_object()/json_gen_pop_object()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the element
|
||||
* \param[in] val Float value of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_obj_set_float(json_gen_str_t *jstr, char *name, float val);
|
||||
|
||||
/** Add a string element to an object
|
||||
*
|
||||
* This adds a string element to an object. Eg. "string_val":"my_string"
|
||||
*
|
||||
* \note This must be called between json_gen_start_object()/json_gen_push_object()
|
||||
* and json_gen_end_object()/json_gen_pop_object()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the element
|
||||
* \param[in] val Null terminated string value of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_obj_set_string(json_gen_str_t *jstr, char *name, char *val);
|
||||
|
||||
/** Add a NULL element to an object
|
||||
*
|
||||
* This adds a NULL element to an object. Eg. "null_val":null
|
||||
*
|
||||
* \note This must be called between json_gen_start_object()/json_gen_push_object()
|
||||
* and json_gen_end_object()/json_gen_pop_object()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_obj_set_null(json_gen_str_t *jstr, char *name);
|
||||
|
||||
/** Add a boolean element to an array
|
||||
*
|
||||
* \note This must be called between json_gen_start_array()/json_gen_push_array()
|
||||
* and json_gen_end_array()/json_gen_pop_array()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] val Boolean value of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_arr_set_bool(json_gen_str_t *jstr, bool val);
|
||||
|
||||
/** Add an integer element to an array
|
||||
*
|
||||
* \note This must be called between json_gen_start_array()/json_gen_push_array()
|
||||
* and json_gen_end_array()/json_gen_pop_array()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] val Integer value of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_arr_set_int(json_gen_str_t *jstr, int val);
|
||||
|
||||
/** Add a float element to an array
|
||||
*
|
||||
* \note This must be called between json_gen_start_array()/json_gen_push_array()
|
||||
* and json_gen_end_array()/json_gen_pop_array()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] val Float value of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_arr_set_float(json_gen_str_t *jstr, float val);
|
||||
|
||||
/** Add a string element to an array
|
||||
*
|
||||
* \note This must be called between json_gen_start_array()/json_gen_push_array()
|
||||
* and json_gen_end_array()/json_gen_pop_array()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] val Null terminated string value of the element
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_arr_set_string(json_gen_str_t *jstr, char *val);
|
||||
|
||||
/** Add a NULL element to an array
|
||||
*
|
||||
* \note This must be called between json_gen_start_array()/json_gen_push_array()
|
||||
* and json_gen_end_array()/json_gen_pop_array()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_arr_set_null(json_gen_str_t *jstr);
|
||||
|
||||
/** Start a Long string in an object
|
||||
*
|
||||
* This starts a string in an object, but does not end it (i.e., does not add the
|
||||
* terminating quotes. This is useful for long strings. Eg. "string_val":"my_string.
|
||||
* The API json_gen_add_to_long_string() must be used to add to this string and the API
|
||||
* json_gen_end_long_string() must be used to terminate it (i.e. add the ending quotes).
|
||||
*
|
||||
* \note This must be called between json_gen_start_object()/json_gen_push_object()
|
||||
* and json_gen_end_object()/json_gen_pop_object()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] name Name of the element
|
||||
* \param[in] val Null terminated initial part of the string value. It can also be NULL
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_obj_start_long_string(json_gen_str_t *jstr, char *name, char *val);
|
||||
|
||||
/** Start a Long string in an array
|
||||
*
|
||||
* This starts a string in an arrayt, but does not end it (i.e., does not add the
|
||||
* terminating quotes. This is useful for long strings.
|
||||
* The API json_gen_add_to_long_string() must be used to add to this string and the API
|
||||
* json_gen_end_long_string() must be used to terminate it (i.e. add the ending quotes).
|
||||
*
|
||||
* \note This must be called between json_gen_start_array()/json_gen_push_array()
|
||||
* and json_gen_end_array()/json_gen_pop_array()
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by
|
||||
* json_gen_str_start()
|
||||
* \param[in] val Null terminated initial part of the string value. It can also be NULL
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_arr_start_long_string(json_gen_str_t *jstr, char *val);
|
||||
|
||||
/** Add to a JSON Long string
|
||||
*
|
||||
* This extends the string initialised by json_gen_obj_start_long_string() or
|
||||
* json_gen_arr_start_long_string(). After the entire string is created, it should be terminated
|
||||
* with json_gen_end_long_string().
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by json_gen_str_start()
|
||||
* \param[in] val Null terminated extending part of the string value.
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_add_to_long_string(json_gen_str_t *jstr, char *val);
|
||||
|
||||
/** End a JSON Long string
|
||||
*
|
||||
* This ends the string initialised by json_gen_obj_start_long_string() or
|
||||
* json_gen_arr_start_long_string() by adding the ending quotes.
|
||||
*
|
||||
* \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by json_gen_str_start()
|
||||
*
|
||||
*
|
||||
* \return 0 on Success
|
||||
* \return -1 if buffer is out of space (possible only if no callback function
|
||||
* is passed to json_gen_str_start(). Else, buffer will be flushed out and new data
|
||||
* added after that
|
||||
*/
|
||||
int json_gen_end_long_string(json_gen_str_t *jstr);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright 2020 Piyush Shah <shahpiyushv@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#define JSMN_PARENT_LINKS
|
||||
#define JSMN_STRICT
|
||||
#define JSMN_STATIC
|
||||
#include <jsmn/jsmn.h>
|
||||
#include <json_parser.h>
|
||||
|
||||
static bool token_matches_str(jparse_ctx_t *ctx, json_tok_t *tok, char *str)
|
||||
{
|
||||
char *js = ctx->js;
|
||||
return ((strncmp(js + tok->start, str, strlen(str)) == 0)
|
||||
&& (strlen(str) == (size_t) (tok->end - tok->start)));
|
||||
}
|
||||
|
||||
static json_tok_t *json_skip_elem(json_tok_t *token)
|
||||
{
|
||||
json_tok_t *cur = token;
|
||||
int cnt = cur->size;
|
||||
while (cnt--) {
|
||||
cur++;
|
||||
cur = json_skip_elem(cur);
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
static int json_tok_to_bool(jparse_ctx_t *jctx, json_tok_t *tok, bool *val)
|
||||
{
|
||||
if (token_matches_str(jctx, tok, "true") || token_matches_str(jctx, tok, "1")) {
|
||||
*val = true;
|
||||
} else if (token_matches_str(jctx, tok, "false") || token_matches_str(jctx, tok, "0")) {
|
||||
*val = false;
|
||||
} else
|
||||
return -OS_FAIL;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
static int json_tok_to_int(jparse_ctx_t *jctx, json_tok_t *tok, int *val)
|
||||
{
|
||||
char *tok_start = &jctx->js[tok->start];
|
||||
char *tok_end = &jctx->js[tok->end];
|
||||
char *endptr;
|
||||
int i = strtoul(tok_start, &endptr, 10);
|
||||
if (endptr == tok_end) {
|
||||
*val = i;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
return -OS_FAIL;
|
||||
}
|
||||
|
||||
static int json_tok_to_int64(jparse_ctx_t *jctx, json_tok_t *tok, int64_t *val)
|
||||
{
|
||||
char *tok_start = &jctx->js[tok->start];
|
||||
char *tok_end = &jctx->js[tok->end];
|
||||
char *endptr;
|
||||
int64_t i64 = strtoull(tok_start, &endptr, 10);
|
||||
if (endptr == tok_end) {
|
||||
*val = i64;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
return -OS_FAIL;
|
||||
}
|
||||
|
||||
static int json_tok_to_float(jparse_ctx_t *jctx, json_tok_t *tok, float *val)
|
||||
{
|
||||
char *tok_start = &jctx->js[tok->start];
|
||||
char *tok_end = &jctx->js[tok->end];
|
||||
char *endptr;
|
||||
float f = strtof(tok_start, &endptr);
|
||||
if (endptr == tok_end) {
|
||||
*val = f;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
return -OS_FAIL;
|
||||
}
|
||||
|
||||
static int json_tok_to_string(jparse_ctx_t *jctx, json_tok_t *tok, char *val, int size)
|
||||
{
|
||||
if ((tok->end - tok->start) > (size - 1))
|
||||
return -OS_FAIL;
|
||||
strncpy(val, jctx->js + tok->start, tok->end - tok->start);
|
||||
val[tok->end - tok->start] = 0;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
static json_tok_t *json_obj_search(jparse_ctx_t *jctx, char *key)
|
||||
{
|
||||
json_tok_t *tok = jctx->cur;
|
||||
int size = tok->size;
|
||||
if (size <= 0)
|
||||
return NULL;
|
||||
if (tok->type != JSMN_OBJECT)
|
||||
return NULL;
|
||||
|
||||
while (size--) {
|
||||
tok++;
|
||||
if (token_matches_str(jctx, tok, key))
|
||||
return tok;
|
||||
tok = json_skip_elem(tok);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_tok_t *json_obj_get_val_tok(jparse_ctx_t *jctx, char *name, jsmntype_t type)
|
||||
{
|
||||
json_tok_t *tok = json_obj_search(jctx, name);
|
||||
if (!tok)
|
||||
return NULL;
|
||||
tok++;
|
||||
if (tok->type != type)
|
||||
return NULL;
|
||||
return tok;
|
||||
}
|
||||
|
||||
int json_obj_get_array(jparse_ctx_t *jctx, char *name, int *num_elem)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_ARRAY);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = tok;
|
||||
*num_elem = tok->size;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_obj_leave_array(jparse_ctx_t *jctx)
|
||||
{
|
||||
/* The array's parent will be the key */
|
||||
if (jctx->cur->parent < 0)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = &jctx->tokens[jctx->cur->parent];
|
||||
|
||||
/* The key's parent will be the actual parent object */
|
||||
if (jctx->cur->parent < 0)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = &jctx->tokens[jctx->cur->parent];
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_obj_get_object(jparse_ctx_t *jctx, char *name)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_OBJECT);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = tok;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_obj_leave_object(jparse_ctx_t *jctx)
|
||||
{
|
||||
/* The objects's parent will be the key */
|
||||
if (jctx->cur->parent < 0)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = &jctx->tokens[jctx->cur->parent];
|
||||
|
||||
/* The key's parent will be the actual parent object */
|
||||
if (jctx->cur->parent < 0)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = &jctx->tokens[jctx->cur->parent];
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_obj_get_bool(jparse_ctx_t *jctx, char *name, bool *val)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_PRIMITIVE);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_bool(jctx, tok, val);
|
||||
}
|
||||
|
||||
int json_obj_get_int(jparse_ctx_t *jctx, char *name, int *val)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_PRIMITIVE);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_int(jctx, tok, val);
|
||||
}
|
||||
|
||||
int json_obj_get_int64(jparse_ctx_t *jctx, char *name, int64_t *val)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_PRIMITIVE);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_int64(jctx, tok, val);
|
||||
}
|
||||
|
||||
int json_obj_get_float(jparse_ctx_t *jctx, char *name, float *val)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_PRIMITIVE);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_float(jctx, tok, val);
|
||||
}
|
||||
|
||||
int json_obj_get_string(jparse_ctx_t *jctx, char *name, char *val, int size)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_STRING);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_string(jctx, tok, val, size);
|
||||
}
|
||||
|
||||
int json_obj_get_strlen(jparse_ctx_t *jctx, char *name, int *strlen)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_STRING);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
*strlen = tok->end - tok->start;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_obj_get_object_str(jparse_ctx_t *jctx, char *name, char *val, int size)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_OBJECT);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_string(jctx, tok, val, size);
|
||||
}
|
||||
|
||||
int json_obj_get_object_strlen(jparse_ctx_t *jctx, char *name, int *strlen)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_OBJECT);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
*strlen = tok->end - tok->start;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
int json_obj_get_array_str(jparse_ctx_t *jctx, char *name, char *val, int size)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_ARRAY);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_string(jctx, tok, val, size);
|
||||
}
|
||||
|
||||
int json_obj_get_array_strlen(jparse_ctx_t *jctx, char *name, int *strlen)
|
||||
{
|
||||
json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_ARRAY);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
*strlen = tok->end - tok->start;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
static json_tok_t *json_arr_search(jparse_ctx_t *ctx, uint32_t index)
|
||||
{
|
||||
json_tok_t *tok = ctx->cur;
|
||||
if ((tok->type != JSMN_ARRAY) || (tok->size <= 0))
|
||||
return NULL;
|
||||
if (index > (uint32_t)(tok->size - 1))
|
||||
return NULL;
|
||||
/* Increment by 1, so that token points to index 0 */
|
||||
tok++;
|
||||
while (index--) {
|
||||
tok = json_skip_elem(tok);
|
||||
tok++;
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
static json_tok_t *json_arr_get_val_tok(jparse_ctx_t *jctx, uint32_t index, jsmntype_t type)
|
||||
{
|
||||
json_tok_t *tok = json_arr_search(jctx, index);
|
||||
if (!tok)
|
||||
return NULL;
|
||||
if (tok->type != type)
|
||||
return NULL;
|
||||
return tok;
|
||||
}
|
||||
|
||||
int json_arr_get_array(jparse_ctx_t *jctx, uint32_t index)
|
||||
{
|
||||
json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_ARRAY);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = tok;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_arr_leave_array(jparse_ctx_t *jctx)
|
||||
{
|
||||
if (jctx->cur->parent < 0)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = &jctx->tokens[jctx->cur->parent];
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_arr_get_object(jparse_ctx_t *jctx, uint32_t index)
|
||||
{
|
||||
json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_OBJECT);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = tok;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_arr_leave_object(jparse_ctx_t *jctx)
|
||||
{
|
||||
if (jctx->cur->parent < 0)
|
||||
return -OS_FAIL;
|
||||
jctx->cur = &jctx->tokens[jctx->cur->parent];
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_arr_get_bool(jparse_ctx_t *jctx, uint32_t index, bool *val)
|
||||
{
|
||||
json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_PRIMITIVE);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_bool(jctx, tok, val);
|
||||
}
|
||||
|
||||
int json_arr_get_int(jparse_ctx_t *jctx, uint32_t index, int *val)
|
||||
{
|
||||
json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_PRIMITIVE);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_int(jctx, tok, val);
|
||||
}
|
||||
|
||||
int json_arr_get_int64(jparse_ctx_t *jctx, uint32_t index, int64_t *val)
|
||||
{
|
||||
json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_PRIMITIVE);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_int64(jctx, tok, val);
|
||||
}
|
||||
|
||||
int json_arr_get_float(jparse_ctx_t *jctx, uint32_t index, float *val)
|
||||
{
|
||||
json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_PRIMITIVE);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_float(jctx, tok, val);
|
||||
}
|
||||
|
||||
int json_arr_get_string(jparse_ctx_t *jctx, uint32_t index, char *val, int size)
|
||||
{
|
||||
json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_STRING);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
return json_tok_to_string(jctx, tok, val, size);
|
||||
}
|
||||
|
||||
int json_arr_get_strlen(jparse_ctx_t *jctx, uint32_t index, int *strlen)
|
||||
{
|
||||
json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_STRING);
|
||||
if (!tok)
|
||||
return -OS_FAIL;
|
||||
*strlen = tok->end - tok->start;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_parse_start(jparse_ctx_t *jctx, char *js, int len)
|
||||
{
|
||||
memset(jctx, 0, sizeof(jparse_ctx_t));
|
||||
jsmn_init(&jctx->parser);
|
||||
int num_tokens = jsmn_parse(&jctx->parser, js, len, NULL, 0);
|
||||
if (num_tokens <= 0)
|
||||
return -OS_FAIL;
|
||||
jctx->num_tokens = num_tokens;
|
||||
jctx->tokens = calloc(num_tokens, sizeof(json_tok_t));
|
||||
if (!jctx->tokens)
|
||||
return -OS_FAIL;
|
||||
jctx->js = js;
|
||||
jsmn_init(&jctx->parser);
|
||||
int ret = jsmn_parse(&jctx->parser, js, len, jctx->tokens, jctx->num_tokens);
|
||||
if (ret <= 0) {
|
||||
free(jctx->tokens);
|
||||
memset(jctx, 0, sizeof(jparse_ctx_t));
|
||||
return -OS_FAIL;
|
||||
}
|
||||
jctx->cur = jctx->tokens;
|
||||
return OS_SUCCESS;
|
||||
}
|
||||
|
||||
int json_parse_end(jparse_ctx_t *jctx)
|
||||
{
|
||||
if (jctx->tokens)
|
||||
free(jctx->tokens);
|
||||
memset(jctx, 0, sizeof(jparse_ctx_t));
|
||||
return OS_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2020 Piyush Shah <shahpiyushv@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _JSON_PARSER_H_
|
||||
#define _JSON_PARSER_H_
|
||||
|
||||
#define JSMN_HEADER
|
||||
#include <jsmn/jsmn.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define OS_SUCCESS 0
|
||||
#define OS_FAIL -1
|
||||
|
||||
typedef jsmn_parser json_parser_t;
|
||||
typedef jsmntok_t json_tok_t;
|
||||
|
||||
typedef struct {
|
||||
json_parser_t parser;
|
||||
char *js;
|
||||
json_tok_t *tokens;
|
||||
json_tok_t *cur;
|
||||
int num_tokens;
|
||||
} jparse_ctx_t;
|
||||
|
||||
int json_parse_start(jparse_ctx_t *jctx, char *js, int len);
|
||||
int json_parse_end(jparse_ctx_t *jctx);
|
||||
|
||||
int json_obj_get_array(jparse_ctx_t *jctx, char *name, int *num_elem);
|
||||
int json_obj_leave_array(jparse_ctx_t *jctx);
|
||||
int json_obj_get_object(jparse_ctx_t *jctx, char *name);
|
||||
int json_obj_leave_object(jparse_ctx_t *jctx);
|
||||
int json_obj_get_bool(jparse_ctx_t *jctx, char *name, bool *val);
|
||||
int json_obj_get_int(jparse_ctx_t *jctx, char *name, int *val);
|
||||
int json_obj_get_int64(jparse_ctx_t *jctx, char *name, int64_t *val);
|
||||
int json_obj_get_float(jparse_ctx_t *jctx, char *name, float *val);
|
||||
int json_obj_get_string(jparse_ctx_t *jctx, char *name, char *val, int size);
|
||||
int json_obj_get_strlen(jparse_ctx_t *jctx, char *name, int *strlen);
|
||||
int json_obj_get_object_str(jparse_ctx_t *jctx, char *name, char *val, int size);
|
||||
int json_obj_get_object_strlen(jparse_ctx_t *jctx, char *name, int *strlen);
|
||||
int json_obj_get_array_str(jparse_ctx_t *jctx, char *name, char *val, int size);
|
||||
int json_obj_get_array_strlen(jparse_ctx_t *jctx, char *name, int *strlen);
|
||||
|
||||
int json_arr_get_array(jparse_ctx_t *jctx, uint32_t index);
|
||||
int json_arr_leave_array(jparse_ctx_t *jctx);
|
||||
int json_arr_get_object(jparse_ctx_t *jctx, uint32_t index);
|
||||
int json_arr_leave_object(jparse_ctx_t *jctx);
|
||||
int json_arr_get_bool(jparse_ctx_t *jctx, uint32_t index, bool *val);
|
||||
int json_arr_get_int(jparse_ctx_t *jctx, uint32_t index, int *val);
|
||||
int json_arr_get_int64(jparse_ctx_t *jctx, uint32_t index, int64_t *val);
|
||||
int json_arr_get_float(jparse_ctx_t *jctx, uint32_t index, float *val);
|
||||
int json_arr_get_string(jparse_ctx_t *jctx, uint32_t index, char *val, int size);
|
||||
int json_arr_get_strlen(jparse_ctx_t *jctx, uint32_t index, int *strlen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _JSON_PARSER_H_ */
|
|
@ -0,0 +1,232 @@
|
|||
#ifndef _MU_BIGNUM_H_
|
||||
#define _MU_BIGNUM_H_
|
||||
|
||||
#define BIGNUM_MBEDTLS
|
||||
|
||||
#ifdef BIGNUM_OPENSSL
|
||||
#include <openssl/bn.h>
|
||||
|
||||
|
||||
typedef BIGNUM mu_bn_t;
|
||||
typedef BN_CTX mu_bn_ctx_t;
|
||||
|
||||
|
||||
static inline mu_bn_t *mu_bn_new_from_hex(const char *hex)
|
||||
{
|
||||
mu_bn_t *a = BN_new();
|
||||
if (a)
|
||||
BN_hex2bn(&a, hex);
|
||||
return a;
|
||||
}
|
||||
|
||||
static inline mu_bn_t *mu_bn_new_from_bin(const char *str, int str_len)
|
||||
{
|
||||
return BN_bin2bn((unsigned char *)str, str_len, NULL);
|
||||
}
|
||||
|
||||
static inline mu_bn_t *mu_bn_new()
|
||||
{
|
||||
return BN_new();
|
||||
}
|
||||
|
||||
static inline void mu_bn_free(mu_bn_t *bn)
|
||||
{
|
||||
return BN_free(bn);
|
||||
}
|
||||
|
||||
static inline mu_bn_ctx_t *mu_bn_ctx_new()
|
||||
{
|
||||
return BN_CTX_new();
|
||||
}
|
||||
|
||||
static inline void mu_bn_ctx_free(mu_bn_ctx_t *ctx)
|
||||
{
|
||||
return BN_CTX_free(ctx);
|
||||
}
|
||||
|
||||
static inline unsigned int mu_bn_sizeof(mu_bn_t *bn)
|
||||
{
|
||||
return BN_num_bytes(bn);
|
||||
}
|
||||
|
||||
|
||||
static inline char *mu_bn_to_bin(mu_bn_t *bn, int *len)
|
||||
{
|
||||
*len = mu_bn_sizeof(bn);
|
||||
char *p = malloc(*len);
|
||||
if (p) {
|
||||
BN_bn2bin(bn, (unsigned char *)p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline int mu_bn_get_rand(mu_bn_t *bn, int bits, int top, int bottom)
|
||||
{
|
||||
return BN_rand(bn, bits, top, bottom);
|
||||
}
|
||||
|
||||
static inline int mu_bn_a_exp_b_mod_c(mu_bn_t *result, mu_bn_t *a, mu_bn_t *b, mu_bn_t *c, mu_bn_ctx_t *ctx)
|
||||
{
|
||||
return BN_mod_exp(result, a, b, c, ctx);
|
||||
}
|
||||
|
||||
static inline int mu_bn_a_mul_b_mod_c(mu_bn_t *result, mu_bn_t *a, mu_bn_t *b, mu_bn_t *c, mu_bn_ctx_t *ctx)
|
||||
{
|
||||
return BN_mod_mul(result, a, b, c, ctx);
|
||||
}
|
||||
|
||||
static inline int mu_bn_a_add_b_mod_c(mu_bn_t *result, mu_bn_t *a, mu_bn_t *b, mu_bn_t *c, mu_bn_ctx_t *ctx)
|
||||
{
|
||||
if (BN_add(result, a, b) != 1)
|
||||
return 1;
|
||||
return BN_mod(result, result, c, ctx);
|
||||
}
|
||||
#endif /* BIGNUM_OPENSSL */
|
||||
|
||||
|
||||
#ifdef BIGNUM_MBEDTLS
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <esp_system.h>
|
||||
#ifdef CONFIG_IDF_TARGET_ESP8266
|
||||
#include <driver/rtc.h>
|
||||
#endif
|
||||
typedef mbedtls_mpi mu_bn_t;
|
||||
typedef mu_bn_t mu_bn_ctx_t;
|
||||
|
||||
static inline mu_bn_t *mu_bn_new()
|
||||
{
|
||||
mu_bn_t *a = malloc(sizeof (mu_bn_t));
|
||||
if (!a)
|
||||
return NULL;
|
||||
mbedtls_mpi_init(a);
|
||||
return a;
|
||||
}
|
||||
static inline mu_bn_t *mu_bn_new_from_hex(const char *hex)
|
||||
{
|
||||
mu_bn_t *a = mu_bn_new();
|
||||
if (!a)
|
||||
return NULL;
|
||||
|
||||
mbedtls_mpi_read_string(a, 16, hex);
|
||||
return a;
|
||||
}
|
||||
|
||||
static inline mu_bn_t *mu_bn_new_from_bin(const char *str, int str_len)
|
||||
{
|
||||
|
||||
mu_bn_t *a = mu_bn_new();
|
||||
if (!a) {
|
||||
return NULL;
|
||||
}
|
||||
mbedtls_mpi_read_binary(a, (unsigned char *)str, str_len);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
static inline void mu_bn_free(mu_bn_t *bn)
|
||||
{
|
||||
if (bn) {
|
||||
mbedtls_mpi_free(bn);
|
||||
free(bn);
|
||||
}
|
||||
}
|
||||
|
||||
static inline mu_bn_ctx_t *mu_bn_ctx_new()
|
||||
{
|
||||
mu_bn_t *bn = mu_bn_new();
|
||||
return ( mu_bn_ctx_t *)bn;
|
||||
}
|
||||
|
||||
static inline void mu_bn_ctx_free(mu_bn_ctx_t *ctx)
|
||||
{
|
||||
mu_bn_free((mu_bn_t *)ctx);
|
||||
}
|
||||
|
||||
static inline unsigned int mu_bn_sizeof(mu_bn_t *bn)
|
||||
{
|
||||
return mbedtls_mpi_size(bn);
|
||||
}
|
||||
|
||||
|
||||
static inline char *mu_bn_to_bin(mu_bn_t *bn, int *len)
|
||||
{
|
||||
*len = mu_bn_sizeof(bn);
|
||||
char *p = malloc(*len);
|
||||
if (p) {
|
||||
mbedtls_mpi_write_binary(bn, (unsigned char *)p, *len);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline int mu_get_random(void *ctx, unsigned char *data, size_t len)
|
||||
{
|
||||
esp_fill_random(data, len);
|
||||
return 0;
|
||||
}
|
||||
static inline int mu_bn_get_rand(mu_bn_t *bn, int bits, int top, int bottom)
|
||||
{
|
||||
|
||||
return mbedtls_mpi_fill_random(bn, bits / 8, mu_get_random, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP8266
|
||||
static inline int mu_bn_a_exp_b_mod_c(mu_bn_t *result, mu_bn_t *a, mu_bn_t *b, mu_bn_t *c, mu_bn_ctx_t *ctx)
|
||||
{
|
||||
int ret;
|
||||
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_160M);
|
||||
ret = mbedtls_mpi_exp_mod(result, a, b, c, (mu_bn_t *) ctx);
|
||||
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_80M);
|
||||
return ret;
|
||||
}
|
||||
static inline int mu_bn_a_mul_b_mod_c(mu_bn_t *result, mu_bn_t *a, mu_bn_t *b, mu_bn_t *c, mu_bn_ctx_t *ctx)
|
||||
{
|
||||
mbedtls_mpi tmp_result;
|
||||
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_160M);
|
||||
mbedtls_mpi_init(&tmp_result);
|
||||
mbedtls_mpi_mul_mpi(&tmp_result, a, b);
|
||||
mbedtls_mpi_mod_mpi(result, &tmp_result, c);
|
||||
mbedtls_mpi_free(&tmp_result);
|
||||
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_80M);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
||||
#include "port/bignum.h"
|
||||
#include "port/bignum_impl.h"
|
||||
|
||||
static inline int mu_bn_a_exp_b_mod_c(mu_bn_t *result, mu_bn_t *a, mu_bn_t *b, mu_bn_t *c, mu_bn_ctx_t *ctx)
|
||||
{
|
||||
//return mbedtls_mpi_exp_mod(result, a, b, c, (mu_bn_t *) ctx);
|
||||
// wangbin changed
|
||||
printf("esp_mpi_exp_mod\n");
|
||||
return esp_mpi_exp_mod(result, a, b, c, (mu_bn_t *) ctx);
|
||||
}
|
||||
|
||||
|
||||
static inline int mu_bn_a_mul_b_mod_c(mu_bn_t *result, mu_bn_t *a, mu_bn_t *b, mu_bn_t *c, mu_bn_ctx_t *ctx)
|
||||
{
|
||||
printf("esp_mpi_mul_mpi_mod\n");
|
||||
return esp_mpi_mul_mpi_mod(result, a, b, c);
|
||||
}
|
||||
#endif /* !CONFIG_IDF_TARGET_ESP8266 */
|
||||
|
||||
static inline int mu_bn_a_add_b_mod_c(mu_bn_t *result, mu_bn_t *a, mu_bn_t *b, mu_bn_t *c, mu_bn_ctx_t *ctx)
|
||||
{
|
||||
int res;
|
||||
mbedtls_mpi t;
|
||||
#ifdef CONFIG_IDF_TARGET_ESP8266
|
||||
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_160M);
|
||||
#endif
|
||||
mbedtls_mpi_init(&t);
|
||||
res = mbedtls_mpi_add_mpi(&t, a, b);
|
||||
if (res == 0) {
|
||||
res = mbedtls_mpi_mod_mpi(result, &t, c);
|
||||
}
|
||||
mbedtls_mpi_free(&t);
|
||||
#ifdef CONFIG_IDF_TARGET_ESP8266
|
||||
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_80M);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
#endif /* BIGNUM_MBEDTLS */
|
||||
#endif /* ! _MU_BIGNUM_H_ */
|
|
@ -0,0 +1,489 @@
|
|||
#include <string.h>
|
||||
#include "hkdf-sha.h"
|
||||
#include "mu_bignum.h"
|
||||
#include "mu_srp.h"
|
||||
|
||||
#ifdef SRP_DEBUG
|
||||
#include <stdio.h>
|
||||
#define srp_print printf
|
||||
static void hex_dbg(char *name, void *buf, int buf_len)
|
||||
{
|
||||
char *p = (char *)buf;
|
||||
int i;
|
||||
srp_print("%s (%d): ", name, buf_len);
|
||||
for (i = 0; i < buf_len; i++) {
|
||||
if (i % 16 == 0)
|
||||
srp_print("\r\n");
|
||||
srp_print("%02x ", (unsigned)(unsigned char)p[i]);
|
||||
}
|
||||
srp_print("\r\n");
|
||||
}
|
||||
|
||||
static void hex_dbg_bn(char *name, mu_bn_t *bn)
|
||||
{
|
||||
int len;
|
||||
char *str = mu_bn_to_bin(bn, &len);
|
||||
if (str) {
|
||||
hex_dbg(name, str, len);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define srp_print(...)
|
||||
#define hex_dbg(...)
|
||||
#define hex_dbg_bn(...)
|
||||
#endif
|
||||
|
||||
static inline void SHA512_hash(const uint8_t *bytes, unsigned int byte_count,
|
||||
uint8_t digest[SHA512HashSize])
|
||||
{
|
||||
SHA512Context ctx;
|
||||
SHA512Reset(&ctx);
|
||||
SHA512Input(&ctx, bytes, byte_count);
|
||||
SHA512Result(&ctx, digest);
|
||||
}
|
||||
|
||||
/************************* SRP Stuff */
|
||||
char N_3072[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2,
|
||||
0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67,
|
||||
0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E,
|
||||
0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
|
||||
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5,
|
||||
0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF,
|
||||
0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE,
|
||||
0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
|
||||
0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3,
|
||||
0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3,
|
||||
0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70,
|
||||
0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
|
||||
0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77,
|
||||
0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5,
|
||||
0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39,
|
||||
0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
|
||||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A,
|
||||
0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB,
|
||||
0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6,
|
||||
0xE1, 0xE4, 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0,
|
||||
0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA,
|
||||
0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F,
|
||||
0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77,
|
||||
0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
|
||||
0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2,
|
||||
0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
char g_3072[] = { 5 };
|
||||
|
||||
|
||||
int mu_srp_init(mu_srp_handle_t *hd, mu_ng_type_t ng)
|
||||
{
|
||||
if (hd->allocated)
|
||||
mu_srp_free(hd);
|
||||
|
||||
memset(hd, 0, sizeof(*hd));
|
||||
hd->allocated = 1;
|
||||
|
||||
hd->ctx = mu_bn_ctx_new();
|
||||
if (! hd->ctx)
|
||||
goto error;
|
||||
if (ng != MU_NG_3072)
|
||||
goto error;
|
||||
|
||||
hd->n = mu_bn_new_from_bin(N_3072, sizeof(N_3072));
|
||||
hd->bytes_n = N_3072;
|
||||
hd->len_n = sizeof(N_3072);
|
||||
if (! hd->n)
|
||||
goto error;
|
||||
|
||||
hd->g = mu_bn_new_from_bin(g_3072, sizeof(g_3072));
|
||||
hd->bytes_g = g_3072;
|
||||
hd->len_g = sizeof(g_3072);
|
||||
if (! hd->g)
|
||||
goto error;
|
||||
hd->type = ng;
|
||||
return 0;
|
||||
error:
|
||||
mu_srp_free(hd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void mu_srp_free(mu_srp_handle_t *hd)
|
||||
{
|
||||
if (hd->allocated != 1)
|
||||
return;
|
||||
|
||||
if (hd->ctx)
|
||||
mu_bn_ctx_free(hd->ctx);
|
||||
if (hd->n)
|
||||
mu_bn_free(hd->n);
|
||||
if (hd->g)
|
||||
mu_bn_free(hd->g);
|
||||
if (hd->s)
|
||||
mu_bn_free(hd->s);
|
||||
if (hd->bytes_s)
|
||||
free(hd->bytes_s);
|
||||
if (hd->v)
|
||||
mu_bn_free(hd->v);
|
||||
if (hd->B)
|
||||
mu_bn_free(hd->B);
|
||||
if (hd->bytes_B)
|
||||
free(hd->bytes_B);
|
||||
if (hd->b)
|
||||
mu_bn_free(hd->b);
|
||||
if (hd->A)
|
||||
mu_bn_free(hd->A);
|
||||
if (hd->bytes_A)
|
||||
free(hd->bytes_A);
|
||||
if (hd->session_key)
|
||||
free(hd->session_key);
|
||||
memset(hd, 0, sizeof(*hd));
|
||||
}
|
||||
|
||||
static mu_bn_t *calculate_x(char *bytes_salt, int salt_len, const char *username, const char *pass, int pass_len)
|
||||
{
|
||||
unsigned char digest[SHA512HashSize];
|
||||
SHA512Context ctx;
|
||||
srp_print("username:%s:\npass:%s:\npass_len:%d:\n", username, pass, pass_len);
|
||||
hex_dbg("salt", bytes_salt, salt_len);
|
||||
SHA512Reset(&ctx);
|
||||
SHA512Input(&ctx, (unsigned char *)username, strlen(username));
|
||||
SHA512Input(&ctx, (unsigned char *)":", 1);
|
||||
SHA512Input(&ctx, (unsigned char *)pass, pass_len);
|
||||
SHA512Result(&ctx, digest);
|
||||
|
||||
SHA512Reset(&ctx);
|
||||
SHA512Input(&ctx, (unsigned char *)bytes_salt, salt_len);
|
||||
SHA512Input(&ctx, digest, sizeof(digest));
|
||||
SHA512Result(&ctx, digest);
|
||||
|
||||
hex_dbg("Digest", digest, sizeof(digest));
|
||||
return mu_bn_new_from_bin((char *)digest, sizeof(digest));
|
||||
}
|
||||
|
||||
static mu_bn_t *calculate_padded_hash(mu_srp_handle_t *hd, char *a, int len_a, char *b, int len_b)
|
||||
{
|
||||
unsigned char digest[SHA512HashSize];
|
||||
SHA512Context ctx;
|
||||
int pad_len;
|
||||
char *s = NULL;
|
||||
|
||||
if (len_a > len_b) {
|
||||
pad_len = hd->len_n - len_b;
|
||||
} else {
|
||||
pad_len = hd->len_n - len_a;
|
||||
}
|
||||
|
||||
if (pad_len) {
|
||||
s = malloc(pad_len);
|
||||
if (s) {
|
||||
memset(s, 0, pad_len);
|
||||
}
|
||||
}
|
||||
|
||||
SHA512Reset(&ctx);
|
||||
/* PAD (a) */
|
||||
if (s && (len_a != hd->len_n)) {
|
||||
SHA512Input(&ctx, (unsigned char *)s, hd->len_n - len_a);
|
||||
}
|
||||
|
||||
SHA512Input(&ctx, (unsigned char *)a, len_a);
|
||||
|
||||
/* PAD (b) */
|
||||
if (s && (len_b != hd->len_n)) {
|
||||
SHA512Input(&ctx, (unsigned char *)s, hd->len_n - len_b);
|
||||
}
|
||||
|
||||
SHA512Input(&ctx, (unsigned char *)b, len_b);
|
||||
|
||||
SHA512Result(&ctx, digest);
|
||||
|
||||
if (s) {
|
||||
free(s);
|
||||
}
|
||||
|
||||
hex_dbg("value", digest, sizeof(digest));
|
||||
return mu_bn_new_from_bin((char *)digest, sizeof(digest));
|
||||
}
|
||||
|
||||
/* k = SHA (N, PAD(g))
|
||||
*
|
||||
* https://tools.ietf.org/html/draft-ietf-tls-srp-08
|
||||
*/
|
||||
static mu_bn_t *calculate_k(mu_srp_handle_t *hd)
|
||||
{
|
||||
srp_print("k-->");
|
||||
return calculate_padded_hash(hd, hd->bytes_n, hd->len_n, hd->bytes_g, hd->len_g);
|
||||
}
|
||||
|
||||
static mu_bn_t *calculate_u(mu_srp_handle_t *hd, char *A, int len_A)
|
||||
{
|
||||
srp_print("u-->");
|
||||
return calculate_padded_hash(hd, A, len_A, hd->bytes_B, hd->len_B);
|
||||
}
|
||||
|
||||
int __mu_srp_srv_pubkey(mu_srp_handle_t *hd, char **bytes_B, int *len_B)
|
||||
{
|
||||
mu_bn_t *k = calculate_k(hd);
|
||||
mu_bn_t *kv = NULL;
|
||||
mu_bn_t *gb = NULL;
|
||||
if (!k)
|
||||
goto error;
|
||||
|
||||
hd->b = mu_bn_new();
|
||||
if (!hd->b)
|
||||
goto error;
|
||||
mu_bn_get_rand(hd->b, 256, -1, 0);
|
||||
hex_dbg_bn("b", hd->b);
|
||||
|
||||
/* B = kv + g^b */
|
||||
kv = mu_bn_new();
|
||||
gb = mu_bn_new();
|
||||
hd->B = mu_bn_new();
|
||||
if (!kv || !gb || ! hd->B)
|
||||
goto error;
|
||||
mu_bn_a_mul_b_mod_c(kv, k, hd->v, hd->n, hd->ctx);
|
||||
mu_bn_a_exp_b_mod_c(gb, hd->g, hd->b, hd->n, hd->ctx);
|
||||
mu_bn_a_add_b_mod_c(hd->B, kv, gb, hd->n, hd->ctx);
|
||||
hd->bytes_B = mu_bn_to_bin(hd->B, len_B);
|
||||
hd->len_B = *len_B;
|
||||
*bytes_B = hd->bytes_B;
|
||||
|
||||
mu_bn_free(k);
|
||||
mu_bn_free(kv);
|
||||
mu_bn_free(gb);
|
||||
return 0;
|
||||
error:
|
||||
if (k)
|
||||
mu_bn_free(k);
|
||||
if (kv)
|
||||
mu_bn_free(kv);
|
||||
if (gb)
|
||||
mu_bn_free(gb);
|
||||
if (hd->B) {
|
||||
mu_bn_free(hd->B);
|
||||
hd->B = NULL;
|
||||
}
|
||||
if (hd->b) {
|
||||
mu_bn_free(hd->b);
|
||||
hd->b = NULL;
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
int mu_srp_srv_pubkey(mu_srp_handle_t *hd, const char *username, const char *pass, int pass_len, int salt_len,
|
||||
char **bytes_B, int *len_B, char **bytes_salt)
|
||||
{
|
||||
/* Get Salt */
|
||||
int str_salt_len;
|
||||
mu_bn_t *x = NULL;
|
||||
hd->s = mu_bn_new();
|
||||
if (! hd->s)
|
||||
goto error;
|
||||
mu_bn_get_rand(hd->s, 8 * salt_len, -1, 0);
|
||||
*bytes_salt = mu_bn_to_bin(hd->s, &str_salt_len);
|
||||
if (! *bytes_salt)
|
||||
goto error;
|
||||
hd->bytes_s = *bytes_salt;
|
||||
hd->len_s = salt_len;
|
||||
hex_dbg("Salt", *bytes_salt, str_salt_len);
|
||||
|
||||
/* Calculate X which is simply a hash for all these things */
|
||||
x = calculate_x(*bytes_salt, str_salt_len, username, pass, pass_len);
|
||||
if (! x)
|
||||
goto error;
|
||||
hex_dbg_bn("x", x);
|
||||
|
||||
/* v = g^x % N */
|
||||
hd->v = mu_bn_new();
|
||||
if (! hd->v)
|
||||
goto error;
|
||||
mu_bn_a_exp_b_mod_c(hd->v, hd->g, x, hd->n, hd->ctx);
|
||||
hex_dbg_bn("Verifier", hd->v);
|
||||
|
||||
if (__mu_srp_srv_pubkey(hd, bytes_B, len_B) < 0 )
|
||||
goto error;
|
||||
|
||||
mu_bn_free(x);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (hd->s) {
|
||||
mu_bn_free(hd->s);
|
||||
hd->s = NULL;
|
||||
}
|
||||
if (*bytes_salt) {
|
||||
free(*bytes_salt);
|
||||
*bytes_salt = NULL;
|
||||
hd->bytes_s = NULL;
|
||||
hd->len_s = 0;
|
||||
}
|
||||
if (x) {
|
||||
mu_bn_free(x);
|
||||
x = NULL;
|
||||
}
|
||||
if (hd->v) {
|
||||
mu_bn_free(hd->v);
|
||||
hd->v = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mu_srp_srv_pubkey_from_salt_verifier(mu_srp_handle_t *hd, char **bytes_B, int *len_B)
|
||||
{
|
||||
return __mu_srp_srv_pubkey(hd, bytes_B, len_B);
|
||||
}
|
||||
|
||||
int mu_srp_set_salt_verifier(mu_srp_handle_t *hd, const char *salt, int salt_len,
|
||||
const char *verifier, int verifier_len)
|
||||
{
|
||||
hd->bytes_s = malloc(salt_len);
|
||||
if (!hd->bytes_s) {
|
||||
goto error;
|
||||
}
|
||||
memcpy(hd->bytes_s, salt, salt_len);
|
||||
hd->len_s = salt_len;
|
||||
|
||||
hd->s = mu_bn_new_from_bin(salt, salt_len);
|
||||
if (!hd->s) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
hd->v = mu_bn_new_from_bin(verifier, verifier_len);
|
||||
if (!hd->v) {
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (hd->bytes_s) {
|
||||
free(hd->bytes_s);
|
||||
hd->bytes_s = NULL;
|
||||
hd->len_s = 0;
|
||||
}
|
||||
if (hd->s) {
|
||||
mu_bn_free(hd->s);
|
||||
hd->s = NULL;
|
||||
}
|
||||
if (hd->v) {
|
||||
mu_bn_free(hd->v);
|
||||
hd->v = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mu_srp_get_session_key(mu_srp_handle_t *hd, char *bytes_A, int len_A, char **bytes_key, int *len_key)
|
||||
{
|
||||
mu_bn_t *u, *vu, *avu, *S;
|
||||
char *bytes_S;
|
||||
int len_S;
|
||||
|
||||
u = vu = avu = S = NULL;
|
||||
bytes_S = NULL;
|
||||
|
||||
hd->bytes_A = malloc(len_A);
|
||||
if (! hd->bytes_A)
|
||||
goto error;
|
||||
memcpy(hd->bytes_A, bytes_A, len_A);
|
||||
hd->len_A = len_A;
|
||||
|
||||
hd->A = mu_bn_new_from_bin(bytes_A, len_A);
|
||||
if (! hd->A)
|
||||
goto error;
|
||||
u = calculate_u(hd, bytes_A, len_A);
|
||||
if (! u)
|
||||
goto error;
|
||||
|
||||
/* S = (A v^u)^b */
|
||||
vu = mu_bn_new();
|
||||
avu = mu_bn_new();
|
||||
S = mu_bn_new();
|
||||
if (!vu || !avu || !S )
|
||||
goto error;
|
||||
|
||||
mu_bn_a_exp_b_mod_c(vu, hd->v, u, hd->n, hd->ctx);
|
||||
mu_bn_a_mul_b_mod_c(avu, hd->A, vu, hd->n, hd->ctx);
|
||||
mu_bn_a_exp_b_mod_c(S, avu, hd->b, hd->n, hd->ctx);
|
||||
hex_dbg_bn("S", S);
|
||||
|
||||
bytes_S = mu_bn_to_bin(S, &len_S);
|
||||
hd->session_key = malloc(SHA512HashSize);
|
||||
if (!hd->session_key || ! bytes_S)
|
||||
goto error;
|
||||
|
||||
SHA512_hash((unsigned char *)bytes_S, len_S, (unsigned char *)hd->session_key);
|
||||
*bytes_key = hd->session_key;
|
||||
*len_key = SHA512HashSize;
|
||||
|
||||
free(bytes_S);
|
||||
mu_bn_free(vu);
|
||||
mu_bn_free(avu);
|
||||
mu_bn_free(S);
|
||||
mu_bn_free(u);
|
||||
return 0;
|
||||
error:
|
||||
if (bytes_S)
|
||||
free(bytes_S);
|
||||
if (vu)
|
||||
mu_bn_free(vu);
|
||||
if (avu)
|
||||
mu_bn_free(avu);
|
||||
if (S)
|
||||
mu_bn_free(S);
|
||||
if (u)
|
||||
mu_bn_free(u);
|
||||
if (hd->session_key) {
|
||||
free(hd->session_key);
|
||||
hd->session_key = NULL;
|
||||
}
|
||||
if (hd->A) {
|
||||
mu_bn_free(hd->A);
|
||||
hd->A = NULL;
|
||||
}
|
||||
if (hd->bytes_A) {
|
||||
free(hd->bytes_A);
|
||||
hd->bytes_A = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mu_srp_exchange_proofs(mu_srp_handle_t *hd, char *username, char *bytes_user_proof, char *bytes_host_proof)
|
||||
{
|
||||
/* First calculate M */
|
||||
unsigned char hash_n[SHA512HashSize];
|
||||
unsigned char hash_g[SHA512HashSize];
|
||||
unsigned char hash_n_xor_g[SHA512HashSize];
|
||||
int i;
|
||||
SHA512_hash((unsigned char *)hd->bytes_n, hd->len_n, (unsigned char *)hash_n);
|
||||
SHA512_hash((unsigned char *)hd->bytes_g, hd->len_g, (unsigned char *)hash_g);
|
||||
for (i = 0; i < SHA512HashSize; i++)
|
||||
hash_n_xor_g[i] = hash_n[i] ^ hash_g[i];
|
||||
|
||||
unsigned char hash_I[SHA512HashSize];
|
||||
SHA512_hash((unsigned char *)username, strlen(username), (unsigned char *)hash_I);
|
||||
|
||||
SHA512Context ctx;
|
||||
unsigned char digest[SHA512HashSize];
|
||||
SHA512Reset(&ctx);
|
||||
SHA512Input(&ctx, hash_n_xor_g, SHA512HashSize);
|
||||
SHA512Input(&ctx, hash_I, SHA512HashSize);
|
||||
SHA512Input(&ctx, (unsigned char *)hd->bytes_s, hd->len_s);
|
||||
SHA512Input(&ctx, (unsigned char *)hd->bytes_A, hd->len_A);
|
||||
SHA512Input(&ctx, (unsigned char *)hd->bytes_B, hd->len_B);
|
||||
SHA512Input(&ctx, (unsigned char *)hd->session_key, SHA512HashSize);
|
||||
SHA512Result(&ctx, digest);
|
||||
|
||||
hex_dbg("M", digest, sizeof(digest));
|
||||
if (memcmp(bytes_user_proof, digest, SHA512HashSize) != 0)
|
||||
return false;
|
||||
/* M is now validated, let's proceed to H(AMK) */
|
||||
SHA512Reset(&ctx);
|
||||
SHA512Input(&ctx, (unsigned char *)hd->bytes_A, hd->len_A);
|
||||
SHA512Input(&ctx, digest, SHA512HashSize);
|
||||
SHA512Input(&ctx, (unsigned char *)hd->session_key, SHA512HashSize);
|
||||
SHA512Result(&ctx, (unsigned char *)bytes_host_proof);
|
||||
hex_dbg("AMK", bytes_host_proof, SHA512HashSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
/************************* SRP Stuff Ends */
|
|
@ -0,0 +1,91 @@
|
|||
#ifndef _MU_SRP_H_
|
||||
#define _MU_SRP_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "mu_bignum.h"
|
||||
|
||||
typedef enum {
|
||||
MU_NG_3072 = 0,
|
||||
} mu_ng_type_t;
|
||||
|
||||
typedef struct mu_srp_handle {
|
||||
int allocated;
|
||||
mu_ng_type_t type;
|
||||
mu_bn_ctx_t *ctx;
|
||||
|
||||
/* N
|
||||
* the bytes_n simply points to the static array
|
||||
*/
|
||||
mu_bn_t *n;
|
||||
char *bytes_n;
|
||||
int len_n;
|
||||
|
||||
/* g
|
||||
* the bytes_g simply points to the static array
|
||||
*/
|
||||
mu_bn_t *g;
|
||||
char *bytes_g;
|
||||
int len_g;
|
||||
|
||||
/* Salt */
|
||||
mu_bn_t *s;
|
||||
char *bytes_s;
|
||||
int len_s;
|
||||
/* Verifier */
|
||||
mu_bn_t *v;
|
||||
/* B */
|
||||
mu_bn_t *B;
|
||||
char *bytes_B;
|
||||
int len_B;
|
||||
/* b */
|
||||
mu_bn_t *b;
|
||||
/* A */
|
||||
mu_bn_t *A;
|
||||
char *bytes_A;
|
||||
int len_A;
|
||||
/* K - session key*/
|
||||
char *session_key;
|
||||
} mu_srp_handle_t;
|
||||
|
||||
int mu_srp_init(mu_srp_handle_t *hd, mu_ng_type_t ng);
|
||||
|
||||
void mu_srp_free(mu_srp_handle_t *hd);
|
||||
/* Returns B (pub key) and salt
|
||||
*
|
||||
* *bytes_B MUST NOT BE FREED BY THE CALLER
|
||||
* *bytes_salt MUST NOT BE FREE BY THE CALLER
|
||||
*
|
||||
*/
|
||||
int mu_srp_srv_pubkey(mu_srp_handle_t *hd, const char *username, const char *pass, int pass_len, int salt_len,
|
||||
char **bytes_B, int *len_B, char **bytes_salt);
|
||||
|
||||
/* Set the Salt and Verifier pre-generated for a given password.
|
||||
* This should be used only if the actual password is not available.
|
||||
* The public key can then be generated using mu_srp_srv_pubkey_from_salt_verifier()
|
||||
* and not mu_srp_srv_pubkey()
|
||||
*/
|
||||
int mu_srp_set_salt_verifier(mu_srp_handle_t *hd, const char *salt, int salt_len,
|
||||
const char *verifier, int verifier_len);
|
||||
|
||||
/* Returns B (pub key) when the salt and verifier are set using mu_srp_set_salt_verifier()
|
||||
*
|
||||
* *bytes_B MUST NOT BE FREED BY THE CALLER
|
||||
*/
|
||||
int mu_srp_srv_pubkey_from_salt_verifier(mu_srp_handle_t *hd, char **bytes_B, int *len_B);
|
||||
|
||||
/* Returns bytes_key
|
||||
* *bytes_key MUST NOT BE FREED BY THE CALLER
|
||||
*/
|
||||
int mu_srp_get_session_key(mu_srp_handle_t *hd, char *bytes_A, int len_A, char **bytes_key, int *len_key);
|
||||
|
||||
/* Exchange proofs
|
||||
* Returns 1 if user's proof is ok. Also 1 when is returned, bytes_host_proof contains our proof.
|
||||
*
|
||||
* bytes_user_proof is parameter in
|
||||
* bytes_host_proof is parameter out (should be SHA512_DIGEST_LENGTH) bytes in size
|
||||
*/
|
||||
int mu_srp_exchange_proofs(mu_srp_handle_t *hd, char *username, char *bytes_user_proof, char *bytes_host_proof);
|
||||
|
||||
|
||||
|
||||
#endif /* ! _MU_SRP_H_ */
|
|
@ -0,0 +1,279 @@
|
|||
/**
|
||||
* \brief Multi-precision integer library, ESP-IDF hardware accelerated parts
|
||||
*
|
||||
* based on mbedTLS implementation
|
||||
*
|
||||
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
||||
* Additions Copyright (C) 2016-2020, Espressif Systems (Shanghai) PTE Ltd
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "soc/hwcrypto_periph.h"
|
||||
#include "soc/hwcrypto_reg.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include <mbedtls/bignum.h>
|
||||
#include "bignum_impl.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/lock.h>
|
||||
|
||||
static _lock_t mpi_lock;
|
||||
|
||||
/* Round up number of words to nearest
|
||||
512 bit (16 word) block count.
|
||||
*/
|
||||
size_t esp_mpi_hardware_words(size_t words)
|
||||
{
|
||||
return (words + 0xF) & ~0xF;
|
||||
}
|
||||
|
||||
void esp_mpi_enable_hardware_hw_op( void )
|
||||
{
|
||||
/* newlib locks lazy initialize on ESP-IDF */
|
||||
_lock_acquire(&mpi_lock);
|
||||
|
||||
/* Enable RSA hardware */
|
||||
periph_module_enable(PERIPH_RSA_MODULE);
|
||||
DPORT_REG_CLR_BIT(DPORT_RSA_PD_CTRL_REG, DPORT_RSA_PD);
|
||||
|
||||
while (DPORT_REG_READ(RSA_CLEAN_REG) != 1)
|
||||
{ }
|
||||
// Note: from enabling RSA clock to here takes about 1.3us
|
||||
}
|
||||
|
||||
void esp_mpi_disable_hardware_hw_op( void )
|
||||
{
|
||||
DPORT_REG_SET_BIT(DPORT_RSA_PD_CTRL_REG, DPORT_RSA_PD);
|
||||
|
||||
/* Disable RSA hardware */
|
||||
periph_module_disable(PERIPH_RSA_MODULE);
|
||||
|
||||
_lock_release(&mpi_lock);
|
||||
}
|
||||
|
||||
|
||||
/* Copy mbedTLS MPI bignum 'mpi' to hardware memory block at 'mem_base'.
|
||||
|
||||
If hw_words is higher than the number of words in the bignum then
|
||||
these additional words will be zeroed in the memory buffer.
|
||||
|
||||
*/
|
||||
static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, size_t hw_words)
|
||||
{
|
||||
uint32_t *pbase = (uint32_t *)mem_base;
|
||||
uint32_t copy_words = MIN(hw_words, mpi->n);
|
||||
|
||||
/* Copy MPI data to memory block registers */
|
||||
for (int i = 0; i < copy_words; i++) {
|
||||
pbase[i] = mpi->p[i];
|
||||
}
|
||||
|
||||
/* Zero any remaining memory block data */
|
||||
for (int i = copy_words; i < hw_words; i++) {
|
||||
pbase[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read mbedTLS MPI bignum back from hardware memory block.
|
||||
|
||||
Reads num_words words from block.
|
||||
|
||||
Bignum 'x' should already be grown to at least num_words by caller (can be done while
|
||||
calculation is in progress, to save some cycles)
|
||||
*/
|
||||
static inline void mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_words)
|
||||
{
|
||||
assert(x->n >= num_words);
|
||||
|
||||
/* Copy data from memory block registers */
|
||||
esp_dport_access_read_buffer(x->p, mem_base, num_words);
|
||||
|
||||
/* Zero any remaining limbs in the bignum, if the buffer is bigger
|
||||
than num_words */
|
||||
for (size_t i = num_words; i < x->n; i++) {
|
||||
x->p[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Begin an RSA operation. op_reg specifies which 'START' register
|
||||
to write to.
|
||||
*/
|
||||
static inline void start_op(uint32_t op_reg)
|
||||
{
|
||||
/* Clear interrupt status */
|
||||
DPORT_REG_WRITE(RSA_INTERRUPT_REG, 1);
|
||||
|
||||
/* Note: above REG_WRITE includes a memw, so we know any writes
|
||||
to the memory blocks are also complete. */
|
||||
|
||||
DPORT_REG_WRITE(op_reg, 1);
|
||||
}
|
||||
|
||||
/* Wait for an RSA operation to complete.
|
||||
*/
|
||||
static inline void wait_op_complete(void)
|
||||
{
|
||||
while (DPORT_REG_READ(RSA_INTERRUPT_REG) != 1)
|
||||
{ }
|
||||
|
||||
/* clear the interrupt */
|
||||
DPORT_REG_WRITE(RSA_INTERRUPT_REG, 1);
|
||||
}
|
||||
|
||||
/* Read result from last MPI operation */
|
||||
void esp_mpi_read_result_hw_op(mbedtls_mpi *Z, size_t z_words)
|
||||
{
|
||||
wait_op_complete();
|
||||
mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, z_words);
|
||||
}
|
||||
|
||||
/* Z = (X * Y) mod M */
|
||||
void esp_mpi_mul_mpi_mod_hw_op(const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M, const mbedtls_mpi *Rinv, mbedtls_mpi_uint Mprime, size_t hw_words)
|
||||
{
|
||||
/* Load M, X, Rinv, Mprime (Mprime is mod 2^32) */
|
||||
mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, hw_words);
|
||||
mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, hw_words);
|
||||
mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, Rinv, hw_words);
|
||||
DPORT_REG_WRITE(RSA_M_DASH_REG, (uint32_t)Mprime);
|
||||
|
||||
/* "mode" register loaded with number of 512-bit blocks, minus 1 */
|
||||
DPORT_REG_WRITE(RSA_MULT_MODE_REG, (hw_words / 16) - 1);
|
||||
|
||||
/* Execute first stage montgomery multiplication */
|
||||
start_op(RSA_MULT_START_REG);
|
||||
|
||||
wait_op_complete();
|
||||
|
||||
/* execute second stage */
|
||||
/* Load Y to X input memory block, rerun */
|
||||
mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, Y, hw_words);
|
||||
|
||||
start_op(RSA_MULT_START_REG);
|
||||
}
|
||||
|
||||
/* Z = X * Y */
|
||||
void esp_mpi_mul_mpi_hw_op(const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t hw_words)
|
||||
{
|
||||
/* Copy X (right-extended) & Y (left-extended) to memory block */
|
||||
mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, hw_words);
|
||||
mpi_to_mem_block(RSA_MEM_Z_BLOCK_BASE + hw_words * 4, Y, hw_words);
|
||||
/* NB: as Y is left-extended, we don't zero the bottom words_mult words of Y block.
|
||||
This is OK for now because zeroing is done by hardware when we do esp_mpi_acquire_hardware().
|
||||
*/
|
||||
|
||||
DPORT_REG_WRITE(RSA_M_DASH_REG, 0);
|
||||
|
||||
/* "mode" register loaded with number of 512-bit blocks in result,
|
||||
plus 7 (for range 9-12). (this is ((N~ / 32) - 1) + 8))
|
||||
*/
|
||||
DPORT_REG_WRITE(RSA_MULT_MODE_REG, ((hw_words * 2) / 16) + 7);
|
||||
|
||||
start_op(RSA_MULT_START_REG);
|
||||
|
||||
}
|
||||
|
||||
|
||||
int esp_mont_hw_op(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M,
|
||||
mbedtls_mpi_uint Mprime,
|
||||
size_t hw_words,
|
||||
bool again)
|
||||
{
|
||||
// Note Z may be the same pointer as X or Y
|
||||
int ret = 0;
|
||||
|
||||
// montgomery mult prepare
|
||||
if (again == false) {
|
||||
mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, hw_words);
|
||||
DPORT_REG_WRITE(RSA_M_DASH_REG, Mprime);
|
||||
DPORT_REG_WRITE(RSA_MULT_MODE_REG, hw_words / 16 - 1);
|
||||
}
|
||||
|
||||
mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, hw_words);
|
||||
mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, Y, hw_words);
|
||||
|
||||
start_op(RSA_MULT_START_REG);
|
||||
Z->s = 1; // The sign of Z will be = M->s (but M->s is always 1)
|
||||
MBEDTLS_MPI_CHK( mbedtls_mpi_grow(Z, hw_words) );
|
||||
|
||||
wait_op_complete();
|
||||
|
||||
/* Read back the result */
|
||||
mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, hw_words);
|
||||
|
||||
|
||||
/* from HAC 14.36 - 3. If Z >= M then Z = Z - M */
|
||||
if (mbedtls_mpi_cmp_mpi(Z, M) >= 0) {
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(Z, Z, M));
|
||||
}
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Special-case of mbedtls_mpi_mult_mpi(), where we use hardware montgomery mod
|
||||
multiplication to calculate an mbedtls_mpi_mult_mpi result where either
|
||||
A or B are >2048 bits so can't use the standard multiplication method.
|
||||
|
||||
Result (z_words, based on A bits + B bits) must still be less than 4096 bits.
|
||||
|
||||
This case is simpler than the general case modulo multiply of
|
||||
esp_mpi_mul_mpi_mod() because we can control the other arguments:
|
||||
|
||||
* Modulus is chosen with M=(2^num_bits - 1) (ie M=R-1), so output
|
||||
isn't actually modulo anything.
|
||||
* Mprime and Rinv are therefore predictable as follows:
|
||||
Mprime = 1
|
||||
Rinv = 1
|
||||
|
||||
(See RSA Accelerator section in Technical Reference for more about Mprime, Rinv)
|
||||
*/
|
||||
void esp_mpi_mult_mpi_failover_mod_mult_hw_op(const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words)
|
||||
{
|
||||
size_t hw_words = num_words;
|
||||
|
||||
/* M = 2^num_words - 1, so block is entirely FF */
|
||||
for (int i = 0; i < hw_words; i++) {
|
||||
DPORT_REG_WRITE(RSA_MEM_M_BLOCK_BASE + i * 4, UINT32_MAX);
|
||||
}
|
||||
/* Mprime = 1 */
|
||||
DPORT_REG_WRITE(RSA_M_DASH_REG, 1);
|
||||
|
||||
/* "mode" register loaded with number of 512-bit blocks, minus 1 */
|
||||
DPORT_REG_WRITE(RSA_MULT_MODE_REG, (hw_words / 16) - 1);
|
||||
|
||||
/* Load X */
|
||||
mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, hw_words);
|
||||
|
||||
/* Rinv = 1, write first word */
|
||||
DPORT_REG_WRITE(RSA_MEM_RB_BLOCK_BASE, 1);
|
||||
|
||||
/* Zero out rest of the Rinv words */
|
||||
for (int i = 1; i < hw_words; i++) {
|
||||
DPORT_REG_WRITE(RSA_MEM_RB_BLOCK_BASE + i * 4, 0);
|
||||
}
|
||||
|
||||
start_op(RSA_MULT_START_REG);
|
||||
|
||||
wait_op_complete();
|
||||
|
||||
/* finish the modular multiplication */
|
||||
/* Load Y to X input memory block, rerun */
|
||||
mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, Y, hw_words);
|
||||
|
||||
start_op(RSA_MULT_START_REG);
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2015-2020 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_next "mbedtls/bignum.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/**
|
||||
* This is a wrapper for the main mbedtls/bignum.h. This wrapper
|
||||
* provides a few additional ESP32-only functions.
|
||||
*
|
||||
* This is because we don't set MBEDTLS_BIGNUM_ALT in the same way we
|
||||
* do for AES, SHA, etc. Because we still use most of the bignum.h
|
||||
* implementation and just replace a few hardware accelerated
|
||||
* functions (see MBEDTLS_MPI_EXP_MOD_ALT & MBEDTLS_MPI_MUL_MPI_ALT in
|
||||
* esp_config.h).
|
||||
*
|
||||
* @note Unlike the other hardware accelerator support functions in esp32/hwcrypto, there is no
|
||||
* generic "hwcrypto/bignum.h" header for using these functions without mbedTLS. The reason for this
|
||||
* is that all of the function implementations depend strongly upon the mbedTLS MPI implementation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Lock access to RSA Accelerator (MPI/bignum operations)
|
||||
*
|
||||
* RSA Accelerator hardware unit can only be used by one
|
||||
* consumer at a time.
|
||||
*
|
||||
* @note This function is non-recursive (do not call it twice from the
|
||||
* same task.)
|
||||
*
|
||||
* @note You do not need to call this if you are using the mbedTLS bignum.h
|
||||
* API or esp_mpi_xxx functions. This function is only needed if you
|
||||
* want to call ROM RSA functions or access the registers directly.
|
||||
*
|
||||
*/
|
||||
void esp_mpi_acquire_hardware(void);
|
||||
|
||||
/**
|
||||
* @brief Unlock access to RSA Accelerator (MPI/bignum operations)
|
||||
*
|
||||
* Has to be called once for each call to esp_mpi_acquire_hardware().
|
||||
*
|
||||
* @note You do not need to call this if you are using the mbedTLS bignum.h
|
||||
* API or esp_mpi_xxx functions. This function is only needed if you
|
||||
* want to call ROM RSA functions or access the registers directly.
|
||||
*/
|
||||
void esp_mpi_release_hardware(void);
|
||||
|
||||
//#if CONFIG_MBEDTLS_HARDWARE_MPI
|
||||
|
||||
/* @brief MPI modular mupltiplication function
|
||||
*
|
||||
* Calculates Z = (X * Y) mod M using MPI hardware acceleration.
|
||||
*
|
||||
* This is not part of the standard mbedTLS bignum API.
|
||||
*
|
||||
* @note All of X, Y & Z should be less than 4096 bit long or an error is returned.
|
||||
*
|
||||
* @param Z Result bignum, should be pre-initialised with mbedtls_mpi_init().
|
||||
* @param X First multiplication argument.
|
||||
* @param Y Second multiplication argument.
|
||||
* @param M Modulus value for result.
|
||||
*
|
||||
* @return 0 on success, mbedTLS MPI error codes on failure.
|
||||
*/
|
||||
int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M);
|
||||
|
||||
//#endif // CONFIG_MBEDTLS_HARDWARE_MPI
|
|
@ -0,0 +1,92 @@
|
|||
#ifndef _ESP_BIGNUM_H_
|
||||
#define _ESP_BIGNUM_H_
|
||||
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Use montgomery exponentiation (HAC 14.94) for calculating X ^ Y mod M,
|
||||
this may be faster for some targets. The hardware acceleration support for modular
|
||||
exponentiation on the ESP32 is slow for public key operations, so use montgomery
|
||||
exponentiation instead.
|
||||
*/
|
||||
|
||||
#define CONFIG_IDF_TARGET_ESP32 1
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define ESP_MPI_USE_MONT_EXP
|
||||
|
||||
#define MBEDTLS_MPI_EXP_MOD_ALT
|
||||
//#define MBEDTLS_MPI_MUL_MPI_ALT
|
||||
#endif
|
||||
|
||||
|
||||
int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M, mbedtls_mpi *_Rinv );
|
||||
|
||||
/**
|
||||
* @brief Enable the MPI hardware and acquire the lock
|
||||
*
|
||||
*/
|
||||
void esp_mpi_enable_hardware_hw_op( void );
|
||||
|
||||
/**
|
||||
* @brief Disable the MPI hardware and release the lock
|
||||
*
|
||||
*/
|
||||
void esp_mpi_disable_hardware_hw_op( void );
|
||||
|
||||
/**
|
||||
* @brief Calculate the number of words needed to represent the input word in hardware
|
||||
*
|
||||
* @param words The number of words to be represented
|
||||
*
|
||||
* @return size_t Number of words required
|
||||
*/
|
||||
size_t esp_mpi_hardware_words(size_t words);
|
||||
|
||||
/**
|
||||
* @brief Starts a (X * Y) Mod M calculation in hardware. Rinv and M_prime needs to be precalculated in software.
|
||||
*
|
||||
*/
|
||||
void esp_mpi_mul_mpi_mod_hw_op(const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M, const mbedtls_mpi *Rinv, mbedtls_mpi_uint Mprime, size_t hw_words);
|
||||
|
||||
/**
|
||||
* @brief Starts a (X * Y) calculation in hardware.
|
||||
*
|
||||
*/
|
||||
void esp_mpi_mul_mpi_hw_op(const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words);
|
||||
|
||||
/**
|
||||
* @brief Special-case of (X * Y), where we use hardware montgomery mod
|
||||
multiplication to calculate result where either A or B are >2048 bits so
|
||||
can't use the standard multiplication method.
|
||||
*
|
||||
*/
|
||||
void esp_mpi_mult_mpi_failover_mod_mult_hw_op(const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words);
|
||||
|
||||
/**
|
||||
* @brief Read out the result from the previous calculation.
|
||||
*
|
||||
*/
|
||||
void esp_mpi_read_result_hw_op(mbedtls_mpi *Z, size_t z_words);
|
||||
|
||||
#ifdef ESP_MPI_USE_MONT_EXP
|
||||
/**
|
||||
* @brief Starts a montgomery multiplication calculation in hardware
|
||||
*
|
||||
*/
|
||||
int esp_mont_hw_op(mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M,
|
||||
mbedtls_mpi_uint Mprime,
|
||||
size_t hw_words,
|
||||
bool again);
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* @brief Starts a (X ^ Y) Mod M calculation in hardware. Rinv and M_prime needs to be precalculated in software.
|
||||
*
|
||||
*/
|
||||
void esp_mpi_exp_mpi_mod_hw_op(const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M, const mbedtls_mpi *Rinv, mbedtls_mpi_uint Mprime, size_t hw_words);
|
||||
|
||||
#endif //ESP_MPI_USE_MONT_EXP
|
||||
|
||||
#endif
|
|
@ -0,0 +1,536 @@
|
|||
/**
|
||||
* \brief Multi-precision integer library, ESP32 hardware accelerated parts
|
||||
*
|
||||
* based on mbedTLS implementation
|
||||
*
|
||||
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
||||
* Additions Copyright (C) 2016, Espressif Systems (Shanghai) PTE Ltd
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <malloc.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
//#include "soc/hwcrypto_periph.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_attr.h"
|
||||
#include "bignum_impl.h"
|
||||
//#include "soc/soc_caps.h"
|
||||
|
||||
#include <mbedtls/bignum.h>
|
||||
|
||||
// wangbin added
|
||||
#if !defined(SOC_RSA_MAX_BIT_LEN)
|
||||
#define SOC_RSA_MAX_BIT_LEN (4096)
|
||||
#endif
|
||||
|
||||
|
||||
/* Some implementation notes:
|
||||
*
|
||||
* - Naming convention x_words, y_words, z_words for number of words (limbs) used in a particular
|
||||
* bignum. This number may be less than the size of the bignum
|
||||
*
|
||||
* - Naming convention hw_words for the hardware length of the operation. This number maybe be rounded up
|
||||
* for targets that requres this (e.g. ESP32), and may be larger than any of the numbers
|
||||
* involved in the calculation.
|
||||
*
|
||||
* - Timing behaviour of these functions will depend on the length of the inputs. This is fundamentally
|
||||
* the same constraint as the software mbedTLS implementations, and relies on the same
|
||||
* countermeasures (exponent blinding, etc) which are used in mbedTLS.
|
||||
*/
|
||||
|
||||
static const __attribute__((unused)) char *TAG = "bignum";
|
||||
|
||||
#define ciL (sizeof(mbedtls_mpi_uint)) /* chars in limb */
|
||||
#define biL (ciL << 3) /* bits in limb */
|
||||
|
||||
|
||||
/* Convert bit count to word count
|
||||
*/
|
||||
static inline size_t bits_to_words(size_t bits)
|
||||
{
|
||||
return (bits + 31) / 32;
|
||||
}
|
||||
|
||||
/* Return the number of words actually used to represent an mpi
|
||||
number.
|
||||
*/
|
||||
#if defined(MBEDTLS_MPI_EXP_MOD_ALT)
|
||||
static size_t mpi_words(const mbedtls_mpi *mpi)
|
||||
{
|
||||
for (size_t i = mpi->n; i > 0; i--) {
|
||||
if (mpi->p[i - 1] != 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif //MBEDTLS_MPI_EXP_MOD_ALT
|
||||
|
||||
/**
|
||||
*
|
||||
* There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1,
|
||||
* where B^-1(B-1) mod N=1. Actually, only the least significant part of
|
||||
* N' is needed, hence the definition N0'=N' mod b. We reproduce below the
|
||||
* simple algorithm from an article by Dusse and Kaliski to efficiently
|
||||
* find N0' from N0 and b
|
||||
*/
|
||||
static mbedtls_mpi_uint modular_inverse(const mbedtls_mpi *M)
|
||||
{
|
||||
int i;
|
||||
uint64_t t = 1;
|
||||
uint64_t two_2_i_minus_1 = 2; /* 2^(i-1) */
|
||||
uint64_t two_2_i = 4; /* 2^i */
|
||||
uint64_t N = M->p[0];
|
||||
|
||||
for (i = 2; i <= 32; i++) {
|
||||
if ((mbedtls_mpi_uint) N * t % two_2_i >= two_2_i_minus_1) {
|
||||
t += two_2_i_minus_1;
|
||||
}
|
||||
|
||||
two_2_i_minus_1 <<= 1;
|
||||
two_2_i <<= 1;
|
||||
}
|
||||
|
||||
return (mbedtls_mpi_uint)(UINT32_MAX - t + 1);
|
||||
}
|
||||
|
||||
/* Calculate Rinv = RR^2 mod M, where:
|
||||
*
|
||||
* R = b^n where b = 2^32, n=num_words,
|
||||
* R = 2^N (where N=num_bits)
|
||||
* RR = R^2 = 2^(2*N) (where N=num_bits=num_words*32)
|
||||
*
|
||||
* This calculation is computationally expensive (mbedtls_mpi_mod_mpi)
|
||||
* so caller should cache the result where possible.
|
||||
*
|
||||
* DO NOT call this function while holding esp_mpi_enable_hardware_hw_op().
|
||||
*
|
||||
*/
|
||||
static int calculate_rinv(mbedtls_mpi *Rinv, const mbedtls_mpi *M, int num_words)
|
||||
{
|
||||
int ret;
|
||||
size_t num_bits = num_words * 32;
|
||||
mbedtls_mpi RR;
|
||||
mbedtls_mpi_init(&RR);
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&RR, num_bits * 2, 1));
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(Rinv, &RR, M));
|
||||
|
||||
cleanup:
|
||||
mbedtls_mpi_free(&RR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Z = (X * Y) mod M
|
||||
|
||||
Not an mbedTLS function
|
||||
*/
|
||||
int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
size_t x_bits = mbedtls_mpi_bitlen(X);
|
||||
size_t y_bits = mbedtls_mpi_bitlen(Y);
|
||||
size_t m_bits = mbedtls_mpi_bitlen(M);
|
||||
size_t z_bits = MIN(m_bits, x_bits + y_bits);
|
||||
size_t x_words = bits_to_words(x_bits);
|
||||
size_t y_words = bits_to_words(y_bits);
|
||||
size_t m_words = bits_to_words(m_bits);
|
||||
size_t z_words = bits_to_words(z_bits);
|
||||
size_t hw_words = esp_mpi_hardware_words(MAX(x_words, MAX(y_words, m_words))); /* longest operand */
|
||||
mbedtls_mpi Rinv;
|
||||
mbedtls_mpi_uint Mprime;
|
||||
|
||||
/* Calculate and load the first stage montgomery multiplication */
|
||||
mbedtls_mpi_init(&Rinv);
|
||||
MBEDTLS_MPI_CHK(calculate_rinv(&Rinv, M, hw_words));
|
||||
Mprime = modular_inverse(M);
|
||||
|
||||
esp_mpi_enable_hardware_hw_op();
|
||||
/* Load and start a (X * Y) mod M calculation */
|
||||
esp_mpi_mul_mpi_mod_hw_op(X, Y, M, &Rinv, Mprime, hw_words);
|
||||
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Z, z_words));
|
||||
|
||||
esp_mpi_read_result_hw_op(Z, z_words);
|
||||
Z->s = X->s * Y->s;
|
||||
|
||||
cleanup:
|
||||
mbedtls_mpi_free(&Rinv);
|
||||
esp_mpi_disable_hardware_hw_op();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_MPI_EXP_MOD_ALT)
|
||||
|
||||
#ifdef ESP_MPI_USE_MONT_EXP
|
||||
/*
|
||||
* Return the most significant one-bit.
|
||||
*/
|
||||
static size_t mbedtls_mpi_msb( const mbedtls_mpi *X )
|
||||
{
|
||||
int i, j;
|
||||
if (X != NULL && X->n != 0) {
|
||||
for (i = X->n - 1; i >= 0; i--) {
|
||||
if (X->p[i] != 0) {
|
||||
for (j = biL - 1; j >= 0; j--) {
|
||||
if ((X->p[i] & (1 << j)) != 0) {
|
||||
return (i * biL) + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Montgomery exponentiation: Z = X ^ Y mod M (HAC 14.94)
|
||||
*/
|
||||
static int mpi_montgomery_exp_calc( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M,
|
||||
mbedtls_mpi *Rinv,
|
||||
size_t hw_words,
|
||||
mbedtls_mpi_uint Mprime )
|
||||
{
|
||||
int ret = 0;
|
||||
mbedtls_mpi X_, one;
|
||||
|
||||
mbedtls_mpi_init(&X_);
|
||||
mbedtls_mpi_init(&one);
|
||||
if ( ( ( ret = mbedtls_mpi_grow(&one, hw_words) ) != 0 ) ||
|
||||
( ( ret = mbedtls_mpi_set_bit(&one, 0, 1) ) != 0 ) ) {
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
// Algorithm from HAC 14.94
|
||||
{
|
||||
// 0 determine t (highest bit set in y)
|
||||
int t = mbedtls_mpi_msb(Y);
|
||||
|
||||
esp_mpi_enable_hardware_hw_op();
|
||||
|
||||
// 1.1 x_ = mont(x, R^2 mod m)
|
||||
// = mont(x, rb)
|
||||
MBEDTLS_MPI_CHK( esp_mont_hw_op(&X_, X, Rinv, M, Mprime, hw_words, false) );
|
||||
|
||||
// 1.2 z = R mod m
|
||||
// now z = R mod m = Mont (R^2 mod m, 1) mod M (as Mont(x) = X&R^-1 mod M)
|
||||
MBEDTLS_MPI_CHK( esp_mont_hw_op(Z, Rinv, &one, M, Mprime, hw_words, true) );
|
||||
|
||||
// 2 for i from t down to 0
|
||||
for (int i = t; i >= 0; i--) {
|
||||
// 2.1 z = mont(z,z)
|
||||
if (i != t) { // skip on the first iteration as is still unity
|
||||
MBEDTLS_MPI_CHK( esp_mont_hw_op(Z, Z, Z, M, Mprime, hw_words, true) );
|
||||
}
|
||||
|
||||
// 2.2 if y[i] = 1 then z = mont(A, x_)
|
||||
if (mbedtls_mpi_get_bit(Y, i)) {
|
||||
MBEDTLS_MPI_CHK( esp_mont_hw_op(Z, Z, &X_, M, Mprime, hw_words, true) );
|
||||
}
|
||||
}
|
||||
|
||||
// 3 z = Mont(z, 1)
|
||||
MBEDTLS_MPI_CHK( esp_mont_hw_op(Z, Z, &one, M, Mprime, hw_words, true) );
|
||||
}
|
||||
|
||||
cleanup:
|
||||
esp_mpi_disable_hardware_hw_op();
|
||||
|
||||
cleanup2:
|
||||
mbedtls_mpi_free(&X_);
|
||||
mbedtls_mpi_free(&one);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif //USE_MONT_EXPONENATIATION
|
||||
|
||||
/*
|
||||
* Z = X ^ Y mod M
|
||||
*
|
||||
* _Rinv is optional pre-calculated version of Rinv (via calculate_rinv()).
|
||||
*
|
||||
* (See RSA Accelerator section in Technical Reference for more about Mprime, Rinv)
|
||||
*
|
||||
*/
|
||||
// wangbin changed mbedtls_mpi_exp_mod -> esp_mpi_exp_mod
|
||||
int esp_mpi_exp_mod( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M, mbedtls_mpi *_Rinv )
|
||||
{
|
||||
int ret = 0;
|
||||
size_t x_words = mpi_words(X);
|
||||
size_t y_words = mpi_words(Y);
|
||||
size_t m_words = mpi_words(M);
|
||||
|
||||
|
||||
/* "all numbers must be the same length", so choose longest number
|
||||
as cardinal length of operation...
|
||||
*/
|
||||
size_t num_words = esp_mpi_hardware_words(MAX(m_words, MAX(x_words, y_words)));
|
||||
|
||||
mbedtls_mpi Rinv_new; /* used if _Rinv == NULL */
|
||||
mbedtls_mpi *Rinv; /* points to _Rinv (if not NULL) othwerwise &RR_new */
|
||||
mbedtls_mpi_uint Mprime;
|
||||
|
||||
if (mbedtls_mpi_cmp_int(M, 0) <= 0 || (M->p[0] & 1) == 0) {
|
||||
return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
if (mbedtls_mpi_cmp_int(Y, 0) < 0) {
|
||||
return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
if (mbedtls_mpi_cmp_int(Y, 0) == 0) {
|
||||
return mbedtls_mpi_lset(Z, 1);
|
||||
}
|
||||
|
||||
if (num_words * 32 > SOC_RSA_MAX_BIT_LEN) {
|
||||
return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE;
|
||||
}
|
||||
|
||||
/* Determine RR pointer, either _RR for cached value
|
||||
or local RR_new */
|
||||
if (_Rinv == NULL) {
|
||||
mbedtls_mpi_init(&Rinv_new);
|
||||
Rinv = &Rinv_new;
|
||||
} else {
|
||||
Rinv = _Rinv;
|
||||
}
|
||||
if (Rinv->p == NULL) {
|
||||
MBEDTLS_MPI_CHK(calculate_rinv(Rinv, M, num_words));
|
||||
}
|
||||
|
||||
Mprime = modular_inverse(M);
|
||||
|
||||
// Montgomery exponentiation: Z = X ^ Y mod M (HAC 14.94)
|
||||
#ifdef ESP_MPI_USE_MONT_EXP
|
||||
ret = mpi_montgomery_exp_calc(Z, X, Y, M, Rinv, num_words, Mprime) ;
|
||||
MBEDTLS_MPI_CHK(ret);
|
||||
#else
|
||||
esp_mpi_enable_hardware_hw_op();
|
||||
|
||||
esp_mpi_exp_mpi_mod_hw_op(X, Y, M, Rinv, Mprime, num_words);
|
||||
ret = mbedtls_mpi_grow(Z, m_words);
|
||||
if (ret != 0) {
|
||||
esp_mpi_disable_hardware_hw_op();
|
||||
goto cleanup;
|
||||
}
|
||||
esp_mpi_read_result_hw_op(Z, m_words);
|
||||
esp_mpi_disable_hardware_hw_op();
|
||||
#endif
|
||||
|
||||
// Compensate for negative X
|
||||
if (X->s == -1 && (Y->p[0] & 1) != 0) {
|
||||
Z->s = -1;
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(Z, M, Z));
|
||||
} else {
|
||||
Z->s = 1;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (_Rinv == NULL) {
|
||||
mbedtls_mpi_free(&Rinv_new);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_MPI_EXP_MOD_ALT */
|
||||
|
||||
|
||||
|
||||
#if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */
|
||||
|
||||
static int mpi_mult_mpi_failover_mod_mult( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t z_words);
|
||||
static int mpi_mult_mpi_overlong(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t y_words, size_t z_words);
|
||||
|
||||
/* Z = X * Y */
|
||||
int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y )
|
||||
{
|
||||
int ret = 0;
|
||||
size_t x_bits = mbedtls_mpi_bitlen(X);
|
||||
size_t y_bits = mbedtls_mpi_bitlen(Y);
|
||||
size_t x_words = bits_to_words(x_bits);
|
||||
size_t y_words = bits_to_words(y_bits);
|
||||
size_t z_words = bits_to_words(x_bits + y_bits);
|
||||
size_t hw_words = esp_mpi_hardware_words(MAX(x_words, y_words)); // length of one operand in hardware
|
||||
|
||||
/* Short-circuit eval if either argument is 0 or 1.
|
||||
|
||||
This is needed as the mpi modular division
|
||||
argument will sometimes call in here when one
|
||||
argument is too large for the hardware unit, but the other
|
||||
argument is zero or one.
|
||||
*/
|
||||
if (x_bits == 0 || y_bits == 0) {
|
||||
mbedtls_mpi_lset(Z, 0);
|
||||
return 0;
|
||||
}
|
||||
if (x_bits == 1) {
|
||||
ret = mbedtls_mpi_copy(Z, Y);
|
||||
Z->s *= X->s;
|
||||
return ret;
|
||||
}
|
||||
if (y_bits == 1) {
|
||||
ret = mbedtls_mpi_copy(Z, X);
|
||||
Z->s *= Y->s;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Grow Z to result size early, avoid interim allocations */
|
||||
MBEDTLS_MPI_CHK( mbedtls_mpi_grow(Z, z_words) );
|
||||
|
||||
/* If either factor is over 2048 bits, we can't use the standard hardware multiplier
|
||||
(it assumes result is double longest factor, and result is max 4096 bits.)
|
||||
|
||||
However, we can fail over to mod_mult for up to 4096 bits of result (modulo
|
||||
multiplication doesn't have the same restriction, so result is simply the
|
||||
number of bits in X plus number of bits in in Y.)
|
||||
*/
|
||||
if (hw_words * 32 > SOC_RSA_MAX_BIT_LEN/2) {
|
||||
if (z_words * 32 <= SOC_RSA_MAX_BIT_LEN) {
|
||||
/* Note: it's possible to use mpi_mult_mpi_overlong
|
||||
for this case as well, but it's very slightly
|
||||
slower and requires a memory allocation.
|
||||
*/
|
||||
return mpi_mult_mpi_failover_mod_mult(Z, X, Y, z_words);
|
||||
} else {
|
||||
/* Still too long for the hardware unit... */
|
||||
if (y_words > x_words) {
|
||||
return mpi_mult_mpi_overlong(Z, X, Y, y_words, z_words);
|
||||
} else {
|
||||
return mpi_mult_mpi_overlong(Z, Y, X, x_words, z_words);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, we can use the (faster) multiply hardware unit */
|
||||
esp_mpi_enable_hardware_hw_op();
|
||||
|
||||
esp_mpi_mul_mpi_hw_op(X, Y, hw_words);
|
||||
esp_mpi_read_result_hw_op(Z, z_words);
|
||||
|
||||
esp_mpi_disable_hardware_hw_op();
|
||||
|
||||
Z->s = X->s * Y->s;
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Deal with the case when X & Y are too long for the hardware unit, by splitting one operand
|
||||
into two halves.
|
||||
|
||||
Y must be the longer operand
|
||||
|
||||
Slice Y into Yp, Ypp such that:
|
||||
Yp = lower 'b' bits of Y
|
||||
Ypp = upper 'b' bits of Y (right shifted)
|
||||
|
||||
Such that
|
||||
Z = X * Y
|
||||
Z = X * (Yp + Ypp<<b)
|
||||
Z = (X * Yp) + (X * Ypp<<b)
|
||||
|
||||
Note that this function may recurse multiple times, if both X & Y
|
||||
are too long for the hardware multiplication unit.
|
||||
*/
|
||||
static int mpi_mult_mpi_overlong(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t y_words, size_t z_words)
|
||||
{
|
||||
int ret = 0;
|
||||
mbedtls_mpi Ztemp;
|
||||
/* Rather than slicing in two on bits we slice on limbs (32 bit words) */
|
||||
const size_t words_slice = y_words / 2;
|
||||
/* Yp holds lower bits of Y (declared to reuse Y's array contents to save on copying) */
|
||||
const mbedtls_mpi Yp = {
|
||||
.p = Y->p,
|
||||
.n = words_slice,
|
||||
.s = Y->s
|
||||
};
|
||||
/* Ypp holds upper bits of Y, right shifted (also reuses Y's array contents) */
|
||||
const mbedtls_mpi Ypp = {
|
||||
.p = Y->p + words_slice,
|
||||
.n = y_words - words_slice,
|
||||
.s = Y->s
|
||||
};
|
||||
mbedtls_mpi_init(&Ztemp);
|
||||
|
||||
/* Get result Ztemp = Yp * X (need temporary variable Ztemp) */
|
||||
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi(&Ztemp, X, &Yp) );
|
||||
|
||||
/* Z = Ypp * Y */
|
||||
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi(Z, X, &Ypp) );
|
||||
|
||||
/* Z = Z << b */
|
||||
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l(Z, words_slice * 32) );
|
||||
|
||||
/* Z += Ztemp */
|
||||
MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi(Z, Z, &Ztemp) );
|
||||
|
||||
cleanup:
|
||||
mbedtls_mpi_free(&Ztemp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Special-case of mbedtls_mpi_mult_mpi(), where we use hardware montgomery mod
|
||||
multiplication to calculate an mbedtls_mpi_mult_mpi result where either
|
||||
A or B are >2048 bits so can't use the standard multiplication method.
|
||||
|
||||
Result (number of words, based on A bits + B bits) must still be less than 4096 bits.
|
||||
|
||||
This case is simpler than the general case modulo multiply of
|
||||
esp_mpi_mul_mpi_mod() because we can control the other arguments:
|
||||
|
||||
* Modulus is chosen with M=(2^num_bits - 1) (ie M=R-1), so output
|
||||
* Mprime and Rinv are therefore predictable as follows:
|
||||
isn't actually modulo anything.
|
||||
Mprime 1
|
||||
Rinv 1
|
||||
|
||||
(See RSA Accelerator section in Technical Reference for more about Mprime, Rinv)
|
||||
*/
|
||||
|
||||
static int mpi_mult_mpi_failover_mod_mult( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t z_words)
|
||||
{
|
||||
int ret;
|
||||
size_t hw_words = esp_mpi_hardware_words(z_words);
|
||||
|
||||
esp_mpi_enable_hardware_hw_op();
|
||||
|
||||
esp_mpi_mult_mpi_failover_mod_mult_hw_op(X, Y, hw_words );
|
||||
MBEDTLS_MPI_CHK( mbedtls_mpi_grow(Z, hw_words) );
|
||||
esp_mpi_read_result_hw_op(Z, hw_words);
|
||||
|
||||
Z->s = X->s * Y->s;
|
||||
cleanup:
|
||||
esp_mpi_disable_hardware_hw_op();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_MPI_MUL_MPI_ALT */
|
|
@ -0,0 +1,29 @@
|
|||
/************************ sha-private.h ************************/
|
||||
/***************** See RFC 6234 for details. *******************/
|
||||
#ifndef _SHA_PRIVATE__H
|
||||
#define _SHA_PRIVATE__H
|
||||
/*
|
||||
* These definitions are defined in FIPS 180-3, section 4.1.
|
||||
* Ch() and Maj() are defined identically in sections 4.1.1,
|
||||
* 4.1.2, and 4.1.3.
|
||||
*
|
||||
* The definitions used in FIPS 180-3 are as follows:
|
||||
*/
|
||||
|
||||
#ifndef USE_MODIFIED_MACROS
|
||||
#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
|
||||
#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||
#else /* USE_MODIFIED_MACROS */
|
||||
/*
|
||||
* The following definitions are equivalent and potentially faster.
|
||||
*/
|
||||
|
||||
#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
|
||||
#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
|
||||
|
||||
#endif /* USE_MODIFIED_MACROS */
|
||||
|
||||
#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))
|
||||
|
||||
#endif /* _SHA_PRIVATE__H */
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
/**************************** sha.h ****************************/
|
||||
/***************** See RFC 6234 for details. *******************/
|
||||
/*
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as
|
||||
authors of the code. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and
|
||||
the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor
|
||||
the names of specific contributors, may be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _SHA_H_
|
||||
#define _SHA_H_
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* This file implements the Secure Hash Algorithms
|
||||
* as defined in the U.S. National Institute of Standards
|
||||
* and Technology Federal Information Processing Standards
|
||||
* Publication (FIPS PUB) 180-3 published in October 2008
|
||||
* and formerly defined in its predecessors, FIPS PUB 180-1
|
||||
* and FIP PUB 180-2.
|
||||
*
|
||||
* A combined document showing all algorithms is available at
|
||||
* http://csrc.nist.gov/publications/fips/
|
||||
* fips180-3/fips180-3_final.pdf
|
||||
*
|
||||
* The five hashes are defined in these sizes:
|
||||
* SHA-1 20 byte / 160 bit
|
||||
* SHA-224 28 byte / 224 bit
|
||||
* SHA-256 32 byte / 256 bit
|
||||
* SHA-384 48 byte / 384 bit
|
||||
* SHA-512 64 byte / 512 bit
|
||||
*
|
||||
* Compilation Note:
|
||||
* These files may be compiled with two options:
|
||||
* USE_32BIT_ONLY - use 32-bit arithmetic only, for systems
|
||||
* without 64-bit integers
|
||||
*
|
||||
* USE_MODIFIED_MACROS - use alternate form of the SHA_Ch()
|
||||
* and SHA_Maj() macros that are equivalent
|
||||
* and potentially faster on many systems
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
/*
|
||||
* If you do not have the ISO standard stdint.h header file, then you
|
||||
* must typedef the following:
|
||||
* name meaning
|
||||
* uint64_t unsigned 64-bit integer
|
||||
* uint32_t unsigned 32-bit integer
|
||||
* uint8_t unsigned 8-bit integer (i.e., unsigned char)
|
||||
* int_least16_t integer of >= 16 bits
|
||||
*
|
||||
* See stdint-example.h
|
||||
*/
|
||||
|
||||
#ifndef _SHA_enum_
|
||||
#define _SHA_enum_
|
||||
/*
|
||||
* All SHA functions return one of these values.
|
||||
*/
|
||||
enum {
|
||||
shaSuccess = 0,
|
||||
shaNull, /* Null pointer parameter */
|
||||
shaInputTooLong, /* input data too long */
|
||||
shaStateError, /* called Input after FinalBits or Result */
|
||||
shaBadParam /* passed a bad parameter */
|
||||
};
|
||||
#endif /* _SHA_enum_ */
|
||||
|
||||
/*
|
||||
* These constants hold size information for each of the SHA
|
||||
* hashing operations
|
||||
*/
|
||||
enum {
|
||||
SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64,
|
||||
SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128,
|
||||
SHA512_Message_Block_Size = 128,
|
||||
USHA_Max_Message_Block_Size = SHA512_Message_Block_Size,
|
||||
|
||||
SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32,
|
||||
SHA384HashSize = 48, SHA512HashSize = 64,
|
||||
USHAMaxHashSize = SHA512HashSize,
|
||||
|
||||
SHA1HashSizeBits = 160, SHA224HashSizeBits = 224,
|
||||
SHA256HashSizeBits = 256, SHA384HashSizeBits = 384,
|
||||
SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits
|
||||
};
|
||||
|
||||
/*
|
||||
* These constants are used in the USHA (Unified SHA) functions.
|
||||
*/
|
||||
typedef enum SHAversion {
|
||||
SHA1, SHA224, SHA256, SHA384, SHA512
|
||||
} SHAversion;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-1
|
||||
* hashing operation.
|
||||
*/
|
||||
typedef struct SHA1Context {
|
||||
uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
|
||||
|
||||
uint32_t Length_High; /* Message length in bits */
|
||||
uint32_t Length_Low; /* Message length in bits */
|
||||
|
||||
int_least16_t Message_Block_Index; /* Message_Block array index */
|
||||
/* 512-bit message blocks */
|
||||
uint8_t Message_Block[SHA1_Message_Block_Size];
|
||||
|
||||
int Computed; /* Is the hash computed? */
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
} SHA1Context;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-256
|
||||
* hashing operation.
|
||||
*/
|
||||
typedef struct SHA256Context {
|
||||
uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */
|
||||
|
||||
uint32_t Length_High; /* Message length in bits */
|
||||
uint32_t Length_Low; /* Message length in bits */
|
||||
|
||||
int_least16_t Message_Block_Index; /* Message_Block array index */
|
||||
/* 512-bit message blocks */
|
||||
uint8_t Message_Block[SHA256_Message_Block_Size];
|
||||
|
||||
int Computed; /* Is the hash computed? */
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
} SHA256Context;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-512
|
||||
* hashing operation.
|
||||
*/
|
||||
typedef struct SHA512Context {
|
||||
#ifdef USE_32BIT_ONLY
|
||||
uint32_t Intermediate_Hash[SHA512HashSize/4]; /* Message Digest */
|
||||
uint32_t Length[4]; /* Message length in bits */
|
||||
#else /* !USE_32BIT_ONLY */
|
||||
uint64_t Intermediate_Hash[SHA512HashSize/8]; /* Message Digest */
|
||||
uint64_t Length_High, Length_Low; /* Message length in bits */
|
||||
#endif /* USE_32BIT_ONLY */
|
||||
|
||||
int_least16_t Message_Block_Index; /* Message_Block array index */
|
||||
/* 1024-bit message blocks */
|
||||
uint8_t Message_Block[SHA512_Message_Block_Size];
|
||||
|
||||
int Computed; /* Is the hash computed?*/
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
} SHA512Context;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-224
|
||||
* hashing operation. It uses the SHA-256 structure for computation.
|
||||
*/
|
||||
typedef struct SHA256Context SHA224Context;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-384
|
||||
* hashing operation. It uses the SHA-512 structure for computation.
|
||||
*/
|
||||
typedef struct SHA512Context SHA384Context;
|
||||
|
||||
/*
|
||||
* This structure holds context information for all SHA
|
||||
* hashing operations.
|
||||
*/
|
||||
typedef struct USHAContext {
|
||||
int whichSha; /* which SHA is being used */
|
||||
union {
|
||||
SHA1Context sha1Context;
|
||||
SHA224Context sha224Context; SHA256Context sha256Context;
|
||||
SHA384Context sha384Context; SHA512Context sha512Context;
|
||||
} ctx;
|
||||
|
||||
} USHAContext;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the HMAC
|
||||
* keyed-hashing operation.
|
||||
*/
|
||||
typedef struct HMACContext {
|
||||
int whichSha; /* which SHA is being used */
|
||||
int hashSize; /* hash size of SHA being used */
|
||||
int blockSize; /* block size of SHA being used */
|
||||
USHAContext shaContext; /* SHA context */
|
||||
unsigned char k_opad[USHA_Max_Message_Block_Size];
|
||||
/* outer padding - key XORd with opad */
|
||||
int Computed; /* Is the MAC computed? */
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
|
||||
} HMACContext;
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the HKDF
|
||||
* extract-and-expand Key Derivation Functions.
|
||||
*/
|
||||
typedef struct HKDFContext {
|
||||
int whichSha; /* which SHA is being used */
|
||||
HMACContext hmacContext;
|
||||
int hashSize; /* hash size of SHA being used */
|
||||
unsigned char prk[USHAMaxHashSize];
|
||||
/* pseudo-random key - output of hkdfInput */
|
||||
int Computed; /* Is the key material computed? */
|
||||
int Corrupted; /* Cumulative corruption code */
|
||||
} HKDFContext;
|
||||
|
||||
/*
|
||||
* Function Prototypes
|
||||
*/
|
||||
|
||||
/* SHA-1 */
|
||||
extern int SHA1Reset(SHA1Context *);
|
||||
extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA1FinalBits(SHA1Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA1Result(SHA1Context *,
|
||||
uint8_t Message_Digest[SHA1HashSize]);
|
||||
|
||||
/* SHA-224 */
|
||||
extern int SHA224Reset(SHA224Context *);
|
||||
extern int SHA224Input(SHA224Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA224FinalBits(SHA224Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA224Result(SHA224Context *,
|
||||
uint8_t Message_Digest[SHA224HashSize]);
|
||||
|
||||
/* SHA-256 */
|
||||
extern int SHA256Reset(SHA256Context *);
|
||||
extern int SHA256Input(SHA256Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA256FinalBits(SHA256Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA256Result(SHA256Context *,
|
||||
uint8_t Message_Digest[SHA256HashSize]);
|
||||
|
||||
/* SHA-384 */
|
||||
extern int SHA384Reset(SHA384Context *);
|
||||
extern int SHA384Input(SHA384Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA384FinalBits(SHA384Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA384Result(SHA384Context *,
|
||||
uint8_t Message_Digest[SHA384HashSize]);
|
||||
|
||||
/* SHA-512 */
|
||||
extern int SHA512Reset(SHA512Context *);
|
||||
extern int SHA512Input(SHA512Context *, const uint8_t *bytes,
|
||||
unsigned int bytecount);
|
||||
extern int SHA512FinalBits(SHA512Context *, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int SHA512Result(SHA512Context *,
|
||||
uint8_t Message_Digest[SHA512HashSize]);
|
||||
|
||||
/* Unified SHA functions, chosen by whichSha */
|
||||
extern int USHAReset(USHAContext *context, SHAversion whichSha);
|
||||
extern int USHAInput(USHAContext *context,
|
||||
const uint8_t *bytes, unsigned int bytecount);
|
||||
extern int USHAFinalBits(USHAContext *context,
|
||||
uint8_t bits, unsigned int bit_count);
|
||||
extern int USHAResult(USHAContext *context,
|
||||
uint8_t Message_Digest[USHAMaxHashSize]);
|
||||
extern int USHABlockSize(enum SHAversion whichSha);
|
||||
extern int USHAHashSize(enum SHAversion whichSha);
|
||||
extern int USHAHashSizeBits(enum SHAversion whichSha);
|
||||
extern const char *USHAHashName(enum SHAversion whichSha);
|
||||
|
||||
/*
|
||||
* HMAC Keyed-Hashing for Message Authentication, RFC 2104,
|
||||
* for all SHAs.
|
||||
* This interface allows a fixed-length text input to be used.
|
||||
*/
|
||||
extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */
|
||||
const unsigned char *text, /* pointer to data stream */
|
||||
int text_len, /* length of data stream */
|
||||
const unsigned char *key, /* pointer to authentication key */
|
||||
int key_len, /* length of authentication key */
|
||||
uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */
|
||||
|
||||
/*
|
||||
* HMAC Keyed-Hashing for Message Authentication, RFC 2104,
|
||||
* for all SHAs.
|
||||
* This interface allows any length of text input to be used.
|
||||
*/
|
||||
extern int hmacReset(HMACContext *context, enum SHAversion whichSha,
|
||||
const unsigned char *key, int key_len);
|
||||
extern int hmacInput(HMACContext *context, const unsigned char *text,
|
||||
int text_len);
|
||||
extern int hmacFinalBits(HMACContext *context, uint8_t bits,
|
||||
unsigned int bit_count);
|
||||
extern int hmacResult(HMACContext *context,
|
||||
uint8_t digest[USHAMaxHashSize]);
|
||||
|
||||
/*
|
||||
* HKDF HMAC-based Extract-and-Expand Key Derivation Function,
|
||||
* RFC 5869, for all SHAs.
|
||||
*/
|
||||
extern int hkdf(SHAversion whichSha, const unsigned char *salt,
|
||||
int salt_len, const unsigned char *ikm, int ikm_len,
|
||||
const unsigned char *info, int info_len,
|
||||
uint8_t okm[ ], int okm_len);
|
||||
extern int hkdfExtract(SHAversion whichSha, const unsigned char *salt,
|
||||
int salt_len, const unsigned char *ikm,
|
||||
int ikm_len, uint8_t prk[USHAMaxHashSize]);
|
||||
extern int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ],
|
||||
int prk_len, const unsigned char *info,
|
||||
int info_len, uint8_t okm[ ], int okm_len);
|
||||
|
||||
/*
|
||||
* HKDF HMAC-based Extract-and-Expand Key Derivation Function,
|
||||
* RFC 5869, for all SHAs.
|
||||
* This interface allows any length of text input to be used.
|
||||
*/
|
||||
extern int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
|
||||
const unsigned char *salt, int salt_len);
|
||||
extern int hkdfInput(HKDFContext *context, const unsigned char *ikm,
|
||||
int ikm_len);
|
||||
extern int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
|
||||
unsigned int ikm_bit_count);
|
||||
extern int hkdfResult(HKDFContext *context,
|
||||
uint8_t prk[USHAMaxHashSize],
|
||||
const unsigned char *info, int info_len,
|
||||
uint8_t okm[USHAMaxHashSize], int okm_len);
|
||||
#endif /* _SHA_H_ */
|
||||
|
|
@ -0,0 +1,414 @@
|
|||
/**************************** sha1.c ***************************/
|
||||
/***************** See RFC 6234 for details. *******************/
|
||||
/* Copyright (c) 2011 IETF Trust and the persons identified as */
|
||||
/* authors of the code. All rights reserved. */
|
||||
/* See sha.h for terms of use and redistribution. */
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* This file implements the Secure Hash Algorithm SHA-1
|
||||
* as defined in the U.S. National Institute of Standards
|
||||
* and Technology Federal Information Processing Standards
|
||||
* Publication (FIPS PUB) 180-3 published in October 2008
|
||||
* and formerly defined in its predecessors, FIPS PUB 180-1
|
||||
* and FIP PUB 180-2.
|
||||
*
|
||||
* A combined document showing all algorithms is available at
|
||||
* http://csrc.nist.gov/publications/fips/
|
||||
* fips180-3/fips180-3_final.pdf
|
||||
*
|
||||
* The SHA-1 algorithm produces a 160-bit message digest for a
|
||||
* given data stream that can serve as a means of providing a
|
||||
* "fingerprint" for a message.
|
||||
*
|
||||
* Portability Issues:
|
||||
* SHA-1 is defined in terms of 32-bit "words". This code
|
||||
* uses <stdint.h> (included via "sha.h") to define 32- and
|
||||
* 8-bit unsigned integer types. If your C compiler does
|
||||
* not support 32-bit unsigned integers, this code is not
|
||||
* appropriate.
|
||||
*
|
||||
* Caveats:
|
||||
* SHA-1 is designed to work with messages less than 2^64 bits
|
||||
* long. This implementation uses SHA1Input() to hash the bits
|
||||
* that are a multiple of the size of an 8-bit octet, and then
|
||||
* optionally uses SHA1FinalBits() to hash the final few bits of
|
||||
* the input.
|
||||
*/
|
||||
|
||||
#include "sha.h"
|
||||
#include "sha-private.h"
|
||||
|
||||
/*
|
||||
* Define the SHA1 circular left shift macro
|
||||
*/
|
||||
#define SHA1_ROTL(bits,word) \
|
||||
(((word) << (bits)) | ((word) >> (32-(bits))))
|
||||
|
||||
/*
|
||||
* Add "length" to the length.
|
||||
* Set Corrupted when overflow has occurred.
|
||||
*/
|
||||
static uint32_t addTemp;
|
||||
#define SHA1AddLength(context, length) \
|
||||
(addTemp = (context)->Length_Low, \
|
||||
(context)->Corrupted = \
|
||||
(((context)->Length_Low += (length)) < addTemp) && \
|
||||
(++(context)->Length_High == 0) ? shaInputTooLong \
|
||||
: (context)->Corrupted )
|
||||
|
||||
/* Local Function Prototypes */
|
||||
static void SHA1ProcessMessageBlock(SHA1Context *context);
|
||||
static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte);
|
||||
static void SHA1PadMessage(SHA1Context *context, uint8_t Pad_Byte);
|
||||
|
||||
/*
|
||||
* SHA1Reset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the SHA1Context in preparation
|
||||
* for computing a new SHA1 message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Reset(SHA1Context *context)
|
||||
{
|
||||
if (!context) return shaNull;
|
||||
|
||||
context->Length_High = context->Length_Low = 0;
|
||||
context->Message_Block_Index = 0;
|
||||
|
||||
/* Initial Hash Values: FIPS 180-3 section 5.3.1 */
|
||||
context->Intermediate_Hash[0] = 0x67452301;
|
||||
context->Intermediate_Hash[1] = 0xEFCDAB89;
|
||||
context->Intermediate_Hash[2] = 0x98BADCFE;
|
||||
context->Intermediate_Hash[3] = 0x10325476;
|
||||
context->Intermediate_Hash[4] = 0xC3D2E1F0;
|
||||
|
||||
context->Computed = 0;
|
||||
context->Corrupted = shaSuccess;
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion
|
||||
* of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
* message_array[ ]: [in]
|
||||
* An array of octets representing the next portion of
|
||||
* the message.
|
||||
* length: [in]
|
||||
* The length of the message in message_array.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Input(SHA1Context *context,
|
||||
const uint8_t *message_array, unsigned length)
|
||||
{
|
||||
if (!context) return shaNull;
|
||||
if (!length) return shaSuccess;
|
||||
if (!message_array) return shaNull;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
|
||||
while (length--) {
|
||||
context->Message_Block[context->Message_Block_Index++] =
|
||||
*message_array;
|
||||
|
||||
if ((SHA1AddLength(context, 8) == shaSuccess) &&
|
||||
(context->Message_Block_Index == SHA1_Message_Block_Size))
|
||||
SHA1ProcessMessageBlock(context);
|
||||
|
||||
message_array++;
|
||||
}
|
||||
|
||||
return context->Corrupted;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1FinalBits
|
||||
*
|
||||
* Description:
|
||||
* This function will add in any final bits of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
* message_bits: [in]
|
||||
* The final bits of the message, in the upper portion of the
|
||||
* byte. (Use 0b###00000 instead of 0b00000### to input the
|
||||
* three bits ###.)
|
||||
* length: [in]
|
||||
* The number of bits in message_bits, between 1 and 7.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int SHA1FinalBits(SHA1Context *context, uint8_t message_bits,
|
||||
unsigned int length)
|
||||
{
|
||||
static uint8_t masks[8] = {
|
||||
/* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
|
||||
/* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
|
||||
/* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
|
||||
/* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
|
||||
};
|
||||
|
||||
static uint8_t markbit[8] = {
|
||||
/* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
|
||||
/* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
|
||||
/* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
|
||||
/* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
|
||||
};
|
||||
|
||||
if (!context) return shaNull;
|
||||
if (!length) return shaSuccess;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
if (length >= 8) return context->Corrupted = shaBadParam;
|
||||
|
||||
SHA1AddLength(context, length);
|
||||
SHA1Finalize(context,
|
||||
(uint8_t) ((message_bits & masks[length]) | markbit[length]));
|
||||
|
||||
return context->Corrupted;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Result
|
||||
*
|
||||
* Description:
|
||||
* This function will return the 160-bit message digest
|
||||
* into the Message_Digest array provided by the caller.
|
||||
* NOTE:
|
||||
* The first octet of hash is stored in the element with index 0,
|
||||
* the last octet of hash in the element with index 19.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to use to calculate the SHA-1 hash.
|
||||
* Message_Digest[ ]: [out]
|
||||
* Where the digest is returned.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Result(SHA1Context *context,
|
||||
uint8_t Message_Digest[SHA1HashSize])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!context) return shaNull;
|
||||
if (!Message_Digest) return shaNull;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
|
||||
if (!context->Computed)
|
||||
SHA1Finalize(context, 0x80);
|
||||
|
||||
for (i = 0; i < SHA1HashSize; ++i)
|
||||
Message_Digest[i] = (uint8_t) (context->Intermediate_Hash[i>>2]
|
||||
>> (8 * ( 3 - ( i & 0x03 ) )));
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1ProcessMessageBlock
|
||||
*
|
||||
* Description:
|
||||
* This helper function will process the next 512 bits of the
|
||||
* message stored in the Message_Block array.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
* Many of the variable names in this code, especially the
|
||||
* single character names, were used because those were the
|
||||
* names used in the Secure Hash Standard.
|
||||
*/
|
||||
static void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
{
|
||||
/* Constants defined in FIPS 180-3, section 4.2.1 */
|
||||
const uint32_t K[4] = {
|
||||
0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
|
||||
};
|
||||
|
||||
int t; /* Loop counter */
|
||||
uint32_t temp; /* Temporary word value */
|
||||
uint32_t W[80]; /* Word sequence */
|
||||
uint32_t A, B, C, D, E; /* Word buffers */
|
||||
|
||||
/*
|
||||
* Initialize the first 16 words in the array W
|
||||
*/
|
||||
for (t = 0; t < 16; t++) {
|
||||
W[t] = ((uint32_t)context->Message_Block[t * 4]) << 24;
|
||||
W[t] |= ((uint32_t)context->Message_Block[t * 4 + 1]) << 16;
|
||||
W[t] |= ((uint32_t)context->Message_Block[t * 4 + 2]) << 8;
|
||||
W[t] |= ((uint32_t)context->Message_Block[t * 4 + 3]);
|
||||
}
|
||||
|
||||
for (t = 16; t < 80; t++)
|
||||
W[t] = SHA1_ROTL(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
||||
|
||||
A = context->Intermediate_Hash[0];
|
||||
B = context->Intermediate_Hash[1];
|
||||
C = context->Intermediate_Hash[2];
|
||||
D = context->Intermediate_Hash[3];
|
||||
E = context->Intermediate_Hash[4];
|
||||
|
||||
for (t = 0; t < 20; t++) {
|
||||
temp = SHA1_ROTL(5,A) + SHA_Ch(B, C, D) + E + W[t] + K[0];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1_ROTL(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for (t = 20; t < 40; t++) {
|
||||
temp = SHA1_ROTL(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[1];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1_ROTL(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for (t = 40; t < 60; t++) {
|
||||
temp = SHA1_ROTL(5,A) + SHA_Maj(B, C, D) + E + W[t] + K[2];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1_ROTL(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for (t = 60; t < 80; t++) {
|
||||
temp = SHA1_ROTL(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[3];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1_ROTL(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
context->Intermediate_Hash[0] += A;
|
||||
context->Intermediate_Hash[1] += B;
|
||||
context->Intermediate_Hash[2] += C;
|
||||
context->Intermediate_Hash[3] += D;
|
||||
context->Intermediate_Hash[4] += E;
|
||||
context->Message_Block_Index = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Finalize
|
||||
*
|
||||
* Description:
|
||||
* This helper function finishes off the digest calculations.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
* Pad_Byte: [in]
|
||||
* The last byte to add to the message block before the 0-padding
|
||||
* and length. This will contain the last bits of the message
|
||||
* followed by another single bit. If the message was an
|
||||
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte)
|
||||
{
|
||||
int i;
|
||||
SHA1PadMessage(context, Pad_Byte);
|
||||
/* message may be sensitive, clear it out */
|
||||
for (i = 0; i < SHA1_Message_Block_Size; ++i)
|
||||
context->Message_Block[i] = 0;
|
||||
context->Length_High = 0; /* and clear length */
|
||||
context->Length_Low = 0;
|
||||
context->Computed = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1PadMessage
|
||||
*
|
||||
* Description:
|
||||
* According to the standard, the message must be padded to the next
|
||||
* even multiple of 512 bits. The first padding bit must be a '1'.
|
||||
* The last 64 bits represent the length of the original message.
|
||||
* All bits in between should be 0. This helper function will pad
|
||||
* the message according to those rules by filling the Message_Block
|
||||
* array accordingly. When it returns, it can be assumed that the
|
||||
* message digest has been computed.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to pad.
|
||||
* Pad_Byte: [in]
|
||||
* The last byte to add to the message block before the 0-padding
|
||||
* and length. This will contain the last bits of the message
|
||||
* followed by another single bit. If the message was an
|
||||
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
static void SHA1PadMessage(SHA1Context *context, uint8_t Pad_Byte)
|
||||
{
|
||||
/*
|
||||
* Check to see if the current message block is too small to hold
|
||||
* the initial padding bits and length. If so, we will pad the
|
||||
* block, process it, and then continue padding into a second
|
||||
* block.
|
||||
*/
|
||||
if (context->Message_Block_Index >= (SHA1_Message_Block_Size - 8)) {
|
||||
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
|
||||
while (context->Message_Block_Index < SHA1_Message_Block_Size)
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
} else
|
||||
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
|
||||
|
||||
while (context->Message_Block_Index < (SHA1_Message_Block_Size - 8))
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
|
||||
/*
|
||||
* Store the message length as the last 8 octets
|
||||
*/
|
||||
context->Message_Block[56] = (uint8_t) (context->Length_High >> 24);
|
||||
context->Message_Block[57] = (uint8_t) (context->Length_High >> 16);
|
||||
context->Message_Block[58] = (uint8_t) (context->Length_High >> 8);
|
||||
context->Message_Block[59] = (uint8_t) (context->Length_High);
|
||||
context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24);
|
||||
context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16);
|
||||
context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8);
|
||||
context->Message_Block[63] = (uint8_t) (context->Length_Low);
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
|
|
@ -0,0 +1,581 @@
|
|||
/************************* sha224-256.c ************************/
|
||||
/***************** See RFC 6234 for details. *******************/
|
||||
/* Copyright (c) 2011 IETF Trust and the persons identified as */
|
||||
/* authors of the code. All rights reserved. */
|
||||
/* See sha.h for terms of use and redistribution. */
|
||||
|
||||
/*
|
||||
* Description:
|
||||
* This file implements the Secure Hash Algorithms SHA-224 and
|
||||
* SHA-256 as defined in the U.S. National Institute of Standards
|
||||
* and Technology Federal Information Processing Standards
|
||||
* Publication (FIPS PUB) 180-3 published in October 2008
|
||||
* and formerly defined in its predecessors, FIPS PUB 180-1
|
||||
* and FIP PUB 180-2.
|
||||
*
|
||||
* A combined document showing all algorithms is available at
|
||||
* http://csrc.nist.gov/publications/fips/
|
||||
* fips180-3/fips180-3_final.pdf
|
||||
*
|
||||
* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
|
||||
* message digests for a given data stream. It should take about
|
||||
* 2**n steps to find a message with the same digest as a given
|
||||
* message and 2**(n/2) to find any two messages with the same
|
||||
* digest, when n is the digest size in bits. Therefore, this
|
||||
* algorithm can serve as a means of providing a
|
||||
* "fingerprint" for a message.
|
||||
*
|
||||
* Portability Issues:
|
||||
* SHA-224 and SHA-256 are defined in terms of 32-bit "words".
|
||||
* This code uses <stdint.h> (included via "sha.h") to define 32-
|
||||
* and 8-bit unsigned integer types. If your C compiler does not
|
||||
* support 32-bit unsigned integers, this code is not
|
||||
* appropriate.
|
||||
*
|
||||
* Caveats:
|
||||
* SHA-224 and SHA-256 are designed to work with messages less
|
||||
* than 2^64 bits long. This implementation uses SHA224/256Input()
|
||||
* to hash the bits that are a multiple of the size of an 8-bit
|
||||
* octet, and then optionally uses SHA224/256FinalBits()
|
||||
* to hash the final few bits of the input.
|
||||
*/
|
||||
|
||||
#include "sha.h"
|
||||
#include "sha-private.h"
|
||||
|
||||
/* Define the SHA shift, rotate left, and rotate right macros */
|
||||
#define SHA256_SHR(bits,word) ((word) >> (bits))
|
||||
#define SHA256_ROTL(bits,word) \
|
||||
(((word) << (bits)) | ((word) >> (32-(bits))))
|
||||
#define SHA256_ROTR(bits,word) \
|
||||
(((word) >> (bits)) | ((word) << (32-(bits))))
|
||||
|
||||
/* Define the SHA SIGMA and sigma macros */
|
||||
#define SHA256_SIGMA0(word) \
|
||||
(SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
|
||||
#define SHA256_SIGMA1(word) \
|
||||
(SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
|
||||
#define SHA256_sigma0(word) \
|
||||
(SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
|
||||
#define SHA256_sigma1(word) \
|
||||
(SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
|
||||
|
||||
/*
|
||||
* Add "length" to the length.
|
||||
* Set Corrupted when overflow has occurred.
|
||||
*/
|
||||
static uint32_t addTemp;
|
||||
#define SHA224_256AddLength(context, length) \
|
||||
(addTemp = (context)->Length_Low, (context)->Corrupted = \
|
||||
(((context)->Length_Low += (length)) < addTemp) && \
|
||||
(++(context)->Length_High == 0) ? shaInputTooLong : \
|
||||
(context)->Corrupted )
|
||||
|
||||
/* Local Function Prototypes */
|
||||
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0);
|
||||
static void SHA224_256ProcessMessageBlock(SHA256Context *context);
|
||||
static void SHA224_256Finalize(SHA256Context *context,
|
||||
uint8_t Pad_Byte);
|
||||
static void SHA224_256PadMessage(SHA256Context *context,
|
||||
uint8_t Pad_Byte);
|
||||
static int SHA224_256ResultN(SHA256Context *context,
|
||||
uint8_t Message_Digest[ ], int HashSize);
|
||||
|
||||
/* Initial Hash Values: FIPS 180-3 section 5.3.2 */
|
||||
static uint32_t SHA224_H0[SHA256HashSize/4] = {
|
||||
0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939,
|
||||
0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4
|
||||
};
|
||||
|
||||
/* Initial Hash Values: FIPS 180-3 section 5.3.3 */
|
||||
static uint32_t SHA256_H0[SHA256HashSize/4] = {
|
||||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
||||
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
||||
};
|
||||
|
||||
/*
|
||||
* SHA224Reset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the SHA224Context in preparation
|
||||
* for computing a new SHA224 message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int SHA224Reset(SHA224Context *context)
|
||||
{
|
||||
return SHA224_256Reset(context, SHA224_H0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA224Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion
|
||||
* of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
* message_array[ ]: [in]
|
||||
* An array of octets representing the next portion of
|
||||
* the message.
|
||||
* length: [in]
|
||||
* The length of the message in message_array.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA224Input(SHA224Context *context, const uint8_t *message_array,
|
||||
unsigned int length)
|
||||
{
|
||||
return SHA256Input(context, message_array, length);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA224FinalBits
|
||||
*
|
||||
* Description:
|
||||
* This function will add in any final bits of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
* message_bits: [in]
|
||||
* The final bits of the message, in the upper portion of the
|
||||
* byte. (Use 0b###00000 instead of 0b00000### to input the
|
||||
* three bits ###.)
|
||||
* length: [in]
|
||||
* The number of bits in message_bits, between 1 and 7.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int SHA224FinalBits(SHA224Context *context,
|
||||
uint8_t message_bits, unsigned int length)
|
||||
{
|
||||
return SHA256FinalBits(context, message_bits, length);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA224Result
|
||||
*
|
||||
* Description:
|
||||
* This function will return the 224-bit message digest
|
||||
* into the Message_Digest array provided by the caller.
|
||||
* NOTE:
|
||||
* The first octet of hash is stored in the element with index 0,
|
||||
* the last octet of hash in the element with index 27.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to use to calculate the SHA hash.
|
||||
* Message_Digest[ ]: [out]
|
||||
* Where the digest is returned.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int SHA224Result(SHA224Context *context,
|
||||
uint8_t Message_Digest[SHA224HashSize])
|
||||
{
|
||||
return SHA224_256ResultN(context, Message_Digest, SHA224HashSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA256Reset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the SHA256Context in preparation
|
||||
* for computing a new SHA256 message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int SHA256Reset(SHA256Context *context)
|
||||
{
|
||||
return SHA224_256Reset(context, SHA256_H0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA256Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion
|
||||
* of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
* message_array[ ]: [in]
|
||||
* An array of octets representing the next portion of
|
||||
* the message.
|
||||
* length: [in]
|
||||
* The length of the message in message_array.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int SHA256Input(SHA256Context *context, const uint8_t *message_array,
|
||||
unsigned int length)
|
||||
{
|
||||
if (!context) return shaNull;
|
||||
if (!length) return shaSuccess;
|
||||
if (!message_array) return shaNull;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
|
||||
while (length--) {
|
||||
context->Message_Block[context->Message_Block_Index++] =
|
||||
*message_array;
|
||||
|
||||
if ((SHA224_256AddLength(context, 8) == shaSuccess) &&
|
||||
(context->Message_Block_Index == SHA256_Message_Block_Size))
|
||||
SHA224_256ProcessMessageBlock(context);
|
||||
|
||||
message_array++;
|
||||
}
|
||||
|
||||
return context->Corrupted;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA256FinalBits
|
||||
*
|
||||
* Description:
|
||||
* This function will add in any final bits of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
* message_bits: [in]
|
||||
* The final bits of the message, in the upper portion of the
|
||||
* byte. (Use 0b###00000 instead of 0b00000### to input the
|
||||
* three bits ###.)
|
||||
* length: [in]
|
||||
* The number of bits in message_bits, between 1 and 7.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int SHA256FinalBits(SHA256Context *context,
|
||||
uint8_t message_bits, unsigned int length)
|
||||
{
|
||||
static uint8_t masks[8] = {
|
||||
/* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
|
||||
/* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
|
||||
/* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
|
||||
/* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
|
||||
};
|
||||
static uint8_t markbit[8] = {
|
||||
/* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
|
||||
/* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
|
||||
/* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
|
||||
/* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
|
||||
};
|
||||
|
||||
if (!context) return shaNull;
|
||||
if (!length) return shaSuccess;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
if (context->Computed) return context->Corrupted = shaStateError;
|
||||
if (length >= 8) return context->Corrupted = shaBadParam;
|
||||
|
||||
SHA224_256AddLength(context, length);
|
||||
SHA224_256Finalize(context, (uint8_t)
|
||||
((message_bits & masks[length]) | markbit[length]));
|
||||
|
||||
return context->Corrupted;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA256Result
|
||||
*
|
||||
* Description:
|
||||
* This function will return the 256-bit message digest
|
||||
* into the Message_Digest array provided by the caller.
|
||||
* NOTE:
|
||||
* The first octet of hash is stored in the element with index 0,
|
||||
* the last octet of hash in the element with index 31.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to use to calculate the SHA hash.
|
||||
* Message_Digest[ ]: [out]
|
||||
* Where the digest is returned.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
int SHA256Result(SHA256Context *context,
|
||||
uint8_t Message_Digest[SHA256HashSize])
|
||||
{
|
||||
return SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA224_256Reset
|
||||
*
|
||||
* Description:
|
||||
* This helper function will initialize the SHA256Context in
|
||||
* preparation for computing a new SHA-224 or SHA-256 message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
* H0[ ]: [in]
|
||||
* The initial hash value array to use.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0)
|
||||
{
|
||||
if (!context) return shaNull;
|
||||
|
||||
context->Length_High = context->Length_Low = 0;
|
||||
context->Message_Block_Index = 0;
|
||||
|
||||
context->Intermediate_Hash[0] = H0[0];
|
||||
context->Intermediate_Hash[1] = H0[1];
|
||||
context->Intermediate_Hash[2] = H0[2];
|
||||
context->Intermediate_Hash[3] = H0[3];
|
||||
context->Intermediate_Hash[4] = H0[4];
|
||||
context->Intermediate_Hash[5] = H0[5];
|
||||
context->Intermediate_Hash[6] = H0[6];
|
||||
context->Intermediate_Hash[7] = H0[7];
|
||||
|
||||
context->Computed = 0;
|
||||
context->Corrupted = shaSuccess;
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA224_256ProcessMessageBlock
|
||||
*
|
||||
* Description:
|
||||
* This helper function will process the next 512 bits of the
|
||||
* message stored in the Message_Block array.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
* Many of the variable names in this code, especially the
|
||||
* single character names, were used because those were the
|
||||
* names used in the Secure Hash Standard.
|
||||
*/
|
||||
static void SHA224_256ProcessMessageBlock(SHA256Context *context)
|
||||
{
|
||||
/* Constants defined in FIPS 180-3, section 4.2.2 */
|
||||
static const uint32_t K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
|
||||
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
|
||||
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
|
||||
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
|
||||
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
||||
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
|
||||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
|
||||
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
|
||||
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
|
||||
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
int t, t4; /* Loop counter */
|
||||
uint32_t temp1, temp2; /* Temporary word value */
|
||||
uint32_t W[64]; /* Word sequence */
|
||||
uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
|
||||
|
||||
/*
|
||||
* Initialize the first 16 words in the array W
|
||||
*/
|
||||
for (t = t4 = 0; t < 16; t++, t4 += 4)
|
||||
W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
|
||||
(((uint32_t)context->Message_Block[t4 + 1]) << 16) |
|
||||
(((uint32_t)context->Message_Block[t4 + 2]) << 8) |
|
||||
(((uint32_t)context->Message_Block[t4 + 3]));
|
||||
for (t = 16; t < 64; t++)
|
||||
W[t] = SHA256_sigma1(W[t-2]) + W[t-7] +
|
||||
SHA256_sigma0(W[t-15]) + W[t-16];
|
||||
|
||||
A = context->Intermediate_Hash[0];
|
||||
B = context->Intermediate_Hash[1];
|
||||
C = context->Intermediate_Hash[2];
|
||||
D = context->Intermediate_Hash[3];
|
||||
E = context->Intermediate_Hash[4];
|
||||
F = context->Intermediate_Hash[5];
|
||||
G = context->Intermediate_Hash[6];
|
||||
H = context->Intermediate_Hash[7];
|
||||
|
||||
for (t = 0; t < 64; t++) {
|
||||
temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t];
|
||||
temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C);
|
||||
H = G;
|
||||
G = F;
|
||||
F = E;
|
||||
E = D + temp1;
|
||||
D = C;
|
||||
C = B;
|
||||
B = A;
|
||||
A = temp1 + temp2;
|
||||
}
|
||||
|
||||
context->Intermediate_Hash[0] += A;
|
||||
context->Intermediate_Hash[1] += B;
|
||||
context->Intermediate_Hash[2] += C;
|
||||
context->Intermediate_Hash[3] += D;
|
||||
context->Intermediate_Hash[4] += E;
|
||||
context->Intermediate_Hash[5] += F;
|
||||
context->Intermediate_Hash[6] += G;
|
||||
context->Intermediate_Hash[7] += H;
|
||||
|
||||
context->Message_Block_Index = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA224_256Finalize
|
||||
*
|
||||
* Description:
|
||||
* This helper function finishes off the digest calculations.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update.
|
||||
* Pad_Byte: [in]
|
||||
* The last byte to add to the message block before the 0-padding
|
||||
* and length. This will contain the last bits of the message
|
||||
* followed by another single bit. If the message was an
|
||||
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
static void SHA224_256Finalize(SHA256Context *context,
|
||||
uint8_t Pad_Byte)
|
||||
{
|
||||
int i;
|
||||
SHA224_256PadMessage(context, Pad_Byte);
|
||||
/* message may be sensitive, so clear it out */
|
||||
for (i = 0; i < SHA256_Message_Block_Size; ++i)
|
||||
context->Message_Block[i] = 0;
|
||||
context->Length_High = 0; /* and clear length */
|
||||
context->Length_Low = 0;
|
||||
context->Computed = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA224_256PadMessage
|
||||
*
|
||||
* Description:
|
||||
* According to the standard, the message must be padded to the next
|
||||
* even multiple of 512 bits. The first padding bit must be a '1'.
|
||||
* The last 64 bits represent the length of the original message.
|
||||
* All bits in between should be 0. This helper function will pad
|
||||
* the message according to those rules by filling the
|
||||
* Message_Block array accordingly. When it returns, it can be
|
||||
* assumed that the message digest has been computed.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to pad.
|
||||
* Pad_Byte: [in]
|
||||
* The last byte to add to the message block before the 0-padding
|
||||
* and length. This will contain the last bits of the message
|
||||
* followed by another single bit. If the message was an
|
||||
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
static void SHA224_256PadMessage(SHA256Context *context,
|
||||
uint8_t Pad_Byte)
|
||||
{
|
||||
/*
|
||||
* Check to see if the current message block is too small to hold
|
||||
* the initial padding bits and length. If so, we will pad the
|
||||
* block, process it, and then continue padding into a second
|
||||
* block.
|
||||
*/
|
||||
if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) {
|
||||
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
|
||||
while (context->Message_Block_Index < SHA256_Message_Block_Size)
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
SHA224_256ProcessMessageBlock(context);
|
||||
} else
|
||||
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
|
||||
|
||||
while (context->Message_Block_Index < (SHA256_Message_Block_Size-8))
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
|
||||
/*
|
||||
* Store the message length as the last 8 octets
|
||||
*/
|
||||
context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
|
||||
context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
|
||||
context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
|
||||
context->Message_Block[59] = (uint8_t)(context->Length_High);
|
||||
context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
|
||||
context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
|
||||
context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
|
||||
context->Message_Block[63] = (uint8_t)(context->Length_Low);
|
||||
|
||||
SHA224_256ProcessMessageBlock(context);
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA224_256ResultN
|
||||
*
|
||||
* Description:
|
||||
* This helper function will return the 224-bit or 256-bit message
|
||||
* digest into the Message_Digest array provided by the caller.
|
||||
* NOTE:
|
||||
* The first octet of hash is stored in the element with index 0,
|
||||
* the last octet of hash in the element with index 27/31.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to use to calculate the SHA hash.
|
||||
* Message_Digest[ ]: [out]
|
||||
* Where the digest is returned.
|
||||
* HashSize: [in]
|
||||
* The size of the hash, either 28 or 32.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*/
|
||||
static int SHA224_256ResultN(SHA256Context *context,
|
||||
uint8_t Message_Digest[ ], int HashSize)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!context) return shaNull;
|
||||
if (!Message_Digest) return shaNull;
|
||||
if (context->Corrupted) return context->Corrupted;
|
||||
|
||||
if (!context->Computed)
|
||||
SHA224_256Finalize(context, 0x80);
|
||||
|
||||
for (i = 0; i < HashSize; ++i)
|
||||
Message_Digest[i] = (uint8_t)
|
||||
(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue