WPS NFC: Logging level configuration to wps-nfc.py and wps-ap-nfc.py
[mech_eap.git] / hostapd / wps-ap-nfc.py
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
12 import argparse
13
14 import nfc
15 import nfc.ndef
16 import nfc.llcp
17 import nfc.handover
18
19 import logging
20
21 import wpaspy
22
23 wpas_ctrl = '/var/run/hostapd'
24 continue_loop = True
25
26 def 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:
41             wpas = wpaspy.Ctrl(ctrl)
42             return wpas
43         except Exception, e:
44             pass
45     return None
46
47
48 def wpas_tag_read(message):
49     wpas = wpas_connect()
50     if (wpas == None):
51         return
52     if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
53         return False
54     return True
55
56
57 def 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
64 def 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
71 def 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
78 def 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
87 class HandoverServer(nfc.handover.HandoverServer):
88     def __init__(self, llc):
89         super(HandoverServer, self).__init__(llc)
90         self.ho_server_processing = False
91         self.success = False
92
93     def process_request(self, request):
94         print "HandoverServer - request received"
95         print "Parsed handover request: " + request.pretty()
96
97         sel = nfc.ndef.HandoverSelectMessage(version="1.2")
98
99         for carrier in request.carriers:
100             print "Remote carrier type: " + carrier.type
101             if carrier.type == "application/vnd.wfa.wsc":
102                 print "WPS carrier type match - add WPS carrier record"
103                 data = wpas_get_handover_sel()
104                 if data is None:
105                     print "Could not get handover select carrier record from hostapd"
106                     continue
107                 print "Handover select carrier record from hostapd:"
108                 print data.encode("hex")
109                 wpas_report_handover(carrier.record, data)
110
111                 message = nfc.ndef.Message(data);
112                 sel.add_carrier(message[0], "active", message[1:])
113
114         print "Handover select:"
115         print sel.pretty()
116         print str(sel).encode("hex")
117
118         print "Sending handover select"
119         self.success = True
120         return sel
121
122
123 def wps_tag_read(tag):
124     success = False
125     if len(tag.ndef.message):
126         for record in tag.ndef.message:
127             print "record type " + record.type
128             if record.type == "application/vnd.wfa.wsc":
129                 print "WPS tag - send to hostapd"
130                 success = wpas_tag_read(tag.ndef.message)
131                 break
132     else:
133         print "Empty tag"
134
135     return success
136
137
138 def rdwr_connected_write(tag):
139     print "Tag found - writing"
140     global write_data
141     tag.ndef.message = str(write_data)
142     print "Done - remove tag"
143     global only_one
144     if only_one:
145         global continue_loop
146         continue_loop = False
147     global write_wait_remove
148     while write_wait_remove and tag.is_present:
149         time.sleep(0.1)
150
151 def wps_write_config_tag(clf, wait_remove=True):
152     print "Write WPS config token"
153     global write_data, write_wait_remove
154     write_wait_remove = wait_remove
155     write_data = wpas_get_config_token()
156     if write_data == None:
157         print "Could not get WPS config token from hostapd"
158         return
159
160     print "Touch an NFC tag"
161     clf.connect(rdwr={'on-connect': rdwr_connected_write})
162
163
164 def wps_write_password_tag(clf, wait_remove=True):
165     print "Write WPS password token"
166     global write_data, write_wait_remove
167     write_wait_remove = wait_remove
168     write_data = wpas_get_password_token()
169     if write_data == None:
170         print "Could not get WPS password token from hostapd"
171         return
172
173     print "Touch an NFC tag"
174     clf.connect(rdwr={'on-connect': rdwr_connected_write})
175
176
177 def rdwr_connected(tag):
178     global only_one, no_wait
179     print "Tag connected: " + str(tag)
180
181     if tag.ndef:
182         print "NDEF tag: " + tag.type
183         try:
184             print tag.ndef.message.pretty()
185         except Exception, e:
186             print e
187         success = wps_tag_read(tag)
188         if only_one and success:
189             global continue_loop
190             continue_loop = False
191     else:
192         print "Not an NDEF tag - remove tag"
193
194     return not no_wait
195
196
197 def llcp_startup(clf, llc):
198     print "Start LLCP server"
199     global srv
200     srv = HandoverServer(llc)
201     return llc
202
203 def llcp_connected(llc):
204     print "P2P LLCP connected"
205     global wait_connection
206     wait_connection = False
207     global srv
208     srv.start()
209     return True
210
211
212 def main():
213     clf = nfc.ContactlessFrontend()
214
215     parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
216     parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
217                         action='store_const', dest='loglevel',
218                         help='verbose debug output')
219     parser.add_argument('-q', const=logging.WARNING, action='store_const',
220                         dest='loglevel', help='be quiet')
221     parser.add_argument('--only-one', '-1', action='store_true',
222                         help='run only one operation and exit')
223     parser.add_argument('--no-wait', action='store_true',
224                         help='do not wait for tag to be removed before exiting')
225     parser.add_argument('command', choices=['write-config',
226                                             'write-password'],
227                         nargs='?')
228     args = parser.parse_args()
229
230     global only_one
231     only_one = args.only_one
232
233     global no_wait
234     no_wait = args.no_wait
235
236     logging.basicConfig(level=args.loglevel)
237
238     try:
239         if not clf.open("usb"):
240             print "Could not open connection with an NFC device"
241             raise SystemExit
242
243         if args.command == "write-config":
244             wps_write_config_tag(clf, wait_remove=not args.no_wait)
245             raise SystemExit
246
247         if args.command == "write-password":
248             wps_write_password_tag(clf, wait_remove=not args.no_wait)
249             raise SystemExit
250
251         global continue_loop
252         while continue_loop:
253             print "Waiting for a tag or peer to be touched"
254             wait_connection = True
255             try:
256                 if not clf.connect(rdwr={'on-connect': rdwr_connected},
257                                    llcp={'on-startup': llcp_startup,
258                                          'on-connect': llcp_connected}):
259                     break
260             except Exception, e:
261                 print "clf.connect failed"
262
263             global srv
264             if only_one and srv and srv.success:
265                 raise SystemExit
266
267     except KeyboardInterrupt:
268         raise SystemExit
269     finally:
270         clf.close()
271
272     raise SystemExit
273
274 if __name__ == '__main__':
275     main()