-
Notifications
You must be signed in to change notification settings - Fork 607
/
sm_pairing_central.c
343 lines (292 loc) · 14.3 KB
/
sm_pairing_central.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
* 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 BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "sm_pairing_central.c"
// *****************************************************************************
/* EXAMPLE_START(sm_pairing_central): LE Central - Test Pairing Methods
*
* @text Depending on the Authentication requiremens and IO Capabilities,
* the pairing process uses different short and long term key generation method.
* This example helps explore the different options incl. LE Secure Connections.
* It scans for advertisements and connects to the first device that lists a
* random service.
*/
// *****************************************************************************
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "btstack.h"
// sm_pairing_central.gatt contains the declaration of the provided GATT Services + Characteristics
// sm_pairing_central.h contains the binary representation of sm_pairing_central.gatt
// it is generated by the build system by calling: $BTSTACK_ROOT/tool/compile_gatt.py sm_pairing_central.gatt sm_pairing_central.h
// it needs to be regenerated when the GATT Database declared in sm_pairing_central.gatt file is modified
#include "sm_pairing_central.h"
// We're looking for a remote device that lists this service in the advertisement
// LightBlue assigns 0x1111 as the UUID for a Blank service.
#define REMOTE_SERVICE 0x1111
// Fixed passkey - used with sm_pairing_peripheral. Passkey is random in general
#define FIXED_PASSKEY 123456U
static btstack_packet_callback_registration_t hci_event_callback_registration;
static btstack_packet_callback_registration_t sm_event_callback_registration;
/* @section GAP LE setup for receiving advertisements
*
* @text GAP LE advertisements are received as custom HCI events of the
* GAP_EVENT_ADVERTISING_REPORT type. To receive them, you'll need to register
* the HCI packet handler, as shown in Listing GAPLEAdvSetup.
*/
/* LISTING_START(GAPLEAdvSetup): Setting up GAP LE client for receiving advertisements */
static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void sm_pairing_central_setup(void){
l2cap_init();
// setup SM: Display only
sm_init();
// setup ATT server
att_server_init(profile_data, NULL, NULL);
// setup GATT Client
gatt_client_init();
// register handler
hci_event_callback_registration.callback = &hci_packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
sm_event_callback_registration.callback = &sm_packet_handler;
sm_add_event_handler(&sm_event_callback_registration);
// Configuration
// Enable mandatory authentication for GATT Client
// - if un-encrypted connections are not supported, e.g. when connecting to own device, this enforces authentication
// gatt_client_set_required_security_level(LEVEL_2);
/**
* Choose ONE of the following configurations
* Bonding is disabled to allow for repeated testing. It can be enabled by or'ing
* SM_AUTHREQ_BONDING to the authentication requirements like this:
* sm_set_authentication_requirements( X | SM_AUTHREQ_BONDING)
*/
// LE Legacy Pairing, Just Works
// sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_YES_NO);
// sm_set_authentication_requirements(0);
// LE Legacy Pairing, Passkey entry initiator enter, responder (us) displays
// sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
// sm_set_authentication_requirements(SM_AUTHREQ_MITM_PROTECTION);
// sm_use_fixed_passkey_in_display_role(FIXED_PASSKEY);
#ifdef ENABLE_LE_SECURE_CONNECTIONS
// enable LE Secure Connections Only mode - disables Legacy pairing
// sm_set_secure_connections_only_mode(true);
// LE Secure Connections, Just Works
// sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_YES_NO);
// sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION);
// LE Secure Connections, Numeric Comparison
// sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_YES_NO);
// sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION);
// LE Secure Pairing, Passkey entry initiator (us) enters, responder displays
// sm_set_io_capabilities(IO_CAPABILITY_KEYBOARD_ONLY);
// sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION);
// sm_use_fixed_passkey_in_display_role(FIXED_PASSKEY);
// LE Secure Pairing, Passkey entry initiator (us) displays, responder enters
// sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
// sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION|SM_AUTHREQ_MITM_PROTECTION);
#endif
}
/* LISTING_END */
/* @section HCI packet handler
*
* @text The HCI packet handler has to start the scanning,
* and to handle received advertisements. Advertisements are received
* as HCI event packets of the GAP_EVENT_ADVERTISING_REPORT type,
* see Listing HCIPacketHandler.
*/
/* LISTING_START(HCIPacketHandler): Scanning and receiving advertisements */
static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
if (packet_type != HCI_EVENT_PACKET) return;
hci_con_handle_t con_handle;
uint8_t status;
switch (hci_event_packet_get_type(packet)) {
case BTSTACK_EVENT_STATE:
// BTstack activated, get started
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){
printf("Start scaning!\n");
gap_set_scan_parameters(1,0x0030, 0x0030);
gap_start_scan();
}
break;
case GAP_EVENT_ADVERTISING_REPORT:{
bd_addr_t address;
gap_event_advertising_report_get_address(packet, address);
uint8_t address_type = gap_event_advertising_report_get_address_type(packet);
uint8_t length = gap_event_advertising_report_get_data_length(packet);
const uint8_t * data = gap_event_advertising_report_get_data(packet);
// printf("Advertisement event: addr-type %u, addr %s, data[%u] ",
// address_type, bd_addr_to_str(address), length);
// printf_hexdump(data, length);
if (!ad_data_contains_uuid16(length, (uint8_t *) data, REMOTE_SERVICE)) break;
printf("Found remote with UUID %04x, connecting...\n", REMOTE_SERVICE);
gap_stop_scan();
gap_connect(address,address_type);
break;
}
case HCI_EVENT_META_GAP:
// wait for connection complete
if (hci_event_gap_meta_get_subevent_code(packet) != GAP_SUBEVENT_LE_CONNECTION_COMPLETE) break;
con_handle = gap_subevent_le_connection_complete_get_connection_handle(packet);
printf("Connection complete\n");
// for testing, choose one of the following actions
// manually start pairing
sm_request_pairing(con_handle);
// gatt client request to authenticated characteristic in sm_pairing_peripheral (short cut, uses hard-coded value handle)
// gatt_client_read_value_of_characteristic_using_value_handle(&hci_packet_handler, con_handle, 0x0009);
// general gatt client request to trigger mandatory authentication
// gatt_client_discover_primary_services(&hci_packet_handler, con_handle);
break;
case GATT_EVENT_QUERY_COMPLETE:
status = gatt_event_query_complete_get_att_status(packet);
switch (status){
case ATT_ERROR_INSUFFICIENT_ENCRYPTION:
printf("GATT Query result: Insufficient Encryption\n");
break;
case ATT_ERROR_INSUFFICIENT_AUTHENTICATION:
printf("GATT Query result: Insufficient Authentication\n");
break;
case ATT_ERROR_BONDING_INFORMATION_MISSING:
printf("GATT Query result: Bonding Information Missing\n");
break;
case ATT_ERROR_SUCCESS:
printf("GATT Query result: OK\n");
break;
default:
printf("GATT Query result: 0x%02x\n", gatt_event_query_complete_get_att_status(packet));
break;
}
break;
default:
break;
}
}
/* @section HCI packet handler
*
* @text The SM packet handler receives Security Manager Events required for pairing.
* It also receives events generated during Identity Resolving
* see Listing SMPacketHandler.
*/
/* LISTING_START(SMPacketHandler): Scanning and receiving advertisements */
static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
if (packet_type != HCI_EVENT_PACKET) return;
bd_addr_t addr;
bd_addr_type_t addr_type;
switch (hci_event_packet_get_type(packet)) {
case SM_EVENT_JUST_WORKS_REQUEST:
printf("Just works requested\n");
sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
break;
case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
break;
case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
break;
case SM_EVENT_PASSKEY_INPUT_NUMBER:
printf("Passkey Input requested\n");
printf("Sending fixed passkey %"PRIu32"\n", (uint32_t) FIXED_PASSKEY);
sm_passkey_input(sm_event_passkey_input_number_get_handle(packet), FIXED_PASSKEY);
break;
case SM_EVENT_PAIRING_STARTED:
printf("Pairing started\n");
break;
case SM_EVENT_PAIRING_COMPLETE:
switch (sm_event_pairing_complete_get_status(packet)){
case ERROR_CODE_SUCCESS:
printf("Pairing complete, success\n");
break;
case ERROR_CODE_CONNECTION_TIMEOUT:
printf("Pairing failed, timeout\n");
break;
case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
printf("Pairing failed, disconnected\n");
break;
case ERROR_CODE_AUTHENTICATION_FAILURE:
printf("Pairing failed, authentication failure with reason = %u\n", sm_event_pairing_complete_get_reason(packet));
break;
default:
break;
}
break;
case SM_EVENT_REENCRYPTION_STARTED:
sm_event_reencryption_complete_get_address(packet, addr);
printf("Bonding information exists for addr type %u, identity addr %s -> start re-encryption\n",
sm_event_reencryption_started_get_addr_type(packet), bd_addr_to_str(addr));
break;
case SM_EVENT_REENCRYPTION_COMPLETE:
switch (sm_event_reencryption_complete_get_status(packet)){
case ERROR_CODE_SUCCESS:
printf("Re-encryption complete, success\n");
break;
case ERROR_CODE_CONNECTION_TIMEOUT:
printf("Re-encryption failed, timeout\n");
break;
case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
printf("Re-encryption failed, disconnected\n");
break;
case ERROR_CODE_PIN_OR_KEY_MISSING:
printf("Re-encryption failed, bonding information missing\n\n");
printf("Assuming remote lost bonding information\n");
printf("Deleting local bonding information and start new pairing...\n");
sm_event_reencryption_complete_get_address(packet, addr);
addr_type = sm_event_reencryption_started_get_addr_type(packet);
gap_delete_bonding(addr_type, addr);
sm_request_pairing(sm_event_reencryption_complete_get_handle(packet));
break;
default:
break;
}
break;
default:
break;
}
}
/* LISTING_END */
int btstack_main(void);
int btstack_main(void)
{
sm_pairing_central_setup();
// turn on!
hci_power_control(HCI_POWER_ON);
return 0;
}
/* EXAMPLE_END */