Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions include/mgos_hap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
33 changes: 25 additions & 8 deletions src/mgos_homekit_adk.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,37 @@ bool mgos_hap_setup_info_from_string(HAPSetupInfo* setupInfo, const char* salt,

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

// save it
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);
}

char* err = NULL;
save_cfg(&mgos_sys_config, &err);
printf("Saving configuration: %s\n", err ? err : "no error");
free(err);
out:
if (cfg != NULL) {
mgos_conf_free(mgos_config_schema(), cfg);
free(cfg);
}

// generated id is always valid.
return true;
Expand All @@ -72,7 +89,7 @@ bool mgos_hap_setup_id_from_string(HAPSetupID* setupID, const char* 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 faith.
} // 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;
Expand All @@ -91,7 +108,7 @@ static void load_setup_info_cb(int ev, void* ev_data, void* userdata) {

static void load_setup_id_cb(int ev, void* ev_data, void* userdata) {

LOG(LL_DEBUG, ("%s: Loading setup identifier...", __func__));
LOG(LL_INFO, ("%s: Loading setup identifier...", __func__));

struct mgos_hap_load_setup_id_arg* arg = (struct mgos_hap_load_setup_id_arg*) ev_data;

Expand All @@ -100,7 +117,7 @@ static void load_setup_id_cb(int ev, void* ev_data, void* userdata) {
*arg->valid = false;
} else {
*arg->valid = true;
LOG(LL_DEBUG, ("Success loading setup id. (Identifier is \"%s\")", arg->setupID->stringValue));
LOG(LL_INFO, ("Success loading setup id. (Identifier is \"%s\")", arg->setupID->stringValue));
}

(void) ev;
Expand Down
224 changes: 188 additions & 36 deletions src/mgos_homekit_adk_rpc_service.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,93 +30,247 @@ 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(
struct mg_rpc_request_info* ri,
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,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaking sensitive info ... will be fixed.

("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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down