1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-18 05:28:40 +00:00

chore(core): decrease webauthn size by 1270 bytes

This commit is contained in:
grdddj 2022-09-19 12:55:46 +02:00 committed by matejcik
parent 3711fd0f19
commit 0c3423b1c7
10 changed files with 665 additions and 595 deletions

View File

@ -1,9 +1,6 @@
def boot() -> None:
from trezor import loop from trezor import loop
import usb import usb
from .fido2 import handle_reports from .fido2 import handle_reports
def boot() -> None:
loop.schedule(handle_reports(usb.iface_webauthn)) loop.schedule(handle_reports(usb.iface_webauthn))

View File

@ -1,17 +1,11 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import storage.device
from trezor import wire
from trezor.messages import Success
from trezor.ui.components.common.webauthn import ConfirmInfo from trezor.ui.components.common.webauthn import ConfirmInfo
from trezor.ui.layouts import show_error_and_raise
from trezor.ui.layouts.webauthn import confirm_webauthn
from .credential import Fido2Credential
from .resident_credentials import store_resident_credential
if TYPE_CHECKING: if TYPE_CHECKING:
from trezor.messages import WebAuthnAddResidentCredential from trezor.messages import WebAuthnAddResidentCredential, Success
from trezor.wire import Context
from .credential import Fido2Credential
class ConfirmAddCredential(ConfirmInfo): class ConfirmAddCredential(ConfirmInfo):
@ -31,9 +25,17 @@ class ConfirmAddCredential(ConfirmInfo):
async def add_resident_credential( async def add_resident_credential(
ctx: wire.Context, msg: WebAuthnAddResidentCredential ctx: Context, msg: WebAuthnAddResidentCredential
) -> Success: ) -> Success:
if not storage.device.is_initialized(): import storage.device as storage_device
from trezor import wire
from trezor.ui.layouts import show_error_and_raise
from trezor.ui.layouts.webauthn import confirm_webauthn
from trezor.messages import Success
from .credential import Fido2Credential
from .resident_credentials import store_resident_credential
if not storage_device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if not msg.credential_id: if not msg.credential_id:
raise wire.ProcessError("Missing credential ID parameter.") raise wire.ProcessError("Missing credential ID parameter.")
@ -44,9 +46,9 @@ async def add_resident_credential(
await show_error_and_raise( await show_error_and_raise(
ctx, ctx,
"warning_credential", "warning_credential",
header="Import credential", "The credential you are trying to import does\nnot belong to this authenticator.",
"Import credential",
button="Close", button="Close",
content="The credential you are trying to import does\nnot belong to this authenticator.",
red=True, red=True,
) )

View File

@ -1,21 +1,26 @@
import ustruct import ustruct
from micropython import const from micropython import const
from typing import Iterable from typing import TYPE_CHECKING
from ubinascii import hexlify from ubinascii import hexlify
import storage.device import storage.device as storage_device
from trezor import log, utils from trezor import utils
from trezor.crypto import bip32, chacha20poly1305, der, hashlib, hmac, random from trezor.crypto import chacha20poly1305, der, hashlib, hmac, random
from trezor.crypto.curve import ed25519, nist256p1 from trezor.crypto.curve import ed25519, nist256p1
from apps.common import cbor, seed from apps.common import cbor, seed
from apps.common.paths import HARDENED from apps.common.paths import HARDENED
from . import common from .common import COSE_ALG_EDDSA, COSE_ALG_ES256, COSE_CURVE_ED25519, COSE_CURVE_P256
if TYPE_CHECKING:
from typing import Iterable
from trezor.crypto import bip32
# Credential ID values # Credential ID values
_CRED_ID_VERSION = b"\xf1\xd0\x02\x00" _CRED_ID_VERSION = b"\xf1\xd0\x02\x00"
CRED_ID_MIN_LENGTH = const(33) _CRED_ID_MIN_LENGTH = const(33)
CRED_ID_MAX_LENGTH = const(1024) CRED_ID_MAX_LENGTH = const(1024)
_KEY_HANDLE_LENGTH = const(64) _KEY_HANDLE_LENGTH = const(64)
@ -24,7 +29,7 @@ _USER_ID_MAX_LENGTH = const(64)
# Maximum supported length of the RP name, user name or user displayName in bytes. # Maximum supported length of the RP name, user name or user displayName in bytes.
# Note: The WebAuthn spec allows authenticators to truncate to 64 bytes or more. # Note: The WebAuthn spec allows authenticators to truncate to 64 bytes or more.
NAME_MAX_LENGTH = const(100) _NAME_MAX_LENGTH = const(100)
# Credential ID keys # Credential ID keys
_CRED_ID_RP_ID = const(1) _CRED_ID_RP_ID = const(1)
@ -39,13 +44,13 @@ _CRED_ID_ALGORITHM = const(9)
_CRED_ID_CURVE = const(10) _CRED_ID_CURVE = const(10)
# Defaults # Defaults
_DEFAULT_ALGORITHM = common.COSE_ALG_ES256 _DEFAULT_ALGORITHM = COSE_ALG_ES256
_DEFAULT_CURVE = common.COSE_CURVE_P256 _DEFAULT_CURVE = COSE_CURVE_P256
# Curves # Curves
_CURVE_NAME = { _CURVE_NAME = {
common.COSE_CURVE_ED25519: "ed25519", COSE_CURVE_ED25519: "ed25519",
common.COSE_CURVE_P256: "nist256p1", COSE_CURVE_P256: "nist256p1",
} }
# Key paths # Key paths
@ -92,7 +97,7 @@ class Credential:
return None return None
def next_signature_counter(self) -> int: def next_signature_counter(self) -> int:
return storage.device.next_u2f_counter() or 0 return storage_device.next_u2f_counter() or 0
@staticmethod @staticmethod
def from_bytes(data: bytes, rp_id_hash: bytes) -> "Credential": def from_bytes(data: bytes, rp_id_hash: bytes) -> "Credential":
@ -128,7 +133,7 @@ class Fido2Credential(Credential):
return True return True
def generate_id(self) -> None: def generate_id(self) -> None:
self.creation_time = storage.device.next_u2f_counter() or 0 self.creation_time = storage_device.next_u2f_counter() or 0
if not self.check_required_fields(): if not self.check_required_fields():
raise AssertionError raise AssertionError
@ -169,7 +174,7 @@ class Fido2Credential(Credential):
def from_cred_id( def from_cred_id(
cls, cred_id: bytes, rp_id_hash: bytes | None cls, cred_id: bytes, rp_id_hash: bytes | None
) -> "Fido2Credential": ) -> "Fido2Credential":
if len(cred_id) < CRED_ID_MIN_LENGTH or cred_id[0:4] != _CRED_ID_VERSION: if len(cred_id) < _CRED_ID_MIN_LENGTH or cred_id[0:4] != _CRED_ID_VERSION:
raise ValueError # invalid length or version raise ValueError # invalid length or version
key = seed.derive_slip21_node_without_passphrase( key = seed.derive_slip21_node_without_passphrase(
@ -202,18 +207,20 @@ class Fido2Credential(Credential):
if not isinstance(data, dict): if not isinstance(data, dict):
raise ValueError # invalid CBOR data raise ValueError # invalid CBOR data
get = data.get # local_cache_attribute
cred = cls() cred = cls()
cred.rp_id = data.get(_CRED_ID_RP_ID, None) cred.rp_id = get(_CRED_ID_RP_ID, None)
cred.rp_id_hash = rp_id_hash cred.rp_id_hash = rp_id_hash
cred.rp_name = data.get(_CRED_ID_RP_NAME, None) cred.rp_name = get(_CRED_ID_RP_NAME, None)
cred.user_id = data.get(_CRED_ID_USER_ID, None) cred.user_id = get(_CRED_ID_USER_ID, None)
cred.user_name = data.get(_CRED_ID_USER_NAME, None) cred.user_name = get(_CRED_ID_USER_NAME, None)
cred.user_display_name = data.get(_CRED_ID_USER_DISPLAY_NAME, None) cred.user_display_name = get(_CRED_ID_USER_DISPLAY_NAME, None)
cred.creation_time = data.get(_CRED_ID_CREATION_TIME, 0) cred.creation_time = get(_CRED_ID_CREATION_TIME, 0)
cred.hmac_secret = data.get(_CRED_ID_HMAC_SECRET, False) cred.hmac_secret = get(_CRED_ID_HMAC_SECRET, False)
cred.use_sign_count = data.get(_CRED_ID_USE_SIGN_COUNT, False) cred.use_sign_count = get(_CRED_ID_USE_SIGN_COUNT, False)
cred.algorithm = data.get(_CRED_ID_ALGORITHM, _DEFAULT_ALGORITHM) cred.algorithm = get(_CRED_ID_ALGORITHM, _DEFAULT_ALGORITHM)
cred.curve = data.get(_CRED_ID_CURVE, _DEFAULT_CURVE) cred.curve = get(_CRED_ID_CURVE, _DEFAULT_CURVE)
cred.id = cred_id cred.id = cred_id
if ( if (
@ -228,14 +235,14 @@ class Fido2Credential(Credential):
def truncate_names(self) -> None: def truncate_names(self) -> None:
if self.rp_name: if self.rp_name:
self.rp_name = utils.truncate_utf8(self.rp_name, NAME_MAX_LENGTH) self.rp_name = utils.truncate_utf8(self.rp_name, _NAME_MAX_LENGTH)
if self.user_name: if self.user_name:
self.user_name = utils.truncate_utf8(self.user_name, NAME_MAX_LENGTH) self.user_name = utils.truncate_utf8(self.user_name, _NAME_MAX_LENGTH)
if self.user_display_name: if self.user_display_name:
self.user_display_name = utils.truncate_utf8( self.user_display_name = utils.truncate_utf8(
self.user_display_name, NAME_MAX_LENGTH self.user_display_name, _NAME_MAX_LENGTH
) )
def check_required_fields(self) -> bool: def check_required_fields(self) -> bool:
@ -288,24 +295,28 @@ class Fido2Credential(Credential):
return node.private_key() return node.private_key()
def public_key(self) -> bytes: def public_key(self) -> bytes:
if self.curve == common.COSE_CURVE_P256: from . import common
curve = self.curve # local_cache_attribute
if curve == COSE_CURVE_P256:
pubkey = nist256p1.publickey(self._private_key(), False) pubkey = nist256p1.publickey(self._private_key(), False)
return cbor.encode( return cbor.encode(
{ {
common.COSE_KEY_ALG: self.algorithm, common.COSE_KEY_ALG: self.algorithm,
common.COSE_KEY_KTY: common.COSE_KEYTYPE_EC2, common.COSE_KEY_KTY: common.COSE_KEYTYPE_EC2,
common.COSE_KEY_CRV: self.curve, common.COSE_KEY_CRV: curve,
common.COSE_KEY_X: pubkey[1:33], common.COSE_KEY_X: pubkey[1:33],
common.COSE_KEY_Y: pubkey[33:], common.COSE_KEY_Y: pubkey[33:],
} }
) )
elif self.curve == common.COSE_CURVE_ED25519: elif curve == COSE_CURVE_ED25519:
pubkey = ed25519.publickey(self._private_key()) pubkey = ed25519.publickey(self._private_key())
return cbor.encode( return cbor.encode(
{ {
common.COSE_KEY_ALG: self.algorithm, common.COSE_KEY_ALG: self.algorithm,
common.COSE_KEY_KTY: common.COSE_KEYTYPE_OKP, common.COSE_KEY_KTY: common.COSE_KEYTYPE_OKP,
common.COSE_KEY_CRV: self.curve, common.COSE_KEY_CRV: curve,
common.COSE_KEY_X: pubkey, common.COSE_KEY_X: pubkey,
} }
) )
@ -313,13 +324,13 @@ class Fido2Credential(Credential):
def sign(self, data: Iterable[bytes]) -> bytes: def sign(self, data: Iterable[bytes]) -> bytes:
if (self.algorithm, self.curve) == ( if (self.algorithm, self.curve) == (
common.COSE_ALG_ES256, COSE_ALG_ES256,
common.COSE_CURVE_P256, COSE_CURVE_P256,
): ):
return self._u2f_sign(data) return self._u2f_sign(data)
elif (self.algorithm, self.curve) == ( elif (self.algorithm, self.curve) == (
common.COSE_ALG_EDDSA, COSE_ALG_EDDSA,
common.COSE_CURVE_ED25519, COSE_CURVE_ED25519,
): ):
return ed25519.sign( return ed25519.sign(
self._private_key(), b"".join(segment for segment in data) self._private_key(), b"".join(segment for segment in data)
@ -329,13 +340,13 @@ class Fido2Credential(Credential):
def bogus_signature(self) -> bytes: def bogus_signature(self) -> bytes:
if (self.algorithm, self.curve) == ( if (self.algorithm, self.curve) == (
common.COSE_ALG_ES256, COSE_ALG_ES256,
common.COSE_CURVE_P256, COSE_CURVE_P256,
): ):
return der.encode_seq((b"\x0a" * 32, b"\x0a" * 32)) return der.encode_seq((b"\x0a" * 32, b"\x0a" * 32))
elif (self.algorithm, self.curve) == ( elif (self.algorithm, self.curve) == (
common.COSE_ALG_EDDSA, COSE_ALG_EDDSA,
common.COSE_CURVE_ED25519, COSE_CURVE_ED25519,
): ):
return b"\x0a" * 64 return b"\x0a" * 64
@ -440,6 +451,8 @@ class U2fCredential(Credential):
def _node_from_key_handle( def _node_from_key_handle(
rp_id_hash: bytes, keyhandle: bytes, pathformat: str rp_id_hash: bytes, keyhandle: bytes, pathformat: str
) -> bip32.HDNode | None: ) -> bip32.HDNode | None:
from trezor import log
# unpack the keypath from the first half of keyhandle # unpack the keypath from the first half of keyhandle
keypath = keyhandle[:32] keypath = keyhandle[:32]
path = ustruct.unpack(pathformat, keypath) path = ustruct.unpack(pathformat, keypath)

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@
# (by running `make templates` in `core`) # (by running `make templates` in `core`)
# do not edit manually! # do not edit manually!
# NOTE: using positional arguments saves 520 bytes in flash space
class FIDOApp: class FIDOApp:
def __init__( def __init__(
@ -22,338 +24,338 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None:
if rp_id_hash == b"\x96\x89\x78\xa2\x99\x53\xde\x52\xd3\xef\x0f\x0c\x71\xb7\xb7\xb6\xb1\xaf\x9f\x08\xe2\x57\x89\x6a\x8d\x81\x26\x91\x85\x30\x29\x3b": if rp_id_hash == b"\x96\x89\x78\xa2\x99\x53\xde\x52\xd3\xef\x0f\x0c\x71\xb7\xb7\xb6\xb1\xaf\x9f\x08\xe2\x57\x89\x6a\x8d\x81\x26\x91\x85\x30\x29\x3b":
# U2F key for Amazon Web Services # U2F key for Amazon Web Services
return FIDOApp( return FIDOApp(
label="aws.amazon.com", "aws.amazon.com", # label
icon="apps/webauthn/res/icon_aws.toif", "apps/webauthn/res/icon_aws.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xc3\x40\x8c\x04\x47\x88\xae\xa5\xb3\xdf\x30\x89\x52\xfd\x8c\xa3\xc7\x0e\x21\xfe\xf4\xf6\xc1\xc2\x37\x4c\xaa\x1d\xf9\xb2\x8d\xdd": if rp_id_hash == b"\xc3\x40\x8c\x04\x47\x88\xae\xa5\xb3\xdf\x30\x89\x52\xfd\x8c\xa3\xc7\x0e\x21\xfe\xf4\xf6\xc1\xc2\x37\x4c\xaa\x1d\xf9\xb2\x8d\xdd":
# WebAuthn key for Binance # WebAuthn key for Binance
return FIDOApp( return FIDOApp(
label="www.binance.com", "www.binance.com", # label
icon="apps/webauthn/res/icon_binance.toif", "apps/webauthn/res/icon_binance.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=True, True, # use_self_attestation
) )
if rp_id_hash == b"\x20\xf6\x61\xb1\x94\x0c\x34\x70\xac\x54\xfa\x2e\xb4\x99\x90\xfd\x33\xb5\x6d\xe8\xde\x60\x18\x70\xff\x02\xa8\x06\x0f\x3b\x7c\x58": if rp_id_hash == b"\x20\xf6\x61\xb1\x94\x0c\x34\x70\xac\x54\xfa\x2e\xb4\x99\x90\xfd\x33\xb5\x6d\xe8\xde\x60\x18\x70\xff\x02\xa8\x06\x0f\x3b\x7c\x58":
# WebAuthn key for Binance # WebAuthn key for Binance
return FIDOApp( return FIDOApp(
label="binance.com", "binance.com", # label
icon="apps/webauthn/res/icon_binance.toif", "apps/webauthn/res/icon_binance.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=True, True, # use_self_attestation
) )
if rp_id_hash == b"\x12\x74\x3b\x92\x12\x97\xb7\x7f\x11\x35\xe4\x1f\xde\xdd\x4a\x84\x6a\xfe\x82\xe1\xf3\x69\x32\xa9\x91\x2f\x3b\x0d\x8d\xfb\x7d\x0e": if rp_id_hash == b"\x12\x74\x3b\x92\x12\x97\xb7\x7f\x11\x35\xe4\x1f\xde\xdd\x4a\x84\x6a\xfe\x82\xe1\xf3\x69\x32\xa9\x91\x2f\x3b\x0d\x8d\xfb\x7d\x0e":
# U2F key for Bitbucket # U2F key for Bitbucket
return FIDOApp( return FIDOApp(
label="bitbucket.org", "bitbucket.org", # label
icon="apps/webauthn/res/icon_bitbucket.toif", "apps/webauthn/res/icon_bitbucket.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x30\x2f\xd5\xb4\x49\x2a\x07\xb9\xfe\xbb\x30\xe7\x32\x69\xec\xa5\x01\x20\x5c\xcf\xe0\xc2\x0b\xf7\xb4\x72\xfa\x2d\x31\xe2\x1e\x63": if rp_id_hash == b"\x30\x2f\xd5\xb4\x49\x2a\x07\xb9\xfe\xbb\x30\xe7\x32\x69\xec\xa5\x01\x20\x5c\xcf\xe0\xc2\x0b\xf7\xb4\x72\xfa\x2d\x31\xe2\x1e\x63":
# U2F key for Bitfinex # U2F key for Bitfinex
return FIDOApp( return FIDOApp(
label="www.bitfinex.com", "www.bitfinex.com", # label
icon="apps/webauthn/res/icon_bitfinex.toif", "apps/webauthn/res/icon_bitfinex.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xa3\x4d\x30\x9f\xfa\x28\xc1\x24\x14\xb8\xba\x6c\x07\xee\x1e\xfa\xe1\xa8\x5e\x8a\x04\x61\x48\x59\xa6\x7c\x04\x93\xb6\x95\x61\x90": if rp_id_hash == b"\xa3\x4d\x30\x9f\xfa\x28\xc1\x24\x14\xb8\xba\x6c\x07\xee\x1e\xfa\xe1\xa8\x5e\x8a\x04\x61\x48\x59\xa6\x7c\x04\x93\xb6\x95\x61\x90":
# U2F key for Bitwarden # U2F key for Bitwarden
return FIDOApp( return FIDOApp(
label="vault.bitwarden.com", "vault.bitwarden.com", # label
icon="apps/webauthn/res/icon_bitwarden.toif", "apps/webauthn/res/icon_bitwarden.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x19\x81\x5c\xb9\xa5\xfb\x25\xd8\x05\xde\xbd\x7b\x32\x53\x7e\xd5\x78\x63\x9b\x3e\xd1\x08\xec\x7c\x5b\xb9\xe8\xf0\xdf\xb1\x68\x73": if rp_id_hash == b"\x19\x81\x5c\xb9\xa5\xfb\x25\xd8\x05\xde\xbd\x7b\x32\x53\x7e\xd5\x78\x63\x9b\x3e\xd1\x08\xec\x7c\x5b\xb9\xe8\xf0\xdf\xb1\x68\x73":
# WebAuthn key for Cloudflare # WebAuthn key for Cloudflare
return FIDOApp( return FIDOApp(
label="dash.cloudflare.com", "dash.cloudflare.com", # label
icon="apps/webauthn/res/icon_cloudflare.toif", "apps/webauthn/res/icon_cloudflare.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xe2\x7d\x61\xb4\xe9\x9d\xe0\xed\x98\x16\x3c\xb3\x8b\x7a\xf9\x33\xc6\x66\x5e\x55\x09\xe8\x49\x08\x37\x05\x58\x13\x77\x8e\x23\x6a": if rp_id_hash == b"\xe2\x7d\x61\xb4\xe9\x9d\xe0\xed\x98\x16\x3c\xb3\x8b\x7a\xf9\x33\xc6\x66\x5e\x55\x09\xe8\x49\x08\x37\x05\x58\x13\x77\x8e\x23\x6a":
# WebAuthn key for Coinbase # WebAuthn key for Coinbase
return FIDOApp( return FIDOApp(
label="coinbase.com", "coinbase.com", # label
icon="apps/webauthn/res/icon_coinbase.toif", "apps/webauthn/res/icon_coinbase.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x68\x20\x19\x15\xd7\x4c\xb4\x2a\xf5\xb3\xcc\x5c\x95\xb9\x55\x3e\x3e\x3a\x83\xb4\xd2\xa9\x3b\x45\xfb\xad\xaa\x84\x69\xff\x8e\x6e": if rp_id_hash == b"\x68\x20\x19\x15\xd7\x4c\xb4\x2a\xf5\xb3\xcc\x5c\x95\xb9\x55\x3e\x3e\x3a\x83\xb4\xd2\xa9\x3b\x45\xfb\xad\xaa\x84\x69\xff\x8e\x6e":
# U2F key for Dashlane # U2F key for Dashlane
return FIDOApp( return FIDOApp(
label="www.dashlane.com", "www.dashlane.com", # label
icon="apps/webauthn/res/icon_dashlane.toif", "apps/webauthn/res/icon_dashlane.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xc5\x0f\x8a\x7b\x70\x8e\x92\xf8\x2e\x7a\x50\xe2\xbd\xc5\x5d\x8f\xd9\x1a\x22\xfe\x6b\x29\xc0\xcd\xf7\x80\x55\x30\x84\x2a\xf5\x81": if rp_id_hash == b"\xc5\x0f\x8a\x7b\x70\x8e\x92\xf8\x2e\x7a\x50\xe2\xbd\xc5\x5d\x8f\xd9\x1a\x22\xfe\x6b\x29\xc0\xcd\xf7\x80\x55\x30\x84\x2a\xf5\x81":
# U2F key for Dropbox # U2F key for Dropbox
return FIDOApp( return FIDOApp(
label="www.dropbox.com", "www.dropbox.com", # label
icon="apps/webauthn/res/icon_dropbox.toif", "apps/webauthn/res/icon_dropbox.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x82\xf4\xa8\xc9\x5f\xec\x94\xb2\x6b\xaf\x9e\x37\x25\x0e\x95\x63\xd9\xa3\x66\xc7\xbe\x26\x1c\xa4\xdd\x01\x01\xf4\xd5\xef\xcb\x83": if rp_id_hash == b"\x82\xf4\xa8\xc9\x5f\xec\x94\xb2\x6b\xaf\x9e\x37\x25\x0e\x95\x63\xd9\xa3\x66\xc7\xbe\x26\x1c\xa4\xdd\x01\x01\xf4\xd5\xef\xcb\x83":
# WebAuthn key for Dropbox # WebAuthn key for Dropbox
return FIDOApp( return FIDOApp(
label="www.dropbox.com", "www.dropbox.com", # label
icon="apps/webauthn/res/icon_dropbox.toif", "apps/webauthn/res/icon_dropbox.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xf3\xe2\x04\x2f\x94\x60\x7d\xa0\xa9\xc1\xf3\xb9\x5e\x0d\x2f\x2b\xb2\xe0\x69\xc5\xbb\x4f\xa7\x64\xaf\xfa\x64\x7d\x84\x7b\x7e\xd6": if rp_id_hash == b"\xf3\xe2\x04\x2f\x94\x60\x7d\xa0\xa9\xc1\xf3\xb9\x5e\x0d\x2f\x2b\xb2\xe0\x69\xc5\xbb\x4f\xa7\x64\xaf\xfa\x64\x7d\x84\x7b\x7e\xd6":
# U2F key for Duo # U2F key for Duo
return FIDOApp( return FIDOApp(
label="duosecurity.com", "duosecurity.com", # label
icon="apps/webauthn/res/icon_duo.toif", "apps/webauthn/res/icon_duo.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x31\x19\x33\x28\xf8\xe2\x1d\xfb\x6c\x99\xf3\x22\xd2\x2d\x7b\x0b\x50\x87\x78\xe6\x4f\xfb\xba\x86\xe5\x22\x93\x37\x90\x31\xb8\x74": if rp_id_hash == b"\x31\x19\x33\x28\xf8\xe2\x1d\xfb\x6c\x99\xf3\x22\xd2\x2d\x7b\x0b\x50\x87\x78\xe6\x4f\xfb\xba\x86\xe5\x22\x93\x37\x90\x31\xb8\x74":
# WebAuthn key for Facebook # WebAuthn key for Facebook
return FIDOApp( return FIDOApp(
label="facebook.com", "facebook.com", # label
icon="apps/webauthn/res/icon_facebook.toif", "apps/webauthn/res/icon_facebook.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x69\x66\xab\xe3\x67\x4e\xa2\xf5\x30\x79\xeb\x71\x01\x97\x84\x8c\x9b\xe6\xf3\x63\x99\x2f\xd0\x29\xe9\x89\x84\x47\xcb\x9f\x00\x84": if rp_id_hash == b"\x69\x66\xab\xe3\x67\x4e\xa2\xf5\x30\x79\xeb\x71\x01\x97\x84\x8c\x9b\xe6\xf3\x63\x99\x2f\xd0\x29\xe9\x89\x84\x47\xcb\x9f\x00\x84":
# U2F key for FastMail # U2F key for FastMail
return FIDOApp( return FIDOApp(
label="www.fastmail.com", "www.fastmail.com", # label
icon="apps/webauthn/res/icon_fastmail.toif", "apps/webauthn/res/icon_fastmail.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x3f\xcb\x82\x82\xb8\x46\x76\xeb\xee\x71\x40\xe3\x9e\xca\xe1\x6e\xeb\x19\x90\x64\xc7\xc7\xe4\x43\x2e\x28\xc9\xb5\x7e\x4b\x60\x39": if rp_id_hash == b"\x3f\xcb\x82\x82\xb8\x46\x76\xeb\xee\x71\x40\xe3\x9e\xca\xe1\x6e\xeb\x19\x90\x64\xc7\xc7\xe4\x43\x2e\x28\xc9\xb5\x7e\x4b\x60\x39":
# WebAuthn key for FastMail # WebAuthn key for FastMail
return FIDOApp( return FIDOApp(
label="fastmail.com", "fastmail.com", # label
icon="apps/webauthn/res/icon_fastmail.toif", "apps/webauthn/res/icon_fastmail.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x9d\x61\x44\x2f\x5c\xe1\x33\xbd\x46\x54\x4f\xc4\x2f\x0a\x6d\x54\xc0\xde\xb8\x88\x40\xca\xc2\xb6\xae\xfa\x65\x14\xf8\x93\x49\xe9": if rp_id_hash == b"\x9d\x61\x44\x2f\x5c\xe1\x33\xbd\x46\x54\x4f\xc4\x2f\x0a\x6d\x54\xc0\xde\xb8\x88\x40\xca\xc2\xb6\xae\xfa\x65\x14\xf8\x93\x49\xe9":
# U2F key for Fedora # U2F key for Fedora
return FIDOApp( return FIDOApp(
label="fedoraproject.org", "fedoraproject.org", # label
icon="apps/webauthn/res/icon_fedora.toif", "apps/webauthn/res/icon_fedora.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xa4\xe2\x2d\xca\xfe\xa7\xe9\x0e\x12\x89\x50\x11\x39\x89\xfc\x45\x97\x8d\xc9\xfb\x87\x76\x75\x60\x51\x6c\x1c\x69\xdf\xdf\xd1\x96": if rp_id_hash == b"\xa4\xe2\x2d\xca\xfe\xa7\xe9\x0e\x12\x89\x50\x11\x39\x89\xfc\x45\x97\x8d\xc9\xfb\x87\x76\x75\x60\x51\x6c\x1c\x69\xdf\xdf\xd1\x96":
# U2F key for Gandi # U2F key for Gandi
return FIDOApp( return FIDOApp(
label="gandi.net", "gandi.net", # label
icon="apps/webauthn/res/icon_gandi.toif", "apps/webauthn/res/icon_gandi.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x54\xce\x65\x1e\xd7\x15\xb4\xaa\xa7\x55\xee\xce\xbd\x4e\xa0\x95\x08\x15\xb3\x34\xbd\x07\xd1\x09\x89\x3e\x96\x30\x18\xcd\xdb\xd9": if rp_id_hash == b"\x54\xce\x65\x1e\xd7\x15\xb4\xaa\xa7\x55\xee\xce\xbd\x4e\xa0\x95\x08\x15\xb3\x34\xbd\x07\xd1\x09\x89\x3e\x96\x30\x18\xcd\xdb\xd9":
# WebAuthn key for Gandi # WebAuthn key for Gandi
return FIDOApp( return FIDOApp(
label="gandi.net", "gandi.net", # label
icon="apps/webauthn/res/icon_gandi.toif", "apps/webauthn/res/icon_gandi.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x86\x06\xc1\x68\xe5\x1f\xc1\x31\xe5\x46\xad\x57\xa1\x9f\x32\x97\xb1\x1e\x0e\x5c\xe8\x3e\x8e\x89\x31\xb2\x85\x08\x11\xcf\xa8\x81": if rp_id_hash == b"\x86\x06\xc1\x68\xe5\x1f\xc1\x31\xe5\x46\xad\x57\xa1\x9f\x32\x97\xb1\x1e\x0e\x5c\xe8\x3e\x8e\x89\x31\xb2\x85\x08\x11\xcf\xa8\x81":
# WebAuthn key for Gemini # WebAuthn key for Gemini
return FIDOApp( return FIDOApp(
label="gemini.com", "gemini.com", # label
icon="apps/webauthn/res/icon_gemini.toif", "apps/webauthn/res/icon_gemini.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=True, True, # use_self_attestation
) )
if rp_id_hash == b"\x70\x61\x7d\xfe\xd0\x65\x86\x3a\xf4\x7c\x15\x55\x6c\x91\x79\x88\x80\x82\x8c\xc4\x07\xfd\xf7\x0a\xe8\x50\x11\x56\x94\x65\xa0\x75": if rp_id_hash == b"\x70\x61\x7d\xfe\xd0\x65\x86\x3a\xf4\x7c\x15\x55\x6c\x91\x79\x88\x80\x82\x8c\xc4\x07\xfd\xf7\x0a\xe8\x50\x11\x56\x94\x65\xa0\x75":
# U2F key for GitHub # U2F key for GitHub
return FIDOApp( return FIDOApp(
label="github.com", "github.com", # label
icon="apps/webauthn/res/icon_github.toif", "apps/webauthn/res/icon_github.toif", # icon
use_sign_count=True, True, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x3a\xeb\x00\x24\x60\x38\x1c\x6f\x25\x8e\x83\x95\xd3\x02\x6f\x57\x1f\x0d\x9a\x76\x48\x8d\xcd\x83\x76\x39\xb1\x3a\xed\x31\x65\x60": if rp_id_hash == b"\x3a\xeb\x00\x24\x60\x38\x1c\x6f\x25\x8e\x83\x95\xd3\x02\x6f\x57\x1f\x0d\x9a\x76\x48\x8d\xcd\x83\x76\x39\xb1\x3a\xed\x31\x65\x60":
# WebAuthn key for GitHub # WebAuthn key for GitHub
return FIDOApp( return FIDOApp(
label="github.com", "github.com", # label
icon="apps/webauthn/res/icon_github.toif", "apps/webauthn/res/icon_github.toif", # icon
use_sign_count=True, True, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xe7\xbe\x96\xa5\x1b\xd0\x19\x2a\x72\x84\x0d\x2e\x59\x09\xf7\x2b\xa8\x2a\x2f\xe9\x3f\xaa\x62\x4f\x03\x39\x6b\x30\xe4\x94\xc8\x04": if rp_id_hash == b"\xe7\xbe\x96\xa5\x1b\xd0\x19\x2a\x72\x84\x0d\x2e\x59\x09\xf7\x2b\xa8\x2a\x2f\xe9\x3f\xaa\x62\x4f\x03\x39\x6b\x30\xe4\x94\xc8\x04":
# U2F key for GitLab # U2F key for GitLab
return FIDOApp( return FIDOApp(
label="gitlab.com", "gitlab.com", # label
icon="apps/webauthn/res/icon_gitlab.toif", "apps/webauthn/res/icon_gitlab.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xa5\x46\x72\xb2\x22\xc4\xcf\x95\xe1\x51\xed\x8d\x4d\x3c\x76\x7a\x6c\xc3\x49\x43\x59\x43\x79\x4e\x88\x4f\x3d\x02\x3a\x82\x29\xfd": if rp_id_hash == b"\xa5\x46\x72\xb2\x22\xc4\xcf\x95\xe1\x51\xed\x8d\x4d\x3c\x76\x7a\x6c\xc3\x49\x43\x59\x43\x79\x4e\x88\x4f\x3d\x02\x3a\x82\x29\xfd":
# U2F key for Google # U2F key for Google
return FIDOApp( return FIDOApp(
label="google.com", "google.com", # label
icon="apps/webauthn/res/icon_google.toif", "apps/webauthn/res/icon_google.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xd4\xc9\xd9\x02\x73\x26\x27\x1a\x89\xce\x51\xfc\xaf\x32\x8e\xd6\x73\xf1\x7b\xe3\x34\x69\xff\x97\x9e\x8a\xb8\xdd\x50\x1e\x66\x4f": if rp_id_hash == b"\xd4\xc9\xd9\x02\x73\x26\x27\x1a\x89\xce\x51\xfc\xaf\x32\x8e\xd6\x73\xf1\x7b\xe3\x34\x69\xff\x97\x9e\x8a\xb8\xdd\x50\x1e\x66\x4f":
# WebAuthn key for Google # WebAuthn key for Google
return FIDOApp( return FIDOApp(
label="google.com", "google.com", # label
icon="apps/webauthn/res/icon_google.toif", "apps/webauthn/res/icon_google.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x9c\x2e\x02\xc4\xff\xf7\x76\x62\xe1\xde\x80\x3b\x43\x9e\x11\xc0\xdd\x0c\x3f\x66\x42\xce\xc4\xe6\x84\xd6\x49\x87\x0a\xd1\xbb\x59": if rp_id_hash == b"\x9c\x2e\x02\xc4\xff\xf7\x76\x62\xe1\xde\x80\x3b\x43\x9e\x11\xc0\xdd\x0c\x3f\x66\x42\xce\xc4\xe6\x84\xd6\x49\x87\x0a\xd1\xbb\x59":
# WebAuthn key for Invity # WebAuthn key for Invity
return FIDOApp( return FIDOApp(
label="invity.io", "invity.io", # label
icon="apps/webauthn/res/icon_invity.toif", "apps/webauthn/res/icon_invity.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x53\xa1\x5b\xa4\x2a\x7c\x03\x25\xb8\xdb\xee\x28\x96\x34\xa4\x8f\x58\xae\xa3\x24\x66\x45\xd5\xff\x41\x8f\x9b\xb8\x81\x98\x85\xa9": if rp_id_hash == b"\x53\xa1\x5b\xa4\x2a\x7c\x03\x25\xb8\xdb\xee\x28\x96\x34\xa4\x8f\x58\xae\xa3\x24\x66\x45\xd5\xff\x41\x8f\x9b\xb8\x81\x98\x85\xa9":
# U2F key for Keeper # U2F key for Keeper
return FIDOApp( return FIDOApp(
label="keepersecurity.com", "keepersecurity.com", # label
icon="apps/webauthn/res/icon_keeper.toif", "apps/webauthn/res/icon_keeper.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xd6\x5f\x00\x5e\xf4\xde\xa9\x32\x0c\x99\x73\x05\x3c\x95\xff\x60\x20\x11\x5d\x5f\xec\x1b\x7f\xee\x41\xa5\x78\xe1\x8d\xf9\xca\x8c": if rp_id_hash == b"\xd6\x5f\x00\x5e\xf4\xde\xa9\x32\x0c\x99\x73\x05\x3c\x95\xff\x60\x20\x11\x5d\x5f\xec\x1b\x7f\xee\x41\xa5\x78\xe1\x8d\xf9\xca\x8c":
# U2F key for Keeper # U2F key for Keeper
return FIDOApp( return FIDOApp(
label="keepersecurity.eu", "keepersecurity.eu", # label
icon="apps/webauthn/res/icon_keeper.toif", "apps/webauthn/res/icon_keeper.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x3f\x37\x50\x85\x33\x2c\xac\x4f\xad\xf9\xe5\xdd\x28\xcd\x54\x69\x8f\xab\x98\x4b\x75\xd9\xc3\x6a\x07\x2c\xb1\x60\x77\x3f\x91\x52": if rp_id_hash == b"\x3f\x37\x50\x85\x33\x2c\xac\x4f\xad\xf9\xe5\xdd\x28\xcd\x54\x69\x8f\xab\x98\x4b\x75\xd9\xc3\x6a\x07\x2c\xb1\x60\x77\x3f\x91\x52":
# WebAuthn key for Kraken # WebAuthn key for Kraken
return FIDOApp( return FIDOApp(
label="kraken.com", "kraken.com", # label
icon="apps/webauthn/res/icon_kraken.toif", "apps/webauthn/res/icon_kraken.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xf8\x3f\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbb\x76\xf6\xb2\xf5\x60\x60\x17\x66\x72\x68\xe5\xb9\xc4\x5e": if rp_id_hash == b"\xf8\x3f\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbb\x76\xf6\xb2\xf5\x60\x60\x17\x66\x72\x68\xe5\xb9\xc4\x5e":
# WebAuthn key for login.gov # WebAuthn key for login.gov
return FIDOApp( return FIDOApp(
label="secure.login.gov", "secure.login.gov", # label
icon="apps/webauthn/res/icon_login.gov.toif", "apps/webauthn/res/icon_login.gov.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x35\x6c\x9e\xd4\xa0\x93\x21\xb9\x69\x5f\x1e\xaf\x91\x82\x03\xf1\xb5\x5f\x68\x9d\xa6\x1f\xbc\x96\x18\x4c\x15\x7d\xda\x68\x0c\x81": if rp_id_hash == b"\x35\x6c\x9e\xd4\xa0\x93\x21\xb9\x69\x5f\x1e\xaf\x91\x82\x03\xf1\xb5\x5f\x68\x9d\xa6\x1f\xbc\x96\x18\x4c\x15\x7d\xda\x68\x0c\x81":
# WebAuthn key for Microsoft # WebAuthn key for Microsoft
return FIDOApp( return FIDOApp(
label="login.microsoft.com", "login.microsoft.com", # label
icon="apps/webauthn/res/icon_microsoft.toif", "apps/webauthn/res/icon_microsoft.toif", # icon
use_sign_count=False, False, # use_sign_count
use_self_attestation=False, False, # use_self_attestation
) )
if rp_id_hash == b"\xab\x2d\xaf\x07\x43\xde\x78\x2a\x70\x18\x9a\x0f\x5e\xfc\x30\x90\x2f\x92\x5b\x9f\x9a\x18\xc5\xd7\x14\x1b\x7b\x12\xf8\xa0\x10\x0c": if rp_id_hash == b"\xab\x2d\xaf\x07\x43\xde\x78\x2a\x70\x18\x9a\x0f\x5e\xfc\x30\x90\x2f\x92\x5b\x9f\x9a\x18\xc5\xd7\x14\x1b\x7b\x12\xf8\xa0\x10\x0c":
# WebAuthn key for mojeID # WebAuthn key for mojeID
return FIDOApp( return FIDOApp(
label="mojeid.cz", "mojeid.cz", # label
icon="apps/webauthn/res/icon_mojeid.toif", "apps/webauthn/res/icon_mojeid.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x85\x71\x01\x36\x1b\x20\xa9\x54\x4c\xdb\x9b\xef\x65\x85\x8b\x6b\xac\x70\x13\x55\x0d\x8f\x84\xf7\xef\xee\x25\x2b\x96\xfa\x7c\x1e": if rp_id_hash == b"\x85\x71\x01\x36\x1b\x20\xa9\x54\x4c\xdb\x9b\xef\x65\x85\x8b\x6b\xac\x70\x13\x55\x0d\x8f\x84\xf7\xef\xee\x25\x2b\x96\xfa\x7c\x1e":
# WebAuthn key for Namecheap # WebAuthn key for Namecheap
return FIDOApp( return FIDOApp(
label="www.namecheap.com", "www.namecheap.com", # label
icon="apps/webauthn/res/icon_namecheap.toif", "apps/webauthn/res/icon_namecheap.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xa2\x59\xc2\xb5\x0d\x78\x50\x80\xf8\xbe\x7f\x17\xca\xf8\x15\x6c\x8d\x18\xf4\x7e\xdb\xaf\x51\x8f\xa6\xf5\x9f\x29\xcd\x28\xf1\x5c": if rp_id_hash == b"\xa2\x59\xc2\xb5\x0d\x78\x50\x80\xf8\xbe\x7f\x17\xca\xf8\x15\x6c\x8d\x18\xf4\x7e\xdb\xaf\x51\x8f\xa6\xf5\x9f\x29\xcd\x28\xf1\x5c":
# WebAuthn key for Proton # WebAuthn key for Proton
return FIDOApp( return FIDOApp(
label="proton.me", "proton.me", # label
icon="apps/webauthn/res/icon_proton.toif", "apps/webauthn/res/icon_proton.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x08\xb2\xa3\xd4\x19\x39\xaa\x31\x66\x84\x93\xcb\x36\xcd\xcc\x4f\x16\xc4\xd9\xb4\xc8\x23\x8b\x73\xc2\xf6\x72\xc0\x33\x00\x71\x97": if rp_id_hash == b"\x08\xb2\xa3\xd4\x19\x39\xaa\x31\x66\x84\x93\xcb\x36\xcd\xcc\x4f\x16\xc4\xd9\xb4\xc8\x23\x8b\x73\xc2\xf6\x72\xc0\x33\x00\x71\x97":
# U2F key for Slush Pool # U2F key for Slush Pool
return FIDOApp( return FIDOApp(
label="slushpool.com", "slushpool.com", # label
icon="apps/webauthn/res/icon_slushpool.toif", "apps/webauthn/res/icon_slushpool.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x38\x80\x4f\x2e\xff\x74\xf2\x28\xb7\x41\x51\xc2\x01\xaa\x82\xe7\xe8\xee\xfc\xac\xfe\xcf\x23\xfa\x14\x6b\x13\xa3\x76\x66\x31\x4f": if rp_id_hash == b"\x38\x80\x4f\x2e\xff\x74\xf2\x28\xb7\x41\x51\xc2\x01\xaa\x82\xe7\xe8\xee\xfc\xac\xfe\xcf\x23\xfa\x14\x6b\x13\xa3\x76\x66\x31\x4f":
# U2F key for Slush Pool # U2F key for Slush Pool
return FIDOApp( return FIDOApp(
label="slushpool.com", "slushpool.com", # label
icon="apps/webauthn/res/icon_slushpool.toif", "apps/webauthn/res/icon_slushpool.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x2a\xc6\xad\x09\xa6\xd0\x77\x2c\x44\xda\x73\xa6\x07\x2f\x9d\x24\x0f\xc6\x85\x4a\x70\xd7\x9c\x10\x24\xff\x7c\x75\x59\x59\x32\x92": if rp_id_hash == b"\x2a\xc6\xad\x09\xa6\xd0\x77\x2c\x44\xda\x73\xa6\x07\x2f\x9d\x24\x0f\xc6\x85\x4a\x70\xd7\x9c\x10\x24\xff\x7c\x75\x59\x59\x32\x92":
# U2F key for Stripe # U2F key for Stripe
return FIDOApp( return FIDOApp(
label="stripe.com", "stripe.com", # label
icon="apps/webauthn/res/icon_stripe.toif", "apps/webauthn/res/icon_stripe.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xfa\xbe\xec\xe3\x98\x2f\xad\x9d\xdc\xc9\x8f\x91\xbd\x2e\x75\xaf\xc7\xd1\xf4\xca\x54\x49\x29\xb2\xd0\xd0\x42\x12\xdf\xfa\x30\xfa": if rp_id_hash == b"\xfa\xbe\xec\xe3\x98\x2f\xad\x9d\xdc\xc9\x8f\x91\xbd\x2e\x75\xaf\xc7\xd1\xf4\xca\x54\x49\x29\xb2\xd0\xd0\x42\x12\xdf\xfa\x30\xfa":
# U2F key for Tutanota # U2F key for Tutanota
return FIDOApp( return FIDOApp(
label="tutanota.com", "tutanota.com", # label
icon="apps/webauthn/res/icon_tutanota.toif", "apps/webauthn/res/icon_tutanota.toif", # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x1b\x3c\x16\xdd\x2f\x7c\x46\xe2\xb4\xc2\x89\xdc\x16\x74\x6b\xcc\x60\xdf\xcf\x0f\xb8\x18\xe1\x32\x15\x52\x6e\x14\x08\xe7\xf4\x68": if rp_id_hash == b"\x1b\x3c\x16\xdd\x2f\x7c\x46\xe2\xb4\xc2\x89\xdc\x16\x74\x6b\xcc\x60\xdf\xcf\x0f\xb8\x18\xe1\x32\x15\x52\x6e\x14\x08\xe7\xf4\x68":
# U2F key for u2f.bin.coffee # U2F key for u2f.bin.coffee
return FIDOApp( return FIDOApp(
label="u2f.bin.coffee", "u2f.bin.coffee", # label
icon=None, None, # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xa6\x42\xd2\x1b\x7c\x6d\x55\xe1\xce\x23\xc5\x39\x98\x28\xd2\xc7\x49\xbf\x6a\x6e\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xed\x53\x08\x8b": if rp_id_hash == b"\xa6\x42\xd2\x1b\x7c\x6d\x55\xe1\xce\x23\xc5\x39\x98\x28\xd2\xc7\x49\xbf\x6a\x6e\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xed\x53\x08\x8b":
# WebAuthn key for webauthn.bin.coffee # WebAuthn key for webauthn.bin.coffee
return FIDOApp( return FIDOApp(
label="webauthn.bin.coffee", "webauthn.bin.coffee", # label
icon=None, None, # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\x74\xa6\xea\x92\x13\xc9\x9c\x2f\x74\xb2\x24\x92\xb3\x20\xcf\x40\x26\x2a\x94\xc1\xa9\x50\xa0\x39\x7f\x29\x25\x0b\x60\x84\x1e\xf0": if rp_id_hash == b"\x74\xa6\xea\x92\x13\xc9\x9c\x2f\x74\xb2\x24\x92\xb3\x20\xcf\x40\x26\x2a\x94\xc1\xa9\x50\xa0\x39\x7f\x29\x25\x0b\x60\x84\x1e\xf0":
# WebAuthn key for WebAuthn.io # WebAuthn key for WebAuthn.io
return FIDOApp( return FIDOApp(
label="webauthn.io", "webauthn.io", # label
icon=None, None, # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xf9\x5b\xc7\x38\x28\xee\x21\x0f\x9f\xd3\xbb\xe7\x2d\x97\x90\x80\x13\xb0\xa3\x75\x9e\x9a\xea\x3d\x0a\xe3\x18\x76\x6c\xd2\xe1\xad": if rp_id_hash == b"\xf9\x5b\xc7\x38\x28\xee\x21\x0f\x9f\xd3\xbb\xe7\x2d\x97\x90\x80\x13\xb0\xa3\x75\x9e\x9a\xea\x3d\x0a\xe3\x18\x76\x6c\xd2\xe1\xad":
# WebAuthn key for WebAuthn.me # WebAuthn key for WebAuthn.me
return FIDOApp( return FIDOApp(
label="webauthn.me", "webauthn.me", # label
icon=None, None, # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
if rp_id_hash == b"\xc4\x6c\xef\x82\xad\x1b\x54\x64\x77\x59\x1d\x00\x8b\x08\x75\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94\x74\xbf\xea\x69\x69\x92\x5d\x03\xb7": if rp_id_hash == b"\xc4\x6c\xef\x82\xad\x1b\x54\x64\x77\x59\x1d\x00\x8b\x08\x75\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94\x74\xbf\xea\x69\x69\x92\x5d\x03\xb7":
# WebAuthn key for demo.yubico.com # WebAuthn key for demo.yubico.com
return FIDOApp( return FIDOApp(
label="demo.yubico.com", "demo.yubico.com", # label
icon=None, None, # icon
use_sign_count=None, None, # use_sign_count
use_self_attestation=None, None, # use_self_attestation
) )
return None return None

View File

@ -2,6 +2,8 @@
# (by running `make templates` in `core`) # (by running `make templates` in `core`)
# do not edit manually! # do not edit manually!
# NOTE: using positional arguments saves 520 bytes in flash space
class FIDOApp: class FIDOApp:
def __init__( def __init__(
@ -38,10 +40,10 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None:
if rp_id_hash == ${black_repr(rp_id_hash)}: if rp_id_hash == ${black_repr(rp_id_hash)}:
# ${type} key for ${app.name} # ${type} key for ${app.name}
return FIDOApp( return FIDOApp(
label=${black_repr(label)}, ${black_repr(label)}, # label
icon=${black_repr(app.icon_res)}, ${black_repr(app.icon_res)}, # icon
use_sign_count=${black_repr(app.use_sign_count)}, ${black_repr(app.use_sign_count)}, # use_sign_count
use_self_attestation=${black_repr(app.use_self_attestation)}, ${black_repr(app.use_self_attestation)}, # use_self_attestation
) )
% endfor % endfor

View File

@ -1,22 +1,22 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from trezor import wire if TYPE_CHECKING:
from trezor.messages import WebAuthnListResidentCredentials, WebAuthnCredentials
from trezor.wire import Context
async def list_resident_credentials(
ctx: Context, msg: WebAuthnListResidentCredentials
) -> WebAuthnCredentials:
from trezor.messages import WebAuthnCredential, WebAuthnCredentials from trezor.messages import WebAuthnCredential, WebAuthnCredentials
from trezor.ui.layouts import confirm_action from trezor.ui.layouts import confirm_action
from . import resident_credentials from . import resident_credentials
if TYPE_CHECKING:
from trezor.messages import WebAuthnListResidentCredentials
async def list_resident_credentials(
ctx: wire.Context, msg: WebAuthnListResidentCredentials
) -> WebAuthnCredentials:
await confirm_action( await confirm_action(
ctx, ctx,
"credentials_list", "credentials_list",
title="List credentials", "List credentials",
description="Do you want to export information about the resident credentials stored on this device?", description="Do you want to export information about the resident credentials stored on this device?",
) )
creds = [ creds = [

View File

@ -1,17 +1,11 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import storage.device
import storage.resident_credentials
from trezor import wire
from trezor.messages import Success
from trezor.ui.components.common.webauthn import ConfirmInfo from trezor.ui.components.common.webauthn import ConfirmInfo
from trezor.ui.layouts.webauthn import confirm_webauthn
from .credential import Fido2Credential
from .resident_credentials import get_resident_credential
if TYPE_CHECKING: if TYPE_CHECKING:
from trezor.messages import WebAuthnRemoveResidentCredential from trezor.messages import WebAuthnRemoveResidentCredential, Success
from .credential import Fido2Credential
from trezor.wire import Context
class ConfirmRemoveCredential(ConfirmInfo): class ConfirmRemoveCredential(ConfirmInfo):
@ -31,8 +25,15 @@ class ConfirmRemoveCredential(ConfirmInfo):
async def remove_resident_credential( async def remove_resident_credential(
ctx: wire.Context, msg: WebAuthnRemoveResidentCredential ctx: Context, msg: WebAuthnRemoveResidentCredential
) -> Success: ) -> Success:
import storage.device
import storage.resident_credentials
from trezor import wire
from trezor.messages import Success
from trezor.ui.layouts.webauthn import confirm_webauthn
from .resident_credentials import get_resident_credential
if not storage.device.is_initialized(): if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized") raise wire.NotInitialized("Device is not initialized")
if msg.index is None: if msg.index is None:

View File

@ -1,17 +1,22 @@
from micropython import const from micropython import const
from typing import Iterator from typing import TYPE_CHECKING
import storage.resident_credentials import storage.resident_credentials as storage_resident_credentials
from storage.resident_credentials import MAX_RESIDENT_CREDENTIALS from storage.resident_credentials import MAX_RESIDENT_CREDENTIALS
if TYPE_CHECKING:
from typing import Iterator
from .credential import Fido2Credential from .credential import Fido2Credential
RP_ID_HASH_LENGTH = const(32)
_RP_ID_HASH_LENGTH = const(32)
def _credential_from_data(index: int, data: bytes) -> Fido2Credential: def _credential_from_data(index: int, data: bytes) -> Fido2Credential:
rp_id_hash = data[:RP_ID_HASH_LENGTH] from .credential import Fido2Credential
cred_id = data[RP_ID_HASH_LENGTH:]
rp_id_hash = data[:_RP_ID_HASH_LENGTH]
cred_id = data[_RP_ID_HASH_LENGTH:]
cred = Fido2Credential.from_cred_id(cred_id, rp_id_hash) cred = Fido2Credential.from_cred_id(cred_id, rp_id_hash)
cred.index = index cred.index = index
return cred return cred
@ -19,20 +24,20 @@ def _credential_from_data(index: int, data: bytes) -> Fido2Credential:
def find_all() -> Iterator[Fido2Credential]: def find_all() -> Iterator[Fido2Credential]:
for index in range(MAX_RESIDENT_CREDENTIALS): for index in range(MAX_RESIDENT_CREDENTIALS):
data = storage.resident_credentials.get(index) data = storage_resident_credentials.get(index)
if data is not None: if data is not None:
yield _credential_from_data(index, data) yield _credential_from_data(index, data)
def find_by_rp_id_hash(rp_id_hash: bytes) -> Iterator[Fido2Credential]: def find_by_rp_id_hash(rp_id_hash: bytes) -> Iterator[Fido2Credential]:
for index in range(MAX_RESIDENT_CREDENTIALS): for index in range(MAX_RESIDENT_CREDENTIALS):
data = storage.resident_credentials.get(index) data = storage_resident_credentials.get(index)
if data is None: if data is None:
# empty slot # empty slot
continue continue
if data[:RP_ID_HASH_LENGTH] != rp_id_hash: if data[:_RP_ID_HASH_LENGTH] != rp_id_hash:
# rp_id_hash mismatch # rp_id_hash mismatch
continue continue
@ -43,7 +48,7 @@ def get_resident_credential(index: int) -> Fido2Credential | None:
if not 0 <= index < MAX_RESIDENT_CREDENTIALS: if not 0 <= index < MAX_RESIDENT_CREDENTIALS:
return None return None
data = storage.resident_credentials.get(index) data = storage_resident_credentials.get(index)
if data is None: if data is None:
return None return None
@ -53,14 +58,14 @@ def get_resident_credential(index: int) -> Fido2Credential | None:
def store_resident_credential(cred: Fido2Credential) -> bool: def store_resident_credential(cred: Fido2Credential) -> bool:
slot = None slot = None
for index in range(MAX_RESIDENT_CREDENTIALS): for index in range(MAX_RESIDENT_CREDENTIALS):
stored_data = storage.resident_credentials.get(index) stored_data = storage_resident_credentials.get(index)
if stored_data is None: if stored_data is None:
# found candidate empty slot # found candidate empty slot
if slot is None: if slot is None:
slot = index slot = index
continue continue
if cred.rp_id_hash != stored_data[:RP_ID_HASH_LENGTH]: if cred.rp_id_hash != stored_data[:_RP_ID_HASH_LENGTH]:
# slot is occupied by a different rp_id_hash # slot is occupied by a different rp_id_hash
continue continue
@ -74,5 +79,5 @@ def store_resident_credential(cred: Fido2Credential) -> bool:
return False return False
cred_data = cred.rp_id_hash + cred.id cred_data = cred.rp_id_hash + cred.id
storage.resident_credentials.set(slot, cred_data) storage_resident_credentials.set(slot, cred_data)
return True return True

View File

@ -2,9 +2,8 @@ from common import *
import storage import storage
import storage.device import storage.device
from apps.common import mnemonic from apps.common import mnemonic
from apps.webauthn.credential import Fido2Credential, U2fCredential, NAME_MAX_LENGTH from apps.webauthn.credential import Fido2Credential, U2fCredential, _NAME_MAX_LENGTH
from apps.webauthn.fido2 import distinguishable_cred_list from apps.webauthn.fido2 import _distinguishable_cred_list
from trezor.crypto.curve import nist256p1
from trezor.crypto.hashlib import sha256 from trezor.crypto.hashlib import sha256
@ -68,13 +67,13 @@ class TestCredential(unittest.TestCase):
self.assertIsNone(cred.user_name) self.assertIsNone(cred.user_name)
self.assertIsNone(cred.user_display_name) self.assertIsNone(cred.user_display_name)
cred.rp_name = "a" * (NAME_MAX_LENGTH - 2) + "\u0123" cred.rp_name = "a" * (_NAME_MAX_LENGTH - 2) + "\u0123"
cred.user_name = "a" * (NAME_MAX_LENGTH - 1) + "\u0123" cred.user_name = "a" * (_NAME_MAX_LENGTH - 1) + "\u0123"
cred.user_display_name = "a" * NAME_MAX_LENGTH + "\u0123" cred.user_display_name = "a" * _NAME_MAX_LENGTH + "\u0123"
cred.truncate_names() cred.truncate_names()
self.assertEqual(cred.rp_name, "a" * (NAME_MAX_LENGTH - 2) + "\u0123") self.assertEqual(cred.rp_name, "a" * (_NAME_MAX_LENGTH - 2) + "\u0123")
self.assertEqual(cred.user_name, "a" * (NAME_MAX_LENGTH - 1)) self.assertEqual(cred.user_name, "a" * (_NAME_MAX_LENGTH - 1))
self.assertEqual(cred.user_display_name, "a" * NAME_MAX_LENGTH) self.assertEqual(cred.user_display_name, "a" * _NAME_MAX_LENGTH)
def test_allow_list_processing(self): def test_allow_list_processing(self):
a1 = Fido2Credential() a1 = Fido2Credential()
@ -108,17 +107,17 @@ class TestCredential(unittest.TestCase):
c2 = U2fCredential() c2 = U2fCredential()
self.assertEqual(sorted(distinguishable_cred_list([a1, a2, a3, b1, b2, c1, c2])), [b2, a3, a1, c1]) self.assertEqual(sorted(_distinguishable_cred_list([a1, a2, a3, b1, b2, c1, c2])), [b2, a3, a1, c1])
self.assertEqual(sorted(distinguishable_cred_list([c2, c1, b2, b1, a3, a2, a1])), [b2, a3, a1, c2]) self.assertEqual(sorted(_distinguishable_cred_list([c2, c1, b2, b1, a3, a2, a1])), [b2, a3, a1, c2])
# Test input by creation time. # Test input by creation time.
self.assertEqual(sorted(distinguishable_cred_list([b2, a3, c1, a2, b1, a1, c2])), [b2, a3, a1, c1]) self.assertEqual(sorted(_distinguishable_cred_list([b2, a3, c1, a2, b1, a1, c2])), [b2, a3, a1, c1])
self.assertEqual(sorted(distinguishable_cred_list([c2, a1, b1, a2, c1, a3, b2])), [b2, a3, a1, c2]) self.assertEqual(sorted(_distinguishable_cred_list([c2, a1, b1, a2, c1, a3, b2])), [b2, a3, a1, c2])
# Test duplicities. # Test duplicities.
self.assertEqual(sorted(distinguishable_cred_list([c1, a1, a1, c2, c1])), [a1, c1]) self.assertEqual(sorted(_distinguishable_cred_list([c1, a1, a1, c2, c1])), [a1, c1])
self.assertEqual(sorted(distinguishable_cred_list([b2, b3])), [b2]) self.assertEqual(sorted(_distinguishable_cred_list([b2, b3])), [b2])
self.assertEqual(sorted(distinguishable_cred_list([b3, b2])), [b3]) self.assertEqual(sorted(_distinguishable_cred_list([b3, b2])), [b3])
if __name__ == '__main__': if __name__ == '__main__':