Merge branch 'develop'

HFP/HSP:
- store received audio as wav file

SM:
- send/receive keypress notifications. -> link into docs
- derive BR/EDR Link Key from LE LTK established via LE Secure Connection
- fix use of stored LTKs in all combinations (LE Legacy Paring/LE Secure Connections - Initiator/Responder)
- truncate LTK in LE Secure Connections if established key size is less than 16 bytes
- implement slower elliptic curve multiplication -> less than 800 bytes for 32-bit CPUs

POSIX:
- add le_device_db_fs that stores db at /tmp/btstack_le_device_db.txt as basic csv
This commit is contained in:
Matthias Ringwald 2016-06-29 11:18:16 +02:00
commit 140b81662c
33 changed files with 1174 additions and 206 deletions

View File

@ -2456,7 +2456,8 @@ void sm_mbedtls_allocator_free(void * data);
/* ECP options */
//#define MBEDTLS_ECP_MAX_BITS 521 /**< Maximum bit size of groups */
#define MBEDTLS_ECP_WINDOW_SIZE 2 /**< Maximum window size used */
#define MBEDTLS_ECP_WINDOW_SIZE 1 /**< Maximum window size used - 1 == uses double and add to save RAM */
#define MBEDTLS_ECP_MUL_NAIVE_SAFE 0 /**< Enable use of temporary point for window size == 1 - adds ~100 bytes but makes branches independ from n */
#define MBEDTLS_ECP_FIXED_POINT_OPTIM 0 /**< Enable fixed-point speed-up */
/* Entropy options */

View File

