/* * Copyright (C) 2017 BlueKitchen GmbH * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * 4. Any redistribution, use, or modification is done solely for * personal benefit and not for any commercial purpose or for * monetary gain. * * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Please inquire about commercial licensing options at * contact@bluekitchen-gmbh.com * */ #define __BTSTACK_FILE__ "provisioning_device.c" #include #include #include #include #include "ble/mesh/pb_adv.h" #include "ble/mesh/pb_gatt.h" #include "ble/mesh/mesh_crypto.h" #include "classic/rfcomm.h" // for crc8 #include "btstack.h" #include "provisioning.h" static void provisioning_attention_timer_set(void); static void prov_key_generated(void * arg); // remote ecc static uint8_t remote_ec_q[64]; static uint8_t dhkey[32]; static btstack_packet_handler_t prov_packet_handler; static uint8_t prov_buffer_out[MESH_PROV_MAX_PROXY_PDU]; // ConfirmationInputs = ProvisioningInvitePDUValue || ProvisioningCapabilitiesPDUValue || ProvisioningStartPDUValue || PublicKeyProvisioner || PublicKeyDevice static uint8_t prov_confirmation_inputs[1 + 11 + 5 + 64 + 64]; static uint8_t prov_authentication_method; static uint8_t prov_authentication_action; static uint8_t prov_public_key_oob_used; static uint8_t prov_emit_public_key_oob_active; static uint8_t prov_emit_output_oob_active; static uint8_t prov_ec_q[64]; static const uint8_t * prov_public_key_oob_q; static const uint8_t * prov_public_key_oob_d; // num elements static uint8_t prov_num_elements = 1; // capabilites static const uint8_t * prov_static_oob_data; static uint16_t prov_static_oob_len; static uint16_t prov_output_oob_actions; static uint16_t prov_input_oob_actions; static uint8_t prov_public_key_oob_available; static uint8_t prov_static_oob_available; static uint8_t prov_output_oob_size; static uint8_t prov_input_oob_size; static uint8_t prov_error_code; static uint8_t prov_waiting_for_outgoing_complete; static uint8_t prov_attention_timer_timeout; static btstack_timer_source_t prov_attention_timer; static btstack_timer_source_t prov_protocol_timer; static btstack_crypto_aes128_cmac_t prov_cmac_request; static btstack_crypto_random_t prov_random_request; static btstack_crypto_ecc_p256_t prov_ecc_p256_request; static btstack_crypto_ccm_t prov_ccm_request; // ConfirmationDevice static uint8_t confirmation_device[16]; // ConfirmationSalt static uint8_t confirmation_salt[16]; // ConfirmationKey static uint8_t confirmation_key[16]; // RandomDevice static uint8_t random_device[16]; // ProvisioningSalt static uint8_t provisioning_salt[16]; // AuthValue static uint8_t auth_value[16]; // SessionKey static uint8_t session_key[16]; // SessionNonce static uint8_t session_nonce[16]; // EncProvisioningData static uint8_t enc_provisioning_data[25]; // ProvisioningData static uint8_t provisioning_data[25]; // DeviceKey static uint8_t device_key[16]; // NetKey static uint8_t net_key[16]; // NetKeyIndex static uint16_t net_key_index; // k2: NID (7), EncryptionKey (128), PrivacyKey (128) static uint8_t k2_result[33]; static uint8_t flags; static uint32_t iv_index; static uint16_t unicast_address; static const uint8_t id128_tag[] = { 'i', 'd', '1', '2', '8', 0x01}; // AES-CMAC_ZERO('nhbk') static const uint8_t mesh_salt_nhbk[] = { 0x2c, 0x24, 0x61, 0x9a, 0xb7, 0x93, 0xc1, 0x23, 0x3f, 0x6e, 0x22, 0x67, 0x38, 0x39, 0x3d, 0xec, }; // AES-CMAC_ZERO('nkik') static const uint8_t mesh_salt_nkik[] = { 0xF8, 0x79, 0x5A, 0x1A, 0xAB, 0xF1, 0x82, 0xE4, 0xF1, 0x63, 0xD8, 0x6E, 0x24, 0x5E, 0x19, 0xF4}; typedef enum { DEVICE_W4_INVITE, DEVICE_SEND_CAPABILITIES, DEVICE_W4_START, DEVICE_W4_INPUT_OOK, DEVICE_SEND_INPUT_COMPLETE, DEVICE_W4_PUB_KEY, DEVICE_SEND_PUB_KEY, DEVICE_W4_CONFIRM, DEVICE_SEND_CONFIRM, DEVICE_W4_RANDOM, DEVICE_SEND_RANDOM, DEVICE_W4_DATA, DEVICE_SEND_COMPLETE, DEVICE_SEND_ERROR, } device_state_t; static device_state_t device_state; static uint16_t pb_transport_cid; // derived static uint8_t network_id[8]; static uint8_t beacon_key[16]; static uint8_t identity_key[16]; static pb_type_t pb_type; static void pb_send_pdu(uint16_t transport_cid, const uint8_t * buffer, uint16_t buffer_size){ switch (pb_type){ case PB_TYPE_ADV: pb_adv_send_pdu(transport_cid, buffer, buffer_size); break; case PB_TYPE_GATT: pb_gatt_send_pdu(transport_cid, buffer, buffer_size); break; } } static void pb_close_link(uint16_t transport_cid, uint8_t reason){ switch (pb_type){ case PB_TYPE_ADV: pb_adv_close_link(transport_cid, reason); break; case PB_TYPE_GATT: pb_gatt_close_link(transport_cid, reason); break; } } static void provisioning_emit_event(uint16_t pb_adv_cid, uint8_t mesh_subevent){ if (!prov_packet_handler) return; uint8_t event[5] = { HCI_EVENT_MESH_META, 3, mesh_subevent}; little_endian_store_16(event, 3, pb_adv_cid); prov_packet_handler(HCI_EVENT_PACKET, 0, event, sizeof(event)); } static void provisioning_emit_output_oob_event(uint16_t pb_adv_cid, uint32_t number){ if (!prov_packet_handler) return; uint8_t event[9] = { HCI_EVENT_MESH_META, 7, MESH_PB_PROV_START_EMIT_OUTPUT_OOB}; little_endian_store_16(event, 3, pb_adv_cid); little_endian_store_16(event, 5, number); prov_packet_handler(HCI_EVENT_PACKET, 0, event, sizeof(event)); } static void provisioning_emit_attention_timer_event(uint16_t pb_adv_cid, uint8_t timer_s){ if (!prov_packet_handler) return; uint8_t event[4] = { HCI_EVENT_MESH_META, 7, MESH_PB_PROV_ATTENTION_TIMER}; event[3] = timer_s; prov_packet_handler(HCI_EVENT_PACKET, 0, event, sizeof(event)); } static void provisiong_timer_handler(btstack_timer_source_t * ts){ UNUSED(ts); printf("Provisioning Protocol Timeout -> Close Link!\n"); pb_close_link(1, 1); } // The provisioning protocol shall have a minimum timeout of 60 seconds that is reset // each time a provisioning protocol PDU is sent or received static void provisioning_timer_start(void){ btstack_run_loop_remove_timer(&prov_protocol_timer); btstack_run_loop_set_timer_handler(&prov_protocol_timer, &provisiong_timer_handler); btstack_run_loop_set_timer(&prov_protocol_timer, PROVISIONING_PROTOCOL_TIMEOUT_MS); btstack_run_loop_add_timer(&prov_protocol_timer); } static void provisioning_timer_stop(void){ btstack_run_loop_remove_timer(&prov_protocol_timer); } static void provisioning_attention_timer_timeout(btstack_timer_source_t * ts){ UNUSED(ts); if (prov_attention_timer_timeout == 0) return; prov_attention_timer_timeout--; provisioning_attention_timer_set(); } static void provisioning_attention_timer_set(void){ provisioning_emit_attention_timer_event(1, prov_attention_timer_timeout); if (prov_attention_timer_timeout){ btstack_run_loop_set_timer_handler(&prov_attention_timer, &provisioning_attention_timer_timeout); btstack_run_loop_set_timer(&prov_attention_timer, 1000); btstack_run_loop_add_timer(&prov_attention_timer); } } // Outgoing Provisioning PDUs static void provisioning_send_provisioning_error(void){ // setup response prov_buffer_out[0] = MESH_PROV_FAILED; prov_buffer_out[1] = prov_error_code; pb_send_pdu(pb_transport_cid, prov_buffer_out, 2); } static void provisioning_send_capabilites(void){ // setup response prov_buffer_out[0] = MESH_PROV_CAPABILITIES; /* Number of Elements supported */ prov_buffer_out[1] = prov_num_elements; /* Supported algorithms - FIPS P-256 Eliptic Curve */ big_endian_store_16(prov_buffer_out, 2, 1); /* Public Key Type - Public Key OOB information available */ prov_buffer_out[4] = prov_public_key_oob_available; /* Static OOB Type - Static OOB information available */ prov_buffer_out[5] = prov_static_oob_available; /* Output OOB Size - max of 8 */ prov_buffer_out[6] = prov_output_oob_size; /* Output OOB Action */ big_endian_store_16(prov_buffer_out, 7, prov_output_oob_actions); /* Input OOB Size - max of 8*/ prov_buffer_out[9] = prov_input_oob_size; /* Input OOB Action */ big_endian_store_16(prov_buffer_out, 10, prov_input_oob_actions); // store for confirmation inputs: len 11 memcpy(&prov_confirmation_inputs[1], &prov_buffer_out[1], 11); // send pb_send_pdu(pb_transport_cid, prov_buffer_out, 12); } static void provisioning_send_public_key(void){ // setup response prov_buffer_out[0] = MESH_PROV_PUB_KEY; memcpy(&prov_buffer_out[1], prov_ec_q, 64); // store for confirmation inputs: len 64 memcpy(&prov_confirmation_inputs[81], &prov_buffer_out[1], 64); // send pb_send_pdu(pb_transport_cid, prov_buffer_out, 65); } static void provisioning_send_input_complete(void){ // setup response prov_buffer_out[0] = MESH_PROV_INPUT_COMPLETE; // send pb_send_pdu(pb_transport_cid, prov_buffer_out, 17); } static void provisioning_send_confirm(void){ // setup response prov_buffer_out[0] = MESH_PROV_CONFIRM; memcpy(&prov_buffer_out[1], confirmation_device, 16); // send pb_send_pdu(pb_transport_cid, prov_buffer_out, 17); } static void provisioning_send_random(void){ // setup response prov_buffer_out[0] = MESH_PROV_RANDOM; memcpy(&prov_buffer_out[1], random_device, 16); // send pdu pb_send_pdu(pb_transport_cid, prov_buffer_out, 17); } static void provisioning_send_complete(void){ // setup response prov_buffer_out[0] = MESH_PROV_COMPLETE; // send pdu pb_send_pdu(pb_transport_cid, prov_buffer_out, 1); } static void provisioning_done(void){ if (prov_emit_public_key_oob_active){ prov_emit_public_key_oob_active = 0; provisioning_emit_event(1, MESH_PB_PROV_STOP_EMIT_PUBLIC_KEY_OOB); } if (prov_emit_output_oob_active){ prov_emit_output_oob_active = 0; provisioning_emit_event(1, MESH_PB_PROV_STOP_EMIT_OUTPUT_OOB); } if (prov_attention_timer_timeout){ prov_attention_timer_timeout = 0; provisioning_emit_attention_timer_event(1, 0); } device_state = DEVICE_W4_INVITE; // generate new public key printf("Generate new public key\n"); btstack_crypto_ecc_p256_generate_key(&prov_ecc_p256_request, prov_ec_q, &prov_key_generated, NULL); } static void provisioning_handle_auth_value_output_oob(void * arg){ // limit auth value to single digit auth_value[15] = auth_value[15] % 9 + 1; printf("Output OOB: %u\n", auth_value[15]); // emit output oob value provisioning_emit_output_oob_event(1, auth_value[15]); prov_emit_output_oob_active = 1; } static void provisioning_public_key_exchange_complete(void){ // reset auth_value memset(auth_value, 0, sizeof(auth_value)); // handle authentication method switch (prov_authentication_method){ case 0x00: device_state = DEVICE_W4_CONFIRM; break; case 0x01: memcpy(&auth_value[16-prov_static_oob_len], prov_static_oob_data, prov_static_oob_len); device_state = DEVICE_W4_CONFIRM; break; case 0x02: device_state = DEVICE_W4_CONFIRM; printf("Generate random for auth_value\n"); // generate single byte of random data to use for authentication btstack_crypto_random_generate(&prov_random_request, &auth_value[15], 1, &provisioning_handle_auth_value_output_oob, NULL); break; case 0x03: // Input OOB printf("Input OOB requested\n"); provisioning_emit_event(1, MESH_PB_PROV_INPUT_OOB_REQUEST); device_state = DEVICE_W4_INPUT_OOK; break; default: break; } } static void provisioning_run(void){ printf("provisioning_run: state %x, wait for outgoing complete %u\n", device_state, prov_waiting_for_outgoing_complete); if (prov_waiting_for_outgoing_complete) return; int start_timer = 1; switch (device_state){ case DEVICE_SEND_ERROR: start_timer = 0; // game over prov_waiting_for_outgoing_complete = 1; provisioning_send_provisioning_error(); provisioning_done(); break; case DEVICE_SEND_CAPABILITIES: device_state = DEVICE_W4_START; prov_waiting_for_outgoing_complete = 1; provisioning_send_capabilites(); break; case DEVICE_SEND_INPUT_COMPLETE: device_state = DEVICE_W4_CONFIRM; prov_waiting_for_outgoing_complete = 1; provisioning_send_input_complete(); break; case DEVICE_SEND_PUB_KEY: prov_waiting_for_outgoing_complete = 1; provisioning_send_public_key(); provisioning_public_key_exchange_complete(); break; case DEVICE_SEND_CONFIRM: device_state = DEVICE_W4_RANDOM; prov_waiting_for_outgoing_complete = 1; provisioning_send_confirm(); break; case DEVICE_SEND_RANDOM: device_state = DEVICE_W4_DATA; prov_waiting_for_outgoing_complete = 1; provisioning_send_random(); break; case DEVICE_SEND_COMPLETE: prov_waiting_for_outgoing_complete = 1; provisioning_send_complete(); provisioning_done(); break; default: return; } if (start_timer){ provisioning_timer_start(); } } static void provisioning_handle_provisioning_error(uint8_t error_code){ printf("PROVISIONING ERROR\n"); provisioning_timer_stop(); prov_error_code = error_code; device_state = DEVICE_SEND_ERROR; provisioning_run(); } static void provisioning_handle_invite(uint8_t *packet, uint16_t size){ if (size != 1) return; // store for confirmation inputs: len 1 memcpy(&prov_confirmation_inputs[0], packet, 1); // handle invite message prov_attention_timer_timeout = packet[0]; provisioning_attention_timer_set(); device_state = DEVICE_SEND_CAPABILITIES; provisioning_run(); } static void provisioning_handle_start(uint8_t * packet, uint16_t size){ if (size != 5) return; // validate Algorithm int ok = 1; if (packet[0] > 0x00){ ok = 0; } // validate Publik Key if (packet[1] > 0x01){ ok = 0; } // validate Authentication Method switch (packet[2]){ case 0: case 1: if (packet[3] != 0 || packet[4] != 0){ ok = 0; break; } break; case 2: if (packet[3] > 0x04 || packet[4] == 0 || packet[4] > 0x08){ ok = 0; break; } break; case 3: if (packet[3] > 0x03 || packet[4] == 0 || packet[4] > 0x08){ ok = 0; break; } break; } if (!ok){ printf("PROV_START arguments incorrect\n"); provisioning_handle_provisioning_error(0x02); return; } // store for confirmation inputs: len 5 memcpy(&prov_confirmation_inputs[12], packet, 5); // public key oob prov_public_key_oob_used = packet[1]; // authentication method prov_authentication_method = packet[2]; // start emit public OOK if specified if (prov_public_key_oob_available && prov_public_key_oob_used){ provisioning_emit_event(1, MESH_PB_PROV_START_EMIT_PUBLIC_KEY_OOB); } printf("PublicKey: %02x\n", prov_public_key_oob_used); printf("AuthMethod: %02x\n", prov_authentication_method); device_state = DEVICE_W4_PUB_KEY; provisioning_run(); } static void provisioning_handle_public_key_dhkey(void * arg){ UNUSED(arg); printf("DHKEY: "); printf_hexdump(dhkey, sizeof(dhkey)); // skip sending own public key when public key oob is used if (prov_public_key_oob_available && prov_public_key_oob_used){ // just copy key for confirmation inputs memcpy(&prov_confirmation_inputs[81], prov_ec_q, 64); provisioning_public_key_exchange_complete(); } else { // queue public key pdu printf("DEVICE_SEND_PUB_KEY\n"); device_state = DEVICE_SEND_PUB_KEY; } provisioning_run(); } static void provisioning_handle_public_key(uint8_t *packet, uint16_t size){ // validate public key if (size != sizeof(remote_ec_q) || btstack_crypto_ecc_p256_validate_public_key(packet) != 0){ printf("Public Key invalid, abort provisioning\n"); provisioning_handle_provisioning_error(0x07); // Unexpected Error return; } // stop emit public OOK if specified and send to crypto module if (prov_public_key_oob_available && prov_public_key_oob_used){ provisioning_emit_event(1, MESH_PB_PROV_STOP_EMIT_PUBLIC_KEY_OOB); printf("Replace generated ECC with Public Key OOB:"); memcpy(prov_ec_q, prov_public_key_oob_q, 64); printf_hexdump(prov_ec_q, sizeof(prov_ec_q)); btstack_crypto_ecc_p256_set_key(prov_public_key_oob_q, prov_public_key_oob_d); } // store for confirmation inputs: len 64 memcpy(&prov_confirmation_inputs[17], packet, 64); // store remote q memcpy(remote_ec_q, packet, sizeof(remote_ec_q)); // calculate DHKey btstack_crypto_ecc_p256_calculate_dhkey(&prov_ecc_p256_request, remote_ec_q, dhkey, provisioning_handle_public_key_dhkey, NULL); } static void provisioning_handle_confirmation_device_calculated(void * arg){ UNUSED(arg); printf("ConfirmationDevice: "); printf_hexdump(confirmation_device, sizeof(confirmation_device)); device_state = DEVICE_SEND_CONFIRM; provisioning_run(); } static void provisioning_handle_confirmation_random_device(void * arg){ // re-use prov_confirmation_inputs buffer memcpy(&prov_confirmation_inputs[0], random_device, 16); memcpy(&prov_confirmation_inputs[16], auth_value, 16); // calc confirmation device btstack_crypto_aes128_cmac_message(&prov_cmac_request, confirmation_key, 32, prov_confirmation_inputs, confirmation_device, &provisioning_handle_confirmation_device_calculated, NULL); } static void provisioning_handle_confirmation_k1_calculated(void * arg){ printf("ConfirmationKey: "); printf_hexdump(confirmation_key, sizeof(confirmation_key)); printf("AuthValue: "); printf_hexdump(auth_value, 16); // generate random_device btstack_crypto_random_generate(&prov_random_request,random_device, 16, &provisioning_handle_confirmation_random_device, NULL); } static void provisioning_handle_confirmation_s1_calculated(void * arg){ UNUSED(arg); // ConfirmationSalt printf("ConfirmationSalt: "); printf_hexdump(confirmation_salt, sizeof(confirmation_salt)); // ConfirmationKey mesh_k1(&prov_cmac_request, dhkey, sizeof(dhkey), confirmation_salt, (const uint8_t*) "prck", 4, confirmation_key, &provisioning_handle_confirmation_k1_calculated, NULL); } static void provisioning_handle_confirmation(uint8_t *packet, uint16_t size){ UNUSED(size); UNUSED(packet); // if (prov_emit_output_oob_active){ prov_emit_output_oob_active = 0; provisioning_emit_event(1, MESH_PB_PROV_STOP_EMIT_OUTPUT_OOB); } // CalculationInputs printf("ConfirmationInputs: "); printf_hexdump(prov_confirmation_inputs, sizeof(prov_confirmation_inputs)); // calculate s1 btstack_crypto_aes128_cmac_zero(&prov_cmac_request, sizeof(prov_confirmation_inputs), prov_confirmation_inputs, confirmation_salt, &provisioning_handle_confirmation_s1_calculated, NULL); } // PROV_RANDOM static void provisioning_handle_random_session_nonce_calculated(void * arg){ UNUSED(arg); // The nonce shall be the 13 least significant octets == zero most significant octets uint8_t temp[13]; memcpy(temp, &session_nonce[3], 13); memcpy(session_nonce, temp, 13); // SessionNonce printf("SessionNonce: "); printf_hexdump(session_nonce, 13); device_state = DEVICE_SEND_RANDOM; provisioning_run(); } static void provisioning_handle_random_session_key_calculated(void * arg){ UNUSED(arg); // SessionKey printf("SessionKey: "); printf_hexdump(session_key, sizeof(session_key)); // SessionNonce mesh_k1(&prov_cmac_request, dhkey, sizeof(dhkey), provisioning_salt, (const uint8_t*) "prsn", 4, session_nonce, &provisioning_handle_random_session_nonce_calculated, NULL); } static void provisioning_handle_random_s1_calculated(void * arg){ UNUSED(arg); // ProvisioningSalt printf("ProvisioningSalt: "); printf_hexdump(provisioning_salt, sizeof(provisioning_salt)); // SessionKey mesh_k1(&prov_cmac_request, dhkey, sizeof(dhkey), provisioning_salt, (const uint8_t*) "prsk", 4, session_key, &provisioning_handle_random_session_key_calculated, NULL); } static void provisioning_handle_random(uint8_t *packet, uint16_t size){ UNUSED(size); UNUSED(packet); // TODO: validate Confirmation // calc ProvisioningSalt = s1(ConfirmationSalt || RandomProvisioner || RandomDevice) memcpy(&prov_confirmation_inputs[0], confirmation_salt, 16); memcpy(&prov_confirmation_inputs[16], packet, 16); memcpy(&prov_confirmation_inputs[32], random_device, 16); btstack_crypto_aes128_cmac_zero(&prov_cmac_request, 48, prov_confirmation_inputs, provisioning_salt, &provisioning_handle_random_s1_calculated, NULL); } // PROV_DATA static void provisioning_handle_data_k2_calculated(void * arg){ // Dump printf("NID: %02x\n", k2_result[0]); printf("EncryptionKey: "); printf_hexdump(&k2_result[1], 16); printf("PrivacyKey: "); printf_hexdump(&k2_result[17], 16); // provisioning_timer_stop(); // notify client provisioning_emit_event(1, MESH_PB_PROV_COMPLETE); device_state = DEVICE_SEND_COMPLETE; provisioning_run(); } static void provisioning_handle_beacon_key_calculated(void *arg){ printf("IdentityKey: "); printf_hexdump(identity_key, 16); // calc k2 mesh_k2(&prov_cmac_request, net_key, k2_result, &provisioning_handle_data_k2_calculated, NULL); } static void provisioning_handle_identity_key_calculated(void *arg){ printf("BeaconKey: "); printf_hexdump(beacon_key, 16); // calc identity key mesh_k1(&prov_cmac_request, net_key, 16, mesh_salt_nkik, id128_tag, sizeof(id128_tag), identity_key, &provisioning_handle_beacon_key_calculated, NULL); } static void provisioning_handle_data_network_id_calculated(void * arg){ // dump printf("Network ID: "); printf_hexdump(network_id, 8); // calc k1 using mesh_k1(&prov_cmac_request, net_key, 16, mesh_salt_nhbk, id128_tag, sizeof(id128_tag), beacon_key, &provisioning_handle_identity_key_calculated, NULL); } static void provisioning_handle_data_device_key(void * arg){ // dump printf("DeviceKey: "); printf_hexdump(device_key, 16); // calculate Network ID mesh_k3(&prov_cmac_request, net_key, network_id, provisioning_handle_data_network_id_calculated, NULL); } static void provisioning_handle_data_ccm(void * arg){ UNUSED(arg); // validate MIC? uint8_t mic[8]; btstack_crypo_ccm_get_authentication_value(&prov_ccm_request, mic); printf("MIC: "); printf_hexdump(mic, 8); // sort provisoning data memcpy(net_key, provisioning_data, 16); net_key_index = big_endian_read_16(provisioning_data, 16); flags = provisioning_data[18]; iv_index = big_endian_read_32(provisioning_data, 19); unicast_address = big_endian_read_16(provisioning_data, 23); // dump printf("NetKey: "); printf_hexdump(net_key, 16); printf("NetKeyIndex: %04x\n", net_key_index); printf("Flags: %02x\n", flags); printf("IVIndex: %04x\n", iv_index); printf("UnicastAddress: %02x\n", unicast_address); // DeviceKey mesh_k1(&prov_cmac_request, dhkey, sizeof(dhkey), provisioning_salt, (const uint8_t*) "prdk", 4, device_key, &provisioning_handle_data_device_key, NULL); } static void provisioning_handle_data(uint8_t *packet, uint16_t size){ UNUSED(size); memcpy(enc_provisioning_data, packet, 25); // decode response btstack_crypo_ccm_init(&prov_ccm_request, session_key, session_nonce, 25, 0, 8); btstack_crypto_ccm_decrypt_block(&prov_ccm_request, 25, enc_provisioning_data, provisioning_data, &provisioning_handle_data_ccm, NULL); } static void provisioning_handle_unexpected_pdu(uint8_t *packet, uint16_t size){ printf("Unexpected PDU #%u in state #%u\n", packet[0], (int) device_state); provisioning_handle_provisioning_error(0x03); } static void provisioning_handle_pdu(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ if (size < 1) return; switch (packet_type){ case HCI_EVENT_PACKET: if (packet[0] != HCI_EVENT_MESH_META) break; switch (packet[2]){ case MESH_PB_TRANSPORT_LINK_OPEN: pb_transport_cid = mesh_pb_transport_link_open_event_get_pb_transport_cid(packet); pb_type = mesh_pb_transport_link_open_event_get_pb_type(packet); printf("Link opened, reset state, transport cid 0x%02x, PB type %d\n", pb_transport_cid, pb_type); provisioning_done(); break; case MESH_PB_TRANSPORT_PDU_SENT: printf("Outgoing packet acked\n"); prov_waiting_for_outgoing_complete = 0; break; case MESH_PB_TRANSPORT_LINK_CLOSED: printf("Link close, reset state\n"); pb_transport_cid = MESH_PB_TRANSPORT_INVALID_CID; provisioning_done(); break; } break; case PROVISIONING_DATA_PACKET: // check state switch (device_state){ case DEVICE_W4_INVITE: if (packet[0] != MESH_PROV_INVITE) provisioning_handle_unexpected_pdu(packet, size); printf("MESH_PROV_INVITE: "); printf_hexdump(&packet[1], size-1); provisioning_handle_invite(&packet[1], size-1); break; case DEVICE_W4_START: if (packet[0] != MESH_PROV_START) provisioning_handle_unexpected_pdu(packet, size); printf("MESH_PROV_START: "); printf_hexdump(&packet[1], size-1); provisioning_handle_start(&packet[1], size-1); break; case DEVICE_W4_PUB_KEY: if (packet[0] != MESH_PROV_PUB_KEY) provisioning_handle_unexpected_pdu(packet, size); printf("MESH_PROV_PUB_KEY: "); printf_hexdump(&packet[1], size-1); provisioning_handle_public_key(&packet[1], size-1); break; case DEVICE_W4_CONFIRM: if (packet[0] != MESH_PROV_CONFIRM) provisioning_handle_unexpected_pdu(packet, size); printf("MESH_PROV_CONFIRM: "); printf_hexdump(&packet[1], size-1); provisioning_handle_confirmation(&packet[1], size-1); break; case DEVICE_W4_RANDOM: if (packet[0] != MESH_PROV_RANDOM) provisioning_handle_unexpected_pdu(packet, size); printf("MESH_PROV_RANDOM: "); printf_hexdump(&packet[1], size-1); provisioning_handle_random(&packet[1], size-1); break; case DEVICE_W4_DATA: if (packet[0] != MESH_PROV_DATA) provisioning_handle_unexpected_pdu(packet, size); printf("MESH_PROV_DATA: "); provisioning_handle_data(&packet[1], size-1); break; default: break; } break; default: break; } provisioning_run(); } static void prov_key_generated(void * arg){ UNUSED(arg); printf("ECC-P256: "); printf_hexdump(prov_ec_q, sizeof(prov_ec_q)); // allow override if (prov_public_key_oob_available){ printf("Replace generated ECC with Public Key OOB:"); memcpy(prov_ec_q, prov_public_key_oob_q, 64); printf_hexdump(prov_ec_q, sizeof(prov_ec_q)); btstack_crypto_ecc_p256_set_key(prov_public_key_oob_q, prov_public_key_oob_d); } } void provisioning_device_init(const uint8_t * device_uuid){ // setup PB ADV pb_adv_init(device_uuid); pb_adv_register_packet_handler(&provisioning_handle_pdu); // setup PB GATT pb_gatt_init(device_uuid); pb_gatt_register_packet_handler(&provisioning_handle_pdu); pb_transport_cid = MESH_PB_TRANSPORT_INVALID_CID; // init provisioning state provisioning_done(); // generate public key btstack_crypto_ecc_p256_generate_key(&prov_ecc_p256_request, prov_ec_q, &prov_key_generated, NULL); } void provisioning_device_register_packet_handler(btstack_packet_handler_t packet_handler){ prov_packet_handler = packet_handler; } void provisioning_device_set_public_key_oob(const uint8_t * public_key, const uint8_t * private_key){ prov_public_key_oob_q = public_key; prov_public_key_oob_d = private_key; prov_public_key_oob_available = 1; btstack_crypto_ecc_p256_set_key(prov_public_key_oob_q, prov_public_key_oob_d); } void provisioning_device_set_static_oob(uint16_t static_oob_len, const uint8_t * static_oob_data){ prov_static_oob_available = 1; prov_static_oob_data = static_oob_data; prov_static_oob_len = btstack_min(static_oob_len, 16); } void provisioning_device_set_output_oob_actions(uint16_t supported_output_oob_action_types, uint8_t max_oob_output_size){ prov_output_oob_actions = supported_output_oob_action_types; prov_output_oob_size = max_oob_output_size; } void provisioning_device_set_input_oob_actions(uint16_t supported_input_oob_action_types, uint8_t max_oob_input_size){ prov_input_oob_actions = supported_input_oob_action_types; prov_input_oob_size = max_oob_input_size; } void provisioning_device_input_oob_complete_numeric(uint16_t pb_adv_cid, uint32_t input_oob){ UNUSED(pb_adv_cid); if (device_state != DEVICE_W4_INPUT_OOK) return; // store input_oob as auth value big_endian_store_32(auth_value, 12, input_oob); device_state = DEVICE_SEND_INPUT_COMPLETE; provisioning_run(); } void provisioning_device_input_oob_complete_alphanumeric(uint16_t pb_adv_cid, const uint8_t * input_oob_data, uint16_t input_oob_len){ UNUSED(pb_adv_cid); if (device_state != DEVICE_W4_INPUT_OOK) return; // store input_oob and fillup with zeros input_oob_len = btstack_min(input_oob_len, 16); memset(auth_value, 0, 16); memcpy(auth_value, input_oob_data, input_oob_len); device_state = DEVICE_SEND_INPUT_COMPLETE; provisioning_run(); } uint8_t provisioning_device_data_get_flags(void){ return flags; } uint16_t provisioning_device_data_get_unicast_address(void){ return unicast_address; } const uint8_t * provisioning_device_data_get_device_key(void){ return network_id; } const uint8_t * provisioning_device_data_get_network_id(void){ return network_id; } uint32_t provisioning_device_data_get_iv_index(void){ return iv_index; } const uint8_t * provisioning_device_data_get_beacon_key(void){ return beacon_key; } const uint8_t * provisioning_device_data_get_identity_key(void){ return identity_key; } uint8_t provisioning_device_data_get_nid(void){ return k2_result[0]; } const uint8_t * provisioning_device_data_get_encryption_key(void){ return &k2_result[1]; } const uint8_t * provisioning_device_data_get_privacy_key(void){ return &k2_result[17]; }