/*-
 * SPDX-License-Identifier: LicenseRef-scancode-bsd-unchanged
 *
 * Copyright (c) 2011-2013 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/stat.h>
#include <sys/param.h>

#include <fcntl.h>

#include <openssl/err.h>
#include <openssl/ssl.h>

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
#include "private/pkgsign.h"

#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
/*
 * This matches the historical usage for pkg.  Older versions sign the hex
 * encoding of the SHA256 checksum.  If we ever deprecated RSA, this can go
 * away.
 */
static EVP_MD *md_pkg_sha1;

static EVP_MD *
EVP_md_pkg_sha1(void)
{

	if (md_pkg_sha1 != NULL)
		return (md_pkg_sha1);

	md_pkg_sha1 = EVP_MD_meth_dup(EVP_sha1());
	if (md_pkg_sha1 == NULL)
		return (NULL);

	EVP_MD_meth_set_result_size(md_pkg_sha1,
	    pkg_checksum_type_size(PKG_HASH_TYPE_SHA256_HEX));
	return (md_pkg_sha1);
}
#endif	/* OPENSSL_VERSION_NUMBER >= 0x10100000L */

struct ossl_sign_ctx {
	struct pkgsign_ctx sctx;
	EVP_PKEY *key;
};

/* Grab the ossl context from a pkgsign_ctx. */
#define	OSSL_CTX(c)	((struct ossl_sign_ctx *)(c))

static int
_load_private_key(struct ossl_sign_ctx *keyinfo)
{
	FILE *fp;

	if ((fp = fopen(keyinfo->sctx.path, "re")) == NULL)
		return (EPKG_FATAL);

	keyinfo->key = PEM_read_PrivateKey(fp, 0, keyinfo->sctx.pw_cb,
	    keyinfo->sctx.path);
	if (keyinfo->key == NULL) {
		fclose(fp);
		return (EPKG_FATAL);
	}

	fclose(fp);
	return (EPKG_OK);
}

static EVP_PKEY *
_load_public_key_buf(unsigned char *cert, int certlen)
{
	EVP_PKEY *pkey;
	BIO *bp;
	char errbuf[1024];

	bp = BIO_new_mem_buf((void *)cert, certlen);
	if (bp == NULL) {
		pkg_emit_error("error allocating public key bio: %s",
		    ERR_error_string(ERR_get_error(), errbuf));
		return (NULL);
	}

	pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
	if (pkey == NULL) {
		pkg_emit_error("error reading public key: %s",
		    ERR_error_string(ERR_get_error(), errbuf));
		BIO_free(bp);
		return (NULL);
	}

	BIO_free(bp);
	return (pkey);
}

struct ossl_verify_cbdata {
	unsigned char *key;
	size_t keylen;
	unsigned char *sig;
	size_t siglen;
	bool verbose;
};

static int
ossl_verify_cert_cb(int fd, void *ud)
{
	struct ossl_verify_cbdata *cbdata = ud;
	char *sha256;
	char *hash;
	char errbuf[1024];
	EVP_PKEY *pkey = NULL;
	EVP_PKEY_CTX *ctx;
	int ret;

	sha256 = pkg_checksum_fd(fd, PKG_HASH_TYPE_SHA256_HEX);
	if (sha256 == NULL)
		return (EPKG_FATAL);

	hash = pkg_checksum_data(sha256, strlen(sha256),
	    PKG_HASH_TYPE_SHA256_RAW);
	free(sha256);

	pkey = _load_public_key_buf(cbdata->key, cbdata->keylen);
	if (pkey == NULL) {
		free(hash);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
		EVP_PKEY_free(pkey);
		free(hash);
		return (EPKG_FATAL);
	}

	ctx = EVP_PKEY_CTX_new(pkey, NULL);
	if (ctx == NULL) {
		EVP_PKEY_free(pkey);
		free(hash);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_verify_init(ctx) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		free(hash);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		free(hash);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		free(hash);
		return (EPKG_FATAL);
	}

	ret = EVP_PKEY_verify(ctx, cbdata->sig, cbdata->siglen, hash,
	    pkg_checksum_type_size(PKG_HASH_TYPE_SHA256_RAW));
	free(hash);
	if (ret <= 0 && cbdata->verbose) {
		if (ret < 0)
			pkg_dbg(PKG_DBG_VERIFY, 1, "rsa verify failed: %s",
					ERR_error_string(ERR_get_error(), errbuf));
		pkg_emit_error("signature verification failure");
	}
	if (ret <= 0) {
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		return (EPKG_FATAL);
	}

	EVP_PKEY_CTX_free(ctx);
	EVP_PKEY_free(pkey);

	return (EPKG_OK);
}

