Home | History | Annotate | Download | only in examples
      1 #!/usr/bin/python
      2 #
      3 # Example nfcpy to wpa_supplicant wrapper for WPS NFC operations
      4 # Copyright (c) 2012-2013, Jouni Malinen <j (at] w1.fi>
      5 #
      6 # This software may be distributed under the terms of the BSD license.
      7 # See README for more details.
      8 
      9 import os
     10 import sys
     11 import time
     12 import random
     13 import StringIO
     14 
     15 import nfc
     16 import nfc.ndef
     17 import nfc.llcp
     18 import nfc.handover
     19 
     20 import logging
     21 logging.basicConfig()
     22 
     23 import wpaspy
     24 
     25 wpas_ctrl = '/var/run/wpa_supplicant'
     26 
     27 def wpas_connect():
     28     ifaces = []
     29     if os.path.isdir(wpas_ctrl):
     30         try:
     31             ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
     32         except OSError, error:
     33             print "Could not find wpa_supplicant: ", error
     34             return None
     35 
     36     if len(ifaces) < 1:
     37         print "No wpa_supplicant control interface found"
     38         return None
     39 
     40     for ctrl in ifaces:
     41         try:
     42             wpas = wpaspy.Ctrl(ctrl)
     43             return wpas
     44         except Exception, e:
     45             pass
     46     return None
     47 
     48 
     49 def wpas_tag_read(message):
     50     wpas = wpas_connect()
     51     if (wpas == None):
     52         return False
     53     if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + message.encode("hex")):
     54         return False
     55     return True
     56 
     57 def wpas_get_config_token(id=None):
     58     wpas = wpas_connect()
     59     if (wpas == None):
     60         return None
     61     if id:
     62         ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF " + id)
     63     else:
     64         ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
     65     if "FAIL" in ret:
     66         return None
     67     return ret.rstrip().decode("hex")
     68 
     69 
     70 def wpas_get_er_config_token(uuid):
     71     wpas = wpas_connect()
     72     if (wpas == None):
     73         return None
     74     return wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid).rstrip().decode("hex")
     75 
     76 
     77 def wpas_get_password_token():
     78     wpas = wpas_connect()
     79     if (wpas == None):
     80         return None
     81     return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
     82 
     83 
     84 def wpas_get_handover_req():
     85     wpas = wpas_connect()
     86     if (wpas == None):
     87         return None
     88     return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex")
     89 
     90 
     91 def wpas_get_handover_sel(uuid):
     92     wpas = wpas_connect()
     93     if (wpas == None):
     94         return None
     95     if uuid is None:
     96         return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
     97     return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip().decode("hex")
     98 
     99 
    100 def wpas_report_handover(req, sel, type):
    101     wpas = wpas_connect()
    102     if (wpas == None):
    103         return None
    104     return wpas.request("NFC_REPORT_HANDOVER " + type + " WPS " +
    105                         str(req).encode("hex") + " " +
    106                         str(sel).encode("hex"))
    107 
    108 
    109 class HandoverServer(nfc.handover.HandoverServer):
    110     def __init__(self):
    111         super(HandoverServer, self).__init__()
    112 
    113     def process_request(self, request):
    114         print "HandoverServer - request received"
    115         print "Parsed handover request: " + request.pretty()
    116 
    117         sel = nfc.ndef.HandoverSelectMessage(version="1.2")
    118 
    119         for carrier in request.carriers:
    120             print "Remote carrier type: " + carrier.type
    121             if carrier.type == "application/vnd.wfa.wsc":
    122                 print "WPS carrier type match - add WPS carrier record"
    123                 self.received_carrier = carrier.record
    124                 data = wpas_get_handover_sel(self.uuid)
    125                 if data is None:
    126                     print "Could not get handover select carrier record from wpa_supplicant"
    127                     continue
    128                 print "Handover select carrier record from wpa_supplicant:"
    129                 print data.encode("hex")
    130                 self.sent_carrier = data
    131 
    132                 message = nfc.ndef.Message(data);
    133                 sel.add_carrier(message[0], "active", message[1:])
    134 
    135         print "Handover select:"
    136         print sel.pretty()
    137         print str(sel).encode("hex")
    138 
    139         print "Sending handover select"
    140         return sel
    141 
    142 
    143 def wps_handover_resp(peer, uuid):
    144     if uuid is None:
    145         print "Trying to handle WPS handover"
    146     else:
    147         print "Trying to handle WPS handover with AP " + uuid
    148 
    149     srv = HandoverServer()
    150     srv.sent_carrier = None
    151     srv.uuid = uuid
    152 
    153     nfc.llcp.activate(peer);
    154 
    155     try:
    156         print "Trying handover";
    157         srv.start()
    158         print "Wait for disconnect"
    159         while nfc.llcp.connected():
    160             time.sleep(0.1)
    161         print "Disconnected after handover"
    162     except nfc.llcp.ConnectRefused:
    163         print "Handover connection refused"
    164         nfc.llcp.shutdown()
    165         return
    166 
    167     if srv.sent_carrier:
    168         wpas_report_handover(srv.received_carrier, srv.sent_carrier, "RESP")
    169 
    170     print "Remove peer"
    171     nfc.llcp.shutdown()
    172     print "Done with handover"
    173     time.sleep(1)
    174 
    175 
    176 def wps_handover_init(peer):
    177     print "Trying to initiate WPS handover"
    178 
    179     data = wpas_get_handover_req()
    180     if (data == None):
    181         print "Could not get handover request carrier record from wpa_supplicant"
    182         return
    183     print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
    184     record = nfc.ndef.Record()
    185     f = StringIO.StringIO(data)
    186     record._read(f)
    187     record = nfc.ndef.HandoverCarrierRecord(record)
    188     print "Parsed handover request carrier record:"
    189     print record.pretty()
    190 
    191     message = nfc.ndef.HandoverRequestMessage(version="1.2")
    192     message.nonce = random.randint(0, 0xffff)
    193     message.add_carrier(record, "active")
    194 
    195     print "Handover request:"
    196     print message.pretty()
    197 
    198     nfc.llcp.activate(peer);
    199 
    200     client = nfc.handover.HandoverClient()
    201     try:
    202         print "Trying handover";
    203         client.connect()
    204         print "Connected for handover"
    205     except nfc.llcp.ConnectRefused:
    206         print "Handover connection refused"
    207         nfc.llcp.shutdown()
    208         client.close()
    209         return
    210 
    211     print "Sending handover request"
    212 
    213     if not client.send(message):
    214         print "Failed to send handover request"
    215 
    216     print "Receiving handover response"
    217     message = client._recv()
    218     if message is None:
    219         print "No response received"
    220         nfc.llcp.shutdown()
    221         client.close()
    222         return
    223     if message.type != "urn:nfc:wkt:Hs":
    224         print "Response was not Hs - received: " + message.type
    225         nfc.llcp.shutdown()
    226         client.close()
    227         return
    228 
    229     print "Received message"
    230     print message.pretty()
    231     message = nfc.ndef.HandoverSelectMessage(message)
    232     print "Handover select received"
    233     print message.pretty()
    234 
    235     for carrier in message.carriers:
    236         print "Remote carrier type: " + carrier.type
    237         if carrier.type == "application/vnd.wfa.wsc":
    238             print "WPS carrier type match - send to wpa_supplicant"
    239             wpas_report_handover(data, carrier.record, "INIT")
    240             wifi = nfc.ndef.WifiConfigRecord(carrier.record)
    241             print wifi.pretty()
    242 
    243     print "Remove peer"
    244     nfc.llcp.shutdown()
    245     client.close()
    246     print "Done with handover"
    247 
    248 
    249 def wps_tag_read(tag, wait_remove=True):
    250     success = False
    251     if len(tag.ndef.message):
    252         message = nfc.ndef.Message(tag.ndef.message)
    253         print "message type " + message.type
    254 
    255         for record in message:
    256             print "record type " + record.type
    257             if record.type == "application/vnd.wfa.wsc":
    258                 print "WPS tag - send to wpa_supplicant"
    259                 success = wpas_tag_read(tag.ndef.message)
    260                 break
    261     else:
    262         print "Empty tag"
    263 
    264     if wait_remove:
    265         print "Remove tag"
    266         while tag.is_present:
    267             time.sleep(0.1)
    268 
    269     return success
    270 
    271 
    272 def wps_write_config_tag(clf, id=None, wait_remove=True):
    273     print "Write WPS config token"
    274     data = wpas_get_config_token(id)
    275     if (data == None):
    276         print "Could not get WPS config token from wpa_supplicant"
    277         sys.exit(1)
    278         return
    279 
    280     print "Touch an NFC tag"
    281     while True:
    282         tag = clf.poll()
    283         if tag == None:
    284             time.sleep(0.1)
    285             continue
    286         break
    287 
    288     print "Tag found - writing"
    289     tag.ndef.message = data
    290     print "Done - remove tag"
    291     while wait_remove and tag.is_present:
    292         time.sleep(0.1)
    293 
    294 
    295 def wps_write_er_config_tag(clf, uuid):
    296     print "Write WPS ER config token"
    297     data = wpas_get_er_config_token(uuid)
    298     if (data == None):
    299         print "Could not get WPS config token from wpa_supplicant"
    300         return
    301 
    302     print "Touch an NFC tag"
    303     while True:
    304         tag = clf.poll()
    305         if tag == None:
    306             time.sleep(0.1)
    307             continue
    308         break
    309 
    310     print "Tag found - writing"
    311     tag.ndef.message = data
    312     print "Done - remove tag"
    313     while tag.is_present:
    314         time.sleep(0.1)
    315 
    316 
    317 def wps_write_password_tag(clf, wait_remove=True):
    318     print "Write WPS password token"
    319     data = wpas_get_password_token()
    320     if (data == None):
    321         print "Could not get WPS password token from wpa_supplicant"
    322         return
    323 
    324     print "Touch an NFC tag"
    325     while True:
    326         tag = clf.poll()
    327         if tag == None:
    328             time.sleep(0.1)
    329             continue
    330         break
    331 
    332     print "Tag found - writing"
    333     tag.ndef.message = data
    334     print "Done - remove tag"
    335     while wait_remove and tag.is_present:
    336         time.sleep(0.1)
    337 
    338 
    339 def find_peer(clf):
    340     while True:
    341         if nfc.llcp.connected():
    342             print "LLCP connected"
    343         general_bytes = nfc.llcp.startup({})
    344         peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
    345         if isinstance(peer, nfc.DEP):
    346             print "listen -> DEP";
    347             if peer.general_bytes.startswith("Ffm"):
    348                 print "Found DEP"
    349                 return peer
    350             print "mismatch in general_bytes"
    351             print peer.general_bytes
    352 
    353         peer = clf.poll(general_bytes)
    354         if isinstance(peer, nfc.DEP):
    355             print "poll -> DEP";
    356             if peer.general_bytes.startswith("Ffm"):
    357                 print "Found DEP"
    358                 return peer
    359             print "mismatch in general_bytes"
    360             print peer.general_bytes
    361 
    362         if peer:
    363             print "Found tag"
    364             return peer
    365 
    366 
    367 def main():
    368     clf = nfc.ContactlessFrontend()
    369 
    370     try:
    371         arg_uuid = None
    372         if len(sys.argv) > 1 and sys.argv[1] != '-1':
    373             arg_uuid = sys.argv[1]
    374 
    375         if len(sys.argv) > 1 and sys.argv[1] == '-1':
    376             only_one = True
    377         else:
    378             only_one = False
    379 
    380         if len(sys.argv) > 1 and sys.argv[1] == "write-config":
    381             wps_write_config_tag(clf)
    382             raise SystemExit
    383 
    384         if len(sys.argv) > 1 and sys.argv[1] == "write-config-no-wait":
    385             wps_write_config_tag(clf, wait_remove=False)
    386             raise SystemExit
    387 
    388         if len(sys.argv) > 2 and sys.argv[1] == "write-config-id":
    389             wps_write_config_tag(clf, sys.argv[2])
    390             raise SystemExit
    391 
    392         if len(sys.argv) > 2 and sys.argv[1] == "write-er-config":
    393             wps_write_er_config_tag(clf, sys.argv[2])
    394             raise SystemExit
    395 
    396         if len(sys.argv) > 1 and sys.argv[1] == "write-password":
    397             wps_write_password_tag(clf)
    398             raise SystemExit
    399 
    400         if len(sys.argv) > 1 and sys.argv[1] == "write-password-no-wait":
    401             wps_write_password_tag(clf, wait_remove=False)
    402             raise SystemExit
    403 
    404         while True:
    405             print "Waiting for a tag or peer to be touched"
    406 
    407             tag = find_peer(clf)
    408             if isinstance(tag, nfc.DEP):
    409                 if arg_uuid is None:
    410                     wps_handover_init(tag)
    411                 elif arg_uuid is "ap":
    412                     wps_handover_resp(tag, None)
    413                 else:
    414                     wps_handover_resp(tag, arg_uuid)
    415                 if only_one:
    416                     break
    417                 continue
    418 
    419             if tag.ndef:
    420                 success = wps_tag_read(tag, not only_one)
    421                 if only_one:
    422                     if not success:
    423                         sys.exit(1)
    424                     break
    425                 continue
    426 
    427             print "Not an NDEF tag - remove tag"
    428             if only_one:
    429                 sys.exit(1)
    430             while tag.is_present:
    431                 time.sleep(0.1)
    432 
    433     except KeyboardInterrupt:
    434         raise SystemExit
    435     finally:
    436         clf.close()
    437 
    438     raise SystemExit
    439 
    440 if __name__ == '__main__':
    441     main()
    442