blob: 58e538a45f906880ff4803ad9b3897f4d9297722 [file] [log] [blame]
Dmitry Shmidtf8623282013-02-20 14:34:59 -08001#!/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
Dmitry Shmidtcf32e602014-01-28 10:57:39 -080012import argparse
Dmitry Shmidtf8623282013-02-20 14:34:59 -080013
14import nfc
15import nfc.ndef
16import nfc.llcp
17import nfc.handover
18
19import logging
Dmitry Shmidtf8623282013-02-20 14:34:59 -080020
Dmitry Shmidt700a1372013-03-15 14:14:44 -070021import wpaspy
Dmitry Shmidtf8623282013-02-20 14:34:59 -080022
23wpas_ctrl = '/var/run/hostapd'
Dmitry Shmidtcf32e602014-01-28 10:57:39 -080024continue_loop = True
Dmitry Shmidtf8623282013-02-20 14:34:59 -080025
26def wpas_connect():
27 ifaces = []
28 if os.path.isdir(wpas_ctrl):
29 try:
30 ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
31 except OSError, error:
32 print "Could not find hostapd: ", error
33 return None
34
35 if len(ifaces) < 1:
36 print "No hostapd control interface found"
37 return None
38
39 for ctrl in ifaces:
40 try:
Dmitry Shmidt700a1372013-03-15 14:14:44 -070041 wpas = wpaspy.Ctrl(ctrl)
Dmitry Shmidtf8623282013-02-20 14:34:59 -080042 return wpas
Dmitry Shmidt700a1372013-03-15 14:14:44 -070043 except Exception, e:
Dmitry Shmidtf8623282013-02-20 14:34:59 -080044 pass
45 return None
46
47
48def wpas_tag_read(message):
49 wpas = wpas_connect()
50 if (wpas == None):
51 return
Dmitry Shmidtcf32e602014-01-28 10:57:39 -080052 if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
53 return False
54 return True
Dmitry Shmidtf8623282013-02-20 14:34:59 -080055
56
57def wpas_get_config_token():
58 wpas = wpas_connect()
59 if (wpas == None):
60 return None
61 return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex")
62
63
64def wpas_get_password_token():
65 wpas = wpas_connect()
66 if (wpas == None):
67 return None
68 return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
69
70
71def wpas_get_handover_sel():
72 wpas = wpas_connect()
73 if (wpas == None):
74 return None
75 return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
76
77
78def wpas_report_handover(req, sel):
79 wpas = wpas_connect()
80 if (wpas == None):
81 return None
82 return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
83 str(req).encode("hex") + " " +
84 str(sel).encode("hex"))
85
86
87class HandoverServer(nfc.handover.HandoverServer):
Dmitry Shmidtcf32e602014-01-28 10:57:39 -080088 def __init__(self, llc):
89 super(HandoverServer, self).__init__(llc)
90 self.ho_server_processing = False
91 self.success = False
Dmitry Shmidtf8623282013-02-20 14:34:59 -080092
93 def process_request(self, request):
94 print "HandoverServer - request received"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -080095 try:
96 print "Parsed handover request: " + request.pretty()
97 except Exception, e:
98 print e
99 print str(request).encode("hex")
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800100
101 sel = nfc.ndef.HandoverSelectMessage(version="1.2")
102
103 for carrier in request.carriers:
104 print "Remote carrier type: " + carrier.type
105 if carrier.type == "application/vnd.wfa.wsc":
106 print "WPS carrier type match - add WPS carrier record"
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800107 data = wpas_get_handover_sel()
108 if data is None:
109 print "Could not get handover select carrier record from hostapd"
110 continue
111 print "Handover select carrier record from hostapd:"
112 print data.encode("hex")
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800113 wpas_report_handover(carrier.record, data)
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800114
115 message = nfc.ndef.Message(data);
116 sel.add_carrier(message[0], "active", message[1:])
117
118 print "Handover select:"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800119 try:
120 print sel.pretty()
121 except Exception, e:
122 print e
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800123 print str(sel).encode("hex")
124
125 print "Sending handover select"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800126 self.success = True
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800127 return sel
128
129
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800130def wps_tag_read(tag):
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800131 success = False
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800132 if len(tag.ndef.message):
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800133 for record in tag.ndef.message:
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800134 print "record type " + record.type
135 if record.type == "application/vnd.wfa.wsc":
136 print "WPS tag - send to hostapd"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800137 success = wpas_tag_read(tag.ndef.message)
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800138 break
139 else:
140 print "Empty tag"
141
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800142 return success
143
144
145def rdwr_connected_write(tag):
146 print "Tag found - writing"
147 global write_data
148 tag.ndef.message = str(write_data)
149 print "Done - remove tag"
150 global only_one
151 if only_one:
152 global continue_loop
153 continue_loop = False
154 global write_wait_remove
155 while write_wait_remove and tag.is_present:
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800156 time.sleep(0.1)
157
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800158def wps_write_config_tag(clf, wait_remove=True):
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800159 print "Write WPS config token"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800160 global write_data, write_wait_remove
161 write_wait_remove = wait_remove
162 write_data = wpas_get_config_token()
163 if write_data == None:
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800164 print "Could not get WPS config token from hostapd"
165 return
166
167 print "Touch an NFC tag"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800168 clf.connect(rdwr={'on-connect': rdwr_connected_write})
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800169
170
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800171def wps_write_password_tag(clf, wait_remove=True):
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800172 print "Write WPS password token"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800173 global write_data, write_wait_remove
174 write_wait_remove = wait_remove
175 write_data = wpas_get_password_token()
176 if write_data == None:
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800177 print "Could not get WPS password token from hostapd"
178 return
179
180 print "Touch an NFC tag"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800181 clf.connect(rdwr={'on-connect': rdwr_connected_write})
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800182
183
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800184def rdwr_connected(tag):
185 global only_one, no_wait
186 print "Tag connected: " + str(tag)
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800187
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800188 if tag.ndef:
189 print "NDEF tag: " + tag.type
190 try:
191 print tag.ndef.message.pretty()
192 except Exception, e:
193 print e
194 success = wps_tag_read(tag)
195 if only_one and success:
196 global continue_loop
197 continue_loop = False
198 else:
199 print "Not an NDEF tag - remove tag"
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800200
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800201 return not no_wait
202
203
204def llcp_startup(clf, llc):
205 print "Start LLCP server"
206 global srv
207 srv = HandoverServer(llc)
208 return llc
209
210def llcp_connected(llc):
211 print "P2P LLCP connected"
212 global wait_connection
213 wait_connection = False
214 global srv
215 srv.start()
216 return True
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800217
218
219def main():
220 clf = nfc.ContactlessFrontend()
221
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800222 parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
223 parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
224 action='store_const', dest='loglevel',
225 help='verbose debug output')
226 parser.add_argument('-q', const=logging.WARNING, action='store_const',
227 dest='loglevel', help='be quiet')
228 parser.add_argument('--only-one', '-1', action='store_true',
229 help='run only one operation and exit')
230 parser.add_argument('--no-wait', action='store_true',
231 help='do not wait for tag to be removed before exiting')
232 parser.add_argument('command', choices=['write-config',
233 'write-password'],
234 nargs='?')
235 args = parser.parse_args()
236
237 global only_one
238 only_one = args.only_one
239
240 global no_wait
241 no_wait = args.no_wait
242
243 logging.basicConfig(level=args.loglevel)
244
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800245 try:
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800246 if not clf.open("usb"):
247 print "Could not open connection with an NFC device"
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800248 raise SystemExit
249
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800250 if args.command == "write-config":
251 wps_write_config_tag(clf, wait_remove=not args.no_wait)
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800252 raise SystemExit
253
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800254 if args.command == "write-password":
255 wps_write_password_tag(clf, wait_remove=not args.no_wait)
256 raise SystemExit
257
258 global continue_loop
259 while continue_loop:
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800260 print "Waiting for a tag or peer to be touched"
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800261 wait_connection = True
262 try:
263 if not clf.connect(rdwr={'on-connect': rdwr_connected},
264 llcp={'on-startup': llcp_startup,
265 'on-connect': llcp_connected}):
266 break
267 except Exception, e:
268 print "clf.connect failed"
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800269
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800270 global srv
271 if only_one and srv and srv.success:
272 raise SystemExit
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800273
274 except KeyboardInterrupt:
275 raise SystemExit
276 finally:
277 clf.close()
278
279 raise SystemExit
280
281if __name__ == '__main__':
282 main()