wps-nfc.py 12 KB

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