pbap_client: add support for md5 authentication challenge

This commit is contained in:
Matthias Ringwald 2018-09-17 17:16:59 +02:00
parent c08e23972a
commit c1bc0b8eb5
8 changed files with 185 additions and 19 deletions

View File

@ -5,6 +5,7 @@ VPATH += ${BTSTACK_ROOT}/src/ble/gatt-service
VPATH += ${BTSTACK_ROOT}/src/classic
VPATH += ${BTSTACK_ROOT}/example
VPATH += ${BTSTACK_ROOT}/3rd-party/hxcmod-player
VPATH += ${BTSTACK_ROOT}/3rd-party/md5
VPATH += ${BTSTACK_ROOT}/3rd-party/micro-ecc
VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/srce
VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder//srce
@ -14,6 +15,7 @@ CFLAGS += -I${BTSTACK_ROOT}/src/ble
CFLAGS += -I${BTSTACK_ROOT}/src/classic
CFLAGS += -I${BTSTACK_ROOT}/src
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/hxcmod-player
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/md5
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/micro-ecc
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/include
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/include
@ -192,7 +194,7 @@ ant_test: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ant_test.c
sdp_rfcomm_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${PAN_OBJ} ${SDP_CLIENT} sdp_rfcomm_query.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
pbap_client_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} obex_iterator.c goep_client.c pbap_client.c pbap_client_demo.c
pbap_client_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} md5.o obex_iterator.c goep_client.c pbap_client.c pbap_client_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
sdp_general_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} sdp_general_query.c

View File

