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]
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