wps-nfc.py 11 KB

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