qubes-issues icon indicating copy to clipboard operation
qubes-issues copied to clipboard

CTAP Proxy: cannot add multiple security keys to website

Open zpc0 opened this issue 1 year ago • 3 comments

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.

zpc0 avatar Feb 18 '24 09:02 zpc0

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.

ben-grande avatar Feb 19 '24 11:02 ben-grande

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
  1. 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
  1. 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 avatar Feb 22 '24 08:02 zpc0

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

zpc0 avatar Feb 23 '24 07:02 zpc0