docs: def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None, ca_pem_file=None, ca_path=None,
                 hmac_key=None, validate_schema=True, parser=None, uri_resolver=None, id_attribute=None,
                 expect_references=1):
          """
          Verify the XML signature supplied in the data and return the XML node signed by the signature, or raise an
          exception if the signature is not valid. By default, this requires the signature to be generated using a valid
          X.509 certificate. To enable other means of signature validation, set the **require_x509** argument to `False`.
  
          .. admonition:: See what is signed
  
           It is important to understand and follow the best practice rule of "See what is signed" when verifying XML
           signatures. The gist of this rule is: if your application neglects to verify that the information it trusts is
           what was actually signed, the attacker can supply a valid signature but point you to malicious data that wasn't
           signed by that signature.
  
           In SignXML, you can ensure that the information signed is what you expect to be signed by only trusting the
           data returned by the ``verify()`` method. The return value is the XML node or string that was signed. Also,
           depending on the signature settings used, comments in the XML data may not be subject to signing, so may need
           to be untrusted.
  
           **Recommended reading:** http://www.w3.org/TR/xmldsig-bestpractices/#practices-applications
  
          .. admonition:: Establish trust
  
           If you do not supply any keyword arguments to ``verify()``, the default behavior is to trust **any** valid XML
           signature generated using a valid X.509 certificate trusted by your system's CA store. This means anyone can
           get an SSL certificate and generate a signature that you will trust. To establish trust in the signer, use the
           ``x509_cert`` argument to specify a certificate that was pre-shared out-of-band (e.g. via SAML metadata, as
           shown in :ref:`Verifying SAML assertions <verifying-saml-assertions>`), or ``cert_subject_name`` to specify a
           subject name that must be in the signing X.509 certificate given by the signature (verified as if it were a
           domain name), or ``ca_pem_file``/``ca_path`` to give a custom CA.
  
          :param data: Signature data to verify
          :type data: String, file-like object, or XML ElementTree Element API compatible object
          :param require_x509:
              If ``True``, a valid X.509 certificate-based signature with an established chain of trust is required to
              pass validation. If ``False``, other types of valid signatures (e.g. HMAC or RSA public key) are accepted.
          :type require_x509: boolean
          :param x509_cert:
              A trusted external X.509 certificate, given as a PEM-formatted string or OpenSSL.crypto.X509 object, to use
              for verification. Overrides any X.509 certificate information supplied by the signature. If left set to
              ``None``, requires that the signature supply a valid X.509 certificate chain that validates against the
              known certificate authorities. Implies **require_x509=True**.
          :type x509_cert: string or OpenSSL.crypto.X509
          :param ca_pem_file:
              Filename of a PEM file containing certificate authority information to use when verifying certificate-based
              signatures.
          :type ca_pem_file: string or bytes
          :param ca_path:
              Path to a directory containing PEM-formatted certificate authority files to use when verifying
              certificate-based signatures. If neither **ca_pem_file** nor **ca_path** is given, the Mozilla CA bundle
              provided by :py:mod:`certifi` will be loaded.
          :type ca_path: string
          :param cert_subject_name:
              Subject Common Name to check the signing X.509 certificate against. Implies **require_x509=True**.
          :type cert_subject_name: string
          :param hmac_key: If using HMAC, a string containing the shared secret.
          :type hmac_key: string
          :param validate_schema: Whether to validate **data** against the XML Signature schema.
          :type validate_schema: boolean
          :param parser: Custom XML parser instance to use when parsing **data**.
          :type parser: :py:class:`lxml.etree.XMLParser` compatible parser
          :param uri_resolver: Function to use to resolve reference URIs that don't start with "#".
          :type uri_resolver: callable
          :param id_attribute:
              Name of the attribute whose value ``URI`` refers to. By default, SignXML will search for "Id", then "ID".
          :type id_attribute: string
          :param expect_references:
              Number of references to expect in the signature. If this is not 1, an array of VerifyResults is returned.
              If set to a non-integer, any number of references is accepted (otherwise a mismatch raises an error).
          :type expect_references: int or boolean
  
          :raises: :py:class:`cryptography.exceptions.InvalidSignature`
  
          :returns: VerifyResult object with the signed data, signed xml and signature xml
          :rtype: VerifyResult
  
          """
          self.hmac_key = hmac_key
          self.require_x509 = require_x509
          self.x509_cert = x509_cert
          self._parser = parser
  
          if x509_cert:
              self.require_x509 = True
  
          if id_attribute is not None:
              self.id_attributes = (id_attribute, )
  
          root = self.get_root(data)
          if root.tag == ds_tag("Signature"):
              signature_ref = root
          else:
              signature_ref = self._find(root, "Signature", anywhere=True)
  
          # HACK: deep copy won't keep root's namespaces
          signature = fromstring(etree.tostring(signature_ref), parser=parser)
  
          if validate_schema:
              self.schema().assertValid(signature)
  
          signed_info = self._find(signature, "SignedInfo")
          c14n_method = self._find(signed_info, "CanonicalizationMethod")
          c14n_algorithm = c14n_method.get("Algorithm")
          signature_method = self._find(signed_info, "SignatureMethod")
          signature_value = self._find(signature, "SignatureValue")
          signature_alg = signature_method.get("Algorithm")
          raw_signature = b64decode(signature_value.text)
          x509_data = signature.find("ds:KeyInfo/ds:X509Data", namespaces=namespaces)
          signed_info_c14n = self._c14n(signed_info, algorithm=c14n_algorithm)
  
          if x509_data is not None or self.require_x509:
              from OpenSSL.crypto import load_certificate, X509, FILETYPE_PEM, verify, Error as OpenSSLCryptoError
  
              if self.x509_cert is None:
                  if x509_data is None:
                      raise InvalidInput("Expected a X.509 certificate based signature")
                  certs = [cert.text for cert in self._findall(x509_data, "X509Certificate")]
                  if not certs:
                      msg = "Expected to find an X509Certificate element in the signature"
                      msg += " (X509SubjectName, X509SKI are not supported)"
                      raise InvalidInput(msg)
                  cert_chain = [load_certificate(FILETYPE_PEM, add_pem_header(cert)) for cert in certs]
                  signing_cert = verify_x509_cert_chain(cert_chain, ca_pem_file=ca_pem_file, ca_path=ca_path)
              elif isinstance(self.x509_cert, X509):
                  signing_cert = self.x509_cert
              else:
                  signing_cert = load_certificate(FILETYPE_PEM, add_pem_header(self.x509_cert))
  
              if cert_subject_name and signing_cert.get_subject().commonName != cert_subject_name:
                  raise InvalidSignature("Certificate subject common name mismatch")
  
              signature_digest_method = self._get_signature_digest_method(signature_alg).name
              try:
                  verify(signing_cert, raw_signature, signed_info_c14n, signature_digest_method)
              except OpenSSLCryptoError as e:
                  try:
                      lib, func, reason = e.args[0][0]
                  except Exception:
                      reason = e
                  raise InvalidSignature("Signature verification failed: {}".format(reason))
              # TODO: CN verification goes here
              # TODO: require one of the following to be set: either x509_cert or (ca_pem_file or ca_path) or common_name
              # Use ssl.match_hostname or code from it to perform match
          elif "hmac-sha" in signature_alg:
              if self.hmac_key is None:
                  raise InvalidInput('Parameter "hmac_key" is required when verifying a HMAC signature')
  
              from cryptography.hazmat.primitives.hmac import HMAC
              signer = HMAC(key=ensure_bytes(self.hmac_key),
                            algorithm=self._get_hmac_digest_method(signature_alg),
                            backend=default_backend())
              signer.update(signed_info_c14n)
              if raw_signature != signer.finalize():
                  raise InvalidSignature("Signature mismatch (HMAC)")
          else:
              key_value = signature.find("ds:KeyInfo/ds:KeyValue", namespaces=namespaces)
              if key_value is None:
                  raise InvalidInput("Expected to find either KeyValue or X509Data XML element in KeyInfo")
  
              self._verify_signature_with_pubkey(signed_info_c14n, raw_signature, key_value, signature_alg)
  
          verify_results = []
          for reference in self._findall(signed_info, "Reference"):
              transforms = self._find(reference, "Transforms", require=False)
              digest_algorithm = self._find(reference, "DigestMethod").get("Algorithm")
              digest_value = self._find(reference, "DigestValue")
              payload = self._resolve_reference(root, reference, uri_resolver=uri_resolver)
              payload_c14n = self._apply_transforms(payload, transforms, signature_ref, c14n_algorithm)
              if digest_value.text != self._get_digest(payload_c14n, self._get_digest_method(digest_algorithm)):
                  raise InvalidDigest("Digest mismatch for reference {}".format(len(verify_results)))
  
              # We return the signed XML (and only that) to ensure no access to unsigned data happens
              try:
                  payload_c14n_xml = fromstring(payload_c14n)
              except etree.XMLSyntaxError:
                  payload_c14n_xml = None
              verify_results.append(VerifyResult(payload_c14n, payload_c14n_xml, signature))
  
          if type(expect_references) is int and len(verify_results) != expect_references:
              msg = "Expected to find {} references, but found {}"
              raise InvalidSignature(msg.format(expect_references, len(verify_results)))
  
          return verify_results if expect_references > 1 else verify_results[0]