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