QubesOS / qubes-issues

The Qubes OS Project issue tracker
https://www.qubes-os.org/doc/issue-tracking/
534 stars 46 forks source link

CTAP Proxy: cannot add multiple security keys to website #8950

Open zpc0 opened 6 months ago

zpc0 commented 6 months ago

Qubes OS release

R4.2

Brief summary

When I try to add another security key to website, website and Firefox returns "This device is already registered" error.

My env is Qubes R4.2 + Fedora 39 + Firefox 122 + Google Titan Security Key Both GitHub and Cloudflare are affected, so I think this isn't server side bug.

Steps to reproduce

  1. Add first security key to website.
  2. Remove first security key from USB port.
  3. Insert second security key to USB port.
  4. Try to add second security key to website.

Expected behavior

Key added successfully.

Actual behavior

"This device is already registered" error.

ben-grande commented 6 months ago

https://github.com/QubesOS/qubes-issues/issues/8847#issuecomment-1883536438 @ben-grande try enabling debug log, maybe it will explain things

Follow the debug instructions, needs to be applied in sys-usb.

zpc0 commented 6 months ago

Hmm, If I use Yubico Security Key instead of Google Titan, the issue is gone...

some logs:

  1. Add first key to github (google titan) -> success
    qctap-get-info: asyncio Using selector: EpollSelector
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-get-info: ctap.request Use CTAP 2 protocol.
    qctap-get-info: ctap.request return CborRequestWrapper
    qctap-get-info: mux.device request: b'\x04'
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: ctap Execute CTAP2 request
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: mux.device response b'[REDACTED]'
    qctap-get-info: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-client-pi: asyncio Using selector: EpollSelector
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: ctap.request Use CTAP 2 protocol.
    qctap-client-pi: ctap.request return CborRequestWrapper
    qctap-client-pi: mux.device request: b'\x04'
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: ctap Execute CTAP2 request
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: mux.device response b'[REDACTED]'
    qctap-client-pi: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-make-cred: asyncio Using selector: EpollSelector
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: ctap.request APDU request: use CTAP 1 protocol.
    qctap-make-cred: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-make-cred: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-make-cred: CommandAPDU from_buffer lc=64 untrusted_request_data=[REDACTED]
    qctap-make-cred: Register __init__(untrusted_cla=0, untrusted_ins=1, untrusted_p1=3, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-make-cred: ctap.request return ApduRequestWrapper
    qctap-make-cred: mux.device request: b'[REDACTED]'
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: mux.device response b'i\x85'
    qctap-make-cred: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-make-cred: asyncio Using selector: EpollSelector
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: ctap.request APDU request: use CTAP 1 protocol.
    qctap-make-cred: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-make-cred: CommandAPDU from_buffer lc=64 untrusted_request_data=[REDACTED]
    qctap-make-cred: Register __init__(untrusted_cla=0, untrusted_ins=1, untrusted_p1=3, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-make-cred: ctap.request return ApduRequestWrapper
    qctap-make-cred: mux.device request: b'[REDACTED]'
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: mux.device response b'[REDACTED]'
    qctap-make-cred: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-make-cred: root attempting to register qrexec rpcname u2f.Authenticate argument [REDACTED] frontend develop-web
  2. Add second key to github (Google Titan) -> Fail
    qctap-get-info: asyncio Using selector: EpollSelector
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-get-info: ctap.request Use CTAP 2 protocol.
    qctap-get-info: ctap.request return CborRequestWrapper
    qctap-get-info: mux.device request: b'\x04'
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: ctap Execute CTAP2 request
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: mux.device response b'[REDACTED]'
    qctap-get-info: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-client-pi: asyncio Using selector: EpollSelector
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: ctap.request Use CTAP 2 protocol.
    qctap-client-pi: ctap.request return CborRequestWrapper
    qctap-client-pi: mux.device request: b'\x04'
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: ctap Execute CTAP2 request
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: mux.device response b'[REDACTED]'
    qctap-client-pi: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-get-asser: asyncio Using selector: EpollSelector
    qctap-get-asser: ctap.request APDU request: use CTAP 1 protocol.
    qctap-get-asser: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-get-asser: CommandAPDU from_buffer lc=225 untrusted_request_data=[REDACTED]
    qctap-get-asser: Authenticate __init__(untrusted_cla=0, untrusted_ins=2, untrusted_p1=7, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-get-asser: ctap.request return ApduRequestWrapper
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: fido2.hid RECV: [REDACTED]
    qctap-get-asser: ctap.request APDU request: use CTAP 1 protocol.
    qctap-get-asser: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-get-asser: CommandAPDU from_buffer lc=225 untrusted_request_data=[REDACTED]
    qctap-get-asser: Authenticate __init__(untrusted_cla=0, untrusted_ins=2, untrusted_p1=7, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-get-asser: ctap.request return ApduRequestWrapper
    qctap-get-asser: mux.device request: b'[REDACTED]'
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: fido2.hid RECV: [REDACTED]
    qctap-get-asser: mux.device response b'i\x85'
    qctap-get-asser: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-make-cred: asyncio Using selector: EpollSelector
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-make-cred: ctap.request APDU request: use CTAP 1 protocol.
    qctap-make-cred: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-make-cred: CommandAPDU from_buffer lc=64 untrusted_request_data=[REDACTED]
    qctap-make-cred: Register __init__(untrusted_cla=0, untrusted_ins=1, untrusted_p1=3, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-make-cred: ctap.request return ApduRequestWrapper
    qctap-make-cred: mux.device request: b"[REDACTED]"
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: mux.device response b'[REDACTED]'
    qctap-make-cred: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-make-cred: root attempting to register qrexec rpcname u2f.Authenticate argument [REDACTED] frontend develop-web
  3. Add second key to github (Yubico Security Key) -> success
    qctap-get-info: asyncio Using selector: EpollSelector
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: ctap.request Use CTAP 2 protocol.
    qctap-get-info: ctap.request return CborRequestWrapper
    qctap-get-info: mux.device request: b'\x04'
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: ctap Execute CTAP2 request
    qctap-get-info: fido2.hid SEND: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: fido2.hid RECV: [REDACTED]
    qctap-get-info: mux.device response b"[REDACTED]"
    qctap-get-info: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-client-pi: asyncio Using selector: EpollSelector
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: ctap.request Use CTAP 2 protocol.
    qctap-client-pi: ctap.request return CborRequestWrapper
    qctap-client-pi: mux.device request: b'\x04'
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: ctap Execute CTAP2 request
    qctap-client-pi: fido2.hid SEND: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: fido2.hid RECV: [REDACTED]
    qctap-client-pi: mux.device response b"[REDACTED]"
    qctap-client-pi: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-get-asser: asyncio Using selector: EpollSelector
    qctap-get-asser: ctap.request APDU request: use CTAP 1 protocol.
    qctap-get-asser: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-get-asser: CommandAPDU from_buffer lc=225 untrusted_request_data=[REDACTED]
    qctap-get-asser: Authenticate __init__(untrusted_cla=0, untrusted_ins=2, untrusted_p1=7, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-get-asser: ctap.request return ApduRequestWrapper
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: fido2.hid RECV: [REDACTED]
    qctap-get-asser: ctap.request APDU request: use CTAP 1 protocol.
    qctap-get-asser: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-get-asser: CommandAPDU from_buffer lc=225 untrusted_request_data=[REDACTED]
    qctap-get-asser: Authenticate __init__(untrusted_cla=0, untrusted_ins=2, untrusted_p1=7, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-get-asser: ctap.request return ApduRequestWrapper
    qctap-get-asser: mux.device request: b'[REDACTED]'
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: fido2.hid SEND: [REDACTED]
    qctap-get-asser: fido2.hid RECV: [REDACTED]
    qctap-get-asser: mux.device response b'j\x80'
    qctap-get-asser: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-make-cred: asyncio Using selector: EpollSelector
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: ctap.request APDU request: use CTAP 1 protocol.
    qctap-make-cred: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-make-cred: CommandAPDU from_buffer lc=64 untrusted_request_data=[REDACTED]
    qctap-make-cred: Register __init__(untrusted_cla=0, untrusted_ins=1, untrusted_p1=3, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-make-cred: ctap.request return ApduRequestWrapper
    qctap-make-cred: mux.device request: b'[REDACTED]'
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: mux.device response b'i\x85'
    qctap-make-cred: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-make-cred: asyncio Using selector: EpollSelector
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: ctap.request APDU request: use CTAP 1 protocol.
    qctap-make-cred: mux pending={<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.11/asyncio/futures.py:387]>}
    qctap-make-cred: CommandAPDU from_buffer(untrusted_data=[REDACTED])
    qctap-make-cred: CommandAPDU from_buffer lc=64 untrusted_request_data=[REDACTED]
    qctap-make-cred: Register __init__(untrusted_cla=0, untrusted_ins=1, untrusted_p1=3, untrusted_p2=0, untrusted_request_data=[REDACTED], untrusted_le=65536)
    qctap-make-cred: ctap.request return ApduRequestWrapper
    qctap-make-cred: mux.device request: b'[REDACTED]'
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid SEND: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: fido2.hid RECV: [REDACTED]
    qctap-make-cred: mux.device response b'[REDACTED]'
    qctap-make-cred: root pending=set() done={<Future finished result=<qubesctap.pr...[REDACTED]>>}
    qctap-make-cred: root attempting to register qrexec rpcname u2f.Authenticate argument [REDACTED] frontend develop-web
zpc0 commented 6 months ago

Hmm, editing some code seems fixes (or workaround?) this problem, but I don't understand what I'm doing. :rofl:

for mux.py L111

if result is None \
    or isinstance(result.data, ApduError) \
    and result.data.code == APDU.WRONG_DATA