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