Tasmota/lib/libesp32_div/ESP32-HomeKit/src/esp_hap_pair_verify.c
2021-03-11 14:48:59 +01:00

465 lines
17 KiB
C

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