@ -70,7 +70,9 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
static bd_addr_t remote_addr;
// MBP2016 "F4-0F-24-3B-1B-E1"
// Nexus 7 "30-85-A9-54-2E-78"
static const char * remote_addr_string = "BC:EC:5D:E6:15:03";
// iPhone SE "BC:EC:5D:E6:15:03"
// PTS "001BDC080AA5"
static char * remote_addr_string = "001BDC080AA5";
static btstack_packet_callback_registration_t hci_event_callback_registration;
static uint16_t pbap_cid;
@ -90,6 +92,7 @@ static void show_usage(void){
printf("d - get phonebook size\n");
printf("e - pull phonebook\n");
printf("f - disconnnect\n");
printf("p - authenticate using password '0000'\n");
printf("\n");
}
@ -116,6 +119,9 @@ static void stdin_process(char c){
case 'f':
pbap_disconnect(pbap_cid);
break;
case 'p':
pbap_authentication_password(pbap_cid, "0000");
break;
default:
show_usage();
break;
@ -148,6 +154,9 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
case PBAP_SUBEVENT_OPERATION_COMPLETED:
printf("[+] Operation complete\n");
break;
case PBAP_SUBEVENT_AUTHENTICATION_REQUEST:
printf("[?] Authentication requested\n");
break;
case PBAP_SUBEVENT_PHONEBOOK_SIZE:
status = pbap_subevent_phonebook_size_get_status(packet);
if (status){

View File

@ -15,6 +15,7 @@ COMPONENT_ADD_INCLUDEDIRS := \
3rd-party/bluedroid/encoder/include \
3rd-party/hxcmod-player \
3rd-party/hxcmod-player/mods \
3rd-party/md5 \
src/classic \
src/ble/gatt-service \
src/ble \
@ -29,6 +30,7 @@ COMPONENT_SRCDIRS := \
3rd-party/bluedroid/encoder/srce \
3rd-party/hxcmod-player \
3rd-party/hxcmod-player/mods \
3rd-party/md5 \
src/ble/gatt-service \
src/ble \
src/classic \

View File

@ -104,6 +104,7 @@ VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/srce
VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/srce
VPATH += ${BTSTACK_ROOT}/3rd-party/hxcmod-player
VPATH += ${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods
VPATH += ${BTSTACK_ROOT}/3rd-party/md5
VPATH += ${BTSTACK_ROOT}/3rd-party/micro-ecc
VPATH += ${BTSTACK_ROOT}/platform/embedded
VPATH += ${BTSTACK_ROOT}/src/ble/gatt-service/
@ -119,6 +120,7 @@ PROJ_CFLAGS += \
-I${BTSTACK_ROOT}/example \
-I${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/include \
-I${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/include \
-I${BTSTACK_ROOT}/3rd-party/md5 \
-I${BTSTACK_ROOT}/3rd-party/micro-ecc \
-I${BTSTACK_ROOT}/3rd-party/hxcmod-player \
@ -226,7 +228,7 @@ SRCS += $(CVSD_PLC_OBJ)
SRCS += $(HXCMOD_PLAYER_OBJ)
SRCS += $(HFP_OBJ)
SRCS += hsp_hs.o hsp_ag.o
SRCS += obex_iterator.o goep_client.o pbap_client.o
SRCS += obex_iterator.o goep_client.o pbap_client.o md5.o
# Enable assertion checking for development
PROJ_CFLAGS+=-DMXC_ASSERT_ENABLE

View File

@ -2056,6 +2056,16 @@ typedef uint8_t sm_key_t[16];
*/
#define PBAP_SUBEVENT_PHONEBOOK_SIZE 0x04
/**
* @format 1211
* @param subevent_code
* @param goep_cid
* @param user_id_required
* @param full_access
*/
#define PBAP_SUBEVENT_AUTHENTICATION_REQUEST 0x05
// HID Meta Event Group
/**

View File

@ -6534,6 +6534,34 @@ static inline uint16_t pbap_subevent_phonebook_size_get_phoneboook_size(const ui
return little_endian_read_16(event, 6);
}
/**
* @brief Get field goep_cid from event PBAP_SUBEVENT_AUTHENTICATION_REQUEST
* @param event packet
* @return goep_cid
* @note: btstack_type 2
*/
static inline uint16_t pbap_subevent_authentication_request_get_goep_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field user_id_required from event PBAP_SUBEVENT_AUTHENTICATION_REQUEST
* @param event packet
* @return user_id_required
* @note: btstack_type 1
*/
static inline uint8_t pbap_subevent_authentication_request_get_user_id_required(const uint8_t * event){
return event[5];
}
/**
* @brief Get field full_access from event PBAP_SUBEVENT_AUTHENTICATION_REQUEST
* @param event packet
* @return full_access
* @note: btstack_type 1
*/
static inline uint8_t pbap_subevent_authentication_request_get_full_access(const uint8_t * event){
return event[6];
}
/**
* @brief Get field hid_cid from event HID_SUBEVENT_CONNECTION_OPENED
* @param event packet

View File

@ -74,6 +74,7 @@
#include "bluetooth_sdp.h"
#include "classic/sdp_client_rfcomm.h"
#include "btstack_event.h"
#include "md5.h"
#include "classic/obex.h"
#include "classic/obex_iterator.h"
@ -90,6 +91,8 @@ typedef enum {
PBAP_W4_GOEP_CONNECTION,
PBAP_W2_SEND_CONNECT_REQUEST,
PBAP_W4_CONNECT_RESPONSE,
PBAP_W4_USER_AUTHENTICATION,
PBAP_W2_SEND_AUTHENTICATED_CONNECT,
PBAP_CONNECT_RESPONSE_RECEIVED,
PBAP_CONNECTED,
//
@ -116,6 +119,9 @@ typedef struct pbap_client {
btstack_packet_handler_t client_handler;
const char * current_folder;
uint16_t set_path_offset;
uint8_t authentication_options;
uint16_t authentication_nonce[16];
const char * authentication_password;
} pbap_client_t;
static pbap_client_t _pbap_client;
@ -183,11 +189,35 @@ static void pbap_client_emit_phonebook_size_event(pbap_client_t * context, uint8
context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static void pbap_client_emit_authentication_event(pbap_client_t * context, uint8_t options){
// split options
uint8_t user_id_required = options & 1 ? 1 : 0;
uint8_t full_access = options & 2 ? 1 : 0;
uint8_t event[7];
int pos = 0;
event[pos++] = HCI_EVENT_PBAP_META;
pos++; // skip len
event[pos++] = PBAP_SUBEVENT_AUTHENTICATION_REQUEST;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[pos++] = user_id_required;
event[pos++] = full_access;
if (pos != sizeof(event)) log_error("pbap_client_emit_authentication_event size %u", pos);
context->client_handler(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static const uint8_t collon = (uint8_t) ':';
static void pbap_handle_can_send_now(void){
uint8_t path_element[20];
uint16_t path_element_start;
uint16_t path_element_len;
uint8_t application_parameters[20];
uint8_t challenge_response[36];
int i;
MD5_CTX md5_ctx;
switch (pbap_client->state){
case PBAP_W2_SEND_CONNECT_REQUEST:
@ -195,7 +225,29 @@ static void pbap_handle_can_send_now(void){
goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid);
pbap_client->state = PBAP_W4_CONNECT_RESPONSE;
goep_client_execute(pbap_client->goep_cid);
return;
break;
case PBAP_W2_SEND_AUTHENTICATED_CONNECT:
goep_client_create_connect_request(pbap_client->goep_cid, OBEX_VERSION, 0, OBEX_MAX_PACKETLEN_DEFAULT);
goep_client_add_header_target(pbap_client->goep_cid, 16, pbap_uuid);
// setup authentication challenge response
i = 0;
challenge_response[i++] = 0; // Tag Digest
challenge_response[i++] = 16; // Len
// calculate md5
MD5_Init(&md5_ctx);
MD5_Update(&md5_ctx, pbap_client->authentication_nonce, 16);
MD5_Update(&md5_ctx, &collon, 1);
MD5_Update(&md5_ctx, pbap_client->authentication_password, strlen(pbap_client->authentication_password));
MD5_Final(&challenge_response[i], &md5_ctx);
i += 16;
challenge_response[i++] = 2; // Tag Nonce
challenge_response[i++] = 16; // Len
memcpy(&challenge_response[i], pbap_client->authentication_nonce, 16);
i += 16;
goep_client_add_header_challenge_response(pbap_client->goep_cid, i, challenge_response);
pbap_client->state = PBAP_W4_CONNECT_RESPONSE;
goep_client_execute(pbap_client->goep_cid);
break;
case PBAP_W2_SEND_DISCONNECT_REQUEST:
goep_client_create_disconnect_request(pbap_client->goep_cid);
pbap_client->state = PBAP_W4_DISCONNECT_RESPONSE;
@ -257,6 +309,38 @@ static void pbap_handle_can_send_now(void){
}
}
static void pbap_parse_authentication_challenge(pbap_client_t * context, const uint8_t * challenge_data, uint16_t challenge_len){
// printf("Challenge: ");
// printf_hexdump(challenge_data, challenge_len);
int i;
uint8_t charset_code = 0;
for (i=0 ; i<challenge_len ; ){
int tag = challenge_data[i];
int len = challenge_data[i + 1];
i += 2;
switch (tag) {
case 0:
if (len != 0x10) {
log_error("Invalid OBEX digest len %u", len);
return;
}
memcpy(context->authentication_nonce, &challenge_data[i], 16);
// printf("Nonce: ");
// printf_hexdump(context->authentication_nonce, 16);
break;
case 1:
context->authentication_options = challenge_data[i];
// printf("Options %u\n", context->authentication_options);
break;
case 2:
// TODO: handle charset
charset_code = challenge_data[i];
break;
}
i += len;
}
}
static void pbap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel); // ok: there is no channel
@ -303,24 +387,37 @@ static void pbap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *
break;
case GOEP_DATA_PACKET:
// TODO: handle chunked data
#if 0
obex_dump_packet(goep_client_get_request_opcode(pbap_client->goep_cid), packet, size);
#if 1
// obex_dump_packet(goep_client_get_request_opcode(pbap_client->goep_cid), packet, size);
#endif
switch (pbap_client->state){
case PBAP_W4_CONNECT_RESPONSE:
for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
uint8_t hi = obex_iterator_get_hi(&it);
if (hi == OBEX_HEADER_CONNECTION_ID){
goep_client_set_connection_id(pbap_client->goep_cid, obex_iterator_get_data_32(&it));
}
}
if (packet[0] == OBEX_RESP_SUCCESS){
pbap_client->state = PBAP_CONNECTED;
pbap_client_emit_connected_event(pbap_client, 0);
} else {
log_info("pbap: obex connect failed, result 0x%02x", packet[0]);
pbap_client->state = PBAP_INIT;
pbap_client_emit_connected_event(pbap_client, OBEX_CONNECT_FAILED);
switch (packet[0]){
case OBEX_RESP_SUCCESS:
for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
uint8_t hi = obex_iterator_get_hi(&it);
if (hi == OBEX_HEADER_CONNECTION_ID){
goep_client_set_connection_id(pbap_client->goep_cid, obex_iterator_get_data_32(&it));
}
}
pbap_client->state = PBAP_CONNECTED;
pbap_client_emit_connected_event(pbap_client, 0);
break;
case OBEX_RESP_UNAUTHORIZED:
for (obex_iterator_init_with_response_packet(&it, goep_client_get_request_opcode(pbap_client->goep_cid), packet, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
uint8_t hi = obex_iterator_get_hi(&it);
if (hi == OBEX_HEADER_AUTHENTICATION_CHALLENGE){
pbap_parse_authentication_challenge(pbap_client, obex_iterator_get_data(&it), obex_iterator_get_data_len(&it));
}
}
pbap_client->state = PBAP_W4_USER_AUTHENTICATION;
pbap_client_emit_authentication_event(pbap_client, pbap_client->authentication_options);
break;
default:
log_info("pbap: obex connect failed, result 0x%02x", packet[0]);
pbap_client->state = PBAP_INIT;
pbap_client_emit_connected_event(pbap_client, OBEX_CONNECT_FAILED);
break;
}
break;
case PBAP_W4_DISCONNECT_RESPONSE:
@ -451,3 +548,12 @@ uint8_t pbap_set_phonebook(uint16_t pbap_cid, const char * path){
goep_client_request_can_send_now(pbap_client->goep_cid);
return 0;
}
uint8_t pbap_authentication_password(uint16_t pbap_cid, const char * password){
UNUSED(pbap_cid);
if (pbap_client->state != PBAP_W4_USER_AUTHENTICATION) return BTSTACK_BUSY;
pbap_client->state = PBAP_W2_SEND_AUTHENTICATED_CONNECT;
pbap_client->authentication_password = password;
goep_client_request_can_send_now(pbap_client->goep_cid);
return 0;
}

View File

@ -60,6 +60,13 @@ void pbap_client_init(void);
*/
uint8_t pbap_connect(btstack_packet_handler_t handler, bd_addr_t addr, uint16_t * out_cid);
/**
* @brief Provide password for OBEX Authentication after receiving PBAP_SUBEVENT_AUTHENTICATION_REQUEST
* @param pbap_cid
* @param password (null terminated string) - not copied, needs to stay valid until connection completed
*/
uint8_t pbap_authentication_password(uint16_t pbap_cid, const char * password);
/**
* @brief Disconnects PBAP connection with given identifier.
* @param pbap_cid