mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-24 04:43:36 +00:00
pbap_client: add support for md5 authentication challenge
This commit is contained in:
parent
c08e23972a
commit
c1bc0b8eb5
@ -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
|
||||
|
@ -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){
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user