wps-nfc.py revision 700a137ab366edc72e371da68ba187b4717ee660
1#!/usr/bin/python
2#
3# Example nfcpy to wpa_supplicant 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
12import random
13import StringIO
14
15import nfc
16import nfc.ndef
17import nfc.llcp
18import nfc.handover
19
20import logging
21logging.basicConfig()
22
23import wpaspy
24
25wpas_ctrl = '/var/run/wpa_supplicant'
26
27def 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 wpa_supplicant: ", error
34            return None
35
36    if len(ifaces) < 1:
37        print "No wpa_supplicant control interface found"
38        return None
39
40    for ctrl in ifaces:
41        try:
42            wpas = wpaspy.Ctrl(ctrl)
43            return wpas
44        except Exception, e:
45            pass
46    return None
47
48
49def wpas_tag_read(message):
50    wpas = wpas_connect()
51    if (wpas == None):
52        return
53    print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex"))
54
55
56def wpas_get_config_token():
57    wpas = wpas_connect()
58    if (wpas == None):
59        return None
60    return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex")
61
62
63def wpas_get_er_config_token(uuid):
64    wpas = wpas_connect()
65    if (wpas == None):
66        return None
67    return wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid).rstrip().decode("hex")
68
69
70def wpas_get_password_token():
71    wpas = wpas_connect()
72    if (wpas == None):
73        return None
74    return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
75
76
77def wpas_get_handover_req():
78    wpas = wpas_connect()
79    if (wpas == None):
80        return None
81    return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex")
82
83
84def wpas_get_handover_sel(uuid):
85    wpas = wpas_connect()
86    if (wpas == None):
87        return None
88    if uuid is None:
89        return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
90    return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip().decode("hex")
91
92
93def wpas_report_handover(req, sel, type):
94    wpas = wpas_connect()
95    if (wpas == None):
96        return None
97    return wpas.request("NFC_REPORT_HANDOVER " + type + " WPS " +
98                        str(req).encode("hex") + " " +
99                        str(sel).encode("hex"))
100
101
102class HandoverServer(nfc.handover.HandoverServer):
103    def __init__(self):
104        super(HandoverServer, self).__init__()
105
106    def process_request(self, request):
107        print "HandoverServer - request received"
108        print "Parsed handover request: " + request.pretty()
109
110        sel = nfc.ndef.HandoverSelectMessage(version="1.2")
111
112        for carrier in request.carriers:
113            print "Remote carrier type: " + carrier.type
114            if carrier.type == "application/vnd.wfa.wsc":
115                print "WPS carrier type match - add WPS carrier record"
116                self.received_carrier = carrier.record
117                data = wpas_get_handover_sel(self.uuid)
118                if data is None:
119                    print "Could not get handover select carrier record from wpa_supplicant"
120                    continue
121                print "Handover select carrier record from wpa_supplicant:"
122                print data.encode("hex")
123                self.sent_carrier = data
124
125                message = nfc.ndef.Message(data);
126                sel.add_carrier(message[0], "active", message[1:])
127
128        print "Handover select:"
129        print sel.pretty()
130        print str(sel).encode("hex")
131
132        print "Sending handover select"
133        return sel
134
135
136def wps_handover_resp(peer, uuid):
137    if uuid is None:
138        print "Trying to handle WPS handover"
139    else:
140        print "Trying to handle WPS handover with AP " + uuid
141
142    srv = HandoverServer()
143    srv.sent_carrier = None
144    srv.uuid = uuid
145
146    nfc.llcp.activate(peer);
147
148    try:
149        print "Trying handover";
150        srv.start()
151        print "Wait for disconnect"
152        while nfc.llcp.connected():
153            time.sleep(0.1)
154        print "Disconnected after handover"
155    except nfc.llcp.ConnectRefused:
156        print "Handover connection refused"
157        nfc.llcp.shutdown()
158        return
159
160    if srv.sent_carrier:
161        wpas_report_handover(srv.received_carrier, srv.sent_carrier, "RESP")
162
163    print "Remove peer"
164    nfc.llcp.shutdown()
165    print "Done with handover"
166    time.sleep(1)
167
168
169def wps_handover_init(peer):
170    print "Trying to initiate WPS handover"
171
172    data = wpas_get_handover_req()
173    if (data == None):
174        print "Could not get handover request carrier record from wpa_supplicant"
175        return
176    print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
177    record = nfc.ndef.Record()
178    f = StringIO.StringIO(data)
179    record._read(f)
180    record = nfc.ndef.HandoverCarrierRecord(record)
181    print "Parsed handover request carrier record:"
182    print record.pretty()
183
184    message = nfc.ndef.HandoverRequestMessage(version="1.2")
185    message.nonce = random.randint(0, 0xffff)
186    message.add_carrier(record, "active")
187
188    print "Handover request:"
189    print message.pretty()
190
191    nfc.llcp.activate(peer);
192
193    client = nfc.handover.HandoverClient()
194    try:
195        print "Trying handover";
196        client.connect()
197        print "Connected for handover"
198    except nfc.llcp.ConnectRefused:
199        print "Handover connection refused"
200        nfc.llcp.shutdown()
201        client.close()
202        return
203
204    print "Sending handover request"
205
206    if not client.send(message):
207        print "Failed to send handover request"
208
209    print "Receiving handover response"
210    message = client._recv()
211    if message is None:
212        print "No response received"
213        nfc.llcp.shutdown()
214        client.close()
215        return
216    if message.type != "urn:nfc:wkt:Hs":
217        print "Response was not Hs - received: " + message.type
218        nfc.llcp.shutdown()
219        client.close()
220        return
221
222    print "Received message"
223    print message.pretty()
224    message = nfc.ndef.HandoverSelectMessage(message)
225    print "Handover select received"
226    print message.pretty()
227
228    for carrier in message.carriers:
229        print "Remote carrier type: " + carrier.type
230        if carrier.type == "application/vnd.wfa.wsc":
231            print "WPS carrier type match - send to wpa_supplicant"
232            wpas_report_handover(data, carrier.record, "INIT")
233            wifi = nfc.ndef.WifiConfigRecord(carrier.record)
234            print wifi.pretty()
235
236    print "Remove peer"
237    nfc.llcp.shutdown()
238    client.close()
239    print "Done with handover"
240
241
242def wps_tag_read(tag):
243    if len(tag.ndef.message):
244        message = nfc.ndef.Message(tag.ndef.message)
245        print "message type " + message.type
246
247        for record in message:
248            print "record type " + record.type
249            if record.type == "application/vnd.wfa.wsc":
250                print "WPS tag - send to wpa_supplicant"
251                wpas_tag_read(tag.ndef.message)
252                break
253    else:
254        print "Empty tag"
255
256    print "Remove tag"
257    while tag.is_present:
258        time.sleep(0.1)
259
260
261def wps_write_config_tag(clf):
262    print "Write WPS config token"
263    data = wpas_get_config_token()
264    if (data == None):
265        print "Could not get WPS config token from wpa_supplicant"
266        return
267
268    print "Touch an NFC tag"
269    while True:
270        tag = clf.poll()
271        if tag == None:
272            time.sleep(0.1)
273            continue
274        break
275
276    print "Tag found - writing"
277    tag.ndef.message = data
278    print "Done - remove tag"
279    while tag.is_present:
280        time.sleep(0.1)
281
282
283def wps_write_er_config_tag(clf, uuid):
284    print "Write WPS ER config token"
285    data = wpas_get_er_config_token(uuid)
286    if (data == None):
287        print "Could not get WPS config token from wpa_supplicant"
288        return
289
290    print "Touch an NFC tag"
291    while True:
292        tag = clf.poll()
293        if tag == None:
294            time.sleep(0.1)
295            continue
296        break
297
298    print "Tag found - writing"
299    tag.ndef.message = data
300    print "Done - remove tag"
301    while tag.is_present:
302        time.sleep(0.1)
303
304
305def wps_write_password_tag(clf):
306    print "Write WPS password token"
307    data = wpas_get_password_token()
308    if (data == None):
309        print "Could not get WPS password token from wpa_supplicant"
310        return
311
312    print "Touch an NFC tag"
313    while True:
314        tag = clf.poll()
315        if tag == None:
316            time.sleep(0.1)
317            continue
318        break
319
320    print "Tag found - writing"
321    tag.ndef.message = data
322    print "Done - remove tag"
323    while tag.is_present:
324        time.sleep(0.1)
325
326
327def find_peer(clf):
328    while True:
329        if nfc.llcp.connected():
330            print "LLCP connected"
331        general_bytes = nfc.llcp.startup({})
332        peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
333        if isinstance(peer, nfc.DEP):
334            print "listen -> DEP";
335            if peer.general_bytes.startswith("Ffm"):
336                print "Found DEP"
337                return peer
338            print "mismatch in general_bytes"
339            print peer.general_bytes
340
341        peer = clf.poll(general_bytes)
342        if isinstance(peer, nfc.DEP):
343            print "poll -> DEP";
344            if peer.general_bytes.startswith("Ffm"):
345                print "Found DEP"
346                return peer
347            print "mismatch in general_bytes"
348            print peer.general_bytes
349
350        if peer:
351            print "Found tag"
352            return peer
353
354
355def main():
356    clf = nfc.ContactlessFrontend()
357
358    try:
359        arg_uuid = None
360        if len(sys.argv) > 1:
361            arg_uuid = sys.argv[1]
362
363        if len(sys.argv) > 1 and sys.argv[1] == "write-config":
364            wps_write_config_tag(clf)
365            raise SystemExit
366
367        if len(sys.argv) > 2 and sys.argv[1] == "write-er-config":
368            wps_write_er_config_tag(clf, sys.argv[2])
369            raise SystemExit
370
371        if len(sys.argv) > 1 and sys.argv[1] == "write-password":
372            wps_write_password_tag(clf)
373            raise SystemExit
374
375        while True:
376            print "Waiting for a tag or peer to be touched"
377
378            tag = find_peer(clf)
379            if isinstance(tag, nfc.DEP):
380                if arg_uuid is None:
381                    wps_handover_init(tag)
382                elif arg_uuid is "ap":
383                    wps_handover_resp(tag, None)
384                else:
385                    wps_handover_resp(tag, arg_uuid)
386                continue
387
388            if tag.ndef:
389                wps_tag_read(tag)
390                continue
391
392            print "Not an NDEF tag - remove tag"
393            while tag.is_present:
394                time.sleep(0.1)
395
396    except KeyboardInterrupt:
397        raise SystemExit
398    finally:
399        clf.close()
400
401    raise SystemExit
402
403if __name__ == '__main__':
404    main()
405