Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preparation for library based QR-Code generation - setup identifier #2

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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