p2p-nfc.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. #!/usr/bin/python
  2. #
  3. # Example nfcpy to wpa_supplicant wrapper for P2P 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 threading
  13. import argparse
  14. import nfc
  15. import nfc.ndef
  16. import nfc.llcp
  17. import nfc.handover
  18. import logging
  19. import wpaspy
  20. wpas_ctrl = '/var/run/wpa_supplicant'
  21. ifname = None
  22. init_on_touch = False
  23. in_raw_mode = False
  24. prev_tcgetattr = 0
  25. include_wps_req = True
  26. include_p2p_req = True
  27. no_input = False
  28. srv = None
  29. continue_loop = True
  30. terminate_now = False
  31. def wpas_connect():
  32. ifaces = []
  33. if os.path.isdir(wpas_ctrl):
  34. try:
  35. ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
  36. except OSError, error:
  37. print "Could not find wpa_supplicant: ", error
  38. return None
  39. if len(ifaces) < 1:
  40. print "No wpa_supplicant control interface found"
  41. return None
  42. for ctrl in ifaces:
  43. if ifname:
  44. if ifname not in ctrl:
  45. continue
  46. try:
  47. print "Trying to use control interface " + ctrl
  48. wpas = wpaspy.Ctrl(ctrl)
  49. return wpas
  50. except Exception, e:
  51. pass
  52. return None
  53. def wpas_tag_read(message):
  54. wpas = wpas_connect()
  55. if (wpas == None):
  56. return
  57. cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex")
  58. global force_freq
  59. if force_freq:
  60. cmd = cmd + " freq=" + force_freq
  61. if "FAIL" in wpas.request(cmd):
  62. return False
  63. return True
  64. def wpas_get_handover_req():
  65. wpas = wpas_connect()
  66. if (wpas == None):
  67. return None
  68. res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip().decode("hex")
  69. if "FAIL" in res:
  70. return None
  71. return res
  72. def wpas_get_handover_req_wps():
  73. wpas = wpas_connect()
  74. if (wpas == None):
  75. return None
  76. return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex")
  77. def wpas_get_handover_sel(tag=False):
  78. wpas = wpas_connect()
  79. if (wpas == None):
  80. return None
  81. if tag:
  82. return wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip().decode("hex")
  83. return wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip().decode("hex")
  84. def wpas_get_handover_sel_wps():
  85. wpas = wpas_connect()
  86. if (wpas == None):
  87. return None
  88. res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR");
  89. if "FAIL" in res:
  90. return None
  91. return res.rstrip().decode("hex")
  92. def wpas_report_handover(req, sel, type):
  93. wpas = wpas_connect()
  94. if (wpas == None):
  95. return None
  96. cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex")
  97. global force_freq
  98. if force_freq:
  99. cmd = cmd + " freq=" + force_freq
  100. return wpas.request(cmd)
  101. def wpas_report_handover_wsc(req, sel, type):
  102. wpas = wpas_connect()
  103. if (wpas == None):
  104. return None
  105. cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex")
  106. if force_freq:
  107. cmd = cmd + " freq=" + force_freq
  108. return wpas.request(cmd)
  109. def p2p_handover_client(llc):
  110. message = nfc.ndef.HandoverRequestMessage(version="1.2")
  111. message.nonce = random.randint(0, 0xffff)
  112. global include_p2p_req
  113. if include_p2p_req:
  114. data = wpas_get_handover_req()
  115. if (data == None):
  116. print "Could not get handover request carrier record from wpa_supplicant"
  117. return
  118. print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
  119. datamsg = nfc.ndef.Message(data)
  120. message.add_carrier(datamsg[0], "active", datamsg[1:])
  121. global include_wps_req
  122. if include_wps_req:
  123. print "Handover request (pre-WPS):"
  124. try:
  125. print message.pretty()
  126. except Exception, e:
  127. print e
  128. data = wpas_get_handover_req_wps()
  129. if data:
  130. print "Add WPS request in addition to P2P"
  131. datamsg = nfc.ndef.Message(data)
  132. message.add_carrier(datamsg[0], "active", datamsg[1:])
  133. print "Handover request:"
  134. try:
  135. print message.pretty()
  136. except Exception, e:
  137. print e
  138. print str(message).encode("hex")
  139. client = nfc.handover.HandoverClient(llc)
  140. try:
  141. print "Trying handover";
  142. client.connect()
  143. print "Connected for handover"
  144. except nfc.llcp.ConnectRefused:
  145. print "Handover connection refused"
  146. client.close()
  147. return
  148. except Exception, e:
  149. print "Other exception: " + str(e)
  150. client.close()
  151. return
  152. print "Sending handover request"
  153. if not client.send(message):
  154. print "Failed to send handover request"
  155. print "Receiving handover response"
  156. message = client._recv()
  157. if message is None:
  158. print "No response received"
  159. client.close()
  160. return
  161. if message.type != "urn:nfc:wkt:Hs":
  162. print "Response was not Hs - received: " + message.type
  163. client.close()
  164. return
  165. print "Received message"
  166. try:
  167. print message.pretty()
  168. except Exception, e:
  169. print e
  170. print str(message).encode("hex")
  171. message = nfc.ndef.HandoverSelectMessage(message)
  172. print "Handover select received"
  173. try:
  174. print message.pretty()
  175. except Exception, e:
  176. print e
  177. for carrier in message.carriers:
  178. print "Remote carrier type: " + carrier.type
  179. if carrier.type == "application/vnd.wfa.p2p":
  180. print "P2P carrier type match - send to wpa_supplicant"
  181. wpas_report_handover(data, carrier.record, "INIT")
  182. break
  183. print "Remove peer"
  184. client.close()
  185. print "Done with handover"
  186. global only_one
  187. if only_one:
  188. print "only_one -> stop loop"
  189. global continue_loop
  190. continue_loop = False
  191. global no_wait
  192. if no_wait:
  193. print "Trying to exit.."
  194. global terminate_now
  195. terminate_now = True
  196. class HandoverServer(nfc.handover.HandoverServer):
  197. def __init__(self, llc):
  198. super(HandoverServer, self).__init__(llc)
  199. self.sent_carrier = None
  200. self.ho_server_processing = False
  201. self.success = False
  202. def process_request(self, request):
  203. self.ho_server_processing = True
  204. clear_raw_mode()
  205. print "HandoverServer - request received"
  206. try:
  207. print "Parsed handover request: " + request.pretty()
  208. except Exception, e:
  209. print e
  210. sel = nfc.ndef.HandoverSelectMessage(version="1.2")
  211. found = False
  212. for carrier in request.carriers:
  213. print "Remote carrier type: " + carrier.type
  214. if carrier.type == "application/vnd.wfa.p2p":
  215. print "P2P carrier type match - add P2P carrier record"
  216. found = True
  217. self.received_carrier = carrier.record
  218. print "Carrier record:"
  219. try:
  220. print carrier.record.pretty()
  221. except Exception, e:
  222. print e
  223. data = wpas_get_handover_sel()
  224. if data is None:
  225. print "Could not get handover select carrier record from wpa_supplicant"
  226. continue
  227. print "Handover select carrier record from wpa_supplicant:"
  228. print data.encode("hex")
  229. self.sent_carrier = data
  230. wpas_report_handover(self.received_carrier, self.sent_carrier,
  231. "RESP")
  232. message = nfc.ndef.Message(data);
  233. sel.add_carrier(message[0], "active", message[1:])
  234. break
  235. for carrier in request.carriers:
  236. if found:
  237. break
  238. print "Remote carrier type: " + carrier.type
  239. if carrier.type == "application/vnd.wfa.wsc":
  240. print "WSC carrier type match - add WSC carrier record"
  241. found = True
  242. self.received_carrier = carrier.record
  243. print "Carrier record:"
  244. try:
  245. print carrier.record.pretty()
  246. except Exception, e:
  247. print e
  248. data = wpas_get_handover_sel_wps()
  249. if data is None:
  250. print "Could not get handover select carrier record from wpa_supplicant"
  251. continue
  252. print "Handover select carrier record from wpa_supplicant:"
  253. print data.encode("hex")
  254. self.sent_carrier = data
  255. wpas_report_handover_wsc(self.received_carrier,
  256. self.sent_carrier, "RESP")
  257. message = nfc.ndef.Message(data);
  258. sel.add_carrier(message[0], "active", message[1:])
  259. found = True
  260. break
  261. print "Handover select:"
  262. try:
  263. print sel.pretty()
  264. except Exception, e:
  265. print e
  266. print str(sel).encode("hex")
  267. print "Sending handover select"
  268. self.success = True
  269. return sel
  270. def clear_raw_mode():
  271. import sys, tty, termios
  272. global prev_tcgetattr, in_raw_mode
  273. if not in_raw_mode:
  274. return
  275. fd = sys.stdin.fileno()
  276. termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
  277. in_raw_mode = False
  278. def getch():
  279. import sys, tty, termios, select
  280. global prev_tcgetattr, in_raw_mode
  281. fd = sys.stdin.fileno()
  282. prev_tcgetattr = termios.tcgetattr(fd)
  283. ch = None
  284. try:
  285. tty.setraw(fd)
  286. in_raw_mode = True
  287. [i, o, e] = select.select([fd], [], [], 0.05)
  288. if i:
  289. ch = sys.stdin.read(1)
  290. finally:
  291. termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
  292. in_raw_mode = False
  293. return ch
  294. def p2p_tag_read(tag):
  295. success = False
  296. if len(tag.ndef.message):
  297. for record in tag.ndef.message:
  298. print "record type " + record.type
  299. if record.type == "application/vnd.wfa.wsc":
  300. print "WPS tag - send to wpa_supplicant"
  301. success = wpas_tag_read(tag.ndef.message)
  302. break
  303. if record.type == "application/vnd.wfa.p2p":
  304. print "P2P tag - send to wpa_supplicant"
  305. success = wpas_tag_read(tag.ndef.message)
  306. break
  307. else:
  308. print "Empty tag"
  309. return success
  310. def rdwr_connected_p2p_write(tag):
  311. print "Tag found - writing"
  312. global p2p_sel_data
  313. tag.ndef.message = str(p2p_sel_data)
  314. print "Done - remove tag"
  315. global only_one
  316. if only_one:
  317. global continue_loop
  318. continue_loop = False
  319. global p2p_sel_wait_remove
  320. return p2p_sel_wait_remove
  321. def wps_write_p2p_handover_sel(clf, wait_remove=True):
  322. print "Write P2P handover select"
  323. data = wpas_get_handover_sel(tag=True)
  324. if (data == None):
  325. print "Could not get P2P handover select from wpa_supplicant"
  326. return
  327. global p2p_sel_wait_remove
  328. p2p_sel_wait_remove = wait_remove
  329. global p2p_sel_data
  330. p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2")
  331. message = nfc.ndef.Message(data);
  332. p2p_sel_data.add_carrier(message[0], "active", message[1:])
  333. print "Handover select:"
  334. try:
  335. print p2p_sel_data.pretty()
  336. except Exception, e:
  337. print e
  338. print str(p2p_sel_data).encode("hex")
  339. print "Touch an NFC tag"
  340. clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write})
  341. def rdwr_connected(tag):
  342. global only_one, no_wait
  343. print "Tag connected: " + str(tag)
  344. if tag.ndef:
  345. print "NDEF tag: " + tag.type
  346. try:
  347. print tag.ndef.message.pretty()
  348. except Exception, e:
  349. print e
  350. success = p2p_tag_read(tag)
  351. if only_one and success:
  352. global continue_loop
  353. continue_loop = False
  354. else:
  355. print "Not an NDEF tag - remove tag"
  356. return not no_wait
  357. def llcp_worker(llc):
  358. global init_on_touch
  359. if init_on_touch:
  360. print "Starting handover client"
  361. p2p_handover_client(llc)
  362. return
  363. global no_input
  364. if no_input:
  365. print "Wait for handover to complete"
  366. else:
  367. print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)"
  368. global srv
  369. global wait_connection
  370. while not wait_connection and srv.sent_carrier is None:
  371. if srv.ho_server_processing:
  372. time.sleep(0.025)
  373. elif no_input:
  374. time.sleep(0.5)
  375. else:
  376. global include_wps_req, include_p2p_req
  377. res = getch()
  378. if res == 'i':
  379. include_wps_req = True
  380. include_p2p_req = True
  381. elif res == 'p':
  382. include_wps_req = False
  383. include_p2p_req = True
  384. elif res == 'w':
  385. include_wps_req = True
  386. include_p2p_req = False
  387. else:
  388. continue
  389. clear_raw_mode()
  390. print "Starting handover client"
  391. p2p_handover_client(llc)
  392. return
  393. clear_raw_mode()
  394. print "Exiting llcp_worker thread"
  395. def llcp_startup(clf, llc):
  396. print "Start LLCP server"
  397. global srv
  398. srv = HandoverServer(llc)
  399. return llc
  400. def llcp_connected(llc):
  401. print "P2P LLCP connected"
  402. global wait_connection
  403. wait_connection = False
  404. global init_on_touch
  405. if not init_on_touch:
  406. global srv
  407. srv.start()
  408. if init_on_touch or not no_input:
  409. threading.Thread(target=llcp_worker, args=(llc,)).start()
  410. return True
  411. def terminate_loop():
  412. global terminate_now
  413. return terminate_now
  414. def main():
  415. clf = nfc.ContactlessFrontend()
  416. parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and WPS NFC operations')
  417. parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
  418. action='store_const', dest='loglevel',
  419. help='verbose debug output')
  420. parser.add_argument('-q', const=logging.WARNING, action='store_const',
  421. dest='loglevel', help='be quiet')
  422. parser.add_argument('--only-one', '-1', action='store_true',
  423. help='run only one operation and exit')
  424. parser.add_argument('--init-on-touch', '-I', action='store_true',
  425. help='initiate handover on touch')
  426. parser.add_argument('--no-wait', action='store_true',
  427. help='do not wait for tag to be removed before exiting')
  428. parser.add_argument('--ifname', '-i',
  429. help='network interface name')
  430. parser.add_argument('--no-wps-req', '-N', action='store_true',
  431. help='do not include WPS carrier record in request')
  432. parser.add_argument('--no-input', '-a', action='store_true',
  433. help='do not use stdout input to initiate handover')
  434. parser.add_argument('--tag-read-only', '-t', action='store_true',
  435. help='tag read only (do not allow connection handover)')
  436. parser.add_argument('--freq', '-f',
  437. help='forced frequency of operating channel in MHz')
  438. parser.add_argument('command', choices=['write-p2p-sel'],
  439. nargs='?')
  440. args = parser.parse_args()
  441. global only_one
  442. only_one = args.only_one
  443. global no_wait
  444. no_wait = args.no_wait
  445. global force_freq
  446. force_freq = args.freq
  447. logging.basicConfig(level=args.loglevel)
  448. global init_on_touch
  449. init_on_touch = args.init_on_touch
  450. if args.ifname:
  451. global ifname
  452. ifname = args.ifname
  453. print "Selected ifname " + ifname
  454. if args.no_wps_req:
  455. global include_wps_req
  456. include_wps_req = False
  457. if args.no_input:
  458. global no_input
  459. no_input = True
  460. clf = nfc.ContactlessFrontend()
  461. global wait_connection
  462. try:
  463. if not clf.open("usb"):
  464. print "Could not open connection with an NFC device"
  465. raise SystemExit
  466. if args.command == "write-p2p-sel":
  467. wps_write_p2p_handover_sel(clf, wait_remove=not args.no_wait)
  468. raise SystemExit
  469. global continue_loop
  470. while continue_loop:
  471. print "Waiting for a tag or peer to be touched"
  472. wait_connection = True
  473. try:
  474. if args.tag_read_only:
  475. if not clf.connect(rdwr={'on-connect': rdwr_connected}):
  476. break
  477. else:
  478. if not clf.connect(rdwr={'on-connect': rdwr_connected},
  479. llcp={'on-startup': llcp_startup,
  480. 'on-connect': llcp_connected},
  481. terminate=terminate_loop):
  482. break
  483. except Exception, e:
  484. print "clf.connect failed"
  485. global srv
  486. if only_one and srv and srv.success:
  487. raise SystemExit
  488. except KeyboardInterrupt:
  489. raise SystemExit
  490. finally:
  491. clf.close()
  492. raise SystemExit
  493. if __name__ == '__main__':
  494. main()