wps-ap-nfc.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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. def wpas_connect():
  21. ifaces = []
  22. if os.path.isdir(wpas_ctrl):
  23. try:
  24. ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
  25. except OSError, error:
  26. print "Could not find hostapd: ", error
  27. return None
  28. if len(ifaces) < 1:
  29. print "No hostapd control interface found"
  30. return None
  31. for ctrl in ifaces:
  32. try:
  33. wpas = wpaspy.Ctrl(ctrl)
  34. return wpas
  35. except Exception, e:
  36. pass
  37. return None
  38. def wpas_tag_read(message):
  39. wpas = wpas_connect()
  40. if (wpas == None):
  41. return
  42. if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
  43. return False
  44. return True
  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_password_token():
  51. wpas = wpas_connect()
  52. if (wpas == None):
  53. return None
  54. return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
  55. def wpas_get_handover_sel():
  56. wpas = wpas_connect()
  57. if (wpas == None):
  58. return None
  59. return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
  60. def wpas_report_handover(req, sel):
  61. wpas = wpas_connect()
  62. if (wpas == None):
  63. return None
  64. return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
  65. str(req).encode("hex") + " " +
  66. str(sel).encode("hex"))
  67. class HandoverServer(nfc.handover.HandoverServer):
  68. def __init__(self, llc):
  69. super(HandoverServer, self).__init__(llc)
  70. self.ho_server_processing = False
  71. self.success = False
  72. def process_request(self, request):
  73. print "HandoverServer - request received"
  74. try:
  75. print "Parsed handover request: " + request.pretty()
  76. except Exception, e:
  77. print e
  78. print str(request).encode("hex")
  79. sel = nfc.ndef.HandoverSelectMessage(version="1.2")
  80. for carrier in request.carriers:
  81. print "Remote carrier type: " + carrier.type
  82. if carrier.type == "application/vnd.wfa.wsc":
  83. print "WPS carrier type match - add WPS carrier record"
  84. data = wpas_get_handover_sel()
  85. if data is None:
  86. print "Could not get handover select carrier record from hostapd"
  87. continue
  88. print "Handover select carrier record from hostapd:"
  89. print data.encode("hex")
  90. wpas_report_handover(carrier.record, data)
  91. message = nfc.ndef.Message(data);
  92. sel.add_carrier(message[0], "active", message[1:])
  93. print "Handover select:"
  94. try:
  95. print sel.pretty()
  96. except Exception, e:
  97. print e
  98. print str(sel).encode("hex")
  99. print "Sending handover select"
  100. self.success = True
  101. return sel
  102. def wps_tag_read(tag):
  103. success = False
  104. if len(tag.ndef.message):
  105. for record in tag.ndef.message:
  106. print "record type " + record.type
  107. if record.type == "application/vnd.wfa.wsc":
  108. print "WPS tag - send to hostapd"
  109. success = wpas_tag_read(tag.ndef.message)
  110. break
  111. else:
  112. print "Empty tag"
  113. return success
  114. def rdwr_connected_write(tag):
  115. print "Tag found - writing"
  116. global write_data
  117. tag.ndef.message = str(write_data)
  118. print "Done - remove tag"
  119. global only_one
  120. if only_one:
  121. global continue_loop
  122. continue_loop = False
  123. global write_wait_remove
  124. while write_wait_remove and tag.is_present:
  125. time.sleep(0.1)
  126. def wps_write_config_tag(clf, wait_remove=True):
  127. print "Write WPS config token"
  128. global write_data, write_wait_remove
  129. write_wait_remove = wait_remove
  130. write_data = wpas_get_config_token()
  131. if write_data == None:
  132. print "Could not get WPS config token from hostapd"
  133. return
  134. print "Touch an NFC tag"
  135. clf.connect(rdwr={'on-connect': rdwr_connected_write})
  136. def wps_write_password_tag(clf, wait_remove=True):
  137. print "Write WPS password token"
  138. global write_data, write_wait_remove
  139. write_wait_remove = wait_remove
  140. write_data = wpas_get_password_token()
  141. if write_data == None:
  142. print "Could not get WPS password token from hostapd"
  143. return
  144. print "Touch an NFC tag"
  145. clf.connect(rdwr={'on-connect': rdwr_connected_write})
  146. def rdwr_connected(tag):
  147. global only_one, no_wait
  148. print "Tag connected: " + str(tag)
  149. if tag.ndef:
  150. print "NDEF tag: " + tag.type
  151. try:
  152. print tag.ndef.message.pretty()
  153. except Exception, e:
  154. print e
  155. success = wps_tag_read(tag)
  156. if only_one and success:
  157. global continue_loop
  158. continue_loop = False
  159. else:
  160. print "Not an NDEF tag - remove tag"
  161. return not no_wait
  162. def llcp_startup(clf, llc):
  163. print "Start LLCP server"
  164. global srv
  165. srv = HandoverServer(llc)
  166. return llc
  167. def llcp_connected(llc):
  168. print "P2P LLCP connected"
  169. global wait_connection
  170. wait_connection = False
  171. global srv
  172. srv.start()
  173. return True
  174. def main():
  175. clf = nfc.ContactlessFrontend()
  176. parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
  177. parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
  178. action='store_const', dest='loglevel',
  179. help='verbose debug output')
  180. parser.add_argument('-q', const=logging.WARNING, action='store_const',
  181. dest='loglevel', help='be quiet')
  182. parser.add_argument('--only-one', '-1', action='store_true',
  183. help='run only one operation and exit')
  184. parser.add_argument('--no-wait', action='store_true',
  185. help='do not wait for tag to be removed before exiting')
  186. parser.add_argument('command', choices=['write-config',
  187. 'write-password'],
  188. nargs='?')
  189. args = parser.parse_args()
  190. global only_one
  191. only_one = args.only_one
  192. global no_wait
  193. no_wait = args.no_wait
  194. logging.basicConfig(level=args.loglevel)
  195. try:
  196. if not clf.open("usb"):
  197. print "Could not open connection with an NFC device"
  198. raise SystemExit
  199. if args.command == "write-config":
  200. wps_write_config_tag(clf, wait_remove=not args.no_wait)
  201. raise SystemExit
  202. if args.command == "write-password":
  203. wps_write_password_tag(clf, wait_remove=not args.no_wait)
  204. raise SystemExit
  205. global continue_loop
  206. while continue_loop:
  207. print "Waiting for a tag or peer to be touched"
  208. wait_connection = True
  209. try:
  210. if not clf.connect(rdwr={'on-connect': rdwr_connected},
  211. llcp={'on-startup': llcp_startup,
  212. 'on-connect': llcp_connected}):
  213. break
  214. except Exception, e:
  215. print "clf.connect failed"
  216. global srv
  217. if only_one and srv and srv.success:
  218. raise SystemExit
  219. except KeyboardInterrupt:
  220. raise SystemExit
  221. finally:
  222. clf.close()
  223. raise SystemExit
  224. if __name__ == '__main__':
  225. main()