Files
fido2-tests/tests/utils.py

188 lines
4.8 KiB
Python

import math
import random
import secrets
import sys
from fido2.ctap2 import ES256, AttestedCredentialData, PinProtocolV1
from fido2.utils import hmac_sha256, sha256
if 'trezor' in sys.argv:
from .vendor.trezor.utils import DeviceSelectCredential
else:
from .vendor.solo.utils import DeviceSelectCredential
name_list = open("data/first-names.txt").readlines()
def shannon_entropy(data):
s = 0.0
total = len(data)
for x in range(0, 256):
freq = data.count(x)
p = freq / total
if p > 0:
s -= p * math.log2(p)
return s
def verify(reg, auth, cdh=None):
credential_data = AttestedCredentialData(reg.auth_data.credential_data)
if cdh is None:
cdh = auth.request.cdh
auth.verify(cdh, credential_data.public_key)
assert auth.credential["id"] == reg.auth_data.credential_data.credential_id
def generate_rp():
return {"id": "example.org", "name": "ExampleRP"}
def generate_user():
# https://www.w3.org/TR/webauthn/#user-handle
user_id_length = random.randint(1, 64)
user_id = secrets.token_bytes(user_id_length)
# https://www.w3.org/TR/webauthn/#dictionary-pkcredentialentity
name = " ".join(random.choice(name_list).strip() for i in range(0, 3))
icon = "https://www.w3.org/TR/webauthn/"
display_name = "Displayed " + name
return {"id": user_id, "name": name, "icon": icon, "displayName": display_name}
counter = 1
def generate_user_maximum():
"""
Generate RK with the maximum lengths of the fields, according to the minimal requirements of the FIDO2 spec
"""
global counter
# https://www.w3.org/TR/webauthn/#user-handle
user_id_length = 64
user_id = secrets.token_bytes(user_id_length)
# https://www.w3.org/TR/webauthn/#dictionary-pkcredentialentity
name = " ".join(random.choice(name_list).strip() for i in range(0, 30))
name = f'{counter}: {name}'
icon = "https://www.w3.org/TR/webauthn/" + 'A'*128
display_name = "Displayed " + name
name = name[:64]
display_name = display_name[:64]
icon = icon[:128]
counter += 1
return {"id": user_id, "name": name, "icon": icon, "displayName": display_name}
def generate_challenge():
return secrets.token_bytes(32)
def get_key_params():
return [{"type": "public-key", "alg": ES256.ALGORITHM}]
def generate_cdh():
return b"123456789abcdef0123456789abcdef0"
def generate(param):
if param == "rp":
return generate_rp()
if param == "user":
return generate_user()
if param == "challenge":
return generate_challenge()
if param == "cdh":
return generate_cdh()
if param == "key_params":
return get_key_params()
if param == "allow_list":
return []
if param == "on_keepalive":
return DeviceSelectCredential(1)
return None
class Empty:
pass
class FidoRequest:
def __init__(self, request=None, **kwargs):
if not isinstance(request, FidoRequest) and request is not None:
request = request.request
self.request = request
for i in (
"cdh",
"key_params",
"allow_list",
"challenge",
"rp",
"user",
"pin_protocol",
"options",
"appid",
"exclude_list",
"extensions",
"pin_auth",
"timeout",
"on_keepalive",
):
self.save_attr(i, kwargs.get(i, Empty), request)
if isinstance(self.rp, dict) and "id" in self.rp:
if hasattr(self.rp["id"], "encode"):
self.appid = sha256(self.rp["id"].encode("utf8"))
# self.chal = sha256(self.challenge.encode("utf8"))
def save_attr(self, attr, value, request):
"""
Will assign attribute from source, in following priority:
Argument, request object, generated
"""
if value != Empty:
setattr(self, attr, value)
elif request is not None:
setattr(self, attr, getattr(request, attr))
else:
setattr(self, attr, generate(attr))
def toGA(self,):
return [
None if not self.rp else self.rp["id"],
self.cdh,
self.allow_list,
self.extensions,
self.options,
self.pin_auth,
self.pin_protocol,
self.timeout,
self.on_keepalive,
]
def toMC(self,):
return [
self.cdh,
self.rp,
self.user,
self.key_params,
self.exclude_list,
self.extensions,
self.options,
self.pin_auth,
self.pin_protocol,
self.timeout,
self.on_keepalive,
]
return args + self.get_optional_args()