15b9c547cSRui Paulo#!/usr/bin/python 25b9c547cSRui Paulo# 35b9c547cSRui Paulo# Example nfcpy to hostapd wrapper for WPS NFC operations 45b9c547cSRui Paulo# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> 55b9c547cSRui Paulo# 65b9c547cSRui Paulo# This software may be distributed under the terms of the BSD license. 75b9c547cSRui Paulo# See README for more details. 85b9c547cSRui Paulo 95b9c547cSRui Pauloimport os 105b9c547cSRui Pauloimport sys 115b9c547cSRui Pauloimport time 125b9c547cSRui Pauloimport argparse 135b9c547cSRui Paulo 145b9c547cSRui Pauloimport nfc 155b9c547cSRui Pauloimport nfc.ndef 165b9c547cSRui Pauloimport nfc.llcp 175b9c547cSRui Pauloimport nfc.handover 185b9c547cSRui Paulo 195b9c547cSRui Pauloimport logging 205b9c547cSRui Paulo 215b9c547cSRui Pauloimport wpaspy 225b9c547cSRui Paulo 235b9c547cSRui Paulowpas_ctrl = '/var/run/hostapd' 245b9c547cSRui Paulocontinue_loop = True 255b9c547cSRui Paulosummary_file = None 265b9c547cSRui Paulosuccess_file = None 275b9c547cSRui Paulo 285b9c547cSRui Paulodef summary(txt): 29*4bc52338SCy Schubert print(txt) 305b9c547cSRui Paulo if summary_file: 315b9c547cSRui Paulo with open(summary_file, 'a') as f: 325b9c547cSRui Paulo f.write(txt + "\n") 335b9c547cSRui Paulo 345b9c547cSRui Paulodef success_report(txt): 355b9c547cSRui Paulo summary(txt) 365b9c547cSRui Paulo if success_file: 375b9c547cSRui Paulo with open(success_file, 'a') as f: 385b9c547cSRui Paulo f.write(txt + "\n") 395b9c547cSRui Paulo 405b9c547cSRui Paulodef wpas_connect(): 415b9c547cSRui Paulo ifaces = [] 425b9c547cSRui Paulo if os.path.isdir(wpas_ctrl): 435b9c547cSRui Paulo try: 445b9c547cSRui Paulo ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] 45*4bc52338SCy Schubert except OSError as error: 46*4bc52338SCy Schubert print("Could not find hostapd: ", error) 475b9c547cSRui Paulo return None 485b9c547cSRui Paulo 495b9c547cSRui Paulo if len(ifaces) < 1: 50*4bc52338SCy Schubert print("No hostapd control interface found") 515b9c547cSRui Paulo return None 525b9c547cSRui Paulo 535b9c547cSRui Paulo for ctrl in ifaces: 545b9c547cSRui Paulo try: 555b9c547cSRui Paulo wpas = wpaspy.Ctrl(ctrl) 565b9c547cSRui Paulo return wpas 57*4bc52338SCy Schubert except Exception as e: 585b9c547cSRui Paulo pass 595b9c547cSRui Paulo return None 605b9c547cSRui Paulo 615b9c547cSRui Paulo 625b9c547cSRui Paulodef wpas_tag_read(message): 635b9c547cSRui Paulo wpas = wpas_connect() 645b9c547cSRui Paulo if (wpas == None): 655b9c547cSRui Paulo return False 665b9c547cSRui Paulo if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): 675b9c547cSRui Paulo return False 685b9c547cSRui Paulo return True 695b9c547cSRui Paulo 705b9c547cSRui Paulo 715b9c547cSRui Paulodef wpas_get_config_token(): 725b9c547cSRui Paulo wpas = wpas_connect() 735b9c547cSRui Paulo if (wpas == None): 745b9c547cSRui Paulo return None 755b9c547cSRui Paulo ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF") 765b9c547cSRui Paulo if "FAIL" in ret: 775b9c547cSRui Paulo return None 785b9c547cSRui Paulo return ret.rstrip().decode("hex") 795b9c547cSRui Paulo 805b9c547cSRui Paulo 815b9c547cSRui Paulodef wpas_get_password_token(): 825b9c547cSRui Paulo wpas = wpas_connect() 835b9c547cSRui Paulo if (wpas == None): 845b9c547cSRui Paulo return None 855b9c547cSRui Paulo ret = wpas.request("WPS_NFC_TOKEN NDEF") 865b9c547cSRui Paulo if "FAIL" in ret: 875b9c547cSRui Paulo return None 885b9c547cSRui Paulo return ret.rstrip().decode("hex") 895b9c547cSRui Paulo 905b9c547cSRui Paulo 915b9c547cSRui Paulodef wpas_get_handover_sel(): 925b9c547cSRui Paulo wpas = wpas_connect() 935b9c547cSRui Paulo if (wpas == None): 945b9c547cSRui Paulo return None 955b9c547cSRui Paulo ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR") 965b9c547cSRui Paulo if "FAIL" in ret: 975b9c547cSRui Paulo return None 985b9c547cSRui Paulo return ret.rstrip().decode("hex") 995b9c547cSRui Paulo 1005b9c547cSRui Paulo 1015b9c547cSRui Paulodef wpas_report_handover(req, sel): 1025b9c547cSRui Paulo wpas = wpas_connect() 1035b9c547cSRui Paulo if (wpas == None): 1045b9c547cSRui Paulo return None 1055b9c547cSRui Paulo return wpas.request("NFC_REPORT_HANDOVER RESP WPS " + 1065b9c547cSRui Paulo str(req).encode("hex") + " " + 1075b9c547cSRui Paulo str(sel).encode("hex")) 1085b9c547cSRui Paulo 1095b9c547cSRui Paulo 1105b9c547cSRui Pauloclass HandoverServer(nfc.handover.HandoverServer): 1115b9c547cSRui Paulo def __init__(self, llc): 1125b9c547cSRui Paulo super(HandoverServer, self).__init__(llc) 1135b9c547cSRui Paulo self.ho_server_processing = False 1145b9c547cSRui Paulo self.success = False 1155b9c547cSRui Paulo 1165b9c547cSRui Paulo # override to avoid parser error in request/response.pretty() in nfcpy 1175b9c547cSRui Paulo # due to new WSC handover format 1185b9c547cSRui Paulo def _process_request(self, request): 1195b9c547cSRui Paulo summary("received handover request {}".format(request.type)) 1205b9c547cSRui Paulo response = nfc.ndef.Message("\xd1\x02\x01Hs\x12") 1215b9c547cSRui Paulo if not request.type == 'urn:nfc:wkt:Hr': 1225b9c547cSRui Paulo summary("not a handover request") 1235b9c547cSRui Paulo else: 1245b9c547cSRui Paulo try: 1255b9c547cSRui Paulo request = nfc.ndef.HandoverRequestMessage(request) 1265b9c547cSRui Paulo except nfc.ndef.DecodeError as e: 1275b9c547cSRui Paulo summary("error decoding 'Hr' message: {}".format(e)) 1285b9c547cSRui Paulo else: 1295b9c547cSRui Paulo response = self.process_request(request) 1305b9c547cSRui Paulo summary("send handover response {}".format(response.type)) 1315b9c547cSRui Paulo return response 1325b9c547cSRui Paulo 1335b9c547cSRui Paulo def process_request(self, request): 1345b9c547cSRui Paulo summary("HandoverServer - request received") 1355b9c547cSRui Paulo try: 136*4bc52338SCy Schubert print("Parsed handover request: " + request.pretty()) 137*4bc52338SCy Schubert except Exception as e: 138*4bc52338SCy Schubert print(e) 139*4bc52338SCy Schubert print(str(request).encode("hex")) 1405b9c547cSRui Paulo 1415b9c547cSRui Paulo sel = nfc.ndef.HandoverSelectMessage(version="1.2") 1425b9c547cSRui Paulo 1435b9c547cSRui Paulo for carrier in request.carriers: 144*4bc52338SCy Schubert print("Remote carrier type: " + carrier.type) 1455b9c547cSRui Paulo if carrier.type == "application/vnd.wfa.wsc": 1465b9c547cSRui Paulo summary("WPS carrier type match - add WPS carrier record") 1475b9c547cSRui Paulo data = wpas_get_handover_sel() 1485b9c547cSRui Paulo if data is None: 1495b9c547cSRui Paulo summary("Could not get handover select carrier record from hostapd") 1505b9c547cSRui Paulo continue 151*4bc52338SCy Schubert print("Handover select carrier record from hostapd:") 152*4bc52338SCy Schubert print(data.encode("hex")) 1535b9c547cSRui Paulo if "OK" in wpas_report_handover(carrier.record, data): 1545b9c547cSRui Paulo success_report("Handover reported successfully") 1555b9c547cSRui Paulo else: 1565b9c547cSRui Paulo summary("Handover report rejected") 1575b9c547cSRui Paulo 1585b9c547cSRui Paulo message = nfc.ndef.Message(data); 1595b9c547cSRui Paulo sel.add_carrier(message[0], "active", message[1:]) 1605b9c547cSRui Paulo 161*4bc52338SCy Schubert print("Handover select:") 1625b9c547cSRui Paulo try: 163*4bc52338SCy Schubert print(sel.pretty()) 164*4bc52338SCy Schubert except Exception as e: 165*4bc52338SCy Schubert print(e) 166*4bc52338SCy Schubert print(str(sel).encode("hex")) 1675b9c547cSRui Paulo 1685b9c547cSRui Paulo summary("Sending handover select") 1695b9c547cSRui Paulo self.success = True 1705b9c547cSRui Paulo return sel 1715b9c547cSRui Paulo 1725b9c547cSRui Paulo 1735b9c547cSRui Paulodef wps_tag_read(tag): 1745b9c547cSRui Paulo success = False 1755b9c547cSRui Paulo if len(tag.ndef.message): 1765b9c547cSRui Paulo for record in tag.ndef.message: 177*4bc52338SCy Schubert print("record type " + record.type) 1785b9c547cSRui Paulo if record.type == "application/vnd.wfa.wsc": 1795b9c547cSRui Paulo summary("WPS tag - send to hostapd") 1805b9c547cSRui Paulo success = wpas_tag_read(tag.ndef.message) 1815b9c547cSRui Paulo break 1825b9c547cSRui Paulo else: 1835b9c547cSRui Paulo summary("Empty tag") 1845b9c547cSRui Paulo 1855b9c547cSRui Paulo if success: 1865b9c547cSRui Paulo success_report("Tag read succeeded") 1875b9c547cSRui Paulo 1885b9c547cSRui Paulo return success 1895b9c547cSRui Paulo 1905b9c547cSRui Paulo 1915b9c547cSRui Paulodef rdwr_connected_write(tag): 1925b9c547cSRui Paulo summary("Tag found - writing - " + str(tag)) 1935b9c547cSRui Paulo global write_data 1945b9c547cSRui Paulo tag.ndef.message = str(write_data) 1955b9c547cSRui Paulo success_report("Tag write succeeded") 196*4bc52338SCy Schubert print("Done - remove tag") 1975b9c547cSRui Paulo global only_one 1985b9c547cSRui Paulo if only_one: 1995b9c547cSRui Paulo global continue_loop 2005b9c547cSRui Paulo continue_loop = False 2015b9c547cSRui Paulo global write_wait_remove 2025b9c547cSRui Paulo while write_wait_remove and tag.is_present: 2035b9c547cSRui Paulo time.sleep(0.1) 2045b9c547cSRui Paulo 2055b9c547cSRui Paulodef wps_write_config_tag(clf, wait_remove=True): 2065b9c547cSRui Paulo summary("Write WPS config token") 2075b9c547cSRui Paulo global write_data, write_wait_remove 2085b9c547cSRui Paulo write_wait_remove = wait_remove 2095b9c547cSRui Paulo write_data = wpas_get_config_token() 2105b9c547cSRui Paulo if write_data == None: 2115b9c547cSRui Paulo summary("Could not get WPS config token from hostapd") 2125b9c547cSRui Paulo return 2135b9c547cSRui Paulo 214*4bc52338SCy Schubert print("Touch an NFC tag") 2155b9c547cSRui Paulo clf.connect(rdwr={'on-connect': rdwr_connected_write}) 2165b9c547cSRui Paulo 2175b9c547cSRui Paulo 2185b9c547cSRui Paulodef wps_write_password_tag(clf, wait_remove=True): 2195b9c547cSRui Paulo summary("Write WPS password token") 2205b9c547cSRui Paulo global write_data, write_wait_remove 2215b9c547cSRui Paulo write_wait_remove = wait_remove 2225b9c547cSRui Paulo write_data = wpas_get_password_token() 2235b9c547cSRui Paulo if write_data == None: 2245b9c547cSRui Paulo summary("Could not get WPS password token from hostapd") 2255b9c547cSRui Paulo return 2265b9c547cSRui Paulo 227*4bc52338SCy Schubert print("Touch an NFC tag") 2285b9c547cSRui Paulo clf.connect(rdwr={'on-connect': rdwr_connected_write}) 2295b9c547cSRui Paulo 2305b9c547cSRui Paulo 2315b9c547cSRui Paulodef rdwr_connected(tag): 2325b9c547cSRui Paulo global only_one, no_wait 2335b9c547cSRui Paulo summary("Tag connected: " + str(tag)) 2345b9c547cSRui Paulo 2355b9c547cSRui Paulo if tag.ndef: 236*4bc52338SCy Schubert print("NDEF tag: " + tag.type) 2375b9c547cSRui Paulo try: 238*4bc52338SCy Schubert print(tag.ndef.message.pretty()) 239*4bc52338SCy Schubert except Exception as e: 240*4bc52338SCy Schubert print(e) 2415b9c547cSRui Paulo success = wps_tag_read(tag) 2425b9c547cSRui Paulo if only_one and success: 2435b9c547cSRui Paulo global continue_loop 2445b9c547cSRui Paulo continue_loop = False 2455b9c547cSRui Paulo else: 2465b9c547cSRui Paulo summary("Not an NDEF tag - remove tag") 2475b9c547cSRui Paulo return True 2485b9c547cSRui Paulo 2495b9c547cSRui Paulo return not no_wait 2505b9c547cSRui Paulo 2515b9c547cSRui Paulo 2525b9c547cSRui Paulodef llcp_startup(clf, llc): 253*4bc52338SCy Schubert print("Start LLCP server") 2545b9c547cSRui Paulo global srv 2555b9c547cSRui Paulo srv = HandoverServer(llc) 2565b9c547cSRui Paulo return llc 2575b9c547cSRui Paulo 2585b9c547cSRui Paulodef llcp_connected(llc): 259*4bc52338SCy Schubert print("P2P LLCP connected") 2605b9c547cSRui Paulo global wait_connection 2615b9c547cSRui Paulo wait_connection = False 2625b9c547cSRui Paulo global srv 2635b9c547cSRui Paulo srv.start() 2645b9c547cSRui Paulo return True 2655b9c547cSRui Paulo 2665b9c547cSRui Paulo 2675b9c547cSRui Paulodef main(): 2685b9c547cSRui Paulo clf = nfc.ContactlessFrontend() 2695b9c547cSRui Paulo 2705b9c547cSRui Paulo parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations') 2715b9c547cSRui Paulo parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, 2725b9c547cSRui Paulo action='store_const', dest='loglevel', 2735b9c547cSRui Paulo help='verbose debug output') 2745b9c547cSRui Paulo parser.add_argument('-q', const=logging.WARNING, action='store_const', 2755b9c547cSRui Paulo dest='loglevel', help='be quiet') 2765b9c547cSRui Paulo parser.add_argument('--only-one', '-1', action='store_true', 2775b9c547cSRui Paulo help='run only one operation and exit') 2785b9c547cSRui Paulo parser.add_argument('--no-wait', action='store_true', 2795b9c547cSRui Paulo help='do not wait for tag to be removed before exiting') 2805b9c547cSRui Paulo parser.add_argument('--summary', 2815b9c547cSRui Paulo help='summary file for writing status updates') 2825b9c547cSRui Paulo parser.add_argument('--success', 2835b9c547cSRui Paulo help='success file for writing success update') 2845b9c547cSRui Paulo parser.add_argument('command', choices=['write-config', 2855b9c547cSRui Paulo 'write-password'], 2865b9c547cSRui Paulo nargs='?') 2875b9c547cSRui Paulo args = parser.parse_args() 2885b9c547cSRui Paulo 2895b9c547cSRui Paulo global only_one 2905b9c547cSRui Paulo only_one = args.only_one 2915b9c547cSRui Paulo 2925b9c547cSRui Paulo global no_wait 2935b9c547cSRui Paulo no_wait = args.no_wait 2945b9c547cSRui Paulo 2955b9c547cSRui Paulo if args.summary: 2965b9c547cSRui Paulo global summary_file 2975b9c547cSRui Paulo summary_file = args.summary 2985b9c547cSRui Paulo 2995b9c547cSRui Paulo if args.success: 3005b9c547cSRui Paulo global success_file 3015b9c547cSRui Paulo success_file = args.success 3025b9c547cSRui Paulo 3035b9c547cSRui Paulo logging.basicConfig(level=args.loglevel) 3045b9c547cSRui Paulo 3055b9c547cSRui Paulo try: 3065b9c547cSRui Paulo if not clf.open("usb"): 307*4bc52338SCy Schubert print("Could not open connection with an NFC device") 3085b9c547cSRui Paulo raise SystemExit 3095b9c547cSRui Paulo 3105b9c547cSRui Paulo if args.command == "write-config": 3115b9c547cSRui Paulo wps_write_config_tag(clf, wait_remove=not args.no_wait) 3125b9c547cSRui Paulo raise SystemExit 3135b9c547cSRui Paulo 3145b9c547cSRui Paulo if args.command == "write-password": 3155b9c547cSRui Paulo wps_write_password_tag(clf, wait_remove=not args.no_wait) 3165b9c547cSRui Paulo raise SystemExit 3175b9c547cSRui Paulo 3185b9c547cSRui Paulo global continue_loop 3195b9c547cSRui Paulo while continue_loop: 320*4bc52338SCy Schubert print("Waiting for a tag or peer to be touched") 3215b9c547cSRui Paulo wait_connection = True 3225b9c547cSRui Paulo try: 3235b9c547cSRui Paulo if not clf.connect(rdwr={'on-connect': rdwr_connected}, 3245b9c547cSRui Paulo llcp={'on-startup': llcp_startup, 3255b9c547cSRui Paulo 'on-connect': llcp_connected}): 3265b9c547cSRui Paulo break 327*4bc52338SCy Schubert except Exception as e: 328*4bc52338SCy Schubert print("clf.connect failed") 3295b9c547cSRui Paulo 3305b9c547cSRui Paulo global srv 3315b9c547cSRui Paulo if only_one and srv and srv.success: 3325b9c547cSRui Paulo raise SystemExit 3335b9c547cSRui Paulo 3345b9c547cSRui Paulo except KeyboardInterrupt: 3355b9c547cSRui Paulo raise SystemExit 3365b9c547cSRui Paulo finally: 3375b9c547cSRui Paulo clf.close() 3385b9c547cSRui Paulo 3395b9c547cSRui Paulo raise SystemExit 3405b9c547cSRui Paulo 3415b9c547cSRui Pauloif __name__ == '__main__': 3425b9c547cSRui Paulo main() 343