]>
Commit | Line | Data |
---|---|---|
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 | ||
9 | import os | |
10 | import sys | |
11 | import time | |
ab1db08c | 12 | import argparse |
68f51f9a JM |
13 | |
14 | import nfc | |
15 | import nfc.ndef | |
16 | import nfc.llcp | |
17 | import nfc.handover | |
18 | ||
8140ae96 JM |
19 | import logging |
20 | logging.basicConfig() | |
21 | ||
c3aa4da9 | 22 | import wpaspy |
68f51f9a JM |
23 | |
24 | wpas_ctrl = '/var/run/hostapd' | |
6f8fa6e5 | 25 | continue_loop = True |
68f51f9a JM |
26 | |
27 | def wpas_connect(): | |
28 | ifaces = [] | |
29 | if os.path.isdir(wpas_ctrl): | |
30 | try: | |
31 | ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] | |
32 | except OSError, error: | |
33 | print "Could not find hostapd: ", error | |
34 | return None | |
35 | ||
36 | if len(ifaces) < 1: | |
37 | print "No hostapd control interface found" | |
38 | return None | |
39 | ||
40 | for ctrl in ifaces: | |
41 | try: | |
c3aa4da9 | 42 | wpas = wpaspy.Ctrl(ctrl) |
68f51f9a | 43 | return wpas |
c3aa4da9 | 44 | except Exception, e: |
68f51f9a JM |
45 | pass |
46 | return None | |
47 | ||
48 | ||
49 | def wpas_tag_read(message): | |
50 | wpas = wpas_connect() | |
51 | if (wpas == None): | |
52 | return | |
6f8fa6e5 JM |
53 | if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): |
54 | return False | |
55 | return True | |
68f51f9a JM |
56 | |
57 | ||
58 | def wpas_get_config_token(): | |
59 | wpas = wpas_connect() | |
60 | if (wpas == None): | |
61 | return None | |
62 | return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex") | |
63 | ||
64 | ||
65 | def wpas_get_password_token(): | |
66 | wpas = wpas_connect() | |
67 | if (wpas == None): | |
68 | return None | |
69 | return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex") | |
70 | ||
71 | ||
51e985dd | 72 | def wpas_get_handover_sel(): |
68f51f9a JM |
73 | wpas = wpas_connect() |
74 | if (wpas == None): | |
75 | return None | |
51e985dd | 76 | return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex") |
68f51f9a JM |
77 | |
78 | ||
e4758827 | 79 | def wpas_report_handover(req, sel): |
68f51f9a JM |
80 | wpas = wpas_connect() |
81 | if (wpas == None): | |
e4758827 JM |
82 | return None |
83 | return wpas.request("NFC_REPORT_HANDOVER RESP WPS " + | |
84 | str(req).encode("hex") + " " + | |
85 | str(sel).encode("hex")) | |
68f51f9a JM |
86 | |
87 | ||
51e985dd | 88 | class HandoverServer(nfc.handover.HandoverServer): |
6f8fa6e5 JM |
89 | def __init__(self, llc): |
90 | super(HandoverServer, self).__init__(llc) | |
91 | self.ho_server_processing = False | |
92 | self.success = False | |
68f51f9a | 93 | |
51e985dd JM |
94 | def process_request(self, request): |
95 | print "HandoverServer - request received" | |
96 | print "Parsed handover request: " + request.pretty() | |
97 | ||
98 | sel = nfc.ndef.HandoverSelectMessage(version="1.2") | |
99 | ||
100 | for carrier in request.carriers: | |
101 | print "Remote carrier type: " + carrier.type | |
102 | if carrier.type == "application/vnd.wfa.wsc": | |
103 | print "WPS carrier type match - add WPS carrier record" | |
104 | data = wpas_get_handover_sel() | |
105 | if data is None: | |
106 | print "Could not get handover select carrier record from hostapd" | |
107 | continue | |
108 | print "Handover select carrier record from hostapd:" | |
109 | print data.encode("hex") | |
6f8fa6e5 | 110 | wpas_report_handover(carrier.record, data) |
51e985dd JM |
111 | |
112 | message = nfc.ndef.Message(data); | |
113 | sel.add_carrier(message[0], "active", message[1:]) | |
114 | ||
115 | print "Handover select:" | |
116 | print sel.pretty() | |
117 | print str(sel).encode("hex") | |
118 | ||
119 | print "Sending handover select" | |
6f8fa6e5 | 120 | self.success = True |
51e985dd JM |
121 | return sel |
122 | ||
123 | ||
68f51f9a | 124 | def wps_tag_read(tag): |
6f8fa6e5 | 125 | success = False |
68f51f9a | 126 | if len(tag.ndef.message): |
6f8fa6e5 | 127 | for record in tag.ndef.message: |
68f51f9a JM |
128 | print "record type " + record.type |
129 | if record.type == "application/vnd.wfa.wsc": | |
130 | print "WPS tag - send to hostapd" | |
6f8fa6e5 | 131 | success = wpas_tag_read(tag.ndef.message) |
68f51f9a JM |
132 | break |
133 | else: | |
134 | print "Empty tag" | |
135 | ||
6f8fa6e5 | 136 | return success |
68f51f9a JM |
137 | |
138 | ||
6f8fa6e5 JM |
139 | def rdwr_connected_write(tag): |
140 | print "Tag found - writing" | |
141 | global write_data | |
142 | tag.ndef.message = str(write_data) | |
143 | print "Done - remove tag" | |
144 | global only_one | |
145 | if only_one: | |
146 | global continue_loop | |
147 | continue_loop = False | |
148 | global write_wait_remove | |
149 | while write_wait_remove and tag.is_present: | |
150 | time.sleep(0.1) | |
151 | ||
1f1b5b31 | 152 | def wps_write_config_tag(clf, wait_remove=True): |
68f51f9a | 153 | print "Write WPS config token" |
6f8fa6e5 | 154 | global write_data, write_wait_remove |
1f1b5b31 | 155 | write_wait_remove = wait_remove |
6f8fa6e5 JM |
156 | write_data = wpas_get_config_token() |
157 | if write_data == None: | |
68f51f9a JM |
158 | print "Could not get WPS config token from hostapd" |
159 | return | |
160 | ||
161 | print "Touch an NFC tag" | |
6f8fa6e5 | 162 | clf.connect(rdwr={'on-connect': rdwr_connected_write}) |
68f51f9a JM |
163 | |
164 | ||
1f1b5b31 | 165 | def wps_write_password_tag(clf, wait_remove=True): |
68f51f9a | 166 | print "Write WPS password token" |
6f8fa6e5 | 167 | global write_data, write_wait_remove |
1f1b5b31 | 168 | write_wait_remove = wait_remove |
6f8fa6e5 JM |
169 | write_data = wpas_get_password_token() |
170 | if write_data == None: | |
68f51f9a JM |
171 | print "Could not get WPS password token from hostapd" |
172 | return | |
173 | ||
174 | print "Touch an NFC tag" | |
6f8fa6e5 JM |
175 | clf.connect(rdwr={'on-connect': rdwr_connected_write}) |
176 | ||
177 | ||
178 | def rdwr_connected(tag): | |
1f1b5b31 | 179 | global only_one, no_wait |
6f8fa6e5 JM |
180 | print "Tag connected: " + str(tag) |
181 | ||
182 | if tag.ndef: | |
183 | print "NDEF tag: " + tag.type | |
184 | try: | |
185 | print tag.ndef.message.pretty() | |
186 | except Exception, e: | |
187 | print e | |
188 | success = wps_tag_read(tag) | |
189 | if only_one and success: | |
190 | global continue_loop | |
191 | continue_loop = False | |
192 | else: | |
193 | print "Not an NDEF tag - remove tag" | |
68f51f9a | 194 | |
6f8fa6e5 JM |
195 | return not no_wait |
196 | ||
68f51f9a | 197 | |
6f8fa6e5 JM |
198 | def llcp_startup(clf, llc): |
199 | print "Start LLCP server" | |
200 | global srv | |
201 | srv = HandoverServer(llc) | |
202 | return llc | |
68f51f9a | 203 | |
6f8fa6e5 JM |
204 | def llcp_connected(llc): |
205 | print "P2P LLCP connected" | |
206 | global wait_connection | |
207 | wait_connection = False | |
208 | global srv | |
209 | srv.start() | |
210 | return True | |
dc6bda11 JM |
211 | |
212 | ||
68f51f9a JM |
213 | def main(): |
214 | clf = nfc.ContactlessFrontend() | |
215 | ||
ab1db08c JM |
216 | parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations') |
217 | parser.add_argument('--only-one', '-1', action='store_true', | |
218 | help='run only one operation and exit') | |
1f1b5b31 JM |
219 | parser.add_argument('--no-wait', action='store_true', |
220 | help='do not wait for tag to be removed before exiting') | |
ab1db08c JM |
221 | parser.add_argument('command', choices=['write-config', |
222 | 'write-password'], | |
223 | nargs='?') | |
224 | args = parser.parse_args() | |
225 | ||
226 | global only_one | |
227 | only_one = args.only_one | |
228 | ||
1f1b5b31 JM |
229 | global no_wait |
230 | no_wait = args.no_wait | |
231 | ||
68f51f9a | 232 | try: |
6f8fa6e5 JM |
233 | if not clf.open("usb"): |
234 | print "Could not open connection with an NFC device" | |
235 | raise SystemExit | |
236 | ||
ab1db08c | 237 | if args.command == "write-config": |
1f1b5b31 | 238 | wps_write_config_tag(clf, wait_remove=not args.no_wait) |
68f51f9a JM |
239 | raise SystemExit |
240 | ||
ab1db08c | 241 | if args.command == "write-password": |
1f1b5b31 | 242 | wps_write_password_tag(clf, wait_remove=not args.no_wait) |
68f51f9a JM |
243 | raise SystemExit |
244 | ||
6f8fa6e5 JM |
245 | global continue_loop |
246 | while continue_loop: | |
68f51f9a | 247 | print "Waiting for a tag or peer to be touched" |
6f8fa6e5 JM |
248 | wait_connection = True |
249 | try: | |
250 | if not clf.connect(rdwr={'on-connect': rdwr_connected}, | |
251 | llcp={'on-startup': llcp_startup, | |
252 | 'on-connect': llcp_connected}): | |
253 | break | |
254 | except Exception, e: | |
255 | print "clf.connect failed" | |
256 | ||
257 | global srv | |
258 | if only_one and srv and srv.success: | |
259 | raise SystemExit | |
68f51f9a JM |
260 | |
261 | except KeyboardInterrupt: | |
262 | raise SystemExit | |
263 | finally: | |
264 | clf.close() | |
265 | ||
266 | raise SystemExit | |
267 | ||
268 | if __name__ == '__main__': | |
269 | main() |