Saltar al contenido
ecdsa.com
Python

py_webauthn

py_webauthn, de Duo Labs, la biblioteca de referencia de relying party WebAuthn para Python: firmas de aserción solo en DER y contadores estrictos, pero la verificación del usuario no se exige salvo que la pidas.
Licencia
BSD-3-Clause
Versión comprobada
3.0.1 ·
Código fuente
github.com/duo-labs/py_webauthn
Documentación
duo-labs.github.io/py_webauthn/

Compatibilidad de un vistazo

WebAuthn

py_webauthn (el paquete webauthn de PyPI) genera las opciones de registro y de autenticación y verifica las respuestas, incluidos todos los formatos de atestación, sobre pyca/cryptography. La comprobación le pasó el mismo registro y las mismas aserciones de un autenticador por software que se usaron con SimpleWebAuthn, go-webauthn y WebAuthn4J, y eso es lo que hace visibles las diferencias entre ellas.

Los valores predeterminados de la política son los más laxos de las cuatro. generate_registration_options() no envía ningún miembro authenticatorSelection, así que los navegadores recurren a la verificación del usuario preferred y a la clave residente discouraged —llaves de acceso que no son detectables—, y verify_registration_response() y verify_authentication_response() aceptaron respuestas con el indicador UV desactivado salvo que se pase require_user_verification=True. Por eso, un inicio de sesión construido sobre los valores predeterminados acepta un toque en una llave de seguridad sin PIN. Establece la política de forma explícita en ambos lados, como hace el ejemplo.

Las firmas de aserción deben ser DER ASN.1: una firma r‖s en bruto de 64 bytes falló con Could not verify authentication signature, un mensaje que no menciona la codificación; el conversor DER ⇄ en bruto muestra si un cliente de prueba produjo la forma equivocada. Una firma DER high-S se verificó. Por defecto se ofrecen Ed25519 (-8), ES256 (-7) y RS256 (-257); no hay ninguna entrada ML-DSA, a diferencia de SimpleWebAuthn 14.

Los contadores son estrictos: Response sign count of 3 was not greater than current count of 5, y lo mismo con 0 tras 5, mientras que 0 tras 0 —lo que notifican las llaves de acceso sincronizadas— se verificó. Guarda el new_sign_count devuelto después de cada inicio de sesión correcto.

Decodifica la salida en bruto del navegador con el inspector de WebAuthn y experimenta sin riesgos en el entorno de pruebas de llaves de acceso.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/py-webauthn/run.sh ejecutó py_webauthn 3.0.1 en la imagen de contenedor python:3.14.8-slim-trixie el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Algoritmos COSE predeterminados

    -8-7-257

    generate_registration_options() enumera los algoritmos COSE -8 (Ed25519), -7 (ES256) y -257 (RS256).

    Registrado por la comprobación
    fact webauthn-default-algorithms = -8, -7, -257
  • Atestación predeterminada

    none

    La preferencia de transmisión de la atestación es none por defecto.

    Registrado por la comprobación
    fact webauthn-attestation = none
  • Verificación del usuario predeterminada

    preferred

    Por defecto no se envía ningún authenticatorSelection, así que el navegador aplica el valor predeterminado de WebAuthn, preferred.

    Registrado por la comprobación
    fact webauthn-user-verification = preferred
    evidence webauthn-user-verification: authenticatorSelection in the generated options: absent
  • Clave residente predeterminada

    discouraged

    Sin authenticatorSelection, se aplica el valor predeterminado de WebAuthn, discouraged, así que no se pide a los autenticadores que creen credenciales detectables.

    Registrado por la comprobación
    fact webauthn-resident-key = discouraged
  • Verificación del usuario exigida por defecto

    no

    Un registro y una aserción con solo UP activado se verificaron; con require_user_verification=True, la aserción falló con User verification is required but user was not verified during authentication.

    Registrado por la comprobación
    fact webauthn-uv-enforced-by-default = no
    evidence webauthn-uv-enforced-by-default: registration with the UV flag clear: verified
  • Codificación de la firma de la aserción

    der

    Una aserción ES256 correcta con firma DER se verificó.

    Registrado por la comprobación
    fact webauthn-assertion-encoding = der
  • Firma de aserción r‖s en bruto

    rejected

    Una firma r‖s en bruto de 64 bytes falló con InvalidAuthenticationResponse: Could not verify authentication signature.

    Registrado por la comprobación
    fact webauthn-raw-signature = rejected
  • Firma de aserción high-S

    accepted

    Una firma DER con s sustituido por n − s se verificó.

    Registrado por la comprobación
    fact webauthn-high-s = accepted
  • Contador de firmas que retrocede

    rejected

    Los contadores 3 y 0 tras un 5 almacenado fallaron con Response sign count of … was not greater than current count of 5; un 0 tras un 0 almacenado se verificó.

    Registrado por la comprobación
    fact webauthn-counter-regression = rejected
  • Credenciales Ed25519

    supported

    Una credencial Ed25519 (COSE -8) se registró y se autenticó correctamente.

    Registrado por la comprobación
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Uso

Genera opciones de registro y de autenticación que exijan la verificación del usuario y una credencial detectable, en lugar de los valores predeterminados de la biblioteca.
import json

from webauthn import generate_authentication_options, generate_registration_options, options_to_json
from webauthn.helpers.structs import AuthenticatorSelectionCriteria, ResidentKeyRequirement, UserVerificationRequirement

# The defaults send no authenticatorSelection at all, and verification does not
# require UV. State the policy in the options, and pass
# require_user_verification=True to verify_registration_response() and
# verify_authentication_response() so the server enforces the same policy.
registration = generate_registration_options(
    rp_id="example.com",
    rp_name="Example",
    user_name="user@example.com",
    authenticator_selection=AuthenticatorSelectionCriteria(
        resident_key=ResidentKeyRequirement.REQUIRED,
        user_verification=UserVerificationRequirement.REQUIRED,
    ),
)
authentication = generate_authentication_options(rp_id="example.com", user_verification=UserVerificationRequirement.REQUIRED)

selection = json.loads(options_to_json(registration))["authenticatorSelection"]
print("uv:", selection["userVerification"], json.loads(options_to_json(authentication))["userVerification"])
print("resident key:", selection["residentKey"])

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor python:3.14.8-slim-trixie, y la ejecución falla si la salida no coincide.

Configuración
pip install --quiet --root-user-action=ignore webauthn==3.0.1
Comando
python usage.py
La salida debe coincidir (expresión regular)
uv: required required\nresident key: required

Bibliotecas