61b5519ace3071e8f9b3b9e05c48c4d4ac060d44
[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
13 import nfc
14 import nfc.ndef
15 import nfc.llcp
16 import nfc.handover
17
18 import logging
19 logging.basicConfig()
20
21 import wpaspy
22
23 wpas_ctrl = '/var/run/hostapd'
24
25 def 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
47 def 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
54 def 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
61 def 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
68 def 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
75 def 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
84 class 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
118 def 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
146 def 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
165 def 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
187 def 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
209 def 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
237 def 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
272 if __name__ == '__main__':
273     main()