common: add support for FIDO apps to cointool
also resize icons to the same 128x128 RGBA format as coin logos are using
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 830 B |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.1 KiB |
@ -1,4 +1,5 @@
|
||||
{
|
||||
"label": "u2f.bin.coffee",
|
||||
"u2f": ["1b3c16dd2f7c46e2b4c289dc16746bcc60dfcf0fb818e13215526e1408e7f468"]
|
||||
"u2f": ["1b3c16dd2f7c46e2b4c289dc16746bcc60dfcf0fb818e13215526e1408e7f468"],
|
||||
"demo": true
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"label": "webauthn.bin.coffee",
|
||||
"webauthn": ["webauthn.bin.coffee"]
|
||||
"webauthn": ["webauthn.bin.coffee"],
|
||||
"demo": true
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"label": "WebAuthn.io",
|
||||
"webauthn": ["webauthn.io"]
|
||||
"webauthn": ["webauthn.io"],
|
||||
"demo": true
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"label": "WebAuthn.me",
|
||||
"webauthn": ["webauthn.me"]
|
||||
"webauthn": ["webauthn.me"],
|
||||
"demo": true
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"label": "demo.yubico.com",
|
||||
"webauthn": ["demo.yubico.com"]
|
||||
"webauthn": ["demo.yubico.com"],
|
||||
"demo": true
|
||||
}
|
||||
|
@ -197,9 +197,9 @@ def validate_btc(coin):
|
||||
|
||||
|
||||
def _load_btc_coins():
|
||||
"""Load btc-like coins from `coins/*.json`"""
|
||||
"""Load btc-like coins from `bitcoin/*.json`"""
|
||||
coins = []
|
||||
for filename in glob.glob(os.path.join(DEFS_DIR, "coins", "*.json")):
|
||||
for filename in glob.glob(os.path.join(DEFS_DIR, "bitcoin", "*.json")):
|
||||
coin = load_json(filename)
|
||||
coin.update(
|
||||
name=coin["coin_label"],
|
||||
@ -259,6 +259,20 @@ def _load_misc():
|
||||
return others
|
||||
|
||||
|
||||
def _load_fido_apps():
|
||||
"""Load btc-like coins from `coins/*.json`"""
|
||||
apps = []
|
||||
for filename in glob.glob(os.path.join(DEFS_DIR, "webauthn", "apps", "*.json")):
|
||||
app_name = os.path.basename(filename)[:-5]
|
||||
app = load_json(filename)
|
||||
app.update(
|
||||
key=app_name,
|
||||
)
|
||||
apps.append(app)
|
||||
|
||||
return apps
|
||||
|
||||
|
||||
# ====== support info ======
|
||||
|
||||
RELEASES_URL = "https://beta-wallet.trezor.io/data/firmware/{}/releases.json"
|
||||
@ -559,6 +573,11 @@ def coin_info():
|
||||
return all_coins
|
||||
|
||||
|
||||
def fido_info():
|
||||
"""Returns info about known FIDO/U2F apps."""
|
||||
return _load_fido_apps()
|
||||
|
||||
|
||||
def search(coins, keyword):
|
||||
kwl = keyword.lower()
|
||||
if isinstance(coins, CoinsInfo):
|
||||
|
@ -168,8 +168,11 @@ def find_collisions(coins, field):
|
||||
"""Detects collisions in a given field. Returns buckets of colliding coins."""
|
||||
collisions = defaultdict(list)
|
||||
for coin in coins:
|
||||
value = coin[field]
|
||||
collisions[value].append(coin)
|
||||
values = coin[field]
|
||||
if not isinstance(values, list):
|
||||
values = [values]
|
||||
for value in values:
|
||||
collisions[value].append(coin)
|
||||
return {k: v for k, v in collisions.items() if len(v) > 1}
|
||||
|
||||
|
||||
@ -473,6 +476,64 @@ def check_segwit(coins):
|
||||
return True
|
||||
|
||||
|
||||
FIDO_KNOWN_KEYS = frozenset(
|
||||
("key", "u2f", "webauthn", "label", "use_sign_count", "demo")
|
||||
)
|
||||
|
||||
|
||||
def check_fido(apps):
|
||||
check_passed = True
|
||||
|
||||
uf2_hashes = find_collisions((a for a in apps if "u2f" in a), "u2f")
|
||||
for key, bucket in uf2_hashes.items():
|
||||
bucket_str = ", ".join(app["key"] for app in bucket)
|
||||
u2f_hash_str = "colliding U2F hash " + crayon(None, key, bold=True) + ":"
|
||||
print_log(logging.ERROR, u2f_hash_str, bucket_str)
|
||||
check_passed = False
|
||||
|
||||
webauthn_domains = find_collisions((a for a in apps if "webauthn" in a), "webauthn")
|
||||
for key, bucket in webauthn_domains.items():
|
||||
bucket_str = ", ".join(app["key"] for app in bucket)
|
||||
webauthn_str = "colliding WebAuthn domain " + crayon(None, key, bold=True) + ":"
|
||||
print_log(logging.ERROR, webauthn_str, bucket_str)
|
||||
check_passed = False
|
||||
|
||||
for app in apps:
|
||||
if "label" not in app:
|
||||
print_log(logging.ERROR, app["key"], ": missing label")
|
||||
check_passed = False
|
||||
|
||||
if not app.get("u2f") and not app.get("webauthn"):
|
||||
print_log(logging.ERROR, app["key"], ": no U2F nor WebAuthn addresses")
|
||||
check_passed = False
|
||||
|
||||
unknown_keys = set(app.keys()) - FIDO_KNOWN_KEYS
|
||||
if unknown_keys:
|
||||
print_log(logging.ERROR, app["key"], ": unrecognized keys:", unknown_keys)
|
||||
|
||||
# check icons
|
||||
icon_file = app["key"].lower() + ".png"
|
||||
try:
|
||||
icon = Image.open(
|
||||
os.path.join(coin_info.DEFS_DIR, "webauthn", "apps", icon_file)
|
||||
)
|
||||
except Exception:
|
||||
if app.get("demo"):
|
||||
log_level = logging.WARNING
|
||||
else:
|
||||
log_level = logging.ERROR
|
||||
check_passed = False
|
||||
print_log(log_level, app["key"], ": failed to open icon file", icon_file)
|
||||
continue
|
||||
|
||||
if icon.size != (128, 128) or icon.mode != "RGBA":
|
||||
print_log(
|
||||
logging.ERROR, app["key"], ": bad icon format (must be RGBA 128x128)"
|
||||
)
|
||||
check_passed = False
|
||||
return check_passed
|
||||
|
||||
|
||||
# ====== coindefs generators ======
|
||||
|
||||
|
||||
@ -638,6 +699,10 @@ def check(backend, icons, show_duplicates):
|
||||
if not check_key_uniformity(coinlist):
|
||||
all_checks_passed = False
|
||||
|
||||
print("Checking FIDO app definitions...")
|
||||
if not check_fido(coin_info.fido_info()):
|
||||
all_checks_passed = False
|
||||
|
||||
if not all_checks_passed:
|
||||
print("Some checks failed.")
|
||||
sys.exit(1)
|
||||
@ -674,7 +739,7 @@ def dump(
|
||||
exclude_tokens,
|
||||
device,
|
||||
):
|
||||
"""Dump coin data in JSON format
|
||||
"""Dump coin data in JSON format.
|
||||
|
||||
This file is structured the same as the internal data. That is, top-level object
|
||||
is a dict with keys: 'bitcoin', 'eth', 'erc20', 'nem' and 'misc'. Value for each
|
||||
|