ai / bt-source /panel /class /webauthn /helpers /parse_authenticator_data.py
GGSheng's picture
feat: deploy Gemma 4 to hf space
17e971c verified
from typing import Union
from .byteslike_to_bytes import byteslike_to_bytes
from .exceptions import InvalidAuthenticatorDataStructure
from .structs import AttestedCredentialData, AuthenticatorData, AuthenticatorDataFlags
from .parse_cbor import parse_cbor
from .encode_cbor import encode_cbor
def parse_authenticator_data(val: bytes) -> AuthenticatorData:
"""
Turn `response.attestationObject.authData` into structured data
"""
val = byteslike_to_bytes(val)
# Don't bother parsing if there aren't enough bytes for at least:
# - rpIdHash (32 bytes)
# - flags (1 byte)
# - signCount (4 bytes)
if len(val) < 37:
raise InvalidAuthenticatorDataStructure(
f"Authenticator data was {len(val)} bytes, expected at least 37 bytes"
)
pointer = 0
rp_id_hash = val[pointer:32]
pointer += 32
# Cast byte to ordinal so we can use bitwise operators on it
flags_bytes = ord(val[pointer : pointer + 1])
pointer += 1
sign_count = val[pointer : pointer + 4]
pointer += 4
# Parse flags
flags = AuthenticatorDataFlags(
up=flags_bytes & (1 << 0) != 0,
uv=flags_bytes & (1 << 2) != 0,
be=flags_bytes & (1 << 3) != 0,
bs=flags_bytes & (1 << 4) != 0,
at=flags_bytes & (1 << 6) != 0,
ed=flags_bytes & (1 << 7) != 0,
)
# The value to return
authenticator_data = AuthenticatorData(
rp_id_hash=rp_id_hash,
flags=flags,
sign_count=int.from_bytes(sign_count, "big"),
)
# Parse AttestedCredentialData if present
if flags.at is True:
aaguid = val[pointer : pointer + 16]
pointer += 16
credential_id_len = int.from_bytes(val[pointer : pointer + 2], "big")
pointer += 2
credential_id = val[pointer : pointer + credential_id_len]
pointer += credential_id_len
"""
Some authenticators incorrectly compose authData when using EdDSA for their public keys.
A CBOR "Map of 3 items" (0xA3) should be "Map of 4 items" (0xA4), and if we manually adjust
the single byte there's a good chance the authData can be correctly parsed. Let's try to
detect when this happens and gracefully handle it.
"""
# Decodes to `{1: "OKP", 3: -8, -1: "Ed25519"}` (it's missing key -2 a.k.a. COSEKey.X)
bad_eddsa_cbor = bytearray.fromhex("a301634f4b500327206745643235353139")
# If we find the bytes here then let's fix the bad data
if val[pointer : pointer + len(bad_eddsa_cbor)] == bad_eddsa_cbor:
# Make a mutable copy of the bytes...
_val = bytearray(val)
# ...Fix the bad byte...
_val[pointer] = 0xA4
# ...Then replace `val` with the fixed bytes
val = bytes(_val)
# Load the next CBOR-encoded value
credential_public_key = parse_cbor(val[pointer:])
credential_public_key_bytes = encode_cbor(credential_public_key)
pointer += len(credential_public_key_bytes)
attested_cred_data = AttestedCredentialData(
aaguid=aaguid,
credential_id=credential_id,
credential_public_key=credential_public_key_bytes,
)
authenticator_data.attested_credential_data = attested_cred_data
if flags.ed is True:
extension_object = parse_cbor(val[pointer:])
extension_bytes = encode_cbor(extension_object)
pointer += len(extension_bytes)
authenticator_data.extensions = extension_bytes
# We should have parsed all authenticator data by this point
if len(val) > pointer:
raise InvalidAuthenticatorDataStructure(
"Leftover bytes detected while parsing authenticator data"
)
return authenticator_data