@ -728,6 +728,25 @@ cleanup:
MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( &N, &N, &grp->P ) )
#if defined(ECP_SHORTWEIERSTRASS)
/*
* Check and define parameters used by the comb method (see below for details)
*/
#if MBEDTLS_ECP_WINDOW_SIZE > 7
#error "MBEDTLS_ECP_WINDOW_SIZE out of bounds"
#endif
#if MBEDTLS_ECP_WINDOW_SIZE >= 2
#define ECP_COMB_METHOD
/* d = ceil( n / w ) */
#define COMB_MAX_D ( MBEDTLS_ECP_MAX_BITS + 1 ) / 2
/* number of precomputed points */
#define COMB_MAX_PRE ( 1 << ( MBEDTLS_ECP_WINDOW_SIZE - 1 ) )
#endif
/*
* For curves in short Weierstrass form, we do all the internal operations in
* Jacobian coordinates.
@ -775,6 +794,7 @@ cleanup:
return( ret );
}
#ifdef ECP_COMB_METHOD
/*
* Normalize jacobian coordinates of an array of (pointers to) points,
* using Montgomery's trick to perform only one inversion mod P.
@ -887,6 +907,7 @@ cleanup:
return( ret );
}
#endif
/*
* Point doubling R = 2 P, Jacobian coordinates
@ -1068,6 +1089,8 @@ cleanup:
return( ret );
}
#ifdef ECP_COMB_METHOD
/*
* Randomize jacobian coordinates:
* (X, Y, Z) -> (l^2 X, l^3 Y, l Z) for random l
@ -1115,19 +1138,6 @@ cleanup:
return( ret );
}
/*
* Check and define parameters used by the comb method (see below for details)
*/
#if MBEDTLS_ECP_WINDOW_SIZE < 2 || MBEDTLS_ECP_WINDOW_SIZE > 7
#error "MBEDTLS_ECP_WINDOW_SIZE out of bounds"
#endif
/* d = ceil( n / w ) */
#define COMB_MAX_D ( MBEDTLS_ECP_MAX_BITS + 1 ) / 2
/* number of precomputed points */
#define COMB_MAX_PRE ( 1 << ( MBEDTLS_ECP_WINDOW_SIZE - 1 ) )
/*
* Compute the representation of m that will be used with our comb method.
*
@ -1422,6 +1432,63 @@ cleanup:
return( ret );
}
#else /* ECP_COMB_METHOD */
/*
* Multiplication using the double and add method,
* for curves in short Weierstrass form to optimize for RAM usage vs. speed
*
* Implemented by BlueKitchen GmbH
* Note:
*/
static int ecp_mul_naive( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
const mbedtls_mpi *m, const mbedtls_ecp_point *P,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng )
{
int ret;
int i;
#if MBEDTLS_ECP_MUL_NAIVE_SAFE == 1
mbedtls_ecp_point T;
mbedtls_ecp_point_init(&T);
#endif
// calculate m * p using double and add in decreasing fashion to use ecp_add_mixed
mbedtls_ecp_point_init(R);
for (i=grp->nbits;i >= 0;i--){
MBEDTLS_MPI_CHK( ecp_double_jac( grp, R, R ) );
#if MBEDTLS_ECP_MUL_NAIVE_SAFE == 1
MBEDTLS_MPI_CHK( ecp_add_mixed(grp, &T, R, P) );
int bit_set = mbedtls_mpi_get_bit(m, i);
MBEDTLS_MPI_CHK( mbedtls_mpi_safe_cond_assign(&R->X, &T.X, bit_set));
MBEDTLS_MPI_CHK( mbedtls_mpi_safe_cond_assign(&R->Y, &T.Y, bit_set));
MBEDTLS_MPI_CHK( mbedtls_mpi_safe_cond_assign(&R->Y, &T.Y, bit_set));
#else
if (mbedtls_mpi_get_bit(m, i)){
MBEDTLS_MPI_CHK( ecp_add_mixed(grp, R, R, P) );
}
#endif
}
// normalize jacobian coordinates
MBEDTLS_MPI_CHK( ecp_normalize_jac( grp, R ) );
cleanup:
#if MBEDTLS_ECP_MUL_NAIVE_SAFE == 1
mbedtls_ecp_point_free(&T);
#endif
if( ret != 0 )
mbedtls_ecp_point_free( R );
return( ret );
}
#endif /* ECP_COMB_METHOD */
#endif /* ECP_SHORTWEIERSTRASS */
#if defined(ECP_MONTGOMERY)
@ -1612,8 +1679,8 @@ int mbedtls_ecp_mul( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
const mbedtls_mpi *m, const mbedtls_ecp_point *P,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
{
int ret;
int ret;
/* Common sanity checks */
if( mbedtls_mpi_cmp_int( &P->Z, 1 ) != 0 )
return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
@ -1628,7 +1695,11 @@ int mbedtls_ecp_mul( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
#endif
#if defined(ECP_SHORTWEIERSTRASS)
if( ecp_get_type( grp ) == ECP_TYPE_SHORT_WEIERSTRASS )
#ifdef ECP_COMB_METHOD
return( ecp_mul_comb( grp, R, m, P, f_rng, p_rng ) );
#else
return( ecp_mul_naive( grp, R, m, P, f_rng, p_rng ) );
#endif
#endif
return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
}

View File

@ -873,6 +873,15 @@ called.
After the bonding process, *SM_EVENT_JUST_WORKS_CANCEL*, *SM_EVENT_PASSKEY_DISPLAY_CANCEL*, or *SM_EVENT_NUMERIC_COMPARISON_CANCEL* is emitted to update the user interface if an Just Works request or a passkey has been shown before.
### Keypress Notifications
As part of Bluetooth Core V4.2 specification, a device with a keyboard but no display can send keypress notifications to provide better user feedback. In BTstack, the *sm_keypress_notification()* function is used for sending notifcations. Notifications are received by BTstack via the *SM_EVENT_KEYPRESS_NOTIFICATION* event.
### Cross-transport Key Derivation for LE Secure Connections
In a dual-mode configuration, BTstack automatically generates an BR/EDR Link Key from the LE LTK via the Link Key Conversion function *h6*. It is then stored in the link key db.
To derive an LE LTK from a BR/EDR link key, the Bluetooth controller needs to support Secure Connections via NIST P-256 elliptic curves and the LE Secure Connections needs to get established via the LE Transport. BTstack does not support LE Secure Connections via LE Transport currently.
### Out-of-Band Data with LE Legacy Pairing

View File

@ -48,7 +48,6 @@ GATT_CLIENT += \
SM_REAL += \
sm.c \
le_device_db_memory.c \
PAN += \
pan.c \

View File

@ -450,11 +450,91 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * event, uint16_t event_size){
if (event[0] == HCI_EVENT_SCO_CAN_SEND_NOW){
sco_demo_send(sco_handle);
return;
switch (packet_type){
case HCI_SCO_DATA_PACKET:
sco_demo_receive(event, event_size);
break;
case HCI_EVENT_PACKET:
switch (event[0]){
case HCI_EVENT_SCO_CAN_SEND_NOW:
sco_demo_send(sco_handle);
break;
case HCI_EVENT_HFP_META:
switch (event[2]) {
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
handle = hfp_subevent_service_level_connection_established_get_con_handle(event);
printf("Service level connection established.\n\n");
break;
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
printf("Service level connection released.\n\n");
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED:
if (hfp_subevent_audio_connection_established_get_status(event)){
sco_handle = 0;
printf("Audio connection establishment failed with status %u\n", hfp_subevent_audio_connection_established_get_status(event));
} else {
sco_handle = hfp_subevent_audio_connection_established_get_handle(event);
printf("Audio connection established with SCO handle 0x%04x.\n", sco_handle);
hci_request_sco_can_send_now_event();
}
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
sco_handle = 0;
printf("Audio connection released\n");
break;
case HFP_SUBEVENT_COMPLETE:
switch (cmd){
case 'd':
printf("HFP AG registration status update enabled.\n");
break;
case 'e':
printf("HFP AG registration status update for individual indicators set.\n");
default:
break;
}
break;
case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED:
printf("AG_INDICATOR_STATUS_CHANGED, AG indicator '%s' (index: %d) to: %d\n", (const char*) &event[5], event[3], event[4]);
break;
case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED:
printf("NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[3], event[4], (char *) &event[5]);
break;
case HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR:
if (event[4])
printf("EXTENDED_AUDIO_GATEWAY_ERROR_REPORT, status : %d\n", event[3]);
break;
case HFP_SUBEVENT_RING:
printf("** Ring **\n");
break;
case HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG:
printf("Phone number for voice tag: %s\n", (const char *) &event[3]);
break;
case HFP_SUBEVENT_SPEAKER_VOLUME:
printf("Speaker volume: %u\n", event[3]);
break;
case HFP_SUBEVENT_MICROPHONE_VOLUME:
printf("Microphone volume: %u\n", event[3]);
break;
default:
printf("event not handled %u\n", event[2]);
break;
}
break;
default:
break;
}
break;
default:
break;
}
if (event[0] != HCI_EVENT_HFP_META) return;
switch (event[2]) {

View File

@ -47,23 +47,28 @@
#define SCO_DEMO_MODE_ASCII 1
#define SCO_DEMO_MODE_COUNTER 2
// SCO demo configuration
#define SCO_DEMO_MODE SCO_DEMO_MODE_SINE
#define SCO_DEMO_MODE SCO_DEMO_MODE_ASCII
#define SCO_REPORT_PERIOD 100
// portaudio config
#define NUM_CHANNELS 1
#define SAMPLE_RATE 8000
#define FRAMES_PER_BUFFER 24
#define PA_SAMPLE_TYPE paInt8
#ifdef HAVE_POSIX_FILE_IO
#define SCO_WAV_FILENAME "sco_input.wav"
#define SCO_WAV_DURATION_IN_SECONDS 30
#endif
#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE)
#define USE_PORTAUDIO
#endif
#ifdef USE_PORTAUDIO
#include <portaudio.h>
// portaudio config
#define NUM_CHANNELS 1
#define SAMPLE_RATE 8000
#define FRAMES_PER_BUFFER 24
#define PA_SAMPLE_TYPE paInt8
// portaudio globals
static PaStream * stream;
#endif
@ -80,6 +85,71 @@ static const uint8_t sine[] = {
#endif
static int phase;
static int count_sent = 0;
static int count_received = 0;
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
#ifdef SCO_WAV_FILENAME
static FILE * wav_file;
static int num_samples_to_write;
static void little_endian_fstore_16(FILE * file, uint16_t value){
uint8_t buf[2];
little_endian_store_32(buf, 0, value);
fwrite(&buf, 1, 2, file);
}
static void little_endian_fstore_32(FILE * file, uint32_t value){
uint8_t buf[4];
little_endian_store_32(buf, 0, value);
fwrite(&buf, 1, 4, file);
}
static FILE * wav_init(const char * filename){
printf("SCO Demo: creating wav file %s\n", filename);
return fopen(filename, "wb");
}
static void write_wav_header(FILE * file, int sample_rate, int num_channels, int num_samples, int bytes_per_sample){
printf("SCO Demo: writing wav header: sample rate %u, num channels %u, duration %u s, bytes per sample %u\n",
sample_rate, num_channels, num_samples / sample_rate / num_channels, bytes_per_sample);
/* write RIFF header */
fwrite("RIFF", 1, 4, file);
// num_samples = blocks * subbands
uint32_t data_bytes = (uint32_t) (bytes_per_sample * num_samples * num_channels);
little_endian_fstore_32(file, data_bytes + 36);
fwrite("WAVE", 1, 4, file);
int byte_rate = sample_rate * num_channels * bytes_per_sample;
int bits_per_sample = 8 * bytes_per_sample;
int block_align = num_channels * bits_per_sample;
int fmt_length = 16;
int fmt_format_tag = 1; // PCM
/* write fmt chunk */
fwrite("fmt ", 1, 4, file);
little_endian_fstore_32(file, fmt_length);
little_endian_fstore_16(file, fmt_format_tag);
little_endian_fstore_16(file, num_channels);
little_endian_fstore_32(file, sample_rate);
little_endian_fstore_32(file, byte_rate);
little_endian_fstore_16(file, block_align);
little_endian_fstore_16(file, bits_per_sample);
/* write data chunk */
fwrite("data", 1, 4, file);
little_endian_fstore_32(file, data_bytes);
}
static void write_wav_data_uint8(FILE * file, unsigned long num_samples, uint8_t * data){
fwrite(data, num_samples, 1, file);
}
#endif
#endif
void sco_demo_init(void){
@ -98,6 +168,18 @@ void sco_demo_init(void){
printf("SCO Demo: Sending counter value, hexdump received data.\n");
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
#ifdef SCO_WAV_FILENAME
wav_file = wav_init(SCO_WAV_FILENAME);
const int sample_rate = 8000;
const int num_samples = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
const int num_channels = 1;
const int bytes_per_sample = 1;
write_wav_header(wav_file, sample_rate, num_channels, num_samples, bytes_per_sample);
num_samples_to_write = num_samples;
#endif
#endif
#ifdef USE_PORTAUDIO
int err;
PaStreamParameters outputParameters;
@ -136,6 +218,9 @@ void sco_demo_init(void){
#endif
}
static void sco_report(void){
printf("SCO: sent %u, received %u\n", count_sent, count_received);
}
void sco_demo_send(hci_con_handle_t sco_handle){
@ -164,8 +249,9 @@ void sco_demo_send(hci_con_handle_t sco_handle){
memset(&sco_packet[3], phase++, frames_per_packet);
if (phase > 'z') phase = 'a';
#else
for (i=0;i<frames_per_packet;i++){
sco_packet[3+i] = phase++;
int j;
for (j=0;j<frames_per_packet;j++){
sco_packet[3+j] = phase++;
}
#endif
#endif
@ -174,9 +260,8 @@ void sco_demo_send(hci_con_handle_t sco_handle){
// request another send event
hci_request_sco_can_send_now_event();
static int count = 0;
count++;
if ((count % SCO_REPORT_PERIOD) == 0) printf("SCO: sent %u\n", count);
count_sent++;
if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
}
/**
@ -184,11 +269,39 @@ void sco_demo_send(hci_con_handle_t sco_handle){
*/
void sco_demo_receive(uint8_t * packet, uint16_t size){
if (packet[1] & 0xf0){
printf("SCO CRC Error: %x - data: ", packet[1] >> 4);
printf_hexdump(&packet[3], size-3);
return;
}
int dump_data = 1;
count_received++;
// if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
#ifdef SCO_WAV_FILENAME
if (num_samples_to_write){
const int num_samples = size - 3;
const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
// convert 8 bit signed to 8 bit unsigned
int i;
for (i=0;i<samples_to_write;i++){
packet[3+i] += 128;
}
write_wav_data_uint8(wav_file, samples_to_write, &packet[3]);
num_samples_to_write -= samples_to_write;
if (num_samples_to_write == 0){
printf("SCO Demo: closing wav file\n");
fclose(wav_file);
}
dump_data = 0;
}
#endif
#endif
if (packet[1] & 0xf0){
printf("SCO CRC Error: %x - data: ", packet[1] >> 4);
printf_hexdump(&packet[3], size-3);
return;
}
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
#ifdef USE_PORTAUDIO
@ -198,22 +311,21 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){
if (end - start > 5){
printf("Portaudio: write stream took %u ms\n", end - start);
}
#else
printf_hexdump(&packet[3], size-3);
dump_data = 0;
#endif
#else
printf("data: ");
#if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
int i;
for (i=3;i<size;i++){
printf("%c", packet[i]);
}
printf("\n");
#endif
printf_hexdump(&packet[3], size-3);
#endif
static int count = 0;
count++;
if ((count % SCO_REPORT_PERIOD) == 0) printf("SCO: received %u\n", count);
if (dump_data){
printf("data: ");
#if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
int i;
for (i=3;i<size;i++){
printf("%c", packet[i]);
}
printf("\n");
dump_data = 0;
#else
printf_hexdump(&packet[3], size-3);
#endif
}
}

View File

@ -0,0 +1,382 @@
/*
* Copyright (C) 2014 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
*
*/
#include "ble/le_device_db.h"
#include "ble/core.h"
#include <stdio.h>
#include <string.h>
#include "btstack_debug.h"
// Central Device db implemenation using static memory
typedef struct le_device_memory_db {
// Identification
int addr_type;
bd_addr_t addr;
sm_key_t irk;
// Stored pairing information allows to re-establish an enncrypted connection
// with a peripheral that doesn't have any persistent memory
sm_key_t ltk;
uint16_t ediv;
uint8_t rand[8];
uint8_t key_size;
uint8_t authenticated;
uint8_t authorized;
// Signed Writes by remote
sm_key_t remote_csrk;
uint32_t remote_counter;
// Signed Writes by us
sm_key_t local_csrk;
uint32_t local_counter;
} le_device_memory_db_t;
#define LE_DEVICE_MEMORY_SIZE 20
#define INVALID_ENTRY_ADDR_TYPE 0xff
#define DB_PATH_TEMPLATE "/tmp/btstack_at_%s_le_device_db.txt"
const char * csv_header = "# addr_type, addr, irk, ltk, ediv, rand[8], key_size, authenticated, authorized, remote_csrk, remote_counter, local_csrk, local_counter";
static char db_path[sizeof(DB_PATH_TEMPLATE) - 2 + 17 + 1];
static le_device_memory_db_t le_devices[LE_DEVICE_MEMORY_SIZE];
static char bd_addr_to_dash_str_buffer[6*3]; // 12-45-78-01-34-67\0
static char * bd_addr_to_dash_str(bd_addr_t addr){
char * p = bd_addr_to_dash_str_buffer;
int i;
for (i = 0; i < 6 ; i++) {
*p++ = char_for_nibble((addr[i] >> 4) & 0x0F);
*p++ = char_for_nibble((addr[i] >> 0) & 0x0F);
*p++ = '-';
}
*--p = 0;
return (char *) bd_addr_to_dash_str_buffer;
}
static inline void write_delimiter(FILE * wFile){
fwrite(", ", 1, 1, wFile);
}
static inline void write_hex_byte(FILE * wFile, uint8_t value){
char buffer[2];
buffer[0] = char_for_nibble(value >> 4);
buffer[1] = char_for_nibble(value & 0x0f);
fwrite(buffer, 2, 1, wFile);
}
static inline void write_str(FILE * wFile, const char * str){
fwrite(str, strlen(str), 1, wFile);
write_delimiter(wFile);
}
static void write_hex(FILE * wFile, const uint8_t * value, int len){
int i;
for (i = 0; i < len; i++){
write_hex_byte(wFile, value[i]);
}
write_delimiter(wFile);
}
static void write_value(FILE * wFile, uint32_t value, int len){
switch (len){
case 4:
write_hex_byte(wFile, value >> 24);
case 3:
write_hex_byte(wFile, value >> 16);
case 2:
write_hex_byte(wFile, value >> 8);
case 1:
default:
write_hex_byte(wFile, value);
}
write_delimiter(wFile);
}
static void le_device_db_store(void) {
int i;
// open file
FILE * wFile = fopen(db_path,"w+");
if (wFile == NULL) return;
fwrite(csv_header, strlen(csv_header), 1, wFile);
fwrite("\n", 1, 1, wFile);
for (i=0;i<LE_DEVICE_MEMORY_SIZE;i++){
if (le_devices[i].addr_type == INVALID_ENTRY_ADDR_TYPE) continue;
write_value(wFile, le_devices[i].addr_type, 1);
write_str(wFile, bd_addr_to_str(le_devices[i].addr));
write_hex(wFile, le_devices[i].irk, 16);
write_hex(wFile, le_devices[i].ltk, 16);
write_value(wFile, le_devices[i].ediv, 2);
write_hex(wFile, le_devices[i].rand, 8);
write_value(wFile, le_devices[i].key_size, 1);
write_value(wFile, le_devices[i].authenticated, 1);
write_value(wFile, le_devices[i].authorized, 1);
write_hex(wFile, le_devices[i].remote_csrk, 16);
write_value(wFile, le_devices[i].remote_counter, 2);
write_hex(wFile, le_devices[i].local_csrk, 16);
write_value(wFile, le_devices[i].local_counter, 2);
fwrite("\n", 1, 1, wFile);
}
fclose(wFile);
}
static void read_delimiter(FILE * wFile){
fgetc(wFile);
}
static uint8_t read_hex_byte(FILE * wFile){
int c = fgetc(wFile);
if (c == ':') {
c = fgetc(wFile);
}
int d = fgetc(wFile);
return nibble_for_char(c) << 4 | nibble_for_char(d);
}
static void read_hex(FILE * wFile, uint8_t * buffer, int len){
int i;
for (i=0;i<len;i++){
buffer[i] = read_hex_byte(wFile);
}
read_delimiter(wFile);
}
static uint32_t read_value(FILE * wFile, int len){
uint32_t res = 0;
int i;
for (i=0;i<len;i++){
res = res << 8 | read_hex_byte(wFile);
}
read_delimiter(wFile);
return res;
}
static void le_device_db_read(void){
// open file
FILE * wFile = fopen(db_path,"r");
if (wFile == NULL) return;
// skip header
while (1) {
int c = fgetc(wFile);
if (feof(wFile)) goto exit;
if (c == '\n') break;
}
// read entries
int i;
for (i=0;i<LE_DEVICE_MEMORY_SIZE && !feof(wFile);i++){
le_devices[i].addr_type = read_value(wFile, 1);
read_hex(wFile, le_devices[i].addr, 6);
read_hex(wFile, le_devices[i].irk, 16);
read_hex(wFile, le_devices[i].ltk, 16);
le_devices[i].ediv = read_value(wFile, 2);
read_hex(wFile, le_devices[i].rand, 8);
le_devices[i].key_size = read_value(wFile, 1);
le_devices[i].authenticated = read_value(wFile, 1);
le_devices[i].authorized = read_value(wFile, 1);
read_hex(wFile, le_devices[i].remote_csrk, 16);
le_devices[i].remote_counter = read_value(wFile, 2);
read_hex(wFile, le_devices[i].local_csrk, 16);
le_devices[i].local_counter = read_value(wFile, 2);
// read newling
fgetc(wFile);
}
exit:
fclose(wFile);
}
void le_device_db_init(void){
int i;
for (i=0;i<LE_DEVICE_MEMORY_SIZE;i++){
le_devices[i].addr_type = INVALID_ENTRY_ADDR_TYPE;
}
sprintf(db_path, DB_PATH_TEMPLATE, "00-00-00-00-00-00");
}
void le_device_db_set_local_bd_addr(bd_addr_t addr){
sprintf(db_path, DB_PATH_TEMPLATE, bd_addr_to_dash_str(addr));
log_info("le_device_db_fs: path %s", db_path);
le_device_db_read();
le_device_db_dump();
}
// @returns number of device in db
int le_device_db_count(void){
int i;
int counter = 0;
for (i=0;i<LE_DEVICE_MEMORY_SIZE;i++){
if (le_devices[i].addr_type != INVALID_ENTRY_ADDR_TYPE) counter++;
}
return counter;
}
// free device
void le_device_db_remove(int index){
le_devices[index].addr_type = INVALID_ENTRY_ADDR_TYPE;
le_device_db_store();
}
int le_device_db_add(int addr_type, bd_addr_t addr, sm_key_t irk){
int i;
int index = -1;
for (i=0;i<LE_DEVICE_MEMORY_SIZE;i++){
if (le_devices[i].addr_type == INVALID_ENTRY_ADDR_TYPE){
index = i;
break;
}
}
if (index < 0) return -1;
log_info("Central Device DB adding type %u - %s", addr_type, bd_addr_to_str(addr));
log_info_key("irk", irk);
le_devices[index].addr_type = addr_type;
memcpy(le_devices[index].addr, addr, 6);
memcpy(le_devices[index].irk, irk, 16);
le_devices[index].remote_counter = 0;
le_device_db_store();
return index;
}
// get device information: addr type and address
void le_device_db_info(int index, int * addr_type, bd_addr_t addr, sm_key_t irk){
if (addr_type) *addr_type = le_devices[index].addr_type;
if (addr) memcpy(addr, le_devices[index].addr, 6);
if (irk) memcpy(irk, le_devices[index].irk, 16);
}
void le_device_db_encryption_set(int index, uint16_t ediv, uint8_t rand[8], sm_key_t ltk, int key_size, int authenticated, int authorized){
log_info("Central Device DB set encryption for %u, ediv x%04x, key size %u, authenticated %u, authorized %u",
index, ediv, key_size, authenticated, authorized);
le_device_memory_db_t * device = &le_devices[index];
device->ediv = ediv;
if (rand) memcpy(device->rand, rand, 8);
if (ltk) memcpy(device->ltk, ltk, 16);
device->key_size = key_size;
device->authenticated = authenticated;
device->authorized = authorized;
le_device_db_store();
}
void le_device_db_encryption_get(int index, uint16_t * ediv, uint8_t rand[8], sm_key_t ltk, int * key_size, int * authenticated, int * authorized){
le_device_memory_db_t * device = &le_devices[index];
log_info("Central Device DB encryption for %u, ediv x%04x, keysize %u, authenticated %u, authorized %u",
index, device->ediv, device->key_size, device->authenticated, device->authorized);
if (ediv) *ediv = device->ediv;
if (rand) memcpy(rand, device->rand, 8);
if (ltk) memcpy(ltk, device->ltk, 16);
if (key_size) *key_size = device->key_size;
if (authenticated) *authenticated = device->authenticated;
if (authorized) *authorized = device->authorized;
}
// get signature key
void le_device_db_remote_csrk_get(int index, sm_key_t csrk){
if (index < 0 || index >= LE_DEVICE_MEMORY_SIZE){
log_error("le_device_db_remote_csrk_get called with invalid index %d", index);
return;
}
if (csrk) memcpy(csrk, le_devices[index].remote_csrk, 16);
}
void le_device_db_remote_csrk_set(int index, sm_key_t csrk){
if (index < 0 || index >= LE_DEVICE_MEMORY_SIZE){
log_error("le_device_db_remote_csrk_set called with invalid index %d", index);
return;
}
if (csrk) memcpy(le_devices[index].remote_csrk, csrk, 16);
le_device_db_store();
}
void le_device_db_local_csrk_get(int index, sm_key_t csrk){
if (index < 0 || index >= LE_DEVICE_MEMORY_SIZE){
log_error("le_device_db_local_csrk_get called with invalid index %d", index);
return;
}
if (csrk) memcpy(csrk, le_devices[index].local_csrk, 16);
}
void le_device_db_local_csrk_set(int index, sm_key_t csrk){
if (index < 0 || index >= LE_DEVICE_MEMORY_SIZE){
log_error("le_device_db_local_csrk_set called with invalid index %d", index);
return;
}
if (csrk) memcpy(le_devices[index].local_csrk, csrk, 16);
le_device_db_store();
}
// query last used/seen signing counter
uint32_t le_device_db_remote_counter_get(int index){
return le_devices[index].remote_counter;
}
// update signing counter
void le_device_db_remote_counter_set(int index, uint32_t counter){
le_devices[index].remote_counter = counter;
le_device_db_store();
}
// query last used/seen signing counter
uint32_t le_device_db_local_counter_get(int index){
return le_devices[index].local_counter;
}
// update signing counter
void le_device_db_local_counter_set(int index, uint32_t counter){
le_devices[index].local_counter = counter;
le_device_db_store();
}
void le_device_db_dump(void){
log_info("Central Device DB dump, devices: %d", le_device_db_count());
int i;
for (i=0;i<LE_DEVICE_MEMORY_SIZE;i++){
if (le_devices[i].addr_type == INVALID_ENTRY_ADDR_TYPE) continue;
log_info("%u: %u %s", i, le_devices[i].addr_type, bd_addr_to_str(le_devices[i].addr));
log_info_key("ltk", le_devices[i].ltk);
log_info_key("irk", le_devices[i].irk);
log_info_key("local csrk", le_devices[i].local_csrk);
log_info_key("remote csrk", le_devices[i].remote_csrk);
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 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
*
*/
#ifndef __LE_DEVICE_DB_FS_H
#define __LE_DEVICE_DB_FS_H
#include "ble/le_device_db.h"
#if defined __cplusplus
extern "C" {
#endif
/*
* @brief Get le device db implementation that stores bonding information in /tmp
*/
const le_device_db_t * le_device_db_fs_instance(void);
/* API_END */
#if defined __cplusplus
}
#endif
#endif // __LE_DEVICE_DB_FS_H

View File

@ -8,14 +8,20 @@ gap_dedicated_bonding
gap_inquiry
gap_inquiry_and_bond
gap_le_advertisements
gap_le_advertisements
gatt_battery_query
gatt_browser
hfp_ag_demo
hfp_hf_demo
hsp_ag_demo
hsp_hs_demo
l2cap_test
le_counter
le_counter.h
le_streamer
le_streamer
le_streamer
le_streamer.h
le_streamer.h
led_counter
panu_demo
@ -23,14 +29,11 @@ profile.h
sdp_bnep_query
sdp_general_query
sdp_rfcomm_query
sm_pairing_central
sm_pairing_peripheral
sm_pairing_peripheral.h
spp_and_le_counter
spp_and_le_counter.h
spp_counter
spp_streamer
le_streamer
le_streamer.h
gap_le_advertisements
le_streamer
spp_streamer
hfp_ag_demo
hfp_hf_demo
spp_streamer

View File

@ -3,7 +3,7 @@ BTSTACK_ROOT = ../..
CORE += main.c stdin_support.c
COMMON += hci_transport_h2_libusb.c btstack_run_loop_posix.c btstack_link_key_db_fs.c
COMMON += hci_transport_h2_libusb.c btstack_run_loop_posix.c btstack_link_key_db_fs.c le_device_db_fs.c
include ${BTSTACK_ROOT}/example/Makefile.inc

View File

@ -3,28 +3,41 @@ ancs_client_demo.h
ble_central_test
ble_peripheral
ble_peripheral_sm_minimal
ble_peripheral_test
bluetooth_init_cc2560_2.44.c
bluetooth_init_cc2560a_2.14.c
bluetooth_init_cc2560B_1.2_BT_Spec_4.0.c
bluetooth_init_cc2564_2.14.c
bluetooth_init_cc2564B_1.2_BT_Spec_4.0.c
bnep_test
classic_test
gap_dedicated_bonding
gap_inquiry
gap_inquiry_and_bond
gap_le_advertisements
gatt_battery_query
gatt_browser
hfp_ag_demo
hfp_hf_demo
hsp_ag_demo
hsp_ag_test
hsp_hs_demo
hsp_hs_test
l2cap_test
le_counter
le_counter.h
le_streamer
le_streamer.h
led_counter
profile.h
sco_input.wav
sdp_bnep_query
sdp_general_query
sdp_rfcomm_query
sm_pairing_peripheral.h
spp_and_le_counter
spp_and_le_counter.h
spp_counter
spp_streamer
led_counter
le_counter.h
ble_peripheral_test
le_counter
gap_le_advertisements
le_streamer
le_streamer.h
TIInit_12.10.28.c
TIInit_12.8.32.c

View File

@ -2,6 +2,7 @@
BTSTACK_ROOT = ../..
CORE += \
bluetooth_init_cc2564B_1.4_BT_Spec_4.1.c \
btstack_chipset_cc256x.c \
btstack_chipset_csr.c \
btstack_chipset_em9301.c \
@ -11,9 +12,9 @@ CORE += \
btstack_run_loop_posix.c \
btstack_uart_block_posix.c \
hci_transport_h4.c \
le_device_db_fs.c \
main.c \
stdin_support.c \
bluetooth_init_cc2564B_1.4_BT_Spec_4.1.c \
# bluetooth_init_cc2564_2.14.c \
# btstack_chipset_bcm.c \

View File

@ -13,6 +13,7 @@ CORE += \
btstack_uart_block_posix.c \
btstack_slip.c \
hci_transport_h5.c \
le_device_db_fs.c \
main.c \
stdin_support.c \
# btstack_chipset_bcm.c \

View File

@ -326,7 +326,7 @@ static void att_run(void){
log_info("Orig Signature: ");
log_info_hexdump( &att_request_buffer[att_request_size-8], 8);
uint16_t attribute_handle = little_endian_read_16(att_request_buffer, 1);
sm_cmac_start(csrk, att_request_buffer[0], attribute_handle, att_request_size - 15, &att_request_buffer[3], counter_packet, att_signed_write_handle_cmac_result);
sm_cmac_signed_write_start(csrk, att_request_buffer[0], attribute_handle, att_request_size - 15, &att_request_buffer[3], counter_packet, att_signed_write_handle_cmac_result);
return;
}

View File

@ -965,7 +965,7 @@ static void gatt_client_run(void){
le_device_db_local_csrk_get(peripheral->le_device_index, csrk);
uint32_t sign_counter = le_device_db_local_counter_get(peripheral->le_device_index);
peripheral->gatt_client_state = P_W4_CMAC_RESULT;
sm_cmac_start(csrk, ATT_SIGNED_WRITE_COMMAND, peripheral->attribute_handle, peripheral->attribute_length, peripheral->attribute_value, sign_counter, att_signed_write_handle_cmac_result);
sm_cmac_signed_write_start(csrk, ATT_SIGNED_WRITE_COMMAND, peripheral->attribute_handle, peripheral->attribute_length, peripheral->attribute_value, sign_counter, att_signed_write_handle_cmac_result);
}
return;

View File

@ -67,6 +67,13 @@ extern "C" {
*/
void le_device_db_init(void);
/**
* @brief sets local bd addr. allows for db per Bluetooth controller
* @param bd_addr
*/
void le_device_db_set_local_bd_addr(bd_addr_t bd_addr);
/**
* @brief add device to db
* @param addr_type, address of the device

View File

@ -82,6 +82,9 @@ void le_device_db_init(void){
}
}
void le_device_db_set_local_bd_addr(bd_addr_t bd_addr){
}
// @returns number of device in db
int le_device_db_count(void){
int i;

View File

@ -231,8 +231,11 @@ static uint8_t ec_qx[32];
static uint8_t ec_qy[32];
static uint8_t ec_d[32];
#ifndef HAVE_MALLOC
// 4304 bytes with 73 allocations
#define MBEDTLS_ALLOC_BUFFER_SIZE (1300+23*sizeof(void *))
// COMP Method with Window 2
// 1300 bytes with 23 allocations
// #define MBEDTLS_ALLOC_BUFFER_SIZE (1300+23*sizeof(void *))
// NAIVE Method with safe cond assignments (without safe cond, order changes and allocations fail)
#define MBEDTLS_ALLOC_BUFFER_SIZE (700+18*sizeof(void *))
static uint8_t mbedtls_memory_buffer[MBEDTLS_ALLOC_BUFFER_SIZE];
#endif
#endif
@ -254,6 +257,7 @@ typedef struct sm_setup_context {
// user response, (Phase 1 and/or 2)
uint8_t sm_user_response;
uint8_t sm_keypress_notification;
// defines which keys will be send after connection is encrypted - calculated during Phase 1, used Phase 3
int sm_key_distribution_send_set;
@ -287,7 +291,7 @@ typedef struct sm_setup_context {
sm_key_t sm_local_dhkey_check;
sm_key_t sm_ra;
sm_key_t sm_rb;
sm_key_t sm_t; // used for f5
sm_key_t sm_t; // used for f5 and h6
sm_key_t sm_mackey;
uint8_t sm_passkey_bit; // also stores number of generated random bytes for EC key generation
#endif
@ -328,9 +332,6 @@ static uint16_t sm_active_connection = 0;
// stores oob data in provided 16 byte buffer if not null
static int (*sm_get_oob_data)(uint8_t addres_type, bd_addr_t addr, uint8_t * oob_data) = NULL;
// used to notify applicationss that user interaction is neccessary, see sm_notify_t below
static btstack_packet_handler_t sm_client_packet_handler = NULL;
// horizontal: initiator capabilities
// vertial: responder capabilities
static const stk_generation_method_t stk_generation_method [5] [5] = {
@ -650,20 +651,6 @@ static void aes_cmac(sm_key_t aes_cmac, const sm_key_t key, const uint8_t * data
aes128_calc_cyphertext(key, sm_cmac_y, aes_cmac);
}
#endif
#if 0
//
// Link Key Conversion Function h6
//
// h6(W, keyID) = AES-CMACW(keyID)
// - W is 128 bits
// - keyID is 32 bits
static void h6(sm_key_t res, const sm_key_t w, const uint32_t key_id){
uint8_t key_id_buffer[4];
big_endian_store_32(key_id_buffer, 0, key_id);
aes_cmac(res, w, key_id_buffer, 4);
}
#endif
#endif
static void sm_setup_event_base(uint8_t * event, int event_size, uint8_t type, hci_con_handle_t con_handle, uint8_t addr_type, bd_addr_t address){
@ -675,9 +662,6 @@ static void sm_setup_event_base(uint8_t * event, int event_size, uint8_t type, h
}
static void sm_dispatch_event(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t size){
if (sm_client_packet_handler) {
sm_client_packet_handler(HCI_EVENT_PACKET, 0, packet, size);
}
// dispatch to all event handlers
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &sm_event_handlers);
@ -872,23 +856,8 @@ static int sm_cmac_last_block_complete(void){
return (sm_cmac_message_len & 0x0f) == 0;
}
static inline uint8_t sm_cmac_message_get_byte(uint16_t offset){
if (offset >= sm_cmac_message_len) {
log_error("sm_cmac_message_get_byte. out of bounds, access %u, len %u", offset, sm_cmac_message_len);
return 0;
}
offset = sm_cmac_message_len - 1 - offset;
// sm_cmac_header[3] | message[] | sm_cmac_sign_counter[4]
if (offset < 3){
return sm_cmac_header[offset];
}
int actual_message_len_incl_header = sm_cmac_message_len - 4;
if (offset < actual_message_len_incl_header){
return sm_cmac_message[offset - 3];
}
return sm_cmac_sign_counter[offset - actual_message_len_incl_header];
int sm_cmac_ready(void){
return sm_cmac_state == CMAC_IDLE;
}
// generic cmac calculation
@ -918,19 +887,35 @@ void sm_cmac_general_start(const sm_key_t key, uint16_t message_len, uint8_t (*g
}
// cmac for ATT Message signing
void sm_cmac_start(const sm_key_t k, uint8_t opcode, hci_con_handle_t con_handle, uint16_t message_len, const uint8_t * message, uint32_t sign_counter, void (*done_handler)(uint8_t * hash)){
static uint8_t sm_cmac_signed_write_message_get_byte(uint16_t offset){
if (offset >= sm_cmac_message_len) {
log_error("sm_cmac_signed_write_message_get_byte. out of bounds, access %u, len %u", offset, sm_cmac_message_len);
return 0;
}
offset = sm_cmac_message_len - 1 - offset;
// sm_cmac_header[3] | message[] | sm_cmac_sign_counter[4]
if (offset < 3){
return sm_cmac_header[offset];
}
int actual_message_len_incl_header = sm_cmac_message_len - 4;
if (offset < actual_message_len_incl_header){
return sm_cmac_message[offset - 3];
}
return sm_cmac_sign_counter[offset - actual_message_len_incl_header];
}
void sm_cmac_signed_write_start(const sm_key_t k, uint8_t opcode, hci_con_handle_t con_handle, uint16_t message_len, const uint8_t * message, uint32_t sign_counter, void (*done_handler)(uint8_t * hash)){
// ATT Message Signing
sm_cmac_header[0] = opcode;
little_endian_store_16(sm_cmac_header, 1, con_handle);
little_endian_store_32(sm_cmac_sign_counter, 0, sign_counter);
uint16_t total_message_len = 3 + message_len + 4; // incl. virtually prepended att opcode, handle and appended sign_counter in LE
sm_cmac_message = message;
sm_cmac_general_start(k, total_message_len, &sm_cmac_message_get_byte, done_handler);
sm_cmac_general_start(k, total_message_len, &sm_cmac_signed_write_message_get_byte, done_handler);
}
int sm_cmac_ready(void){
return sm_cmac_state == CMAC_IDLE;
}
static void sm_cmac_handle_aes_engine_ready(void){
switch (sm_cmac_state){
@ -1100,11 +1085,16 @@ static int sm_key_distribution_flags_for_auth_req(void){
return flags;
}
static void sm_reset_setup(void){
// fill in sm setup
setup->sm_state_vars = 0;
setup->sm_keypress_notification = 0xff;
sm_reset_tk();
}
static void sm_init_setup(sm_connection_t * sm_conn){
// fill in sm setup
setup->sm_state_vars = 0;
sm_reset_tk();
setup->sm_peer_addr_type = sm_conn->sm_peer_addr_type;
memcpy(setup->sm_peer_address, sm_conn->sm_peer_address, 6);
@ -1188,7 +1178,7 @@ static void sm_address_resolution_handle_event(address_resolution_event_t event)
hci_con_handle_t con_handle = 0;
sm_connection_t * sm_connection;
uint16_t ediv;
sm_key_t ltk;
switch (mode){
case ADDRESS_RESOLUTION_GENERAL:
break;
@ -1204,8 +1194,8 @@ static void sm_address_resolution_handle_event(address_resolution_event_t event)
if (!sm_connection->sm_bonding_requested && !sm_connection->sm_security_request_received) break;
sm_connection->sm_security_request_received = 0;
sm_connection->sm_bonding_requested = 0;
le_device_db_encryption_get(sm_connection->sm_le_db_index, &ediv, NULL, NULL, NULL, NULL, NULL);
if (ediv){
le_device_db_encryption_get(sm_connection->sm_le_db_index, NULL, NULL, ltk, NULL, NULL, NULL);
if (!sm_is_null_key(ltk)){
sm_connection->sm_engine_state = SM_INITIATOR_PH0_HAS_LTK;
} else {
sm_connection->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST;
@ -1278,7 +1268,6 @@ static void sm_key_distribution_handle_all_received(sm_connection_t * sm_conn){
}
if (le_db_index >= 0){
le_device_db_local_counter_set(le_db_index, 0);
// store local CSRK
if (setup->sm_key_distribution_send_set & SM_KEYDIST_FLAG_SIGNING_IDENTIFICATION){
@ -1294,13 +1283,23 @@ static void sm_key_distribution_handle_all_received(sm_connection_t * sm_conn){
le_device_db_remote_counter_set(le_db_index, 0);
}
// store encryption information
if (setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_ENCRYPTION_INFORMATION
&& setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_MASTER_IDENTIFICATION){
// store encryption information for secure connections: LTK generated by ECDH
if (setup->sm_use_secure_connections){
log_info("sm: store SC LTK (key size %u, authenticatd %u)", sm_conn->sm_actual_encryption_key_size, sm_conn->sm_connection_authenticated);
uint8_t zero_rand[8];
memset(zero_rand, 0, 8);
le_device_db_encryption_set(le_db_index, 0, zero_rand, setup->sm_ltk, sm_conn->sm_actual_encryption_key_size,
sm_conn->sm_connection_authenticated, sm_conn->sm_connection_authorization_state == AUTHORIZATION_GRANTED);
}
// store encryption infromation for legacy pairing: peer LTK, EDIV, RAND
else if ( (setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_ENCRYPTION_INFORMATION)
&& (setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_MASTER_IDENTIFICATION )){
log_info("sm: set encryption information (key size %u, authenticatd %u)", sm_conn->sm_actual_encryption_key_size, sm_conn->sm_connection_authenticated);
le_device_db_encryption_set(le_db_index, setup->sm_peer_ediv, setup->sm_peer_rand, setup->sm_peer_ltk,
sm_conn->sm_actual_encryption_key_size, sm_conn->sm_connection_authenticated, sm_conn->sm_connection_authorization_state == AUTHORIZATION_GRANTED);
}
}
}
// keep le_db_index
@ -1379,6 +1378,7 @@ static void sm_sc_cmac_done(uint8_t * hash){
sm_connection_t * sm_conn = sm_cmac_connection;
sm_cmac_connection = NULL;
link_key_type_t link_key_type;
switch (sm_conn->sm_engine_state){
case SM_SC_W4_CMAC_FOR_CONFIRMATION:
@ -1409,7 +1409,13 @@ static void sm_sc_cmac_done(uint8_t * hash){
sm_conn->sm_engine_state = SM_SC_W2_CALCULATE_F5_LTK;
break;
case SM_SC_W4_CALCULATE_F5_LTK:
// truncate sm_ltk, but keep full LTK for cross-transport key derivation in sm_local_ltk
// Errata Service Release to the Bluetooth Specification: ESR09
// E6405 Cross transport key derivation from a key of size less than 128 bits
// Note: When the BR/EDR link key is being derived from the LTK, the derivation is done before the LTK gets masked."
memcpy(setup->sm_ltk, hash, 16);
memcpy(setup->sm_local_ltk, hash, 16);
sm_truncate_key(setup->sm_ltk, sm_conn->sm_actual_encryption_key_size);
sm_conn->sm_engine_state = SM_SC_W2_CALCULATE_F6_FOR_DHKEY_CHECK;
break;
case SM_SC_W4_CALCULATE_F6_FOR_DHKEY_CHECK:
@ -1438,6 +1444,23 @@ static void sm_sc_cmac_done(uint8_t * hash){
sm_conn->sm_engine_state = SM_INITIATOR_PH3_SEND_START_ENCRYPTION;
}
break;
case SM_SC_W4_CALCULATE_H6_ILK:
memcpy(setup->sm_t, hash, 16);
sm_conn->sm_engine_state = SM_SC_W2_CALCULATE_H6_BR_EDR_LINK_KEY;
break;
case SM_SC_W4_CALCULATE_H6_BR_EDR_LINK_KEY:
reverse_128(hash, setup->sm_t);
link_key_type = sm_conn->sm_connection_authenticated ?
AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P256 : UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P256;
if (sm_conn->sm_role){
gap_store_link_key_for_bd_addr(setup->sm_m_address, setup->sm_t, link_key_type);
sm_conn->sm_engine_state = SM_RESPONDER_IDLE;
} else {
gap_store_link_key_for_bd_addr(setup->sm_s_address, setup->sm_t, link_key_type);
sm_conn->sm_engine_state = SM_INITIATOR_CONNECTED;
}
sm_done_for_handle(sm_conn->sm_handle);
break;
default:
log_error("sm_sc_cmac_done in state %u", sm_conn->sm_engine_state);
break;
@ -1474,12 +1497,12 @@ static void sm_sc_calculate_dhkey(sm_key256_t dhkey){
mbedtls_mpi_read_binary(&d, ec_d, 32);
mbedtls_mpi_read_binary(&Q.X, setup->sm_peer_qx, 32);
mbedtls_mpi_read_binary(&Q.Y, setup->sm_peer_qy, 32);
mbedtls_mpi_read_string(&Q.Z, 16, "1" );
mbedtls_mpi_lset(&Q.Z, 1);
mbedtls_ecp_mul(&mbedtls_ec_group, &DH, &d, &Q, NULL, NULL);
mbedtls_mpi_write_binary(&DH.X, dhkey, 32);
mbedtls_ecp_point_free(&DH);
mbedtls_mpi_free(&d);
mbedtls_ecp_point_free(&Q);
mbedtls_ecp_point_free(&DH);
#endif
log_info("dhkey");
log_info_hexdump(dhkey, 32);
@ -1578,7 +1601,7 @@ static void g2_engine(sm_connection_t * sm_conn, const sm_key256_t u, const sm_k
log_info("g2 key");
log_info_hexdump(x, 16);
log_info("g2 message");
log_info_hexdump(sm_cmac_sc_buffer, sizeof(sm_cmac_sc_buffer));
log_info_hexdump(sm_cmac_sc_buffer, message_len);
sm_cmac_general_start(x, message_len, &sm_sc_cmac_get_byte, &sm_sc_cmac_done);
}
@ -1667,8 +1690,48 @@ static void sm_sc_calculate_f6_to_verify_dhkey_check(sm_connection_t * sm_conn){
f6_engine(sm_conn, setup->sm_mackey, setup->sm_peer_nonce, setup->sm_local_nonce, setup->sm_ra, iocap_b, bd_addr_slave, bd_addr_master);
}
}
//
// Link Key Conversion Function h6
//
// h6(W, keyID) = AES-CMACW(keyID)
// - W is 128 bits
// - keyID is 32 bits
static void h6_engine(sm_connection_t * sm_conn, const sm_key_t w, const uint32_t key_id){
const uint16_t message_len = 4;
sm_cmac_connection = sm_conn;
big_endian_store_32(sm_cmac_sc_buffer, 0, key_id);
log_info("h6 key");
log_info_hexdump(w, 16);
log_info("h6 message");
log_info_hexdump(sm_cmac_sc_buffer, message_len);
sm_cmac_general_start(w, message_len, &sm_sc_cmac_get_byte, &sm_sc_cmac_done);
}
// For SC, setup->sm_local_ltk holds full LTK (sm_ltk is already truncated)
// Errata Service Release to the Bluetooth Specification: ESR09
// E6405 Cross transport key derivation from a key of size less than 128 bits
// "Note: When the BR/EDR link key is being derived from the LTK, the derivation is done before the LTK gets masked."
static void h6_calculate_ilk(sm_connection_t * sm_conn){
h6_engine(sm_conn, setup->sm_local_ltk, 0x746D7031); // "tmp1"
}
static void h6_calculate_br_edr_link_key(sm_connection_t * sm_conn){
h6_engine(sm_conn, setup->sm_t, 0x6c656272); // "lebr"
}
#endif
// key management legacy connections:
// - potentially two different LTKs based on direction. each device stores LTK provided by peer
// - master stores LTK, EDIV, RAND. responder optionally stored master LTK (only if it needs to reconnect)
// - initiators reconnects: initiator uses stored LTK, EDIV, RAND generated by responder
// - responder reconnects: responder uses LTK receveived from master
// key management secure connections:
// - both devices store same LTK from ECDH key exchange.
static void sm_load_security_info(sm_connection_t * sm_connection){
int encryption_key_size;
int authenticated;
@ -1683,6 +1746,19 @@ static void sm_load_security_info(sm_connection_t * sm_connection){
sm_connection->sm_connection_authorization_state = authorized ? AUTHORIZATION_GRANTED : AUTHORIZATION_UNKNOWN;
}
static void sm_start_calculating_ltk_from_ediv_and_rand(sm_connection_t * sm_connection){
memcpy(setup->sm_local_rand, sm_connection->sm_local_rand, 8);
setup->sm_local_ediv = sm_connection->sm_local_ediv;
// re-establish used key encryption size
// no db for encryption size hack: encryption size is stored in lowest nibble of setup->sm_local_rand
sm_connection->sm_actual_encryption_key_size = (setup->sm_local_rand[7] & 0x0f) + 1;
// no db for authenticated flag hack: flag is stored in bit 4 of LSB
sm_connection->sm_connection_authenticated = (setup->sm_local_rand[7] & 0x10) >> 4;
log_info("sm: received ltk request with key size %u, authenticated %u",
sm_connection->sm_actual_encryption_key_size, sm_connection->sm_connection_authenticated);
sm_connection->sm_engine_state = SM_RESPONDER_PH4_Y_GET_ENC;
}
static void sm_run(void){
btstack_linked_list_iterator_t it;
@ -1861,6 +1937,7 @@ static void sm_run(void){
done = 0;
break;
case SM_RESPONDER_PH1_PAIRING_REQUEST_RECEIVED:
sm_reset_setup();
sm_init_setup(sm_connection);
// recover pairing request
memcpy(&setup->sm_m_preq, &sm_connection->sm_m_preq, sizeof(sm_pairing_packet_t));
@ -1879,44 +1956,43 @@ static void sm_run(void){
sm_connection->sm_engine_state = SM_RESPONDER_PH1_SEND_PAIRING_RESPONSE;
break;
case SM_INITIATOR_PH0_HAS_LTK:
sm_reset_setup();
sm_load_security_info(sm_connection);
sm_connection->sm_engine_state = SM_INITIATOR_PH0_SEND_START_ENCRYPTION;
break;
case SM_RESPONDER_PH0_RECEIVED_LTK:
case SM_RESPONDER_PH0_RECEIVED_LTK_REQUEST:
#ifdef ENABLE_LE_SECURE_CONNECTIONS
switch (sm_connection->sm_irk_lookup_state){
case IRK_LOOKUP_SUCCEEDED:{
sm_load_security_info(sm_connection);
sm_connection->sm_engine_state = SM_RESPONDER_PH2_SEND_LTK_REPLY;
case IRK_LOOKUP_SUCCEEDED:
// assuming Secure Connection, we have a stored LTK and the EDIV/RAND are null
sm_reset_setup();
sm_load_security_info(sm_connection);
if (setup->sm_peer_ediv == 0 && sm_is_null_random(setup->sm_peer_rand) && !sm_is_null_key(setup->sm_peer_ltk)){
memcpy(setup->sm_ltk, setup->sm_peer_ltk, 16);
sm_connection->sm_engine_state = SM_RESPONDER_PH4_SEND_LTK_REPLY;
break;
}
log_info("LTK Request: ediv & random are empty, but no stored LTK (IRK Lookup Succeeded)");
sm_connection->sm_engine_state = SM_RESPONDER_PH0_SEND_LTK_REQUESTED_NEGATIVE_REPLY;
// don't lock setup context yet
done = 0;
break;
case IRK_LOOKUP_FAILED:
// assume that we don't have a LTK for ediv == 0 and random == null
if (sm_connection->sm_local_ediv == 0 && sm_is_null_random(sm_connection->sm_local_rand)){
log_info("LTK Request: ediv & random are empty");
sm_connection->sm_engine_state = SM_RESPONDER_PH0_SEND_LTK_REQUESTED_NEGATIVE_REPLY;
// TODO: no need to lock context yet -> done = 0;
break;
}
// re-establish previously used LTK using Rand and EDIV
memcpy(setup->sm_local_rand, sm_connection->sm_local_rand, 8);
setup->sm_local_ediv = sm_connection->sm_local_ediv;
// re-establish used key encryption size
// no db for encryption size hack: encryption size is stored in lowest nibble of setup->sm_local_rand
sm_connection->sm_actual_encryption_key_size = (setup->sm_local_rand[7] & 0x0f) + 1;
// no db for authenticated flag hack: flag is stored in bit 4 of LSB
sm_connection->sm_connection_authenticated = (setup->sm_local_rand[7] & 0x10) >> 4;
log_info("sm: received ltk request with key size %u, authenticated %u",
sm_connection->sm_actual_encryption_key_size, sm_connection->sm_connection_authenticated);
sm_connection->sm_engine_state = SM_RESPONDER_PH4_Y_GET_ENC;
log_info("LTK Request: ediv & random are empty, but no stored LTK (IRK Lookup Failed)");
sm_connection->sm_engine_state = SM_RESPONDER_PH0_SEND_LTK_REQUESTED_NEGATIVE_REPLY;
// don't lock setup context yet
done = 0;
break;
default:
// just wait until IRK lookup is completed
// don't lock sxetup context yet
// don't lock setup context yet
done = 0;
break;
}
#endif
break;
case SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST:
sm_reset_setup();
sm_init_setup(sm_connection);
sm_timeout_start(sm_connection);
sm_connection->sm_engine_state = SM_INITIATOR_PH1_SEND_PAIRING_REQUEST;
@ -1946,6 +2022,16 @@ static void sm_run(void){
sm_connection_t * connection = sm_get_connection_for_handle(sm_active_connection);
if (!connection) return;
// send keypress notifications
if (setup->sm_keypress_notification != 0xff){
uint8_t buffer[2];
buffer[0] = SM_CODE_KEYPRESS_NOTIFICATION;
buffer[1] = setup->sm_keypress_notification;
setup->sm_keypress_notification = 0xff;
l2cap_send_connectionless(connection->sm_handle, L2CAP_CID_SECURITY_MANAGER_PROTOCOL, (uint8_t*) buffer, sizeof(buffer));
return;
}
sm_key_t plaintext;
int key_distribution_flags;
@ -2014,6 +2100,16 @@ static void sm_run(void){
connection->sm_engine_state = SM_SC_W4_CALCULATE_G2;
g2_calculate(connection);
break;
case SM_SC_W2_CALCULATE_H6_ILK:
if (!sm_cmac_ready()) break;
connection->sm_engine_state = SM_SC_W4_CALCULATE_H6_ILK;
h6_calculate_ilk(connection);
break;
case SM_SC_W2_CALCULATE_H6_BR_EDR_LINK_KEY:
if (!sm_cmac_ready()) break;
connection->sm_engine_state = SM_SC_W4_CALCULATE_H6_BR_EDR_LINK_KEY;
h6_calculate_br_edr_link_key(connection);
break;
#endif
// initiator side
@ -2293,7 +2389,7 @@ static void sm_run(void){
hci_send_cmd(&hci_le_start_encryption, connection->sm_handle, 0, 0, 0, stk_flipped);
return;
}
case SM_RESPONDER_PH4_SEND_LTK: {
case SM_RESPONDER_PH4_SEND_LTK_REPLY: {
sm_key_t ltk_flipped;
reverse_128(setup->sm_ltk, ltk_flipped);
connection->sm_engine_state = SM_RESPONDER_IDLE;
@ -2540,9 +2636,13 @@ static void sm_handle_encryption_result(uint8_t * data){
// slave -> receive master keys
connection->sm_engine_state = SM_PH3_RECEIVE_KEYS;
} else {
// master -> all done
connection->sm_engine_state = SM_INITIATOR_CONNECTED;
sm_done_for_handle(connection->sm_handle);
if (setup->sm_use_secure_connections && (setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_IDENTITY_ADDRESS_INFORMATION)){
connection->sm_engine_state = SM_SC_W2_CALCULATE_H6_ILK;
} else {
// master -> all done
connection->sm_engine_state = SM_INITIATOR_CONNECTED;
sm_done_for_handle(connection->sm_handle);
}
}
}
return;
@ -2550,7 +2650,7 @@ static void sm_handle_encryption_result(uint8_t * data){
reverse_128(data, setup->sm_ltk);
sm_truncate_key(setup->sm_ltk, connection->sm_actual_encryption_key_size);
log_info_key("ltk", setup->sm_ltk);
connection->sm_engine_state = SM_RESPONDER_PH4_SEND_LTK;
connection->sm_engine_state = SM_RESPONDER_PH4_SEND_LTK_REPLY;
return;
default:
break;
@ -2590,6 +2690,7 @@ static void sm_handle_random_result(uint8_t * data){
setup->sm_passkey_bit = num_bytes;
if (num_bytes >= 64){
// generate EC key
setup->sm_passkey_bit = 0;
mbedtls_mpi d;
@ -2607,11 +2708,15 @@ static void sm_handle_random_result(uint8_t * data){
sm_log_ec_keypair();
#if 0
printf("test dhkey check\n");
int i;
sm_key256_t dhkey;
memcpy(setup->sm_peer_qx, ec_qx, 32);
memcpy(setup->sm_peer_qy, ec_qy, 32);
sm_sc_calculate_dhkey(dhkey);
for (i=0;i<10;i++){
// printf("test dhkey check\n");
memcpy(setup->sm_peer_qx, ec_qx, 32);
memcpy(setup->sm_peer_qy, ec_qy, 32);
sm_sc_calculate_dhkey(dhkey);
// printf("test dhkey check end\n");
}
#endif
}
@ -2731,6 +2836,12 @@ static void sm_event_packet_handler (uint8_t packet_type, uint16_t channel, uint
// bt stack activated, get started
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){
log_info("HCI Working!");
// set local addr for le device db
bd_addr_t local_bd_addr;
gap_local_bd_addr(local_bd_addr);
le_device_db_set_local_bd_addr(local_bd_addr);
dkg_state = sm_persistent_irk_ready ? DKG_CALC_DHK : DKG_CALC_IRK;
rau_state = RAU_IDLE;
#ifdef USE_MBEDTLS_FOR_ECDH
@ -2802,6 +2913,7 @@ static void sm_event_packet_handler (uint8_t packet_type, uint16_t channel, uint
break;
}
if (sm_conn->sm_engine_state == SM_SC_W4_LTK_REQUEST_SC){
// PH2 SEND LTK as we need to exchange keys in PH3
sm_conn->sm_engine_state = SM_RESPONDER_PH2_SEND_LTK_REPLY;
break;
}
@ -2809,7 +2921,20 @@ static void sm_event_packet_handler (uint8_t packet_type, uint16_t channel, uint
// store rand and ediv
reverse_64(&packet[5], sm_conn->sm_local_rand);
sm_conn->sm_local_ediv = little_endian_read_16(packet, 13);
sm_conn->sm_engine_state = SM_RESPONDER_PH0_RECEIVED_LTK;
// For Legacy Pairing (<=> EDIV != 0 || RAND != NULL), we need to recalculated our LTK as a
// potentially stored LTK is from the master
if (sm_conn->sm_local_ediv != 0 || !sm_is_null_random(sm_conn->sm_local_rand)){
sm_start_calculating_ltk_from_ediv_and_rand(sm_conn);
break;
}
#ifdef ENABLE_LE_SECURE_CONNECTIONS
sm_conn->sm_engine_state = SM_RESPONDER_PH0_RECEIVED_LTK_REQUEST;
#else
log_info("LTK Request: ediv & random are empty, but LE Secure Connections not supported");
sm_conn->sm_engine_state = SM_RESPONDER_PH0_SEND_LTK_REQUESTED_NEGATIVE_REPLY;
#endif
break;
default:
@ -2994,6 +3119,16 @@ static void sm_pdu_handler(uint8_t packet_type, hci_con_handle_t con_handle, uin
int err;
if (packet[0] == SM_CODE_KEYPRESS_NOTIFICATION){
uint8_t buffer[5];
buffer[0] = SM_EVENT_KEYPRESS_NOTIFICATION;
buffer[1] = 3;
little_endian_store_16(buffer, 2, con_handle);
buffer[4] = packet[1];
sm_dispatch_event(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
return;
}
switch (sm_conn->sm_engine_state){
// a sm timeout requries a new physical connection
@ -3011,10 +3146,9 @@ static void sm_pdu_handler(uint8_t packet_type, hci_con_handle_t con_handle, uin
break;
}
if (sm_conn->sm_irk_lookup_state == IRK_LOOKUP_SUCCEEDED){
uint16_t ediv;
sm_key_t ltk;
le_device_db_encryption_get(sm_conn->sm_le_db_index, &ediv, NULL, ltk, NULL, NULL, NULL);
if (!sm_is_null_key(ltk) || ediv){
le_device_db_encryption_get(sm_conn->sm_le_db_index, NULL, NULL, ltk, NULL, NULL, NULL);
if (!sm_is_null_key(ltk)){
log_info("sm: Setting up previous ltk/ediv/rand for device index %u", sm_conn->sm_le_db_index);
sm_conn->sm_engine_state = SM_INITIATOR_PH0_HAS_LTK;
} else {
@ -3122,7 +3256,7 @@ static void sm_pdu_handler(uint8_t packet_type, hci_con_handle_t con_handle, uin
mbedtls_ecp_point_init( &Q );
mbedtls_mpi_read_binary(&Q.X, setup->sm_peer_qx, 32);
mbedtls_mpi_read_binary(&Q.Y, setup->sm_peer_qy, 32);
mbedtls_mpi_read_string(&Q.Z, 16, "1" );
mbedtls_mpi_lset(&Q.Z, 1);
err = mbedtls_ecp_check_pubkey(&mbedtls_ec_group, &Q);
mbedtls_ecp_point_free( & Q);
if (err){
@ -3316,8 +3450,12 @@ static void sm_pdu_handler(uint8_t packet_type, hci_con_handle_t con_handle, uin
sm_key_distribution_handle_all_received(sm_conn);
if (sm_conn->sm_role){
sm_conn->sm_engine_state = SM_RESPONDER_IDLE;
sm_done_for_handle(sm_conn->sm_handle);
if (setup->sm_use_secure_connections && (setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_IDENTITY_ADDRESS_INFORMATION)){
sm_conn->sm_engine_state = SM_SC_W2_CALCULATE_H6_ILK;
} else {
sm_conn->sm_engine_state = SM_RESPONDER_IDLE;
sm_done_for_handle(sm_conn->sm_handle);
}
} else {
if (setup->sm_use_secure_connections){
sm_conn->sm_engine_state = SM_PH3_DISTRIBUTE_KEYS;
@ -3435,7 +3573,6 @@ void sm_init(void){
#endif
mbedtls_ecp_group_init(&mbedtls_ec_group);
mbedtls_ecp_group_load(&mbedtls_ec_group, MBEDTLS_ECP_DP_SECP256R1);
#if 0
// test
sm_test_use_fixed_ec_keypair();
@ -3652,6 +3789,14 @@ void sm_passkey_input(hci_con_handle_t con_handle, uint32_t passkey){
sm_run();
}
void sm_keypress_notification(hci_con_handle_t con_handle, uint8_t action){
sm_connection_t * sm_conn = sm_get_connection_for_handle(con_handle);
if (!sm_conn) return; // wrong connection
if (action > SM_KEYPRESS_PASSKEY_ENTRY_COMPLETED) return;
setup->sm_keypress_notification = action;
sm_run();
}
/**
* @brief Identify device in LE Device DB
* @param handle

View File

@ -189,6 +189,13 @@ void sm_numeric_comparison_confirm(hci_con_handle_t con_handle);
*/
void sm_passkey_input(hci_con_handle_t con_handle, uint32_t passkey);
/**
* @brief Send keypress notification for keyboard only devices
* @param con_handle
* @param action see SM_KEYPRESS_* in bluetooth.h
*/
void sm_keypress_notification(hci_con_handle_t con_handle, uint8_t action);
/**
*
* @brief Get encryption key size.
@ -229,6 +236,23 @@ void sm_authorization_decline(hci_con_handle_t con_handle);
*/
void sm_authorization_grant(hci_con_handle_t con_handle);
/**
* @brief Check if CMAC AES engine is ready
* @return ready
*/
int sm_cmac_ready(void);
/*
* @brief Generic CMAC AES
* @param key
* @param message_len
* @param get_byte_callback
* @param done_callback
* @note hash is 16 bytes in big endian
*/
void sm_cmac_general_start(const sm_key_t key, uint16_t message_len, uint8_t (*get_byte_callback)(uint16_t offset), void (*done_callback)(uint8_t * hash));
/**
* @brief Support for signed writes, used by att_server.
* @note Message is in little endian to allows passing in ATT PDU without flipping.
@ -241,18 +265,7 @@ void sm_authorization_grant(hci_con_handle_t con_handle);
* @param message
* @param sign_counter
*/
int sm_cmac_ready(void);
void sm_cmac_start(const sm_key_t key, uint8_t opcode, uint16_t attribute_handle, uint16_t message_len, const uint8_t * message, uint32_t sign_counter, void (*done_callback)(uint8_t * hash));
/*
* @brief Generic CMAC AES
* @param key
* @param message_len
* @param get_byte_callback
* @param done_callback
* @note hash is 16 bytes in big endian
*/
void sm_cmac_general_start(const sm_key_t key, uint16_t message_len, uint8_t (*get_byte_callback)(uint16_t offset), void (*done_callback)(uint8_t * hash));
void sm_cmac_signed_write_start(const sm_key_t key, uint8_t opcode, uint16_t attribute_handle, uint16_t message_len, const uint8_t * message, uint32_t sign_counter, void (*done_callback)(uint8_t * hash));
/*
* @brief Match address against bonded devices

View File

@ -954,6 +954,7 @@ typedef enum {
SM_CODE_SECURITY_REQUEST,
SM_CODE_PAIRING_PUBLIC_KEY,
SM_CODE_PAIRING_DHKEY_CHECK,
SM_CODE_KEYPRESS_NOTIFICATION,
} SECURITY_MANAGER_COMMANDS;
// IO Capability Values
@ -1009,6 +1010,14 @@ typedef enum {
// also, invalid parameters
// and reserved
// Keypress Notifications
#define SM_KEYPRESS_PASSKEY_ENTRY_STARTED 0x00
#define SM_KEYPRESS_PASSKEY_DIGIT_ENTERED 0x01
#define SM_KEYPRESS_PASSKEY_DIGIT_ERASED 0x02
#define SM_KEYPRESS_PASSKEY_CLEARED 0x03
#define SM_KEYPRESS_PASSKEY_ENTRY_COMPLETED 0x04
// Company identifiers / manufacturers
#define COMPANY_ID_CAMBRIDGE_SILICON_RADIO 0x000A
#define COMPANY_ID_TEXAS_INSTRUMENTS_INC 0x000D

View File

@ -793,6 +793,14 @@ typedef uint8_t sm_key_t[16];
*/
#define SM_EVENT_AUTHORIZATION_RESULT 0xDC
/**
* @format H1
* @param handle
* @param action see SM_KEYPRESS_*
*/
#define SM_EVENT_KEYPRESS_NOTIFICATION 0xDD
// GAP
/**

View File

@ -2417,6 +2417,27 @@ static inline uint8_t sm_event_authorization_result_get_authorization_result(con
}
#endif
#ifdef ENABLE_BLE
/**
* @brief Get field handle from event SM_EVENT_KEYPRESS_NOTIFICATION
* @param event packet
* @return handle
* @note: btstack_type H
*/
static inline hci_con_handle_t sm_event_keypress_notification_get_handle(const uint8_t * event){
return little_endian_read_16(event, 2);
}
/**
* @brief Get field action from event SM_EVENT_KEYPRESS_NOTIFICATION
* @param event packet
* @return action
* @note: btstack_type 1
*/
static inline uint8_t sm_event_keypress_notification_get_action(const uint8_t * event){
return event[4];
}
#endif
/**
* @brief Get field handle from event GAP_EVENT_SECURITY_LEVEL
* @param event packet

View File

@ -44,10 +44,11 @@
*/
#include "btstack_config.h"
#include "btstack_debug.h"
#include "btstack_util.h"
#include <stdio.h>
#include <string.h>
#include "btstack_debug.h"
/**
@ -171,17 +172,12 @@ void printf_hexdump(const void *data, int size){
printf("\n");
}
// void log_info_hexdump(..){
//
// }
void log_info_hexdump(const void *data, int size){
#ifdef ENABLE_LOG_INFO
char buffer[6*16+1];
int i, j;
uint8_t low = 0x0F;
uint8_t high = 0xF0;
const uint8_t low = 0x0F;
const uint8_t high = 0xF0;
j = 0;
for (i=0; i<size;i++){
uint8_t byte = ((uint8_t *)data)[i];
@ -205,8 +201,20 @@ void log_info_hexdump(const void *data, int size){
}
void log_info_key(const char * name, sm_key_t key){
// log_info("%-6s ", name);
// hexdump(key, 16);
#ifdef ENABLE_LOG_INFO
char buffer[16*2+1];
const uint8_t low = 0x0F;
const uint8_t high = 0xF0;
int i;
int j = 0;
for (i=0; i<16;i++){
uint8_t byte = key[i];
buffer[j++] = char_for_nibble((byte & high) >> 4);
buffer[j++] = char_for_nibble(byte & low);
}
buffer[j] = 0;
log_info("%-6s %s", name, buffer);
#endif
}
// UUIDs are stored in big endian, similar to bd_addr_t

View File

@ -353,9 +353,18 @@ void gap_local_bd_addr(bd_addr_t address_buffer);
/**
* @brief Deletes link key for remote device with baseband address.
* @param addr
*/
void gap_drop_link_key_for_bd_addr(bd_addr_t addr);
/**
* @brief Store link key for remote device with baseband address
* @param addr
* @param link_key
* @param link_key_type
*/
void gap_store_link_key_for_bd_addr(bd_addr_t addr, link_key_t link_key, link_key_type_t type);
// LE
/**

View File

@ -263,9 +263,15 @@ int hci_authentication_active_for_handle(hci_con_handle_t handle){
}
void gap_drop_link_key_for_bd_addr(bd_addr_t addr){
if (hci_stack->link_key_db) {
hci_stack->link_key_db->delete_link_key(addr);
}
if (!hci_stack->link_key_db) return;
log_info("gap_drop_link_key_for_bd_addr: %s", bd_addr_to_str(addr));
hci_stack->link_key_db->delete_link_key(addr);
}
void gap_store_link_key_for_bd_addr(bd_addr_t addr, link_key_t link_key, link_key_type_t type){
if (!hci_stack->link_key_db) return;
log_info("gap_store_link_key_for_bd_addr: %s, type %u", bd_addr_to_str(addr), type);
hci_stack->link_key_db->put_link_key(addr, link_key, type);
}
static int hci_is_le_connection(hci_connection_t * connection){
@ -273,7 +279,6 @@ static int hci_is_le_connection(hci_connection_t * connection){
connection->address_type == BD_ADDR_TYPE_LE_RANDOM;
}
/**
* count connections
*/
@ -1631,8 +1636,7 @@ static void event_handler(uint8_t *packet, int size){
if (link_key_type != CHANGED_COMBINATION_KEY){
conn->link_key_type = link_key_type;
}
if (!hci_stack->link_key_db) break;
hci_stack->link_key_db->put_link_key(addr, &packet[8], conn->link_key_type);
gap_store_link_key_for_bd_addr(addr, &packet[8], conn->link_key_type);
// still forward event to allow dismiss of pairing dialog
break;
}

View File

@ -271,7 +271,7 @@ typedef enum {
// state = 35
SM_RESPONDER_IDLE,
SM_RESPONDER_SEND_SECURITY_REQUEST,
SM_RESPONDER_PH0_RECEIVED_LTK,
SM_RESPONDER_PH0_RECEIVED_LTK_REQUEST,
SM_RESPONDER_PH0_SEND_LTK_REQUESTED_NEGATIVE_REPLY,
SM_RESPONDER_PH1_W4_PAIRING_REQUEST,
SM_RESPONDER_PH1_PAIRING_REQUEST_RECEIVED,
@ -287,7 +287,7 @@ typedef enum {
SM_RESPONDER_PH4_Y_W4_ENC,
SM_RESPONDER_PH4_LTK_GET_ENC,
SM_RESPONDER_PH4_LTK_W4_ENC,
SM_RESPONDER_PH4_SEND_LTK,
SM_RESPONDER_PH4_SEND_LTK_REPLY,
// INITITIATOR ROLE
// state = 51
@ -333,7 +333,10 @@ typedef enum {
SM_SC_SEND_DHKEY_CHECK_COMMAND,
SM_SC_W4_DHKEY_CHECK_COMMAND,
SM_SC_W4_LTK_REQUEST_SC,
SM_SC_W2_CALCULATE_H6_ILK,
SM_SC_W4_CALCULATE_H6_ILK,
SM_SC_W2_CALCULATE_H6_BR_EDR_LINK_KEY,
SM_SC_W4_CALCULATE_H6_BR_EDR_LINK_KEY,
} security_manager_state_t;
typedef enum {

View File

@ -1,2 +1,4 @@
remote_device_db_fs_test
remote_device_db_memory_test
btstack_link_key_db_fs_test
btstack_link_key_db_memory_test

View File

@ -121,7 +121,7 @@ int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t l
int sm_cmac_ready(void){
return 1;
}
void sm_cmac_start(const sm_key_t key, uint8_t opcode, uint16_t attribute_handle, uint16_t message_len, const uint8_t * message, uint32_t sign_counter, void (*done_callback)(uint8_t * hash)){
void sm_cmac_signed_write_start(const sm_key_t key, uint8_t opcode, uint16_t attribute_handle, uint16_t message_len, const uint8_t * message, uint32_t sign_counter, void (*done_callback)(uint8_t * hash)){
//sm_notify_client(SM_EVENT_IDENTITY_RESOLVING_SUCCEEDED, sm_central_device_addr_type, sm_central_device_address, 0, sm_central_device_matched);
}
int sm_le_device_index(uint16_t handle ){

View File

@ -3,7 +3,7 @@ BTSTACK_ROOT = ../..
CORE += main.c stdin_support.c
COMMON += hci_transport_h2_libusb.c btstack_run_loop_posix.c btstack_link_key_db_fs.c
COMMON += hci_transport_h2_libusb.c btstack_run_loop_posix.c btstack_link_key_db_fs.c le_device_db_fs.c
include ${BTSTACK_ROOT}/example/Makefile.inc

View File

@ -1457,7 +1457,7 @@ static void ui_process_command(char buffer){
// fetch csrk
le_device_db_local_csrk_get(le_device_db_index, signing_csrk);
// calc signature
sm_cmac_start(signing_csrk, ATT_SIGNED_WRITE_COMMAND, pts_signed_write_characteristic_handle, sizeof(signed_write_value), signed_write_value, 0, att_signed_write_handle_cmac_result);
sm_cmac_signed_write_start(signing_csrk, ATT_SIGNED_WRITE_COMMAND, pts_signed_write_characteristic_handle, sizeof(signed_write_value), signed_write_value, 0, att_signed_write_handle_cmac_result);
break;
case 'x':
sm_min_key_size = 7;

View File

@ -97,7 +97,7 @@ void mock_simulate_sm_data_packet(uint8_t * packet, uint16_t len){
}
void mock_simulate_command_complete(const hci_cmd_t *cmd){
uint8_t packet[] = {HCI_EVENT_COMMAND_COMPLETE, 4, 1, cmd->opcode & 0xff, cmd->opcode >> 8, 0};
uint8_t packet[] = {HCI_EVENT_COMMAND_COMPLETE, 4, 1, (uint8_t) cmd->opcode & 0xff, (uint8_t) cmd->opcode >> 8, 0};
mock_simulate_hci_event((uint8_t *)&packet, sizeof(packet));
}
@ -118,6 +118,12 @@ void att_init_connection(att_connection_t * att_connection){
att_connection->authorized = 0;
}
void gap_local_bd_addr(bd_addr_t address_buffer){
int i;
for (i=0;i<6;i++) {
address_buffer[i] = 0x11 * (i+1);
}
}
int hci_can_send_command_packet_now(void){
return 1;
}

View File

@ -275,7 +275,7 @@ TEST(SecurityManager, CMACTest){
parse_hex(key, key_string);
uint8_t message [] = "hallo";
cmac_hash_received = 0;
sm_cmac_start(key, 0x11, 0x1234, sizeof(message), message, 1, &cmac_done);
sm_cmac_signed_write_start(key, 0x11, 0x1234, sizeof(message), message, 1, &cmac_done);
while (!cmac_hash_received){
aes128_report_result();
}

View File

@ -143,7 +143,7 @@ static inline uint8_t hci_event_{meta_event}_meta_get_subevent_code(const uint8_
'''
# global variables/defines
gen_path = '../src/btstack_event.h'
# gen_path = '../src/btstack_event.h'
defines = dict()
defines_used = set()
@ -264,8 +264,8 @@ def create_events(events):
fout.write(hfile_header_end)
# set root
parser.set_btstack_root('..')
btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
gen_path = btstack_root + '/src/btstack_event.h'
print(program_info)