static int
ossl_verify_cert(const struct pkgsign_ctx *sctx __unused, unsigned char *key,
    size_t keylen, unsigned char *sig, size_t siglen, int fd)
{
	int ret;
	bool need_close = false;
	struct ossl_verify_cbdata cbdata;

	(void)lseek(fd, 0, SEEK_SET);

	cbdata.key = key;
	cbdata.keylen = keylen;
	cbdata.sig = sig;
	cbdata.siglen = siglen;
	cbdata.verbose = true;

	SSL_load_error_strings();
	OpenSSL_add_all_algorithms();
	OpenSSL_add_all_ciphers();

	ret = pkg_emit_sandbox_call(ossl_verify_cert_cb, fd, &cbdata);
	if (need_close)
		close(fd);

	return (ret);
}

static int
ossl_verify_cb(int fd, void *ud)
{
	struct ossl_verify_cbdata *cbdata = ud;
	char *sha256;
	char errbuf[1024];
	EVP_PKEY *pkey = NULL;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
	EVP_PKEY_CTX *ctx;
#else
	RSA *rsa;
#endif
	int ret;

	sha256 = pkg_checksum_fd(fd, PKG_HASH_TYPE_SHA256_HEX);
	if (sha256 == NULL)
		return (EPKG_FATAL);

	pkey = _load_public_key_buf(cbdata->key, cbdata->keylen);
	if (pkey == NULL) {
		free(sha256);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
		EVP_PKEY_free(pkey);
		free(sha256);
		return (EPKG_FATAL);
	}

#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
	ctx = EVP_PKEY_CTX_new(pkey, NULL);
	if (ctx == NULL) {
		EVP_PKEY_free(pkey);
		free(sha256);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_verify_init(ctx) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		free(sha256);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		free(sha256);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_md_pkg_sha1()) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(pkey);
		free(sha256);
		return (EPKG_FATAL);
	}

	ret = EVP_PKEY_verify(ctx, cbdata->sig, cbdata->siglen, sha256,
	    pkg_checksum_type_size(PKG_HASH_TYPE_SHA256_HEX));
#else
	rsa = EVP_PKEY_get1_RSA(pkey);

	ret = RSA_verify(NID_sha1, sha256,
	    pkg_checksum_type_size(PKG_HASH_TYPE_SHA256_HEX), cbdata->sig,
	    cbdata->siglen, rsa);
#endif
	free(sha256);
	if (ret <= 0) {
		if (ret < 0)
			pkg_dbg(PKG_DBG_VERIFY, 1, "%s: %s", cbdata->key,
				ERR_error_string(ERR_get_error(), errbuf));
		pkg_emit_error("%s: signature verification failure",
		    cbdata->key);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
		EVP_PKEY_CTX_free(ctx);
#else
		RSA_free(rsa);
#endif
		EVP_PKEY_free(pkey);
		return (EPKG_FATAL);
	}

#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
	EVP_PKEY_CTX_free(ctx);
#else
	RSA_free(rsa);
#endif
	EVP_PKEY_free(pkey);

	return (EPKG_OK);
}

