Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
pkg: add a `key --sign` operation to sign arbitrary data
Kyle Evans committed 2 years ago
commit 0d6683e1e1412161349835eea6b3cdb138fe99a2
parent 012369c
7 files changed +161 -34
modified libpkg/pkg.h.in
@@ -1489,6 +1489,8 @@ int pkg_key_new(struct pkg_key **, const char *, const char *,
    pkg_password_cb *);
int pkg_key_create(struct pkg_key *, const struct iovec *, int);
int pkg_key_pubkey(struct pkg_key *, char **, size_t *);
+
int pkg_key_sign_data(struct pkg_key *, const unsigned char *, size_t,
+
   unsigned char **, size_t *);
int pkg_key_info(struct pkg_key *, struct iovec **, int *);
void pkg_key_free(struct pkg_key *);

modified libpkg/pkg_key.c
@@ -87,6 +87,14 @@ pkg_key_create(struct pkg_key *key, const struct iovec *iov, int niov)
}

int
+
pkg_key_sign_data(struct pkg_key *key, const unsigned char *msg, size_t msgsz,
+
    unsigned char **sig, size_t *siglen)
+
{
+

+
	return (pkgsign_sign_data(key->ctx, msg, msgsz, sig, siglen));
+
}
+

+
int
pkg_key_info(struct pkg_key *key, struct iovec **iov, int *niov)
{
	int rc;
modified libpkg/pkgsign.c
@@ -211,6 +211,16 @@ pkgsign_generate(struct pkgsign_ctx *ctx, const struct iovec *iov, int niov)
}

int
+
pkgsign_sign_data(struct pkgsign_ctx *ctx, const unsigned char *msg, size_t msgsz,
+
    unsigned char **sig, size_t *siglen)
+
{
+

+
	if (ctx->impl->pi_ops->pkgsign_sign_data == NULL)
+
		return (EPKG_OPNOTSUPP);
+
	return (*ctx->impl->pi_ops->pkgsign_sign_data)(ctx, msg, msgsz, sig, siglen);
+
}
+

