NFC: Add no waiting and no multiple operations options for scripts
[mech_eap.git] / wpa_supplicant / examples / wps-nfc.py
1 #!/usr/bin/python
2 #
3 # Example nfcpy to wpa_supplicant wrapper for WPS NFC operations
4 # Copyright (c) 2012-2013, Jouni Malinen <j@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()