1281681Srpaulo#!/usr/bin/python 2281681Srpaulo# 3281681Srpaulo# Example nfcpy to hostapd wrapper for WPS NFC operations 4281681Srpaulo# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> 5281681Srpaulo# 6281681Srpaulo# This software may be distributed under the terms of the BSD license. 7281681Srpaulo# See README for more details. 8281681Srpaulo 9281681Srpauloimport os 10281681Srpauloimport sys 11281681Srpauloimport time 12281681Srpauloimport argparse 13281681Srpaulo 14281681Srpauloimport nfc 15281681Srpauloimport nfc.ndef 16281681Srpauloimport nfc.llcp 17281681Srpauloimport nfc.handover 18281681Srpaulo 19281681Srpauloimport logging 20281681Srpaulo 21281681Srpauloimport wpaspy 22281681Srpaulo 23281681Srpaulowpas_ctrl = '/var/run/hostapd' 24281681Srpaulocontinue_loop = True 25281681Srpaulosummary_file = None 26281681Srpaulosuccess_file = None 27281681Srpaulo 28281681Srpaulodef summary(txt): 29281681Srpaulo print txt 30281681Srpaulo if summary_file: 31281681Srpaulo with open(summary_file, 'a') as f: 32281681Srpaulo f.write(txt + "\n") 33281681Srpaulo 34281681Srpaulodef success_report(txt): 35281681Srpaulo summary(txt) 36281681Srpaulo if success_file: 37281681Srpaulo with open(success_file, 'a') as f: 38281681Srpaulo f.write(txt + "\n") 39281681Srpaulo 40281681Srpaulodef wpas_connect(): 41281681Srpaulo ifaces = [] 42281681Srpaulo if os.path.isdir(wpas_ctrl): 43281681Srpaulo try: 44281681Srpaulo ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] 45281681Srpaulo except OSError, error: 46281681Srpaulo print "Could not find hostapd: ", error 47281681Srpaulo return None 48281681Srpaulo 49281681Srpaulo if len(ifaces) < 1: 50281681Srpaulo print "No hostapd control interface found" 51281681Srpaulo return None 52281681Srpaulo 53281681Srpaulo for ctrl in ifaces: 54281681Srpaulo try: 55281681Srpaulo wpas = wpaspy.Ctrl(ctrl) 56281681Srpaulo return wpas 57281681Srpaulo except Exception, e: 58281681Srpaulo pass 59281681Srpaulo return None 60281681Srpaulo 61281681Srpaulo 62281681Srpaulodef wpas_tag_read(message): 63281681Srpaulo wpas = wpas_connect() 64281681Srpaulo if (wpas == None): 65281681Srpaulo return False 66281681Srpaulo if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): 67281681Srpaulo return False 68281681Srpaulo return True 69281681Srpaulo 70281681Srpaulo 71281681Srpaulodef wpas_get_config_token(): 72281681Srpaulo wpas = wpas_connect() 73281681Srpaulo if (wpas == None): 74281681Srpaulo return None 75281681Srpaulo ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF") 76281681Srpaulo if "FAIL" in ret: 77281681Srpaulo return None 78281681Srpaulo return ret.rstrip().decode("hex") 79281681Srpaulo 80281681Srpaulo 81281681Srpaulodef wpas_get_password_token(): 82281681Srpaulo wpas = wpas_connect() 83281681Srpaulo if (wpas == None): 84281681Srpaulo return None 85281681Srpaulo ret = wpas.request("WPS_NFC_TOKEN NDEF") 86281681Srpaulo if "FAIL" in ret: 87281681Srpaulo return None 88281681Srpaulo return ret.rstrip().decode("hex") 89281681Srpaulo 90281681Srpaulo 91281681Srpaulodef wpas_get_handover_sel(): 92281681Srpaulo wpas = wpas_connect() 93281681Srpaulo if (wpas == None): 94281681Srpaulo return None 95281681Srpaulo ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR") 96281681Srpaulo if "FAIL" in ret: 97281681Srpaulo return None 98281681Srpaulo return ret.rstrip().decode("hex") 99281681Srpaulo 100281681Srpaulo 101281681Srpaulodef wpas_report_handover(req, sel): 102281681Srpaulo wpas = wpas_connect() 103281681Srpaulo if (wpas == None): 104281681Srpaulo return None 105281681Srpaulo return wpas.request("NFC_REPORT_HANDOVER RESP WPS " + 106281681Srpaulo str(req).encode("hex") + " " + 107281681Srpaulo str(sel).encode("hex")) 108281681Srpaulo 109281681Srpaulo 110281681Srpauloclass HandoverServer(nfc.handover.HandoverServer): 111281681Srpaulo def __init__(self, llc): 112281681Srpaulo super(HandoverServer, self).__init__(llc) 113281681Srpaulo self.ho_server_processing = False 114281681Srpaulo self.success = False 115281681Srpaulo 116281681Srpaulo # override to avoid parser error in request/response.pretty() in nfcpy 117281681Srpaulo # due to new WSC handover format 118281681Srpaulo def _process_request(self, request): 119281681Srpaulo summary("received handover request {}".format(request.type)) 120281681Srpaulo response = nfc.ndef.Message("\xd1\x02\x01Hs\x12") 121281681Srpaulo if not request.type == 'urn:nfc:wkt:Hr': 122281681Srpaulo summary("not a handover request") 123281681Srpaulo else: 124281681Srpaulo try: 125281681Srpaulo request = nfc.ndef.HandoverRequestMessage(request) 126281681Srpaulo except nfc.ndef.DecodeError as e: 127281681Srpaulo summary("error decoding 'Hr' message: {}".format(e)) 128281681Srpaulo else: 129281681Srpaulo response = self.process_request(request) 130281681Srpaulo summary("send handover response {}".format(response.type)) 131281681Srpaulo return response 132281681Srpaulo 133281681Srpaulo def process_request(self, request): 134281681Srpaulo summary("HandoverServer - request received") 135281681Srpaulo try: 136281681Srpaulo print "Parsed handover request: " + request.pretty() 137281681Srpaulo except Exception, e: 138281681Srpaulo print e 139281681Srpaulo print str(request).encode("hex") 140281681Srpaulo 141281681Srpaulo sel = nfc.ndef.HandoverSelectMessage(version="1.2") 142281681Srpaulo 143281681Srpaulo for carrier in request.carriers: 144281681Srpaulo print "Remote carrier type: " + carrier.type 145281681Srpaulo if carrier.type == "application/vnd.wfa.wsc": 146281681Srpaulo summary("WPS carrier type match - add WPS carrier record") 147281681Srpaulo data = wpas_get_handover_sel() 148281681Srpaulo if data is None: 149281681Srpaulo summary("Could not get handover select carrier record from hostapd") 150281681Srpaulo continue 151281681Srpaulo print "Handover select carrier record from hostapd:" 152281681Srpaulo print data.encode("hex") 153281681Srpaulo if "OK" in wpas_report_handover(carrier.record, data): 154281681Srpaulo success_report("Handover reported successfully") 155281681Srpaulo else: 156281681Srpaulo summary("Handover report rejected") 157281681Srpaulo 158281681Srpaulo message = nfc.ndef.Message(data); 159281681Srpaulo sel.add_carrier(message[0], "active", message[1:]) 160281681Srpaulo 161281681Srpaulo print "Handover select:" 162281681Srpaulo try: 163281681Srpaulo print sel.pretty() 164281681Srpaulo except Exception, e: 165281681Srpaulo print e 166281681Srpaulo print str(sel).encode("hex") 167281681Srpaulo 168281681Srpaulo summary("Sending handover select") 169281681Srpaulo self.success = True 170281681Srpaulo return sel 171281681Srpaulo 172281681Srpaulo 173281681Srpaulodef wps_tag_read(tag): 174281681Srpaulo success = False 175281681Srpaulo if len(tag.ndef.message): 176281681Srpaulo for record in tag.ndef.message: 177281681Srpaulo print "record type " + record.type 178281681Srpaulo if record.type == "application/vnd.wfa.wsc": 179281681Srpaulo summary("WPS tag - send to hostapd") 180281681Srpaulo success = wpas_tag_read(tag.ndef.message) 181281681Srpaulo break 182281681Srpaulo else: 183281681Srpaulo summary("Empty tag") 184281681Srpaulo 185281681Srpaulo if success: 186281681Srpaulo success_report("Tag read succeeded") 187281681Srpaulo 188281681Srpaulo return success 189281681Srpaulo 190281681Srpaulo 191281681Srpaulodef rdwr_connected_write(tag): 192281681Srpaulo summary("Tag found - writing - " + str(tag)) 193281681Srpaulo global write_data 194281681Srpaulo tag.ndef.message = str(write_data) 195281681Srpaulo success_report("Tag write succeeded") 196281681Srpaulo print "Done - remove tag" 197281681Srpaulo global only_one 198281681Srpaulo if only_one: 199281681Srpaulo global continue_loop 200281681Srpaulo continue_loop = False 201281681Srpaulo global write_wait_remove 202281681Srpaulo while write_wait_remove and tag.is_present: 203281681Srpaulo time.sleep(0.1) 204281681Srpaulo 205281681Srpaulodef wps_write_config_tag(clf, wait_remove=True): 206281681Srpaulo summary("Write WPS config token") 207281681Srpaulo global write_data, write_wait_remove 208281681Srpaulo write_wait_remove = wait_remove 209281681Srpaulo write_data = wpas_get_config_token() 210281681Srpaulo if write_data == None: 211281681Srpaulo summary("Could not get WPS config token from hostapd") 212281681Srpaulo return 213281681Srpaulo 214281681Srpaulo print "Touch an NFC tag" 215281681Srpaulo clf.connect(rdwr={'on-connect': rdwr_connected_write}) 216281681Srpaulo 217281681Srpaulo 218281681Srpaulodef wps_write_password_tag(clf, wait_remove=True): 219281681Srpaulo summary("Write WPS password token") 220281681Srpaulo global write_data, write_wait_remove 221281681Srpaulo write_wait_remove = wait_remove 222281681Srpaulo write_data = wpas_get_password_token() 223281681Srpaulo if write_data == None: 224281681Srpaulo summary("Could not get WPS password token from hostapd") 225281681Srpaulo return 226281681Srpaulo 227281681Srpaulo print "Touch an NFC tag" 228281681Srpaulo clf.connect(rdwr={'on-connect': rdwr_connected_write}) 229281681Srpaulo 230281681Srpaulo 231281681Srpaulodef rdwr_connected(tag): 232281681Srpaulo global only_one, no_wait 233281681Srpaulo summary("Tag connected: " + str(tag)) 234281681Srpaulo 235281681Srpaulo if tag.ndef: 236281681Srpaulo print "NDEF tag: " + tag.type 237281681Srpaulo try: 238281681Srpaulo print tag.ndef.message.pretty() 239281681Srpaulo except Exception, e: 240281681Srpaulo print e 241281681Srpaulo success = wps_tag_read(tag) 242281681Srpaulo if only_one and success: 243281681Srpaulo global continue_loop 244281681Srpaulo continue_loop = False 245281681Srpaulo else: 246281681Srpaulo summary("Not an NDEF tag - remove tag") 247281681Srpaulo return True 248281681Srpaulo 249281681Srpaulo return not no_wait 250281681Srpaulo 251281681Srpaulo 252281681Srpaulodef llcp_startup(clf, llc): 253281681Srpaulo print "Start LLCP server" 254281681Srpaulo global srv 255281681Srpaulo srv = HandoverServer(llc) 256281681Srpaulo return llc 257281681Srpaulo 258281681Srpaulodef llcp_connected(llc): 259281681Srpaulo print "P2P LLCP connected" 260281681Srpaulo global wait_connection 261281681Srpaulo wait_connection = False 262281681Srpaulo global srv 263281681Srpaulo srv.start() 264281681Srpaulo return True 265281681Srpaulo 266281681Srpaulo 267281681Srpaulodef main(): 268281681Srpaulo clf = nfc.ContactlessFrontend() 269281681Srpaulo 270281681Srpaulo parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations') 271281681Srpaulo parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, 272281681Srpaulo action='store_const', dest='loglevel', 273281681Srpaulo help='verbose debug output') 274281681Srpaulo parser.add_argument('-q', const=logging.WARNING, action='store_const', 275281681Srpaulo dest='loglevel', help='be quiet') 276281681Srpaulo parser.add_argument('--only-one', '-1', action='store_true', 277281681Srpaulo help='run only one operation and exit') 278281681Srpaulo parser.add_argument('--no-wait', action='store_true', 279281681Srpaulo help='do not wait for tag to be removed before exiting') 280281681Srpaulo parser.add_argument('--summary', 281281681Srpaulo help='summary file for writing status updates') 282281681Srpaulo parser.add_argument('--success', 283281681Srpaulo help='success file for writing success update') 284281681Srpaulo parser.add_argument('command', choices=['write-config', 285281681Srpaulo 'write-password'], 286281681Srpaulo nargs='?') 287281681Srpaulo args = parser.parse_args() 288281681Srpaulo 289281681Srpaulo global only_one 290281681Srpaulo only_one = args.only_one 291281681Srpaulo 292281681Srpaulo global no_wait 293281681Srpaulo no_wait = args.no_wait 294281681Srpaulo 295281681Srpaulo if args.summary: 296281681Srpaulo global summary_file 297281681Srpaulo summary_file = args.summary 298281681Srpaulo 299281681Srpaulo if args.success: 300281681Srpaulo global success_file 301281681Srpaulo success_file = args.success 302281681Srpaulo 303281681Srpaulo logging.basicConfig(level=args.loglevel) 304281681Srpaulo 305281681Srpaulo try: 306281681Srpaulo if not clf.open("usb"): 307281681Srpaulo print "Could not open connection with an NFC device" 308281681Srpaulo raise SystemExit 309281681Srpaulo 310281681Srpaulo if args.command == "write-config": 311281681Srpaulo wps_write_config_tag(clf, wait_remove=not args.no_wait) 312281681Srpaulo raise SystemExit 313281681Srpaulo 314281681Srpaulo if args.command == "write-password": 315281681Srpaulo wps_write_password_tag(clf, wait_remove=not args.no_wait) 316281681Srpaulo raise SystemExit 317281681Srpaulo 318281681Srpaulo global continue_loop 319281681Srpaulo while continue_loop: 320281681Srpaulo print "Waiting for a tag or peer to be touched" 321281681Srpaulo wait_connection = True 322281681Srpaulo try: 323281681Srpaulo if not clf.connect(rdwr={'on-connect': rdwr_connected}, 324281681Srpaulo llcp={'on-startup': llcp_startup, 325281681Srpaulo 'on-connect': llcp_connected}): 326281681Srpaulo break 327281681Srpaulo except Exception, e: 328281681Srpaulo print "clf.connect failed" 329281681Srpaulo 330281681Srpaulo global srv 331281681Srpaulo if only_one and srv and srv.success: 332281681Srpaulo raise SystemExit 333281681Srpaulo 334281681Srpaulo except KeyboardInterrupt: 335281681Srpaulo raise SystemExit 336281681Srpaulo finally: 337281681Srpaulo clf.close() 338281681Srpaulo 339281681Srpaulo raise SystemExit 340281681Srpaulo 341281681Srpauloif __name__ == '__main__': 342281681Srpaulo main() 343