diff --git a/include/mgos_hap.h b/include/mgos_hap.h index d272bd5..ca38a7d 100644 --- a/include/mgos_hap.h +++ b/include/mgos_hap.h @@ -105,14 +105,17 @@ bool mgos_hap_setup_info_from_string( const char* _Nonnull salt, const char* _Nonnull verifier); +/* + * Load setup identifier from string. + */ +bool mgos_hap_setup_id_from_string(HAPSetupID* _Nonnull setupID, const char* setup_id); + #ifdef MGOS_HAP_SIMPLE_CONFIG bool mgos_hap_config_valid(void); #ifdef MGOS_HAVE_RPC_COMMON // Simple case: only one primary accessory, constant. -void mgos_hap_add_rpc_service( - HAPAccessoryServerRef* _Nonnull server, - const HAPAccessory* _Nonnull accessory); +void mgos_hap_add_rpc_service(HAPAccessoryServerRef* _Nonnull server, const HAPAccessory* _Nonnull accessory); // More complicated variant. void mgos_hap_add_rpc_service_cb( HAPAccessoryServerRef* _Nonnull server, diff --git a/mos.yml b/mos.yml index e1d2379..a175603 100644 --- a/mos.yml +++ b/mos.yml @@ -47,6 +47,7 @@ conds: - ["hap", "o", {"title": "HAP settings"}] - ["hap.salt", "s", "", {"title": "Device verifier salt"}] - ["hap.verifier", "s", "", {"title": "Device verifier"}] + - ["hap.setupid", "s", "", {"title": "Device setup id (Four characters)"}] cdefs: MGOS_HAP_SIMPLE_CONFIG: 1 diff --git a/src/PAL/HAPPlatformAccessorySetup.c b/src/PAL/HAPPlatformAccessorySetup.c index 7e88985..30e3408 100644 --- a/src/PAL/HAPPlatformAccessorySetup.c +++ b/src/PAL/HAPPlatformAccessorySetup.c @@ -15,6 +15,9 @@ * limitations under the License. */ +#include "mgos.h" +#include "mgos_hap.h" + #include "HAPPlatformAccessorySetup.h" #include "HAPPlatformAccessorySetup+Init.h" @@ -23,6 +26,34 @@ void HAPPlatformAccessorySetupCreate( const HAPPlatformAccessorySetupOptions* options HAP_UNUSED) { } +void HAPPlatformAccessorySetupLoadSetupInfo(HAPPlatformAccessorySetupRef accessorySetup, HAPSetupInfo* setupInfo) { + struct mgos_hap_load_setup_info_arg arg = { + .accessorySetup = accessorySetup, + .setupInfo = setupInfo, + }; + mgos_event_trigger(MGOS_HAP_EV_LOAD_SETUP_INFO, &arg); +} + +void HAPPlatformAccessorySetupLoadSetupCode(HAPPlatformAccessorySetupRef accessorySetup, HAPSetupCode* setupCode) { + struct mgos_hap_load_setup_code_arg arg = { + .accessorySetup = accessorySetup, + .setupCode = setupCode, + }; + mgos_event_trigger(MGOS_HAP_EV_LOAD_SETUP_CODE, &arg); +} + +void HAPPlatformAccessorySetupLoadSetupID( + HAPPlatformAccessorySetupRef accessorySetup, + bool* valid, + HAPSetupID* setupID) { + struct mgos_hap_load_setup_id_arg arg = { + .accessorySetup = accessorySetup, + .valid = valid, + .setupID = setupID, + }; + mgos_event_trigger(MGOS_HAP_EV_LOAD_SETUP_ID, &arg); +} + HAPPlatformAccessorySetupCapabilities HAPPlatformAccessorySetupGetCapabilities(HAPPlatformAccessorySetupRef accessorySetup) { HAPPrecondition(accessorySetup); diff --git a/src/PAL/HAPPlatformAccessorySetupDisplay+Init.h b/src/PAL/HAPPlatformAccessorySetupDisplay+Init.h new file mode 100644 index 0000000..c5a80c4 --- /dev/null +++ b/src/PAL/HAPPlatformAccessorySetupDisplay+Init.h @@ -0,0 +1,71 @@ +// Copyright (c) 2015-2019 The HomeKit ADK Contributors +// +// Licensed under the Apache License, Version 2.0 (the “License”); +// you may not use this file except in compliance with the License. +// See [CONTRIBUTORS.md] for the list of HomeKit ADK project authors. + +#ifndef HAP_PLATFORM_ACCESSORY_SETUP_DISPLAY_INIT_H +#define HAP_PLATFORM_ACCESSORY_SETUP_DISPLAY_INIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "HAPPlatform.h" + +#if __has_feature(nullability) +#pragma clang assume_nonnull begin +#endif + +#ifndef HAVE_DISPLAY +#define HAVE_DISPLAY 0 +#endif + +/**@file + * Accessory setup display. + * + * - The HAPLog APIs are used to display the setup payload and setup code. + * For a real display the implementation needs to be adjusted. + * + * **Example** + + @code{.c} + + // Allocate Accessory setup display. + static HAPPlatformAccessorySetupDisplay setupDisplay; + + // Initialize Accessory setup display. + HAPPlatformAccessorySetupDisplayCreate(&setupDisplay); + + @endcode +*/ + +/** + * Accessory setup display. + */ +struct HAPPlatformAccessorySetupDisplay { + // Opaque type. Do not access the instance fields directly. + /**@cond */ + HAPSetupPayload setupPayload; + HAPSetupCode setupCode; + bool setupPayloadIsSet : 1; + bool setupCodeIsSet : 1; + /**@endcond */ +}; + +/** + * Initializes Accessory setup display. + * + * @param[out] setupDisplay Accessory setup display. + */ +void HAPPlatformAccessorySetupDisplayCreate(HAPPlatformAccessorySetupDisplayRef setupDisplay); + +#if __has_feature(nullability) +#pragma clang assume_nonnull end +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/PAL/HAPPlatformAccessorySetupDisplay.c b/src/PAL/HAPPlatformAccessorySetupDisplay.c new file mode 100644 index 0000000..34a548c --- /dev/null +++ b/src/PAL/HAPPlatformAccessorySetupDisplay.c @@ -0,0 +1,54 @@ +// Copyright (c) 2015-2019 The HomeKit ADK Contributors +// Copyright (c) 2019 Deomid "rojer" Ryabkov +// +// Licensed under the Apache License, Version 2.0 (the “License”); +// you may not use this file except in compliance with the License. +// See [CONTRIBUTORS.md] for the list of HomeKit ADK project authors. + +#include +#include +#include +#include +#include +#include + +#include "mgos.h" +#include "mgos_hap.h" + +#include "HAPPlatform.h" +#include "HAPPlatformAccessorySetupDisplay+Init.h" + +void HAPPlatformAccessorySetupDisplayCreate(HAPPlatformAccessorySetupDisplayRef setupDisplay) { + HAPPrecondition(HAVE_DISPLAY); + HAPPrecondition(setupDisplay); + + HAPRawBufferZero(setupDisplay, sizeof *setupDisplay); +} + +void HAPPlatformAccessorySetupDisplayUpdateSetupPayload( + HAPPlatformAccessorySetupDisplayRef setupDisplay, + const HAPSetupPayload* _Nullable setupPayload, + const HAPSetupCode* _Nullable setupCode) { + + struct mgos_hap_display_update_setup_payload_arg arg = { + .setupDisplay = setupDisplay, + .setupPayload = setupPayload, + .setupCode = setupCode, + }; + + mgos_event_trigger(MGOS_HAP_EV_DISPLAY_UPDATE_SETUP_PAYLOAD, &arg); +} + +void HAPPlatformAccessorySetupDisplayHandleStartPairing(HAPPlatformAccessorySetupDisplayRef setupDisplay) { + struct mgos_hap_display_start_pairing_arg arg = { + .setupDisplay = setupDisplay, + }; + mgos_event_trigger(MGOS_HAP_EV_DISPLAY_START_PAIRING, &arg); +} + +void HAPPlatformAccessorySetupDisplayHandleStopPairing(HAPPlatformAccessorySetupDisplayRef setupDisplay) { + struct mgos_hap_display_stop_pairing_arg arg = { + .setupDisplay = setupDisplay, + }; + mgos_event_trigger(MGOS_HAP_EV_DISPLAY_STOP_PAIRING, &arg); +} diff --git a/src/PAL/HAPPlatformAccessorySetupNFC.c b/src/PAL/HAPPlatformAccessorySetupNFC.c new file mode 100644 index 0000000..0605e1e --- /dev/null +++ b/src/PAL/HAPPlatformAccessorySetupNFC.c @@ -0,0 +1,31 @@ +// Copyright (c) 2015-2019 The HomeKit ADK Contributors +// +// Licensed under the Apache License, Version 2.0 (the “License”); +// you may not use this file except in compliance with the License. +// See [CONTRIBUTORS.md] for the list of HomeKit ADK project authors. + +#include "mgos.h" +#include "mgos_hap.h" + +#include "HAPPlatform.h" + +#ifndef HAVE_NFC +#define HAVE_NFC 0 +#endif + +#if HAVE_NFC +#include +#include +#endif + +void HAPPlatformAccessorySetupNFCUpdateSetupPayload( + HAPPlatformAccessorySetupNFCRef setupNFC, + const HAPSetupPayload* setupPayload, + bool isPairable) { + struct mgos_hap_nfc_update_setup_payload_arg arg = { + .setupNFC = setupNFC, + .setupPayload = setupPayload, + .isPairable = isPairable, + }; + mgos_event_trigger(MGOS_HAP_EV_NFC_UPDATE_SETUP_PAYLOAD, &arg); +} diff --git a/src/PAL/HAPPlatformTCPStreamManager.c b/src/PAL/HAPPlatformTCPStreamManager.c index e4780f2..27cefe7 100644 --- a/src/PAL/HAPPlatformTCPStreamManager.c +++ b/src/PAL/HAPPlatformTCPStreamManager.c @@ -40,7 +40,8 @@ bool HAPPlatformTCPStreamManagerIsListenerOpen(HAPPlatformTCPStreamManagerRef tc } static struct mg_connection* HAPMGListenerGetNextPendingConnection(HAPPlatformTCPStreamManagerRef tm) { - if (tm->listener == NULL) return NULL; // Stopping. + if (tm->listener == NULL) + return NULL; // Stopping. struct mg_mgr* mgr = tm->listener->mgr; struct mg_connection* result = NULL; for (struct mg_connection* nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { diff --git a/src/mgos_homekit_adk.c b/src/mgos_homekit_adk.c index 1ed5956..1cb0d33 100644 --- a/src/mgos_homekit_adk.c +++ b/src/mgos_homekit_adk.c @@ -18,73 +18,7 @@ #include "mgos_hap.h" #include "mgos.h" - -void HAPPlatformAccessorySetupLoadSetupInfo(HAPPlatformAccessorySetupRef accessorySetup, HAPSetupInfo* setupInfo) { - struct mgos_hap_load_setup_info_arg arg = { - .accessorySetup = accessorySetup, - .setupInfo = setupInfo, - }; - mgos_event_trigger(MGOS_HAP_EV_LOAD_SETUP_INFO, &arg); -} - -void HAPPlatformAccessorySetupLoadSetupCode(HAPPlatformAccessorySetupRef accessorySetup, HAPSetupCode* setupCode) { - struct mgos_hap_load_setup_code_arg arg = { - .accessorySetup = accessorySetup, - .setupCode = setupCode, - }; - mgos_event_trigger(MGOS_HAP_EV_LOAD_SETUP_CODE, &arg); -} - -void HAPPlatformAccessorySetupLoadSetupID( - HAPPlatformAccessorySetupRef accessorySetup, - bool* valid, - HAPSetupID* setupID) { - struct mgos_hap_load_setup_id_arg arg = { - .accessorySetup = accessorySetup, - .valid = valid, - .setupID = setupID, - }; - mgos_event_trigger(MGOS_HAP_EV_LOAD_SETUP_ID, &arg); -} - -void HAPPlatformAccessorySetupDisplayUpdateSetupPayload( - HAPPlatformAccessorySetupDisplayRef setupDisplay, - const HAPSetupPayload* _Nullable setupPayload, - const HAPSetupCode* _Nullable setupCode) { - - struct mgos_hap_display_update_setup_payload_arg arg = { - .setupDisplay = setupDisplay, - .setupPayload = setupPayload, - .setupCode = setupCode, - }; - mgos_event_trigger(MGOS_HAP_EV_DISPLAY_UPDATE_SETUP_PAYLOAD, &arg); -} - -void HAPPlatformAccessorySetupDisplayHandleStartPairing(HAPPlatformAccessorySetupDisplayRef setupDisplay) { - struct mgos_hap_display_start_pairing_arg arg = { - .setupDisplay = setupDisplay, - }; - mgos_event_trigger(MGOS_HAP_EV_DISPLAY_START_PAIRING, &arg); -} - -void HAPPlatformAccessorySetupDisplayHandleStopPairing(HAPPlatformAccessorySetupDisplayRef setupDisplay) { - struct mgos_hap_display_stop_pairing_arg arg = { - .setupDisplay = setupDisplay, - }; - mgos_event_trigger(MGOS_HAP_EV_DISPLAY_STOP_PAIRING, &arg); -} - -void HAPPlatformAccessorySetupNFCUpdateSetupPayload( - HAPPlatformAccessorySetupNFCRef setupNFC, - const HAPSetupPayload* setupPayload, - bool isPairable) { - struct mgos_hap_nfc_update_setup_payload_arg arg = { - .setupNFC = setupNFC, - .setupPayload = setupPayload, - .isPairable = isPairable, - }; - mgos_event_trigger(MGOS_HAP_EV_NFC_UPDATE_SETUP_PAYLOAD, &arg); -} +#include "HAPAccessorySetup.h" bool mgos_hap_setup_info_from_string(HAPSetupInfo* setupInfo, const char* salt, const char* verifier) { int salt_len = strlen(salt != NULL ? salt : ""); @@ -112,8 +46,57 @@ bool mgos_hap_setup_info_from_string(HAPSetupInfo* setupInfo, const char* salt, return (d == (int) sizeof(setupInfo->verifier)); } +bool mgos_hap_setup_id_from_string(HAPSetupID* setupID, const char* id) { + + int config_level = 2; + int id_len = strlen(id != NULL ? id : ""); + + // when no setup id was provided, generate an id + if (id_len == 0 || id_len > sizeof(HAPSetupID)) { + + HAPAccessorySetupGenerateRandomSetupID(setupID); + + struct mgos_config* cfg = (struct mgos_config*) calloc(1, sizeof(*cfg)); + + // load configuration for requested level + if (!mgos_sys_config_load_level(cfg, (enum mgos_config_level) config_level)) { + LOG(LL_ERROR, ("%s: failed to load config.", __func__)); + goto out; + } + + mgos_sys_config_set_hap_setupid(setupID->stringValue); + mgos_conf_set_str(&cfg->hap.setupid, setupID->stringValue); + + // save configuration + char* msg = NULL; + if (!mgos_sys_config_save_level(cfg, (enum mgos_config_level) config_level, false, &msg)) { + LOG(LL_ERROR, ("%s: Error saving config: %s", __func__, (msg ? msg : ""))); + free(msg); + } + + out: + if (cfg != NULL) { + mgos_conf_free(mgos_config_schema(), cfg); + free(cfg); + } + + // generated id is always valid. + return true; + } + + // check if provided id is valid + if (HAPAccessorySetupIsValidSetupID(id)) { + // copy valid id string to stringValue + strncpy(setupID->stringValue, id, sizeof(HAPSetupID)); + return true; + } // ending here ... if a non valid setup id was provided don't overrule it. Chosen fate. + + // no valid setup id was generated or read + return false; +} + #ifdef MGOS_HAP_SIMPLE_CONFIG -static void setup_info_cb(int ev, void* ev_data, void* userdata) { +static void load_setup_info_cb(int ev, void* ev_data, void* userdata) { struct mgos_hap_load_setup_info_arg* arg = (struct mgos_hap_load_setup_info_arg*) ev_data; if (!mgos_hap_setup_info_from_string( arg->setupInfo, mgos_sys_config_get_hap_salt(), mgos_sys_config_get_hap_verifier())) { @@ -123,16 +106,37 @@ static void setup_info_cb(int ev, void* ev_data, void* userdata) { (void) userdata; } +static void load_setup_id_cb(int ev, void* ev_data, void* userdata) { + + LOG(LL_DEBUG, ("%s: Loading setup identifier...", __func__)); + + struct mgos_hap_load_setup_id_arg* arg = (struct mgos_hap_load_setup_id_arg*) ev_data; + + if (!mgos_hap_setup_id_from_string(arg->setupID, mgos_sys_config_get_hap_setupid())) { + LOG(LL_DEBUG, ("Failed to load or generate HAP accessory setup identifier!")); + *arg->valid = false; + } else { + *arg->valid = true; + LOG(LL_DEBUG, ("Success loading setup id. (Identifier is \"%s\")", arg->setupID->stringValue)); + } + + (void) ev; + (void) userdata; +} + bool mgos_hap_config_valid(void) { HAPSetupInfo setupInfo; + HAPSetupID setupID; return mgos_hap_setup_info_from_string( - &setupInfo, mgos_sys_config_get_hap_salt(), mgos_sys_config_get_hap_verifier()); + &setupInfo, mgos_sys_config_get_hap_salt(), mgos_sys_config_get_hap_verifier()) && + mgos_hap_setup_id_from_string(&setupID, mgos_sys_config_get_hap_setupid()); } #endif bool mgos_homekit_adk_init(void) { #ifdef MGOS_HAP_SIMPLE_CONFIG - mgos_event_add_handler(MGOS_HAP_EV_LOAD_SETUP_INFO, setup_info_cb, NULL); + mgos_event_add_handler(MGOS_HAP_EV_LOAD_SETUP_INFO, load_setup_info_cb, NULL); + mgos_event_add_handler(MGOS_HAP_EV_LOAD_SETUP_ID, load_setup_id_cb, NULL); #endif mgos_event_register_base(MGOS_HAP_EV_BASE, "HAP"); return true; diff --git a/src/mgos_homekit_adk_rpc_service.c b/src/mgos_homekit_adk_rpc_service.c index 059561e..e3c9342 100644 --- a/src/mgos_homekit_adk_rpc_service.c +++ b/src/mgos_homekit_adk_rpc_service.c @@ -30,34 +30,98 @@ static const HAPAccessory* s_acc = NULL; static HAPAccessoryServerRef* s_server = NULL; static void (*s_start_cb)(HAPAccessoryServerRef* _Nonnull server) = NULL; -static bool set_salt_and_verfier(const char* salt, const char* verifier, int config_level) { +static void clear_provision_info(int config_level) { struct mgos_config* cfg = (struct mgos_config*) calloc(1, sizeof(*cfg)); + // load configuration for requested level if (!mgos_sys_config_load_level(cfg, (enum mgos_config_level) config_level)) { - LOG(LL_ERROR, ("failed to load config")); + LOG(LL_ERROR, ("%s: failed to load config.", __func__)); goto out; } - mgos_conf_set_str(&cfg->hap.salt, salt); - mgos_conf_set_str(&cfg->hap.verifier, verifier); + mgos_sys_config_set_hap_salt(NULL); + mgos_sys_config_set_hap_verifier(NULL); + mgos_sys_config_set_hap_setupid(NULL); + + mgos_conf_set_str(&cfg->hap.salt, NULL); + mgos_conf_set_str(&cfg->hap.verifier, NULL); + mgos_conf_set_str(&cfg->hap.setupid, NULL); + // save configuration char* msg = NULL; if (!mgos_sys_config_save_level(cfg, (enum mgos_config_level) config_level, false, &msg)) { - LOG(LL_ERROR, ("error saving config: %s", (msg ? msg : ""))); + LOG(LL_ERROR, ("Error saving config: %s", (msg ? msg : ""))); free(msg); + } + +out: + if (cfg != NULL) { + mgos_conf_free(mgos_config_schema(), cfg); + free(cfg); + } + + return; +} + +static bool provision(HAPSetupInfo* info, HAPSetupID* id, int config_level) { + + char* salt = NULL; + char* verifier = NULL; + + bool provisioned = false; + + struct mgos_config* cfg = (struct mgos_config*) calloc(1, sizeof(*cfg)); + + // load configuration for requested level + if (!mgos_sys_config_load_level(cfg, (enum mgos_config_level) config_level)) { + LOG(LL_ERROR, ("failed to load config")); goto out; } - mgos_sys_config_set_hap_salt(salt); - mgos_sys_config_set_hap_verifier(verifier); + // HAPSetupID provided, provision for dynamic setup info pairing (R14, setupDisplay, setupNFC) + if (id != NULL) { + // set it in configuration + mgos_conf_set_str(&cfg->hap.setupid, id->stringValue); + mgos_sys_config_set_hap_setupid(id->stringValue); + } + + // HAPSetupInfo provided + if (info != NULL) { + salt = calloc(1, 24 + 1); + cs_base64_encode(info->salt, 16, salt); + + verifier = calloc(1, 512 + 1); + cs_base64_encode(info->verifier, 384, verifier); + + // set salted verifier in configuration + mgos_conf_set_str(&cfg->hap.salt, salt); + mgos_conf_set_str(&cfg->hap.verifier, verifier); + + mgos_sys_config_set_hap_salt(salt); + mgos_sys_config_set_hap_verifier(verifier); + } + + // save configuration + char* msg = NULL; + if (!mgos_sys_config_save_level(cfg, (enum mgos_config_level) config_level, false, &msg)) { + LOG(LL_ERROR, ("Error saving config: %s", (msg ? msg : ""))); + free(msg); + provisioned = false; + } else { + provisioned = true; + } out: if (cfg != NULL) { mgos_conf_free(mgos_config_schema(), cfg); free(cfg); } - return true; + + free(salt); + free(verifier); + + return provisioned; } static void mgos_hap_setup_handler( @@ -65,58 +129,148 @@ static void mgos_hap_setup_handler( void* cb_arg, struct mg_rpc_frame_info* fi, struct mg_str args) { + + char* setup_id = NULL; char* code = NULL; char *salt = NULL, *verifier = NULL; + int config_level = 2; - bool start_server = true; - HAPSetupInfo setupInfo; + bool start_server = false; + + if (HAPAccessoryServerIsPaired(s_server)) { - json_scanf(args.p, args.len, ri->args_fmt, &code, &salt, &verifier, &config_level, &start_server); + mg_rpc_send_error_jsonf( + ri, 400, "{ err: %d, msg: %Q}", 100, "Accessory is paired. Unpair or reset server first."); + ri = NULL; + goto out; + } - if (code != NULL && (salt == NULL && verifier == NULL)) { - if (!HAPAccessorySetupIsValidSetupCode(code)) { - mg_rpc_send_errorf(ri, 400, "invalid code"); + json_scanf(args.p, args.len, ri->args_fmt, &setup_id, &code, &salt, &verifier, &config_level, &start_server); + + // Setup identifier + HAPSetupID setupID; + bool setup_id_is_set = false; + + // if a previous setup identifier exists, use it + if (mgos_hap_setup_id_from_string(&setupID, mgos_sys_config_get_hap_setupid())) { + setup_id_is_set = true; + } + + // override if a new setup identifier is provided, hash is already deployed + if (setup_id != NULL) { + if (HAPAccessorySetupIsValidSetupID(setup_id)) { + HAPRawBufferCopyBytes(setupID.stringValue, setup_id, sizeof setupID.stringValue); + setup_id_is_set = true; + } else { + // invalid setup identifier provided + mg_rpc_send_error_jsonf(ri, 400, "{ err: %d, msg: %Q}", 101, "Invalid setup identifier"); ri = NULL; goto out; } + + } else if (!setup_id_is_set) { // don't override a saved identifier. + HAPAccessorySetupGenerateRandomSetupID(&setupID); + setup_id_is_set = true; + } + + // Setup info + HAPSetupInfo setupInfo; + bool setup_info_is_set = false; + + if (salt != NULL) { + if (verifier != NULL) { + // fill setup info with legacy pairing info + if (mgos_hap_setup_info_from_string(&setupInfo, salt, verifier)) { + setup_info_is_set = true; // legacy provisioning + } + } + } + + // if a setup code is provided, provided salt and verifier are dismissed. + // if either a code was provided nor salted verifier, create a valid code. + HAPSetupCode setupCode; + bool code_is_set = false; + + if (code != NULL) { + if (HAPAccessorySetupIsValidSetupCode(code)) { + HAPRawBufferCopyBytes(setupCode.stringValue, code, sizeof setupCode.stringValue); + code_is_set = true; + } + } else if (!setup_info_is_set) { + + // create a valid code + HAPAccessorySetupGenerateRandomSetupCode(&setupCode); + code_is_set = true; + } + + // the code was set if no legacy pairing info was provided (salted verifier), check it + if (code_is_set && !setup_info_is_set) { + + // create salted verifier HAPPlatformRandomNumberFill(setupInfo.salt, sizeof setupInfo.salt); + const uint8_t srpUserName[] = "Pair-Setup"; HAP_srp_verifier( setupInfo.verifier, setupInfo.salt, srpUserName, sizeof(srpUserName) - 1, - (const uint8_t*) code, - strlen(code)); - salt = calloc(1, 24 + 1); - cs_base64_encode(setupInfo.salt, 16, salt); - verifier = calloc(1, 512 + 1); - cs_base64_encode(setupInfo.verifier, 384, verifier); - } else if (code == NULL && (salt != NULL && verifier != NULL)) { - if (!mgos_hap_setup_info_from_string(&setupInfo, salt, verifier)) { - mg_rpc_send_errorf(ri, 400, "invalid salt + verifier"); + (const uint8_t*) setupCode.stringValue, + sizeof setupCode.stringValue - 1); + + setup_info_is_set = true; + } + + bool provisioned_successful = false; + + if (setup_info_is_set) { + + provisioned_successful = provision(&setupInfo, &setupID, config_level); + + if (!provisioned_successful) { + mg_rpc_send_error_jsonf(ri, 400, "{ err: %d, msg: %Q}", 102, "Failed to save setup info."); ri = NULL; goto out; + } else { + start_server = true; } - } else { - mg_rpc_send_errorf(ri, 400, "either code or salt + verifier required"); - ri = NULL; - goto out; } - if (!set_salt_and_verfier(salt, verifier, config_level)) { - mg_rpc_send_errorf(ri, 500, "failed to set code"); - ri = NULL; - goto out; + // Setup payload. + HAPSetupPayload setupPayload; + bool setup_payload_is_set = false; + + if (setup_id_is_set && code_is_set) { + // Derive setup payload flags and category. + HAPAccessoryServer* server = (HAPAccessoryServer*) s_server; + HAPAccessorySetupSetupPayloadFlags flags = { .isPaired = HAPAccessoryServerIsPaired(s_server), + .ipSupported = (server->transports.ip != NULL), + .bleSupported = (server->transports.ble != NULL) }; + HAPAccessoryCategory category = s_acc->category; + + LOG(LL_INFO, + ("Creating payload with code: %s, id: %s, category: %d", + setupCode.stringValue, + setupID.stringValue, + category)); + HAPAccessorySetupGetSetupPayload(&setupPayload, &setupCode, &setupID, flags, category); + setup_payload_is_set = true; } - mg_rpc_send_responsef(ri, NULL); + char* successMessage = "{code: %Q, payload: %Q}"; + + mg_rpc_send_responsef( + ri, + successMessage, + (code_is_set ? setupCode.stringValue : ""), + (setup_payload_is_set ? setupPayload.stringValue : "")); if (start_server && HAPAccessoryServerGetState(s_server) == kHAPAccessoryServerState_Idle) { s_start_cb(s_server); } out: + free(setup_id); free(code); free(salt); free(verifier); @@ -157,9 +311,7 @@ static void stop_and_reset(void* arg) { if (ctx->reset_code) { LOG(LL_INFO, ("Resetting code")); // How can we determine the right level? - if (!set_salt_and_verfier(NULL, NULL, 2)) { - res = false; - } + clear_provision_info(2); } if (res && ctx->stopped_server && mgos_hap_config_valid()) { // We stopped server for reset, restart it. @@ -215,7 +367,7 @@ void mgos_hap_add_rpc_service_cb( mg_rpc_add_handler( mgos_rpc_get_global(), "HAP.Setup", - "{code: %Q, salt: %Q, verifier: %Q, config_level: %d, start_server: %B}", + "{setup_id: %Q, code: %Q, salt: %Q, verifier: %Q, config_level: %d, start_server: %B}", mgos_hap_setup_handler, NULL); mg_rpc_add_handler(