From ad912a6c9e5942fbb6ca1b7a80d131d25f7bd89d Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Tue, 27 Mar 2018 14:36:28 +0200 Subject: [PATCH] pts/sm_test: allow simulation of pairing failures --- test/pts/.gitignore | 1 + test/pts/Makefile | 1 + test/pts/sm_test.c | 16 +++++++++- test/pts/sm_test.csv | 66 +++++++++++++++++++++++++++++++++++++-- test/pts/sm_test.py | 74 +++++++++++++++++++++++++++++++++++++------- 5 files changed, 143 insertions(+), 15 deletions(-) diff --git a/test/pts/.gitignore b/test/pts/.gitignore index b1acffd99..83556df9c 100644 --- a/test/pts/.gitignore +++ b/test/pts/.gitignore @@ -23,3 +23,4 @@ sco_loopback sco_loopbackiopt sm_test.h sm_test +SM_* diff --git a/test/pts/Makefile b/test/pts/Makefile index befeabdc6..d0aece628 100644 --- a/test/pts/Makefile +++ b/test/pts/Makefile @@ -91,6 +91,7 @@ CFLAGS += -g -Wall -Wmissing-prototypes -Wstrict-prototypes -Wshadow -Wunused-va CFLAGS += -D ENABLE_LE_DATA_CHANNELS CFLAGS += -D ENABLE_LE_SECURE_CONNECTIONS CFLAGS += -D ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS +CFLAGS += -D ENABLE_TESTING_SUPPORT CFLAGS += -I.. CFLAGS += -I${BTSTACK_ROOT}/src CFLAGS += -I${BTSTACK_ROOT}/src/classic diff --git a/test/pts/sm_test.c b/test/pts/sm_test.c index 3dc89fbe1..03b9cbd3f 100644 --- a/test/pts/sm_test.c +++ b/test/pts/sm_test.c @@ -79,7 +79,7 @@ const uint8_t adv_data_len = sizeof(adv_data); static int sm_have_oob_data = 0; static uint8_t sm_io_capabilities = 0; static uint8_t sm_auth_req = 0; - +static uint8_t sm_failure = 0; // static uint8_t * sm_oob_data = (uint8_t *) "0123456789012345"; // = { 0x30...0x39, 0x30..0x35} static uint8_t sm_oob_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; @@ -294,6 +294,7 @@ static void app_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t * case SM_EVENT_PAIRING_COMPLETE: if (!wait_for_pairing_complete) break; printf("PAIRING_COMPLETE: %u,%u\n", sm_event_pairing_complete_get_status(packet), sm_event_pairing_complete_get_reason(packet)); + if (sm_event_pairing_complete_get_status(packet)) break; if (we_are_central){ printf("Search for LE Counter service.\n"); state = TC_W4_SERVICE_RESULT; @@ -333,6 +334,10 @@ static void stdin_process(char c){ printf("accepting just works\n"); sm_just_works_confirm(connection_handle); break; + case 'd': + printf("decline bonding\n"); + sm_bonding_decline(connection_handle); + break; case 'x': printf("Exit\n"); exit(0); @@ -371,6 +376,10 @@ int btstack_main(int argc, const char * argv[]){ arg++; sm_auth_req = atoi(argv[arg++]); } + if(!strcmp(argv[arg], "-f") || !strcmp(argv[arg], "--failure")){ + arg++; + sm_failure = atoi(argv[arg++]); + } } // parse command line flags @@ -378,6 +387,7 @@ int btstack_main(int argc, const char * argv[]){ printf("Security Managet Tester starting up...\n"); log_info("IO_CAPABILITIES: %u", sm_io_capabilities); log_info("AUTH_REQ: %u", sm_auth_req); + log_info("FAILURE: %u", sm_failure); if (we_are_central){ log_info("ROLE: CENTRAL"); } else { @@ -413,6 +423,10 @@ 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){ + sm_test_set_pairing_failure(sm_failure); + } + sm_event_callback_registration.callback = &app_packet_handler; sm_add_event_handler(&sm_event_callback_registration); diff --git a/test/pts/sm_test.csv b/test/pts/sm_test.csv index 23cdecc40..010a3d090 100644 --- a/test/pts/sm_test.csv +++ b/test/pts/sm_test.csv @@ -1,2 +1,64 @@ -name,iut_io_capabilities,tester_io_capabilities,iut_auth_req,tester_auth_req,iut_oob_data,tester_oob_data, -SM/MAS/SCJW/BV-01-C,0,0,8,8,0,0 +name,iut_io_capabilities,tester_io_capabilities,iut_auth_req,tester_auth_req,iut_oob_data,tester_oob_data,tester_failure + +# -- Just Works (SCJW) -- + +SM/MAS/SCJW/BV-01-C/01,0,0,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/02,1,0,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/03,3,0,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/04,0,1,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/05,1,1,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/06,3,1,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/07,4,1,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/08,3,2,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/09,0,3,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/10,1,3,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/11,2,3,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/12,3,3,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/13,4,3,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/14,1,4,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/15,3,4,8,8,0,0,0 +SM/MAS/SCJW/BV-01-C/16,4,4,8,8,0,0,0 + +# same as SM/MAS/SCJW/BV-01, just with IUT in Responder role + +SM/SLA/SCJW/BV-02-C/01,0,0,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/02,1,0,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/03,3,0,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/04,0,1,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/05,1,1,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/06,3,1,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/07,4,1,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/08,3,2,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/09,0,3,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/10,1,3,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/11,2,3,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/12,3,3,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/13,4,3,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/14,1,4,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/15,3,4,8,8,0,0,0 +SM/SLA/SCJW/BV-02-C/16,4,4,8,8,0,0,0 + +# additional bits set in authreq + +SM/SLA/SCJW/BV-03-C/01,3,3,8,72,0,0,0 +SM/SLA/SCJW/BV-03-C/02,3,3,8,136,0,0,0 +SM/SLA/SCJW/BV-03-C/03,3,3,8,200,0,0,0 + +# same as SM/SLA/SCJW/BV-03-C, just with IUT in Initiator role + +SM/MAS/SCJW/BV-04-C/01,3,3,8,72,0,0,0 +SM/MAS/SCJW/BV-04-C/02,3,3,8,136,0,0,0 +SM/MAS/SCJW/BV-04-C/03,3,3,8,200,0,0,0 + +# tester responds with pairing failure in responder role + +SM/MAS/SCJW/BI-01-C/01,0,0,8,8,0,0,3 +SM/MAS/SCJW/BI-01-C/02,0,0,8,8,0,0,8 +SM/MAS/SCJW/BI-01-C/03,0,0,8,8,0,0,5 +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 +SM/SLA/SCJW/BI-02-C,0,3,8,8,0,0,4 diff --git a/test/pts/sm_test.py b/test/pts/sm_test.py index a6f753f1e..efa3f88ac 100755 --- a/test/pts/sm_test.py +++ b/test/pts/sm_test.py @@ -29,8 +29,25 @@ SM_AUTHREQ_MITM_PROTECTION = 0x04 SM_AUTHREQ_SECURE_CONNECTION = 0x08 SM_AUTHREQ_KEYPRESS = 0x10 +failures = [ +'RESERVED', +'PASSKEY_ENTRY_FAILED', +'OOB_NOT_AVAILABLE', +'AUTHENTHICATION_REQUIREMENTS', +'CONFIRM_VALUE_FAILED', +'PAIRING_NOT_SUPPORTED', +'ENCRYPTION_KEY_SIZE', +'COMMAND_NOT_SUPPORTED', +'UNSPECIFIED_REASON', +'REPEATED_ATTEMPTS', +'INVALID_PARAMETERS', +'DHKEY_CHECK_FAILED', +'NUMERIC_COMPARISON_FAILED', +] + # tester config -regenerate = True +debug = True +regenerate = False # usb_paths = ['4', '6'] usb_paths = ['3', '5'] @@ -40,6 +57,7 @@ class Node: self.name = 'node' self._got_line = False self.peer_addr = None + self.failure = None def get_name(self): return self.name @@ -56,6 +74,9 @@ class Node: def set_oob_data(self, oob_data): self.oob_data = oob_data + def set_failure(self, failure): + self.failure = failure + def set_usb_path(self, path): self.usb_path = path @@ -86,6 +107,9 @@ class Node: if self.peer_addr != None: args.append('-a') args.append(self.peer_addr) + if self.failure != None: + args.append('-f') + args.append(self.failure) args.append('-r') args.append(self.auth_req) print('%s - "%s"' % (self.name, ' '.join(args))) @@ -128,7 +152,8 @@ def run(test_descriptor, nodes): node.read_stdout() if node.got_line(): line = node.fetch_line() - # print('%s: %s' % (node.get_name(), line)) + if debug: + print('%s: %s' % (node.get_name(), line)) if line.startswith('Packet Log: '): path = line.split(': ')[1] node.set_packet_log(path) @@ -148,6 +173,8 @@ def run(test_descriptor, nodes): master.set_peer_addr(addr) master.set_auth_req(test_descriptor[master_role + '_auth_req']) master.set_io_capabilities(test_descriptor[master_role + '_io_capabilities']) + if master_role == 'tester': + master.set_failure(test_descriptor['tester_failure']) master.start_process() nodes.append(master) if line.startswith('CONNECTED:'): @@ -156,7 +183,12 @@ def run(test_descriptor, nodes): state = 'W4_PAIRING' if line.startswith('JUST_WORKS_REQUEST'): print('%s just works requested' % node.get_name()) - node.write('a') + if node.get_name() == 'tester' and test_descriptor['tester_failure'] == '12': + print('Decline bonding') + node.write('d') + else: + print('Accept bonding') + node.write('a') if line.startswith('PAIRING_COMPLETE'): result = line.split(': ')[1] (status,reason) = result.split(',') @@ -179,9 +211,11 @@ def write_config(fout, test_descriptor): '---', 'bd_addr', 'role', + 'failure', 'io_capabilities', 'mitm', 'secure_connection', + 'rfu', 'oob_data', 'pairing_complete_status', 'pairing_complete_reason'] @@ -190,8 +224,8 @@ def write_config(fout, test_descriptor): fout.write('Test: %s\n' % test_descriptor['name']) fout.write('Date: %s\n' % str(datetime.datetime.now())) fout.write('\n') - attribute_len = 25 - value_len = 30 + attribute_len = 28 + value_len = 35 format_string = '%%-%us|%%-%us|%%-%us\n' % (attribute_len, value_len, value_len) for attribute in attributes: name = attribute @@ -212,6 +246,12 @@ 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 == 'rfu': + iut = (int(test_descriptor['iut_auth_req' ]) & 192) >> 6 + tester = (int(test_descriptor['tester_auth_req']) & 192) >> 6 + elif attribute == 'failure': + iut = '' + tester = failures[int(test_descriptor['tester_failure'])] else: iut = test_descriptor['iut_' + attribute] tester = test_descriptor['tester_' + attribute] @@ -235,16 +275,18 @@ def run_test(test_descriptor): if '/SLA/' in test_descriptor['name']: iut_role = 'responder' - tester_role = 'master' + tester_role = 'initiator' slave_role = 'iut' - test_descriptor['master_role'] = 'tester' + master_role = 'tester' else: iut_role = 'initiator' - tester_role = "slave" + tester_role = 'responder' slave_role = 'tester' - test_descriptor['master_role'] = 'iut' + master_role = 'iut' test_descriptor['iut_role' ] = iut_role test_descriptor['tester_role'] = tester_role + test_descriptor['master_role'] = master_role + test_descriptor['slave_role'] = slave_role slave = Node() @@ -253,6 +295,8 @@ def run_test(test_descriptor): slave.usb_path = usb_paths[0] slave.set_auth_req(test_descriptor[slave_role + '_auth_req']) slave.set_io_capabilities(test_descriptor[slave_role + '_io_capabilities']) + if slave_role == 'tester': + slave.set_failure(test_descriptor['tester_failure']) # start up slave slave.start_process() @@ -264,7 +308,7 @@ def run_test(test_descriptor): run(test_descriptor, nodes) # identify iut and tester - if iut_role == 'slave': + if iut_role == 'responder': iut = nodes[0] tester = nodes[1] else: @@ -274,12 +318,12 @@ def run_test(test_descriptor): # move hci logs into result folder test_folder = test_descriptor['test_folder'] os.makedirs(test_folder) - shutil.move(iut.get_packet_log(), test_folder + '/iut.pklg') + shutil.move(iut.get_packet_log(), test_folder + '/iut.pklg') shutil.move(tester.get_packet_log(), test_folder + '/tester.pklg') # write config with open (test_folder + '/config.txt', "wt") as fout: - test_descriptor['iut_bd_addr'] = iut.get_bd_addr() + test_descriptor['iut_bd_addr'] = iut.get_bd_addr() test_descriptor['tester_bd_addr'] = tester.get_bd_addr() write_config(fout, test_descriptor) @@ -297,6 +341,12 @@ with open('sm_test.csv') as csvfile: reader = csv.DictReader(csvfile) for test_descriptor in reader: test_name = test_descriptor['name'] + + if test_name.startswith('#'): + continue + if len(test_name) == 0: + continue + test_folder = test_name.replace('/', '_') test_descriptor['test_folder'] = test_folder