Skip to content

Commit

Permalink
Fix dataclass serialization to CBOR
Browse files Browse the repository at this point in the history
  • Loading branch information
dainnilsson committed Oct 9, 2024
1 parent 4710f25 commit 0c50d92
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 24 deletions.
25 changes: 7 additions & 18 deletions fido2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@
)
from .cose import ES256
from .rpid import verify_rp_id
from .utils import sha256, _DataClassMapping
from .utils import sha256
from enum import IntEnum, unique
from urllib.parse import urlparse
from dataclasses import replace, asdict
from dataclasses import replace
from threading import Timer, Event
from typing import (
Type,
Expand All @@ -69,17 +69,6 @@
logger = logging.getLogger(__name__)


def _as_cbor(data):
if data is None:
return None
if isinstance(data, Sequence):
return [_as_cbor(d) for d in data]
if isinstance(data, _DataClassMapping):
# Remove empty values and do not serialize value
return {k: v for k, v in asdict(data).items() if v is not None} # type: ignore
return data


class ClientError(Exception):
@unique
class ERR(IntEnum):
Expand Down Expand Up @@ -814,10 +803,10 @@ def make_credential(
try:
return self._backend.do_make_credential(
client_data,
_as_cbor(rp),
_as_cbor(options.user),
_as_cbor(options.pub_key_cred_params),
_as_cbor(options.exclude_credentials),
rp,
options.user,
options.pub_key_cred_params,
options.exclude_credentials,
options.extensions,
selection.require_resident_key,
selection.user_verification,
Expand Down Expand Up @@ -859,7 +848,7 @@ def get_assertion(
return self._backend.do_get_assertion(
client_data,
options.rp_id,
_as_cbor(options.allow_credentials),
options.allow_credentials,
options.extensions,
options.user_verification,
event,
Expand Down
20 changes: 17 additions & 3 deletions fido2/ctap2/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,37 @@
from ..webauthn import AuthenticatorData, Aaguid

from enum import IntEnum, unique
from dataclasses import dataclass, field, fields, Field
from dataclasses import dataclass, field, fields, Field, asdict
from threading import Event
from typing import Mapping, Dict, Any, List, Optional, Callable
from typing import Mapping, Dict, Any, List, Optional, Callable, Sequence
import struct
import logging

logger = logging.getLogger(__name__)


def _as_cbor(data):
if data is None:
return None
# Avoid handling bytes | str as Sequence
if isinstance(data, (bytes, str)):
return data
if isinstance(data, Sequence):
return [_as_cbor(d) for d in data]
if isinstance(data, _DataClassMapping):
# Remove empty values and do not serialize value
return {k: v for k, v in asdict(data).items() if v is not None} # type: ignore
return data


def args(*params) -> Dict[int, Any]:
"""Constructs a dict from a list of arguments for sending a CBOR command.
None elements will be omitted.
:param params: Arguments, in order, to add to the command.
:return: The input parameters as a dict.
"""
return dict((i, v) for i, v in enumerate(params, 1) if v is not None)
return dict((i, _as_cbor(v)) for i, v in enumerate(params, 1) if v is not None)


class _CborDataObject(_DataClassMapping[int]):
Expand Down
9 changes: 6 additions & 3 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
PublicKeyCredentialCreationOptions,
AttestationObject,
CollectedClientData,
PublicKeyCredentialUserEntity,
PublicKeyCredentialRpEntity,
PublicKeyCredentialParameters,
)


Expand Down Expand Up @@ -146,9 +149,9 @@ def test_make_credential_ctap2(self, PatchedCtap2):

ctap2.make_credential.assert_called_with(
response.client_data.hash,
rp,
user,
[{"type": "public-key", "alg": -7}],
PublicKeyCredentialRpEntity(**rp),
PublicKeyCredentialUserEntity(**user),
[PublicKeyCredentialParameters(**{"type": "public-key", "alg": -7})],
None,
None,
None,
Expand Down

0 comments on commit 0c50d92

Please sign in to comment.