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