p2p-nfc.py revision 281681
150959Speter#!/usr/bin/python 250120Swpaul# 350120Swpaul# Example nfcpy to wpa_supplicant wrapper for P2P NFC operations 450120Swpaul# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> 550120Swpaul# 650120Swpaul# This software may be distributed under the terms of the BSD license. 750120Swpaul# See README for more details. 850120Swpaul 950120Swpaulimport os 1050120Swpaulimport sys 1150120Swpaulimport time 1250120Swpaulimport random 1350120Swpaulimport threading 1450120Swpaulimport argparse 1550120Swpaul 1650120Swpaulimport nfc 1750120Swpaulimport nfc.ndef 1850120Swpaulimport nfc.llcp 1950120Swpaulimport nfc.handover 2050120Swpaul 2150120Swpaulimport logging 2250120Swpaul 2350120Swpaulimport wpaspy 2450120Swpaul 2550120Swpaulwpas_ctrl = '/var/run/wpa_supplicant' 2650120Swpaulifname = None 2750120Swpaulinit_on_touch = False 2850120Swpaulin_raw_mode = False 2950120Swpaulprev_tcgetattr = 0 3050120Swpaulinclude_wps_req = True 3150120Swpaulinclude_p2p_req = True 3250120Swpaulno_input = False 3350120Swpaulsrv = None 3450120Swpaulcontinue_loop = True 3550120Swpaulterminate_now = False 3650120Swpaulsummary_file = None 3750120Swpaulsuccess_file = None 3850120Swpaul 3950120Swpauldef summary(txt): 4050120Swpaul print txt 4150120Swpaul if summary_file: 4250120Swpaul with open(summary_file, 'a') as f: 4350120Swpaul f.write(txt + "\n") 4450120Swpaul 4550120Swpauldef success_report(txt): 4650120Swpaul summary(txt) 4750120Swpaul if success_file: 4850120Swpaul with open(success_file, 'a') as f: 4950120Swpaul f.write(txt + "\n") 5050120Swpaul 5150120Swpauldef wpas_connect(): 52179895Sdelphij ifaces = [] 5361907Ssemenu if os.path.isdir(wpas_ctrl): 5450120Swpaul try: 55190538Simp ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] 56179098Syongari except OSError, error: 5750577Swpaul print "Could not find wpa_supplicant: ", error 58178667Sjhb return None 59135048Swpaul 6050120Swpaul if len(ifaces) < 1: 61160637Syongari print "No wpa_supplicant control interface found" 6250120Swpaul return None 6350120Swpaul 6477078Swpaul for ctrl in ifaces: 65179335Syongari if ifname: 6650120Swpaul if ifname not in ctrl: 6750120Swpaul continue 6850120Swpaul try: 6994149Swpaul print "Trying to use control interface " + ctrl 7050120Swpaul wpas = wpaspy.Ctrl(ctrl) 7150120Swpaul return wpas 72179592Sbenno except Exception, e: 7366989Simp pass 7450120Swpaul return None 75170364Syongari 7659475Swpaul 7775353Smjacobdef wpas_tag_read(message): 7884145Sjlemon wpas = wpas_connect() 7950120Swpaul if (wpas == None): 8050120Swpaul return False 8150120Swpaul cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex") 8250120Swpaul global force_freq 8374129Sjlemon if force_freq: 8474129Sjlemon cmd = cmd + " freq=" + force_freq 8574129Sjlemon if "FAIL" in wpas.request(cmd): 8650120Swpaul return False 8750120Swpaul return True 8861907Ssemenu 8959475Swpaul 90165090Sscottldef wpas_get_handover_req(): 9150120Swpaul wpas = wpas_connect() 9250120Swpaul if (wpas == None): 9350120Swpaul return None 9450120Swpaul res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip() 9559475Swpaul if "FAIL" in res: 9650120Swpaul return None 9750120Swpaul return res.decode("hex") 9850120Swpaul 9950120Swpauldef wpas_get_handover_req_wps(): 10050120Swpaul wpas = wpas_connect() 10150120Swpaul if (wpas == None): 10250120Swpaul return None 10350120Swpaul res = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip() 104173130Syongari if "FAIL" in res: 105119976Swpaul return None 10650120Swpaul return res.decode("hex") 10750120Swpaul 10850120Swpaul 10950120Swpauldef wpas_get_handover_sel(tag=False): 11050120Swpaul wpas = wpas_connect() 111179895Sdelphij if (wpas == None): 112179895Sdelphij return None 113179895Sdelphij if tag: 11461907Ssemenu res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip() 11561907Ssemenu else: 116109147Sobrien res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip() 117164833Smarius if "FAIL" in res: 11861907Ssemenu return None 11950120Swpaul return res.decode("hex") 12050120Swpaul 12166127Swpaul 122164833Smariusdef wpas_get_handover_sel_wps(): 12350120Swpaul wpas = wpas_connect() 124190538Simp if (wpas == None): 125190538Simp return None 126190538Simp res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR"); 127179098Syongari if "FAIL" in res: 128179098Syongari return None 129183567Sstas return res.rstrip().decode("hex") 130179098Syongari 13150577Swpaul 13299440Sbennodef wpas_report_handover(req, sel, type): 13399439Sbenno wpas = wpas_connect() 13499440Sbenno if (wpas == None): 13599440Sbenno return None 136119917Swpaul cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex") 13759475Swpaul global force_freq 13883029Swpaul if force_freq: 13983029Swpaul cmd = cmd + " freq=" + force_freq 140165090Sscottl return wpas.request(cmd) 141161749Syongari 14292931Swpaul 143170391Sdavidchdef wpas_report_handover_wsc(req, sel, type): 144103103Sjdp wpas = wpas_connect() 145114547Sps if (wpas == None): 146117659Swpaul return None 147135772Sps cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex") 148146413Sps if force_freq: 149157041Soleg cmd = cmd + " freq=" + force_freq 150166876Sjhb return wpas.request(cmd) 151168601Smarius 152166031Sjkim 153170391Sdavidchdef p2p_handover_client(llc): 154176850Sdavidch message = nfc.ndef.HandoverRequestMessage(version="1.2") 155176881Sjhb message.nonce = random.randint(0, 0xffff) 156176850Sdavidch 157178667Sjhb global include_p2p_req 15850577Swpaul if include_p2p_req: 159135048Swpaul data = wpas_get_handover_req() 160135048Swpaul if (data == None): 161176773Sraj summary("Could not get handover request carrier record from wpa_supplicant") 162184192Syongari return 163135048Swpaul print "Handover request carrier record from wpa_supplicant: " + data.encode("hex") 164135048Swpaul datamsg = nfc.ndef.Message(data) 165178598Sraj message.add_carrier(datamsg[0], "active", datamsg[1:]) 166170364Syongari 167135048Swpaul global include_wps_req 16850120Swpaul if include_wps_req: 169164833Smarius print "Handover request (pre-WPS):" 17050120Swpaul try: 17150120Swpaul print message.pretty() 17250120Swpaul except Exception, e: 173170524Syongari print e 17450120Swpaul 175170524Syongari data = wpas_get_handover_req_wps() 176170524Syongari if data: 17750120Swpaul print "Add WPS request in addition to P2P" 178160637Syongari datamsg = nfc.ndef.Message(data) 179170366Syongari message.add_carrier(datamsg[0], "active", datamsg[1:]) 180160637Syongari 181177930Syongari print "Handover request:" 182160637Syongari try: 18350120Swpaul print message.pretty() 18474129Sjlemon except Exception, e: 18576483Sjlemon print e 18676483Sjlemon print str(message).encode("hex") 18776483Sjlemon 18874129Sjlemon client = nfc.handover.HandoverClient(llc) 18950120Swpaul try: 19077078Swpaul summary("Trying to initiate NFC connection handover") 19177078Swpaul client.connect() 19277078Swpaul summary("Connected for handover") 193179335Syongari except nfc.llcp.ConnectRefused: 194179335Syongari summary("Handover connection refused") 195179335Syongari client.close() 196179335Syongari return 19750120Swpaul except Exception, e: 19850120Swpaul summary("Other exception: " + str(e)) 19950120Swpaul client.close() 20050120Swpaul return 20150120Swpaul 20250120Swpaul summary("Sending handover request") 203175702Smarius 204175702Smarius if not client.send(message): 20576479Swpaul summary("Failed to send handover request") 20676479Swpaul client.close() 20750120Swpaul return 20850120Swpaul 20950120Swpaul summary("Receiving handover response") 21050120Swpaul message = client._recv() 21194149Swpaul if message is None: 21294149Swpaul summary("No response received") 213165782Sticso client.close() 214173130Syongari return 21594149Swpaul if message.type != "urn:nfc:wkt:Hs": 21650120Swpaul summary("Response was not Hs - received: " + message.type) 21750120Swpaul client.close() 21850120Swpaul return 21950120Swpaul 22050120Swpaul print "Received message" 22150120Swpaul try: 22250120Swpaul print message.pretty() 223179592Sbenno except Exception, e: 224179592Sbenno print e 225179592Sbenno print str(message).encode("hex") 22666989Simp message = nfc.ndef.HandoverSelectMessage(message) 22766989Simp summary("Handover select received") 22866989Simp try: 22950120Swpaul print message.pretty() 23050120Swpaul except Exception, e: 23150120Swpaul print e 23259475Swpaul 23359475Swpaul for carrier in message.carriers: 23459475Swpaul print "Remote carrier type: " + carrier.type 23575353Smjacob if carrier.type == "application/vnd.wfa.p2p": 23675353Smjacob print "P2P carrier type match - send to wpa_supplicant" 23784145Sjlemon if "OK" in wpas_report_handover(data, carrier.record, "INIT"): 238120281Swilko success_report("P2P handover reported successfully (initiator)") 239165096Syongari else: 240165096Syongari summary("P2P handover report rejected") 241165096Syongari break 242165096Syongari 243165096Syongari print "Remove peer" 244165096Syongari client.close() 245165096Syongari print "Done with handover" 246165096Syongari global only_one 247165096Syongari if only_one: 248182751Sraj print "only_one -> stop loop" 249165096Syongari global continue_loop 25084145Sjlemon continue_loop = False 251165096Syongari 252165096Syongari global no_wait 253165096Syongari if no_wait: 254165096Syongari print "Trying to exit.." 255 global terminate_now 256 terminate_now = True 257 258 259class HandoverServer(nfc.handover.HandoverServer): 260 def __init__(self, llc): 261 super(HandoverServer, self).__init__(llc) 262 self.sent_carrier = None 263 self.ho_server_processing = False 264 self.success = False 265 266 # override to avoid parser error in request/response.pretty() in nfcpy 267 # due to new WSC handover format 268 def _process_request(self, request): 269 summary("received handover request {}".format(request.type)) 270 response = nfc.ndef.Message("\xd1\x02\x01Hs\x12") 271 if not request.type == 'urn:nfc:wkt:Hr': 272 summary("not a handover request") 273 else: 274 try: 275 request = nfc.ndef.HandoverRequestMessage(request) 276 except nfc.ndef.DecodeError as e: 277 summary("error decoding 'Hr' message: {}".format(e)) 278 else: 279 response = self.process_request(request) 280 summary("send handover response {}".format(response.type)) 281 return response 282 283 def process_request(self, request): 284 self.ho_server_processing = True 285 clear_raw_mode() 286 print "HandoverServer - request received" 287 try: 288 print "Parsed handover request: " + request.pretty() 289 except Exception, e: 290 print e 291 292 sel = nfc.ndef.HandoverSelectMessage(version="1.2") 293 294 found = False 295 296 for carrier in request.carriers: 297 print "Remote carrier type: " + carrier.type 298 if carrier.type == "application/vnd.wfa.p2p": 299 print "P2P carrier type match - add P2P carrier record" 300 found = True 301 self.received_carrier = carrier.record 302 print "Carrier record:" 303 try: 304 print carrier.record.pretty() 305 except Exception, e: 306 print e 307 data = wpas_get_handover_sel() 308 if data is None: 309 print "Could not get handover select carrier record from wpa_supplicant" 310 continue 311 print "Handover select carrier record from wpa_supplicant:" 312 print data.encode("hex") 313 self.sent_carrier = data 314 if "OK" in wpas_report_handover(self.received_carrier, self.sent_carrier, "RESP"): 315 success_report("P2P handover reported successfully (responder)") 316 else: 317 summary("P2P handover report rejected") 318 break 319 320 message = nfc.ndef.Message(data); 321 sel.add_carrier(message[0], "active", message[1:]) 322 break 323 324 for carrier in request.carriers: 325 if found: 326 break 327 print "Remote carrier type: " + carrier.type 328 if carrier.type == "application/vnd.wfa.wsc": 329 print "WSC carrier type match - add WSC carrier record" 330 found = True 331 self.received_carrier = carrier.record 332 print "Carrier record:" 333 try: 334 print carrier.record.pretty() 335 except Exception, e: 336 print e 337 data = wpas_get_handover_sel_wps() 338 if data is None: 339 print "Could not get handover select carrier record from wpa_supplicant" 340 continue 341 print "Handover select carrier record from wpa_supplicant:" 342 print data.encode("hex") 343 self.sent_carrier = data 344 if "OK" in wpas_report_handover_wsc(self.received_carrier, self.sent_carrier, "RESP"): 345 success_report("WSC handover reported successfully") 346 else: 347 summary("WSC handover report rejected") 348 break 349 350 message = nfc.ndef.Message(data); 351 sel.add_carrier(message[0], "active", message[1:]) 352 found = True 353 break 354 355 print "Handover select:" 356 try: 357 print sel.pretty() 358 except Exception, e: 359 print e 360 print str(sel).encode("hex") 361 362 summary("Sending handover select") 363 self.success = True 364 return sel 365 366 367def clear_raw_mode(): 368 import sys, tty, termios 369 global prev_tcgetattr, in_raw_mode 370 if not in_raw_mode: 371 return 372 fd = sys.stdin.fileno() 373 termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr) 374 in_raw_mode = False 375 376 377def getch(): 378 import sys, tty, termios, select 379 global prev_tcgetattr, in_raw_mode 380 fd = sys.stdin.fileno() 381 prev_tcgetattr = termios.tcgetattr(fd) 382 ch = None 383 try: 384 tty.setraw(fd) 385 in_raw_mode = True 386 [i, o, e] = select.select([fd], [], [], 0.05) 387 if i: 388 ch = sys.stdin.read(1) 389 finally: 390 termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr) 391 in_raw_mode = False 392 return ch 393 394 395def p2p_tag_read(tag): 396 success = False 397 if len(tag.ndef.message): 398 for record in tag.ndef.message: 399 print "record type " + record.type 400 if record.type == "application/vnd.wfa.wsc": 401 summary("WPS tag - send to wpa_supplicant") 402 success = wpas_tag_read(tag.ndef.message) 403 break 404 if record.type == "application/vnd.wfa.p2p": 405 summary("P2P tag - send to wpa_supplicant") 406 success = wpas_tag_read(tag.ndef.message) 407 break 408 else: 409 summary("Empty tag") 410 411 if success: 412 success_report("Tag read succeeded") 413 414 return success 415 416 417def rdwr_connected_p2p_write(tag): 418 summary("Tag found - writing - " + str(tag)) 419 global p2p_sel_data 420 tag.ndef.message = str(p2p_sel_data) 421 success_report("Tag write succeeded") 422 print "Done - remove tag" 423 global only_one 424 if only_one: 425 global continue_loop 426 continue_loop = False 427 global p2p_sel_wait_remove 428 return p2p_sel_wait_remove 429 430def wps_write_p2p_handover_sel(clf, wait_remove=True): 431 print "Write P2P handover select" 432 data = wpas_get_handover_sel(tag=True) 433 if (data == None): 434 summary("Could not get P2P handover select from wpa_supplicant") 435 return 436 437 global p2p_sel_wait_remove 438 p2p_sel_wait_remove = wait_remove 439 global p2p_sel_data 440 p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2") 441 message = nfc.ndef.Message(data); 442 p2p_sel_data.add_carrier(message[0], "active", message[1:]) 443 print "Handover select:" 444 try: 445 print p2p_sel_data.pretty() 446 except Exception, e: 447 print e 448 print str(p2p_sel_data).encode("hex") 449 450 print "Touch an NFC tag" 451 clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write}) 452 453 454def rdwr_connected(tag): 455 global only_one, no_wait 456 summary("Tag connected: " + str(tag)) 457 458 if tag.ndef: 459 print "NDEF tag: " + tag.type 460 try: 461 print tag.ndef.message.pretty() 462 except Exception, e: 463 print e 464 success = p2p_tag_read(tag) 465 if only_one and success: 466 global continue_loop 467 continue_loop = False 468 else: 469 summary("Not an NDEF tag - remove tag") 470 return True 471 472 return not no_wait 473 474 475def llcp_worker(llc): 476 global init_on_touch 477 if init_on_touch: 478 print "Starting handover client" 479 p2p_handover_client(llc) 480 return 481 482 global no_input 483 if no_input: 484 print "Wait for handover to complete" 485 else: 486 print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)" 487 global srv 488 global wait_connection 489 while not wait_connection and srv.sent_carrier is None: 490 if srv.ho_server_processing: 491 time.sleep(0.025) 492 elif no_input: 493 time.sleep(0.5) 494 else: 495 global include_wps_req, include_p2p_req 496 res = getch() 497 if res == 'i': 498 include_wps_req = True 499 include_p2p_req = True 500 elif res == 'p': 501 include_wps_req = False 502 include_p2p_req = True 503 elif res == 'w': 504 include_wps_req = True 505 include_p2p_req = False 506 else: 507 continue 508 clear_raw_mode() 509 print "Starting handover client" 510 p2p_handover_client(llc) 511 return 512 513 clear_raw_mode() 514 print "Exiting llcp_worker thread" 515 516def llcp_startup(clf, llc): 517 print "Start LLCP server" 518 global srv 519 srv = HandoverServer(llc) 520 return llc 521 522def llcp_connected(llc): 523 print "P2P LLCP connected" 524 global wait_connection 525 wait_connection = False 526 global init_on_touch 527 if not init_on_touch: 528 global srv 529 srv.start() 530 if init_on_touch or not no_input: 531 threading.Thread(target=llcp_worker, args=(llc,)).start() 532 return True 533 534def terminate_loop(): 535 global terminate_now 536 return terminate_now 537 538def main(): 539 clf = nfc.ContactlessFrontend() 540 541 parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and WPS NFC operations') 542 parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, 543 action='store_const', dest='loglevel', 544 help='verbose debug output') 545 parser.add_argument('-q', const=logging.WARNING, action='store_const', 546 dest='loglevel', help='be quiet') 547 parser.add_argument('--only-one', '-1', action='store_true', 548 help='run only one operation and exit') 549 parser.add_argument('--init-on-touch', '-I', action='store_true', 550 help='initiate handover on touch') 551 parser.add_argument('--no-wait', action='store_true', 552 help='do not wait for tag to be removed before exiting') 553 parser.add_argument('--ifname', '-i', 554 help='network interface name') 555 parser.add_argument('--no-wps-req', '-N', action='store_true', 556 help='do not include WPS carrier record in request') 557 parser.add_argument('--no-input', '-a', action='store_true', 558 help='do not use stdout input to initiate handover') 559 parser.add_argument('--tag-read-only', '-t', action='store_true', 560 help='tag read only (do not allow connection handover)') 561 parser.add_argument('--handover-only', action='store_true', 562 help='connection handover only (do not allow tag read)') 563 parser.add_argument('--freq', '-f', 564 help='forced frequency of operating channel in MHz') 565 parser.add_argument('--summary', 566 help='summary file for writing status updates') 567 parser.add_argument('--success', 568 help='success file for writing success update') 569 parser.add_argument('command', choices=['write-p2p-sel'], 570 nargs='?') 571 args = parser.parse_args() 572 573 global only_one 574 only_one = args.only_one 575 576 global no_wait 577 no_wait = args.no_wait 578 579 global force_freq 580 force_freq = args.freq 581 582 logging.basicConfig(level=args.loglevel) 583 584 global init_on_touch 585 init_on_touch = args.init_on_touch 586 587 if args.ifname: 588 global ifname 589 ifname = args.ifname 590 print "Selected ifname " + ifname 591 592 if args.no_wps_req: 593 global include_wps_req 594 include_wps_req = False 595 596 if args.summary: 597 global summary_file 598 summary_file = args.summary 599 600 if args.success: 601 global success_file 602 success_file = args.success 603 604 if args.no_input: 605 global no_input 606 no_input = True 607 608 clf = nfc.ContactlessFrontend() 609 global wait_connection 610 611 try: 612 if not clf.open("usb"): 613 print "Could not open connection with an NFC device" 614 raise SystemExit 615 616 if args.command == "write-p2p-sel": 617 wps_write_p2p_handover_sel(clf, wait_remove=not args.no_wait) 618 raise SystemExit 619 620 global continue_loop 621 while continue_loop: 622 print "Waiting for a tag or peer to be touched" 623 wait_connection = True 624 try: 625 if args.tag_read_only: 626 if not clf.connect(rdwr={'on-connect': rdwr_connected}): 627 break 628 elif args.handover_only: 629 if not clf.connect(llcp={'on-startup': llcp_startup, 630 'on-connect': llcp_connected}, 631 terminate=terminate_loop): 632 break 633 else: 634 if not clf.connect(rdwr={'on-connect': rdwr_connected}, 635 llcp={'on-startup': llcp_startup, 636 'on-connect': llcp_connected}, 637 terminate=terminate_loop): 638 break 639 except Exception, e: 640 print "clf.connect failed" 641 642 global srv 643 if only_one and srv and srv.success: 644 raise SystemExit 645 646 except KeyboardInterrupt: 647 raise SystemExit 648 finally: 649 clf.close() 650 651 raise SystemExit 652 653if __name__ == '__main__': 654 main() 655