Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
if not subject.get_attributes_for_oid(COMMON_NAME):
raise RegistrationRejectedException(
'Attestation certificate must have subject-CN.')
extensions = x509_att_cert.extensions
# * If the related attestation root certificate is used
# for multiple authenticator models, the Extension OID
# 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) MUST
# be present, containing the AAGUID as a 16-byte OCTET
# STRING. The extension MUST NOT be marked as critical.
try:
oid = x509.ObjectIdentifier('1.3.6.1.4.1.45724.1.1.4')
aaguid_ext = extensions.get_extension_for_oid(oid)
if aaguid_ext.value.value[2:] != aaguid:
raise RegistrationRejectedException(
'Attestation certificate AAGUID must match '
'authenticator data.')
if aaguid_ext.critical:
raise RegistrationRejectedException(
"Attestation certificate's "
"'id-fido-gen-ce-aaguid' extension must not be "
"marked critical.")
except x509.ExtensionNotFound:
pass # Optional extension
# * The Basic Constraints extension MUST have the CA
# component set to false.
bc_extension = extensions.get_extension_for_class(
x509.BasicConstraints)
if not bc_extension or bc_extension.value.ca:
raise RegistrationRejectedException(
if 'x5c' not in att_stmt or 'sig' not in att_stmt:
raise RegistrationRejectedException(
'Attestation statement must be a valid CBOR object.')
# Step 2.
#
# Let attCert be the value of the first element of x5c. Let certificate
# public key be the public key conveyed by attCert. If certificate public
# key is not an Elliptic Curve (EC) public key over the P-256 curve,
# terminate this algorithm and return an appropriate error.
att_cert = att_stmt.get('x5c')[0]
x509_att_cert = load_der_x509_certificate(att_cert,
default_backend())
certificate_public_key = x509_att_cert.public_key()
if not isinstance(certificate_public_key.curve, SECP256R1):
raise RegistrationRejectedException(
'Bad certificate public key.')
# Step 3.
#
# Extract the claimed rpIdHash from authenticatorData, and the
# claimed credentialId and credentialPublicKey from
# authenticatorData.attestedCredentialData.
# The credential public key encoded in COSE_Key format, as defined in Section 7
# of [RFC8152], using the CTAP2 canonical CBOR encoding form. The COSE_Key-encoded
# credential public key MUST contain the optional "alg" parameter and MUST NOT
# contain any other optional parameters. The "alg" parameter MUST contain a
# COSEAlgorithmIdentifier value. The encoded credential public key MUST also
# contain any additional required parameters stipulated by the relevant key type
# specification, i.e., required for the key type "kty" and algorithm "alg" (see
# Section 8 of [RFC8152]).
#
# Assess the attestation trustworthiness using the outputs of the
# verification procedure in step 14, as follows:
#
# * If self attestation was used, check if self attestation is
# acceptable under Relying Party policy.
# * If ECDAA was used, verify that the identifier of the
# ECDAA-Issuer public key used is included in the set of
# acceptable trust anchors obtained in step 15.
# * Otherwise, use the X.509 certificates returned by the
# verification procedure to verify that the attestation
# public key correctly chains up to an acceptable root
# certificate.
if attestation_type == AT_SELF_ATTESTATION:
if not self.self_attestation_permitted:
raise RegistrationRejectedException(
'Self attestation is not permitted.')
elif attestation_type == AT_ATTESTATION_CA:
raise NotImplementedError(
'Attestation CA attestation type is not currently supported.'
)
elif attestation_type == AT_ECDAA:
raise NotImplementedError(
'ECDAA attestation type is not currently supported.')
elif attestation_type == AT_BASIC:
if self.trusted_attestation_cert_required:
if not _is_trusted_attestation_cert(
trust_path, trust_anchors):
raise RegistrationRejectedException(
'Untrusted attestation certificate.')
elif attestation_type == AT_NONE:
pass
# identifier values in the clientExtensionResults and the extensions
# in authData MUST be also be present as extension identifier values
# in the extensions member of options, i.e., no extensions are
# present that were not requested. In the general case, the meaning
# of "are as expected" is specific to the Relying Party and which
# extensions are in use.
registration_client_extensions = self.registration_response.get(
'registrationClientExtensions')
if registration_client_extensions:
rce = json.loads(registration_client_extensions)
if not _verify_client_extensions(rce, self.expected_registration_client_extensions):
raise RegistrationRejectedException(
'Unable to verify client extensions.')
if not _verify_authenticator_extensions(
c, self.expected_registration_authenticator_extensions):
raise RegistrationRejectedException(
'Unable to verify authenticator extensions.')
# Step 13.
#
# Determine the attestation statement format by performing
# a USASCII case-sensitive match on fmt against the set of
# supported WebAuthn Attestation Statement Format Identifier
# values. The up-to-date list of registered WebAuthn
# Attestation Statement Format Identifier values is maintained
# in the in the IANA registry of the same name
# [WebAuthn-Registries].
if not _verify_attestation_statement_format(fmt):
raise RegistrationRejectedException(
'Unable to verify attestation statement format.')
# Step 14.
extensions = x509_att_cert.extensions
# * If the related attestation root certificate is used
# for multiple authenticator models, the Extension OID
# 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) MUST
# be present, containing the AAGUID as a 16-byte OCTET
# STRING. The extension MUST NOT be marked as critical.
try:
oid = x509.ObjectIdentifier('1.3.6.1.4.1.45724.1.1.4')
aaguid_ext = extensions.get_extension_for_oid(oid)
if aaguid_ext.value.value[2:] != aaguid:
raise RegistrationRejectedException(
'Attestation certificate AAGUID must match '
'authenticator data.')
if aaguid_ext.critical:
raise RegistrationRejectedException(
"Attestation certificate's "
"'id-fido-gen-ce-aaguid' extension must not be "
"marked critical.")
except x509.ExtensionNotFound:
pass # Optional extension
# * The Basic Constraints extension MUST have the CA
# component set to false.
bc_extension = extensions.get_extension_for_class(
x509.BasicConstraints)
if not bc_extension or bc_extension.value.ca:
raise RegistrationRejectedException(
'Attestation certificate must have Basic Constraints '
'extension with CA=false.')
# * If successful, return attestation type Basic and
raise RegistrationRejectedException(
'Invalid signature received.')
except NotImplementedError:
raise RegistrationRejectedException(
'Unsupported algorithm.')
# * Verify that attestnCert meets the requirements in
# §8.2.1 Packed attestation statement certificate
# requirements.
# The attestation certificate MUST have the following
# fields/extensions:
# * Version MUST be set to 3 (which is indicated by an
# ASN.1 INTEGER with value 2).
if x509_att_cert.version != x509.Version.v3:
raise RegistrationRejectedException(
'Invalid attestation certificate version.')
# * Subject field MUST be set to:
subject = x509_att_cert.subject
COUNTRY_NAME = x509.NameOID.COUNTRY_NAME
ORGANIZATION_NAME = x509.NameOID.ORGANIZATION_NAME
ORG_UNIT_NAME = x509.NameOID.ORGANIZATIONAL_UNIT_NAME
COMMON_NAME = x509.NameOID.COMMON_NAME
# * Subject-C: ISO 3166 code specifying the country
# where the Authenticator vendor is
# incorporated
if not subject.get_attributes_for_oid(COUNTRY_NAME):
raise RegistrationRejectedException(
'Attestation certificate must have subject-C.')
# * If successful, return attestation type Basic and
# attestation trust path x5c.
attestation_type = AT_BASIC
trust_path = [x509_att_cert]
elif 'ecdaaKeyId' in att_stmt:
# Step 3.
#
# If ecdaaKeyId is present, then the attestation type is
# ECDAA. In this case:
# * Verify that sig is a valid signature over the
# concatenation of authenticatorData and clientDataHash
# using ECDAA-Verify with ECDAA-Issuer public key
# identified by ecdaaKeyId (see [FIDOEcdaaAlgorithm]).
# * If successful, return attestation type ECDAA and
# attestation trust path ecdaaKeyId.
raise RegistrationRejectedException(
'ECDAA attestation type is not currently supported.')
else:
# Step 4.
#
# If neither x5c nor ecdaaKeyId is present, self
# attestation is in use.
# * Validate that alg matches the algorithm of the
# credentialPublicKey in authenticatorData.
try:
public_key_alg, credential_public_key = _load_cose_public_key(
credential_pub_key)
except COSEKeyException as e:
raise RegistrationRejectedException(str(e))
if public_key_alg != alg:
raise RegistrationRejectedException(
subject = x509_att_cert.subject
COUNTRY_NAME = x509.NameOID.COUNTRY_NAME
ORGANIZATION_NAME = x509.NameOID.ORGANIZATION_NAME
ORG_UNIT_NAME = x509.NameOID.ORGANIZATIONAL_UNIT_NAME
COMMON_NAME = x509.NameOID.COMMON_NAME
# * Subject-C: ISO 3166 code specifying the country
# where the Authenticator vendor is
# incorporated
if not subject.get_attributes_for_oid(COUNTRY_NAME):
raise RegistrationRejectedException(
'Attestation certificate must have subject-C.')
# * Subject-O: Legal name of the Authenticator vendor
if not subject.get_attributes_for_oid(ORGANIZATION_NAME):
raise RegistrationRejectedException(
'Attestation certificate must have subject-O.')
# * Subject-OU: Literal string
# “Authenticator Attestation”
ou = subject.get_attributes_for_oid(ORG_UNIT_NAME)
if not ou or ou[0].value != 'Authenticator Attestation':
raise RegistrationRejectedException(
"Attestation certificate must have subject-OU set to "
"'Authenticator Attestation'.")
# * Subject-CN: A UTF8String of the vendor’s choosing
if not subject.get_attributes_for_oid(COMMON_NAME):
raise RegistrationRejectedException(
'Attestation certificate must have subject-CN.')
extensions = x509_att_cert.extensions
'Invalid signature received.')
except NotImplementedError:
raise RegistrationRejectedException(
'Unsupported algorithm.')
# * If successful, return attestation type Self and empty
# attestation trust path.
attestation_type = AT_SELF_ATTESTATION
trust_path = []
return (attestation_type, trust_path, credential_pub_key, cred_id)
elif fmt == AT_FMT_NONE:
# `none` - indicates that the Relying Party is not interested in
# authenticator attestation.
if not self.none_attestation_permitted:
raise RegistrationRejectedException(
'Authenticator attestation is required.')
# Step 1.
#
# Return attestation type None with an empty trust path.
attestation_type = AT_NONE
trust_path = []
return (attestation_type, trust_path, credential_pub_key, cred_id)
else:
raise RegistrationRejectedException('Invalid format.')
# * If successful, return attestation type ECDAA and
# attestation trust path ecdaaKeyId.
raise RegistrationRejectedException(
'ECDAA attestation type is not currently supported.')
else:
# Step 4.
#
# If neither x5c nor ecdaaKeyId is present, self
# attestation is in use.
# * Validate that alg matches the algorithm of the
# credentialPublicKey in authenticatorData.
try:
public_key_alg, credential_public_key = _load_cose_public_key(
credential_pub_key)
except COSEKeyException as e:
raise RegistrationRejectedException(str(e))
if public_key_alg != alg:
raise RegistrationRejectedException(
'Public key algorithm does not match.')
# * Verify that sig is a valid signature over the
# concatenation of authenticatorData and clientDataHash
# using the credential public key with alg.
try:
_verify_signature(credential_public_key, alg,
verification_data, signature)
except InvalidSignature:
raise RegistrationRejectedException(
'Invalid signature received.')
except NotImplementedError:
raise RegistrationRejectedException(