From eaf63fff45b0d5b7ebeeef72973794c562504c3e Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Mon, 23 Sep 2019 15:48:50 +0200 Subject: [PATCH 01/13] core/webauthn: In GetAssertion do not return user ID for server resident credentials. --- core/src/apps/webauthn/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index 5f46a655ad..e7482bf0a2 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -735,6 +735,7 @@ class Fido2ConfirmGetAssertion(Fido2State, ConfirmInfo, Pageable): client_data_hash: bytes, creds: List[Credential], hmac_secret: Optional[dict], + resident: bool, user_verification: bool, ) -> None: Fido2State.__init__(self, cid, iface) @@ -743,6 +744,7 @@ class Fido2ConfirmGetAssertion(Fido2State, ConfirmInfo, Pageable): self._client_data_hash = client_data_hash self._creds = creds self._hmac_secret = hmac_secret + self._resident = resident self._user_verification = user_verification self.load_icon(self._creds[0].rp_id_hash) @@ -777,6 +779,7 @@ class Fido2ConfirmGetAssertion(Fido2State, ConfirmInfo, Pageable): cred.rp_id_hash, cred, self._hmac_secret, + self._resident, True, self._user_verification, ) @@ -804,7 +807,9 @@ class Fido2ConfirmNoCredentials(Fido2ConfirmGetAssertion): def __init__(self, cid: int, iface: io.HID, rp_id: str) -> None: cred = Fido2Credential() cred.rp_id = rp_id - super().__init__(cid, iface, b"", [cred], {}, user_verification=False) + super().__init__( + cid, iface, b"", [cred], {}, resident=False, user_verification=False + ) async def on_confirm(self) -> None: cmd = cbor_error(self.cid, _ERR_NO_CREDENTIALS) @@ -1400,10 +1405,12 @@ def cbor_get_assertion(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: if cred.rp_id is None: cred.rp_id = rp_id cred_list.append(cred) + resident = False else: # Allow list is empty. Get resident credentials. if _ALLOW_RESIDENT_CREDENTIALS: cred_list = get_resident_credentials(rp_id_hash) + resident = True # Sort credentials by time of creation. cred_list.sort() @@ -1461,6 +1468,7 @@ def cbor_get_assertion(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: rp_id_hash, cred_list[0], hmac_secret, + resident, user_presence, user_verification, ) @@ -1476,6 +1484,7 @@ def cbor_get_assertion(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: client_data_hash, cred_list, hmac_secret, + resident, user_verification, ) ) @@ -1536,6 +1545,7 @@ def cbor_get_assertion_sign( rp_id_hash: bytes, cred: Credential, hmac_secret: Optional[dict], + resident: bool, user_presence: bool, user_verification: bool, ) -> bytes: @@ -1585,7 +1595,7 @@ def cbor_get_assertion_sign( _GETASSERT_RESP_SIGNATURE: sig, } - if user_presence and cred.user_id is not None: + if resident and user_presence and cred.user_id is not None: response[_GETASSERT_RESP_USER] = {"id": cred.user_id} return cbor.encode(response) From 3d91cb5c5ba1ca58c515bac7750cc4864d68a335 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Mon, 23 Sep 2019 19:14:02 +0200 Subject: [PATCH 02/13] core/webauthn: Return ERR_MISSING_PARAMETER instead of generic error. --- core/src/apps/webauthn/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index e7482bf0a2..a3afbc1685 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -132,6 +132,7 @@ _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_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 _ERR_UNSUPPORTED_ALGORITHM = const(0x26) # requested COSE algorithm not supported _ERR_OPERATION_DENIED = const(0x27) # user declined or timed out @@ -1270,6 +1271,8 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: ) client_data_hash = param[_MAKECRED_CMD_CLIENT_DATA_HASH] + except KeyError: + return cbor_error(req.cid, _ERR_MISSING_PARAMETER) except Exception: return cbor_error(req.cid, _ERR_INVALID_CBOR) @@ -1428,6 +1431,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 KeyError: + return cbor_error(req.cid, _ERR_MISSING_PARAMETER) except Exception: return cbor_error(req.cid, _ERR_INVALID_CBOR) From 7ad1467dbf50db0ad5a982e29473ea0c895dc84a Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Mon, 23 Sep 2019 19:17:03 +0200 Subject: [PATCH 03/13] core/webauthn: Process debuglink signals in Fido2ConfirmGetAssertion. --- core/src/apps/webauthn/__init__.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index a3afbc1685..a6002f006b 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -541,6 +541,14 @@ async def confirm(*args: Any, **kwargs: Any) -> bool: return await dialog is CONFIRMED +async def confirm_pageable(*args: Any, **kwargs: Any) -> bool: + dialog = ConfirmPageable(*args, **kwargs) + if __debug__: + return await loop.race(dialog, confirm_signal()) is CONFIRMED + else: + return await dialog is CONFIRMED + + class State: def __init__(self, cid: int, iface: io.HID) -> None: self.cid = cid @@ -763,7 +771,7 @@ class Fido2ConfirmGetAssertion(Fido2State, ConfirmInfo, Pageable): async def confirm_dialog(self) -> bool: content = ConfirmContent(self) - if await ConfirmPageable(self, content) is not CONFIRMED: + if not await confirm_pageable(self, content): return False if self._user_verification: return await verify_user(KeepaliveCallback(self.cid, self.iface)) From 5ca4ed23471ff9b7ef0dd1732bd1a3b3cf4a2ae7 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Mon, 23 Sep 2019 19:18:17 +0200 Subject: [PATCH 04/13] core/webauth: Add _AUTOCONFIRM option for testing. --- core/src/apps/webauthn/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index a6002f006b..baad808796 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -208,6 +208,8 @@ _ALLOW_RESIDENT_CREDENTIALS = True # The attestation type to use in MakeCredential responses. If false, then self attestation will be used. _USE_BASIC_ATTESTATION = False +_AUTOCONFIRM = False + def frame_init() -> dict: # uint32_t cid; // Channel identifier @@ -534,6 +536,8 @@ async def verify_user(keepalive_callback: KeepaliveCallback) -> bool: async def confirm(*args: Any, **kwargs: Any) -> bool: + if _AUTOCONFIRM: + return True dialog = Confirm(*args, **kwargs) if __debug__: return await loop.race(dialog, confirm_signal()) is CONFIRMED @@ -542,6 +546,8 @@ async def confirm(*args: Any, **kwargs: Any) -> bool: async def confirm_pageable(*args: Any, **kwargs: Any) -> bool: + if _AUTOCONFIRM: + return True dialog = ConfirmPageable(*args, **kwargs) if __debug__: return await loop.race(dialog, confirm_signal()) is CONFIRMED From ae70741e4819e25221916d8cdcde44ff352d7ce6 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Wed, 25 Sep 2019 11:28:23 +0200 Subject: [PATCH 05/13] core/webauthn: Add more type checking for CBOR command parameters and return CTAP2_ERR_CBOR_UNEXPECTED_TYPE. --- core/src/apps/webauthn/__init__.py | 87 +++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index baad808796..370eb61b69 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -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: From e341f133a34883a812bd853c89d00eb4a4367429 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 26 Sep 2019 12:24:30 +0200 Subject: [PATCH 06/13] core/webauthn: Add length checks in CTAPHID protocol. --- core/src/apps/webauthn/__init__.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index 370eb61b69..263dd23272 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -25,7 +25,6 @@ if __debug__: if False: from typing import Any, Coroutine, List, Optional -_HID_RPT_SIZE = const(64) _CID_BROADCAST = const(0xFFFFFFFF) # broadcast channel id # types of frame @@ -33,6 +32,12 @@ _TYPE_MASK = const(0x80) # frame type mask _TYPE_INIT = const(0x80) # initial frame identifier _TYPE_CONT = const(0x00) # continuation frame identifier +# U2F HID sizes +_HID_RPT_SIZE = const(64) +_FRAME_INIT_SIZE = const(57) +_FRAME_CONT_SIZE = const(59) +_MAX_U2FHID_MSG_PAYLOAD_LEN = const(_FRAME_INIT_SIZE + 128 * _FRAME_CONT_SIZE) + # types of cmd _CMD_PING = const(0x81) # echo data through local processor only _CMD_MSG = const(0x83) # send U2F message frame @@ -195,9 +200,6 @@ _RESULT_DECLINE = const(2) # User declined. _RESULT_CANCEL = const(3) # Request was cancelled by _CMD_CANCEL. _RESULT_TIMEOUT = const(4) # Request exceeded _FIDO2_CONFIRM_TIMEOUT_MS. -_FRAME_INIT_SIZE = 57 -_FRAME_CONT_SIZE = 59 - # Generate the authenticatorKeyAgreementKey used for ECDH in authenticatorClientPIN getKeyAgreement. _KEY_AGREEMENT_PRIVKEY = nist256p1.generate_secret() _KEY_AGREEMENT_PUBKEY = nist256p1.publickey(_KEY_AGREEMENT_PRIVKEY, False) @@ -374,6 +376,13 @@ async def read_cmd(iface: io.HID) -> Optional[Cmd]: log.warning(__name__, "_TYPE_CONT") return None + if bcnt > _MAX_U2FHID_MSG_PAYLOAD_LEN: + # invalid payload length, abort current msg + if __debug__: + log.warning(__name__, "_MAX_U2FHID_MSG_PAYLOAD_LEN") + await send_cmd(cmd_error(ifrm.cid, _ERR_INVALID_LEN), iface) + return None + if datalen < bcnt: databuf = bytearray(bcnt) utils.memcpy(databuf, 0, data, 0, bcnt) @@ -952,7 +961,10 @@ class DialogManager: def dispatch_cmd(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: if req.cmd == _CMD_MSG: - m = req.to_msg() + try: + m = req.to_msg() + except IndexError: + return cmd_error(req.cid, _ERR_INVALID_LEN) if m.cla != 0: if __debug__: @@ -995,6 +1007,8 @@ def dispatch_cmd(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: loop.schedule(ui.alert()) return req elif req.cmd == _CMD_CBOR and _ALLOW_FIDO2: + if not req.data: + return cmd_error(req.cid, _ERR_INVALID_LEN) if req.data[0] == _CBOR_MAKE_CREDENTIAL: if __debug__: log.debug(__name__, "_CBOR_MAKE_CREDENTIAL") From 528ee9ccf1023c8d24be9cf065188f9c4a098669 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 26 Sep 2019 12:25:52 +0200 Subject: [PATCH 07/13] core/webauthn: Ensure user-presence option is not present in MakeCredential requests. --- core/src/apps/webauthn/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index 263dd23272..9f9234545c 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -1356,6 +1356,9 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: return cbor_error(req.cid, _ERR_CBOR_UNEXPECTED_TYPE) # Check options. + if "up" in options: + return cbor_error(req.cid, _ERR_INVALID_OPTION) + if resident_key and not _ALLOW_RESIDENT_CREDENTIALS: return cbor_error(req.cid, _ERR_UNSUPPORTED_OPTION) From 839c6cdac5fa0758161783f4bd48cc709b90e5db Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 26 Sep 2019 16:18:47 +0200 Subject: [PATCH 08/13] common/webauthn: Allow multiple URLs per app. --- common/defs/webauthn/apps/binance.json | 2 +- common/defs/webauthn/apps/bitbucket.json | 2 +- common/defs/webauthn/apps/bitfinex.json | 2 +- common/defs/webauthn/apps/bitwarden.json | 2 +- common/defs/webauthn/apps/dashlane.json | 2 +- common/defs/webauthn/apps/dropbox.json | 4 ++-- common/defs/webauthn/apps/duo.json | 2 +- common/defs/webauthn/apps/fastmail.json | 2 +- common/defs/webauthn/apps/fedora.json | 2 +- common/defs/webauthn/apps/gandi.json | 2 +- common/defs/webauthn/apps/github.json | 4 ++-- common/defs/webauthn/apps/gitlab.json | 2 +- common/defs/webauthn/apps/google.json | 4 ++-- common/defs/webauthn/apps/keeper.json | 2 +- common/defs/webauthn/apps/lastpass.json | 2 +- common/defs/webauthn/apps/login.gov.json | 2 +- common/defs/webauthn/apps/microsoft.json | 2 +- common/defs/webauthn/apps/slushpool.json | 2 +- common/defs/webauthn/apps/stripe.json | 2 +- common/defs/webauthn/apps/u2f.bin.coffee.json | 2 +- .../webauthn/apps/webauthn.bin.coffee.json | 2 +- common/defs/webauthn/apps/webauthn.io.json | 2 +- common/defs/webauthn/apps/webauthn.me.json | 2 +- common/defs/webauthn/apps/yubico-demo.json | 2 +- common/defs/webauthn/gen.py | 20 ++++++++----------- 25 files changed, 35 insertions(+), 39 deletions(-) diff --git a/common/defs/webauthn/apps/binance.json b/common/defs/webauthn/apps/binance.json index 39477a49e3..b57b7bca5f 100644 --- a/common/defs/webauthn/apps/binance.json +++ b/common/defs/webauthn/apps/binance.json @@ -1,5 +1,5 @@ { "label": "Binance", - "webauthn": "www.binance.com", + "webauthn": ["www.binance.com"], "use_sign_count": false } diff --git a/common/defs/webauthn/apps/bitbucket.json b/common/defs/webauthn/apps/bitbucket.json index e5e7a0bff4..309dd7888e 100644 --- a/common/defs/webauthn/apps/bitbucket.json +++ b/common/defs/webauthn/apps/bitbucket.json @@ -1,4 +1,4 @@ { "label": "Bitbucket", - "u2f": "https://bitbucket.org" + "u2f": ["https://bitbucket.org"] } diff --git a/common/defs/webauthn/apps/bitfinex.json b/common/defs/webauthn/apps/bitfinex.json index c144860bcd..21bc5935eb 100644 --- a/common/defs/webauthn/apps/bitfinex.json +++ b/common/defs/webauthn/apps/bitfinex.json @@ -1,4 +1,4 @@ { "label": "Bitfinex", - "u2f": "https://www.bitfinex.com" + "u2f": ["https://www.bitfinex.com"] } diff --git a/common/defs/webauthn/apps/bitwarden.json b/common/defs/webauthn/apps/bitwarden.json index 12f185f891..901c18bc45 100644 --- a/common/defs/webauthn/apps/bitwarden.json +++ b/common/defs/webauthn/apps/bitwarden.json @@ -1,4 +1,4 @@ { "label": "Bitwarden", - "u2f": "https://vault.bitwarden.com/app-id.json" + "u2f": ["https://vault.bitwarden.com/app-id.json"] } diff --git a/common/defs/webauthn/apps/dashlane.json b/common/defs/webauthn/apps/dashlane.json index 6969adfe37..dd8293d84e 100644 --- a/common/defs/webauthn/apps/dashlane.json +++ b/common/defs/webauthn/apps/dashlane.json @@ -1,4 +1,4 @@ { "label": "Dashlane", - "u2f": "https://www.dashlane.com" + "u2f": ["https://www.dashlane.com"] } diff --git a/common/defs/webauthn/apps/dropbox.json b/common/defs/webauthn/apps/dropbox.json index e97f52884b..a5be49143a 100644 --- a/common/defs/webauthn/apps/dropbox.json +++ b/common/defs/webauthn/apps/dropbox.json @@ -1,5 +1,5 @@ { "label": "Dropbox", - "u2f": "https://www.dropbox.com/u2f-app-id.json", - "webauthn": "www.dropbox.com" + "u2f": ["https://www.dropbox.com/u2f-app-id.json"], + "webauthn": ["www.dropbox.com"] } diff --git a/common/defs/webauthn/apps/duo.json b/common/defs/webauthn/apps/duo.json index 51d83661c5..a04432e819 100644 --- a/common/defs/webauthn/apps/duo.json +++ b/common/defs/webauthn/apps/duo.json @@ -1,4 +1,4 @@ { "label": "Duo", - "u2f": "https://api-9dcf9b83.duosecurity.com" + "u2f": ["https://api-9dcf9b83.duosecurity.com"] } diff --git a/common/defs/webauthn/apps/fastmail.json b/common/defs/webauthn/apps/fastmail.json index 2e3d2e8f32..f31f90f388 100644 --- a/common/defs/webauthn/apps/fastmail.json +++ b/common/defs/webauthn/apps/fastmail.json @@ -1,4 +1,4 @@ { "label": "FastMail", - "u2f": "https://www.fastmail.com" + "u2f": ["https://www.fastmail.com"] } diff --git a/common/defs/webauthn/apps/fedora.json b/common/defs/webauthn/apps/fedora.json index b1c9213938..3a1da08aa2 100644 --- a/common/defs/webauthn/apps/fedora.json +++ b/common/defs/webauthn/apps/fedora.json @@ -1,4 +1,4 @@ { "label": "Fedora", - "u2f": "https://id.fedoraproject.org/u2f-origins.json" + "u2f": ["https://id.fedoraproject.org/u2f-origins.json"] } diff --git a/common/defs/webauthn/apps/gandi.json b/common/defs/webauthn/apps/gandi.json index 9f27a26680..bc329c242f 100644 --- a/common/defs/webauthn/apps/gandi.json +++ b/common/defs/webauthn/apps/gandi.json @@ -1,4 +1,4 @@ { "label": "Gandi", - "u2f": "https://account.gandi.net/api/u2f/trusted_facets.json" + "u2f": ["https://account.gandi.net/api/u2f/trusted_facets.json"] } diff --git a/common/defs/webauthn/apps/github.json b/common/defs/webauthn/apps/github.json index 0f63bf22fd..123241068e 100644 --- a/common/defs/webauthn/apps/github.json +++ b/common/defs/webauthn/apps/github.json @@ -1,6 +1,6 @@ { "label": "GitHub", - "u2f": "https://github.com/u2f/trusted_facets", - "webauthn": "github.com", + "u2f": ["https://github.com/u2f/trusted_facets"], + "webauthn": ["github.com"], "use_sign_count": true } diff --git a/common/defs/webauthn/apps/gitlab.json b/common/defs/webauthn/apps/gitlab.json index 261f3fa334..7f2ba750e8 100644 --- a/common/defs/webauthn/apps/gitlab.json +++ b/common/defs/webauthn/apps/gitlab.json @@ -1,4 +1,4 @@ { "label": "GitLab", - "u2f": "https://gitlab.com" + "u2f": ["https://gitlab.com"] } diff --git a/common/defs/webauthn/apps/google.json b/common/defs/webauthn/apps/google.json index e99b2e5f9f..6449bc472a 100644 --- a/common/defs/webauthn/apps/google.json +++ b/common/defs/webauthn/apps/google.json @@ -1,5 +1,5 @@ { "label": "Google", - "u2f": "https://www.gstatic.com/securitykey/origins.json", - "webauthn": "google.com" + "u2f": ["https://www.gstatic.com/securitykey/origins.json"], + "webauthn": ["google.com"] } diff --git a/common/defs/webauthn/apps/keeper.json b/common/defs/webauthn/apps/keeper.json index 97677a061b..bec466e12f 100644 --- a/common/defs/webauthn/apps/keeper.json +++ b/common/defs/webauthn/apps/keeper.json @@ -1,4 +1,4 @@ { "label": "Keeper", - "u2f": "https://keepersecurity.com" + "u2f": ["https://keepersecurity.com"] } diff --git a/common/defs/webauthn/apps/lastpass.json b/common/defs/webauthn/apps/lastpass.json index 721de8a75d..7e312c358f 100644 --- a/common/defs/webauthn/apps/lastpass.json +++ b/common/defs/webauthn/apps/lastpass.json @@ -1,4 +1,4 @@ { "label": "LastPass", - "u2f": "https://lastpass.com" + "u2f": ["https://lastpass.com"] } diff --git a/common/defs/webauthn/apps/login.gov.json b/common/defs/webauthn/apps/login.gov.json index c892da4207..8407d00a85 100644 --- a/common/defs/webauthn/apps/login.gov.json +++ b/common/defs/webauthn/apps/login.gov.json @@ -1,4 +1,4 @@ { "label": "login.gov", - "webauthn": "secure.login.gov" + "webauthn": ["secure.login.gov"] } diff --git a/common/defs/webauthn/apps/microsoft.json b/common/defs/webauthn/apps/microsoft.json index 719023fafc..bf29641046 100644 --- a/common/defs/webauthn/apps/microsoft.json +++ b/common/defs/webauthn/apps/microsoft.json @@ -1,5 +1,5 @@ { "label": "Microsoft", - "webauthn": "login.microsoft.com", + "webauthn": ["login.microsoft.com"], "use_sign_count": false } diff --git a/common/defs/webauthn/apps/slushpool.json b/common/defs/webauthn/apps/slushpool.json index c821da7690..54fcf24565 100644 --- a/common/defs/webauthn/apps/slushpool.json +++ b/common/defs/webauthn/apps/slushpool.json @@ -1,4 +1,4 @@ { "label": "Slush Pool", - "u2f": "https://slushpool.com/static/security/u2f.json" + "u2f": ["https://slushpool.com/static/security/u2f.json"] } diff --git a/common/defs/webauthn/apps/stripe.json b/common/defs/webauthn/apps/stripe.json index 403696c7f2..7169bad37b 100644 --- a/common/defs/webauthn/apps/stripe.json +++ b/common/defs/webauthn/apps/stripe.json @@ -1,4 +1,4 @@ { "label": "Stripe", - "u2f": "https://dashboard.stripe.com" + "u2f": ["https://dashboard.stripe.com"] } diff --git a/common/defs/webauthn/apps/u2f.bin.coffee.json b/common/defs/webauthn/apps/u2f.bin.coffee.json index 6fecab4647..1edb18e707 100644 --- a/common/defs/webauthn/apps/u2f.bin.coffee.json +++ b/common/defs/webauthn/apps/u2f.bin.coffee.json @@ -1,4 +1,4 @@ { "label": "u2f.bin.coffee", - "u2f": "https://u2f.bin.coffee" + "u2f": ["https://u2f.bin.coffee"] } diff --git a/common/defs/webauthn/apps/webauthn.bin.coffee.json b/common/defs/webauthn/apps/webauthn.bin.coffee.json index 716aa0c8d6..6542db9239 100644 --- a/common/defs/webauthn/apps/webauthn.bin.coffee.json +++ b/common/defs/webauthn/apps/webauthn.bin.coffee.json @@ -1,4 +1,4 @@ { "label": "webauthn.bin.coffee", - "webauthn": "webauthn.bin.coffee" + "webauthn": ["webauthn.bin.coffee"] } diff --git a/common/defs/webauthn/apps/webauthn.io.json b/common/defs/webauthn/apps/webauthn.io.json index 71ab058571..70ec5929ca 100644 --- a/common/defs/webauthn/apps/webauthn.io.json +++ b/common/defs/webauthn/apps/webauthn.io.json @@ -1,4 +1,4 @@ { "label": "WebAuthn.io", - "webauthn": "webauthn.io" + "webauthn": ["webauthn.io"] } diff --git a/common/defs/webauthn/apps/webauthn.me.json b/common/defs/webauthn/apps/webauthn.me.json index 3d658d2771..212f0e2880 100644 --- a/common/defs/webauthn/apps/webauthn.me.json +++ b/common/defs/webauthn/apps/webauthn.me.json @@ -1,4 +1,4 @@ { "label": "WebAuthn.me", - "webauthn": "webauthn.me" + "webauthn": ["webauthn.me"] } diff --git a/common/defs/webauthn/apps/yubico-demo.json b/common/defs/webauthn/apps/yubico-demo.json index bb40392d67..34f3a25f1a 100644 --- a/common/defs/webauthn/apps/yubico-demo.json +++ b/common/defs/webauthn/apps/yubico-demo.json @@ -1,4 +1,4 @@ { "label": "demo.yubico.com", - "webauthn": "demo.yubico.com" + "webauthn": ["demo.yubico.com"] } diff --git a/common/defs/webauthn/gen.py b/common/defs/webauthn/gen.py index 994cae37a7..2202d38b32 100755 --- a/common/defs/webauthn/gen.py +++ b/common/defs/webauthn/gen.py @@ -19,17 +19,13 @@ def gen_core(data): print("_knownapps = {") print(" # U2F") for d in data: - if "u2f" in d: - url, label = d["u2f"], d["label"] + for url in d.get("u2f", []): + label = d["label"] print(' "%s": {"label": "%s", "use_sign_count": True},' % (url, label)) print(" # WebAuthn") for d in data: - if "webauthn" in d: - origin, label, use_sign_count = ( - d["webauthn"], - d["label"], - d.get("use_sign_count", None), - ) + for origin in d.get("webauthn", []): + label, use_sign_count = (d["label"], d.get("use_sign_count", None)) if use_sign_count is None: print(' "%s": {"label": "%s"},' % (origin, label)) else: @@ -42,15 +38,15 @@ def gen_core(data): def gen_mcu(data): for d in data: - if "u2f" in d: - url, label = d["u2f"], d["label"] + for url in d.get("u2f", []): + label = d["label"] h = sha256(url.encode()).digest() print( '\t{\n\t\t// U2F: %s\n\t\t%s,\n\t\t"%s"\n\t},' % (url, c_bytes(h), label) ) - if "webauthn" in d: - origin, label = d["webauthn"], d["label"] + for origin in d.get("webauthn", []): + label = d["label"] h = sha256(origin.encode()).digest() print( '\t{\n\t\t// WebAuthn: %s\n\t\t%s,\n\t\t"%s"\n\t},' From 6a33889706eac553129b14b08699da94a304f212 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 26 Sep 2019 16:22:32 +0200 Subject: [PATCH 09/13] common/webauthn: Add new URLs for gandi.net and Slush Pool. --- common/defs/webauthn/apps/gandi.json | 3 ++- common/defs/webauthn/apps/slushpool.json | 2 +- core/src/apps/webauthn/knownapps.py | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/common/defs/webauthn/apps/gandi.json b/common/defs/webauthn/apps/gandi.json index bc329c242f..a220cd11ac 100644 --- a/common/defs/webauthn/apps/gandi.json +++ b/common/defs/webauthn/apps/gandi.json @@ -1,4 +1,5 @@ { "label": "Gandi", - "u2f": ["https://account.gandi.net/api/u2f/trusted_facets.json"] + "u2f": ["https://account.gandi.net/api/u2f/trusted_facets.json"], + "webauthn": ["gandi.net"] } diff --git a/common/defs/webauthn/apps/slushpool.json b/common/defs/webauthn/apps/slushpool.json index 54fcf24565..3bb33a1f02 100644 --- a/common/defs/webauthn/apps/slushpool.json +++ b/common/defs/webauthn/apps/slushpool.json @@ -1,4 +1,4 @@ { "label": "Slush Pool", - "u2f": ["https://slushpool.com/static/security/u2f.json"] + "u2f": ["https://slushpool.com/static/security/u2f.json", "https://slushpool.com/u2f.json"] } diff --git a/core/src/apps/webauthn/knownapps.py b/core/src/apps/webauthn/knownapps.py index 1268bbc790..23ad454290 100644 --- a/core/src/apps/webauthn/knownapps.py +++ b/core/src/apps/webauthn/knownapps.py @@ -42,11 +42,13 @@ _knownapps = { "label": "Slush Pool", "use_sign_count": True, }, + "https://slushpool.com/u2f.json": {"label": "Slush Pool", "use_sign_count": True}, "https://dashboard.stripe.com": {"label": "Stripe", "use_sign_count": True}, "https://u2f.bin.coffee": {"label": "u2f.bin.coffee", "use_sign_count": True}, # WebAuthn "www.binance.com": {"label": "Binance", "use_sign_count": False}, "www.dropbox.com": {"label": "Dropbox"}, + "gandi.net": {"label": "Gandi"}, "github.com": {"label": "GitHub", "use_sign_count": True}, "google.com": {"label": "Google"}, "secure.login.gov": {"label": "login.gov"}, From e4c13b6357dd89ef1f0ef8f741a61d14a1b9cdaa Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 26 Sep 2019 17:42:35 +0200 Subject: [PATCH 10/13] u2f: Store hashes of U2F application parameters instead of pre-images. --- common/defs/webauthn/apps/bitbucket.json | 2 +- common/defs/webauthn/apps/bitfinex.json | 2 +- common/defs/webauthn/apps/bitwarden.json | 2 +- common/defs/webauthn/apps/dashlane.json | 2 +- common/defs/webauthn/apps/dropbox.json | 2 +- common/defs/webauthn/apps/duo.json | 2 +- common/defs/webauthn/apps/fastmail.json | 2 +- common/defs/webauthn/apps/fedora.json | 2 +- common/defs/webauthn/apps/gandi.json | 2 +- common/defs/webauthn/apps/github.json | 2 +- common/defs/webauthn/apps/gitlab.json | 2 +- common/defs/webauthn/apps/google.json | 2 +- common/defs/webauthn/apps/keeper.json | 2 +- common/defs/webauthn/apps/lastpass.json | 2 +- common/defs/webauthn/apps/slushpool.json | 2 +- common/defs/webauthn/apps/stripe.json | 2 +- common/defs/webauthn/apps/u2f.bin.coffee.json | 2 +- common/defs/webauthn/gen.py | 36 +++--- core/src/apps/webauthn/knownapps.py | 122 +++++++++++++----- legacy/firmware/u2f_knownapps.h | 59 ++++++--- 20 files changed, 167 insertions(+), 84 deletions(-) diff --git a/common/defs/webauthn/apps/bitbucket.json b/common/defs/webauthn/apps/bitbucket.json index 309dd7888e..58295e18cc 100644 --- a/common/defs/webauthn/apps/bitbucket.json +++ b/common/defs/webauthn/apps/bitbucket.json @@ -1,4 +1,4 @@ { "label": "Bitbucket", - "u2f": ["https://bitbucket.org"] + "u2f": ["12743b921297b77f1135e41fdedd4a846afe82e1f36932a9912f3b0d8dfb7d0e"] } diff --git a/common/defs/webauthn/apps/bitfinex.json b/common/defs/webauthn/apps/bitfinex.json index 21bc5935eb..a51dc9dd09 100644 --- a/common/defs/webauthn/apps/bitfinex.json +++ b/common/defs/webauthn/apps/bitfinex.json @@ -1,4 +1,4 @@ { "label": "Bitfinex", - "u2f": ["https://www.bitfinex.com"] + "u2f": ["302fd5b4492a07b9febb30e73269eca501205ccfe0c20bf7b472fa2d31e21e63"] } diff --git a/common/defs/webauthn/apps/bitwarden.json b/common/defs/webauthn/apps/bitwarden.json index 901c18bc45..062104d4ab 100644 --- a/common/defs/webauthn/apps/bitwarden.json +++ b/common/defs/webauthn/apps/bitwarden.json @@ -1,4 +1,4 @@ { "label": "Bitwarden", - "u2f": ["https://vault.bitwarden.com/app-id.json"] + "u2f": ["a34d309ffa28c12414b8ba6c07ee1efae1a85e8a04614859a67c0493b6956190"] } diff --git a/common/defs/webauthn/apps/dashlane.json b/common/defs/webauthn/apps/dashlane.json index dd8293d84e..9e8dc7373f 100644 --- a/common/defs/webauthn/apps/dashlane.json +++ b/common/defs/webauthn/apps/dashlane.json @@ -1,4 +1,4 @@ { "label": "Dashlane", - "u2f": ["https://www.dashlane.com"] + "u2f": ["68201915d74cb42af5b3cc5c95b9553e3e3a83b4d2a93b45fbadaa8469ff8e6e"] } diff --git a/common/defs/webauthn/apps/dropbox.json b/common/defs/webauthn/apps/dropbox.json index a5be49143a..ae770553ca 100644 --- a/common/defs/webauthn/apps/dropbox.json +++ b/common/defs/webauthn/apps/dropbox.json @@ -1,5 +1,5 @@ { "label": "Dropbox", - "u2f": ["https://www.dropbox.com/u2f-app-id.json"], + "u2f": ["c50f8a7b708e92f82e7a50e2bdc55d8fd91a22fe6b29c0cdf7805530842af581"], "webauthn": ["www.dropbox.com"] } diff --git a/common/defs/webauthn/apps/duo.json b/common/defs/webauthn/apps/duo.json index a04432e819..dc54c6e5da 100644 --- a/common/defs/webauthn/apps/duo.json +++ b/common/defs/webauthn/apps/duo.json @@ -1,4 +1,4 @@ { "label": "Duo", - "u2f": ["https://api-9dcf9b83.duosecurity.com"] + "u2f": ["f3e2042f94607da0a9c1f3b95e0d2f2bb2e069c5bb4fa764affa647d847b7ed6"] } diff --git a/common/defs/webauthn/apps/fastmail.json b/common/defs/webauthn/apps/fastmail.json index f31f90f388..7c98272c93 100644 --- a/common/defs/webauthn/apps/fastmail.json +++ b/common/defs/webauthn/apps/fastmail.json @@ -1,4 +1,4 @@ { "label": "FastMail", - "u2f": ["https://www.fastmail.com"] + "u2f": ["6966abe3674ea2f53079eb710197848c9be6f363992fd029e9898447cb9f0084"] } diff --git a/common/defs/webauthn/apps/fedora.json b/common/defs/webauthn/apps/fedora.json index 3a1da08aa2..dd09948793 100644 --- a/common/defs/webauthn/apps/fedora.json +++ b/common/defs/webauthn/apps/fedora.json @@ -1,4 +1,4 @@ { "label": "Fedora", - "u2f": ["https://id.fedoraproject.org/u2f-origins.json"] + "u2f": ["9d61442f5ce133bd46544fc42f0a6d54c0deb88840cac2b6aefa6514f89349e9"] } diff --git a/common/defs/webauthn/apps/gandi.json b/common/defs/webauthn/apps/gandi.json index a220cd11ac..4e0de39fe3 100644 --- a/common/defs/webauthn/apps/gandi.json +++ b/common/defs/webauthn/apps/gandi.json @@ -1,5 +1,5 @@ { "label": "Gandi", - "u2f": ["https://account.gandi.net/api/u2f/trusted_facets.json"], + "u2f": ["a4e22dcafea7e90e128950113989fc45978dc9fb87767560516c1c69dfdfd196"], "webauthn": ["gandi.net"] } diff --git a/common/defs/webauthn/apps/github.json b/common/defs/webauthn/apps/github.json index 123241068e..7cb230d972 100644 --- a/common/defs/webauthn/apps/github.json +++ b/common/defs/webauthn/apps/github.json @@ -1,6 +1,6 @@ { "label": "GitHub", - "u2f": ["https://github.com/u2f/trusted_facets"], + "u2f": ["70617dfed065863af47c15556c91798880828cc407fdf70ae85011569465a075"], "webauthn": ["github.com"], "use_sign_count": true } diff --git a/common/defs/webauthn/apps/gitlab.json b/common/defs/webauthn/apps/gitlab.json index 7f2ba750e8..9923edf616 100644 --- a/common/defs/webauthn/apps/gitlab.json +++ b/common/defs/webauthn/apps/gitlab.json @@ -1,4 +1,4 @@ { "label": "GitLab", - "u2f": ["https://gitlab.com"] + "u2f": ["e7be96a51bd0192a72840d2e5909f72ba82a2fe93faa624f03396b30e494c804"] } diff --git a/common/defs/webauthn/apps/google.json b/common/defs/webauthn/apps/google.json index 6449bc472a..d52bbf7c4c 100644 --- a/common/defs/webauthn/apps/google.json +++ b/common/defs/webauthn/apps/google.json @@ -1,5 +1,5 @@ { "label": "Google", - "u2f": ["https://www.gstatic.com/securitykey/origins.json"], + "u2f": ["a54672b222c4cf95e151ed8d4d3c767a6cc349435943794e884f3d023a8229fd"], "webauthn": ["google.com"] } diff --git a/common/defs/webauthn/apps/keeper.json b/common/defs/webauthn/apps/keeper.json index bec466e12f..05381dd53e 100644 --- a/common/defs/webauthn/apps/keeper.json +++ b/common/defs/webauthn/apps/keeper.json @@ -1,4 +1,4 @@ { "label": "Keeper", - "u2f": ["https://keepersecurity.com"] + "u2f": ["53a15ba42a7c0325b8dbee289634a48f58aea3246645d5ff418f9bb8819885a9"] } diff --git a/common/defs/webauthn/apps/lastpass.json b/common/defs/webauthn/apps/lastpass.json index 7e312c358f..f7bf592e21 100644 --- a/common/defs/webauthn/apps/lastpass.json +++ b/common/defs/webauthn/apps/lastpass.json @@ -1,4 +1,4 @@ { "label": "LastPass", - "u2f": ["https://lastpass.com"] + "u2f": ["d755c527a86bf78445c282e713dcb86d46ff8b3cafcfb73b2e8cbe6c0884cb24"] } diff --git a/common/defs/webauthn/apps/slushpool.json b/common/defs/webauthn/apps/slushpool.json index 3bb33a1f02..66c283bac7 100644 --- a/common/defs/webauthn/apps/slushpool.json +++ b/common/defs/webauthn/apps/slushpool.json @@ -1,4 +1,4 @@ { "label": "Slush Pool", - "u2f": ["https://slushpool.com/static/security/u2f.json", "https://slushpool.com/u2f.json"] + "u2f": ["08b2a3d41939aa31668493cb36cdcc4f16c4d9b4c8238b73c2f672c033007197", "38804f2eff74f228b74151c201aa82e7e8eefcacfecf23fa146b13a37666314f"] } diff --git a/common/defs/webauthn/apps/stripe.json b/common/defs/webauthn/apps/stripe.json index 7169bad37b..86929e7928 100644 --- a/common/defs/webauthn/apps/stripe.json +++ b/common/defs/webauthn/apps/stripe.json @@ -1,4 +1,4 @@ { "label": "Stripe", - "u2f": ["https://dashboard.stripe.com"] + "u2f": ["2ac6ad09a6d0772c44da73a6072f9d240fc6854a70d79c1024ff7c7559593292"] } diff --git a/common/defs/webauthn/apps/u2f.bin.coffee.json b/common/defs/webauthn/apps/u2f.bin.coffee.json index 1edb18e707..4be50c62da 100644 --- a/common/defs/webauthn/apps/u2f.bin.coffee.json +++ b/common/defs/webauthn/apps/u2f.bin.coffee.json @@ -1,4 +1,4 @@ { "label": "u2f.bin.coffee", - "u2f": ["https://u2f.bin.coffee"] + "u2f": ["1b3c16dd2f7c46e2b4c289dc16746bcc60dfcf0fb818e13215526e1408e7f468"] } diff --git a/common/defs/webauthn/gen.py b/common/defs/webauthn/gen.py index 2202d38b32..c6ca8805c6 100755 --- a/common/defs/webauthn/gen.py +++ b/common/defs/webauthn/gen.py @@ -16,35 +16,39 @@ def c_bytes(h): def gen_core(data): - print("_knownapps = {") + print("# contents generated via script in") + print("# trezor-common/defs/webauthn/gen.py") + print("# do not edit manually") + print() + print("knownapps = {") print(" # U2F") for d in data: - for url in d.get("u2f", []): + for appid in d.get("u2f", []): label = d["label"] - print(' "%s": {"label": "%s", "use_sign_count": True},' % (url, label)) + h = bytes.fromhex(appid) + print(" %s: {" % h) + print(' "label": "%s",' % label) + print(' "use_sign_count": True,') + print(" },") print(" # WebAuthn") for d in data: for origin in d.get("webauthn", []): + h = sha256(origin.encode()).digest() label, use_sign_count = (d["label"], d.get("use_sign_count", None)) - if use_sign_count is None: - print(' "%s": {"label": "%s"},' % (origin, label)) - else: - print( - ' "%s": {"label": "%s", "use_sign_count": %s},' - % (origin, label, use_sign_count) - ) + print(" %s: {" % h) + print(' "label": "%s",' % label) + if use_sign_count is not None: + print(' "use_sign_count": %s,' % use_sign_count) + print(" },") print("}") def gen_mcu(data): for d in data: - for url in d.get("u2f", []): + for appid in d.get("u2f", []): label = d["label"] - h = sha256(url.encode()).digest() - print( - '\t{\n\t\t// U2F: %s\n\t\t%s,\n\t\t"%s"\n\t},' - % (url, c_bytes(h), label) - ) + h = bytes.fromhex(appid) + print('\t{\n\t\t// U2F\n\t\t%s,\n\t\t"%s"\n\t},' % (c_bytes(h), label)) for origin in d.get("webauthn", []): label = d["label"] h = sha256(origin.encode()).digest() diff --git a/core/src/apps/webauthn/knownapps.py b/core/src/apps/webauthn/knownapps.py index 23ad454290..94128a5b4e 100644 --- a/core/src/apps/webauthn/knownapps.py +++ b/core/src/apps/webauthn/knownapps.py @@ -1,62 +1,116 @@ -from trezor.crypto.hashlib import sha256 - # contents generated via script in # trezor-common/defs/webauthn/gen.py # do not edit manually -_knownapps = { +knownapps = { # U2F - "https://bitbucket.org": {"label": "Bitbucket", "use_sign_count": True}, - "https://www.bitfinex.com": {"label": "Bitfinex", "use_sign_count": True}, - "https://vault.bitwarden.com/app-id.json": { + b"\x12t;\x92\x12\x97\xb7\x7f\x115\xe4\x1f\xde\xddJ\x84j\xfe\x82\xe1\xf3i2\xa9\x91/;\r\x8d\xfb}\x0e": { + "label": "Bitbucket", + "use_sign_count": True, + }, + b"0/\xd5\xb4I*\x07\xb9\xfe\xbb0\xe72i\xec\xa5\x01 \\\xcf\xe0\xc2\x0b\xf7\xb4r\xfa-1\xe2\x1ec": { + "label": "Bitfinex", + "use_sign_count": True, + }, + b"\xa3M0\x9f\xfa(\xc1$\x14\xb8\xbal\x07\xee\x1e\xfa\xe1\xa8^\x8a\x04aHY\xa6|\x04\x93\xb6\x95a\x90": { "label": "Bitwarden", "use_sign_count": True, }, - "https://www.dashlane.com": {"label": "Dashlane", "use_sign_count": True}, - "https://www.dropbox.com/u2f-app-id.json": { + b"h \x19\x15\xd7L\xb4*\xf5\xb3\xcc\\\x95\xb9U>>:\x83\xb4\xd2\xa9;E\xfb\xad\xaa\x84i\xff\x8en": { + "label": "Dashlane", + "use_sign_count": True, + }, + b'\xc5\x0f\x8a{p\x8e\x92\xf8.zP\xe2\xbd\xc5]\x8f\xd9\x1a"\xfek)\xc0\xcd\xf7\x80U0\x84*\xf5\x81': { "label": "Dropbox", "use_sign_count": True, }, - "https://api-9dcf9b83.duosecurity.com": {"label": "Duo", "use_sign_count": True}, - "https://www.fastmail.com": {"label": "FastMail", "use_sign_count": True}, - "https://id.fedoraproject.org/u2f-origins.json": { + b"\xf3\xe2\x04/\x94`}\xa0\xa9\xc1\xf3\xb9^\r/+\xb2\xe0i\xc5\xbbO\xa7d\xaf\xfad}\x84{~\xd6": { + "label": "Duo", + "use_sign_count": True, + }, + b"if\xab\xe3gN\xa2\xf50y\xebq\x01\x97\x84\x8c\x9b\xe6\xf3c\x99/\xd0)\xe9\x89\x84G\xcb\x9f\x00\x84": { + "label": "FastMail", + "use_sign_count": True, + }, + b"\x9daD/\\\xe13\xbdFTO\xc4/\nmT\xc0\xde\xb8\x88@\xca\xc2\xb6\xae\xfae\x14\xf8\x93I\xe9": { "label": "Fedora", "use_sign_count": True, }, - "https://account.gandi.net/api/u2f/trusted_facets.json": { + b"\xa4\xe2-\xca\xfe\xa7\xe9\x0e\x12\x89P\x119\x89\xfcE\x97\x8d\xc9\xfb\x87vu`Ql\x1ci\xdf\xdf\xd1\x96": { "label": "Gandi", "use_sign_count": True, }, - "https://github.com/u2f/trusted_facets": { + b"pa}\xfe\xd0e\x86:\xf4|\x15Ul\x91y\x88\x80\x82\x8c\xc4\x07\xfd\xf7\n\xe8P\x11V\x94e\xa0u": { "label": "GitHub", "use_sign_count": True, }, - "https://gitlab.com": {"label": "GitLab", "use_sign_count": True}, - "https://www.gstatic.com/securitykey/origins.json": { + b"\xe7\xbe\x96\xa5\x1b\xd0\x19*r\x84\r.Y\t\xf7+\xa8*/\xe9?\xaabO\x039k0\xe4\x94\xc8\x04": { + "label": "GitLab", + "use_sign_count": True, + }, + b'\xa5Fr\xb2"\xc4\xcf\x95\xe1Q\xed\x8dM\x960\x18\xcd\xdb\xd9": { + "label": "Gandi" + }, + b":\xeb\x00$`8\x1co%\x8e\x83\x95\xd3\x02oW\x1f\r\x9avH\x8d\xcd\x83v9\xb1:\xed1e`": { + "label": "GitHub", + "use_sign_count": True, + }, + b"\xd4\xc9\xd9\x02s&'\x1a\x89\xceQ\xfc\xaf2\x8e\xd6s\xf1{\xe34i\xff\x97\x9e\x8a\xb8\xddP\x1efO": { + "label": "Google" + }, + b"\xf8?\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbbv\xf6\xb2\xf5``\x17frh\xe5\xb9\xc4^": { + "label": "login.gov" + }, + b"5l\x9e\xd4\xa0\x93!\xb9i_\x1e\xaf\x91\x82\x03\xf1\xb5_h\x9d\xa6\x1f\xbc\x96\x18L\x15}\xdah\x0c\x81": { + "label": "Microsoft", + "use_sign_count": False, + }, + b"\xa6B\xd2\x1b|mU\xe1\xce#\xc59\x98(\xd2\xc7I\xbfjn\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xedS\x08\x8b": { + "label": "webauthn.bin.coffee" + }, + b"t\xa6\xea\x92\x13\xc9\x9c/t\xb2$\x92\xb3 \xcf@&*\x94\xc1\xa9P\xa09\x7f)%\x0b`\x84\x1e\xf0": { + "label": "WebAuthn.io" + }, + b"\xf9[\xc78(\xee!\x0f\x9f\xd3\xbb\xe7-\x97\x90\x80\x13\xb0\xa3u\x9e\x9a\xea=\n\xe3\x18vl\xd2\xe1\xad": { + "label": "WebAuthn.me" + }, + b"\xc4l\xef\x82\xad\x1bTdwY\x1d\x00\x8b\x08u\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94t\xbf\xeaii\x92]\x03\xb7": { + "label": "demo.yubico.com" + }, } - -knownapps = {sha256(k.encode()).digest(): v for (k, v) in _knownapps.items()} diff --git a/legacy/firmware/u2f_knownapps.h b/legacy/firmware/u2f_knownapps.h index eb31ff0fad..1b2b53bb0a 100644 --- a/legacy/firmware/u2f_knownapps.h +++ b/legacy/firmware/u2f_knownapps.h @@ -40,27 +40,27 @@ static const U2FWellKnown u2f_well_known[] = { "Binance" }, { - // U2F: https://bitbucket.org + // U2F { 0x12, 0x74, 0x3b, 0x92, 0x12, 0x97, 0xb7, 0x7f, 0x11, 0x35, 0xe4, 0x1f, 0xde, 0xdd, 0x4a, 0x84, 0x6a, 0xfe, 0x82, 0xe1, 0xf3, 0x69, 0x32, 0xa9, 0x91, 0x2f, 0x3b, 0x0d, 0x8d, 0xfb, 0x7d, 0x0e }, "Bitbucket" }, { - // U2F: https://www.bitfinex.com + // U2F { 0x30, 0x2f, 0xd5, 0xb4, 0x49, 0x2a, 0x07, 0xb9, 0xfe, 0xbb, 0x30, 0xe7, 0x32, 0x69, 0xec, 0xa5, 0x01, 0x20, 0x5c, 0xcf, 0xe0, 0xc2, 0x0b, 0xf7, 0xb4, 0x72, 0xfa, 0x2d, 0x31, 0xe2, 0x1e, 0x63 }, "Bitfinex" }, { - // U2F: https://vault.bitwarden.com/app-id.json + // U2F { 0xa3, 0x4d, 0x30, 0x9f, 0xfa, 0x28, 0xc1, 0x24, 0x14, 0xb8, 0xba, 0x6c, 0x07, 0xee, 0x1e, 0xfa, 0xe1, 0xa8, 0x5e, 0x8a, 0x04, 0x61, 0x48, 0x59, 0xa6, 0x7c, 0x04, 0x93, 0xb6, 0x95, 0x61, 0x90 }, "Bitwarden" }, { - // U2F: https://www.dashlane.com + // U2F { 0x68, 0x20, 0x19, 0x15, 0xd7, 0x4c, 0xb4, 0x2a, 0xf5, 0xb3, 0xcc, 0x5c, 0x95, 0xb9, 0x55, 0x3e, 0x3e, 0x3a, 0x83, 0xb4, 0xd2, 0xa9, 0x3b, 0x45, 0xfb, 0xad, 0xaa, 0x84, 0x69, 0xff, 0x8e, 0x6e }, "Dashlane" }, { - // U2F: https://www.dropbox.com/u2f-app-id.json + // U2F { 0xc5, 0x0f, 0x8a, 0x7b, 0x70, 0x8e, 0x92, 0xf8, 0x2e, 0x7a, 0x50, 0xe2, 0xbd, 0xc5, 0x5d, 0x8f, 0xd9, 0x1a, 0x22, 0xfe, 0x6b, 0x29, 0xc0, 0xcd, 0xf7, 0x80, 0x55, 0x30, 0x84, 0x2a, 0xf5, 0x81 }, "Dropbox" }, @@ -70,47 +70,62 @@ static const U2FWellKnown u2f_well_known[] = { "Dropbox" }, { - // U2F: https://api-9dcf9b83.duosecurity.com + // U2F { 0xf3, 0xe2, 0x04, 0x2f, 0x94, 0x60, 0x7d, 0xa0, 0xa9, 0xc1, 0xf3, 0xb9, 0x5e, 0x0d, 0x2f, 0x2b, 0xb2, 0xe0, 0x69, 0xc5, 0xbb, 0x4f, 0xa7, 0x64, 0xaf, 0xfa, 0x64, 0x7d, 0x84, 0x7b, 0x7e, 0xd6 }, "Duo" }, { - // U2F: https://www.fastmail.com + // U2F { 0x69, 0x66, 0xab, 0xe3, 0x67, 0x4e, 0xa2, 0xf5, 0x30, 0x79, 0xeb, 0x71, 0x01, 0x97, 0x84, 0x8c, 0x9b, 0xe6, 0xf3, 0x63, 0x99, 0x2f, 0xd0, 0x29, 0xe9, 0x89, 0x84, 0x47, 0xcb, 0x9f, 0x00, 0x84 }, "FastMail" }, { - // U2F: https://id.fedoraproject.org/u2f-origins.json + // U2F { 0x9d, 0x61, 0x44, 0x2f, 0x5c, 0xe1, 0x33, 0xbd, 0x46, 0x54, 0x4f, 0xc4, 0x2f, 0x0a, 0x6d, 0x54, 0xc0, 0xde, 0xb8, 0x88, 0x40, 0xca, 0xc2, 0xb6, 0xae, 0xfa, 0x65, 0x14, 0xf8, 0x93, 0x49, 0xe9 }, "Fedora" }, { - // U2F: https://account.gandi.net/api/u2f/trusted_facets.json + // U2F { 0xa4, 0xe2, 0x2d, 0xca, 0xfe, 0xa7, 0xe9, 0x0e, 0x12, 0x89, 0x50, 0x11, 0x39, 0x89, 0xfc, 0x45, 0x97, 0x8d, 0xc9, 0xfb, 0x87, 0x76, 0x75, 0x60, 0x51, 0x6c, 0x1c, 0x69, 0xdf, 0xdf, 0xd1, 0x96 }, "Gandi" }, { - // U2F: https://github.com/u2f/trusted_facets + // WebAuthn: gandi.net + { 0x54, 0xce, 0x65, 0x1e, 0xd7, 0x15, 0xb4, 0xaa, 0xa7, 0x55, 0xee, 0xce, 0xbd, 0x4e, 0xa0, 0x95, 0x08, 0x15, 0xb3, 0x34, 0xbd, 0x07, 0xd1, 0x09, 0x89, 0x3e, 0x96, 0x30, 0x18, 0xcd, 0xdb, 0xd9 }, + "Gandi" + }, + { + // U2F { 0x70, 0x61, 0x7d, 0xfe, 0xd0, 0x65, 0x86, 0x3a, 0xf4, 0x7c, 0x15, 0x55, 0x6c, 0x91, 0x79, 0x88, 0x80, 0x82, 0x8c, 0xc4, 0x07, 0xfd, 0xf7, 0x0a, 0xe8, 0x50, 0x11, 0x56, 0x94, 0x65, 0xa0, 0x75 }, "GitHub" }, { - // U2F: https://gitlab.com + // WebAuthn: github.com + { 0x3a, 0xeb, 0x00, 0x24, 0x60, 0x38, 0x1c, 0x6f, 0x25, 0x8e, 0x83, 0x95, 0xd3, 0x02, 0x6f, 0x57, 0x1f, 0x0d, 0x9a, 0x76, 0x48, 0x8d, 0xcd, 0x83, 0x76, 0x39, 0xb1, 0x3a, 0xed, 0x31, 0x65, 0x60 }, + "GitHub" + }, + { + // U2F { 0xe7, 0xbe, 0x96, 0xa5, 0x1b, 0xd0, 0x19, 0x2a, 0x72, 0x84, 0x0d, 0x2e, 0x59, 0x09, 0xf7, 0x2b, 0xa8, 0x2a, 0x2f, 0xe9, 0x3f, 0xaa, 0x62, 0x4f, 0x03, 0x39, 0x6b, 0x30, 0xe4, 0x94, 0xc8, 0x04 }, "GitLab" }, { - // U2F: https://www.gstatic.com/securitykey/origins.json + // U2F { 0xa5, 0x46, 0x72, 0xb2, 0x22, 0xc4, 0xcf, 0x95, 0xe1, 0x51, 0xed, 0x8d, 0x4d, 0x3c, 0x76, 0x7a, 0x6c, 0xc3, 0x49, 0x43, 0x59, 0x43, 0x79, 0x4e, 0x88, 0x4f, 0x3d, 0x02, 0x3a, 0x82, 0x29, 0xfd }, "Google" }, { - // U2F: https://keepersecurity.com + // WebAuthn: google.com + { 0xd4, 0xc9, 0xd9, 0x02, 0x73, 0x26, 0x27, 0x1a, 0x89, 0xce, 0x51, 0xfc, 0xaf, 0x32, 0x8e, 0xd6, 0x73, 0xf1, 0x7b, 0xe3, 0x34, 0x69, 0xff, 0x97, 0x9e, 0x8a, 0xb8, 0xdd, 0x50, 0x1e, 0x66, 0x4f }, + "Google" + }, + { + // U2F { 0x53, 0xa1, 0x5b, 0xa4, 0x2a, 0x7c, 0x03, 0x25, 0xb8, 0xdb, 0xee, 0x28, 0x96, 0x34, 0xa4, 0x8f, 0x58, 0xae, 0xa3, 0x24, 0x66, 0x45, 0xd5, 0xff, 0x41, 0x8f, 0x9b, 0xb8, 0x81, 0x98, 0x85, 0xa9 }, "Keeper" }, { - // U2F: https://lastpass.com + // U2F { 0xd7, 0x55, 0xc5, 0x27, 0xa8, 0x6b, 0xf7, 0x84, 0x45, 0xc2, 0x82, 0xe7, 0x13, 0xdc, 0xb8, 0x6d, 0x46, 0xff, 0x8b, 0x3c, 0xaf, 0xcf, 0xb7, 0x3b, 0x2e, 0x8c, 0xbe, 0x6c, 0x08, 0x84, 0xcb, 0x24 }, "LastPass" }, @@ -120,17 +135,27 @@ static const U2FWellKnown u2f_well_known[] = { "login.gov" }, { - // U2F: https://slushpool.com/static/security/u2f.json + // WebAuthn: login.microsoft.com + { 0x35, 0x6c, 0x9e, 0xd4, 0xa0, 0x93, 0x21, 0xb9, 0x69, 0x5f, 0x1e, 0xaf, 0x91, 0x82, 0x03, 0xf1, 0xb5, 0x5f, 0x68, 0x9d, 0xa6, 0x1f, 0xbc, 0x96, 0x18, 0x4c, 0x15, 0x7d, 0xda, 0x68, 0x0c, 0x81 }, + "Microsoft" + }, + { + // U2F { 0x08, 0xb2, 0xa3, 0xd4, 0x19, 0x39, 0xaa, 0x31, 0x66, 0x84, 0x93, 0xcb, 0x36, 0xcd, 0xcc, 0x4f, 0x16, 0xc4, 0xd9, 0xb4, 0xc8, 0x23, 0x8b, 0x73, 0xc2, 0xf6, 0x72, 0xc0, 0x33, 0x00, 0x71, 0x97 }, "Slush Pool" }, { - // U2F: https://dashboard.stripe.com + // U2F + { 0x38, 0x80, 0x4f, 0x2e, 0xff, 0x74, 0xf2, 0x28, 0xb7, 0x41, 0x51, 0xc2, 0x01, 0xaa, 0x82, 0xe7, 0xe8, 0xee, 0xfc, 0xac, 0xfe, 0xcf, 0x23, 0xfa, 0x14, 0x6b, 0x13, 0xa3, 0x76, 0x66, 0x31, 0x4f }, + "Slush Pool" + }, + { + // U2F { 0x2a, 0xc6, 0xad, 0x09, 0xa6, 0xd0, 0x77, 0x2c, 0x44, 0xda, 0x73, 0xa6, 0x07, 0x2f, 0x9d, 0x24, 0x0f, 0xc6, 0x85, 0x4a, 0x70, 0xd7, 0x9c, 0x10, 0x24, 0xff, 0x7c, 0x75, 0x59, 0x59, 0x32, 0x92 }, "Stripe" }, { - // U2F: https://u2f.bin.coffee + // U2F { 0x1b, 0x3c, 0x16, 0xdd, 0x2f, 0x7c, 0x46, 0xe2, 0xb4, 0xc2, 0x89, 0xdc, 0x16, 0x74, 0x6b, 0xcc, 0x60, 0xdf, 0xcf, 0x0f, 0xb8, 0x18, 0xe1, 0x32, 0x15, 0x52, 0x6e, 0x14, 0x08, 0xe7, 0xf4, 0x68 }, "u2f.bin.coffee" }, From 9ea81365457868f5eb6f306b5835d83a64a21bd7 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 26 Sep 2019 18:20:27 +0200 Subject: [PATCH 11/13] u2f: Add keepersecurity.eu to knownapps. --- common/defs/webauthn/apps/keeper.json | 5 ++++- common/defs/webauthn/apps/slushpool.json | 5 ++++- core/src/apps/webauthn/knownapps.py | 4 ++++ legacy/firmware/u2f_knownapps.h | 5 +++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/common/defs/webauthn/apps/keeper.json b/common/defs/webauthn/apps/keeper.json index 05381dd53e..1b35f3b429 100644 --- a/common/defs/webauthn/apps/keeper.json +++ b/common/defs/webauthn/apps/keeper.json @@ -1,4 +1,7 @@ { "label": "Keeper", - "u2f": ["53a15ba42a7c0325b8dbee289634a48f58aea3246645d5ff418f9bb8819885a9"] + "u2f": [ + "53a15ba42a7c0325b8dbee289634a48f58aea3246645d5ff418f9bb8819885a9", + "d65f005ef4dea9320c9973053c95ff6020115d5fec1b7fee41a578e18df9ca8c" + ] } diff --git a/common/defs/webauthn/apps/slushpool.json b/common/defs/webauthn/apps/slushpool.json index 66c283bac7..e15d386ea2 100644 --- a/common/defs/webauthn/apps/slushpool.json +++ b/common/defs/webauthn/apps/slushpool.json @@ -1,4 +1,7 @@ { "label": "Slush Pool", - "u2f": ["08b2a3d41939aa31668493cb36cdcc4f16c4d9b4c8238b73c2f672c033007197", "38804f2eff74f228b74151c201aa82e7e8eefcacfecf23fa146b13a37666314f"] + "u2f": [ + "08b2a3d41939aa31668493cb36cdcc4f16c4d9b4c8238b73c2f672c033007197", + "38804f2eff74f228b74151c201aa82e7e8eefcacfecf23fa146b13a37666314f" + ] } diff --git a/core/src/apps/webauthn/knownapps.py b/core/src/apps/webauthn/knownapps.py index 94128a5b4e..81b63aeb98 100644 --- a/core/src/apps/webauthn/knownapps.py +++ b/core/src/apps/webauthn/knownapps.py @@ -56,6 +56,10 @@ knownapps = { "label": "Keeper", "use_sign_count": True, }, + b"\xd6_\x00^\xf4\xde\xa92\x0c\x99s\x05<\x95\xff` \x11]_\xec\x1b\x7f\xeeA\xa5x\xe1\x8d\xf9\xca\x8c": { + "label": "Keeper", + "use_sign_count": True, + }, b"\xd7U\xc5'\xa8k\xf7\x84E\xc2\x82\xe7\x13\xdc\xb8mF\xff\x8b<\xaf\xcf\xb7;.\x8c\xbel\x08\x84\xcb$": { "label": "LastPass", "use_sign_count": True, diff --git a/legacy/firmware/u2f_knownapps.h b/legacy/firmware/u2f_knownapps.h index 1b2b53bb0a..e8b2fc4579 100644 --- a/legacy/firmware/u2f_knownapps.h +++ b/legacy/firmware/u2f_knownapps.h @@ -124,6 +124,11 @@ static const U2FWellKnown u2f_well_known[] = { { 0x53, 0xa1, 0x5b, 0xa4, 0x2a, 0x7c, 0x03, 0x25, 0xb8, 0xdb, 0xee, 0x28, 0x96, 0x34, 0xa4, 0x8f, 0x58, 0xae, 0xa3, 0x24, 0x66, 0x45, 0xd5, 0xff, 0x41, 0x8f, 0x9b, 0xb8, 0x81, 0x98, 0x85, 0xa9 }, "Keeper" }, + { + // U2F + { 0xd6, 0x5f, 0x00, 0x5e, 0xf4, 0xde, 0xa9, 0x32, 0x0c, 0x99, 0x73, 0x05, 0x3c, 0x95, 0xff, 0x60, 0x20, 0x11, 0x5d, 0x5f, 0xec, 0x1b, 0x7f, 0xee, 0x41, 0xa5, 0x78, 0xe1, 0x8d, 0xf9, 0xca, 0x8c }, + "Keeper" + }, { // U2F { 0xd7, 0x55, 0xc5, 0x27, 0xa8, 0x6b, 0xf7, 0x84, 0x45, 0xc2, 0x82, 0xe7, 0x13, 0xdc, 0xb8, 0x6d, 0x46, 0xff, 0x8b, 0x3c, 0xaf, 0xcf, 0xb7, 0x3b, 0x2e, 0x8c, 0xbe, 0x6c, 0x08, 0x84, 0xcb, 0x24 }, From 0495d18b1e10bc5f8cc3071113e47265415b113a Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 26 Sep 2019 18:42:51 +0200 Subject: [PATCH 12/13] core/webauthn: Fix CTAP HID protocol to correctly handle invalid channel IDs and interleaving packets from different channels. --- core/src/apps/webauthn/__init__.py | 111 +++++++++++++++-------------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index 9f9234545c..e92f5d7942 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -363,63 +363,66 @@ async def read_cmd(iface: io.HID) -> Optional[Cmd]: read = loop.wait(iface.iface_num() | io.POLL_READ) buf = await read + while True: + ifrm = overlay_struct(buf, desc_init) + bcnt = ifrm.bcnt + data = ifrm.data + datalen = len(data) + seq = 0 - ifrm = overlay_struct(buf, desc_init) - bcnt = ifrm.bcnt - data = ifrm.data - datalen = len(data) - seq = 0 - - if ifrm.cmd & _TYPE_MASK == _TYPE_CONT: - # unexpected cont packet, abort current msg - if __debug__: - log.warning(__name__, "_TYPE_CONT") - return None - - if bcnt > _MAX_U2FHID_MSG_PAYLOAD_LEN: - # invalid payload length, abort current msg - if __debug__: - log.warning(__name__, "_MAX_U2FHID_MSG_PAYLOAD_LEN") - await send_cmd(cmd_error(ifrm.cid, _ERR_INVALID_LEN), iface) - return None - - if datalen < bcnt: - databuf = bytearray(bcnt) - utils.memcpy(databuf, 0, data, 0, bcnt) - data = databuf - else: - data = data[:bcnt] - - while datalen < bcnt: - buf = await read - - cfrm = overlay_struct(buf, desc_cont) - - if cfrm.seq == _CMD_INIT: - # _CMD_INIT frame, cancels current channel - ifrm = overlay_struct(buf, desc_init) - data = ifrm.data[: ifrm.bcnt] - break - - if cfrm.cid != ifrm.cid: - # cont frame for a different channel, reply with BUSY and skip + if ifrm.cmd & _TYPE_MASK == _TYPE_CONT: + # unexpected cont packet, abort current msg if __debug__: - log.warning(__name__, "_ERR_CHANNEL_BUSY") - await send_cmd(cmd_error(cfrm.cid, _ERR_CHANNEL_BUSY), iface) - continue - - if cfrm.seq != seq: - # cont frame for this channel, but incorrect seq number, abort - # current msg - if __debug__: - log.warning(__name__, "_ERR_INVALID_SEQ") - await send_cmd(cmd_error(cfrm.cid, _ERR_INVALID_SEQ), iface) + log.warning(__name__, "_TYPE_CONT") return None - datalen += utils.memcpy(data, datalen, cfrm.data, 0, bcnt - datalen) - seq += 1 + if ifrm.cid == 0 or ((ifrm.cid == _CID_BROADCAST) and (ifrm.cmd != _CMD_INIT)): + # CID 0 is reserved for future use and _CID_BROADCAST is reserved for channel allocation + await send_cmd(cmd_error(ifrm.cid, _ERR_INVALID_CID), iface) + return None - return Cmd(ifrm.cid, ifrm.cmd, data) + if bcnt > _MAX_U2FHID_MSG_PAYLOAD_LEN: + # invalid payload length, abort current msg + if __debug__: + log.warning(__name__, "_MAX_U2FHID_MSG_PAYLOAD_LEN") + await send_cmd(cmd_error(ifrm.cid, _ERR_INVALID_LEN), iface) + return None + + if datalen < bcnt: + databuf = bytearray(bcnt) + utils.memcpy(databuf, 0, data, 0, bcnt) + data = databuf + else: + data = data[:bcnt] + + while datalen < bcnt: + buf = await read + + cfrm = overlay_struct(buf, desc_cont) + + if cfrm.seq == _CMD_INIT: + # _CMD_INIT frame, cancels current channel + break + + if cfrm.cid != ifrm.cid: + # cont frame for a different channel, reply with BUSY and abort + if __debug__: + log.warning(__name__, "_ERR_CHANNEL_BUSY") + await send_cmd(cmd_error(cfrm.cid, _ERR_CHANNEL_BUSY), iface) + continue + + if cfrm.seq != seq: + # cont frame for this channel, but incorrect seq number, abort + # current msg + if __debug__: + log.warning(__name__, "_ERR_INVALID_SEQ") + await send_cmd(cmd_error(cfrm.cid, _ERR_INVALID_SEQ), iface) + return None + + datalen += utils.memcpy(data, datalen, cfrm.data, 0, bcnt - datalen) + seq += 1 + else: + return Cmd(ifrm.cid, ifrm.cmd, data) async def send_cmd(cmd: Cmd, iface: io.HID) -> None: @@ -1051,9 +1054,7 @@ def dispatch_cmd(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]: def cmd_init(req: Cmd) -> Cmd: - if req.cid == 0: - return cmd_error(req.cid, _ERR_INVALID_CID) - elif req.cid == _CID_BROADCAST: + if req.cid == _CID_BROADCAST: # uint32_t except 0 and 0xffffffff resp_cid = random.uniform(0xFFFFFFFE) + 1 else: From 500401d81ff2c9fce534c0707f18db66e2924844 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 26 Sep 2019 18:50:58 +0200 Subject: [PATCH 13/13] core/webauthn: Place a 500 ms timeout on CTAP HID continuation packets. --- core/src/apps/webauthn/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index e92f5d7942..411fd0694e 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -113,6 +113,7 @@ _KEEPALIVE_STATUS_UP_NEEDED = const(0x02) # waiting for user presence # time intervals and timeouts _KEEPALIVE_INTERVAL_MS = const(80) # interval between keepalive commands +_CTAP_HID_TIMEOUT_MS = const(500) _U2F_CONFIRM_TIMEOUT_MS = const(10 * 1000) _FIDO2_CONFIRM_TIMEOUT_MS = const(60 * 1000) @@ -396,7 +397,10 @@ async def read_cmd(iface: io.HID) -> Optional[Cmd]: data = data[:bcnt] while datalen < bcnt: - buf = await read + buf = await loop.race(read, loop.sleep(_CTAP_HID_TIMEOUT_MS * 1000)) + if not isinstance(buf, (bytes, bytearray)): + await send_cmd(cmd_error(ifrm.cid, _ERR_MSG_TIMEOUT), iface) + return None cfrm = overlay_struct(buf, desc_cont)