wps-ap-nfc.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. def process_request(self, request):
  94. summary("HandoverServer - request received")
  95. try:
  96. print "Parsed handover request: " + request.pretty()
  97. except Exception, e:
  98. print e
  99. print str(request).encode("hex")
  100. sel = nfc.ndef.HandoverSelectMessage(version="1.2")
  101. for carrier in request.carriers:
  102. print "Remote carrier type: " + carrier.type
  103. if carrier.type == "application/vnd.wfa.wsc":
  104. summary("WPS carrier type match - add WPS carrier record")
  105. data = wpas_get_handover_sel()
  106. if data is None:
  107. summary("Could not get handover select carrier record from hostapd")
  108. continue
  109. print "Handover select carrier record from hostapd:"
  110. print data.encode("hex")
  111. if "OK" in wpas_report_handover(carrier.record, data):
  112. success_report("Handover reported successfully")
  113. else:
  114. summary("Handover report rejected")
  115. message = nfc.ndef.Message(data);
  116. sel.add_carrier(message[0], "active", message[1:])
  117. print "Handover select:"
  118. try:
  119. print sel.pretty()
  120. except Exception, e:
  121. print e
  122. print str(sel).encode("hex")
  123. summary("Sending handover select")
  124. self.success = True
  125. return sel
  126. def wps_tag_read(tag):
  127. success = False
  128. if len(tag.ndef.message):
  129. for record in tag.ndef.message:
  130. print "record type " + record.type
  131. if record.type == "application/vnd.wfa.wsc":
  132. summary("WPS tag - send to hostapd")
  133. success = wpas_tag_read(tag.ndef.message)
  134. break
  135. else:
  136. summary("Empty tag")
  137. if success:
  138. success_report("Tag read succeeded")
  139. return success
  140. def rdwr_connected_write(tag):
  141. summary("Tag found - writing - " + str(tag))
  142. global write_data
  143. tag.ndef.message = str(write_data)
  144. success_report("Tag write succeeded")
  145. print "Done - remove tag"
  146. global only_one
  147. if only_one:
  148. global continue_loop
  149. continue_loop = False
  150. global write_wait_remove
  151. while write_wait_remove and tag.is_present:
  152. time.sleep(0.1)
  153. def wps_write_config_tag(clf, wait_remove=True):
  154. summary("Write WPS config token")
  155. global write_data, write_wait_remove
  156. write_wait_remove = wait_remove
  157. write_data = wpas_get_config_token()
  158. if write_data == None:
  159. summary("Could not get WPS config token from hostapd")
  160. return
  161. print "Touch an NFC tag"
  162. clf.connect(rdwr={'on-connect': rdwr_connected_write})
  163. def wps_write_password_tag(clf, wait_remove=True):
  164. summary("Write WPS password token")
  165. global write_data, write_wait_remove
  166. write_wait_remove = wait_remove
  167. write_data = wpas_get_password_token()
  168. if write_data == None:
  169. summary("Could not get WPS password token from hostapd")
  170. return
  171. print "Touch an NFC tag"
  172. clf.connect(rdwr={'on-connect': rdwr_connected_write})
  173. def rdwr_connected(tag):
  174. global only_one, no_wait
  175. summary("Tag connected: " + str(tag))
  176. if tag.ndef:
  177. print "NDEF tag: " + tag.type
  178. try:
  179. print tag.ndef.message.pretty()
  180. except Exception, e:
  181. print e
  182. success = wps_tag_read(tag)
  183. if only_one and success:
  184. global continue_loop
  185. continue_loop = False
  186. else:
  187. summary("Not an NDEF tag - remove tag")
  188. return True
  189. return not no_wait
  190. def llcp_startup(clf, llc):
  191. print "Start LLCP server"
  192. global srv
  193. srv = HandoverServer(llc)
  194. return llc
  195. def llcp_connected(llc):
  196. print "P2P LLCP connected"
  197. global wait_connection
  198. wait_connection = False
  199. global srv
  200. srv.start()
  201. return True
  202. def main():
  203. clf = nfc.ContactlessFrontend()
  204. parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
  205. parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
  206. action='store_const', dest='loglevel',
  207. help='verbose debug output')
  208. parser.add_argument('-q', const=logging.WARNING, action='store_const',
  209. dest='loglevel', help='be quiet')
  210. parser.add_argument('--only-one', '-1', action='store_true',
  211. help='run only one operation and exit')
  212. parser.add_argument('--no-wait', action='store_true',
  213. help='do not wait for tag to be removed before exiting')
  214. parser.add_argument('--summary',
  215. help='summary file for writing status updates')
  216. parser.add_argument('--success',
  217. help='success file for writing success update')
  218. parser.add_argument('command', choices=['write-config',
  219. 'write-password'],
  220. nargs='?')
  221. args = parser.parse_args()
  222. global only_one
  223. only_one = args.only_one
  224. global no_wait
  225. no_wait = args.no_wait
  226. if args.summary:
  227. global summary_file
  228. summary_file = args.summary
  229. if args.success:
  230. global success_file
  231. success_file = args.success
  232. logging.basicConfig(level=args.loglevel)
  233. try:
  234. if not clf.open("usb"):
  235. print "Could not open connection with an NFC device"
  236. raise SystemExit
  237. if args.command == "write-config":
  238. wps_write_config_tag(clf, wait_remove=not args.no_wait)
  239. raise SystemExit
  240. if args.command == "write-password":
  241. wps_write_password_tag(clf, wait_remove=not args.no_wait)
  242. raise SystemExit
  243. global continue_loop
  244. while continue_loop:
  245. print "Waiting for a tag or peer to be touched"
  246. wait_connection = True
  247. try:
  248. if not clf.connect(rdwr={'on-connect': rdwr_connected},
  249. llcp={'on-startup': llcp_startup,
  250. 'on-connect': llcp_connected}):
  251. break
  252. except Exception, e:
  253. print "clf.connect failed"
  254. global srv
  255. if only_one and srv and srv.success:
  256. raise SystemExit
  257. except KeyboardInterrupt:
  258. raise SystemExit
  259. finally:
  260. clf.close()
  261. raise SystemExit
  262. if __name__ == '__main__':
  263. main()