]> git.ipfire.org Git - thirdparty/hostap.git/blame - hostapd/wps-ap-nfc.py
Add attribute for dwell time in QCA vendor scan
[thirdparty/hostap.git] / hostapd / wps-ap-nfc.py
CommitLineData
68f51f9a
JM
1#!/usr/bin/python
2#
3# Example nfcpy to hostapd 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
ab1db08c 12import argparse
68f51f9a
JM
13
14import nfc
15import nfc.ndef
16import nfc.llcp
17import nfc.handover
18
8140ae96 19import logging
8140ae96 20
c3aa4da9 21import wpaspy
68f51f9a
JM
22
23wpas_ctrl = '/var/run/hostapd'
6f8fa6e5 24continue_loop = True
d6bfaaac
JM
25summary_file = None
26success_file = None
27
28def summary(txt):
89896c00 29 print(txt)
d6bfaaac
JM
30 if summary_file:
31 with open(summary_file, 'a') as f:
32 f.write(txt + "\n")
33
34def success_report(txt):
35 summary(txt)
36 if success_file:
37 with open(success_file, 'a') as f:
38 f.write(txt + "\n")
68f51f9a
JM
39
40def wpas_connect():
41 ifaces = []
42 if os.path.isdir(wpas_ctrl):
43 try:
44 ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
bab493b9 45 except OSError as error:
89896c00 46 print("Could not find hostapd: ", error)
68f51f9a
JM
47 return None
48
49 if len(ifaces) < 1:
89896c00 50 print("No hostapd control interface found")
68f51f9a
JM
51 return None
52
53 for ctrl in ifaces:
54 try:
c3aa4da9 55 wpas = wpaspy.Ctrl(ctrl)
68f51f9a 56 return wpas
bab493b9 57 except Exception as e:
68f51f9a
JM
58 pass
59 return None
60
61
62def wpas_tag_read(message):
63 wpas = wpas_connect()
64 if (wpas == None):
d6bfaaac 65 return False
6f8fa6e5
JM
66 if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
67 return False
68 return True
68f51f9a
JM
69
70
71def wpas_get_config_token():
72 wpas = wpas_connect()
73 if (wpas == None):
74 return None
d6bfaaac
JM
75 ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
76 if "FAIL" in ret:
77 return None
78 return ret.rstrip().decode("hex")
68f51f9a
JM
79
80
81def wpas_get_password_token():
82 wpas = wpas_connect()
83 if (wpas == None):
84 return None
d6bfaaac
JM
85 ret = wpas.request("WPS_NFC_TOKEN NDEF")
86 if "FAIL" in ret:
87 return None
88 return ret.rstrip().decode("hex")
68f51f9a
JM
89
90
51e985dd 91def wpas_get_handover_sel():
68f51f9a
JM
92 wpas = wpas_connect()
93 if (wpas == None):
94 return None
d6bfaaac
JM
95 ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR")
96 if "FAIL" in ret:
97 return None
98 return ret.rstrip().decode("hex")
68f51f9a
JM
99
100
e4758827 101def wpas_report_handover(req, sel):
68f51f9a
JM
102 wpas = wpas_connect()
103 if (wpas == None):
e4758827
JM
104 return None
105 return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
106 str(req).encode("hex") + " " +
107 str(sel).encode("hex"))
68f51f9a
JM
108
109
51e985dd 110class HandoverServer(nfc.handover.HandoverServer):
6f8fa6e5
JM
111 def __init__(self, llc):
112 super(HandoverServer, self).__init__(llc)
113 self.ho_server_processing = False
114 self.success = False
68f51f9a 115
7ae7a84e
JM
116 # override to avoid parser error in request/response.pretty() in nfcpy
117 # due to new WSC handover format
118 def _process_request(self, request):
119 summary("received handover request {}".format(request.type))
120 response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
121 if not request.type == 'urn:nfc:wkt:Hr':
122 summary("not a handover request")
123 else:
124 try:
125 request = nfc.ndef.HandoverRequestMessage(request)
126 except nfc.ndef.DecodeError as e:
127 summary("error decoding 'Hr' message: {}".format(e))
128 else:
129 response = self.process_request(request)
130 summary("send handover response {}".format(response.type))
131 return response
132
51e985dd 133 def process_request(self, request):
d6bfaaac 134 summary("HandoverServer - request received")
12288d84 135 try:
89896c00 136 print("Parsed handover request: " + request.pretty())
bab493b9 137 except Exception as e:
89896c00
MH
138 print(e)
139 print(str(request).encode("hex"))
51e985dd
JM
140
141 sel = nfc.ndef.HandoverSelectMessage(version="1.2")
142
143 for carrier in request.carriers:
89896c00 144 print("Remote carrier type: " + carrier.type)
51e985dd 145 if carrier.type == "application/vnd.wfa.wsc":
d6bfaaac 146 summary("WPS carrier type match - add WPS carrier record")
51e985dd
JM
147 data = wpas_get_handover_sel()
148 if data is None:
d6bfaaac 149 summary("Could not get handover select carrier record from hostapd")
51e985dd 150 continue
89896c00
MH
151 print("Handover select carrier record from hostapd:")
152 print(data.encode("hex"))
d6bfaaac
JM
153 if "OK" in wpas_report_handover(carrier.record, data):
154 success_report("Handover reported successfully")
155 else:
156 summary("Handover report rejected")
51e985dd
JM
157
158 message = nfc.ndef.Message(data);
159 sel.add_carrier(message[0], "active", message[1:])
160
89896c00 161 print("Handover select:")
12288d84 162 try:
89896c00 163 print(sel.pretty())
bab493b9 164 except Exception as e:
89896c00
MH
165 print(e)
166 print(str(sel).encode("hex"))
51e985dd 167
d6bfaaac 168 summary("Sending handover select")
6f8fa6e5 169 self.success = True
51e985dd
JM
170 return sel
171
172
68f51f9a 173def wps_tag_read(tag):
6f8fa6e5 174 success = False
68f51f9a 175 if len(tag.ndef.message):
6f8fa6e5 176 for record in tag.ndef.message:
89896c00 177 print("record type " + record.type)
68f51f9a 178 if record.type == "application/vnd.wfa.wsc":
d6bfaaac 179 summary("WPS tag - send to hostapd")
6f8fa6e5 180 success = wpas_tag_read(tag.ndef.message)
68f51f9a
JM
181 break
182 else:
d6bfaaac
JM
183 summary("Empty tag")
184
185 if success:
186 success_report("Tag read succeeded")
68f51f9a 187
6f8fa6e5 188 return success
68f51f9a
JM
189
190
6f8fa6e5 191def rdwr_connected_write(tag):
d6bfaaac 192 summary("Tag found - writing - " + str(tag))
6f8fa6e5
JM
193 global write_data
194 tag.ndef.message = str(write_data)
d6bfaaac 195 success_report("Tag write succeeded")
89896c00 196 print("Done - remove tag")
6f8fa6e5
JM
197 global only_one
198 if only_one:
199 global continue_loop
200 continue_loop = False
201 global write_wait_remove
202 while write_wait_remove and tag.is_present:
203 time.sleep(0.1)
204
1f1b5b31 205def wps_write_config_tag(clf, wait_remove=True):
d6bfaaac 206 summary("Write WPS config token")
6f8fa6e5 207 global write_data, write_wait_remove
1f1b5b31 208 write_wait_remove = wait_remove
6f8fa6e5
JM
209 write_data = wpas_get_config_token()
210 if write_data == None:
d6bfaaac 211 summary("Could not get WPS config token from hostapd")
68f51f9a
JM
212 return
213
89896c00 214 print("Touch an NFC tag")
6f8fa6e5 215 clf.connect(rdwr={'on-connect': rdwr_connected_write})
68f51f9a
JM
216
217
1f1b5b31 218def wps_write_password_tag(clf, wait_remove=True):
d6bfaaac 219 summary("Write WPS password token")
6f8fa6e5 220 global write_data, write_wait_remove
1f1b5b31 221 write_wait_remove = wait_remove
6f8fa6e5
JM
222 write_data = wpas_get_password_token()
223 if write_data == None:
d6bfaaac 224 summary("Could not get WPS password token from hostapd")
68f51f9a
JM
225 return
226
89896c00 227 print("Touch an NFC tag")
6f8fa6e5
JM
228 clf.connect(rdwr={'on-connect': rdwr_connected_write})
229
230
231def rdwr_connected(tag):
1f1b5b31 232 global only_one, no_wait
d6bfaaac 233 summary("Tag connected: " + str(tag))
6f8fa6e5
JM
234
235 if tag.ndef:
89896c00 236 print("NDEF tag: " + tag.type)
6f8fa6e5 237 try:
89896c00 238 print(tag.ndef.message.pretty())
bab493b9 239 except Exception as e:
89896c00 240 print(e)
6f8fa6e5
JM
241 success = wps_tag_read(tag)
242 if only_one and success:
243 global continue_loop
244 continue_loop = False
245 else:
d6bfaaac
JM
246 summary("Not an NDEF tag - remove tag")
247 return True
68f51f9a 248
6f8fa6e5
JM
249 return not no_wait
250
68f51f9a 251
6f8fa6e5 252def llcp_startup(clf, llc):
89896c00 253 print("Start LLCP server")
6f8fa6e5
JM
254 global srv
255 srv = HandoverServer(llc)
256 return llc
68f51f9a 257
6f8fa6e5 258def llcp_connected(llc):
89896c00 259 print("P2P LLCP connected")
6f8fa6e5
JM
260 global wait_connection
261 wait_connection = False
262 global srv
263 srv.start()
264 return True
dc6bda11
JM
265
266
68f51f9a
JM
267def main():
268 clf = nfc.ContactlessFrontend()
269
ab1db08c 270 parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
6202500f
JM
271 parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
272 action='store_const', dest='loglevel',
273 help='verbose debug output')
274 parser.add_argument('-q', const=logging.WARNING, action='store_const',
275 dest='loglevel', help='be quiet')
ab1db08c
JM
276 parser.add_argument('--only-one', '-1', action='store_true',
277 help='run only one operation and exit')
1f1b5b31
JM
278 parser.add_argument('--no-wait', action='store_true',
279 help='do not wait for tag to be removed before exiting')
d6bfaaac
JM
280 parser.add_argument('--summary',
281 help='summary file for writing status updates')
282 parser.add_argument('--success',
283 help='success file for writing success update')
ab1db08c
JM
284 parser.add_argument('command', choices=['write-config',
285 'write-password'],
286 nargs='?')
287 args = parser.parse_args()
288
289 global only_one
290 only_one = args.only_one
291
1f1b5b31
JM
292 global no_wait
293 no_wait = args.no_wait
294
d6bfaaac
JM
295 if args.summary:
296 global summary_file
297 summary_file = args.summary
298
299 if args.success:
300 global success_file
301 success_file = args.success
302
6202500f
JM
303 logging.basicConfig(level=args.loglevel)
304
68f51f9a 305 try:
6f8fa6e5 306 if not clf.open("usb"):
89896c00 307 print("Could not open connection with an NFC device")
6f8fa6e5
JM
308 raise SystemExit
309
ab1db08c 310 if args.command == "write-config":
1f1b5b31 311 wps_write_config_tag(clf, wait_remove=not args.no_wait)
68f51f9a
JM
312 raise SystemExit
313
ab1db08c 314 if args.command == "write-password":
1f1b5b31 315 wps_write_password_tag(clf, wait_remove=not args.no_wait)
68f51f9a
JM
316 raise SystemExit
317
6f8fa6e5
JM
318 global continue_loop
319 while continue_loop:
89896c00 320 print("Waiting for a tag or peer to be touched")
6f8fa6e5
JM
321 wait_connection = True
322 try:
323 if not clf.connect(rdwr={'on-connect': rdwr_connected},
324 llcp={'on-startup': llcp_startup,
325 'on-connect': llcp_connected}):
326 break
bab493b9 327 except Exception as e:
89896c00 328 print("clf.connect failed")
6f8fa6e5
JM
329
330 global srv
331 if only_one and srv and srv.success:
332 raise SystemExit
68f51f9a
JM
333
334 except KeyboardInterrupt:
335 raise SystemExit
336 finally:
337 clf.close()
338
339 raise SystemExit
340
341if __name__ == '__main__':
342 main()