static int
ossl_verify(const struct pkgsign_ctx *sctx __unused, const char *keypath,
    unsigned char *sig, size_t sig_len, int fd)
{
	int ret;
	bool need_close = false;
	struct ossl_verify_cbdata cbdata;
	char *key_buf;
	off_t key_len;

	if (file_to_buffer(keypath, (char**)&key_buf, &key_len) != EPKG_OK) {
		pkg_emit_errno("ossl_verify", "cannot read key");
		return (EPKG_FATAL);
	}

	(void)lseek(fd, 0, SEEK_SET);

	/*
	 * XXX Older versions of pkg write out the NUL terminator of the
	 * signature, so we shim it out here to avoid breaking compatibility.
	 * We can't do it at a lower level in the caller, because other signers
	 * may use a binary format that could legitimately contain a nul byte.
	 */
	if (sig[sig_len - 1] == '\0')
		sig_len--;

	cbdata.key = key_buf;
	cbdata.keylen = key_len;
	cbdata.sig = sig;
	cbdata.siglen = sig_len;
	cbdata.verbose = false;

	SSL_load_error_strings();
	OpenSSL_add_all_algorithms();
	OpenSSL_add_all_ciphers();

	ret = pkg_emit_sandbox_call(ossl_verify_cert_cb, fd, &cbdata);
	if (need_close)
		close(fd);
	if (ret != EPKG_OK) {
		cbdata.verbose = true;
		(void)lseek(fd, 0, SEEK_SET);
		ret = pkg_emit_sandbox_call(ossl_verify_cb, fd, &cbdata);
	}

	free(key_buf);

	return (ret);
}

int
ossl_sign_data(struct pkgsign_ctx *sctx, const unsigned char *msg, size_t msgsz,
    unsigned char **sigret, size_t *siglen)
{
	char errbuf[1024];
	struct ossl_sign_ctx *keyinfo = OSSL_CTX(sctx);
	int max_len = 0, ret;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
	EVP_PKEY_CTX *ctx;
	const EVP_MD *md;

#if OPENSSL_VERSION_NUMBER < 0x30000000L
	md = EVP_md_pkg_sha1();
#else
	md = EVP_sha256();
	char *hash;
#endif
#else
	RSA *rsa;
#endif

	if (keyinfo->key == NULL && _load_private_key(keyinfo) != EPKG_OK) {
		pkg_emit_error("can't load key from %s", keyinfo->sctx.path);
		return (EPKG_FATAL);
	}

	max_len = EVP_PKEY_size(keyinfo->key);
	*sigret = xcalloc(1, max_len + 1);

#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
	ctx = EVP_PKEY_CTX_new(keyinfo->key, NULL);
	if (ctx == NULL)
		return (EPKG_FATAL);

	if (EVP_PKEY_sign_init(ctx) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		return (EPKG_FATAL);
	}

	if (EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) {
		EVP_PKEY_CTX_free(ctx);
		return (EPKG_FATAL);
	}

	*siglen = max_len;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
	hash = pkg_checksum_data(msg, msgsz, PKG_HASH_TYPE_SHA256_RAW);
	ret = EVP_PKEY_sign(ctx, *sigret, siglen, hash,
	    EVP_MD_size(md));
	free(hash);
#else
	ret = EVP_PKEY_sign(ctx, *sigret, siglen, msg, msgsz);
#endif

#else	/* OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER */
	rsa = EVP_PKEY_get1_RSA(keyinfo->key);

	ret = RSA_sign(NID_sha1, msg, msgsz, *sigret, siglen, rsa);
#endif
	if (ret <= 0) {
		pkg_dbg(PKG_DBG_VERIFY, 1, "%s: %s", keyinfo->sctx.path,
		   ERR_error_string(ERR_get_error(), errbuf));
		pkg_emit_error("%s: signing failed", keyinfo->sctx.path);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
		EVP_PKEY_CTX_free(ctx);
#else
		RSA_free(rsa);
#endif
		return (EPKG_FATAL);
	}

#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
	assert(*siglen < INT_MAX);
	EVP_PKEY_CTX_free(ctx);
#else
	RSA_free(rsa);
#endif
	*siglen += 1;
	return (EPKG_OK);
}

int
ossl_sign(struct pkgsign_ctx *sctx, const char *path, unsigned char **sigret,
    size_t *siglen)
{
	struct ossl_sign_ctx *keyinfo = OSSL_CTX(sctx);
	char *sha256;
	int ret;

	if (access(keyinfo->sctx.path, R_OK) == -1) {
		pkg_emit_errno("access", keyinfo->sctx.path);
		return (EPKG_FATAL);
	}

	sha256 = pkg_checksum_file(path, PKG_HASH_TYPE_SHA256_HEX);
	if (sha256 == NULL)
		return (EPKG_FATAL);

#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
	ret = ossl_sign_data(sctx, sha256, strlen(sha256), sigret, siglen);
#else
	ret = ossl_sign_data(sctx, sha256,
	    pkg_checksum_type_size(PKG_HASH_TYPE_SHA256_HEX), sigret, siglen);
#endif

	free(sha256);

	return (ret);
}

