mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-05-29 20:28:45 +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_CHANNEL_BUSY = const(0x06) # channel busy
|
||||||
_ERR_LOCK_REQUIRED = const(0x0A) # command requires channel lock
|
_ERR_LOCK_REQUIRED = const(0x0A) # command requires channel lock
|
||||||
_ERR_INVALID_CID = const(0x0B) # command not allowed on this cid
|
_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_INVALID_CBOR = const(0x12) # error when parsing CBOR
|
||||||
_ERR_MISSING_PARAMETER = const(0x14) # missing non-optional parameter
|
_ERR_MISSING_PARAMETER = const(0x14) # missing non-optional parameter
|
||||||
_ERR_CREDENTIAL_EXCLUDED = const(0x19) # valid credential found in the exclude list
|
_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))
|
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]:
|
def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||||
from apps.webauthn.knownapps import knownapps
|
from apps.webauthn.knownapps import knownapps
|
||||||
|
|
||||||
@ -1241,7 +1279,8 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
param = cbor.decode(req.data[1:])
|
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()
|
rp_id_hash = hashlib.sha256(rp_id).digest()
|
||||||
|
|
||||||
# Prepare the new credential.
|
# 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.
|
# Check if any of the credential descriptors in the exclude list belong to this authenticator.
|
||||||
exclude_list = param.get(_MAKECRED_CMD_EXCLUDE_LIST, [])
|
exclude_list = param.get(_MAKECRED_CMD_EXCLUDE_LIST, [])
|
||||||
for credential_descriptor in exclude_list:
|
if credentials_from_descriptor_list(exclude_list, rp_id_hash):
|
||||||
excl_cred = Credential.from_bytes(credential_descriptor["id"], rp_id_hash)
|
# This authenticator is already registered.
|
||||||
if credential_descriptor["type"] == "public-key" and excl_cred is not None:
|
if not dialog_mgr.set_state(
|
||||||
# This authenticator is already registered.
|
Fido2ConfirmExcluded(req.cid, dialog_mgr.iface, cred)
|
||||||
if not dialog_mgr.set_state(
|
):
|
||||||
Fido2ConfirmExcluded(req.cid, dialog_mgr.iface, cred)
|
return cmd_error(req.cid, _ERR_CHANNEL_BUSY)
|
||||||
):
|
return None
|
||||||
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.
|
# 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]
|
pub_key_cred_params = param[_MAKECRED_CMD_PUB_KEY_CRED_PARAMS]
|
||||||
if ("public-key", _COSE_ALG_ES256) not in (
|
if _COSE_ALG_ES256 not in algorithms_from_pub_key_cred_params(
|
||||||
(pkcp.get("type", None), pkcp.get("alg", None))
|
pub_key_cred_params
|
||||||
for pkcp in pub_key_cred_params
|
|
||||||
):
|
):
|
||||||
return cbor_error(req.cid, _ERR_UNSUPPORTED_ALGORITHM)
|
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]
|
client_data_hash = param[_MAKECRED_CMD_CLIENT_DATA_HASH]
|
||||||
|
except TypeError:
|
||||||
|
return cbor_error(req.cid, _ERR_CBOR_UNEXPECTED_TYPE)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return cbor_error(req.cid, _ERR_MISSING_PARAMETER)
|
return cbor_error(req.cid, _ERR_MISSING_PARAMETER)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -1295,11 +1333,13 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
|||||||
# Check data types.
|
# Check data types.
|
||||||
if (
|
if (
|
||||||
not cred.check_data_types()
|
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(client_data_hash, (bytes, bytearray))
|
||||||
or not isinstance(resident_key, bool)
|
or not isinstance(resident_key, bool)
|
||||||
or not isinstance(user_verification, 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.
|
# Check options.
|
||||||
if resident_key and not _ALLOW_RESIDENT_CREDENTIALS:
|
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 = param[_GETASSERT_CMD_RP_ID]
|
||||||
rp_id_hash = hashlib.sha256(rp_id).digest()
|
rp_id_hash = hashlib.sha256(rp_id).digest()
|
||||||
|
|
||||||
cred_list = []
|
|
||||||
allow_list = param.get(_GETASSERT_CMD_ALLOW_LIST, [])
|
allow_list = param.get(_GETASSERT_CMD_ALLOW_LIST, [])
|
||||||
if allow_list:
|
if allow_list:
|
||||||
# Get all credentials from the allow list that belong to this authenticator.
|
# Get all credentials from the allow list that belong to this authenticator.
|
||||||
for credential_descriptor in allow_list:
|
cred_list = credentials_from_descriptor_list(allow_list, rp_id_hash)
|
||||||
if credential_descriptor["type"] != "public-key":
|
for cred in cred_list:
|
||||||
continue
|
if cred.rp_id is None:
|
||||||
cred = Credential.from_bytes(credential_descriptor["id"], rp_id_hash)
|
cred.rp_id = rp_id
|
||||||
if cred is not None:
|
|
||||||
if cred.rp_id is None:
|
|
||||||
cred.rp_id = rp_id
|
|
||||||
cred_list.append(cred)
|
|
||||||
resident = False
|
resident = False
|
||||||
else:
|
else:
|
||||||
# Allow list is empty. Get resident credentials.
|
# Allow list is empty. Get resident credentials.
|
||||||
if _ALLOW_RESIDENT_CREDENTIALS:
|
if _ALLOW_RESIDENT_CREDENTIALS:
|
||||||
cred_list = get_resident_credentials(rp_id_hash)
|
cred_list = get_resident_credentials(rp_id_hash)
|
||||||
|
else:
|
||||||
|
cred_list = []
|
||||||
resident = True
|
resident = True
|
||||||
|
|
||||||
# Sort credentials by time of creation.
|
# 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)
|
hmac_secret = param.get(_GETASSERT_CMD_EXTENSIONS, {}).get("hmac-secret", None)
|
||||||
|
|
||||||
client_data_hash = param[_GETASSERT_CMD_CLIENT_DATA_HASH]
|
client_data_hash = param[_GETASSERT_CMD_CLIENT_DATA_HASH]
|
||||||
|
except TypeError:
|
||||||
|
return cbor_error(req.cid, _ERR_CBOR_UNEXPECTED_TYPE)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return cbor_error(req.cid, _ERR_MISSING_PARAMETER)
|
return cbor_error(req.cid, _ERR_MISSING_PARAMETER)
|
||||||
except Exception:
|
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_presence, bool)
|
||||||
or not isinstance(user_verification, 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.
|
# Check options.
|
||||||
if "rk" in options:
|
if "rk" in options:
|
||||||
|
Loading…
Reference in New Issue
Block a user