mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-05-07 09:29:04 +00:00
core/webauthn: Disable FIDO2 signature counter for some relying parties
This commit is contained in:
parent
5628d1254d
commit
315a30b42b
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"label": "Binance",
|
"label": "Binance",
|
||||||
"webauthn": "www.binance.com"
|
"webauthn": "www.binance.com",
|
||||||
|
"use_sign_count": false
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
"label": "GitHub",
|
"label": "GitHub",
|
||||||
"u2f": "https://github.com/u2f/trusted_facets"
|
"u2f": "https://github.com/u2f/trusted_facets",
|
||||||
|
"webauthn": "github.com",
|
||||||
|
"use_sign_count": true
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"label": "Microsoft",
|
"label": "Microsoft",
|
||||||
"webauthn": "login.microsoft.com"
|
"webauthn": "login.microsoft.com",
|
||||||
|
"use_sign_count": false
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,22 @@ def gen_core(data):
|
|||||||
for d in data:
|
for d in data:
|
||||||
if "u2f" in d:
|
if "u2f" in d:
|
||||||
url, label = d["u2f"], d["label"]
|
url, label = d["u2f"], d["label"]
|
||||||
print(' "%s": "%s",' % (url, label))
|
print(' "%s": {"label": "%s", "use_sign_count": True},' % (url, label))
|
||||||
print(" # WebAuthn")
|
print(" # WebAuthn")
|
||||||
for d in data:
|
for d in data:
|
||||||
if "webauthn" in d:
|
if "webauthn" in d:
|
||||||
origin, label = d["webauthn"], d["label"]
|
origin, label, use_sign_count = (
|
||||||
print(' "%s": "%s",' % (origin, label))
|
d["webauthn"],
|
||||||
|
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("}")
|
print("}")
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,5 +52,6 @@ message WebAuthnCredentials {
|
|||||||
optional string user_display_name = 7;
|
optional string user_display_name = 7;
|
||||||
optional uint32 creation_time = 8;
|
optional uint32 creation_time = 8;
|
||||||
optional bool hmac_secret = 9;
|
optional bool hmac_secret = 9;
|
||||||
|
optional bool use_sign_count = 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1152,18 +1152,20 @@ def msg_authenticate(req: Msg, dialog_mgr: DialogManager) -> Cmd:
|
|||||||
# sign the authentication challenge and return
|
# sign the authentication challenge and return
|
||||||
if __debug__:
|
if __debug__:
|
||||||
log.info(__name__, "signing authentication")
|
log.info(__name__, "signing authentication")
|
||||||
buf = msg_authenticate_sign(auth.chal, auth.appId, cred.private_key())
|
buf = msg_authenticate_sign(auth.chal, auth.appId, cred)
|
||||||
|
|
||||||
dialog_mgr.reset()
|
dialog_mgr.reset()
|
||||||
|
|
||||||
return Cmd(req.cid, _CMD_MSG, buf)
|
return Cmd(req.cid, _CMD_MSG, buf)
|
||||||
|
|
||||||
|
|
||||||
def msg_authenticate_sign(challenge: bytes, rp_id_hash: bytes, privkey: bytes) -> bytes:
|
def msg_authenticate_sign(
|
||||||
|
challenge: bytes, rp_id_hash: bytes, cred: Credential
|
||||||
|
) -> bytes:
|
||||||
flags = bytes([_AUTH_FLAG_UP])
|
flags = bytes([_AUTH_FLAG_UP])
|
||||||
|
|
||||||
# get next counter
|
# get next counter
|
||||||
ctr = storage.device.next_u2f_counter()
|
ctr = cred.next_signature_counter()
|
||||||
ctrbuf = ustruct.pack(">L", ctr)
|
ctrbuf = ustruct.pack(">L", ctr)
|
||||||
|
|
||||||
# hash input data together with counter
|
# hash input data together with counter
|
||||||
@ -1174,7 +1176,7 @@ def msg_authenticate_sign(challenge: bytes, rp_id_hash: bytes, privkey: bytes) -
|
|||||||
dig.update(challenge) # uint8_t chal[32];
|
dig.update(challenge) # uint8_t chal[32];
|
||||||
|
|
||||||
# sign the digest and convert to der
|
# sign the digest and convert to der
|
||||||
sig = nist256p1.sign(privkey, dig.digest(), False)
|
sig = nist256p1.sign(cred.private_key(), dig.digest(), False)
|
||||||
sig = der.encode_seq((sig[1:33], sig[33:]))
|
sig = der.encode_seq((sig[1:33], sig[33:]))
|
||||||
|
|
||||||
# pack to a response
|
# pack to a response
|
||||||
@ -1206,6 +1208,8 @@ def cbor_error(cid: int, code: int) -> Cmd:
|
|||||||
|
|
||||||
|
|
||||||
def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
||||||
|
from apps.webauthn.knownapps import knownapps
|
||||||
|
|
||||||
if not storage.is_initialized():
|
if not storage.is_initialized():
|
||||||
if __debug__:
|
if __debug__:
|
||||||
log.warning(__name__, "not initialized")
|
log.warning(__name__, "not initialized")
|
||||||
@ -1260,6 +1264,8 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Optional[Cmd]:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return cbor_error(req.cid, _ERR_INVALID_CBOR)
|
return cbor_error(req.cid, _ERR_INVALID_CBOR)
|
||||||
|
|
||||||
|
cred.use_sign_count = knownapps.get(rp_id_hash, {}).get("use_sign_count", True)
|
||||||
|
|
||||||
# Check data types.
|
# Check data types.
|
||||||
if (
|
if (
|
||||||
not cred.check_data_types()
|
not cred.check_data_types()
|
||||||
@ -1333,10 +1339,12 @@ def cbor_make_credential_sign(
|
|||||||
extensions = cbor.encode({"hmac-secret": True})
|
extensions = cbor.encode({"hmac-secret": True})
|
||||||
flags |= _AUTH_FLAG_ED
|
flags |= _AUTH_FLAG_ED
|
||||||
|
|
||||||
|
ctr = cred.next_signature_counter()
|
||||||
|
|
||||||
authenticator_data = (
|
authenticator_data = (
|
||||||
cred.rp_id_hash
|
cred.rp_id_hash
|
||||||
+ bytes([flags])
|
+ bytes([flags])
|
||||||
+ b"\x00\x00\x00\x00"
|
+ ctr.to_bytes(4, "big")
|
||||||
+ att_cred_data
|
+ att_cred_data
|
||||||
+ extensions
|
+ extensions
|
||||||
)
|
)
|
||||||
@ -1541,7 +1549,8 @@ def cbor_get_assertion_sign(
|
|||||||
flags |= _AUTH_FLAG_ED
|
flags |= _AUTH_FLAG_ED
|
||||||
encoded_extensions = cbor.encode(extensions)
|
encoded_extensions = cbor.encode(extensions)
|
||||||
|
|
||||||
ctr = storage.device.next_u2f_counter() or 0
|
ctr = cred.next_signature_counter()
|
||||||
|
|
||||||
authenticator_data = (
|
authenticator_data = (
|
||||||
rp_id_hash + bytes([flags]) + ctr.to_bytes(4, "big") + encoded_extensions
|
rp_id_hash + bytes([flags]) + ctr.to_bytes(4, "big") + encoded_extensions
|
||||||
)
|
)
|
||||||
|
@ -20,10 +20,10 @@ class ConfirmInfo:
|
|||||||
|
|
||||||
def load_icon(self, rp_id_hash: bytes) -> None:
|
def load_icon(self, rp_id_hash: bytes) -> None:
|
||||||
from trezor import res
|
from trezor import res
|
||||||
from apps.webauthn import knownapps
|
from apps.webauthn.knownapps import knownapps
|
||||||
|
|
||||||
try:
|
try:
|
||||||
namepart = knownapps.knownapps[rp_id_hash].lower().replace(" ", "_")
|
namepart = knownapps[rp_id_hash]["label"].lower().replace(" ", "_")
|
||||||
icon = res.load("apps/webauthn/res/icon_%s.toif" % namepart)
|
icon = res.load("apps/webauthn/res/icon_%s.toif" % namepart)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
icon = res.load("apps/webauthn/res/icon_webauthn.toif")
|
icon = res.load("apps/webauthn/res/icon_webauthn.toif")
|
||||||
|
@ -23,6 +23,7 @@ _CRED_ID_USER_NAME = const(0x04)
|
|||||||
_CRED_ID_USER_DISPLAY_NAME = const(0x05)
|
_CRED_ID_USER_DISPLAY_NAME = const(0x05)
|
||||||
_CRED_ID_CREATION_TIME = const(0x06)
|
_CRED_ID_CREATION_TIME = const(0x06)
|
||||||
_CRED_ID_HMAC_SECRET = const(0x07)
|
_CRED_ID_HMAC_SECRET = const(0x07)
|
||||||
|
_CRED_ID_USE_SIGN_COUNT = const(0x08)
|
||||||
|
|
||||||
# Key paths
|
# Key paths
|
||||||
_U2F_KEY_PATH = const(0x80553246)
|
_U2F_KEY_PATH = const(0x80553246)
|
||||||
@ -30,7 +31,7 @@ _U2F_KEY_PATH = const(0x80553246)
|
|||||||
|
|
||||||
class Credential:
|
class Credential:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.index = None # type Optional[int]
|
self.index = None # type: Optional[int]
|
||||||
self.id = b"" # type: bytes
|
self.id = b"" # type: bytes
|
||||||
self.rp_id = "" # type: str
|
self.rp_id = "" # type: str
|
||||||
self.rp_id_hash = b"" # type: bytes
|
self.rp_id_hash = b"" # type: bytes
|
||||||
@ -48,6 +49,9 @@ class Credential:
|
|||||||
def hmac_secret_key(self) -> Optional[bytes]:
|
def hmac_secret_key(self) -> Optional[bytes]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def next_signature_counter(self) -> int:
|
||||||
|
return storage.device.next_u2f_counter() or 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_bytes(data: bytes, rp_id_hash: bytes) -> Optional["Credential"]:
|
def from_bytes(data: bytes, rp_id_hash: bytes) -> Optional["Credential"]:
|
||||||
cred = Fido2Credential.from_cred_id(
|
cred = Fido2Credential.from_cred_id(
|
||||||
@ -65,19 +69,20 @@ class Fido2Credential(Credential):
|
|||||||
self.rp_name = None # type: Optional[str]
|
self.rp_name = None # type: Optional[str]
|
||||||
self.user_name = None # type: Optional[str]
|
self.user_name = None # type: Optional[str]
|
||||||
self.user_display_name = None # type: Optional[str]
|
self.user_display_name = None # type: Optional[str]
|
||||||
self._creation_time = 0 # type: int
|
self.creation_time = 0 # type: int
|
||||||
self.hmac_secret = False # type: bool
|
self.hmac_secret = False # type: bool
|
||||||
|
self.use_sign_count = False # type: bool
|
||||||
|
|
||||||
def __lt__(self, other: Credential) -> bool:
|
def __lt__(self, other: Credential) -> bool:
|
||||||
# Sort FIDO2 credentials newest first amongst each other.
|
# Sort FIDO2 credentials newest first amongst each other.
|
||||||
if isinstance(other, Fido2Credential):
|
if isinstance(other, Fido2Credential):
|
||||||
return self._creation_time > other._creation_time
|
return self.creation_time > other.creation_time
|
||||||
|
|
||||||
# Sort FIDO2 credentials before U2F credentials.
|
# Sort FIDO2 credentials before U2F credentials.
|
||||||
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
|
||||||
|
|
||||||
data = cbor.encode(
|
data = cbor.encode(
|
||||||
{
|
{
|
||||||
@ -88,8 +93,9 @@ class Fido2Credential(Credential):
|
|||||||
(_CRED_ID_USER_ID, self.user_id),
|
(_CRED_ID_USER_ID, self.user_id),
|
||||||
(_CRED_ID_USER_NAME, self.user_name),
|
(_CRED_ID_USER_NAME, self.user_name),
|
||||||
(_CRED_ID_USER_DISPLAY_NAME, self.user_display_name),
|
(_CRED_ID_USER_DISPLAY_NAME, self.user_display_name),
|
||||||
(_CRED_ID_CREATION_TIME, self._creation_time),
|
(_CRED_ID_CREATION_TIME, self.creation_time),
|
||||||
(_CRED_ID_HMAC_SECRET, self.hmac_secret),
|
(_CRED_ID_HMAC_SECRET, self.hmac_secret),
|
||||||
|
(_CRED_ID_USE_SIGN_COUNT, self.use_sign_count),
|
||||||
)
|
)
|
||||||
if value
|
if value
|
||||||
}
|
}
|
||||||
@ -148,8 +154,9 @@ class Fido2Credential(Credential):
|
|||||||
cred.user_id = data.get(_CRED_ID_USER_ID, None)
|
cred.user_id = data.get(_CRED_ID_USER_ID, None)
|
||||||
cred.user_name = data.get(_CRED_ID_USER_NAME, None)
|
cred.user_name = data.get(_CRED_ID_USER_NAME, None)
|
||||||
cred.user_display_name = data.get(_CRED_ID_USER_DISPLAY_NAME, None)
|
cred.user_display_name = data.get(_CRED_ID_USER_DISPLAY_NAME, None)
|
||||||
cred._creation_time = data.get(_CRED_ID_CREATION_TIME, 0)
|
cred.creation_time = data.get(_CRED_ID_CREATION_TIME, 0)
|
||||||
cred.hmac_secret = data.get(_CRED_ID_HMAC_SECRET, False)
|
cred.hmac_secret = data.get(_CRED_ID_HMAC_SECRET, False)
|
||||||
|
cred.use_sign_count = data.get(_CRED_ID_USE_SIGN_COUNT, False)
|
||||||
cred.id = cred_id
|
cred.id = cred_id
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -165,7 +172,7 @@ class Fido2Credential(Credential):
|
|||||||
return (
|
return (
|
||||||
self.rp_id is not None
|
self.rp_id is not None
|
||||||
and self.user_id is not None
|
and self.user_id is not None
|
||||||
and self._creation_time is not None
|
and self.creation_time is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_data_types(self) -> bool:
|
def check_data_types(self) -> bool:
|
||||||
@ -176,7 +183,8 @@ class Fido2Credential(Credential):
|
|||||||
and isinstance(self.user_name, (str, type(None)))
|
and isinstance(self.user_name, (str, type(None)))
|
||||||
and isinstance(self.user_display_name, (str, type(None)))
|
and isinstance(self.user_display_name, (str, type(None)))
|
||||||
and isinstance(self.hmac_secret, bool)
|
and isinstance(self.hmac_secret, bool)
|
||||||
and isinstance(self._creation_time, (int, type(None)))
|
and isinstance(self.use_sign_count, bool)
|
||||||
|
and isinstance(self.creation_time, (int, type(None)))
|
||||||
and isinstance(self.id, (bytes, bytearray))
|
and isinstance(self.id, (bytes, bytearray))
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -212,6 +220,11 @@ class Fido2Credential(Credential):
|
|||||||
|
|
||||||
return node.key()
|
return node.key()
|
||||||
|
|
||||||
|
def next_signature_counter(self) -> int:
|
||||||
|
if not self.use_sign_count:
|
||||||
|
return 0
|
||||||
|
return super().next_signature_counter()
|
||||||
|
|
||||||
|
|
||||||
class U2fCredential(Credential):
|
class U2fCredential(Credential):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@ -249,9 +262,9 @@ class U2fCredential(Credential):
|
|||||||
self.id = keypath + mac.digest()
|
self.id = keypath + mac.digest()
|
||||||
|
|
||||||
def app_name(self) -> str:
|
def app_name(self) -> str:
|
||||||
from apps.webauthn import knownapps
|
from apps.webauthn.knownapps import knownapps
|
||||||
|
|
||||||
app_name = knownapps.knownapps.get(self.rp_id_hash, None)
|
app_name = knownapps.get(self.rp_id_hash, {}).get("label", None)
|
||||||
if app_name is None:
|
if app_name is None:
|
||||||
app_name = "%s...%s" % (
|
app_name = "%s...%s" % (
|
||||||
hexlify(self.rp_id_hash[:4]).decode(),
|
hexlify(self.rp_id_hash[:4]).decode(),
|
||||||
|
@ -6,32 +6,54 @@ from trezor.crypto.hashlib import sha256
|
|||||||
|
|
||||||
_knownapps = {
|
_knownapps = {
|
||||||
# U2F
|
# U2F
|
||||||
"https://bitbucket.org": "Bitbucket",
|
"https://bitbucket.org": {"label": "Bitbucket", "use_sign_count": True},
|
||||||
"https://www.bitfinex.com": "Bitfinex",
|
"https://www.bitfinex.com": {"label": "Bitfinex", "use_sign_count": True},
|
||||||
"https://vault.bitwarden.com/app-id.json": "Bitwarden",
|
"https://vault.bitwarden.com/app-id.json": {
|
||||||
"https://www.dashlane.com": "Dashlane",
|
"label": "Bitwarden",
|
||||||
"https://www.dropbox.com/u2f-app-id.json": "Dropbox",
|
"use_sign_count": True,
|
||||||
"https://api-9dcf9b83.duosecurity.com": "Duo",
|
},
|
||||||
"https://www.fastmail.com": "FastMail",
|
"https://www.dashlane.com": {"label": "Dashlane", "use_sign_count": True},
|
||||||
"https://id.fedoraproject.org/u2f-origins.json": "Fedora",
|
"https://www.dropbox.com/u2f-app-id.json": {
|
||||||
"https://account.gandi.net/api/u2f/trusted_facets.json": "Gandi",
|
"label": "Dropbox",
|
||||||
"https://github.com/u2f/trusted_facets": "GitHub",
|
"use_sign_count": True,
|
||||||
"https://gitlab.com": "GitLab",
|
},
|
||||||
"https://www.gstatic.com/securitykey/origins.json": "Google",
|
"https://api-9dcf9b83.duosecurity.com": {"label": "Duo", "use_sign_count": True},
|
||||||
"https://keepersecurity.com": "Keeper",
|
"https://www.fastmail.com": {"label": "FastMail", "use_sign_count": True},
|
||||||
"https://lastpass.com": "LastPass",
|
"https://id.fedoraproject.org/u2f-origins.json": {
|
||||||
"https://slushpool.com/static/security/u2f.json": "Slush Pool",
|
"label": "Fedora",
|
||||||
"https://dashboard.stripe.com": "Stripe",
|
"use_sign_count": True,
|
||||||
"https://u2f.bin.coffee": "u2f.bin.coffee",
|
},
|
||||||
|
"https://account.gandi.net/api/u2f/trusted_facets.json": {
|
||||||
|
"label": "Gandi",
|
||||||
|
"use_sign_count": True,
|
||||||
|
},
|
||||||
|
"https://github.com/u2f/trusted_facets": {
|
||||||
|
"label": "GitHub",
|
||||||
|
"use_sign_count": True,
|
||||||
|
},
|
||||||
|
"https://gitlab.com": {"label": "GitLab", "use_sign_count": True},
|
||||||
|
"https://www.gstatic.com/securitykey/origins.json": {
|
||||||
|
"label": "Google",
|
||||||
|
"use_sign_count": True,
|
||||||
|
},
|
||||||
|
"https://keepersecurity.com": {"label": "Keeper", "use_sign_count": True},
|
||||||
|
"https://lastpass.com": {"label": "LastPass", "use_sign_count": True},
|
||||||
|
"https://slushpool.com/static/security/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
|
# WebAuthn
|
||||||
"www.binance.com": "Binance",
|
"www.binance.com": {"label": "Binance", "use_sign_count": False},
|
||||||
"www.dropbox.com": "Dropbox",
|
"www.dropbox.com": {"label": "Dropbox"},
|
||||||
"secure.login.gov": "login.gov",
|
"github.com": {"label": "GitHub", "use_sign_count": True},
|
||||||
"login.microsoft.com": "Microsoft",
|
"secure.login.gov": {"label": "login.gov"},
|
||||||
"webauthn.bin.coffee": "webauthn.bin.coffee",
|
"login.microsoft.com": {"label": "Microsoft", "use_sign_count": False},
|
||||||
"webauthn.io": "WebAuthn.io",
|
"webauthn.bin.coffee": {"label": "webauthn.bin.coffee"},
|
||||||
"webauthn.me": "WebAuthn.me",
|
"webauthn.io": {"label": "WebAuthn.io"},
|
||||||
"demo.yubico.com": "demo.yubico.com",
|
"webauthn.me": {"label": "WebAuthn.me"},
|
||||||
|
"demo.yubico.com": {"label": "demo.yubico.com"},
|
||||||
}
|
}
|
||||||
|
|
||||||
knownapps = {sha256(k.encode()).digest(): v for (k, v) in _knownapps.items()}
|
knownapps = {sha256(k.encode()).digest(): v for (k, v) in _knownapps.items()}
|
||||||
|
@ -30,8 +30,9 @@ async def list_resident_credentials(
|
|||||||
user_id=cred.user_id,
|
user_id=cred.user_id,
|
||||||
user_name=cred.user_name,
|
user_name=cred.user_name,
|
||||||
user_display_name=cred.user_display_name,
|
user_display_name=cred.user_display_name,
|
||||||
creation_time=cred._creation_time,
|
creation_time=cred.creation_time,
|
||||||
hmac_secret=cred.hmac_secret,
|
hmac_secret=cred.hmac_secret,
|
||||||
|
use_sign_count=cred.use_sign_count,
|
||||||
)
|
)
|
||||||
for cred in get_resident_credentials()
|
for cred in get_resident_credentials()
|
||||||
]
|
]
|
||||||
|
@ -23,6 +23,7 @@ class WebAuthnCredential(p.MessageType):
|
|||||||
user_display_name: str = None,
|
user_display_name: str = None,
|
||||||
creation_time: int = None,
|
creation_time: int = None,
|
||||||
hmac_secret: bool = None,
|
hmac_secret: bool = None,
|
||||||
|
use_sign_count: bool = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.index = index
|
self.index = index
|
||||||
self.id = id
|
self.id = id
|
||||||
@ -33,6 +34,7 @@ class WebAuthnCredential(p.MessageType):
|
|||||||
self.user_display_name = user_display_name
|
self.user_display_name = user_display_name
|
||||||
self.creation_time = creation_time
|
self.creation_time = creation_time
|
||||||
self.hmac_secret = hmac_secret
|
self.hmac_secret = hmac_secret
|
||||||
|
self.use_sign_count = use_sign_count
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_fields(cls) -> Dict:
|
def get_fields(cls) -> Dict:
|
||||||
@ -46,4 +48,5 @@ class WebAuthnCredential(p.MessageType):
|
|||||||
7: ('user_display_name', p.UnicodeType, 0),
|
7: ('user_display_name', p.UnicodeType, 0),
|
||||||
8: ('creation_time', p.UVarintType, 0),
|
8: ('creation_time', p.UVarintType, 0),
|
||||||
9: ('hmac_secret', p.BoolType, 0),
|
9: ('hmac_secret', p.BoolType, 0),
|
||||||
|
10: ('use_sign_count', p.BoolType, 0),
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class TestCredential(unittest.TestCase):
|
|||||||
self.assertEqual(cred.rp_id_hash, rp_id_hash)
|
self.assertEqual(cred.rp_id_hash, rp_id_hash)
|
||||||
self.assertEqual(hexlify(cred.user_id), user_id)
|
self.assertEqual(hexlify(cred.user_id), user_id)
|
||||||
self.assertEqual(cred.user_name, user_name)
|
self.assertEqual(cred.user_name, user_name)
|
||||||
self.assertEqual(cred._creation_time, 2)
|
self.assertEqual(cred.creation_time, creation_time)
|
||||||
self.assertTrue(cred.hmac_secret)
|
self.assertTrue(cred.hmac_secret)
|
||||||
self.assertIsNone(cred.rp_name)
|
self.assertIsNone(cred.rp_name)
|
||||||
self.assertIsNone(cred.user_display_name)
|
self.assertIsNone(cred.user_display_name)
|
||||||
|
@ -1973,20 +1973,22 @@ def webauthn_list_credentials(connect):
|
|||||||
click.echo("")
|
click.echo("")
|
||||||
click.echo("WebAuthn credential at index {}:".format(cred.index))
|
click.echo("WebAuthn credential at index {}:".format(cred.index))
|
||||||
if cred.rp_id is not None:
|
if cred.rp_id is not None:
|
||||||
click.echo(" Relying party ID: {}".format(cred.rp_id))
|
click.echo(" Relying party ID: {}".format(cred.rp_id))
|
||||||
if cred.rp_name is not None:
|
if cred.rp_name is not None:
|
||||||
click.echo(" Relying party name: {}".format(cred.rp_name))
|
click.echo(" Relying party name: {}".format(cred.rp_name))
|
||||||
if cred.user_id is not None:
|
if cred.user_id is not None:
|
||||||
click.echo(" User ID: {}".format(cred.user_id.hex()))
|
click.echo(" User ID: {}".format(cred.user_id.hex()))
|
||||||
if cred.user_name is not None:
|
if cred.user_name is not None:
|
||||||
click.echo(" User name: {}".format(cred.user_name))
|
click.echo(" User name: {}".format(cred.user_name))
|
||||||
if cred.user_display_name is not None:
|
if cred.user_display_name is not None:
|
||||||
click.echo(" User display name: {}".format(cred.user_display_name))
|
click.echo(" User display name: {}".format(cred.user_display_name))
|
||||||
if cred.creation_time is not None:
|
if cred.creation_time is not None:
|
||||||
click.echo(" Creation time: {}".format(cred.creation_time))
|
click.echo(" Creation time: {}".format(cred.creation_time))
|
||||||
if cred.hmac_secret is not None:
|
if cred.hmac_secret is not None:
|
||||||
click.echo(" hmac-secret enabled: {}".format(cred.hmac_secret))
|
click.echo(" hmac-secret enabled: {}".format(cred.hmac_secret))
|
||||||
click.echo(" Credential ID: {}".format(cred.id.hex()))
|
if cred.use_sign_count is not None:
|
||||||
|
click.echo(" Use signature counter: {}".format(cred.use_sign_count))
|
||||||
|
click.echo(" Credential ID: {}".format(cred.id.hex()))
|
||||||
|
|
||||||
if not creds:
|
if not creds:
|
||||||
click.echo("There are no resident credentials stored on the device.")
|
click.echo("There are no resident credentials stored on the device.")
|
||||||
|
@ -23,6 +23,7 @@ class WebAuthnCredential(p.MessageType):
|
|||||||
user_display_name: str = None,
|
user_display_name: str = None,
|
||||||
creation_time: int = None,
|
creation_time: int = None,
|
||||||
hmac_secret: bool = None,
|
hmac_secret: bool = None,
|
||||||
|
use_sign_count: bool = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.index = index
|
self.index = index
|
||||||
self.id = id
|
self.id = id
|
||||||
@ -33,6 +34,7 @@ class WebAuthnCredential(p.MessageType):
|
|||||||
self.user_display_name = user_display_name
|
self.user_display_name = user_display_name
|
||||||
self.creation_time = creation_time
|
self.creation_time = creation_time
|
||||||
self.hmac_secret = hmac_secret
|
self.hmac_secret = hmac_secret
|
||||||
|
self.use_sign_count = use_sign_count
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_fields(cls) -> Dict:
|
def get_fields(cls) -> Dict:
|
||||||
@ -46,4 +48,5 @@ class WebAuthnCredential(p.MessageType):
|
|||||||
7: ('user_display_name', p.UnicodeType, 0),
|
7: ('user_display_name', p.UnicodeType, 0),
|
||||||
8: ('creation_time', p.UVarintType, 0),
|
8: ('creation_time', p.UVarintType, 0),
|
||||||
9: ('hmac_secret', p.BoolType, 0),
|
9: ('hmac_secret', p.BoolType, 0),
|
||||||
|
10: ('use_sign_count', p.BoolType, 0),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user