| #!/usr/bin/python | 
 | # | 
 | # Example nfcpy to hostapd wrapper for WPS NFC operations | 
 | # Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> | 
 | # | 
 | # This software may be distributed under the terms of the BSD license. | 
 | # See README for more details. | 
 |  | 
 | import os | 
 | import sys | 
 | import time | 
 | import argparse | 
 |  | 
 | import nfc | 
 | import nfc.ndef | 
 | import nfc.llcp | 
 | import nfc.handover | 
 |  | 
 | import logging | 
 |  | 
 | import wpaspy | 
 |  | 
 | wpas_ctrl = '/var/run/hostapd' | 
 | continue_loop = True | 
 | summary_file = None | 
 | success_file = None | 
 |  | 
 | def summary(txt): | 
 |     print(txt) | 
 |     if summary_file: | 
 |         with open(summary_file, 'a') as f: | 
 |             f.write(txt + "\n") | 
 |  | 
 | def success_report(txt): | 
 |     summary(txt) | 
 |     if success_file: | 
 |         with open(success_file, 'a') as f: | 
 |             f.write(txt + "\n") | 
 |  | 
 | def wpas_connect(): | 
 |     ifaces = [] | 
 |     if os.path.isdir(wpas_ctrl): | 
 |         try: | 
 |             ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] | 
 |         except OSError as error: | 
 |             print("Could not find hostapd: ", error) | 
 |             return None | 
 |  | 
 |     if len(ifaces) < 1: | 
 |         print("No hostapd control interface found") | 
 |         return None | 
 |  | 
 |     for ctrl in ifaces: | 
 |         try: | 
 |             wpas = wpaspy.Ctrl(ctrl) | 
 |             return wpas | 
 |         except Exception as e: | 
 |             pass | 
 |     return None | 
 |  | 
 |  | 
 | def wpas_tag_read(message): | 
 |     wpas = wpas_connect() | 
 |     if (wpas == None): | 
 |         return False | 
 |     if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): | 
 |         return False | 
 |     return True | 
 |  | 
 |  | 
 | def wpas_get_config_token(): | 
 |     wpas = wpas_connect() | 
 |     if (wpas == None): | 
 |         return None | 
 |     ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF") | 
 |     if "FAIL" in ret: | 
 |         return None | 
 |     return ret.rstrip().decode("hex") | 
 |  | 
 |  | 
 | def wpas_get_password_token(): | 
 |     wpas = wpas_connect() | 
 |     if (wpas == None): | 
 |         return None | 
 |     ret = wpas.request("WPS_NFC_TOKEN NDEF") | 
 |     if "FAIL" in ret: | 
 |         return None | 
 |     return ret.rstrip().decode("hex") | 
 |  | 
 |  | 
 | def wpas_get_handover_sel(): | 
 |     wpas = wpas_connect() | 
 |     if (wpas == None): | 
 |         return None | 
 |     ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR") | 
 |     if "FAIL" in ret: | 
 |         return None | 
 |     return ret.rstrip().decode("hex") | 
 |  | 
 |  | 
 | def wpas_report_handover(req, sel): | 
 |     wpas = wpas_connect() | 
 |     if (wpas == None): | 
 |         return None | 
 |     return wpas.request("NFC_REPORT_HANDOVER RESP WPS " + | 
 |                         str(req).encode("hex") + " " + | 
 |                         str(sel).encode("hex")) | 
 |  | 
 |  | 
 | class HandoverServer(nfc.handover.HandoverServer): | 
 |     def __init__(self, llc): | 
 |         super(HandoverServer, self).__init__(llc) | 
 |         self.ho_server_processing = False | 
 |         self.success = False | 
 |  | 
 |     # override to avoid parser error in request/response.pretty() in nfcpy | 
 |     # due to new WSC handover format | 
 |     def _process_request(self, request): | 
 |         summary("received handover request {}".format(request.type)) | 
 |         response = nfc.ndef.Message("\xd1\x02\x01Hs\x12") | 
 |         if not request.type == 'urn:nfc:wkt:Hr': | 
 |             summary("not a handover request") | 
 |         else: | 
 |             try: | 
 |                 request = nfc.ndef.HandoverRequestMessage(request) | 
 |             except nfc.ndef.DecodeError as e: | 
 |                 summary("error decoding 'Hr' message: {}".format(e)) | 
 |             else: | 
 |                 response = self.process_request(request) | 
 |         summary("send handover response {}".format(response.type)) | 
 |         return response | 
 |  | 
 |     def process_request(self, request): | 
 |         summary("HandoverServer - request received") | 
 |         try: | 
 |             print("Parsed handover request: " + request.pretty()) | 
 |         except Exception as e: | 
 |             print(e) | 
 |         print(str(request).encode("hex")) | 
 |  | 
 |         sel = nfc.ndef.HandoverSelectMessage(version="1.2") | 
 |  | 
 |         for carrier in request.carriers: | 
 |             print("Remote carrier type: " + carrier.type) | 
 |             if carrier.type == "application/vnd.wfa.wsc": | 
 |                 summary("WPS carrier type match - add WPS carrier record") | 
 |                 data = wpas_get_handover_sel() | 
 |                 if data is None: | 
 |                     summary("Could not get handover select carrier record from hostapd") | 
 |                     continue | 
 |                 print("Handover select carrier record from hostapd:") | 
 |                 print(data.encode("hex")) | 
 |                 if "OK" in wpas_report_handover(carrier.record, data): | 
 |                     success_report("Handover reported successfully") | 
 |                 else: | 
 |                     summary("Handover report rejected") | 
 |  | 
 |                 message = nfc.ndef.Message(data); | 
 |                 sel.add_carrier(message[0], "active", message[1:]) | 
 |  | 
 |         print("Handover select:") | 
 |         try: | 
 |             print(sel.pretty()) | 
 |         except Exception as e: | 
 |             print(e) | 
 |         print(str(sel).encode("hex")) | 
 |  | 
 |         summary("Sending handover select") | 
 |         self.success = True | 
 |         return sel | 
 |  | 
 |  | 
 | def wps_tag_read(tag): | 
 |     success = False | 
 |     if len(tag.ndef.message): | 
 |         for record in tag.ndef.message: | 
 |             print("record type " + record.type) | 
 |             if record.type == "application/vnd.wfa.wsc": | 
 |                 summary("WPS tag - send to hostapd") | 
 |                 success = wpas_tag_read(tag.ndef.message) | 
 |                 break | 
 |     else: | 
 |         summary("Empty tag") | 
 |  | 
 |     if success: | 
 |         success_report("Tag read succeeded") | 
 |  | 
 |     return success | 
 |  | 
 |  | 
 | def rdwr_connected_write(tag): | 
 |     summary("Tag found - writing - " + str(tag)) | 
 |     global write_data | 
 |     tag.ndef.message = str(write_data) | 
 |     success_report("Tag write succeeded") | 
 |     print("Done - remove tag") | 
 |     global only_one | 
 |     if only_one: | 
 |         global continue_loop | 
 |         continue_loop = False | 
 |     global write_wait_remove | 
 |     while write_wait_remove and tag.is_present: | 
 |         time.sleep(0.1) | 
 |  | 
 | def wps_write_config_tag(clf, wait_remove=True): | 
 |     summary("Write WPS config token") | 
 |     global write_data, write_wait_remove | 
 |     write_wait_remove = wait_remove | 
 |     write_data = wpas_get_config_token() | 
 |     if write_data == None: | 
 |         summary("Could not get WPS config token from hostapd") | 
 |         return | 
 |  | 
 |     print("Touch an NFC tag") | 
 |     clf.connect(rdwr={'on-connect': rdwr_connected_write}) | 
 |  | 
 |  | 
 | def wps_write_password_tag(clf, wait_remove=True): | 
 |     summary("Write WPS password token") | 
 |     global write_data, write_wait_remove | 
 |     write_wait_remove = wait_remove | 
 |     write_data = wpas_get_password_token() | 
 |     if write_data == None: | 
 |         summary("Could not get WPS password token from hostapd") | 
 |         return | 
 |  | 
 |     print("Touch an NFC tag") | 
 |     clf.connect(rdwr={'on-connect': rdwr_connected_write}) | 
 |  | 
 |  | 
 | def rdwr_connected(tag): | 
 |     global only_one, no_wait | 
 |     summary("Tag connected: " + str(tag)) | 
 |  | 
 |     if tag.ndef: | 
 |         print("NDEF tag: " + tag.type) | 
 |         try: | 
 |             print(tag.ndef.message.pretty()) | 
 |         except Exception as e: | 
 |             print(e) | 
 |         success = wps_tag_read(tag) | 
 |         if only_one and success: | 
 |             global continue_loop | 
 |             continue_loop = False | 
 |     else: | 
 |         summary("Not an NDEF tag - remove tag") | 
 |         return True | 
 |  | 
 |     return not no_wait | 
 |  | 
 |  | 
 | def llcp_startup(clf, llc): | 
 |     print("Start LLCP server") | 
 |     global srv | 
 |     srv = HandoverServer(llc) | 
 |     return llc | 
 |  | 
 | def llcp_connected(llc): | 
 |     print("P2P LLCP connected") | 
 |     global wait_connection | 
 |     wait_connection = False | 
 |     global srv | 
 |     srv.start() | 
 |     return True | 
 |  | 
 |  | 
 | def main(): | 
 |     clf = nfc.ContactlessFrontend() | 
 |  | 
 |     parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations') | 
 |     parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, | 
 |                         action='store_const', dest='loglevel', | 
 |                         help='verbose debug output') | 
 |     parser.add_argument('-q', const=logging.WARNING, action='store_const', | 
 |                         dest='loglevel', help='be quiet') | 
 |     parser.add_argument('--only-one', '-1', action='store_true', | 
 |                         help='run only one operation and exit') | 
 |     parser.add_argument('--no-wait', action='store_true', | 
 |                         help='do not wait for tag to be removed before exiting') | 
 |     parser.add_argument('--summary', | 
 |                         help='summary file for writing status updates') | 
 |     parser.add_argument('--success', | 
 |                         help='success file for writing success update') | 
 |     parser.add_argument('command', choices=['write-config', | 
 |                                             'write-password'], | 
 |                         nargs='?') | 
 |     args = parser.parse_args() | 
 |  | 
 |     global only_one | 
 |     only_one = args.only_one | 
 |  | 
 |     global no_wait | 
 |     no_wait = args.no_wait | 
 |  | 
 |     if args.summary: | 
 |         global summary_file | 
 |         summary_file = args.summary | 
 |  | 
 |     if args.success: | 
 |         global success_file | 
 |         success_file = args.success | 
 |  | 
 |     logging.basicConfig(level=args.loglevel) | 
 |  | 
 |     try: | 
 |         if not clf.open("usb"): | 
 |             print("Could not open connection with an NFC device") | 
 |             raise SystemExit | 
 |  | 
 |         if args.command == "write-config": | 
 |             wps_write_config_tag(clf, wait_remove=not args.no_wait) | 
 |             raise SystemExit | 
 |  | 
 |         if args.command == "write-password": | 
 |             wps_write_password_tag(clf, wait_remove=not args.no_wait) | 
 |             raise SystemExit | 
 |  | 
 |         global continue_loop | 
 |         while continue_loop: | 
 |             print("Waiting for a tag or peer to be touched") | 
 |             wait_connection = True | 
 |             try: | 
 |                 if not clf.connect(rdwr={'on-connect': rdwr_connected}, | 
 |                                    llcp={'on-startup': llcp_startup, | 
 |                                          'on-connect': llcp_connected}): | 
 |                     break | 
 |             except Exception as e: | 
 |                 print("clf.connect failed") | 
 |  | 
 |             global srv | 
 |             if only_one and srv and srv.success: | 
 |                 raise SystemExit | 
 |  | 
 |     except KeyboardInterrupt: | 
 |         raise SystemExit | 
 |     finally: | 
 |         clf.close() | 
 |  | 
 |     raise SystemExit | 
 |  | 
 | if __name__ == '__main__': | 
 |     main() |