]> git.ipfire.org Git - thirdparty/hostap.git/blob - wpa_supplicant/examples/p2p-nfc.py
tests: Use python3 compatible "except" statement
[thirdparty/hostap.git] / wpa_supplicant / examples / p2p-nfc.py
1 #!/usr/bin/python
2 #
3 # Example nfcpy to wpa_supplicant wrapper for P2P 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 threading
14 import argparse
15
16 import nfc
17 import nfc.ndef
18 import nfc.llcp
19 import nfc.handover
20
21 import logging
22
23 import wpaspy
24
25 wpas_ctrl = '/var/run/wpa_supplicant'
26 ifname = None
27 init_on_touch = False
28 in_raw_mode = False
29 prev_tcgetattr = 0
30 include_wps_req = True
31 include_p2p_req = True
32 no_input = False
33 srv = None
34 continue_loop = True
35 terminate_now = False
36 summary_file = None
37 success_file = None
38
39 def summary(txt):
40 print txt
41 if summary_file:
42 with open(summary_file, 'a') as f:
43 f.write(txt + "\n")
44
45 def success_report(txt):
46 summary(txt)
47 if success_file:
48 with open(success_file, 'a') as f:
49 f.write(txt + "\n")
50
51 def wpas_connect():
52 ifaces = []
53 if os.path.isdir(wpas_ctrl):
54 try:
55 ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
56 except OSError as error:
57 print "Could not find wpa_supplicant: ", error
58 return None
59
60 if len(ifaces) < 1:
61 print "No wpa_supplicant control interface found"
62 return None
63
64 for ctrl in ifaces:
65 if ifname:
66 if ifname not in ctrl:
67 continue
68 try:
69 print "Trying to use control interface " + ctrl
70 wpas = wpaspy.Ctrl(ctrl)
71 return wpas
72 except Exception as e:
73 pass
74 return None
75
76
77 def wpas_tag_read(message):
78 wpas = wpas_connect()
79 if (wpas == None):
80 return False
81 cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex")
82 global force_freq
83 if force_freq:
84 cmd = cmd + " freq=" + force_freq
85 if "FAIL" in wpas.request(cmd):
86 return False
87 return True
88
89
90 def wpas_get_handover_req():
91 wpas = wpas_connect()
92 if (wpas == None):
93 return None
94 res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
95 if "FAIL" in res:
96 return None
97 return res.decode("hex")
98
99 def wpas_get_handover_req_wps():
100 wpas = wpas_connect()
101 if (wpas == None):
102 return None
103 res = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
104 if "FAIL" in res:
105 return None
106 return res.decode("hex")
107
108
109 def wpas_get_handover_sel(tag=False):
110 wpas = wpas_connect()
111 if (wpas == None):
112 return None
113 if tag:
114 res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
115 else:
116 res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
117 if "FAIL" in res:
118 return None
119 return res.decode("hex")
120
121
122 def wpas_get_handover_sel_wps():
123 wpas = wpas_connect()
124 if (wpas == None):
125 return None
126 res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR");
127 if "FAIL" in res:
128 return None
129 return res.rstrip().decode("hex")
130
131
132 def wpas_report_handover(req, sel, type):
133 wpas = wpas_connect()
134 if (wpas == None):
135 return None
136 cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex")
137 global force_freq
138 if force_freq:
139 cmd = cmd + " freq=" + force_freq
140 return wpas.request(cmd)
141
142
143 def wpas_report_handover_wsc(req, sel, type):
144 wpas = wpas_connect()
145 if (wpas == None):
146 return None
147 cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex")
148 if force_freq:
149 cmd = cmd + " freq=" + force_freq
150 return wpas.request(cmd)
151
152
153 def p2p_handover_client(llc):
154 message = nfc.ndef.HandoverRequestMessage(version="1.2")
155 message.nonce = random.randint(0, 0xffff)
156
157 global include_p2p_req
158 if include_p2p_req:
159 data = wpas_get_handover_req()
160 if (data == None):
161 summary("Could not get handover request carrier record from wpa_supplicant")
162 return
163 print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
164 datamsg = nfc.ndef.Message(data)
165 message.add_carrier(datamsg[0], "active", datamsg[1:])
166
167 global include_wps_req
168 if include_wps_req:
169 print "Handover request (pre-WPS):"
170 try:
171 print message.pretty()
172 except Exception as e:
173 print e
174
175 data = wpas_get_handover_req_wps()
176 if data:
177 print "Add WPS request in addition to P2P"
178 datamsg = nfc.ndef.Message(data)
179 message.add_carrier(datamsg[0], "active", datamsg[1:])
180
181 print "Handover request:"
182 try:
183 print message.pretty()
184 except Exception as e:
185 print e
186 print str(message).encode("hex")
187
188 client = nfc.handover.HandoverClient(llc)
189 try:
190 summary("Trying to initiate NFC connection handover")
191 client.connect()
192 summary("Connected for handover")
193 except nfc.llcp.ConnectRefused:
194 summary("Handover connection refused")
195 client.close()
196 return
197 except Exception as e:
198 summary("Other exception: " + str(e))
199 client.close()
200 return
201
202 summary("Sending handover request")
203
204 if not client.send(message):
205 summary("Failed to send handover request")
206 client.close()
207 return
208
209 summary("Receiving handover response")
210 message = client._recv()
211 if message is None:
212 summary("No response received")
213 client.close()
214 return
215 if message.type != "urn:nfc:wkt:Hs":
216 summary("Response was not Hs - received: " + message.type)
217 client.close()
218 return
219
220 print "Received message"
221 try:
222 print message.pretty()
223 except Exception as e:
224 print e
225 print str(message).encode("hex")
226 message = nfc.ndef.HandoverSelectMessage(message)
227 summary("Handover select received")
228 try:
229 print message.pretty()
230 except Exception as e:
231 print e
232
233 for carrier in message.carriers:
234 print "Remote carrier type: " + carrier.type
235 if carrier.type == "application/vnd.wfa.p2p":
236 print "P2P carrier type match - send to wpa_supplicant"
237 if "OK" in wpas_report_handover(data, carrier.record, "INIT"):
238 success_report("P2P handover reported successfully (initiator)")
239 else:
240 summary("P2P handover report rejected")
241 break
242
243 print "Remove peer"
244 client.close()
245 print "Done with handover"
246 global only_one
247 if only_one:
248 print "only_one -> stop loop"
249 global continue_loop
250 continue_loop = False
251
252 global no_wait
253 if no_wait:
254 print "Trying to exit.."
255 global terminate_now
256 terminate_now = True
257
258
259 class HandoverServer(nfc.handover.HandoverServer):
260 def __init__(self, llc):
261 super(HandoverServer, self).__init__(llc)
262 self.sent_carrier = None
263 self.ho_server_processing = False
264 self.success = False
265
266 # override to avoid parser error in request/response.pretty() in nfcpy
267 # due to new WSC handover format
268 def _process_request(self, request):
269 summary("received handover request {}".format(request.type))
270 response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
271 if not request.type == 'urn:nfc:wkt:Hr':
272 summary("not a handover request")
273 else:
274 try:
275 request = nfc.ndef.HandoverRequestMessage(request)
276 except nfc.ndef.DecodeError as e:
277 summary("error decoding 'Hr' message: {}".format(e))
278 else:
279 response = self.process_request(request)
280 summary("send handover response {}".format(response.type))
281 return response
282
283 def process_request(self, request):
284 self.ho_server_processing = True
285 clear_raw_mode()
286 print "HandoverServer - request received"
287 try:
288 print "Parsed handover request: " + request.pretty()
289 except Exception as e:
290 print e
291
292 sel = nfc.ndef.HandoverSelectMessage(version="1.2")
293
294 found = False
295
296 for carrier in request.carriers:
297 print "Remote carrier type: " + carrier.type
298 if carrier.type == "application/vnd.wfa.p2p":
299 print "P2P carrier type match - add P2P carrier record"
300 found = True
301 self.received_carrier = carrier.record
302 print "Carrier record:"
303 try:
304 print carrier.record.pretty()
305 except Exception as e:
306 print e
307 data = wpas_get_handover_sel()
308 if data is None:
309 print "Could not get handover select carrier record from wpa_supplicant"
310 continue
311 print "Handover select carrier record from wpa_supplicant:"
312 print data.encode("hex")
313 self.sent_carrier = data
314 if "OK" in wpas_report_handover(self.received_carrier, self.sent_carrier, "RESP"):
315 success_report("P2P handover reported successfully (responder)")
316 else:
317 summary("P2P handover report rejected")
318 break
319
320 message = nfc.ndef.Message(data);
321 sel.add_carrier(message[0], "active", message[1:])
322 break
323
324 for carrier in request.carriers:
325 if found:
326 break
327 print "Remote carrier type: " + carrier.type
328 if carrier.type == "application/vnd.wfa.wsc":
329 print "WSC carrier type match - add WSC carrier record"
330 found = True
331 self.received_carrier = carrier.record
332 print "Carrier record:"
333 try:
334 print carrier.record.pretty()
335 except Exception as e:
336 print e
337 data = wpas_get_handover_sel_wps()
338 if data is None:
339 print "Could not get handover select carrier record from wpa_supplicant"
340 continue
341 print "Handover select carrier record from wpa_supplicant:"
342 print data.encode("hex")
343 self.sent_carrier = data
344 if "OK" in wpas_report_handover_wsc(self.received_carrier, self.sent_carrier, "RESP"):
345 success_report("WSC handover reported successfully")
346 else:
347 summary("WSC handover report rejected")
348 break
349
350 message = nfc.ndef.Message(data);
351 sel.add_carrier(message[0], "active", message[1:])
352 found = True
353 break
354
355 print "Handover select:"
356 try:
357 print sel.pretty()
358 except Exception as e:
359 print e
360 print str(sel).encode("hex")
361
362 summary("Sending handover select")
363 self.success = True
364 return sel
365
366
367 def clear_raw_mode():
368 import sys, tty, termios
369 global prev_tcgetattr, in_raw_mode
370 if not in_raw_mode:
371 return
372 fd = sys.stdin.fileno()
373 termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
374 in_raw_mode = False
375
376
377 def getch():
378 import sys, tty, termios, select
379 global prev_tcgetattr, in_raw_mode
380 fd = sys.stdin.fileno()
381 prev_tcgetattr = termios.tcgetattr(fd)
382 ch = None
383 try:
384 tty.setraw(fd)
385 in_raw_mode = True
386 [i, o, e] = select.select([fd], [], [], 0.05)
387 if i:
388 ch = sys.stdin.read(1)
389 finally:
390 termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
391 in_raw_mode = False
392 return ch
393
394
395 def p2p_tag_read(tag):
396 success = False
397 if len(tag.ndef.message):
398 for record in tag.ndef.message:
399 print "record type " + record.type
400 if record.type == "application/vnd.wfa.wsc":
401 summary("WPS tag - send to wpa_supplicant")
402 success = wpas_tag_read(tag.ndef.message)
403 break
404 if record.type == "application/vnd.wfa.p2p":
405 summary("P2P tag - send to wpa_supplicant")
406 success = wpas_tag_read(tag.ndef.message)
407 break
408 else:
409 summary("Empty tag")
410
411 if success:
412 success_report("Tag read succeeded")
413
414 return success
415
416
417 def rdwr_connected_p2p_write(tag):
418 summary("Tag found - writing - " + str(tag))
419 global p2p_sel_data
420 tag.ndef.message = str(p2p_sel_data)
421 success_report("Tag write succeeded")
422 print "Done - remove tag"
423 global only_one
424 if only_one:
425 global continue_loop
426 continue_loop = False
427 global p2p_sel_wait_remove
428 return p2p_sel_wait_remove
429
430 def wps_write_p2p_handover_sel(clf, wait_remove=True):
431 print "Write P2P handover select"
432 data = wpas_get_handover_sel(tag=True)
433 if (data == None):
434 summary("Could not get P2P handover select from wpa_supplicant")
435 return
436
437 global p2p_sel_wait_remove
438 p2p_sel_wait_remove = wait_remove
439 global p2p_sel_data
440 p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2")
441 message = nfc.ndef.Message(data);
442 p2p_sel_data.add_carrier(message[0], "active", message[1:])
443 print "Handover select:"
444 try:
445 print p2p_sel_data.pretty()
446 except Exception as e:
447 print e
448 print str(p2p_sel_data).encode("hex")
449
450 print "Touch an NFC tag"
451 clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write})
452
453
454 def rdwr_connected(tag):
455 global only_one, no_wait
456 summary("Tag connected: " + str(tag))
457
458 if tag.ndef:
459 print "NDEF tag: " + tag.type
460 try:
461 print tag.ndef.message.pretty()
462 except Exception as e:
463 print e
464 success = p2p_tag_read(tag)
465 if only_one and success:
466 global continue_loop
467 continue_loop = False
468 else:
469 summary("Not an NDEF tag - remove tag")
470 return True
471
472 return not no_wait
473
474
475 def llcp_worker(llc):
476 global init_on_touch
477 if init_on_touch:
478 print "Starting handover client"
479 p2p_handover_client(llc)
480 return
481
482 global no_input
483 if no_input:
484 print "Wait for handover to complete"
485 else:
486 print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)"
487 global srv
488 global wait_connection
489 while not wait_connection and srv.sent_carrier is None:
490 if srv.ho_server_processing:
491 time.sleep(0.025)
492 elif no_input:
493 time.sleep(0.5)
494 else:
495 global include_wps_req, include_p2p_req
496 res = getch()
497 if res == 'i':
498 include_wps_req = True
499 include_p2p_req = True
500 elif res == 'p':
501 include_wps_req = False
502 include_p2p_req = True
503 elif res == 'w':
504 include_wps_req = True
505 include_p2p_req = False
506 else:
507 continue
508 clear_raw_mode()
509 print "Starting handover client"
510 p2p_handover_client(llc)
511 return
512
513 clear_raw_mode()
514 print "Exiting llcp_worker thread"
515
516 def llcp_startup(clf, llc):
517 print "Start LLCP server"
518 global srv
519 srv = HandoverServer(llc)
520 return llc
521
522 def llcp_connected(llc):
523 print "P2P LLCP connected"
524 global wait_connection
525 wait_connection = False
526 global init_on_touch
527 if not init_on_touch:
528 global srv
529 srv.start()
530 if init_on_touch or not no_input:
531 threading.Thread(target=llcp_worker, args=(llc,)).start()
532 return True
533
534 def terminate_loop():
535 global terminate_now
536 return terminate_now
537
538 def main():
539 clf = nfc.ContactlessFrontend()
540
541 parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and WPS NFC operations')
542 parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
543 action='store_const', dest='loglevel',
544 help='verbose debug output')
545 parser.add_argument('-q', const=logging.WARNING, action='store_const',
546 dest='loglevel', help='be quiet')
547 parser.add_argument('--only-one', '-1', action='store_true',
548 help='run only one operation and exit')
549 parser.add_argument('--init-on-touch', '-I', action='store_true',
550 help='initiate handover on touch')
551 parser.add_argument('--no-wait', action='store_true',
552 help='do not wait for tag to be removed before exiting')
553 parser.add_argument('--ifname', '-i',
554 help='network interface name')
555 parser.add_argument('--no-wps-req', '-N', action='store_true',
556 help='do not include WPS carrier record in request')
557 parser.add_argument('--no-input', '-a', action='store_true',
558 help='do not use stdout input to initiate handover')
559 parser.add_argument('--tag-read-only', '-t', action='store_true',
560 help='tag read only (do not allow connection handover)')
561 parser.add_argument('--handover-only', action='store_true',
562 help='connection handover only (do not allow tag read)')
563 parser.add_argument('--freq', '-f',
564 help='forced frequency of operating channel in MHz')
565 parser.add_argument('--summary',
566 help='summary file for writing status updates')
567 parser.add_argument('--success',
568 help='success file for writing success update')
569 parser.add_argument('command', choices=['write-p2p-sel'],
570 nargs='?')
571 args = parser.parse_args()
572
573 global only_one
574 only_one = args.only_one
575
576 global no_wait
577 no_wait = args.no_wait
578
579 global force_freq
580 force_freq = args.freq
581
582 logging.basicConfig(level=args.loglevel)
583
584 global init_on_touch
585 init_on_touch = args.init_on_touch
586
587 if args.ifname:
588 global ifname
589 ifname = args.ifname
590 print "Selected ifname " + ifname
591
592 if args.no_wps_req:
593 global include_wps_req
594 include_wps_req = False
595
596 if args.summary:
597 global summary_file
598 summary_file = args.summary
599
600 if args.success:
601 global success_file
602 success_file = args.success
603
604 if args.no_input:
605 global no_input
606 no_input = True
607
608 clf = nfc.ContactlessFrontend()
609 global wait_connection
610
611 try:
612 if not clf.open("usb"):
613 print "Could not open connection with an NFC device"
614 raise SystemExit
615
616 if args.command == "write-p2p-sel":
617 wps_write_p2p_handover_sel(clf, wait_remove=not args.no_wait)
618 raise SystemExit
619
620 global continue_loop
621 while continue_loop:
622 print "Waiting for a tag or peer to be touched"
623 wait_connection = True
624 try:
625 if args.tag_read_only:
626 if not clf.connect(rdwr={'on-connect': rdwr_connected}):
627 break
628 elif args.handover_only:
629 if not clf.connect(llcp={'on-startup': llcp_startup,
630 'on-connect': llcp_connected},
631 terminate=terminate_loop):
632 break
633 else:
634 if not clf.connect(rdwr={'on-connect': rdwr_connected},
635 llcp={'on-startup': llcp_startup,
636 'on-connect': llcp_connected},
637 terminate=terminate_loop):
638 break
639 except Exception as e:
640 print "clf.connect failed"
641
642 global srv
643 if only_one and srv and srv.success:
644 raise SystemExit
645
646 except KeyboardInterrupt:
647 raise SystemExit
648 finally:
649 clf.close()
650
651 raise SystemExit
652
653 if __name__ == '__main__':
654 main()