wps-ap-nfc.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. import os
  9. import sys
  10. import time
  11. import argparse
  12. import nfc
  13. import nfc.ndef
  14. import nfc.llcp
  15. import nfc.handover
  16. import logging
  17. import wpaspy
  18. wpas_ctrl = '/var/run/hostapd'
  19. continue_loop = True
  20. summary_file = None
  21. success_file = None
  22. def summary(txt):
  23. print txt
  24. if summary_file:
  25. with open(summary_file, 'a') as f:
  26. f.write(txt + "\n")
  27. def success_report(txt):
  28. summary(txt)
  29. if success_file:
  30. with open(success_file, 'a') as f:
  31. f.write(txt + "\n")
  32. def wpas_connect():
  33. ifaces = []
  34. if os.path.isdir(wpas_ctrl):
  35. try:
  36. ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
  37. except OSError, error:
  38. print "Could not find hostapd: ", error
  39. return None
  40. if len(ifaces) < 1:
  41. print "No hostapd control interface found"
  42. return None
  43. for ctrl in ifaces:
  44. try:
  45. wpas = wpaspy.Ctrl(ctrl)
  46. return wpas
  47. except Exception, e:
  48. pass
  49. return None
  50. def wpas_tag_read(message):
  51. wpas = wpas_connect()
  52. if (wpas == None):
  53. return False
  54. if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
  55. return False
  56. return True
  57. def wpas_get_config_token():
  58. wpas = wpas_connect()
  59. if (wpas == None):
  60. return None
  61. ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
  62. if "FAIL" in ret:
  63. return None
  64. return ret.rstrip().decode("hex")
  65. def wpas_get_password_token():
  66. wpas = wpas_connect()
  67. if (wpas == None):
  68. return None
  69. ret = wpas.request("WPS_NFC_TOKEN NDEF")
  70. if "FAIL" in ret:
  71. return None
  72. return ret.rstrip().decode("hex")
  73. def wpas_get_handover_sel():
  74. wpas = wpas_connect()
  75. if (wpas == None):
  76. return None
  77. ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR")
  78. if "FAIL" in ret:
  79. return None
  80. return ret.rstrip().decode("hex")
  81. def wpas_report_handover(req, sel):
  82. wpas = wpas_connect()
  83. if (wpas == None):
  84. return None
  85. return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
  86. str(req).encode("hex") + " " +
  87. str(sel).encode("hex"))
  88. class HandoverServer(nfc.handover.HandoverServer):
  89. def __init__(self, llc):
  90. super(HandoverServer, self).__init__(llc)
  91. self.ho_server_processing = False
  92. self.success = False
  93. # override to avoid parser error in request/response.pretty() in nfcpy
  94. # due to new WSC handover format
  95. def _process_request(self, request):
  96. summary("received handover request {}".format(request.type))
  97. response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
  98. if not request.type == 'urn:nfc:wkt:Hr':
  99. summary("not a handover request")
  100. else:
  101. try:
  102. request = nfc.ndef.HandoverRequestMessage(request)
  103. except nfc.ndef.DecodeError as e:
  104. summary("error decoding 'Hr' message: {}".format(e))
  105. else:
  106. response = self.process_request(request)
  107. summary("send handover response {}".format(response.type))
  108. return response
  109. def process_request(self, request):
  110. summary("HandoverServer - request received")
  111. try:
  112. print "Parsed handover request: " + request.pretty()
  113. except Exception, e:
  114. print e
  115. print str(request).encode("hex")
  116. sel = nfc.ndef.HandoverSelectMessage(version="1.2")
  117. for carrier in request.carriers:
  118. print "Remote carrier type: " + carrier.type
  119. if carrier.type == "application/vnd.wfa.wsc":
  120. summary("WPS carrier type match - add WPS carrier record")
  121. data = wpas_get_handover_sel()
  122. if data is None:
  123. summary("Could not get handover select carrier record from hostapd")
  124. continue
  125. print "Handover select carrier record from hostapd:"
  126. print data.encode("hex")
  127. if "OK" in wpas_report_handover(carrier.record, data):
  128. success_report("Handover reported successfully")
  129. else:
  130. summary("Handover report rejected")
  131. message = nfc.ndef.Message(data);
  132. sel.add_carrier(message[0], "active", message[1:])
  133. print "Handover select:"
  134. try:
  135. print sel.pretty()
  136. except Exception, e:
  137. print e
  138. print str(sel).encode("hex")
  139. summary("Sending handover select")
  140. self.success = True
  141. return sel
  142. def wps_tag_read(tag):
  143. success = False
  144. if len(tag.ndef.message):
  145. for record in tag.ndef.message:
  146. print "record type " + record.type
  147. if record.type == "application/vnd.wfa.wsc":
  148. summary("WPS tag - send to hostapd")
  149. success = wpas_tag_read(tag.ndef.message)
  150. break
  151. else:
  152. summary("Empty tag")
  153. if success:
  154. success_report("Tag read succeeded")
  155. return success
  156. def rdwr_connected_write(tag):
  157. summary("Tag found - writing - " + str(tag))
  158. global write_data
  159. tag.ndef.message = str(write_data)
  160. success_report("Tag write succeeded")
  161. print "Done - remove tag"
  162. global only_one
  163. if only_one:
  164. global continue_loop
  165. continue_loop = False
  166. global write_wait_remove
  167. while write_wait_remove and tag.is_present:
  168. time.sleep(0.1)
  169. def wps_write_config_tag(clf, wait_remove=True):
  170. summary("Write WPS config token")
  171. global write_data, write_wait_remove
  172. write_wait_remove = wait_remove
  173. write_data = wpas_get_config_token()
  174. if write_data == None:
  175. summary("Could not get WPS config token from hostapd")
  176. return
  177. print "Touch an NFC tag"
  178. clf.connect(rdwr={'on-connect': rdwr_connected_write})
  179. def wps_write_password_tag(clf, wait_remove=True):
  180. summary("Write WPS password token")
  181. global write_data, write_wait_remove
  182. write_wait_remove = wait_remove
  183. write_data = wpas_get_password_token()
  184. if write_data == None:
  185. summary("Could not get WPS password token from hostapd")
  186. return
  187. print "Touch an NFC tag"
  188. clf.connect(rdwr={'on-connect': rdwr_connected_write})
  189. def rdwr_connected(tag):
  190. global only_one, no_wait
  191. summary("Tag connected: " + str(tag))
  192. if tag.ndef:
  193. print "NDEF tag: " + tag.type
  194. try:
  195. print tag.ndef.message.pretty()
  196. except Exception, e:
  197. print e
  198. success = wps_tag_read(tag)
  199. if only_one and success:
  200. global continue_loop
  201. continue_loop = False
  202. else:
  203. summary("Not an NDEF tag - remove tag")
  204. return True
  205. return not no_wait
  206. def llcp_startup(clf, llc):
  207. print "Start LLCP server"
  208. global srv
  209. srv = HandoverServer(llc)
  210. return llc
  211. def llcp_connected(llc):
  212. print "P2P LLCP connected"
  213. global wait_connection
  214. wait_connection = False
  215. global srv
  216. srv.start()
  217. return True
  218. def main():
  219. clf = nfc.ContactlessFrontend()
  220. parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
  221. parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
  222. action='store_const', dest='loglevel',
  223. help='verbose debug output')
  224. parser.add_argument('-q', const=logging.WARNING, action='store_const',
  225. dest='loglevel', help='be quiet')
  226. parser.add_argument('--only-one', '-1', action='store_true',
  227. help='run only one operation and exit')
  228. parser.add_argument('--no-wait', action='store_true',
  229. help='do not wait for tag to be removed before exiting')
  230. parser.add_argument('--summary',
  231. help='summary file for writing status updates')
  232. parser.add_argument('--success',
  233. help='success file for writing success update')
  234. parser.add_argument('command', choices=['write-config',
  235. 'write-password'],
  236. nargs='?')
  237. args = parser.parse_args()
  238. global only_one
  239. only_one = args.only_one
  240. global no_wait
  241. no_wait = args.no_wait
  242. if args.summary:
  243. global summary_file
  244. summary_file = args.summary
  245. if args.success:
  246. global success_file
  247. success_file = args.success
  248. logging.basicConfig(level=args.loglevel)
  249. try:
  250. if not clf.open("usb"):
  251. print "Could not open connection with an NFC device"
  252. raise SystemExit
  253. if args.command == "write-config":
  254. wps_write_config_tag(clf, wait_remove=not args.no_wait)
  255. raise SystemExit
  256. if args.command == "write-password":
  257. wps_write_password_tag(clf, wait_remove=not args.no_wait)
  258. raise SystemExit
  259. global continue_loop
  260. while continue_loop:
  261. print "Waiting for a tag or peer to be touched"
  262. wait_connection = True
  263. try:
  264. if not clf.connect(rdwr={'on-connect': rdwr_connected},
  265. llcp={'on-startup': llcp_startup,
  266. 'on-connect': llcp_connected}):
  267. break
  268. except Exception, e:
  269. print "clf.connect failed"
  270. global srv
  271. if only_one and srv and srv.success:
  272. raise SystemExit
  273. except KeyboardInterrupt:
  274. raise SystemExit
  275. finally:
  276. clf.close()
  277. raise SystemExit
  278. if __name__ == '__main__':
  279. main()