+
int
pkgsign_keyinfo(struct pkgsign_ctx *ctx, struct iovec **iov, int *niov)
{

modified libpkg/pkgsign_ecc.c
@@ -1272,4 +1272,5 @@ const struct pkgsign_ops pkgsign_ecc = {

	.pkgsign_generate = ecc_generate,
	.pkgsign_pubkey = ecc_pubkey,
+
	.pkgsign_sign_data = ecc_sign_data,
};
modified libpkg/pkgsign_ossl.c
@@ -369,8 +369,8 @@ ossl_verify(const struct pkgsign_ctx *sctx __unused, const char *keypath,
}

int
-
ossl_sign(struct pkgsign_ctx *sctx, const char *path, unsigned char **sigret,
-
    size_t *siglen)
+
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);
@@ -388,12 +388,6 @@ ossl_sign(struct pkgsign_ctx *sctx, const char *path, unsigned char **sigret,
#else
	RSA *rsa;
#endif
-
	char *sha256;
-

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

	if (keyinfo->key == NULL && _load_private_key(keyinfo) != EPKG_OK) {
		pkg_emit_error("can't load key from %s", keyinfo->sctx.path);
@@ -403,57 +397,41 @@ ossl_sign(struct pkgsign_ctx *sctx, const char *path, unsigned char **sigret,
	max_len = EVP_PKEY_size(keyinfo->key);
	*sigret = xcalloc(1, max_len + 1);

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

#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
-

-
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-
	hash = pkg_checksum_data(sha256, strlen(sha256),
-
	    PKG_HASH_TYPE_SHA256_RAW);
-
#endif
	ctx = EVP_PKEY_CTX_new(keyinfo->key, NULL);
-
	if (ctx == NULL) {
-
		free(sha256);
+
	if (ctx == NULL)
		return (EPKG_FATAL);
-
	}

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

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

	if (EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) {
		EVP_PKEY_CTX_free(ctx);
-
		free(sha256);
		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, sha256,
-
	    pkg_checksum_type_size(PKG_HASH_TYPE_SHA256_HEX));
+
	ret = EVP_PKEY_sign(ctx, *sigret, siglen, msg, msgsz);
#endif

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

-
	ret = RSA_sign(NID_sha1, sha256,
-
	    pkg_checksum_type_size(PKG_HASH_TYPE_SHA256_HEX),
-
	    *sigret, siglen, rsa);
+
	ret = RSA_sign(NID_sha1, msg, msgsz, *sigret, siglen, rsa);
#endif
-
	free(sha256);
	if (ret <= 0) {
		pkg_emit_error("%s: %s", keyinfo->sctx.path,
		   ERR_error_string(ERR_get_error(), errbuf));
@@ -471,10 +449,38 @@ ossl_sign(struct pkgsign_ctx *sctx, const char *path, unsigned char **sigret,
#else
	RSA_free(rsa);
#endif
-

	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)
@@ -605,4 +611,5 @@ const struct pkgsign_ops pkgsign_ossl = {

	.pkgsign_generate = ossl_generate,
	.pkgsign_pubkey = ossl_pubkey,
+
	.pkgsign_sign_data = ossl_sign_data,
};
modified libpkg/private/pkgsign.h
@@ -81,6 +81,10 @@ typedef int pkgsign_generate_cb(struct pkgsign_ctx *, const struct iovec *,
typedef int pkgsign_keyinfo_cb(struct pkgsign_ctx *, struct iovec **,
    int *);

+
/* Sign arbitrary data. */
+
typedef int pkgsign_sign_data_cb(struct pkgsign_ctx *, const unsigned char *,
+
    size_t, unsigned char **, size_t *);
+

/* Return the public key. */
typedef int pkgsign_pubkey_cb(struct pkgsign_ctx *, char **, size_t *);

@@ -99,6 +103,7 @@ struct pkgsign_ops {
	pkgsign_generate_cb		*pkgsign_generate;
	pkgsign_keyinfo_cb		*pkgsign_keyinfo;
	pkgsign_pubkey_cb		*pkgsign_pubkey;
+
	pkgsign_sign_data_cb	*pkgsign_sign_data;

	/* Non-optional. */
	pkgsign_sign_cb			*pkgsign_sign;
@@ -122,6 +127,8 @@ int pkgsign_verify_cert(const struct pkgsign_ctx *, unsigned char *, size_t,
int pkgsign_generate(struct pkgsign_ctx *, const struct iovec *, int);
int pkgsign_keyinfo(struct pkgsign_ctx *, struct iovec **, int *);
int pkgsign_pubkey(struct pkgsign_ctx *, char **, size_t *);
+
int pkgsign_sign_data(struct pkgsign_ctx *, const unsigned char *,
+
    size_t, unsigned char **, size_t *);

const char *pkgsign_impl_name(const struct pkgsign_ctx *);

modified src/key.c
@@ -31,6 +31,7 @@

#include <bsd_compat.h>
#include <assert.h>
+
#include <err.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
@@ -52,18 +53,20 @@
enum {
	ARG_CREATE = CHAR_MAX + 1,
	ARG_PUBLIC,
+
	ARG_SIGN,
};

typedef enum {
	MODE_UNSPECIFIED = 0,
	MODE_CREATE,
	MODE_PUBLIC,
+
	MODE_SIGN,
} key_mode_t;

void
usage_key(void)
{
-
	fprintf(stderr, "Usage: pkg key [--create | --public] [-t <type>] "
+
	fprintf(stderr, "Usage: pkg key [--create | --public | --sign] [-t <type>] "
	    "<key-path>\n");
	fprintf(stderr, "For more information see 'pkg help key'.\n");
}
@@ -87,13 +90,78 @@ key_pubout(struct pkg_key *key)
		return (ret);

	fwrite(keybuf, keylen, 1, stdout);
-
	if (keybuf[keylen - 1] != '\n')
-
		fputc('\n', stdout);
	free(keybuf);
	return (0);
}

static int
+
key_sign_data(struct pkg_key *key, const char *name)
+
{
+
	char buf[BUFSIZ];
+
	xstring *datastr;
+
	char *data;
+
	unsigned char *sig;
+
	size_t datasz, readsz, siglen;
+
	FILE *datafile;
+
	int rc;
+

+
	datafile = NULL;
+
	datastr = NULL;
+
	rc = EPKG_FATAL;
+
	if (strcmp(name, "-") == 0) {
+
		datafile = stdin;	/* XXX Make it configurable? */
+
		name = "stdin";
+
	} else {
+
		datafile = fopen(name, "rb");
+
		if (datafile == NULL)
+
			err(EXIT_FAILURE, "fopen");
+
	}
+

+
	datastr = xstring_new();
+
	while (!feof(datafile)) {
+
		readsz = fread(&buf[0], 1, sizeof(buf), datafile);
+
		if (readsz == 0 && ferror(datafile)) {
+
			fprintf(stderr, "%s: I/O error\n", name);
+
			goto out;
+
		}
+

+
		fwrite(buf, readsz, 1, datastr->fp);
+
	}
+

+
	data = xstring_get_binary(datastr, &datasz);
+
	datastr = NULL;
+

+
	sig = NULL;
+
	rc = pkg_key_sign_data(key, (unsigned char *)data, datasz, &sig, &siglen);
+
	free(data);
+

+
#if 0
+
	fprintf(stderr, "SIGNED: %s\n", data);
+
#endif
+
/*
+
+SIGNED: 64628d55add8b281b9868aea00c4829a3ad260cfc4262e9d1244a1ab67584935
+
+SIGNED: a2eb46d60cd26657b273ec55a0909e642ef522f35074a9c62c3c4b42608e55e1
+
*/
+

+
	if (rc == EPKG_OK) {
+
		size_t writesz;
+

+
		if ((writesz = fwrite(sig, 1, siglen, stdout)) < siglen) {
+
			fprintf(stderr, "Failed to write signature out [%zu/%zu]\n",
+
			    writesz, siglen);
+
			rc = EPKG_FATAL;
+
		}
+
	}
+
	free(sig);
+

+
out:
+
	xstring_free(datastr);
+
	if (datafile != stdin)
+
		fclose(datafile);
+
	return rc;
+
}
+

+
static int
key_info(struct pkg_key *key, const char *file, const char *type)
{
	struct iovec *iov;
@@ -163,6 +231,7 @@ exec_key(int argc, char **argv)
	struct option longopts[] = {
		{ "create",	no_argument,		NULL,	ARG_CREATE },
		{ "public", no_argument,		NULL,	ARG_PUBLIC },
+
		{ "sign", no_argument,		NULL,		ARG_SIGN },
		{ NULL,		0,			NULL,	0 },
	};

@@ -185,6 +254,13 @@ exec_key(int argc, char **argv)
			}
			keymode = MODE_PUBLIC;
			break;
+
		case ARG_SIGN:
+
			if (keymode != MODE_UNSPECIFIED) {
+
				usage_key();
+
				return (EXIT_FAILURE);
+
			}
+
			keymode = MODE_SIGN;
+
			break;
		case 't':
			keytype = optarg;
			break;
@@ -253,6 +329,22 @@ exec_key(int argc, char **argv)
		}

		break;
+
	case MODE_SIGN:
+
		ret = key_sign_data(key, "-");
+
		if (ret != EPKG_OK) {
+
			switch (ret) {
+
			case EPKG_OPNOTSUPP:
+
				fprintf(stderr, "Type '%s' does not support signing.\n",
+
				    keytype);
+
				break;
+
			default:
+
				fprintf(stderr, "Failed to sign.\n");
+
				break;
+
			}
+

+
			goto out;
+
		}
+
		break;
	case MODE_UNSPECIFIED:
		ret = key_info(key, keypath, keytype);
		if (ret != EPKG_OK) {