From a5171bb237a8dfb2bf47938b0fc60295791677c1 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Tue, 27 Mar 2018 16:09:33 +0200 Subject: [PATCH] pts/sm_test: support passkey entry --- test/pts/sm_test.c | 38 ++++++++++++++++++++++++++++++++---- test/pts/sm_test.csv | 46 +++++++++++++++++++++++++++++++++++++++++++- test/pts/sm_test.py | 44 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 115 insertions(+), 13 deletions(-) diff --git a/test/pts/sm_test.c b/test/pts/sm_test.c index 03b9cbd3f..070832554 100644 --- a/test/pts/sm_test.c +++ b/test/pts/sm_test.c @@ -282,6 +282,7 @@ static void app_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t * printf("PASSKEY_INPUT_NUMBER\n"); ui_passkey = 0; ui_digits_for_passkey = 6; + // sm_keypress_notification(connection_handle, SM_KEYPRESS_PASSKEY_ENTRY_STARTED); break; case SM_EVENT_PASSKEY_DISPLAY_NUMBER: // display number @@ -313,8 +314,34 @@ static void app_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t * fflush(stdout); } -static void stdin_process(char c){ +#define KEYPRESS_DELAY 100 +static btstack_timer_source_t passkey_timer; +static int num_keypresses_to_send; +static void passkey_entry_complete(btstack_timer_source_t * ts){ + if (num_keypresses_to_send > 0){ + num_keypresses_to_send--; + // sm_keypress_notification(connection_handle, SM_KEYPRESS_PASSKEY_DIGIT_ENTERED); + btstack_run_loop_set_timer(ts, KEYPRESS_DELAY); + btstack_run_loop_add_timer(ts); + return; + } + if (num_keypresses_to_send == 0){ + num_keypresses_to_send--; + // sm_keypress_notification(connection_handle, SM_KEYPRESS_PASSKEY_ENTRY_COMPLETED); + btstack_run_loop_set_timer(ts, KEYPRESS_DELAY); + btstack_run_loop_add_timer(ts); + return; + } + printf("\nSending Passkey '%06u'\n", ui_passkey); + if (sm_failure == SM_REASON_PASSKEY_ENTRY_FAILED){ + // simulate passkey failure + ui_passkey--; + } + sm_passkey_input(connection_handle, ui_passkey); +} + +static void stdin_process(char c){ // passkey input if (ui_digits_for_passkey){ if (c < '0' || c > '9') return; @@ -323,8 +350,11 @@ static void stdin_process(char c){ ui_passkey = ui_passkey * 10 + c - '0'; ui_digits_for_passkey--; if (ui_digits_for_passkey == 0){ - printf("Sending Passkey '%06x'\n", ui_passkey); - sm_passkey_input(connection_handle, ui_passkey); + num_keypresses_to_send = 6; + // set one-shot timer + passkey_timer.process = &passkey_entry_complete; + btstack_run_loop_set_timer(&passkey_timer, KEYPRESS_DELAY); + btstack_run_loop_add_timer(&passkey_timer); } return; } @@ -423,7 +453,7 @@ int btstack_main(int argc, const char * argv[]){ sm_set_authentication_requirements(sm_auth_req); sm_register_oob_data_callback(get_oob_data_callback); - if (sm_failure < SM_REASON_NUMERIC_COMPARISON_FAILED){ + if (sm_failure < SM_REASON_NUMERIC_COMPARISON_FAILED && sm_failure != SM_REASON_PASSKEY_ENTRY_FAILED){ sm_test_set_pairing_failure(sm_failure); } diff --git a/test/pts/sm_test.csv b/test/pts/sm_test.csv index 010a3d090..7fedb3b19 100644 --- a/test/pts/sm_test.csv +++ b/test/pts/sm_test.csv @@ -60,5 +60,49 @@ SM/MAS/SCJW/BI-01-C/04,0,0,8,8,0,0,9 # tester responds with pairing failure during phase 2 SM/MAS/SCJW/BI-01-C/05,0,0,8,8,0,0,12 -# tester responds with pairing failure during phase 2 +# TODO: tester responds with pairing failure during phase 2 SM/SLA/SCJW/BI-02-C,0,3,8,8,0,0,4 + +# -- Passkey Entry (SCPK) -- + +# TODO: keypress notifications prevent pairing exchange +SM/MAS/SCPK/BV-01-C,0,2,24,28,0,0,0 +SM/SLA/SCPK/BV-02-C/01,2,4,24,12,0,0,0 +SM/SLA/SCPK/BV-02-C/02,2,4,24,28,0,0,0 + +# additional bits set in authreq + +SM/SLA/SCPK/BV-03-C/01,0,2,8,76,0,0,0 +SM/SLA/SCPK/BV-03-C/02,0,2,8,140,0,0,0 +SM/SLA/SCPK/BV-03-C/03,0,2,8,204,0,0,0 + +# same as SM/SLA/SCPK/BV-03-C, just with IUT in Initiator role + +SM/MAS/SCPK/BV-03-C/01,0,2,8,76,0,0,0 +SM/MAS/SCPK/BV-03-C/02,0,2,8,140,0,0,0 +SM/MAS/SCPK/BV-03-C/03,0,2,8,204,0,0,0 + +# tester responds with pairing failure in responder role + +SM/MAS/SCPK/BI-01-C/01,0,0,8,8,0,0,3 +SM/MAS/SCPK/BI-01-C/02,0,0,8,8,0,0,8 +SM/MAS/SCPK/BI-01-C/03,0,0,8,8,0,0,5 +SM/MAS/SCPK/BI-01-C/04,0,0,8,8,0,0,9 + +# TODO: we enter incorrect Passkey, and get DHKey Check error instead of passkey entry failed +SM/MAS/SCPK/BI-01-C/05,0,2,8,12,0,0,1 + +SM/SLA/SCPK/BI-04-C/01,0,0,8,8,0,0,3 +SM/SLA/SCPK/BI-04-C/02,0,0,8,8,0,0,8 +SM/SLA/SCPK/BI-04-C/03,0,0,8,8,0,0,5 +SM/SLA/SCPK/BI-04-C/04,0,0,8,8,0,0,9 + +# TODO: tester responds with pairing failure during phase 2 +SM/MAS/SCPK/BI-02-C,0,3,8,8,0,0,4 + +# TODO: tester responds with pairing failure during phase 2 +SM/SLA/SCPK/BI-03-C,0,3,8,8,0,0,4 + +# TODO: we enter incorrect Passkey, and get DHKey Check error instead of passkey entry failed +SM/SLA/SCPK/BI-04-C/05,0,2,8,12,0,0,1 + diff --git a/test/pts/sm_test.py b/test/pts/sm_test.py index efa3f88ac..55655a803 100755 --- a/test/pts/sm_test.py +++ b/test/pts/sm_test.py @@ -30,7 +30,7 @@ SM_AUTHREQ_SECURE_CONNECTION = 0x08 SM_AUTHREQ_KEYPRESS = 0x10 failures = [ -'RESERVED', +'', 'PASSKEY_ENTRY_FAILED', 'OOB_NOT_AVAILABLE', 'AUTHENTHICATION_REQUIREMENTS', @@ -46,7 +46,7 @@ failures = [ ] # tester config -debug = True +debug = False regenerate = False # usb_paths = ['4', '6'] usb_paths = ['3', '5'] @@ -110,6 +110,8 @@ class Node: if self.failure != None: args.append('-f') args.append(self.failure) + args.append('-i') + args.append(self.io_capabilities) args.append('-r') args.append(self.auth_req) print('%s - "%s"' % (self.name, ' '.join(args))) @@ -133,7 +135,9 @@ class Node: self.peer_addr = addr def write(self, string): - self.stdin.write(string) + for c in string: + self.stdin.write(string) + time.sleep (0.1); def terminate(self): self.write('x') @@ -158,7 +162,7 @@ def run(test_descriptor, nodes): path = line.split(': ')[1] node.set_packet_log(path) print('%s log %s' % (node.get_name(), path)) - if line.startswith('BD_ADDR: '): + elif line.startswith('BD_ADDR: '): addr = line.split(': ')[1] node.set_bd_addr(addr) print('%s started' % node.get_name()) @@ -177,11 +181,11 @@ def run(test_descriptor, nodes): master.set_failure(test_descriptor['tester_failure']) master.start_process() nodes.append(master) - if line.startswith('CONNECTED:'): + elif line.startswith('CONNECTED:'): print('%s connected' % node.get_name()) if state == 'W4_CONNECTED': state = 'W4_PAIRING' - if line.startswith('JUST_WORKS_REQUEST'): + elif line.startswith('JUST_WORKS_REQUEST'): print('%s just works requested' % node.get_name()) if node.get_name() == 'tester' and test_descriptor['tester_failure'] == '12': print('Decline bonding') @@ -189,7 +193,21 @@ def run(test_descriptor, nodes): else: print('Accept bonding') node.write('a') - if line.startswith('PAIRING_COMPLETE'): + elif line.startswith('PASSKEY_DISPLAY_NUMBER'): + passkey = line.split(': ')[1] + print('%s passkey display %s' % (node.get_name(), passkey)) + test_descriptor['passkey'] = passkey + if state == 'W4_PAIRING': + state = 'W4_PASSKEY_INPUT' + else: + test_descriptor['waiting_node'].write(test_descriptor['passkey']) + elif line.startswith('PASSKEY_INPUT_NUMBER'): + if state == 'W4_PASSKEY_INPUT': + node.write(test_descriptor['passkey']) + else: + test_descriptor['waiting_node'] = node + state = 'W4_PASSKEY_DISPLAY' + elif line.startswith('PAIRING_COMPLETE'): result = line.split(': ')[1] (status,reason) = result.split(',') test_descriptor[node.get_name()+'_pairing_complete_status'] = status @@ -201,7 +219,7 @@ def run(test_descriptor, nodes): # on error, test is finished, else wait for notify if status != '0': return - if line.startswith('COUNTER'): + elif line.startswith('COUNTER'): print('%s notification received' % node.get_name()) return; @@ -215,8 +233,10 @@ def write_config(fout, test_descriptor): 'io_capabilities', 'mitm', 'secure_connection', + 'keypress', 'rfu', 'oob_data', + 'passkey', 'pairing_complete_status', 'pairing_complete_reason'] @@ -246,9 +266,17 @@ def write_config(fout, test_descriptor): elif attribute == 'secure_connection': iut = (int(test_descriptor['iut_auth_req' ]) & SM_AUTHREQ_SECURE_CONNECTION) >> 3 tester = (int(test_descriptor['tester_auth_req']) & SM_AUTHREQ_SECURE_CONNECTION) >> 3 + elif attribute == 'keypress': + iut = (int(test_descriptor['iut_auth_req' ]) & SM_AUTHREQ_KEYPRESS) >> 4 + tester = (int(test_descriptor['tester_auth_req']) & SM_AUTHREQ_KEYPRESS) >> 4 elif attribute == 'rfu': iut = (int(test_descriptor['iut_auth_req' ]) & 192) >> 6 tester = (int(test_descriptor['tester_auth_req']) & 192) >> 6 + elif attribute == 'passkey': + if not 'passkey' in test_descriptor: + continue + iut = test_descriptor['passkey'] + tester = test_descriptor['passkey'] elif attribute == 'failure': iut = '' tester = failures[int(test_descriptor['tester_failure'])]