1e5b75505Sopenharmony_ci#!/usr/bin/python 2e5b75505Sopenharmony_ci# 3e5b75505Sopenharmony_ci# Example nfcpy to hostapd wrapper for WPS NFC operations 4e5b75505Sopenharmony_ci# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> 5e5b75505Sopenharmony_ci# 6e5b75505Sopenharmony_ci# This software may be distributed under the terms of the BSD license. 7e5b75505Sopenharmony_ci# See README for more details. 8e5b75505Sopenharmony_ci 9e5b75505Sopenharmony_ciimport os 10e5b75505Sopenharmony_ciimport sys 11e5b75505Sopenharmony_ciimport time 12e5b75505Sopenharmony_ciimport argparse 13e5b75505Sopenharmony_ci 14e5b75505Sopenharmony_ciimport nfc 15e5b75505Sopenharmony_ciimport nfc.ndef 16e5b75505Sopenharmony_ciimport nfc.llcp 17e5b75505Sopenharmony_ciimport nfc.handover 18e5b75505Sopenharmony_ci 19e5b75505Sopenharmony_ciimport logging 20e5b75505Sopenharmony_ci 21e5b75505Sopenharmony_ciimport wpaspy 22e5b75505Sopenharmony_ci 23e5b75505Sopenharmony_ciwpas_ctrl = '/var/run/hostapd' 24e5b75505Sopenharmony_cicontinue_loop = True 25e5b75505Sopenharmony_cisummary_file = None 26e5b75505Sopenharmony_cisuccess_file = None 27e5b75505Sopenharmony_ci 28e5b75505Sopenharmony_cidef summary(txt): 29e5b75505Sopenharmony_ci print(txt) 30e5b75505Sopenharmony_ci if summary_file: 31e5b75505Sopenharmony_ci with open(summary_file, 'a') as f: 32e5b75505Sopenharmony_ci f.write(txt + "\n") 33e5b75505Sopenharmony_ci 34e5b75505Sopenharmony_cidef success_report(txt): 35e5b75505Sopenharmony_ci summary(txt) 36e5b75505Sopenharmony_ci if success_file: 37e5b75505Sopenharmony_ci with open(success_file, 'a') as f: 38e5b75505Sopenharmony_ci f.write(txt + "\n") 39e5b75505Sopenharmony_ci 40e5b75505Sopenharmony_cidef wpas_connect(): 41e5b75505Sopenharmony_ci ifaces = [] 42e5b75505Sopenharmony_ci if os.path.isdir(wpas_ctrl): 43e5b75505Sopenharmony_ci try: 44e5b75505Sopenharmony_ci ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] 45e5b75505Sopenharmony_ci except OSError as error: 46e5b75505Sopenharmony_ci print("Could not find hostapd: ", error) 47e5b75505Sopenharmony_ci return None 48e5b75505Sopenharmony_ci 49e5b75505Sopenharmony_ci if len(ifaces) < 1: 50e5b75505Sopenharmony_ci print("No hostapd control interface found") 51e5b75505Sopenharmony_ci return None 52e5b75505Sopenharmony_ci 53e5b75505Sopenharmony_ci for ctrl in ifaces: 54e5b75505Sopenharmony_ci try: 55e5b75505Sopenharmony_ci wpas = wpaspy.Ctrl(ctrl) 56e5b75505Sopenharmony_ci return wpas 57e5b75505Sopenharmony_ci except Exception as e: 58e5b75505Sopenharmony_ci pass 59e5b75505Sopenharmony_ci return None 60e5b75505Sopenharmony_ci 61e5b75505Sopenharmony_ci 62e5b75505Sopenharmony_cidef wpas_tag_read(message): 63e5b75505Sopenharmony_ci wpas = wpas_connect() 64e5b75505Sopenharmony_ci if (wpas == None): 65e5b75505Sopenharmony_ci return False 66e5b75505Sopenharmony_ci if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): 67e5b75505Sopenharmony_ci return False 68e5b75505Sopenharmony_ci return True 69e5b75505Sopenharmony_ci 70e5b75505Sopenharmony_ci 71e5b75505Sopenharmony_cidef wpas_get_config_token(): 72e5b75505Sopenharmony_ci wpas = wpas_connect() 73e5b75505Sopenharmony_ci if (wpas == None): 74e5b75505Sopenharmony_ci return None 75e5b75505Sopenharmony_ci ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF") 76e5b75505Sopenharmony_ci if "FAIL" in ret: 77e5b75505Sopenharmony_ci return None 78e5b75505Sopenharmony_ci return ret.rstrip().decode("hex") 79e5b75505Sopenharmony_ci 80e5b75505Sopenharmony_ci 81e5b75505Sopenharmony_cidef wpas_get_password_token(): 82e5b75505Sopenharmony_ci wpas = wpas_connect() 83e5b75505Sopenharmony_ci if (wpas == None): 84e5b75505Sopenharmony_ci return None 85e5b75505Sopenharmony_ci ret = wpas.request("WPS_NFC_TOKEN NDEF") 86e5b75505Sopenharmony_ci if "FAIL" in ret: 87e5b75505Sopenharmony_ci return None 88e5b75505Sopenharmony_ci return ret.rstrip().decode("hex") 89e5b75505Sopenharmony_ci 90e5b75505Sopenharmony_ci 91e5b75505Sopenharmony_cidef wpas_get_handover_sel(): 92e5b75505Sopenharmony_ci wpas = wpas_connect() 93e5b75505Sopenharmony_ci if (wpas == None): 94e5b75505Sopenharmony_ci return None 95e5b75505Sopenharmony_ci ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR") 96e5b75505Sopenharmony_ci if "FAIL" in ret: 97e5b75505Sopenharmony_ci return None 98e5b75505Sopenharmony_ci return ret.rstrip().decode("hex") 99e5b75505Sopenharmony_ci 100e5b75505Sopenharmony_ci 101e5b75505Sopenharmony_cidef wpas_report_handover(req, sel): 102e5b75505Sopenharmony_ci wpas = wpas_connect() 103e5b75505Sopenharmony_ci if (wpas == None): 104e5b75505Sopenharmony_ci return None 105e5b75505Sopenharmony_ci return wpas.request("NFC_REPORT_HANDOVER RESP WPS " + 106e5b75505Sopenharmony_ci str(req).encode("hex") + " " + 107e5b75505Sopenharmony_ci str(sel).encode("hex")) 108e5b75505Sopenharmony_ci 109e5b75505Sopenharmony_ci 110e5b75505Sopenharmony_ciclass HandoverServer(nfc.handover.HandoverServer): 111e5b75505Sopenharmony_ci def __init__(self, llc): 112e5b75505Sopenharmony_ci super(HandoverServer, self).__init__(llc) 113e5b75505Sopenharmony_ci self.ho_server_processing = False 114e5b75505Sopenharmony_ci self.success = False 115e5b75505Sopenharmony_ci 116e5b75505Sopenharmony_ci # override to avoid parser error in request/response.pretty() in nfcpy 117e5b75505Sopenharmony_ci # due to new WSC handover format 118e5b75505Sopenharmony_ci def _process_request(self, request): 119e5b75505Sopenharmony_ci summary("received handover request {}".format(request.type)) 120e5b75505Sopenharmony_ci response = nfc.ndef.Message("\xd1\x02\x01Hs\x12") 121e5b75505Sopenharmony_ci if not request.type == 'urn:nfc:wkt:Hr': 122e5b75505Sopenharmony_ci summary("not a handover request") 123e5b75505Sopenharmony_ci else: 124e5b75505Sopenharmony_ci try: 125e5b75505Sopenharmony_ci request = nfc.ndef.HandoverRequestMessage(request) 126e5b75505Sopenharmony_ci except nfc.ndef.DecodeError as e: 127e5b75505Sopenharmony_ci summary("error decoding 'Hr' message: {}".format(e)) 128e5b75505Sopenharmony_ci else: 129e5b75505Sopenharmony_ci response = self.process_request(request) 130e5b75505Sopenharmony_ci summary("send handover response {}".format(response.type)) 131e5b75505Sopenharmony_ci return response 132e5b75505Sopenharmony_ci 133e5b75505Sopenharmony_ci def process_request(self, request): 134e5b75505Sopenharmony_ci summary("HandoverServer - request received") 135e5b75505Sopenharmony_ci try: 136e5b75505Sopenharmony_ci print("Parsed handover request: " + request.pretty()) 137e5b75505Sopenharmony_ci except Exception as e: 138e5b75505Sopenharmony_ci print(e) 139e5b75505Sopenharmony_ci print(str(request).encode("hex")) 140e5b75505Sopenharmony_ci 141e5b75505Sopenharmony_ci sel = nfc.ndef.HandoverSelectMessage(version="1.2") 142e5b75505Sopenharmony_ci 143e5b75505Sopenharmony_ci for carrier in request.carriers: 144e5b75505Sopenharmony_ci print("Remote carrier type: " + carrier.type) 145e5b75505Sopenharmony_ci if carrier.type == "application/vnd.wfa.wsc": 146e5b75505Sopenharmony_ci summary("WPS carrier type match - add WPS carrier record") 147e5b75505Sopenharmony_ci data = wpas_get_handover_sel() 148e5b75505Sopenharmony_ci if data is None: 149e5b75505Sopenharmony_ci summary("Could not get handover select carrier record from hostapd") 150e5b75505Sopenharmony_ci continue 151e5b75505Sopenharmony_ci print("Handover select carrier record from hostapd:") 152e5b75505Sopenharmony_ci print(data.encode("hex")) 153e5b75505Sopenharmony_ci if "OK" in wpas_report_handover(carrier.record, data): 154e5b75505Sopenharmony_ci success_report("Handover reported successfully") 155e5b75505Sopenharmony_ci else: 156e5b75505Sopenharmony_ci summary("Handover report rejected") 157e5b75505Sopenharmony_ci 158e5b75505Sopenharmony_ci message = nfc.ndef.Message(data); 159e5b75505Sopenharmony_ci sel.add_carrier(message[0], "active", message[1:]) 160e5b75505Sopenharmony_ci 161e5b75505Sopenharmony_ci print("Handover select:") 162e5b75505Sopenharmony_ci try: 163e5b75505Sopenharmony_ci print(sel.pretty()) 164e5b75505Sopenharmony_ci except Exception as e: 165e5b75505Sopenharmony_ci print(e) 166e5b75505Sopenharmony_ci print(str(sel).encode("hex")) 167e5b75505Sopenharmony_ci 168e5b75505Sopenharmony_ci summary("Sending handover select") 169e5b75505Sopenharmony_ci self.success = True 170e5b75505Sopenharmony_ci return sel 171e5b75505Sopenharmony_ci 172e5b75505Sopenharmony_ci 173e5b75505Sopenharmony_cidef wps_tag_read(tag): 174e5b75505Sopenharmony_ci success = False 175e5b75505Sopenharmony_ci if len(tag.ndef.message): 176e5b75505Sopenharmony_ci for record in tag.ndef.message: 177e5b75505Sopenharmony_ci print("record type " + record.type) 178e5b75505Sopenharmony_ci if record.type == "application/vnd.wfa.wsc": 179e5b75505Sopenharmony_ci summary("WPS tag - send to hostapd") 180e5b75505Sopenharmony_ci success = wpas_tag_read(tag.ndef.message) 181e5b75505Sopenharmony_ci break 182e5b75505Sopenharmony_ci else: 183e5b75505Sopenharmony_ci summary("Empty tag") 184e5b75505Sopenharmony_ci 185e5b75505Sopenharmony_ci if success: 186e5b75505Sopenharmony_ci success_report("Tag read succeeded") 187e5b75505Sopenharmony_ci 188e5b75505Sopenharmony_ci return success 189e5b75505Sopenharmony_ci 190e5b75505Sopenharmony_ci 191e5b75505Sopenharmony_cidef rdwr_connected_write(tag): 192e5b75505Sopenharmony_ci summary("Tag found - writing - " + str(tag)) 193e5b75505Sopenharmony_ci global write_data 194e5b75505Sopenharmony_ci tag.ndef.message = str(write_data) 195e5b75505Sopenharmony_ci success_report("Tag write succeeded") 196e5b75505Sopenharmony_ci print("Done - remove tag") 197e5b75505Sopenharmony_ci global only_one 198e5b75505Sopenharmony_ci if only_one: 199e5b75505Sopenharmony_ci global continue_loop 200e5b75505Sopenharmony_ci continue_loop = False 201e5b75505Sopenharmony_ci global write_wait_remove 202e5b75505Sopenharmony_ci while write_wait_remove and tag.is_present: 203e5b75505Sopenharmony_ci time.sleep(0.1) 204e5b75505Sopenharmony_ci 205e5b75505Sopenharmony_cidef wps_write_config_tag(clf, wait_remove=True): 206e5b75505Sopenharmony_ci summary("Write WPS config token") 207e5b75505Sopenharmony_ci global write_data, write_wait_remove 208e5b75505Sopenharmony_ci write_wait_remove = wait_remove 209e5b75505Sopenharmony_ci write_data = wpas_get_config_token() 210e5b75505Sopenharmony_ci if write_data == None: 211e5b75505Sopenharmony_ci summary("Could not get WPS config token from hostapd") 212e5b75505Sopenharmony_ci return 213e5b75505Sopenharmony_ci 214e5b75505Sopenharmony_ci print("Touch an NFC tag") 215e5b75505Sopenharmony_ci clf.connect(rdwr={'on-connect': rdwr_connected_write}) 216e5b75505Sopenharmony_ci 217e5b75505Sopenharmony_ci 218e5b75505Sopenharmony_cidef wps_write_password_tag(clf, wait_remove=True): 219e5b75505Sopenharmony_ci summary("Write WPS password token") 220e5b75505Sopenharmony_ci global write_data, write_wait_remove 221e5b75505Sopenharmony_ci write_wait_remove = wait_remove 222e5b75505Sopenharmony_ci write_data = wpas_get_password_token() 223e5b75505Sopenharmony_ci if write_data == None: 224e5b75505Sopenharmony_ci summary("Could not get WPS password token from hostapd") 225e5b75505Sopenharmony_ci return 226e5b75505Sopenharmony_ci 227e5b75505Sopenharmony_ci print("Touch an NFC tag") 228e5b75505Sopenharmony_ci clf.connect(rdwr={'on-connect': rdwr_connected_write}) 229e5b75505Sopenharmony_ci 230e5b75505Sopenharmony_ci 231e5b75505Sopenharmony_cidef rdwr_connected(tag): 232e5b75505Sopenharmony_ci global only_one, no_wait 233e5b75505Sopenharmony_ci summary("Tag connected: " + str(tag)) 234e5b75505Sopenharmony_ci 235e5b75505Sopenharmony_ci if tag.ndef: 236e5b75505Sopenharmony_ci print("NDEF tag: " + tag.type) 237e5b75505Sopenharmony_ci try: 238e5b75505Sopenharmony_ci print(tag.ndef.message.pretty()) 239e5b75505Sopenharmony_ci except Exception as e: 240e5b75505Sopenharmony_ci print(e) 241e5b75505Sopenharmony_ci success = wps_tag_read(tag) 242e5b75505Sopenharmony_ci if only_one and success: 243e5b75505Sopenharmony_ci global continue_loop 244e5b75505Sopenharmony_ci continue_loop = False 245e5b75505Sopenharmony_ci else: 246e5b75505Sopenharmony_ci summary("Not an NDEF tag - remove tag") 247e5b75505Sopenharmony_ci return True 248e5b75505Sopenharmony_ci 249e5b75505Sopenharmony_ci return not no_wait 250e5b75505Sopenharmony_ci 251e5b75505Sopenharmony_ci 252e5b75505Sopenharmony_cidef llcp_startup(clf, llc): 253e5b75505Sopenharmony_ci print("Start LLCP server") 254e5b75505Sopenharmony_ci global srv 255e5b75505Sopenharmony_ci srv = HandoverServer(llc) 256e5b75505Sopenharmony_ci return llc 257e5b75505Sopenharmony_ci 258e5b75505Sopenharmony_cidef llcp_connected(llc): 259e5b75505Sopenharmony_ci print("P2P LLCP connected") 260e5b75505Sopenharmony_ci global wait_connection 261e5b75505Sopenharmony_ci wait_connection = False 262e5b75505Sopenharmony_ci global srv 263e5b75505Sopenharmony_ci srv.start() 264e5b75505Sopenharmony_ci return True 265e5b75505Sopenharmony_ci 266e5b75505Sopenharmony_ci 267e5b75505Sopenharmony_cidef main(): 268e5b75505Sopenharmony_ci clf = nfc.ContactlessFrontend() 269e5b75505Sopenharmony_ci 270e5b75505Sopenharmony_ci parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations') 271e5b75505Sopenharmony_ci parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, 272e5b75505Sopenharmony_ci action='store_const', dest='loglevel', 273e5b75505Sopenharmony_ci help='verbose debug output') 274e5b75505Sopenharmony_ci parser.add_argument('-q', const=logging.WARNING, action='store_const', 275e5b75505Sopenharmony_ci dest='loglevel', help='be quiet') 276e5b75505Sopenharmony_ci parser.add_argument('--only-one', '-1', action='store_true', 277e5b75505Sopenharmony_ci help='run only one operation and exit') 278e5b75505Sopenharmony_ci parser.add_argument('--no-wait', action='store_true', 279e5b75505Sopenharmony_ci help='do not wait for tag to be removed before exiting') 280e5b75505Sopenharmony_ci parser.add_argument('--summary', 281e5b75505Sopenharmony_ci help='summary file for writing status updates') 282e5b75505Sopenharmony_ci parser.add_argument('--success', 283e5b75505Sopenharmony_ci help='success file for writing success update') 284e5b75505Sopenharmony_ci parser.add_argument('command', choices=['write-config', 285e5b75505Sopenharmony_ci 'write-password'], 286e5b75505Sopenharmony_ci nargs='?') 287e5b75505Sopenharmony_ci args = parser.parse_args() 288e5b75505Sopenharmony_ci 289e5b75505Sopenharmony_ci global only_one 290e5b75505Sopenharmony_ci only_one = args.only_one 291e5b75505Sopenharmony_ci 292e5b75505Sopenharmony_ci global no_wait 293e5b75505Sopenharmony_ci no_wait = args.no_wait 294e5b75505Sopenharmony_ci 295e5b75505Sopenharmony_ci if args.summary: 296e5b75505Sopenharmony_ci global summary_file 297e5b75505Sopenharmony_ci summary_file = args.summary 298e5b75505Sopenharmony_ci 299e5b75505Sopenharmony_ci if args.success: 300e5b75505Sopenharmony_ci global success_file 301e5b75505Sopenharmony_ci success_file = args.success 302e5b75505Sopenharmony_ci 303e5b75505Sopenharmony_ci logging.basicConfig(level=args.loglevel) 304e5b75505Sopenharmony_ci 305e5b75505Sopenharmony_ci try: 306e5b75505Sopenharmony_ci if not clf.open("usb"): 307e5b75505Sopenharmony_ci print("Could not open connection with an NFC device") 308e5b75505Sopenharmony_ci raise SystemExit 309e5b75505Sopenharmony_ci 310e5b75505Sopenharmony_ci if args.command == "write-config": 311e5b75505Sopenharmony_ci wps_write_config_tag(clf, wait_remove=not args.no_wait) 312e5b75505Sopenharmony_ci raise SystemExit 313e5b75505Sopenharmony_ci 314e5b75505Sopenharmony_ci if args.command == "write-password": 315e5b75505Sopenharmony_ci wps_write_password_tag(clf, wait_remove=not args.no_wait) 316e5b75505Sopenharmony_ci raise SystemExit 317e5b75505Sopenharmony_ci 318e5b75505Sopenharmony_ci global continue_loop 319e5b75505Sopenharmony_ci while continue_loop: 320e5b75505Sopenharmony_ci print("Waiting for a tag or peer to be touched") 321e5b75505Sopenharmony_ci wait_connection = True 322e5b75505Sopenharmony_ci try: 323e5b75505Sopenharmony_ci if not clf.connect(rdwr={'on-connect': rdwr_connected}, 324e5b75505Sopenharmony_ci llcp={'on-startup': llcp_startup, 325e5b75505Sopenharmony_ci 'on-connect': llcp_connected}): 326e5b75505Sopenharmony_ci break 327e5b75505Sopenharmony_ci except Exception as e: 328e5b75505Sopenharmony_ci print("clf.connect failed") 329e5b75505Sopenharmony_ci 330e5b75505Sopenharmony_ci global srv 331e5b75505Sopenharmony_ci if only_one and srv and srv.success: 332e5b75505Sopenharmony_ci raise SystemExit 333e5b75505Sopenharmony_ci 334e5b75505Sopenharmony_ci except KeyboardInterrupt: 335e5b75505Sopenharmony_ci raise SystemExit 336e5b75505Sopenharmony_ci finally: 337e5b75505Sopenharmony_ci clf.close() 338e5b75505Sopenharmony_ci 339e5b75505Sopenharmony_ci raise SystemExit 340e5b75505Sopenharmony_ci 341e5b75505Sopenharmony_ciif __name__ == '__main__': 342e5b75505Sopenharmony_ci main() 343