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