Skip to content

Commit c2c34ad

Browse files
committed
better presence ipc
1 parent 92ad013 commit c2c34ad

File tree

4 files changed

+57
-9
lines changed

4 files changed

+57
-9
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ If your distribution uses `plugdev`, add `, GROUP="plugdev"` to both lines.
4848
See the help text for command line flags:
4949

5050
```
51-
usage: bridge.py [-h] [-f [{chaining,extended}]] [-e] [-nr] [-it [IDLETIMEOUT]] [-st [SCANTIMEOUT]] [-v]
51+
usage: bridge.py [-h] [-f [{chaining,extended}]] [-e] [-nr] [-np] [-it [IDLETIMEOUT]] [-st [SCANTIMEOUT]] [-v]
5252
5353
FIDO2 PC/SC CTAPHID Bridge
5454
@@ -59,6 +59,8 @@ options:
5959
-e, --exit-on-error Exit on APDU error responses (for fuzzing)
6060
-nr, --no-simulate-replug
6161
Do not simulate USB re-plugging (for fuzzing)
62+
-np, --no-simulate-presence
63+
Do not simulate user presence, instead wait for control pipe (for fuzzing)
6264
-it [IDLETIMEOUT], --idle-timeout [IDLETIMEOUT]
6365
Idle timeout after which to disconnect from the card in seconds
6466
-st [SCANTIMEOUT], --scan-timeout [SCANTIMEOUT]

bridge.py

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22

3-
import sys, signal, os, logging, threading, argparse, time, datetime
4-
import cbor2, json, usb.core, usb.util
3+
import sys, signal, os, logging, threading, argparse, time, datetime, select
4+
import cbor2, json, usb.core, usb.util, setproctitle
55
from pathlib import Path
66

77
from hid.ctap import CTAPHID
@@ -25,11 +25,13 @@
2525

2626
bridge = None
2727
args = None
28+
presence = threading.Lock()
2829

2930
APDU_SELECT = [0x00, 0xA4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01]
3031
APDU_SELECT_RESP = [0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F, 0x30]
3132

3233
scripts = Path(__file__).parent.resolve() / "scripts"
34+
cpipe = Path(__file__).parent.resolve() / "cpipe"
3335

3436
class BytesEncoder(json.JSONEncoder):
3537
def default(self, obj):
@@ -193,8 +195,20 @@ def process_cbor(self, cbor_data:bytes, keep_alive: CTAPHIDKeepAlive, cid:bytes=
193195
log.debug("No CBOR command payload")
194196

195197
if(self.requires_up(cbor_data)):
196-
log.info("CTAP command requires user presence, sending prompt response")
197-
keep_alive.update_status(CTAPHID_KEEPALIVE_STATUS.STATUS_UPNEEDED)
198+
present = False
199+
if(not args.simpresence) :
200+
log.info("Waiting for control pipe to signal user presence")
201+
try:
202+
if(not presence.acquire(timeout=5)):
203+
raise Exception("Timeout")
204+
preset = True
205+
except Exception as e:
206+
raise BridgeException(CTAP_STATUS_CODE.CTAP2_ERR_USER_ACTION_TIMEOUT, "Cannot acquire presence lock: " + str(e))
207+
else:
208+
present = True
209+
if(present):
210+
log.info("CTAP command requires user presence, sending 'waiting for acknowledgment' status")
211+
keep_alive.update_status(CTAPHID_KEEPALIVE_STATUS.STATUS_UPNEEDED)
198212

199213
res = bytes([])
200214
err = None
@@ -339,12 +353,29 @@ def get_version(self)->AuthenticatorVersion:
339353
return AuthenticatorVersion(1, 0, 0, 0)
340354

341355

342-
def signal_handler(sig, frame):
356+
def shutdown_handler(sig, frame):
343357
log.info("Shutting down")
344358
bridge.shutdown()
345359
sys.exit(0)
346360

361+
def presence_thread():
362+
with open(cpipe) as fifo:
363+
while True:
364+
select.select([fifo],[],[fifo])
365+
data = fifo.read()
366+
if(data.strip() == "p"):
367+
log.info("Simulating user presence for pending response")
368+
try:
369+
presence.release()
370+
except Exception as e:
371+
log.error("Cannot release presence lock: %s", e)
372+
elif(data.strip() == "r"):
373+
log.info("Signaling external card reset")
374+
bridge.disconnect_card()
375+
347376
if __name__ == "__main__":
377+
setproctitle.setproctitle('ctap-bridge')
378+
348379
parser = argparse.ArgumentParser(description = 'FIDO2 PC/SC CTAPHID Bridge')
349380
parser.add_argument('-f', '--fragmentation', nargs='?', dest='frag', type=str,
350381
const='chaining', default='chaining', choices=['chaining', 'extended'],
@@ -353,6 +384,8 @@ def signal_handler(sig, frame):
353384
help='Exit on APDU error responses (for fuzzing)')
354385
parser.add_argument('-nr', '--no-simulate-replug', action='store_false', dest='simreplug',
355386
help='Do not simulate USB re-plugging (for fuzzing)')
387+
parser.add_argument('-np', '--no-simulate-presence', action='store_false', dest='simpresence',
388+
help='Do not simulate user presence, instead wait for control pipe (for fuzzing)')
356389
parser.add_argument('-it', '--idle-timeout', nargs='?', dest='idletimeout', type=int,
357390
const=20, default=20, help='Idle timeout after which to disconnect from the card in seconds')
358391
parser.add_argument('-st', '--scan-timeout', nargs='?', dest='scantimeout', type=int,
@@ -361,12 +394,23 @@ def signal_handler(sig, frame):
361394
help='Log verbose APDU data')
362395
args = parser.parse_args()
363396

397+
if(not args.simpresence):
398+
try:
399+
os.mkfifo(cpipe)
400+
except Exception as e:
401+
log.error("Cannot create user presence control pipe: %s", e)
402+
pr = threading.Thread(target=presence_thread)
403+
pr.daemon = True
404+
pr.start()
405+
364406
log.info("FIDO2 PC/SC CTAPHID Bridge running")
365407
log.info("Press Ctrl+C to stop")
366408

367409
bridge = Bridge()
368410
bridge.start()
369411

370-
signal.signal(signal.SIGINT, signal_handler)
371-
signal.signal(signal.SIGTERM, signal_handler)
412+
presence.acquire()
413+
414+
signal.signal(signal.SIGINT, shutdown_handler)
415+
signal.signal(signal.SIGTERM, shutdown_handler)
372416
signal.pause()

flake.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
pyscard
2020
pyusb
2121
cbor2
22+
setproctitle
2223
]))
2324
];
2425
};

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pyscard==2.0.5
22
pyusb==1.2.1
3-
cbor2==5.4.6
3+
cbor2==5.4.6
4+
setproctitle==1.3.2

0 commit comments

Comments
 (0)