mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 16:24:28 +08:00
683 lines
17 KiB
C
683 lines
17 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2018-2019 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <sys/select.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <getopt.h>
|
|
#include <time.h>
|
|
#include <ell/ell.h>
|
|
|
|
#include "mesh/mesh-defs.h"
|
|
#include "src/shared/ecc.h"
|
|
|
|
#include "mesh/util.h"
|
|
#include "mesh/net-keys.h"
|
|
#include "mesh/crypto.h"
|
|
#include "mesh/net.h"
|
|
#include "mesh/error.h"
|
|
#include "mesh/prov.h"
|
|
#include "mesh/provision.h"
|
|
#include "mesh/pb-adv.h"
|
|
#include "mesh/mesh.h"
|
|
#include "mesh/agent.h"
|
|
|
|
/* Quick size sanity check */
|
|
static const uint16_t expected_pdu_size[] = {
|
|
2, /* PROV_INVITE */
|
|
12, /* PROV_CAPS */
|
|
6, /* PROV_START */
|
|
65, /* PROV_PUB_KEY */
|
|
1, /* PROV_INP_CMPLT */
|
|
17, /* PROV_CONFIRM */
|
|
17, /* PROV_RANDOM */
|
|
34, /* PROV_DATA */
|
|
1, /* PROV_COMPLETE */
|
|
2, /* PROV_FAILED */
|
|
};
|
|
|
|
#define BEACON_TYPE_UNPROVISIONED 0x00
|
|
|
|
static const uint8_t pkt_filter = MESH_AD_TYPE_PROVISION;
|
|
static const uint8_t bec_filter[] = {MESH_AD_TYPE_BEACON,
|
|
BEACON_TYPE_UNPROVISIONED};
|
|
|
|
enum acp_state {
|
|
ACP_PROV_IDLE = 0,
|
|
ACP_PROV_CAPS_SENT,
|
|
ACP_PROV_CAPS_ACKED,
|
|
ACP_PROV_KEY_SENT,
|
|
ACP_PROV_KEY_ACKED,
|
|
ACP_PROV_INP_CMPLT_SENT,
|
|
ACP_PROV_INP_CMPLT_ACKED,
|
|
ACP_PROV_CONF_SENT,
|
|
ACP_PROV_CONF_ACKED,
|
|
ACP_PROV_RAND_SENT,
|
|
ACP_PROV_RAND_ACKED,
|
|
ACP_PROV_CMPLT_SENT,
|
|
ACP_PROV_FAIL_SENT,
|
|
};
|
|
|
|
#define MAT_REMOTE_PUBLIC 0x01
|
|
#define MAT_LOCAL_PRIVATE 0x02
|
|
#define MAT_RAND_AUTH 0x04
|
|
#define MAT_SECRET (MAT_REMOTE_PUBLIC | MAT_LOCAL_PRIVATE)
|
|
|
|
struct mesh_prov_acceptor {
|
|
mesh_prov_acceptor_complete_func_t cmplt;
|
|
prov_trans_tx_t trans_tx;
|
|
void *agent;
|
|
void *caller_data;
|
|
void *trans_data;
|
|
struct l_timeout *timeout;
|
|
uint32_t to_secs;
|
|
enum acp_state state;
|
|
uint8_t transport;
|
|
uint8_t material;
|
|
uint8_t expected;
|
|
int8_t previous;
|
|
struct conf_input conf_inputs;
|
|
uint8_t calc_key[16];
|
|
uint8_t salt[16];
|
|
uint8_t confirm[16];
|
|
uint8_t s_key[16];
|
|
uint8_t s_nonce[13];
|
|
uint8_t private_key[32];
|
|
uint8_t secret[32];
|
|
uint8_t rand_auth_workspace[48];
|
|
};
|
|
|
|
static struct mesh_prov_acceptor *prov = NULL;
|
|
|
|
static void acceptor_free(void)
|
|
{
|
|
if (!prov)
|
|
return;
|
|
|
|
l_timeout_remove(prov->timeout);
|
|
|
|
mesh_send_cancel(bec_filter, sizeof(bec_filter));
|
|
mesh_send_cancel(&pkt_filter, sizeof(pkt_filter));
|
|
|
|
pb_adv_unreg(prov);
|
|
|
|
l_free(prov);
|
|
prov = NULL;
|
|
}
|
|
|
|
static void acp_prov_close(void *user_data, uint8_t reason)
|
|
{
|
|
/* TODO: Handle Close */
|
|
}
|
|
|
|
static void prov_to(struct l_timeout *timeout, void *user_data)
|
|
{
|
|
struct mesh_prov_acceptor *rx_prov = user_data;
|
|
uint8_t fail_code[2] = {PROV_FAILED, PROV_ERR_UNEXPECTED_ERR};
|
|
|
|
if (rx_prov != prov)
|
|
return;
|
|
|
|
prov->timeout = NULL;
|
|
|
|
if (prov->cmplt && prov->trans_tx) {
|
|
prov->cmplt(prov->caller_data, PROV_ERR_TIMEOUT, NULL);
|
|
prov->cmplt = NULL;
|
|
prov->trans_tx(prov->trans_data, fail_code, 2);
|
|
prov->timeout = l_timeout_create(1, prov_to, prov, NULL);
|
|
return;
|
|
}
|
|
|
|
acceptor_free();
|
|
}
|
|
|
|
static void acp_prov_open(void *user_data, prov_trans_tx_t trans_tx,
|
|
void *trans_data, uint8_t transport)
|
|
{
|
|
struct mesh_prov_acceptor *rx_prov = user_data;
|
|
|
|
/* Only one provisioning session may be open at a time */
|
|
if (rx_prov != prov)
|
|
return;
|
|
|
|
/* Only one provisioning session may be open at a time */
|
|
if (prov->trans_tx && prov->trans_tx != trans_tx &&
|
|
prov->transport != transport)
|
|
return;
|
|
|
|
if (transport != PB_ADV)
|
|
return;
|
|
|
|
prov->trans_tx = trans_tx;
|
|
prov->transport = transport;
|
|
prov->trans_data = trans_data;
|
|
prov->timeout = l_timeout_create(prov->to_secs, prov_to, prov, NULL);
|
|
}
|
|
|
|
static void swap_u256_bytes(uint8_t *u256)
|
|
{
|
|
int i;
|
|
|
|
/* End-to-End byte reflection of 32 octet buffer */
|
|
for (i = 0; i < 16; i++) {
|
|
u256[i] ^= u256[31 - i];
|
|
u256[31 - i] ^= u256[i];
|
|
u256[i] ^= u256[31 - i];
|
|
}
|
|
}
|
|
|
|
static void prov_calc_secret(const uint8_t *pub, const uint8_t *priv,
|
|
uint8_t *secret)
|
|
{
|
|
uint8_t tmp[64];
|
|
|
|
/* Convert to ECC byte order */
|
|
memcpy(tmp, pub, 64);
|
|
swap_u256_bytes(tmp);
|
|
swap_u256_bytes(tmp + 32);
|
|
|
|
ecdh_shared_secret(tmp, priv, secret);
|
|
|
|
/* Convert to Mesh byte order */
|
|
swap_u256_bytes(secret);
|
|
}
|
|
|
|
static void acp_credentials(struct mesh_prov_acceptor *prov)
|
|
{
|
|
prov_calc_secret(prov->conf_inputs.prv_pub_key,
|
|
prov->private_key, prov->secret);
|
|
|
|
mesh_crypto_s1(&prov->conf_inputs,
|
|
sizeof(prov->conf_inputs), prov->salt);
|
|
|
|
mesh_crypto_prov_conf_key(prov->secret, prov->salt,
|
|
prov->calc_key);
|
|
|
|
l_getrandom(prov->rand_auth_workspace, 16);
|
|
|
|
print_packet("PublicKeyProv", prov->conf_inputs.prv_pub_key, 64);
|
|
print_packet("PublicKeyDev", prov->conf_inputs.dev_pub_key, 64);
|
|
print_packet("PrivateKeyLocal", prov->private_key, 32);
|
|
print_packet("ConfirmationInputs", &prov->conf_inputs,
|
|
sizeof(prov->conf_inputs));
|
|
print_packet("ECDHSecret", prov->secret, 32);
|
|
print_packet("LocalRandom", prov->rand_auth_workspace, 16);
|
|
print_packet("ConfirmationSalt", prov->salt, 16);
|
|
print_packet("ConfirmationKey", prov->calc_key, 16);
|
|
}
|
|
|
|
static uint32_t digit_mod(uint8_t power)
|
|
{
|
|
uint32_t ret = 1;
|
|
|
|
while (power--)
|
|
ret *= 10;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void number_cb(void *user_data, int err, uint32_t number)
|
|
{
|
|
struct mesh_prov_acceptor *rx_prov = user_data;
|
|
uint8_t out[2];
|
|
|
|
if (prov != rx_prov)
|
|
return;
|
|
|
|
if (err) {
|
|
out[0] = PROV_FAILED;
|
|
out[1] = PROV_ERR_UNEXPECTED_ERR;
|
|
prov->trans_tx(prov->trans_data, out, 2);
|
|
return;
|
|
}
|
|
|
|
/* Save two copies, to generate two confirmation values */
|
|
l_put_be32(number, prov->rand_auth_workspace + 28);
|
|
l_put_be32(number, prov->rand_auth_workspace + 44);
|
|
prov->material |= MAT_RAND_AUTH;
|
|
out[0] = PROV_INP_CMPLT;
|
|
prov->trans_tx(prov->trans_data, out, 1);
|
|
}
|
|
|
|
static void static_cb(void *user_data, int err, uint8_t *key, uint32_t len)
|
|
{
|
|
struct mesh_prov_acceptor *rx_prov = user_data;
|
|
uint8_t out[2];
|
|
|
|
if (prov != rx_prov)
|
|
return;
|
|
|
|
if (err || !key || len != 16) {
|
|
out[0] = PROV_FAILED;
|
|
out[1] = PROV_ERR_UNEXPECTED_ERR;
|
|
prov->trans_tx(prov->trans_data, out, 2);
|
|
return;
|
|
}
|
|
|
|
/* Save two copies, to generate two confirmation values */
|
|
memcpy(prov->rand_auth_workspace + 16, key, 16);
|
|
memcpy(prov->rand_auth_workspace + 32, key, 16);
|
|
prov->material |= MAT_RAND_AUTH;
|
|
}
|
|
|
|
static void priv_key_cb(void *user_data, int err, uint8_t *key, uint32_t len)
|
|
{
|
|
struct mesh_prov_acceptor *rx_prov = user_data;
|
|
uint8_t out[2];
|
|
|
|
if (prov != rx_prov)
|
|
return;
|
|
|
|
if (err || !key || len != 32) {
|
|
out[0] = PROV_FAILED;
|
|
out[1] = PROV_ERR_UNEXPECTED_ERR;
|
|
prov->trans_tx(prov->trans_data, out, 2);
|
|
return;
|
|
}
|
|
|
|
memcpy(prov->private_key, key, 32);
|
|
ecc_make_public_key(prov->private_key,
|
|
prov->conf_inputs.dev_pub_key);
|
|
|
|
/* Convert to Mesh byte order */
|
|
swap_u256_bytes(prov->conf_inputs.dev_pub_key);
|
|
swap_u256_bytes(prov->conf_inputs.dev_pub_key + 32);
|
|
|
|
prov->material |= MAT_LOCAL_PRIVATE;
|
|
if ((prov->material & MAT_SECRET) == MAT_SECRET)
|
|
acp_credentials(prov);
|
|
}
|
|
|
|
static void acp_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
|
|
{
|
|
struct mesh_prov_acceptor *rx_prov = user_data;
|
|
struct mesh_prov_node_info *info;
|
|
uint8_t *out;
|
|
uint8_t type = *data++;
|
|
uint8_t fail_code[2];
|
|
uint32_t oob_key;
|
|
uint64_t decode_mic;
|
|
bool result;
|
|
|
|
if (rx_prov != prov || !prov->trans_tx)
|
|
return;
|
|
|
|
l_debug("Provisioning packet received type: %2.2x (%u octets)",
|
|
type, len);
|
|
|
|
if (type == prov->previous) {
|
|
l_error("Ignore repeated %2.2x packet", type);
|
|
return;
|
|
} else if (type > prov->expected || type < prov->previous) {
|
|
l_error("Expected %2.2x, Got:%2.2x", prov->expected, type);
|
|
fail_code[1] = PROV_ERR_UNEXPECTED_PDU;
|
|
goto failure;
|
|
}
|
|
|
|
if (type >= L_ARRAY_SIZE(expected_pdu_size) ||
|
|
len != expected_pdu_size[type]) {
|
|
l_error("Expected PDU size %d, Got %d (type: %2.2x)",
|
|
len, expected_pdu_size[type], type);
|
|
fail_code[1] = PROV_ERR_INVALID_FORMAT;
|
|
goto failure;
|
|
}
|
|
|
|
switch (type){
|
|
case PROV_INVITE: /* Prov Invite */
|
|
/* Prov Capabilities */
|
|
out = l_malloc(1 + sizeof(struct mesh_net_prov_caps));
|
|
out[0] = PROV_CAPS;
|
|
memcpy(out + 1, &prov->conf_inputs.caps,
|
|
sizeof(prov->conf_inputs.caps));
|
|
|
|
prov->conf_inputs.invite.attention = data[0];
|
|
|
|
prov->state = ACP_PROV_CAPS_SENT;
|
|
prov->expected = PROV_START;
|
|
prov->trans_tx(prov->trans_data,
|
|
out, sizeof(prov->conf_inputs.caps) + 1);
|
|
l_free(out);
|
|
break;
|
|
|
|
case PROV_START: /* Prov Start */
|
|
memcpy(&prov->conf_inputs.start, data,
|
|
sizeof(prov->conf_inputs.start));
|
|
|
|
if (prov->conf_inputs.start.algorithm ||
|
|
prov->conf_inputs.start.pub_key > 1 ||
|
|
prov->conf_inputs.start.auth_method > 3) {
|
|
fail_code[1] = PROV_ERR_INVALID_FORMAT;
|
|
goto failure;
|
|
}
|
|
|
|
if (prov->conf_inputs.start.pub_key) {
|
|
if (prov->conf_inputs.caps.pub_type) {
|
|
/* Prompt Agent for Private Key of OOB */
|
|
mesh_agent_request_private_key(prov->agent,
|
|
priv_key_cb, prov);
|
|
} else {
|
|
fail_code[1] = PROV_ERR_INVALID_PDU;
|
|
goto failure;
|
|
}
|
|
} else {
|
|
/* Ephemeral Public Key requested */
|
|
ecc_make_key(prov->conf_inputs.dev_pub_key,
|
|
prov->private_key);
|
|
swap_u256_bytes(prov->conf_inputs.dev_pub_key);
|
|
swap_u256_bytes(prov->conf_inputs.dev_pub_key + 32);
|
|
prov->material |= MAT_LOCAL_PRIVATE;
|
|
}
|
|
|
|
prov->expected = PROV_PUB_KEY;
|
|
break;
|
|
|
|
case PROV_PUB_KEY: /* Public Key */
|
|
/* Save Key */
|
|
memcpy(prov->conf_inputs.prv_pub_key, data, 64);
|
|
prov->material |= MAT_REMOTE_PUBLIC;
|
|
prov->expected = PROV_CONFIRM;
|
|
|
|
if ((prov->material & MAT_SECRET) != MAT_SECRET)
|
|
return;
|
|
|
|
acp_credentials(prov);
|
|
|
|
if (!prov->conf_inputs.start.pub_key) {
|
|
out = l_malloc(65);
|
|
out[0] = PROV_PUB_KEY;
|
|
memcpy(out + 1, prov->conf_inputs.dev_pub_key, 64);
|
|
prov->trans_tx(prov->trans_data, out, 65);
|
|
l_free(out);
|
|
}
|
|
|
|
/* Start Step 3 */
|
|
switch (prov->conf_inputs.start.auth_method) {
|
|
default:
|
|
case 0:
|
|
/* Auth Type 3c - No OOB */
|
|
break;
|
|
|
|
case 1:
|
|
/* Auth Type 3c - Static OOB */
|
|
/* Prompt Agent for Static OOB */
|
|
fail_code[1] = mesh_agent_request_static(prov->agent,
|
|
static_cb, prov);
|
|
|
|
if (fail_code[1])
|
|
goto failure;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
/* Auth Type 3a - Output OOB */
|
|
l_getrandom(&oob_key, sizeof(oob_key));
|
|
oob_key %= digit_mod(prov->conf_inputs.start.auth_size);
|
|
|
|
/* Save two copies, for two confirmation values */
|
|
l_put_be32(oob_key, prov->rand_auth_workspace + 28);
|
|
l_put_be32(oob_key, prov->rand_auth_workspace + 44);
|
|
prov->material |= MAT_RAND_AUTH;
|
|
|
|
if (prov->conf_inputs.start.auth_action ==
|
|
PROV_ACTION_OUT_ALPHA) {
|
|
/* TODO: Construst NUL-term string to pass */
|
|
fail_code[1] = mesh_agent_display_string(
|
|
prov->agent, NULL, NULL, prov);
|
|
} else {
|
|
/* Ask Agent to Display U32 */
|
|
fail_code[1] = mesh_agent_display_number(
|
|
prov->agent, false,
|
|
prov->conf_inputs.start.auth_action,
|
|
oob_key, NULL, prov);
|
|
}
|
|
|
|
if (fail_code[1])
|
|
goto failure;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
/* Auth Type 3b - input OOB */
|
|
/* Prompt Agent for Input OOB */
|
|
if (prov->conf_inputs.start.auth_action ==
|
|
PROV_ACTION_IN_ALPHA) {
|
|
fail_code[1] = mesh_agent_prompt_alpha(
|
|
prov->agent,
|
|
static_cb, prov);
|
|
} else {
|
|
fail_code[1] = mesh_agent_prompt_number(
|
|
prov->agent, false,
|
|
prov->conf_inputs.start.auth_action,
|
|
number_cb, prov);
|
|
}
|
|
|
|
if (fail_code[1])
|
|
goto failure;
|
|
|
|
break;
|
|
}
|
|
|
|
prov->expected = PROV_CONFIRM;
|
|
break;
|
|
|
|
case PROV_CONFIRM: /* Confirmation */
|
|
out = l_malloc(17);
|
|
out[0] = PROV_CONFIRM;
|
|
|
|
/* Calculate and Send our Confirmation */
|
|
mesh_crypto_aes_cmac(prov->calc_key, prov->rand_auth_workspace,
|
|
32, out + 1);
|
|
prov->trans_tx(prov->trans_data, out, 17);
|
|
l_free(out);
|
|
|
|
/* Save Provisioners confirmation for later compare */
|
|
memcpy(prov->confirm, data, 16);
|
|
prov->expected = PROV_RANDOM;
|
|
break;
|
|
|
|
case PROV_RANDOM: /* Random Value */
|
|
out = l_malloc(17);
|
|
/* Calculate Session key (needed later) while data is fresh */
|
|
mesh_crypto_prov_prov_salt(prov->salt, data,
|
|
prov->rand_auth_workspace,
|
|
prov->salt);
|
|
mesh_crypto_session_key(prov->secret, prov->salt, prov->s_key);
|
|
mesh_crypto_nonce(prov->secret, prov->salt, prov->s_nonce);
|
|
|
|
/* Calculate expected Provisioner Confirm */
|
|
memcpy(prov->rand_auth_workspace + 16, data, 16);
|
|
mesh_crypto_aes_cmac(prov->calc_key,
|
|
prov->rand_auth_workspace + 16, 32, out);
|
|
|
|
/* Compare our calculation with Provisioners */
|
|
if (memcmp(out, prov->confirm, 16)) {
|
|
fail_code[1] = PROV_ERR_CONFIRM_FAILED;
|
|
l_free(out);
|
|
goto failure;
|
|
}
|
|
|
|
/* Send Random value we used */
|
|
out[0] = PROV_RANDOM;
|
|
memcpy(out + 1, prov->rand_auth_workspace, 16);
|
|
prov->trans_tx(prov->trans_data, out, 17);
|
|
l_free(out);
|
|
prov->expected = PROV_DATA;
|
|
break;
|
|
|
|
case PROV_DATA: /* Provisioning Data */
|
|
|
|
/* Calculate our device key */
|
|
mesh_crypto_device_key(prov->secret,
|
|
prov->salt,
|
|
prov->calc_key);
|
|
|
|
/* Decrypt new node data into workspace */
|
|
mesh_crypto_aes_ccm_decrypt(prov->s_nonce, prov->s_key,
|
|
NULL, 0,
|
|
data, len - 1, prov->rand_auth_workspace,
|
|
&decode_mic, sizeof(decode_mic));
|
|
|
|
/* Validate that the data hasn't been messed with in transit */
|
|
if (l_get_be64(data + 25) != decode_mic) {
|
|
l_error("Provisioning Failed-MIC compare");
|
|
fail_code[1] = PROV_ERR_DECRYPT_FAILED;
|
|
goto failure;
|
|
}
|
|
|
|
info = l_malloc(sizeof(struct mesh_prov_node_info));
|
|
|
|
memcpy(info->device_key, prov->calc_key, 16);
|
|
memcpy(info->net_key, prov->rand_auth_workspace, 16);
|
|
info->net_index = l_get_be16(prov->rand_auth_workspace + 16);
|
|
info->flags = prov->rand_auth_workspace[18];
|
|
info->iv_index = l_get_be32(prov->rand_auth_workspace + 19);
|
|
info->unicast = l_get_be16(prov->rand_auth_workspace + 23);
|
|
|
|
result = prov->cmplt(prov->caller_data, PROV_ERR_SUCCESS, info);
|
|
prov->cmplt = NULL;
|
|
l_free(info);
|
|
|
|
if (result) {
|
|
prov->rand_auth_workspace[0] = PROV_COMPLETE;
|
|
prov->trans_tx(prov->trans_data,
|
|
prov->rand_auth_workspace, 1);
|
|
goto cleanup;
|
|
} else {
|
|
fail_code[1] = PROV_ERR_UNEXPECTED_ERR;
|
|
goto failure;
|
|
}
|
|
break;
|
|
|
|
case PROV_FAILED: /* Provisioning Error -- abort */
|
|
/* TODO: Call Complete Callback (Fail)*/
|
|
prov->cmplt(prov->caller_data,
|
|
data[0] ? data[0] : PROV_ERR_UNEXPECTED_ERR,
|
|
NULL);
|
|
prov->cmplt = NULL;
|
|
goto cleanup;
|
|
}
|
|
|
|
prov->previous = type;
|
|
return;
|
|
|
|
failure:
|
|
fail_code[0] = PROV_FAILED;
|
|
prov->trans_tx(prov->trans_data, fail_code, 2);
|
|
if (prov->cmplt)
|
|
prov->cmplt(prov->caller_data, fail_code[1], NULL);
|
|
prov->cmplt = NULL;
|
|
|
|
cleanup:
|
|
l_timeout_remove(prov->timeout);
|
|
|
|
/* Give PB Link 5 seconds to end session */
|
|
prov->timeout = l_timeout_create(5, prov_to, prov, NULL);
|
|
}
|
|
|
|
static void acp_prov_ack(void *user_data, uint8_t msg_num)
|
|
{
|
|
/* TODO: Handle PB-ADV Ack */
|
|
}
|
|
|
|
|
|
/* This starts unprovisioned device beacon */
|
|
bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
|
|
uint16_t algorithms, uint32_t timeout,
|
|
struct mesh_agent *agent,
|
|
mesh_prov_acceptor_complete_func_t complete_cb,
|
|
void *caller_data)
|
|
{
|
|
struct mesh_agent_prov_caps *caps;
|
|
uint8_t beacon[24] = {MESH_AD_TYPE_BEACON,
|
|
BEACON_TYPE_UNPROVISIONED};
|
|
uint8_t len = sizeof(beacon) - sizeof(uint32_t);
|
|
bool result;
|
|
|
|
/* Invoked from Join() method in mesh-api.txt, to join a
|
|
* remote mesh network.
|
|
*/
|
|
|
|
if (prov)
|
|
return false;
|
|
|
|
prov = l_new(struct mesh_prov_acceptor, 1);
|
|
prov->to_secs = timeout;
|
|
prov->agent = agent;
|
|
prov->cmplt = complete_cb;
|
|
prov->previous = -1;
|
|
prov->caller_data = caller_data;
|
|
|
|
caps = mesh_agent_get_caps(agent);
|
|
|
|
/* TODO: Should we sanity check values here or elsewhere? */
|
|
prov->conf_inputs.caps.num_ele = num_ele;
|
|
prov->conf_inputs.caps.pub_type = caps->pub_type;
|
|
prov->conf_inputs.caps.static_type = caps->static_type;
|
|
prov->conf_inputs.caps.output_size = caps->output_size;
|
|
prov->conf_inputs.caps.input_size = caps->input_size;
|
|
|
|
/* Store UINT16 values in Over-the-Air order, in packed structure
|
|
* for crypto inputs
|
|
*/
|
|
l_put_be16(algorithms, &prov->conf_inputs.caps.algorithms);
|
|
l_put_be16(caps->output_action, &prov->conf_inputs.caps.output_action);
|
|
l_put_be16(caps->input_action, &prov->conf_inputs.caps.input_action);
|
|
|
|
/* Compose Unprovisioned Beacon */
|
|
memcpy(beacon + 2, uuid, 16);
|
|
l_put_be16(caps->oob_info, beacon + 18);
|
|
if (caps->oob_info & OOB_INFO_URI_HASH){
|
|
l_put_be32(caps->uri_hash, beacon + 20);
|
|
len += sizeof(uint32_t);
|
|
}
|
|
|
|
/* Infinitely Beacon until Canceled, or Provisioning Starts */
|
|
result = mesh_send_pkt(0, 500, beacon, len);
|
|
|
|
if (!result)
|
|
goto error_fail;
|
|
|
|
/* Always register for PB-ADV */
|
|
result = pb_adv_reg(acp_prov_open, acp_prov_close, acp_prov_rx,
|
|
acp_prov_ack, uuid, prov);
|
|
|
|
if (result)
|
|
return true;
|
|
|
|
error_fail:
|
|
acceptor_free();
|
|
return false;
|
|
}
|
|
|
|
void acceptor_cancel(void *user_data)
|
|
{
|
|
acceptor_free();
|
|
}
|