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