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