static int
ossl_generate(struct pkgsign_ctx *sctx, const struct iovec *iov __unused,
    int niov __unused)
{
	char errbuf[1024];
	struct ossl_sign_ctx *keyinfo = OSSL_CTX(sctx);
	const char *path = sctx->path;
	EVP_PKEY_CTX *ctx;
	EVP_PKEY *pkey;
	FILE *fp;
	int rc;

	if (niov != 0)
		return (EPKG_FATAL);

	fp = fopen(path, "w");
	if (fp == NULL) {
		pkg_emit_errno("fopen write", path);
		return (EPKG_FATAL);
	}

	if (fchmod(fileno(fp), 0400) != 0) {
		pkg_emit_errno("fchmod", path);
		fclose(fp);
		return (EPKG_FATAL);
	}

	pkey = NULL;
	rc = EPKG_FATAL;
	ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
	if (ctx == NULL)
		goto out;

	if (EVP_PKEY_keygen_init(ctx) <= 0)
		goto out;

	if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0)
		goto out;

	if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
		goto out;

	if (PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, 0, NULL) <= 0)
		goto out;

	rc = EPKG_OK;
	if (keyinfo->key != NULL)
		EVP_PKEY_free(keyinfo->key);
	keyinfo->key = pkey;
out:
	if (rc != EPKG_OK) {
		pkg_dbg(PKG_DBG_VERIFY, 1, "%s: %s", path,
		    ERR_error_string(ERR_get_error(), errbuf));
		pkg_emit_error("%s: error loading private key", path);

		/* keyinfo claims the pkey on success for any future operations. */
		EVP_PKEY_free(pkey);
	}

	fclose(fp);
	EVP_PKEY_CTX_free(ctx);
	return (rc);
}

static int
ossl_pubkey(struct pkgsign_ctx *sctx, char **pubkey, size_t *pubkeylen)
{
	char errbuf[1024];
	struct ossl_sign_ctx *keyinfo = OSSL_CTX(sctx);
	BIO *bp;

	if (keyinfo->key == NULL && _load_private_key(keyinfo) != EPKG_OK) {
		pkg_emit_error("can't load key from %s", sctx->path);
		return (EPKG_FATAL);
	}

	bp = BIO_new(BIO_s_mem());
	if (bp == NULL) {
		pkg_emit_error("error allocating public key bio: %s",
		    ERR_error_string(ERR_get_error(), errbuf));
		return (EPKG_FATAL);
	}

	BIO_set_close(bp, BIO_NOCLOSE);

	if (PEM_write_bio_PUBKEY(bp, keyinfo->key) <= 0) {
		pkg_emit_error("error writing public key: %s",
		    ERR_error_string(ERR_get_error(), errbuf));
		BIO_free(bp);
		return (EPKG_FATAL);
	}

	*pubkeylen = BIO_get_mem_data(bp, pubkey);
	BIO_free(bp);
	return (EPKG_OK);
}

static int
ossl_new(const char *name __unused, struct pkgsign_ctx *sctx __unused)
{

	SSL_load_error_strings();

	OpenSSL_add_all_algorithms();
	OpenSSL_add_all_ciphers();

	return (0);
}

static void
ossl_free(struct pkgsign_ctx *sctx)
{
	struct ossl_sign_ctx *keyinfo = OSSL_CTX(sctx);

	if (keyinfo->key != NULL)
		EVP_PKEY_free(keyinfo->key);

	ERR_free_strings();
}

const struct pkgsign_ops pkgsign_ossl = {
	.pkgsign_ctx_size = sizeof(struct ossl_sign_ctx),
	.pkgsign_new = ossl_new,
	.pkgsign_free = ossl_free,

	.pkgsign_sign = ossl_sign,
	.pkgsign_verify = ossl_verify,
	.pkgsign_verify_cert = ossl_verify_cert,

	.pkgsign_generate = ossl_generate,
	.pkgsign_pubkey = ossl_pubkey,
	.pkgsign_sign_data = ossl_sign_data,
};
