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