]>
git.ipfire.org Git - thirdparty/hostap.git/blob - wpa_supplicant/examples/dpp-nfc.py
9aaff9247ebb585bdd67c7b3df3c10b82d1e090e
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
7 # This software may be distributed under the terms of the BSD license.
8 # See README for more details.
21 scriptsdir
= os
.path
.dirname(os
.path
.realpath("dpp-nfc.py"))
22 sys
.path
.append(os
.path
.join(scriptsdir
, '..', '..', 'wpaspy'))
25 wpas_ctrl
= '/var/run/wpa_supplicant'
40 with
open(summary_file
, 'a') as f
:
43 def success_report(txt
):
46 with
open(success_file
, 'a') as f
:
51 if os
.path
.isdir(wpas_ctrl
):
53 ifaces
= [os
.path
.join(wpas_ctrl
, i
) for i
in os
.listdir(wpas_ctrl
)]
54 except OSError as error
:
55 print("Could not find wpa_supplicant: ", error
)
59 print("No wpa_supplicant control interface found")
64 if ifname
not in ctrl
:
67 print("Trying to use control interface " + ctrl
)
68 wpas
= wpaspy
.Ctrl(ctrl
)
70 except Exception as e
:
74 def dpp_nfc_uri_process(uri
):
78 peer_id
= wpas
.request("DPP_NFC_URI " + uri
)
80 print("Could not parse DPP URI from NFC URI record")
82 peer_id
= int(peer_id
)
83 print("peer_id=%d" % peer_id
)
84 cmd
= "DPP_AUTH_INIT peer=%d" % peer_id
85 res
= wpas
.request(cmd
)
87 print("Failed to initiate DPP Authentication")
89 print("DPP Authentication initiated")
92 def dpp_hs_tag_read(record
):
97 if len(record
.data
) < 5:
98 print("Too short DPP HS")
100 if record
.data
[0] != 0:
101 print("Unexpected URI Identifier Code")
103 uribuf
= record
.data
[1:]
105 uri
= uribuf
.decode()
107 print("Invalid URI payload")
110 if not uri
.startswith("DPP:"):
111 print("Not a DPP URI")
113 return dpp_nfc_uri_process(uri
)
115 def get_status(wpas
, extra
=None):
120 res
= wpas
.request("STATUS" + extra
)
121 lines
= res
.splitlines()
125 [name
, value
] = l
.split('=', 1)
127 logger
.info("Ignore unexpected status line: " + l
)
132 def get_status_field(wpas
, field
, extra
=None):
133 vals
= get_status(wpas
, extra
)
139 return get_status_field(wpas
, "address")
141 def dpp_bootstrap_gen(wpas
, type="qrcode", chan
=None, mac
=None, info
=None,
142 curve
=None, key
=None):
143 cmd
= "DPP_BOOTSTRAP_GEN type=" + type
145 cmd
+= " chan=" + chan
149 cmd
+= " mac=" + mac
.replace(':', '')
151 cmd
+= " info=" + info
153 cmd
+= " curve=" + curve
156 res
= wpas
.request(cmd
)
158 raise Exception("Failed to generate bootstrapping info")
161 def wpas_get_nfc_uri(start_listen
=True):
162 wpas
= wpas_connect()
166 own_id
= dpp_bootstrap_gen(wpas
, type="nfc-uri", chan
="81/1", mac
=True)
167 res
= wpas
.request("DPP_BOOTSTRAP_GET_URI %d" % own_id
).rstrip()
171 wpas
.request("DPP_LISTEN 2412 netrole=configurator")
174 def wpas_report_handover_req(uri
):
175 wpas
= wpas_connect()
179 cmd
= "DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (own_id
, uri
)
180 return wpas
.request(cmd
)
182 def wpas_report_handover_sel(uri
):
183 wpas
= wpas_connect()
187 cmd
= "DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (own_id
, uri
)
188 return wpas
.request(cmd
)
190 def dpp_handover_client(llc
):
191 uri
= wpas_get_nfc_uri(start_listen
=False)
192 uri
= ndef
.UriRecord(uri
)
193 print("NFC URI record for DPP: " + str(uri
))
194 carrier
= ndef
.Record('application/vnd.wfa.dpp', 'A', uri
.data
)
195 hr
= ndef
.HandoverRequestRecord(version
="1.4", crn
=os
.urandom(2))
196 hr
.add_alternative_carrier('active', carrier
.name
)
197 message
= [hr
, carrier
]
198 print("NFC Handover Request message for DPP: " + str(message
))
200 client
= nfc
.handover
.HandoverClient(llc
)
202 summary("Trying to initiate NFC connection handover")
204 summary("Connected for handover")
205 except nfc
.llcp
.ConnectRefused
:
206 summary("Handover connection refused")
209 except Exception as e
:
210 summary("Other exception: " + str(e
))
214 summary("Sending handover request")
216 if not client
.send_records(message
):
217 summary("Failed to send handover request")
221 summary("Receiving handover response")
222 message
= client
.recv_records(timeout
=3.0)
224 summary("No response received")
227 print("Received message: " + str(message
))
228 if len(message
) < 1 or \
229 not isinstance(message
[0], ndef
.HandoverSelectRecord
):
230 summary("Response was not Hs - received: " + message
.type)
234 print("Received message")
235 print("alternative carriers: " + str(message
[0].alternative_carriers
))
237 for carrier
in message
:
238 if isinstance(carrier
, ndef
.HandoverSelectRecord
):
240 print("Remote carrier type: " + carrier
.type)
241 if carrier
.type == "application/vnd.wfa.dpp":
242 if len(carrier
.data
) == 0 or carrier
.data
[0] != 0:
243 print("URI Identifier Code 'None' not seen")
245 print("DPP carrier type match - send to wpa_supplicant")
246 uri
= carrier
.data
[1:].decode("utf-8")
247 print("DPP URI: " + uri
)
248 res
= wpas_report_handover_sel(uri
)
249 if res
is None or "FAIL" in res
:
250 summary("DPP handover report rejected")
253 success_report("DPP handover reported successfully (initiator)")
254 print("peer_id=" + res
)
256 # TODO: Single Configurator instance
257 wpas
= wpas_connect()
260 res
= wpas
.request("DPP_CONFIGURATOR_ADD")
262 print("Failed to initiate Configurator")
266 print("Initiate DPP authentication")
267 cmd
= "DPP_AUTH_INIT peer=%d own=%d conf=sta-dpp configurator=%d" % (peer_id
, own_id
, conf_id
)
268 res
= wpas
.request(cmd
)
270 print("Failed to initiate DPP authentication")
275 print("Done with handover")
278 print("only_one -> stop loop")
280 continue_loop
= False
284 print("Trying to exit..")
288 print("Returning from dpp_handover_client")
290 class HandoverServer(nfc
.handover
.HandoverServer
):
291 def __init__(self
, llc
):
292 super(HandoverServer
, self
).__init
__(llc
)
293 self
.sent_carrier
= None
294 self
.ho_server_processing
= False
297 def process_handover_request_message(self
, records
):
298 self
.ho_server_processing
= True
300 print("\nHandoverServer - request received: " + str(records
))
303 hs
= ndef
.HandoverSelectRecord('1.4')
308 for carrier
in records
:
309 if isinstance(carrier
, ndef
.HandoverRequestRecord
):
311 print("Remote carrier type: " + carrier
.type)
312 if carrier
.type == "application/vnd.wfa.dpp":
313 print("DPP carrier type match - add DPP carrier record")
314 if len(carrier
.data
) == 0 or carrier
.data
[0] != 0:
315 print("URI Identifier Code 'None' not seen")
317 uri
= carrier
.data
[1:].decode("utf-8")
318 print("Received DPP URI: " + uri
)
320 data
= wpas_get_nfc_uri(start_listen
=False)
321 print("Own URI (pre-processing): %s" % data
)
323 res
= wpas_report_handover_req(uri
)
324 if res
is None or "FAIL" in res
:
325 print("DPP handover request processing failed")
329 self
.received_carrier
= carrier
331 wpas
= wpas_connect()
335 data
= wpas
.request("DPP_BOOTSTRAP_GET_URI %d" % own_id
).rstrip()
338 print("Own URI (post-processing): %s" % data
)
339 uri
= ndef
.UriRecord(data
)
340 print("Own bootstrapping NFC URI record: " + str(uri
))
342 info
= wpas
.request("DPP_BOOTSTRAP_INFO %d" % own_id
)
344 for line
in info
.splitlines():
345 if line
.startswith("use_freq="):
346 freq
= int(line
.split('=')[1])
348 print("No channel negotiated over NFC - use channel 1")
350 res
= wpas
.request("DPP_LISTEN %d" % freq
)
352 print("Failed to start DPP listen")
355 carrier
= ndef
.Record('application/vnd.wfa.dpp', 'A', uri
.data
)
356 print("Own DPP carrier record: " + str(carrier
))
357 hs
.add_alternative_carrier('active', carrier
.name
)
361 summary("Sending handover select: " + str(sel
))
365 def clear_raw_mode():
366 import sys
, tty
, termios
367 global prev_tcgetattr
, in_raw_mode
370 fd
= sys
.stdin
.fileno()
371 termios
.tcsetattr(fd
, termios
.TCSADRAIN
, prev_tcgetattr
)
375 import sys
, tty
, termios
, select
376 global prev_tcgetattr
, in_raw_mode
377 fd
= sys
.stdin
.fileno()
378 prev_tcgetattr
= termios
.tcgetattr(fd
)
383 [i
, o
, e
] = select
.select([fd
], [], [], 0.05)
385 ch
= sys
.stdin
.read(1)
387 termios
.tcsetattr(fd
, termios
.TCSADRAIN
, prev_tcgetattr
)
391 def dpp_tag_read(tag
):
393 for record
in tag
.ndef
.records
:
395 print("record type " + record
.type)
396 if record
.type == "application/vnd.wfa.dpp":
397 summary("DPP HS tag - send to wpa_supplicant")
398 success
= dpp_hs_tag_read(record
)
400 if isinstance(record
, ndef
.UriRecord
):
401 print("URI record: uri=" + record
.uri
)
402 print("URI record: iri=" + record
.iri
)
403 if record
.iri
.startswith("DPP:"):
405 if not dpp_nfc_uri_process(record
.iri
):
409 print("Ignore unknown URI")
413 success_report("Tag read succeeded")
417 def rdwr_connected_write_tag(tag
):
418 summary("Tag found - writing - " + str(tag
))
419 if not tag
.ndef
.is_writeable
:
420 print("Not a writable tag")
423 if tag
.ndef
.capacity
< len(dpp_tag_data
):
424 print("Not enough room for the message")
426 tag
.ndef
.records
= dpp_tag_data
427 success_report("Tag write succeeded")
428 print("Done - remove tag")
432 continue_loop
= False
433 global dpp_sel_wait_remove
434 return dpp_sel_wait_remove
436 def write_nfc_uri(clf
, wait_remove
=True):
437 print("Write NFC URI record")
438 data
= wpas_get_nfc_uri()
440 summary("Could not get NFC URI from wpa_supplicant")
443 global dpp_sel_wait_remove
444 dpp_sel_wait_remove
= wait_remove
445 print("URI: %s" % data
)
446 uri
= ndef
.UriRecord(data
)
449 print("Touch an NFC tag")
452 clf
.connect(rdwr
={'on-connect': rdwr_connected_write_tag
})
454 def write_nfc_hs(clf
, wait_remove
=True):
455 print("Write NFC Handover Select record on a tag")
456 data
= wpas_get_nfc_uri()
458 summary("Could not get NFC URI from wpa_supplicant")
461 global dpp_sel_wait_remove
462 dpp_sel_wait_remove
= wait_remove
463 print("URI: %s" % data
)
464 uri
= ndef
.UriRecord(data
)
466 carrier
= ndef
.Record('application/vnd.wfa.dpp', 'A', uri
.data
)
467 hs
= ndef
.HandoverSelectRecord('1.4')
468 hs
.add_alternative_carrier('active', carrier
.name
)
472 print("Touch an NFC tag")
474 dpp_tag_data
= [hs
, carrier
]
476 clf
.connect(rdwr
={'on-connect': rdwr_connected_write_tag
})
478 def rdwr_connected(tag
):
479 global only_one
, no_wait
480 summary("Tag connected: " + str(tag
))
483 print("NDEF tag: " + tag
.type)
484 print(tag
.ndef
.records
)
485 success
= dpp_tag_read(tag
)
486 if only_one
and success
:
488 continue_loop
= False
490 summary("Not an NDEF tag - remove tag")
495 def llcp_worker(llc
):
498 print("Starting handover client")
499 dpp_handover_client(llc
)
500 print("Exiting llcp_worker thread (init_in_touch)")
505 print("Wait for handover to complete")
507 print("Wait for handover to complete - press 'i' to initiate")
509 global wait_connection
510 while not wait_connection
and srv
.sent_carrier
is None:
511 if srv
.ho_server_processing
:
520 print("Starting handover client")
521 dpp_handover_client(llc
)
522 print("Exiting llcp_worker thread (manual init)")
526 print("\rExiting llcp_worker thread")
528 def llcp_startup(llc
):
529 print("Start LLCP server")
531 srv
= HandoverServer(llc
)
534 def llcp_connected(llc
):
535 print("P2P LLCP connected")
536 global wait_connection
537 wait_connection
= False
539 if not init_on_touch
:
542 if init_on_touch
or not no_input
:
543 threading
.Thread(target
=llcp_worker
, args
=(llc
,)).start()
546 def llcp_release(llc
):
547 print("LLCP release")
550 def terminate_loop():
555 clf
= nfc
.ContactlessFrontend()
557 parser
= argparse
.ArgumentParser(description
='nfcpy to wpa_supplicant integration for DPP NFC operations')
558 parser
.add_argument('-d', const
=logging
.DEBUG
, default
=logging
.INFO
,
559 action
='store_const', dest
='loglevel',
560 help='verbose debug output')
561 parser
.add_argument('-q', const
=logging
.WARNING
, action
='store_const',
562 dest
='loglevel', help='be quiet')
563 parser
.add_argument('--only-one', '-1', action
='store_true',
564 help='run only one operation and exit')
565 parser
.add_argument('--init-on-touch', '-I', action
='store_true',
566 help='initiate handover on touch')
567 parser
.add_argument('--no-wait', action
='store_true',
568 help='do not wait for tag to be removed before exiting')
569 parser
.add_argument('--ifname', '-i',
570 help='network interface name')
571 parser
.add_argument('--no-input', '-a', action
='store_true',
572 help='do not use stdout input to initiate handover')
573 parser
.add_argument('--tag-read-only', '-t', action
='store_true',
574 help='tag read only (do not allow connection handover)')
575 parser
.add_argument('--handover-only', action
='store_true',
576 help='connection handover only (do not allow tag read)')
577 parser
.add_argument('--summary',
578 help='summary file for writing status updates')
579 parser
.add_argument('--success',
580 help='success file for writing success update')
581 parser
.add_argument('--device', default
='usb', help='NFC device to open')
582 parser
.add_argument('command', choices
=['write-nfc-uri',
585 args
= parser
.parse_args()
589 only_one
= args
.only_one
592 no_wait
= args
.no_wait
594 logging
.basicConfig(level
=args
.loglevel
)
597 init_on_touch
= args
.init_on_touch
602 print("Selected ifname " + ifname
)
606 summary_file
= args
.summary
610 success_file
= args
.success
616 clf
= nfc
.ContactlessFrontend()
617 global wait_connection
620 if not clf
.open(args
.device
):
621 print("Could not open connection with an NFC device")
624 if args
.command
== "write-nfc-uri":
625 write_nfc_uri(clf
, wait_remove
=not args
.no_wait
)
628 if args
.command
== "write-nfc-hs":
629 write_nfc_hs(clf
, wait_remove
=not args
.no_wait
)
635 print("\rWaiting for a tag or peer to be touched")
636 wait_connection
= True
638 if args
.tag_read_only
:
639 if not clf
.connect(rdwr
={'on-connect': rdwr_connected
}):
641 elif args
.handover_only
:
642 if not clf
.connect(llcp
={'on-startup': llcp_startup
,
643 'on-connect': llcp_connected
,
644 'on-release': llcp_release
},
645 terminate
=terminate_loop
):
648 if not clf
.connect(rdwr
={'on-connect': rdwr_connected
},
649 llcp
={'on-startup': llcp_startup
,
650 'on-connect': llcp_connected
,
651 'on-release': llcp_release
},
652 terminate
=terminate_loop
):
654 except Exception as e
:
655 print("clf.connect failed: " + str(e
))
659 if only_one
and srv
and srv
.success
:
662 except KeyboardInterrupt:
669 if __name__
== '__main__':