docs: def sign(self, data, key=None, passphrase=None, cert=None, reference_uri=None, key_name=None, key_info=None,
               id_attribute=None):
          """
          Sign the data and return the root element of the resulting XML tree.
  
          :param data: Data to sign
          :type data: String, file-like object, or XML ElementTree Element API compatible object
          :param key:
              Key to be used for signing. When signing with a certificate or RSA/DSA/ECDSA key, this can be a string
              containing a PEM-formatted key, or a :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`,
              :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or
              :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object. When signing with a
              HMAC, this should be a string containing the shared secret.
          :type key:
              string, :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`,
              :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or
              :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object
          :param passphrase: Passphrase to use to decrypt the key, if any.
          :type passphrase: string
          :param cert:
              X.509 certificate to use for signing. This should be a string containing a PEM-formatted certificate, or an
              array of strings or OpenSSL.crypto.X509 objects containing the certificate and a chain of intermediate
              certificates.
          :type cert: string, array of strings, or array of OpenSSL.crypto.X509 objects
          :param reference_uri:
              Custom reference URI or list of reference URIs to incorporate into the signature. When ``method`` is set to
              ``detached`` or ``enveloped``, reference URIs are set to this value and only the referenced elements are
              signed.
          :type reference_uri: string or list
          :param key_name: Add a KeyName element in the KeyInfo element that may be used by the signer to communicate a
              key identifier to the recipient. Typically, KeyName contains an identifier related to the key pair used to
              sign the message.
          :type key_name: string
          :param key_info: A custom KeyInfo element to insert in the signature. Use this to supply
              ``<wsse:SecurityTokenReference>`` or other custom key references.
          :type key_info: :py:class:`lxml.etree.Element`
          :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
  
          :returns:
              A :py:class:`lxml.etree.Element` object representing the root of the XML tree containing the signature and
              the payload data.
  
          To specify the location of an enveloped signature within **data**, insert a
          ``<ds:Signature Id="placeholder"></ds:Signature>`` element in **data** (where
          "ds" is the "http://www.w3.org/2000/09/xmldsig#" namespace). This element will
          be replaced by the generated signature, and excised when generating the digest.
          """
          if id_attribute is not None:
              self.id_attributes = (id_attribute, )
  
          if isinstance(cert, (str, bytes)):
              cert_chain = list(iterate_pem(cert))
          else:
              cert_chain = cert
  
          if isinstance(reference_uri, (str, bytes)):
              reference_uris = [reference_uri]
          else:
              reference_uris = reference_uri
  
          sig_root, doc_root, c14n_inputs, reference_uris = self._unpack(data, reference_uris)
          signed_info_element, signature_value_element = self._build_sig(sig_root, reference_uris, c14n_inputs)
  
          if key is None:
              raise InvalidInput('Parameter "key" is required')
  
          signed_info_c14n = self._c14n(signed_info_element, algorithm=self.c14n_alg)
          if self.sign_alg.startswith("hmac-"):
              from cryptography.hazmat.primitives.hmac import HMAC
              signer = HMAC(key=key,
                            algorithm=self._get_hmac_digest_method_by_tag(self.sign_alg),
                            backend=default_backend())
              signer.update(signed_info_c14n)
              signature_value_element.text = ensure_str(b64encode(signer.finalize()))
              sig_root.append(signature_value_element)
          elif any(self.sign_alg.startswith(i) for i in ["dsa-", "rsa-", "ecdsa-"]):
              if isinstance(key, (str, bytes)):
                  from cryptography.hazmat.primitives.serialization import load_pem_private_key
                  key = load_pem_private_key(key, password=passphrase, backend=default_backend())
  
              hash_alg = self._get_signature_digest_method_by_tag(self.sign_alg)
              if self.sign_alg.startswith("dsa-"):
                  signature = key.sign(signed_info_c14n, algorithm=hash_alg)
              elif self.sign_alg.startswith("ecdsa-"):
                  signature = key.sign(signed_info_c14n, signature_algorithm=ec.ECDSA(algorithm=hash_alg))
              elif self.sign_alg.startswith("rsa-"):
                  signature = key.sign(signed_info_c14n, padding=PKCS1v15(), algorithm=hash_alg)
              else:
                  raise NotImplementedError()
              if self.sign_alg.startswith("dsa-"):
                  # Note: The output of the DSA signer is a DER-encoded ASN.1 sequence of two DER integers.
                  from asn1crypto.algos import DSASignature
                  decoded_signature = DSASignature.load(signature).native
                  r = decoded_signature['r']
                  s = decoded_signature['s']
                  signature = long_to_bytes(r).rjust(32, b"\0") + long_to_bytes(s).rjust(32, b"\0")
  
              signature_value_element.text = ensure_str(b64encode(signature))
  
              if key_info is None:
                  key_info = SubElement(sig_root, ds_tag("KeyInfo"))
                  if key_name is not None:
                      keyname = SubElement(key_info, ds_tag("KeyName"))
                      keyname.text = key_name
  
                  if cert_chain is None:
                      self._serialize_key_value(key, key_info)
                  else:
                      x509_data = SubElement(key_info, ds_tag("X509Data"))
                      for cert in cert_chain:
                          x509_certificate = SubElement(x509_data, ds_tag("X509Certificate"))
                          if isinstance(cert, (str, bytes)):
                              x509_certificate.text = strip_pem_header(cert)
                          else:
                              from OpenSSL.crypto import dump_certificate, FILETYPE_PEM
                              x509_certificate.text = strip_pem_header(dump_certificate(FILETYPE_PEM, cert))
              else:
                  sig_root.append(key_info)
          else:
              raise NotImplementedError()
  
          if self.method == methods.enveloping:
              for c14n_input in c14n_inputs:
                  doc_root.append(c14n_input)
          return doc_root if self.method == methods.enveloped else sig_root