blob: 1883545533394ce28abca8bdad04ca51a915cdb4 [file] [log] [blame]
Hai Shalomfdcde762020-04-02 11:19:20 -07001#!/usr/bin/python3
2#
3# Example nfcpy to wpa_supplicant wrapper for DPP NFC operations
4# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
5# Copyright (c) 2019-2020, The Linux Foundation
6#
7# This software may be distributed under the terms of the BSD license.
8# See README for more details.
9
10import os
Hai Shalom4fbc08f2020-05-18 12:37:00 -070011import struct
Hai Shalomfdcde762020-04-02 11:19:20 -070012import sys
13import time
14import threading
15import argparse
16
17import nfc
18import ndef
19
20import logging
21
Hai Shalom4fbc08f2020-05-18 12:37:00 -070022scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
Hai Shalomfdcde762020-04-02 11:19:20 -070023sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
24import wpaspy
25
26wpas_ctrl = '/var/run/wpa_supplicant'
27ifname = None
28init_on_touch = False
29in_raw_mode = False
30prev_tcgetattr = 0
31no_input = False
32srv = None
33continue_loop = True
34terminate_now = False
35summary_file = None
36success_file = None
Hai Shalom4fbc08f2020-05-18 12:37:00 -070037my_crn_ready = False
38my_crn = None
39peer_crn = None
40hs_sent = False
41mutex = threading.Lock()
Hai Shalomfdcde762020-04-02 11:19:20 -070042
43def summary(txt):
Hai Shalom4fbc08f2020-05-18 12:37:00 -070044 with mutex:
45 print(txt)
46 if summary_file:
47 with open(summary_file, 'a') as f:
48 f.write(txt + "\n")
Hai Shalomfdcde762020-04-02 11:19:20 -070049
50def success_report(txt):
51 summary(txt)
52 if success_file:
53 with open(success_file, 'a') as f:
54 f.write(txt + "\n")
55
56def wpas_connect():
57 ifaces = []
58 if os.path.isdir(wpas_ctrl):
59 try:
60 ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
61 except OSError as error:
Hai Shalom4fbc08f2020-05-18 12:37:00 -070062 summary("Could not find wpa_supplicant: %s", str(error))
Hai Shalomfdcde762020-04-02 11:19:20 -070063 return None
64
65 if len(ifaces) < 1:
Hai Shalom4fbc08f2020-05-18 12:37:00 -070066 summary("No wpa_supplicant control interface found")
Hai Shalomfdcde762020-04-02 11:19:20 -070067 return None
68
69 for ctrl in ifaces:
70 if ifname:
71 if ifname not in ctrl:
72 continue
73 try:
Hai Shalom4fbc08f2020-05-18 12:37:00 -070074 summary("Trying to use control interface " + ctrl)
Hai Shalomfdcde762020-04-02 11:19:20 -070075 wpas = wpaspy.Ctrl(ctrl)
76 return wpas
77 except Exception as e:
78 pass
79 return None
80
81def dpp_nfc_uri_process(uri):
82 wpas = wpas_connect()
83 if wpas is None:
84 return False
85 peer_id = wpas.request("DPP_NFC_URI " + uri)
86 if "FAIL" in peer_id:
Hai Shalom4fbc08f2020-05-18 12:37:00 -070087 summary("Could not parse DPP URI from NFC URI record")
Hai Shalomfdcde762020-04-02 11:19:20 -070088 return False
89 peer_id = int(peer_id)
Hai Shalom4fbc08f2020-05-18 12:37:00 -070090 summary("peer_id=%d for URI from NFC Tag: %s" % (peer_id, uri))
Hai Shalomfdcde762020-04-02 11:19:20 -070091 cmd = "DPP_AUTH_INIT peer=%d" % peer_id
Hai Shalom4fbc08f2020-05-18 12:37:00 -070092 global enrollee_only, configurator_only, config_params
93 if enrollee_only:
94 cmd += " role=enrollee"
95 elif configurator_only:
96 cmd += " role=configurator"
97 if config_params:
98 cmd += " " + config_params
99 summary("Initiate DPP authentication: " + cmd)
Hai Shalomfdcde762020-04-02 11:19:20 -0700100 res = wpas.request(cmd)
101 if "OK" not in res:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700102 summary("Failed to initiate DPP Authentication")
Hai Shalomfdcde762020-04-02 11:19:20 -0700103 return False
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700104 summary("DPP Authentication initiated")
Hai Shalomfdcde762020-04-02 11:19:20 -0700105 return True
106
107def dpp_hs_tag_read(record):
108 wpas = wpas_connect()
109 if wpas is None:
110 return False
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700111 summary(record)
Hai Shalomfdcde762020-04-02 11:19:20 -0700112 if len(record.data) < 5:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700113 summary("Too short DPP HS")
Hai Shalomfdcde762020-04-02 11:19:20 -0700114 return False
115 if record.data[0] != 0:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700116 summary("Unexpected URI Identifier Code")
Hai Shalomfdcde762020-04-02 11:19:20 -0700117 return False
118 uribuf = record.data[1:]
119 try:
120 uri = uribuf.decode()
121 except:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700122 summary("Invalid URI payload")
Hai Shalomfdcde762020-04-02 11:19:20 -0700123 return False
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700124 summary("URI: " + uri)
Hai Shalomfdcde762020-04-02 11:19:20 -0700125 if not uri.startswith("DPP:"):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700126 summary("Not a DPP URI")
Hai Shalomfdcde762020-04-02 11:19:20 -0700127 return False
128 return dpp_nfc_uri_process(uri)
129
130def get_status(wpas, extra=None):
131 if extra:
132 extra = "-" + extra
133 else:
134 extra = ""
135 res = wpas.request("STATUS" + extra)
136 lines = res.splitlines()
137 vals = dict()
138 for l in lines:
139 try:
140 [name, value] = l.split('=', 1)
141 except ValueError:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700142 summary("Ignore unexpected status line: %s" % l)
Hai Shalomfdcde762020-04-02 11:19:20 -0700143 continue
144 vals[name] = value
145 return vals
146
147def get_status_field(wpas, field, extra=None):
148 vals = get_status(wpas, extra)
149 if field in vals:
150 return vals[field]
151 return None
152
153def own_addr(wpas):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700154 addr = get_status_field(wpas, "address")
155 if addr is None:
156 addr = get_status_field(wpas, "bssid[0]")
157 return addr
Hai Shalomfdcde762020-04-02 11:19:20 -0700158
159def dpp_bootstrap_gen(wpas, type="qrcode", chan=None, mac=None, info=None,
160 curve=None, key=None):
161 cmd = "DPP_BOOTSTRAP_GEN type=" + type
162 if chan:
163 cmd += " chan=" + chan
164 if mac:
165 if mac is True:
166 mac = own_addr(wpas)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700167 if mac is None:
168 summary("Could not determine local MAC address for bootstrap info")
169 else:
170 cmd += " mac=" + mac.replace(':', '')
Hai Shalomfdcde762020-04-02 11:19:20 -0700171 if info:
172 cmd += " info=" + info
173 if curve:
174 cmd += " curve=" + curve
175 if key:
176 cmd += " key=" + key
177 res = wpas.request(cmd)
178 if "FAIL" in res:
179 raise Exception("Failed to generate bootstrapping info")
180 return int(res)
181
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700182def wpas_get_nfc_uri(start_listen=True, pick_channel=False):
Hai Shalomfdcde762020-04-02 11:19:20 -0700183 wpas = wpas_connect()
184 if wpas is None:
185 return None
186 global own_id, chanlist
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700187 chan = chanlist
188 if chan is None and get_status_field(wpas, "bssid[0]"):
189 freq = get_status_field(wpas, "freq")
190 if freq:
191 freq = int(freq)
192 if freq >= 2412 and freq <= 2462:
193 chan = "81/%d" % ((freq - 2407) / 5)
194 summary("Use current AP operating channel (%d MHz) as the URI channel list (%s)" % (freq, chan))
195 if chan is None and pick_channel:
196 chan = "81/6"
197 summary("Use channel 2437 MHz since no other preference provided")
198 own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chan, mac=True)
Hai Shalomfdcde762020-04-02 11:19:20 -0700199 res = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
200 if "FAIL" in res:
201 return None
202 if start_listen:
203 wpas.request("DPP_LISTEN 2412 netrole=configurator")
204 return res
205
206def wpas_report_handover_req(uri):
207 wpas = wpas_connect()
208 if wpas is None:
209 return None
210 global own_id
211 cmd = "DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (own_id, uri)
212 return wpas.request(cmd)
213
214def wpas_report_handover_sel(uri):
215 wpas = wpas_connect()
216 if wpas is None:
217 return None
218 global own_id
219 cmd = "DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (own_id, uri)
220 return wpas.request(cmd)
221
222def dpp_handover_client(llc):
223 uri = wpas_get_nfc_uri(start_listen=False)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700224 if uri is None:
225 summary("Cannot start handover client - no bootstrap URI available")
226 return
Hai Shalomfdcde762020-04-02 11:19:20 -0700227 uri = ndef.UriRecord(uri)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700228 summary("NFC URI record for DPP: " + str(uri))
Hai Shalomfdcde762020-04-02 11:19:20 -0700229 carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700230 crn = os.urandom(2)
231 hr = ndef.HandoverRequestRecord(version="1.4", crn=crn)
Hai Shalomfdcde762020-04-02 11:19:20 -0700232 hr.add_alternative_carrier('active', carrier.name)
233 message = [hr, carrier]
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700234 summary("NFC Handover Request message for DPP: " + str(message))
Hai Shalomfdcde762020-04-02 11:19:20 -0700235
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700236 global peer_crn
237 if peer_crn is not None:
238 summary("NFC handover request from peer was already received - do not send own")
239 return
Hai Shalomfdcde762020-04-02 11:19:20 -0700240 client = nfc.handover.HandoverClient(llc)
241 try:
242 summary("Trying to initiate NFC connection handover")
243 client.connect()
244 summary("Connected for handover")
245 except nfc.llcp.ConnectRefused:
246 summary("Handover connection refused")
247 client.close()
248 return
249 except Exception as e:
250 summary("Other exception: " + str(e))
251 client.close()
252 return
253
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700254 if peer_crn is not None:
255 summary("NFC handover request from peer was already received - do not send own")
256 client.close()
257 return
258
Hai Shalomfdcde762020-04-02 11:19:20 -0700259 summary("Sending handover request")
260
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700261 global my_crn, my_crn_ready, hs_sent
262 my_crn_ready = True
263
Hai Shalomfdcde762020-04-02 11:19:20 -0700264 if not client.send_records(message):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700265 my_crn_ready = False
Hai Shalomfdcde762020-04-02 11:19:20 -0700266 summary("Failed to send handover request")
267 client.close()
268 return
269
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700270 my_crn, = struct.unpack('>H', crn)
271
Hai Shalomfdcde762020-04-02 11:19:20 -0700272 summary("Receiving handover response")
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700273 try:
274 message = client.recv_records(timeout=3.0)
275 except Exception as e:
276 # This is fine if we are the handover selector
277 if hs_sent:
278 summary("Client receive failed as expected since I'm the handover server: %s" % str(e))
279 else:
280 summary("Client receive failed: %s" % str(e))
281 message = None
Hai Shalomfdcde762020-04-02 11:19:20 -0700282 if message is None:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700283 if hs_sent:
284 summary("No response received as expected since I'm the handover server")
285 else:
286 summary("No response received")
Hai Shalomfdcde762020-04-02 11:19:20 -0700287 client.close()
288 return
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700289 summary("Received message: " + str(message))
Hai Shalomfdcde762020-04-02 11:19:20 -0700290 if len(message) < 1 or \
291 not isinstance(message[0], ndef.HandoverSelectRecord):
292 summary("Response was not Hs - received: " + message.type)
293 client.close()
294 return
295
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700296 summary("Received handover select message")
297 summary("alternative carriers: " + str(message[0].alternative_carriers))
Hai Shalomfdcde762020-04-02 11:19:20 -0700298
299 dpp_found = False
300 for carrier in message:
301 if isinstance(carrier, ndef.HandoverSelectRecord):
302 continue
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700303 summary("Remote carrier type: " + carrier.type)
Hai Shalomfdcde762020-04-02 11:19:20 -0700304 if carrier.type == "application/vnd.wfa.dpp":
305 if len(carrier.data) == 0 or carrier.data[0] != 0:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700306 summary("URI Identifier Code 'None' not seen")
Hai Shalomfdcde762020-04-02 11:19:20 -0700307 continue
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700308 summary("DPP carrier type match - send to wpa_supplicant")
Hai Shalomfdcde762020-04-02 11:19:20 -0700309 dpp_found = True
310 uri = carrier.data[1:].decode("utf-8")
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700311 summary("DPP URI: " + uri)
Hai Shalomfdcde762020-04-02 11:19:20 -0700312 res = wpas_report_handover_sel(uri)
313 if res is None or "FAIL" in res:
314 summary("DPP handover report rejected")
315 break
316
317 success_report("DPP handover reported successfully (initiator)")
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700318 summary("peer_id=" + res)
Hai Shalomfdcde762020-04-02 11:19:20 -0700319 peer_id = int(res)
Hai Shalomfdcde762020-04-02 11:19:20 -0700320 wpas = wpas_connect()
321 if wpas is None:
322 break
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700323
324 global enrollee_only
325 global config_params
326 if enrollee_only:
327 extra = " role=enrollee"
328 elif config_params:
329 extra = " role=configurator " + config_params
330 else:
331 # TODO: Single Configurator instance
332 res = wpas.request("DPP_CONFIGURATOR_ADD")
333 if "FAIL" in res:
334 summary("Failed to initiate Configurator")
335 break
336 conf_id = int(res)
337 extra = " conf=sta-dpp configurator=%d" % conf_id
Hai Shalomfdcde762020-04-02 11:19:20 -0700338 global own_id
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700339 summary("Initiate DPP authentication")
340 cmd = "DPP_AUTH_INIT peer=%d own=%d" % (peer_id, own_id)
341 cmd += extra
Hai Shalomfdcde762020-04-02 11:19:20 -0700342 res = wpas.request(cmd)
343 if "FAIL" in res:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700344 summary("Failed to initiate DPP authentication")
Hai Shalomfdcde762020-04-02 11:19:20 -0700345 break
346
347 if not dpp_found:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700348 summary("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
Hai Shalomfdcde762020-04-02 11:19:20 -0700349 client.close()
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700350 summary("Returning from dpp_handover_client")
Hai Shalomfdcde762020-04-02 11:19:20 -0700351 return
352
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700353 summary("Remove peer")
Hai Shalomfdcde762020-04-02 11:19:20 -0700354 client.close()
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700355 summary("Done with handover")
Hai Shalomfdcde762020-04-02 11:19:20 -0700356 global only_one
357 if only_one:
358 print("only_one -> stop loop")
359 global continue_loop
360 continue_loop = False
361
362 global no_wait
363 if no_wait:
364 print("Trying to exit..")
365 global terminate_now
366 terminate_now = True
367
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700368 summary("Returning from dpp_handover_client")
Hai Shalomfdcde762020-04-02 11:19:20 -0700369
370class HandoverServer(nfc.handover.HandoverServer):
371 def __init__(self, llc):
372 super(HandoverServer, self).__init__(llc)
373 self.sent_carrier = None
374 self.ho_server_processing = False
375 self.success = False
376 self.try_own = False
377
378 def process_handover_request_message(self, records):
379 self.ho_server_processing = True
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700380 global in_raw_mode
381 was_in_raw_mode = in_raw_mode
Hai Shalomfdcde762020-04-02 11:19:20 -0700382 clear_raw_mode()
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700383 if was_in_raw_mode:
384 print("\n")
385 summary("HandoverServer - request received: " + str(records))
Hai Shalomfdcde762020-04-02 11:19:20 -0700386
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700387 global my_crn, peer_crn, my_crn_ready
388
389 for carrier in records:
390 if not isinstance(carrier, ndef.HandoverRequestRecord):
391 continue
392 if carrier.collision_resolution_number:
393 peer_crn = carrier.collision_resolution_number
394 summary("peer_crn: %d" % peer_crn)
395
396 if my_crn is None and my_crn_ready:
397 summary("Still trying to send own handover request - wait a moment to see if that succeeds before checking crn values")
398 for i in range(10):
399 if my_crn is not None:
400 break
401 time.sleep(0.01)
402 if my_crn is not None:
403 summary("my_crn: %d" % my_crn)
404
405 if my_crn is not None and peer_crn is not None:
406 if my_crn == peer_crn:
407 summary("Same crn used - automatic collision resolution failed")
408 # TODO: Should generate a new Handover Request message
409 return ''
410 if ((my_crn & 1) == (peer_crn & 1) and my_crn > peer_crn) or \
411 ((my_crn & 1) != (peer_crn & 1) and my_crn < peer_crn):
412 summary("I'm the Handover Selector Device")
413 pass
414 else:
415 summary("Peer is the Handover Selector device")
416 summary("Ignore the received request.")
417 return ''
418
Hai Shalomfdcde762020-04-02 11:19:20 -0700419 hs = ndef.HandoverSelectRecord('1.4')
420 sel = [hs]
421
422 found = False
423
424 for carrier in records:
425 if isinstance(carrier, ndef.HandoverRequestRecord):
426 continue
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700427 summary("Remote carrier type: " + carrier.type)
Hai Shalomfdcde762020-04-02 11:19:20 -0700428 if carrier.type == "application/vnd.wfa.dpp":
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700429 summary("DPP carrier type match - add DPP carrier record")
Hai Shalomfdcde762020-04-02 11:19:20 -0700430 if len(carrier.data) == 0 or carrier.data[0] != 0:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700431 summary("URI Identifier Code 'None' not seen")
Hai Shalomfdcde762020-04-02 11:19:20 -0700432 continue
433 uri = carrier.data[1:].decode("utf-8")
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700434 summary("Received DPP URI: " + uri)
Hai Shalomfdcde762020-04-02 11:19:20 -0700435
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700436 data = wpas_get_nfc_uri(start_listen=False, pick_channel=True)
437 summary("Own URI (pre-processing): %s" % data)
Hai Shalomfdcde762020-04-02 11:19:20 -0700438
439 res = wpas_report_handover_req(uri)
440 if res is None or "FAIL" in res:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700441 summary("DPP handover request processing failed")
Hai Shalomfdcde762020-04-02 11:19:20 -0700442 continue
443
444 found = True
Hai Shalomfdcde762020-04-02 11:19:20 -0700445
446 wpas = wpas_connect()
447 if wpas is None:
448 continue
449 global own_id
450 data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
451 if "FAIL" in data:
452 continue
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700453 summary("Own URI (post-processing): %s" % data)
Hai Shalomfdcde762020-04-02 11:19:20 -0700454 uri = ndef.UriRecord(data)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700455 summary("Own bootstrapping NFC URI record: " + str(uri))
Hai Shalomfdcde762020-04-02 11:19:20 -0700456
457 info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id)
458 freq = None
459 for line in info.splitlines():
460 if line.startswith("use_freq="):
461 freq = int(line.split('=')[1])
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700462 if freq is None or freq == 0:
463 summary("No channel negotiated over NFC - use channel 6")
464 freq = 2437
465 else:
466 summary("Negotiated channel: %d MHz" % freq)
467 if get_status_field(wpas, "bssid[0]"):
468 summary("Own AP freq: %s MHz" % str(get_status_field(wpas, "freq")))
469 if get_status_field(wpas, "beacon_set", extra="DRIVER") is None:
470 summary("Enable beaconing to have radio ready for RX")
471 wpas.request("DISABLE")
472 wpas.request("SET start_disabled 0")
473 wpas.request("ENABLE")
474 cmd = "DPP_LISTEN %d" % freq
475 global enrollee_only
476 global configurator_only
477 if enrollee_only:
478 cmd += " role=enrollee"
479 elif configurator_only:
480 cmd += " role=configurator"
481 summary(cmd)
482 res = wpas.request(cmd)
Hai Shalomfdcde762020-04-02 11:19:20 -0700483 if "OK" not in res:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700484 summary("Failed to start DPP listen")
Hai Shalomfdcde762020-04-02 11:19:20 -0700485 break
486
487 carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700488 summary("Own DPP carrier record: " + str(carrier))
Hai Shalomfdcde762020-04-02 11:19:20 -0700489 hs.add_alternative_carrier('active', carrier.name)
490 sel = [hs, carrier]
491 break
492
493 summary("Sending handover select: " + str(sel))
494 if found:
495 self.success = True
496 else:
497 self.try_own = True
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700498 global hs_sent
499 hs_sent = True
Hai Shalomfdcde762020-04-02 11:19:20 -0700500 return sel
501
502def clear_raw_mode():
503 import sys, tty, termios
504 global prev_tcgetattr, in_raw_mode
505 if not in_raw_mode:
506 return
507 fd = sys.stdin.fileno()
508 termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
509 in_raw_mode = False
510
511def getch():
512 import sys, tty, termios, select
513 global prev_tcgetattr, in_raw_mode
514 fd = sys.stdin.fileno()
515 prev_tcgetattr = termios.tcgetattr(fd)
516 ch = None
517 try:
518 tty.setraw(fd)
519 in_raw_mode = True
520 [i, o, e] = select.select([fd], [], [], 0.05)
521 if i:
522 ch = sys.stdin.read(1)
523 finally:
524 termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
525 in_raw_mode = False
526 return ch
527
528def dpp_tag_read(tag):
529 success = False
530 for record in tag.ndef.records:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700531 summary(record)
532 summary("record type " + record.type)
Hai Shalomfdcde762020-04-02 11:19:20 -0700533 if record.type == "application/vnd.wfa.dpp":
534 summary("DPP HS tag - send to wpa_supplicant")
535 success = dpp_hs_tag_read(record)
536 break
537 if isinstance(record, ndef.UriRecord):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700538 summary("URI record: uri=" + record.uri)
539 summary("URI record: iri=" + record.iri)
Hai Shalomfdcde762020-04-02 11:19:20 -0700540 if record.iri.startswith("DPP:"):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700541 summary("DPP URI")
Hai Shalomfdcde762020-04-02 11:19:20 -0700542 if not dpp_nfc_uri_process(record.iri):
543 break
544 success = True
545 else:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700546 summary("Ignore unknown URI")
Hai Shalomfdcde762020-04-02 11:19:20 -0700547 break
548
549 if success:
550 success_report("Tag read succeeded")
551
552 return success
553
554def rdwr_connected_write_tag(tag):
555 summary("Tag found - writing - " + str(tag))
556 if not tag.ndef.is_writeable:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700557 summary("Not a writable tag")
Hai Shalomfdcde762020-04-02 11:19:20 -0700558 return
559 global dpp_tag_data
560 if tag.ndef.capacity < len(dpp_tag_data):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700561 summary("Not enough room for the message")
Hai Shalomfdcde762020-04-02 11:19:20 -0700562 return
563 tag.ndef.records = dpp_tag_data
564 success_report("Tag write succeeded")
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700565 summary("Done - remove tag")
Hai Shalomfdcde762020-04-02 11:19:20 -0700566 global only_one
567 if only_one:
568 global continue_loop
569 continue_loop = False
570 global dpp_sel_wait_remove
571 return dpp_sel_wait_remove
572
573def write_nfc_uri(clf, wait_remove=True):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700574 summary("Write NFC URI record")
Hai Shalomfdcde762020-04-02 11:19:20 -0700575 data = wpas_get_nfc_uri()
576 if data is None:
577 summary("Could not get NFC URI from wpa_supplicant")
578 return
579
580 global dpp_sel_wait_remove
581 dpp_sel_wait_remove = wait_remove
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700582 summary("URI: %s" % data)
Hai Shalomfdcde762020-04-02 11:19:20 -0700583 uri = ndef.UriRecord(data)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700584 summary(uri)
Hai Shalomfdcde762020-04-02 11:19:20 -0700585
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700586 summary("Touch an NFC tag")
Hai Shalomfdcde762020-04-02 11:19:20 -0700587 global dpp_tag_data
588 dpp_tag_data = [uri]
589 clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
590
591def write_nfc_hs(clf, wait_remove=True):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700592 summary("Write NFC Handover Select record on a tag")
Hai Shalomfdcde762020-04-02 11:19:20 -0700593 data = wpas_get_nfc_uri()
594 if data is None:
595 summary("Could not get NFC URI from wpa_supplicant")
596 return
597
598 global dpp_sel_wait_remove
599 dpp_sel_wait_remove = wait_remove
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700600 summary("URI: %s" % data)
Hai Shalomfdcde762020-04-02 11:19:20 -0700601 uri = ndef.UriRecord(data)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700602 summary(uri)
Hai Shalomfdcde762020-04-02 11:19:20 -0700603 carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
604 hs = ndef.HandoverSelectRecord('1.4')
605 hs.add_alternative_carrier('active', carrier.name)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700606 summary(hs)
607 summary(carrier)
Hai Shalomfdcde762020-04-02 11:19:20 -0700608
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700609 summary("Touch an NFC tag")
Hai Shalomfdcde762020-04-02 11:19:20 -0700610 global dpp_tag_data
611 dpp_tag_data = [hs, carrier]
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700612 summary(dpp_tag_data)
Hai Shalomfdcde762020-04-02 11:19:20 -0700613 clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
614
615def rdwr_connected(tag):
616 global only_one, no_wait
617 summary("Tag connected: " + str(tag))
618
619 if tag.ndef:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700620 summary("NDEF tag: " + tag.type)
621 summary(tag.ndef.records)
Hai Shalomfdcde762020-04-02 11:19:20 -0700622 success = dpp_tag_read(tag)
623 if only_one and success:
624 global continue_loop
625 continue_loop = False
626 else:
627 summary("Not an NDEF tag - remove tag")
628 return True
629
630 return not no_wait
631
632def llcp_worker(llc):
633 global init_on_touch
634 if init_on_touch:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700635 summary("Starting handover client")
Hai Shalomfdcde762020-04-02 11:19:20 -0700636 dpp_handover_client(llc)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700637 summary("Exiting llcp_worker thread (init_in_touch)")
Hai Shalomfdcde762020-04-02 11:19:20 -0700638 return
639
640 global no_input
641 if no_input:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700642 summary("Wait for handover to complete")
Hai Shalomfdcde762020-04-02 11:19:20 -0700643 else:
644 print("Wait for handover to complete - press 'i' to initiate")
645 global srv
646 global wait_connection
647 while not wait_connection and srv.sent_carrier is None:
648 if srv.try_own:
649 srv.try_own = False
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700650 summary("Try to initiate another handover with own parameters")
Hai Shalomfdcde762020-04-02 11:19:20 -0700651 dpp_handover_client(llc)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700652 summary("Exiting llcp_worker thread (retry with own parameters)")
Hai Shalomfdcde762020-04-02 11:19:20 -0700653 return
654 if srv.ho_server_processing:
655 time.sleep(0.025)
656 elif no_input:
657 time.sleep(0.5)
658 else:
659 res = getch()
660 if res != 'i':
661 continue
662 clear_raw_mode()
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700663 summary("Starting handover client")
Hai Shalomfdcde762020-04-02 11:19:20 -0700664 dpp_handover_client(llc)
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700665 summary("Exiting llcp_worker thread (manual init)")
Hai Shalomfdcde762020-04-02 11:19:20 -0700666 return
667
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700668 global in_raw_mode
669 was_in_raw_mode = in_raw_mode
Hai Shalomfdcde762020-04-02 11:19:20 -0700670 clear_raw_mode()
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700671 if was_in_raw_mode:
672 print("\r")
673 summary("Exiting llcp_worker thread")
Hai Shalomfdcde762020-04-02 11:19:20 -0700674
675def llcp_startup(llc):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700676 summary("Start LLCP server")
Hai Shalomfdcde762020-04-02 11:19:20 -0700677 global srv
678 srv = HandoverServer(llc)
679 return llc
680
681def llcp_connected(llc):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700682 summary("P2P LLCP connected")
683 global wait_connection, my_crn, peer_crn, my_crn_ready, hs_sent
Hai Shalomfdcde762020-04-02 11:19:20 -0700684 wait_connection = False
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700685 my_crn_ready = False
686 my_crn = None
687 peer_crn = None
688 hs_sent = False
689 global srv
690 srv.start()
Hai Shalomfdcde762020-04-02 11:19:20 -0700691 if init_on_touch or not no_input:
692 threading.Thread(target=llcp_worker, args=(llc,)).start()
693 return True
694
695def llcp_release(llc):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700696 summary("LLCP release")
Hai Shalomfdcde762020-04-02 11:19:20 -0700697 return True
698
699def terminate_loop():
700 global terminate_now
701 return terminate_now
702
703def main():
704 clf = nfc.ContactlessFrontend()
705
706 parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for DPP NFC operations')
707 parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
708 action='store_const', dest='loglevel',
709 help='verbose debug output')
710 parser.add_argument('-q', const=logging.WARNING, action='store_const',
711 dest='loglevel', help='be quiet')
712 parser.add_argument('--only-one', '-1', action='store_true',
713 help='run only one operation and exit')
714 parser.add_argument('--init-on-touch', '-I', action='store_true',
715 help='initiate handover on touch')
716 parser.add_argument('--no-wait', action='store_true',
717 help='do not wait for tag to be removed before exiting')
718 parser.add_argument('--ifname', '-i',
719 help='network interface name')
720 parser.add_argument('--no-input', '-a', action='store_true',
721 help='do not use stdout input to initiate handover')
722 parser.add_argument('--tag-read-only', '-t', action='store_true',
723 help='tag read only (do not allow connection handover)')
724 parser.add_argument('--handover-only', action='store_true',
725 help='connection handover only (do not allow tag read)')
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700726 parser.add_argument('--enrollee', action='store_true',
727 help='run as Enrollee-only')
728 parser.add_argument('--configurator', action='store_true',
729 help='run as Configurator-only')
730 parser.add_argument('--config-params', default='',
731 help='configurator parameters')
732 parser.add_argument('--ctrl', default='/var/run/wpa_supplicant',
733 help='wpa_supplicant/hostapd control interface')
Hai Shalomfdcde762020-04-02 11:19:20 -0700734 parser.add_argument('--summary',
735 help='summary file for writing status updates')
736 parser.add_argument('--success',
737 help='success file for writing success update')
738 parser.add_argument('--device', default='usb', help='NFC device to open')
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700739 parser.add_argument('--chan', default=None, help='channel list')
Hai Shalomfdcde762020-04-02 11:19:20 -0700740 parser.add_argument('command', choices=['write-nfc-uri',
741 'write-nfc-hs'],
742 nargs='?')
743 args = parser.parse_args()
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700744 summary(args)
Hai Shalomfdcde762020-04-02 11:19:20 -0700745
746 global only_one
747 only_one = args.only_one
748
749 global no_wait
750 no_wait = args.no_wait
751
752 global chanlist
753 chanlist = args.chan
754
755 logging.basicConfig(level=args.loglevel)
756
757 global init_on_touch
758 init_on_touch = args.init_on_touch
759
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700760 global enrollee_only
761 enrollee_only = args.enrollee
762
763 global configurator_only
764 configurator_only = args.configurator
765
766 global config_params
767 config_params = args.config_params
768
Hai Shalomfdcde762020-04-02 11:19:20 -0700769 if args.ifname:
770 global ifname
771 ifname = args.ifname
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700772 summary("Selected ifname " + ifname)
773
774 if args.ctrl:
775 global wpas_ctrl
776 wpas_ctrl = args.ctrl
Hai Shalomfdcde762020-04-02 11:19:20 -0700777
778 if args.summary:
779 global summary_file
780 summary_file = args.summary
781
782 if args.success:
783 global success_file
784 success_file = args.success
785
786 if args.no_input:
787 global no_input
788 no_input = True
789
790 clf = nfc.ContactlessFrontend()
791 global wait_connection
792
793 try:
794 if not clf.open(args.device):
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700795 summary("Could not open connection with an NFC device")
Hai Shalomfdcde762020-04-02 11:19:20 -0700796 raise SystemExit
797
798 if args.command == "write-nfc-uri":
799 write_nfc_uri(clf, wait_remove=not args.no_wait)
800 raise SystemExit
801
802 if args.command == "write-nfc-hs":
803 write_nfc_hs(clf, wait_remove=not args.no_wait)
804 raise SystemExit
805
806 global continue_loop
807 while continue_loop:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700808 global in_raw_mode
809 was_in_raw_mode = in_raw_mode
Hai Shalomfdcde762020-04-02 11:19:20 -0700810 clear_raw_mode()
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700811 if was_in_raw_mode:
812 print("\r")
813 summary("Waiting for a tag or peer to be touched")
Hai Shalomfdcde762020-04-02 11:19:20 -0700814 wait_connection = True
815 try:
816 if args.tag_read_only:
817 if not clf.connect(rdwr={'on-connect': rdwr_connected}):
818 break
819 elif args.handover_only:
820 if not clf.connect(llcp={'on-startup': llcp_startup,
821 'on-connect': llcp_connected,
822 'on-release': llcp_release},
823 terminate=terminate_loop):
824 break
825 else:
826 if not clf.connect(rdwr={'on-connect': rdwr_connected},
827 llcp={'on-startup': llcp_startup,
828 'on-connect': llcp_connected,
829 'on-release': llcp_release},
830 terminate=terminate_loop):
831 break
832 except Exception as e:
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700833 summary("clf.connect failed: " + str(e))
Hai Shalomfdcde762020-04-02 11:19:20 -0700834 break
835
836 global srv
837 if only_one and srv and srv.success:
838 raise SystemExit
839
840 except KeyboardInterrupt:
841 raise SystemExit
842 finally:
843 clf.close()
844
845 raise SystemExit
846
847if __name__ == '__main__':
848 main()