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