Pkcs7SignedDataBuilder::Pkcs7SignedDataBuilder(MessageDigest::Algorithm mesDigAlgorithm,
			Certificate &cert, PrivateKey &privKey, bool attached)
		throw (Pkcs7Exception)
{
	int rc;
	PKCS7_SIGNER_INFO *si;
	PKCS7_set_type(this->pkcs7, NID_pkcs7_signed);
	PKCS7_content_new(this->pkcs7, NID_pkcs7_data);
	if (!attached)
	{
		PKCS7_set_detached(this->pkcs7, 1);
	}
	si = PKCS7_add_signature(this->pkcs7, cert.getX509(), privKey.getEvpPkey(), MessageDigest::getMessageDigest(mesDigAlgorithm));
	if (!si)
	{
		PKCS7_free(this->pkcs7);
		this->pkcs7 = NULL;
		throw Pkcs7Exception(Pkcs7Exception::ADDING_SIGNER, "Pkcs7SignedDataBuilder::Pkcs7SignedDataBuilder", true);
	}
	rc = PKCS7_add_certificate(this->pkcs7, cert.getX509());
	if (!rc)
	{
		PKCS7_free(this->pkcs7);
		this->pkcs7 = NULL;
		throw Pkcs7Exception(Pkcs7Exception::ADDING_CERTIFICATE, "Pkcs7SignedDataBuilder::Pkcs7SignedDataBuilder", true);
	}
	this->state = Pkcs7Builder::INIT;
}
static VALUE
ossl_pkcs7_set_detached(VALUE self, VALUE flag)
{
    PKCS7 *p7;

    GetPKCS7(self, p7);
    if(flag != Qtrue && flag != Qfalse)
	ossl_raise(ePKCS7Error, "must specify a boolean");
    if(!PKCS7_set_detached(p7, flag == Qtrue ? 1 : 0))
	ossl_raise(ePKCS7Error, NULL);

    return flag;
}
void Pkcs7SignedDataBuilder::init(MessageDigest::Algorithm mesDigAlgorithm, Certificate &cert,
			PrivateKey &privKey, bool attached)
	throw (Pkcs7Exception)
{
	int rc;
	if (this->state != Pkcs7Builder::NO_INIT)
	{
		PKCS7_free(this->pkcs7);
		this->pkcs7 = NULL;
		if (this->state == Pkcs7Builder::UPDATE)
		{
			BIO_free(this->p7bio);
			this->p7bio = NULL;
		}
	}
	this->pkcs7 = PKCS7_new();
	PKCS7_set_type(this->pkcs7, NID_pkcs7_signed);
	if (!attached)
	{
		PKCS7_set_detached(this->pkcs7, 1);
	}
	if (!PKCS7_add_signature(this->pkcs7, cert.getX509(), privKey.getEvpPkey(), MessageDigest::getMessageDigest(mesDigAlgorithm)))
	{
		PKCS7_free(this->pkcs7);
		this->pkcs7 = NULL;
		throw Pkcs7Exception(Pkcs7Exception::ADDING_SIGNER, "Pkcs7SignedDataBuilder::Pkcs7SignedDataBuilder", true);
	}
	rc = PKCS7_add_certificate(this->pkcs7, cert.getX509());
	if (!rc)//inversor adicionado (martin 28/11/07)
	{
		PKCS7_free(this->pkcs7);
		this->pkcs7 = NULL;
		throw Pkcs7Exception(Pkcs7Exception::ADDING_CERTIFICATE, "Pkcs7SignedDataBuilder::Pkcs7SignedDataBuilder", true);
	}
	this->state = Pkcs7Builder::INIT;
}
示例#4
0
/* will return 0, 1, 3, or 99 */
static int
_pkgtrans(char *device1, char *device2, char **pkg, int options,
    keystore_handle_t keystore, char *keystore_alias)
{
	BIO			*p7_bio = NULL;
	EVP_PKEY		*privkey = NULL;
	PKCS7			*sec_pkcs7 = NULL;
	PKCS7_SIGNER_INFO	*sec_signerinfo = NULL;
	PKG_ERR			*err;
	STACK_OF(X509)		*cacerts = NULL;
	STACK_OF(X509)		*clcerts = NULL;
	STACK_OF(X509)		*sec_chain = NULL;
	X509			*pubcert = NULL;
	boolean_t		making_sig = B_FALSE;
	char			*src, *dst;
	int			errflg, i, n;
	struct			dm_buf *hdr;

	making_sig = (keystore != NULL) ? B_TRUE : B_FALSE;

	if (making_sig) {

		/* new error object */
		err = pkgerr_new();

		/* find matching cert and key */
		if (find_key_cert_pair(err, keystore,
		    keystore_alias, &privkey, &pubcert) != 0) {
			pkgerr(err);
			pkgerr_free(err);
			return (1);
		}

		/* get CA certificates */
		if (find_ca_certs(err, keystore, &cacerts) != 0) {
			pkgerr(err);
			pkgerr_free(err);
			return (1);
		}

		/* get CL (aka "chain") certificates */
		if (find_cl_certs(err, keystore, &clcerts) != 0) {
			pkgerr(err);
			pkgerr_free(err);
			return (1);
		}

		/* initialize PKCS7 object to be filled in later */
		sec_pkcs7 = PKCS7_new();
		(void) PKCS7_set_type(sec_pkcs7, NID_pkcs7_signed);
		sec_signerinfo = PKCS7_add_signature(sec_pkcs7,
		    pubcert, privkey, EVP_sha1());

		if (sec_signerinfo == NULL) {
			progerr(gettext(ERR_SEC), keystore_alias);
			ERR_print_errors_fp(stderr);
			pkgerr_free(err);
			return (1);
		}

		/* add signer cert into signature */
		(void) PKCS7_add_certificate(sec_pkcs7, pubcert);

		/* attempt to resolve cert chain starting at the signer cert */
		if (get_cert_chain(err, pubcert, clcerts, cacerts,
		    &sec_chain) != 0) {
			pkgerr(err);
			pkgerr_free(err);
			return (1);
		}

		/*
		 * add the verification chain of certs into the signature.
		 * The first cert is the user cert, which we don't need,
		 * since it's baked in already, so skip it
		 */
		for (i = 1; i < sk_X509_num(sec_chain); i++) {
			(void) PKCS7_add_certificate(sec_pkcs7,
			    sk_X509_value(sec_chain, i));
		}

		pkgerr_free(err);
		err = NULL;
	}

	if (signal_received > 0) {
		return (1);
	}

	/* transfer spool to appropriate device */
	if (devtype(device1, &srcdev)) {
		progerr(pkg_gt(ERR_TRANSFER));
		logerr(pkg_gt(MSG_BADDEV), device1);
		return (1);
	}
	srcdev.rdonly++;

	/* check for datastream */
	ids_name = NULL;
	if (srcdev.bdevice) {
		if (n = _getvol(srcdev.bdevice, NULL, NULL,
		    pkg_gt("Insert %v into %p."), srcdev.norewind)) {
			cleanup();
			if (n == 3)
				return (3);
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_GETVOL));
			return (1);
		}
		if (ds_readbuf(srcdev.cdevice))
			ids_name = srcdev.cdevice;
	}

	if (srcdev.cdevice && !srcdev.bdevice)
		ids_name = srcdev.cdevice;
	else if (srcdev.pathname) {
		ids_name = srcdev.pathname;
		if (access(ids_name, 0) == -1) {
			progerr(ERR_TRANSFER);
			logerr(pkg_gt(MSG_GETVOL));
			return (1);
		}
	}

	if (!ids_name && device2 == (char *)0) {
		if (n = pkgmount(&srcdev, NULL, 1, 0, 0)) {
			cleanup();
			return (n);
		}
		if (srcdev.mount && *srcdev.mount)
			pkgdir = strdup(srcdev.mount);
		return (0);
	}

	if (ids_name && device2 == (char *)0) {
		tmppath = tmpnam(NULL);
		tmppath = strdup(tmppath);
		if (tmppath == NULL) {
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_MEM));
			return (1);
		}
		if (mkdir(tmppath, 0755)) {
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_MKDIR), tmppath);
			return (1);
		}
		device2 = tmppath;
	}

	if (devtype(device2, &dstdev)) {
		progerr(pkg_gt(ERR_TRANSFER));
		logerr(pkg_gt(MSG_BADDEV), device2);
		return (1);
	}

	if ((srcdev.cdevice && dstdev.cdevice) &&
	    strcmp(srcdev.cdevice, dstdev.cdevice) == 0) {
		progerr(pkg_gt(ERR_TRANSFER));
		logerr(pkg_gt(MSG_SAMEDEV));
		return (1);
	}

	ods_name = NULL;
	if (dstdev.cdevice && !dstdev.bdevice || dstdev.pathname)
		options |= PT_ODTSTREAM;

	if (options & PT_ODTSTREAM) {
		if (!((ods_name = dstdev.cdevice) != NULL ||
		    (ods_name = dstdev.pathname) != NULL)) {
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_BADDEV), device2);
			return (1);
		}
		if (ids_name) {
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_TWODSTREAM));
			return (1);
		}
	} else {
		/*
		 * output device isn't a stream.  If we're making a signed
		 * package, then fail, since we can't make signed,
		 * non-stream pkgs
		 */
		if (making_sig) {
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(ERR_CANTSIGN));
			return (1);
		}
	}

	if ((srcdev.dirname && dstdev.dirname) &&
	    strcmp(srcdev.dirname, dstdev.dirname) == 0) {
		progerr(pkg_gt(ERR_TRANSFER));
		logerr(pkg_gt(MSG_SAMEDEV));
		return (1);
	}

	if ((srcdev.pathname && dstdev.pathname) &&
	    strcmp(srcdev.pathname, dstdev.pathname) == 0) {
		progerr(pkg_gt(ERR_TRANSFER));
		logerr(pkg_gt(MSG_SAMEDEV));
		return (1);
	}

	if (signal_received > 0) {
		return (1);
	}

	if (ids_name) {
		if (srcdev.cdevice && !srcdev.bdevice &&
		(n = _getvol(srcdev.cdevice, NULL, NULL, NULL,
		    srcdev.norewind))) {
			cleanup();
			if (n == 3)
				return (3);
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_GETVOL));
			return (1);
		}
		if (srcdev.dirname = tmpnam(NULL))
			tmpdir = srcdev.dirname = strdup(srcdev.dirname);

		if ((srcdev.dirname == NULL) || mkdir(srcdev.dirname, 0755) ||
		    chdir(srcdev.dirname)) {
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_NOTEMP), srcdev.dirname);
			cleanup();
			return (1);
		}
		if (ds_init(ids_name, pkg, srcdev.norewind)) {
			cleanup();
			return (1);
		}
	} else if (srcdev.mount) {
		if (n = pkgmount(&srcdev, NULL, 1, 0, 0)) {
			cleanup();
			return (n);
		}
	}

	src = srcdev.dirname;
	dst = dstdev.dirname;

	if (chdir(src)) {
		progerr(pkg_gt(ERR_TRANSFER));
		logerr(pkg_gt(MSG_CHDIR), src);
		cleanup();
		return (1);
	}

	if (signal_received > 0) {
		return (1);
	}

	xpkg = pkg = gpkglist(src, pkg, NULL);
	if (!pkg) {
		progerr(pkg_gt(ERR_TRANSFER));
		logerr(pkg_gt(MSG_NOPKGS), src);
		cleanup();
		return (1);
	}

	for (nxpkg = 0; pkg[nxpkg]; /* void */) {
		nxpkg++; /* count */
	}

	if (ids_name) {
		ds_order(pkg); /* order requests */
	}

	if (signal_received > 0) {
		return (1);
	}

	if (options & PT_ODTSTREAM) {
		char line[128];

		if (!dstdev.pathname &&
		    (n = _getvol(ods_name, NULL, DM_FORMAT, NULL,
		    dstdev.norewind))) {
			cleanup();
			if (n == 3)
				return (3);
			progerr(pkg_gt(ERR_TRANSFER));
			logerr(pkg_gt(MSG_GETVOL));
			return (1);
		}
		if ((hdr = genheader(src, pkg)) == NULL) {
			cleanup();
			return (1);
		}
		if (making_sig) {
			/* start up signature data stream */
			(void) PKCS7_content_new(sec_pkcs7, NID_pkcs7_data);
			(void) PKCS7_set_detached(sec_pkcs7, 1);
			p7_bio = PKCS7_dataInit(sec_pkcs7, NULL);

			/*
			 * Here we generate all the data that will go into
			 * the package, and send it through the signature
			 * generator, essentially calculating the signature
			 * of the entire package so we can place it in the
			 * header.  Otherwise we'd have to place it at the end
			 * of the pkg, which would break the ABI
			 */
			if (!(options & PT_SILENT)) {
				(void) fprintf(stderr, pkg_gt(MSG_SIGNING),
				    get_subject_display_name(pubcert));
			}
			if (dump_hdr_and_pkgs(p7_bio, hdr, pkg) != 0) {
			    progerr(gettext(ERR_NOGEN));
			    logerr(pkg_gt(MSG_GETVOL));
			    cleanup();
			    return (1);

			}

			BIO_flush(p7_bio);

			/*
			 * now generate PKCS7 signature
			 */
			if (!PKCS7_dataFinal(sec_pkcs7, p7_bio)) {
			    progerr(gettext(ERR_NOGEN));
			    logerr(pkg_gt(MSG_GETVOL));
			    cleanup();
			    return (1);
			}

			(void) BIO_free(p7_bio);
		}

		/* write out header to stream, which includes signature */
		if (wdsheader(hdr, src, ods_name, pkg, sec_pkcs7)) {
			cleanup();
			return (1);
		}

		if (sec_pkcs7 != NULL) {
			/* nuke in-memory signature for safety */
			PKCS7_free(sec_pkcs7);
			sec_pkcs7 = NULL;
		}

		ds_volno = 1; /* number of volumes in datastream */
		pinput = hdrbuf.text_buffer;
		/* skip past first line in header */
		(void) mgets(line, 128);
	}

	if (signal_received > 0) {
		return (1);
	}

	errflg = 0;

	for (i = 0; pkg[i]; i++) {

		if (signal_received > 0) {
			return (1);
		}

		if (!(options & PT_ODTSTREAM) && dstdev.mount) {
			if (n = pkgmount(&dstdev, NULL, 0, 0, 1)) {
				cleanup();
				return (n);
			}
		}
		if (errflg = pkgxfer(pkg[i], options)) {
			pkg[i] = NULL;
			if ((options & PT_ODTSTREAM) || (errflg != 2))
				break;
		} else if (strcmp(dstinst, pkg[i]))
			pkg[i] = strdup(dstinst);
	}

	if (!(options & PT_ODTSTREAM) && dst) {
		pkgdir = strdup(dst);
	}

	/*
	 * No cleanup of temporary directories created in this
	 * function is done here. The calling function must do
	 * the cleanup.
	 */

	return (signal_received > 0 ? 1 : errflg);
}
示例#5
0
// Write signed variable
EFI_STATUS SetSignedVariable(IN CHAR16   *DatabaseName,
                             IN EFI_GUID *DatabaseGuid,
                             IN UINT32    Attributes,
                             IN VOID     *Database,
                             IN UINTN     DatabaseSize)
{
  EFI_STATUS                     Status;
  EFI_VARIABLE_AUTHENTICATION_2 *Authentication;
  UINTN                          Size, NameLen;
  UINTN                          DataSize = 0;
  EFI_TIME                       Timestamp;
  VOID                          *Data = NULL;
  BIO                           *BioData = NULL;
  PKCS7                         *p7;
  X509                          *Certificate = NULL;
  EVP_PKEY                      *PrivateKey = NULL;
  const EVP_MD                  *md;
  // Check parameters
  if ((DatabaseName == NULL) || (DatabaseGuid == NULL)) {
    return EFI_INVALID_PARAMETER;
  }
  DBG("Setting secure variable: %g %s 0x%X (0x%X)\n", DatabaseGuid, DatabaseName, Database, DatabaseSize);
  NameLen = StrLen(DatabaseName);
  if (NameLen == 0) {
    return EFI_INVALID_PARAMETER;
  }
  // Get the current time
  DBG("Getting timestamp ...\n");
  Status = GetUTCTime(&Timestamp);
  if (EFI_ERROR(Status)) {
    return Status;
  }
  DBG("Timestamp: %t\n", Timestamp);
  // In user mode we need to sign the database with exchange key
  if (!gSettings.SecureBootSetupMode) {
    // Initialize the cyphers and digests
    ERR_load_crypto_strings();
    OpenSSL_add_all_digests();
    OpenSSL_add_all_ciphers();
    // Create signing certificate
    BioData = BIO_new_mem_buf((void *)gSecureBootExchangeKey, sizeof(gSecureBootExchangeKey));
    if (BioData == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    Certificate = PEM_read_bio_X509(BioData, NULL, NULL, NULL);
    BIO_free(BioData);
    if (Certificate == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    // Create signing private key
    BioData = BIO_new_mem_buf((void *)gSecureBootExchangePrivateKey, sizeof(gSecureBootExchangePrivateKey));
    if (BioData == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    PrivateKey = PEM_read_bio_PrivateKey(BioData, NULL, NULL, NULL);
    BIO_free(BioData);
    if (PrivateKey == NULL) {
      X509_free(Certificate);
      return EFI_OUT_OF_RESOURCES;
    }
    // Do the actual signing process
    BioData = BIO_new(BIO_s_mem());
    BIO_write(BioData, DatabaseName, (int)StrLen(DatabaseName));
    BIO_write(BioData, DatabaseGuid, sizeof(EFI_GUID));
    BIO_write(BioData, &Attributes, sizeof(UINT32));
    BIO_write(BioData, &Timestamp, sizeof(EFI_TIME));
    BIO_write(BioData, Database, (int)DatabaseSize);

    md = EVP_get_digestbyname("SHA256");

    p7 = PKCS7_new();
    PKCS7_set_type(p7, NID_pkcs7_signed);

    PKCS7_content_new(p7, NID_pkcs7_data);

    PKCS7_sign_add_signer(p7, Certificate, PrivateKey, md, PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOSMIMECAP);

    PKCS7_set_detached(p7, 1);

    PKCS7_final(p7, BioData, PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOSMIMECAP);
    
    X509_free(Certificate);
    EVP_PKEY_free(PrivateKey);

    DataSize = i2d_PKCS7(p7, NULL);
    Data = AllocateZeroPool(DataSize);

    i2d_PKCS7(p7, (unsigned char **)&Data);

    PKCS7_free(p7);

    // Set the authentication buffer size
    Size = sizeof(EFI_TIME) + sizeof(EFI_GUID) + sizeof(UINT32) + sizeof(UINT16) + sizeof(UINT16) + DataSize;
  } else {
    // In setup mode we don't need to sign, so just set the database
    DBG("In setup mode, not signing ...\n");
    Size = sizeof(EFI_TIME) + sizeof(EFI_GUID) + sizeof(UINT32) + sizeof(UINT16) + sizeof(UINT16) + DatabaseSize;
  }
  // Create the authentication buffer
  DBG("Creating authentication ...\n");
  Authentication = (EFI_VARIABLE_AUTHENTICATION_2 *)AllocateZeroPool(Size);
  if (Authentication == NULL) {
    if (Data != NULL) {
      FreePool(Data);
    }
    return EFI_OUT_OF_RESOURCES;
  }
  // Set the certificate elements
  CopyMem(&(Authentication->TimeStamp), &Timestamp, sizeof(EFI_TIME));
  Authentication->AuthInfo.Hdr.dwLength         = (UINT32)(sizeof(EFI_GUID) + sizeof(UINT32) + sizeof(UINT16) + sizeof(UINT16) + DataSize);
  Authentication->AuthInfo.Hdr.wRevision        = 0x0200;
  Authentication->AuthInfo.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
  CopyMem(&(Authentication->AuthInfo.CertType), &gEfiCertPkcs7Guid, sizeof(EFI_GUID));
  // Copy the data into the authentication
  if (Data != NULL) {
    CopyMem(((UINT8 *)Authentication) + sizeof(EFI_TIME) + sizeof(EFI_GUID) + sizeof(UINT32) + sizeof(UINT16) + sizeof(UINT16), Data, DataSize);
    FreePool(Data);
  } else {
    CopyMem(((UINT8 *)Authentication) + sizeof(EFI_TIME) + sizeof(EFI_GUID) + sizeof(UINT32) + sizeof(UINT16) + sizeof(UINT16), Database, DatabaseSize); //Payload, PayloadSize);
  }
  DBG("Writing secure variable 0x%X (0x%X) ...\n", Authentication, Size);
  // Write the database variable
  Status = gRT->SetVariable(DatabaseName, DatabaseGuid, SET_DATABASE_ATTRIBUTES, Size, Authentication);
  // Cleanup the authentication buffer
  FreePool(Authentication);
  return Status;
}
示例#6
0
static int add_auth_descriptor(varsign_context *ctx, unsigned char dont_sign)
{
	EFI_VARIABLE_AUTHENTICATION_2 *auth;
	int rc=0, len=0, flags=0;
	EFI_TIME timestamp;
	const EVP_MD *md;
	BIO *data_bio = NULL;
	PKCS7 *p7;
    UINT8 *signpkg;

	if (set_timestamp(&timestamp))
		return -1;

	/* create a BIO for our variable data, containing:
	 *  * Variablename (not including trailing nul)
	 *  * VendorGUID
	 *  * Attributes
	 *  * TimeStamp
	 *  * Data
	 */
    if (dont_sign == 0)
    {
        data_bio = BIO_new(BIO_s_mem());

        BIO_write(data_bio, ctx->var_name, ctx->var_name_bytes);
        BIO_write(data_bio, &ctx->var_guid, sizeof(ctx->var_guid));
        BIO_write(data_bio, &ctx->var_attrs, sizeof(ctx->var_attrs));
        BIO_write(data_bio, &timestamp, sizeof(timestamp));
        BIO_write(data_bio, ctx->data, ctx->data_len);
        
        md = EVP_get_digestbyname("SHA256");
        
        p7 = PKCS7_new();

        flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOSMIMECAP;;

        PKCS7_set_type(p7, NID_pkcs7_signed);
        
        PKCS7_content_new(p7, NID_pkcs7_data);
        
        PKCS7_sign_add_signer(p7, ctx->cert, ctx->key, md, flags);
        
        PKCS7_set_detached(p7, 1);
        
        rc = PKCS7_final(p7, data_bio, flags);

        if (!rc) {
            fprintf(stderr, "Error signing variable data\n");

            ERR_print_errors_fp(stderr);

            BIO_free_all(data_bio);

            return -1;
        }
        
        len = i2d_PKCS7(p7, NULL);
    } else {
        len = 0;
    }

	/* set up our auth descriptor */
	auth = talloc_size(ctx, len + offsetof (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo.CertData));

	auth->TimeStamp = timestamp;
	auth->AuthInfo.Hdr.dwLength = len + offsetof (WIN_CERTIFICATE_UEFI_GUID, CertData);
	auth->AuthInfo.Hdr.wRevision = 0x0200;
	auth->AuthInfo.Hdr.wCertificateType = 0x0EF1;
	auth->AuthInfo.CertType = cert_pkcs7_guid;

    if (dont_sign == 0)
    {
        signpkg = auth->AuthInfo.CertData;
        i2d_PKCS7(p7, &signpkg);
    }

	ctx->auth_descriptor = auth;
	ctx->auth_descriptor_len = len + offsetof (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo.CertData);

	BIO_free_all(data_bio);

	return 0;
}
示例#7
0
static LUA_FUNCTION(openssl_pkcs7_sign_digest)
{
  PKCS7 *p7 = CHECK_OBJECT(1, PKCS7, "openssl.pkcs7");
  size_t l;
  const char* data = luaL_checklstring(L, 2, &l);
  long flags = luaL_optint(L, 3, 0);
  int hash = lua_isnoneornil(L, 4) ? 0 : lua_toboolean(L, 4);

  int ret = 0;
  int i, j;

  const EVP_MD* md;
  PKCS7_SIGNER_INFO *si;
  EVP_MD_CTX mdc;
  STACK_OF(X509_ATTRIBUTE) *sk;
  STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL;
  ASN1_OCTET_STRING *os = NULL;

  if (p7->d.ptr == NULL)
  {
    luaL_error(L, "pkcs7 without content");
    return 0;
  }

  flags |= PKCS7_DETACHED;
  PKCS7_set_detached(p7, 1);

  EVP_MD_CTX_init(&mdc);
  i = OBJ_obj2nid(p7->type);
  p7->state = PKCS7_S_HEADER;

  switch (i)
  {
  case NID_pkcs7_data:
    os = p7->d.data;
    break;
  case NID_pkcs7_signedAndEnveloped:
    /* XXXXXXXXXXXXXXXX */
    si_sk = p7->d.signed_and_enveloped->signer_info;
    os = p7->d.signed_and_enveloped->enc_data->enc_data;
    if (!os)
    {
      os = M_ASN1_OCTET_STRING_new();
      if (!os)
      {
        PKCS7err(PKCS7_F_PKCS7_DATAFINAL, ERR_R_MALLOC_FAILURE);
        goto err;
      }
      p7->d.signed_and_enveloped->enc_data->enc_data = os;
    }
    break;
  case NID_pkcs7_enveloped:
    /* XXXXXXXXXXXXXXXX */
    os = p7->d.enveloped->enc_data->enc_data;
    if (!os)
    {
      os = M_ASN1_OCTET_STRING_new();
      if (!os)
      {
        PKCS7err(PKCS7_F_PKCS7_DATAFINAL, ERR_R_MALLOC_FAILURE);
        goto err;
      }
      p7->d.enveloped->enc_data->enc_data = os;
    }
    break;
  case NID_pkcs7_signed:
    si_sk = p7->d.sign->signer_info;
    os = PKCS7_get_octet_string(p7->d.sign->contents);
    /* If detached data then the content is excluded */
    if (PKCS7_type_is_data(p7->d.sign->contents) && p7->detached)
    {
      M_ASN1_OCTET_STRING_free(os);
      os = NULL;
      p7->d.sign->contents->d.data = NULL;
    }
    break;

  case NID_pkcs7_digest:
    os = PKCS7_get_octet_string(p7->d.digest->contents);
    /* If detached data then the content is excluded */
    if (PKCS7_type_is_data(p7->d.digest->contents) && p7->detached)
    {
      M_ASN1_OCTET_STRING_free(os);
      os = NULL;
      p7->d.digest->contents->d.data = NULL;
    }
    break;

  default:
    PKCS7err(PKCS7_F_PKCS7_DATAFINAL, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
    goto err;
  }

  if (si_sk != NULL)
  {
    for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(si_sk); i++)
    {
      si = sk_PKCS7_SIGNER_INFO_value(si_sk, i);
      if (si->pkey == NULL)
        continue;
      j = OBJ_obj2nid(si->digest_alg->algorithm);
      md = EVP_get_digestbynid(j);
      EVP_DigestInit_ex(&mdc, md, NULL);

      if (hash)
      {
        if (l == (size_t) mdc.digest->ctx_size)
        {
          memcpy(mdc.md_data, data, l);
        }
        else
        {
          EVP_MD_CTX_cleanup(&mdc);
          luaL_argerror(L, 2, "data with wrong length");
        }
      }
      else
        EVP_DigestUpdate(&mdc, data, l);

      sk = si->auth_attr;

      /*
      * If there are attributes, we add the digest attribute and only
      * sign the attributes
      */
      if (sk_X509_ATTRIBUTE_num(sk) > 0)
      {
        if (!do_pkcs7_signed_attrib(si, &mdc))
          goto err;
      }
      else
      {
        unsigned char *abuf = NULL;
        unsigned int abuflen;
        abuflen = EVP_PKEY_size(si->pkey);
        abuf = OPENSSL_malloc(abuflen);
        if (!abuf)
          goto err;

        if (!EVP_SignFinal(&mdc, abuf, &abuflen, si->pkey))
        {
          PKCS7err(PKCS7_F_PKCS7_DATAFINAL, ERR_R_EVP_LIB);
          goto err;
        }
        ASN1_STRING_set0(si->enc_digest, abuf, abuflen);
      }
    }
  }
  else if (i == NID_pkcs7_digest)
  {
    unsigned char md_data[EVP_MAX_MD_SIZE];
    unsigned int md_len;
    md = EVP_get_digestbynid(OBJ_obj2nid(p7->d.digest->md->algorithm));
    EVP_DigestInit_ex(&mdc, md, NULL);
    if (l == (size_t) mdc.digest->ctx_size)
    {
      memcpy(mdc.md_data, data, l);
    }
    else
    {
      EVP_MD_CTX_cleanup(&mdc);
      luaL_error(L, "data with wrong data");
    }
    if (!EVP_DigestFinal_ex(&mdc, md_data, &md_len))
      goto err;
    M_ASN1_OCTET_STRING_set(p7->d.digest->digest, md_data, md_len);
  }

  if (!PKCS7_is_detached(p7))
  {
    /*
    * NOTE(emilia): I think we only reach os == NULL here because detached
    * digested data support is broken.
    */
    if (os == NULL)
      goto err;
    if (!(os->flags & ASN1_STRING_FLAG_NDEF))
    {
      char *cont = memdup(data, l);
      long contlen = l;
      ASN1_STRING_set0(os, (unsigned char *) cont, contlen);
    }
  }

  ret = 1;
err:
  EVP_MD_CTX_cleanup(&mdc);
  return openssl_pushresult(L, ret);
}