3 # Example nfcpy to wpa_supplicant wrapper for WPS NFC operations
4 # Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
6 # This software may be distributed under the terms of the BSD license.
7 # See README for more details.
25 wpas_ctrl = '/var/run/wpa_supplicant'
35 with open(summary_file, 'a') as f:
38 def success_report(txt):
41 with open(success_file, 'a') as f:
46 if os.path.isdir(wpas_ctrl):
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
54 print "No wpa_supplicant control interface found"
59 wpas = wpaspy.Ctrl(ctrl)
66 def wpas_tag_read(message):
70 if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
74 def wpas_get_config_token(id=None):
79 ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF " + id)
81 ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
84 return ret.rstrip().decode("hex")
87 def wpas_get_er_config_token(uuid):
91 ret = wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid)
94 return ret.rstrip().decode("hex")
97 def wpas_get_password_token():
101 ret = wpas.request("WPS_NFC_TOKEN NDEF")
104 return ret.rstrip().decode("hex")
106 def wpas_get_handover_req():
107 wpas = wpas_connect()
110 ret = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR")
113 return ret.rstrip().decode("hex")
116 def wpas_get_handover_sel(uuid):
117 wpas = wpas_connect()
121 res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
123 res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip()
126 return res.decode("hex")
129 def wpas_report_handover(req, sel, type):
130 wpas = wpas_connect()
133 return wpas.request("NFC_REPORT_HANDOVER " + type + " WPS " +
134 str(req).encode("hex") + " " +
135 str(sel).encode("hex"))
138 class 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
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")
154 request = nfc.ndef.HandoverRequestMessage(request)
155 except nfc.ndef.DecodeError as e:
156 summary("error decoding 'Hr' message: {}".format(e))
158 response = self.process_request(request)
159 summary("send handover response {}".format(response.type))
162 def process_request(self, request):
163 self.ho_server_processing = True
164 summary("HandoverServer - request received")
166 print "Parsed handover request: " + request.pretty()
170 sel = nfc.ndef.HandoverSelectMessage(version="1.2")
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)
178 summary("Could not get handover select carrier record from wpa_supplicant")
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)")
186 summary("Handover report rejected (responder)")
188 message = nfc.ndef.Message(data);
189 sel.add_carrier(message[0], "active", message[1:])
191 print "Handover select:"
196 print str(sel).encode("hex")
198 summary("Sending handover select")
203 def wps_handover_init(llc):
204 summary("Trying to initiate WPS handover")
206 data = wpas_get_handover_req()
208 summary("Could not get handover request carrier record from wpa_supplicant")
210 print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
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:])
217 print "Handover request:"
219 print message.pretty()
222 print str(message).encode("hex")
224 client = nfc.handover.HandoverClient(llc)
226 summary("Trying to initiate NFC connection handover")
228 summary("Connected for handover")
229 except nfc.llcp.ConnectRefused:
230 summary("Handover connection refused")
234 summary("Other exception: " + str(e))
238 summary("Sending handover request")
240 if not client.send(message):
241 summary("Failed to send handover request")
245 summary("Receiving handover response")
246 message = client._recv()
248 summary("No response received")
251 if message.type != "urn:nfc:wkt:Hs":
252 summary("Response was not Hs - received: " + message.type)
256 print "Received message"
258 print message.pretty()
261 print str(message).encode("hex")
262 message = nfc.ndef.HandoverSelectMessage(message)
263 summary("Handover select received")
265 print message.pretty()
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)")
276 summary("Handover report rejected (initiator)")
277 # nfcpy does not support the new format..
278 #wifi = nfc.ndef.WifiConfigRecord(carrier.record)
283 print "Done with handover"
287 continue_loop = False
291 print "Trying to exit.."
295 def wps_tag_read(tag, wait_remove=True):
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)
308 success_report("Tag read succeeded")
312 while tag.is_present:
318 def rdwr_connected_write(tag):
319 summary("Tag found - writing - " + str(tag))
321 tag.ndef.message = str(write_data)
322 success_report("Tag write succeeded")
323 print "Done - remove tag"
327 continue_loop = False
328 global write_wait_remove
329 while write_wait_remove and tag.is_present:
332 def 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"
341 print "Touch an NFC tag"
342 clf.connect(rdwr={'on-connect': rdwr_connected_write})
345 def 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"
354 print "Touch an NFC tag"
355 clf.connect(rdwr={'on-connect': rdwr_connected_write})
358 def 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"
367 print "Touch an NFC tag"
368 clf.connect(rdwr={'on-connect': rdwr_connected_write})
371 def rdwr_connected(tag):
372 global only_one, no_wait
373 summary("Tag connected: " + str(tag))
376 print "NDEF tag: " + tag.type
378 print tag.ndef.message.pretty()
381 success = wps_tag_read(tag, not only_one)
382 if only_one and success:
384 continue_loop = False
386 summary("Not an NDEF tag - remove tag")
392 def llcp_worker(llc):
395 wps_handover_init(llc)
396 print "Exiting llcp_worker thread"
400 global wait_connection
401 while not wait_connection and srv.sent_carrier is None:
402 if srv.ho_server_processing:
405 def llcp_startup(clf, llc):
408 print "Start LLCP server"
410 srv = HandoverServer(llc)
412 print "Trying to handle WPS handover"
415 print "Trying to handle WPS handover with AP " + arg_uuid
419 def llcp_connected(llc):
420 print "P2P LLCP connected"
421 global wait_connection
422 wait_connection = False
428 threading.Thread(target=llcp_worker, args=(llc,)).start()
429 print "llcp_connected returning"
433 def terminate_loop():
438 clf = nfc.ContactlessFrontend()
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',
462 args = parser.parse_args()
468 only_one = args.only_one
471 no_wait = args.no_wait
475 summary_file = args.summary
479 success_file = args.success
481 logging.basicConfig(level=args.loglevel)
484 if not clf.open("usb"):
485 print "Could not open connection with an NFC device"
488 if args.command == "write-config":
489 wps_write_config_tag(clf, id=args.id, wait_remove=not args.no_wait)
492 if args.command == "write-er-config":
493 wps_write_er_config_tag(clf, args.uuid, wait_remove=not args.no_wait)
496 if args.command == "write-password":
497 wps_write_password_tag(clf, wait_remove=not args.no_wait)
502 print "Waiting for a tag or peer to be touched"
503 wait_connection = True
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):
511 print "clf.connect failed"
514 if only_one and srv and srv.success:
517 except KeyboardInterrupt:
524 if __name__ == '__main__':