mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-15 12:08:59 +00:00
core/webauthn: Add more type checking for CBOR command parameters and return CTAP2_ERR_CBOR_UNEXPECTED_TYPE.
This commit is contained in:
parent
5ca4ed2347
commit
ae70741e48
@ -131,6 +131,7 @@ _ERR_MSG_TIMEOUT = const(0x05) # message has timed out
|
||||
_ERR_CHANNEL_BUSY = const(0x06) # channel busy
|
||||
_ERR_LOCK_REQUIRED = const(0x0A) # command requires channel lock
|
||||
_ERR_INVALID_CID = const(0x0B) # command not allowed on this cid
|
||||
_ERR_CBOR_UNEXPECTED_TYPE = const(0x11) # invalid/unexpected CBOR
|
||||
_ERR_INVALID_CBOR = const(0x12) # error when parsing CBOR
|
||||
_ERR_MISSING_PARAMETER = const(0x14) # missing non-optional parameter
|
||||
_ERR_CREDENTIAL_EXCLUDED = const(0x19) # valid credential found in the exclude list
|
||||
@ -1231,6 +1232,43 @@ def cbor_error(cid: int, code: int) -> Cmd:
|
||||
return Cmd(cid, _CMD_CBOR, ustruct.pack(">B", code))
|
||||
|
||||
|
||||
def credentials_from_descriptor_list(
|
||||
descriptor_list: List[dict], rp_id_hash: bytes
|
||||
) -> List[Credential]:
|
||||
cred_list = []
|
||||
for credential_descriptor in descriptor_list:
|
||||
credential_type = credential_descriptor["type"]
|
||||
if not isinstance(credential_type, str):
|
||||
raise TypeError
|
||||
if credential_type != "public-key":
|
||||
continue
|
||||
|
||||
credential_id = credential_descriptor["id"]
|
||||
if not isinstance(credential_id, (bytes, bytearray)):
|
||||
raise TypeError
|
||||
cred = Credential.from_bytes(credential_id, rp_id_hash)
|
||||
if cred is not None:
|
||||
cred_list.append(cred)
|
||||
|
||||
return cred_list
|
||||
|
||||
|
||||
def algorithms_from_pub_key_cred_params(pub_key_cred_params: List[dict]) -> List[int]:
|
||||
alg_list = []
|
||||
for pkcp in pub_key_cred_params:
|
||||
pub_key_cred_type = pkcp["type"]
|
||||
if not isinstance(pub_key_cred_type, str):
|
||||
raise TypeError
|
||||
if pub_key_cred_type != "public-key":
|
||||
continue
|
||||
|
||||
pub_key_cred_alg = pkcp["alg"]
|
||||
if not isinstance(pub_key_cred_alg, int):
|
||||
raise TypeError
|
||||
alg_list.append(pub_key_cred_alg)
|
||||
return alg_list
|
||||
|
||||
|
||||
def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||
from apps.webauthn.knownapps import knownapps
|
||||
|
||||
@ -1241,7 +1279,8 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||
|
||||
try:
|
||||
param = cbor.decode(req.data[1:])
|
||||
rp_id = param[_MAKECRED_CMD_RP]["id"]
|
||||
rp = param[_MAKECRED_CMD_RP]
|
||||
rp_id = rp["id"]
|
||||
rp_id_hash = hashlib.sha256(rp_id).digest()
|
||||
|
||||
# Prepare the new credential.
|
||||
@ -1256,21 +1295,18 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||
|
||||
# Check if any of the credential descriptors in the exclude list belong to this authenticator.
|
||||
exclude_list = param.get(_MAKECRED_CMD_EXCLUDE_LIST, [])
|
||||
for credential_descriptor in exclude_list:
|
||||
excl_cred = Credential.from_bytes(credential_descriptor["id"], rp_id_hash)
|
||||
if credential_descriptor["type"] == "public-key" and excl_cred is not None:
|
||||
# This authenticator is already registered.
|
||||
if not dialog_mgr.set_state(
|
||||
Fido2ConfirmExcluded(req.cid, dialog_mgr.iface, cred)
|
||||
):
|
||||
return cmd_error(req.cid, _ERR_CHANNEL_BUSY)
|
||||
return None
|
||||
if credentials_from_descriptor_list(exclude_list, rp_id_hash):
|
||||
# This authenticator is already registered.
|
||||
if not dialog_mgr.set_state(
|
||||
Fido2ConfirmExcluded(req.cid, dialog_mgr.iface, cred)
|
||||
):
|
||||
return cmd_error(req.cid, _ERR_CHANNEL_BUSY)
|
||||
return None
|
||||
|
||||
# Check that the relying party supports ECDSA P-256 with SHA-256. We don't support any other algorithms.
|
||||
pub_key_cred_params = param[_MAKECRED_CMD_PUB_KEY_CRED_PARAMS]
|
||||
if ("public-key", _COSE_ALG_ES256) not in (
|
||||
(pkcp.get("type", None), pkcp.get("alg", None))
|
||||
for pkcp in pub_key_cred_params
|
||||
if _COSE_ALG_ES256 not in algorithms_from_pub_key_cred_params(
|
||||
pub_key_cred_params
|
||||
):
|
||||
return cbor_error(req.cid, _ERR_UNSUPPORTED_ALGORITHM)
|
||||
|
||||
@ -1285,6 +1321,8 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||
)
|
||||
|
||||
client_data_hash = param[_MAKECRED_CMD_CLIENT_DATA_HASH]
|
||||
except TypeError:
|
||||
return cbor_error(req.cid, _ERR_CBOR_UNEXPECTED_TYPE)
|
||||
except KeyError:
|
||||
return cbor_error(req.cid, _ERR_MISSING_PARAMETER)
|
||||
except Exception:
|
||||
@ -1295,11 +1333,13 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||
# Check data types.
|
||||
if (
|
||||
not cred.check_data_types()
|
||||
or not isinstance(user.get("icon", ""), str)
|
||||
or not isinstance(rp.get("icon", ""), str)
|
||||
or not isinstance(client_data_hash, (bytes, bytearray))
|
||||
or not isinstance(resident_key, bool)
|
||||
or not isinstance(user_verification, bool)
|
||||
):
|
||||
return cbor_error(req.cid, _ERR_INVALID_CBOR)
|
||||
return cbor_error(req.cid, _ERR_CBOR_UNEXPECTED_TYPE)
|
||||
|
||||
# Check options.
|
||||
if resident_key and not _ALLOW_RESIDENT_CREDENTIALS:
|
||||
@ -1410,23 +1450,20 @@ def cbor_get_assertion(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||
rp_id = param[_GETASSERT_CMD_RP_ID]
|
||||
rp_id_hash = hashlib.sha256(rp_id).digest()
|
||||
|
||||
cred_list = []
|
||||
allow_list = param.get(_GETASSERT_CMD_ALLOW_LIST, [])
|
||||
if allow_list:
|
||||
# Get all credentials from the allow list that belong to this authenticator.
|
||||
for credential_descriptor in allow_list:
|
||||
if credential_descriptor["type"] != "public-key":
|
||||
continue
|
||||
cred = Credential.from_bytes(credential_descriptor["id"], rp_id_hash)
|
||||
if cred is not None:
|
||||
if cred.rp_id is None:
|
||||
cred.rp_id = rp_id
|
||||
cred_list.append(cred)
|
||||
cred_list = credentials_from_descriptor_list(allow_list, rp_id_hash)
|
||||
for cred in cred_list:
|
||||
if cred.rp_id is None:
|
||||
cred.rp_id = rp_id
|
||||
resident = False
|
||||
else:
|
||||
# Allow list is empty. Get resident credentials.
|
||||
if _ALLOW_RESIDENT_CREDENTIALS:
|
||||
cred_list = get_resident_credentials(rp_id_hash)
|
||||
else:
|
||||
cred_list = []
|
||||
resident = True
|
||||
|
||||
# Sort credentials by time of creation.
|
||||
@ -1445,6 +1482,8 @@ def cbor_get_assertion(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||
hmac_secret = param.get(_GETASSERT_CMD_EXTENSIONS, {}).get("hmac-secret", None)
|
||||
|
||||
client_data_hash = param[_GETASSERT_CMD_CLIENT_DATA_HASH]
|
||||
except TypeError:
|
||||
return cbor_error(req.cid, _ERR_CBOR_UNEXPECTED_TYPE)
|
||||
except KeyError:
|
||||
return cbor_error(req.cid, _ERR_MISSING_PARAMETER)
|
||||
except Exception:
|
||||
@ -1457,7 +1496,7 @@ def cbor_get_assertion(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||
or not isinstance(user_presence, bool)
|
||||
or not isinstance(user_verification, bool)
|
||||
):
|
||||
return cbor_error(req.cid, _ERR_INVALID_CBOR)
|
||||
return cbor_error(req.cid, _ERR_CBOR_UNEXPECTED_TYPE)
|
||||
|
||||
# Check options.
|
||||
if "rk" in options:
|
||||
|
Loading…
Reference in New Issue
Block a user