From 709e675cdf0c8f9b07507aad12400398f354cc90 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Mon, 16 Jan 2017 09:19:43 -0500
Subject: [PATCH] Add OpenSSL support for digest and signatures
Autotools: add --with-crypto=openssl
This enables RPM to locate the appropriate flags for compiling
against OpenSSL for digest and hash functions.
This implementation changes the old behavior of
--with[out]-beecrypt toggling between beecrypt and nss. It will
now throw an error if attempting to use --with-beecrypt
indicating that the user should instead use --with-crypto=
See also:
https://github.com/rpm-software-management/rpm/issues/119
(cherry picked from commit 64028f9a1c25ada8ffc7a48775f526600edcbf85)
Conflicts:
Makefile.am
configure.ac
---
INSTALL | 27 +-
Makefile.am | 17 +-
configure.ac | 115 ++++++-
rpmio/Makefile.am | 6 +
rpmio/digest_openssl.c | 838 +++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 982 insertions(+), 21 deletions(-)
create mode 100644 rpmio/digest_openssl.c
diff --git a/INSTALL b/INSTALL
index 60536e316..8eefef799 100644
--- a/INSTALL
+++ b/INSTALL
@@ -9,17 +9,34 @@ The libmagic (aka file) library for file type detection (used by rpmbuild).
The source for the file utility + library is available from
ftp://ftp.astron.com/pub/file/
-The NSS >= 3.12 library for encryption, and NSPR library which NSS uses.
-Both NSPR and NSS libraries and headers need to be installed during RPM
-compilation. As NSPR and NSS typically install their headers outside
-the regular include search path, you need to tell configure about this,
-eg something like:
+You will need a cryptographic library to support digests and signatures.
+This library may be Mozilla NSS, OpenSSL or beecrypt. Which library to use
+must be specified with the --with-crypto=[beecrypt|nss|openssl] argument
+to configure.
+
+If using the Mozilla NSS library for encyption (and NSPR library which
+NSS uses) it must be version 3.12 or later. Both NSPR and NSS libraries and
+headers need to be installed during RPM compilation. As NSPR and NSS
+typically install their headers outside the regular include search path,
+you need to tell configure about this, eg something like:
./configure <......> CPPFLAGS="-I/usr/include/nspr -I/usr/include/nss"
The NSPR and NSS libraries are available from
http://www.mozilla.org/projects/security/pki/nss/
http://www.mozilla.org/projects/nspr/
+If using the OpenSSL library for encryption, it must be version 1.0.2 or
+later. Note: when compiling against OpenSSL, there is a possible license
+incompatibility. For more details on this, see
+https://people.gnome.org/~markmc/openssl-and-the-gpl.html
+Some Linux distributions have different legal interpretations of this
+possible incompatibility. It is recommended to consult with a lawyer before
+building RPM against OpenSSL.
+Fedora: https://fedoraproject.org/wiki/Licensing:FAQ#What.27s_the_deal_with_the_OpenSSL_license.3F
+Debian: https://lists.debian.org/debian-legal/2002/10/msg00113.html
+
+The OpenSSL crypto library is available from https://www.openssl.org/
+
The Berkeley DB >= 4.3.x (4.5.x or newer recommended) is required for the
default database backend. BDB can be downloaded from
http://www.oracle.com/technology/software/products/berkeley-db/index.html
diff --git a/Makefile.am b/Makefile.am
index 1b77730aa..a5268030b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -41,6 +41,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/build
AM_CPPFLAGS += -I$(top_srcdir)/lib
AM_CPPFLAGS += -I$(top_srcdir)/rpmio
AM_CPPFLAGS += @WITH_NSS_INCLUDE@
+AM_CPPFLAGS += @WITH_OPENSSL_INCLUDE@
AM_CPPFLAGS += @WITH_POPT_INCLUDE@
AM_CPPFLAGS += -I$(top_srcdir)/misc
AM_CPPFLAGS += -DLOCALEDIR="\"$(localedir)\""
@@ -113,45 +114,45 @@ rpm_SOURCES = rpmqv.c debug.h system.h
rpm_CPPFLAGS = $(AM_CPPFLAGS) -DIAM_RPMEIU -DIAM_RPMQ -DIAM_RPMV
rpm_LDADD = libcliutils.la
rpm_LDADD += lib/librpm.la rpmio/librpmio.la
-rpm_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpm_LDADD += @WITH_NSS_LIB@ @WITH_OPENSSL_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
rpmdb_SOURCES = rpmdb.c debug.h system.h
rpmdb_CPPFLAGS = $(AM_CPPFLAGS)
rpmdb_LDADD = libcliutils.la
rpmdb_LDADD += lib/librpm.la rpmio/librpmio.la
-rpmdb_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmdb_LDADD += @WITH_NSS_LIB@ @WITH_OPENSSL_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
rpmkeys_SOURCES = rpmkeys.c debug.h system.h
rpmkeys_CPPFLAGS = $(AM_CPPFLAGS)
rpmkeys_LDADD = libcliutils.la
rpmkeys_LDADD += lib/librpm.la rpmio/librpmio.la
-rpmkeys_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmkeys_LDADD += @WITH_NSS_LIB@ @WITH_OPENSSL_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
rpmsign_SOURCES = rpmsign.c debug.h system.h
rpmsign_CPPFLAGS = $(AM_CPPFLAGS)
rpmsign_LDADD = libcliutils.la
rpmsign_LDADD += sign/librpmsign.la lib/librpm.la rpmio/librpmio.la
-rpmsign_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmsign_LDADD += @WITH_NSS_LIB@ @WITH_OPENSSL_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
rpmbuild_SOURCES = rpmbuild.c debug.h system.h
rpmbuild_CPPFLAGS = $(AM_CPPFLAGS)
rpmbuild_LDADD = libcliutils.la
rpmbuild_LDADD += build/librpmbuild.la lib/librpm.la rpmio/librpmio.la
-rpmbuild_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmbuild_LDADD += @WITH_NSS_LIB@ @WITH_OPENSSL_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
rpmspec_SOURCES = rpmspec.c debug.h system.h
rpmspec_CPPFLAGS = $(AM_CPPFLAGS)
rpmspec_LDADD = libcliutils.la
rpmspec_LDADD += build/librpmbuild.la lib/librpm.la rpmio/librpmio.la
-rpmspec_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmspec_LDADD += @WITH_NSS_LIB@ @WITH_OPENSSL_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
rpm2cpio_SOURCES = rpm2cpio.c debug.h system.h
rpm2cpio_LDADD = lib/librpm.la rpmio/librpmio.la
-rpm2cpio_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpm2cpio_LDADD += @WITH_NSS_LIB@ @WITH_OPENSSL_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
rpm2archive_SOURCES = rpm2archive.c debug.h system.h
rpm2archive_LDADD = lib/librpm.la rpmio/librpmio.la
-rpm2archive_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@ @WITH_ARCHIVE_LIB@
+rpm2archive_LDADD += @WITH_NSS_LIB@ @WITH_OPENSSL_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@ @WITH_ARCHIVE_LIB@
if LIBELF
diff --git a/configure.ac b/configure.ac
index e6362535b..65052870a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -243,18 +243,30 @@ AC_CHECK_HEADERS([dwarf.h], [
AM_CONDITIONAL(LIBDWARF,[test "$WITH_LIBDWARF" = yes])
#=================
+# Select crypto library
+AC_ARG_WITH(crypto,
+ [AC_HELP_STRING([--with-crypto=CRYPTO_LIB],
+ [The cryptographic library to use (nss|beecrypt|openssl). The default is nss.])
+ ],[],
+ [with_crypto=nss])
+
+# Refuse to proceed if someone specified --with-beecrypt (removed)
+AC_ARG_WITH(beecrypt,
+ [AC_HELP_STRING([--with-beecrypt (OBSOLETE)], [Obsolete argument. Use --with-crypto=beecrypt])
+ ],[AC_MSG_ERROR([--with-beecrypt no longer supported. Use --with-crypto=beecrypt])],
+ [])
+
# Check for beecrypt library if requested.
-AC_ARG_WITH(beecrypt, [ --with-beecrypt build with beecrypt support ],,[with_beecrypt=no])
AC_ARG_WITH(internal_beecrypt, [ --with-internal-beecrypt build with internal beecrypt library ],,[with_internal_beecrypt=no])
AM_CONDITIONAL([WITH_INTERNAL_BEECRYPT],[test "$with_internal_beecrypt" = yes])
if test "$with_internal_beecrypt" = yes ; then
- with_beecrypt=yes
+ with_crypto=beecrypt
fi
-AM_CONDITIONAL([WITH_BEECRYPT],[test "$with_beecrypt" = yes])
+AM_CONDITIONAL([WITH_BEECRYPT],[test "$with_crypto" = beecrypt])
WITH_BEECRYPT_INCLUDE=
WITH_BEECRYPT_LIB=
-if test "$with_beecrypt" = yes ; then
+if test "$with_crypto" = beecrypt ; then
AC_DEFINE(WITH_BEECRYPT, 1, [Build with beecrypt instead of nss3 support?])
if test "$with_internal_beecrypt" = yes ; then
WITH_BEECRYPT_INCLUDE="-I\$(top_srcdir)/beecrypt"
@@ -263,7 +275,7 @@ if test "$with_beecrypt" = yes ; then
AC_CHECK_LIB(beecrypt, mpfprintln, [
WITH_BEECRYPT_LIB="-lbeecrypt"
],[
- AC_MSG_ERROR([missing required library 'beecrypt'])
+ AC_MSG_ERROR([missing required library 'beecrypt'])
])
AC_CHECK_HEADER([beecrypt/api.h], [AC_DEFINE(HAVE_BEECRYPT_API_H, 1, [Define to 1 if you have the <beecrypt/api.h> header file.])
])
@@ -273,13 +285,100 @@ AC_SUBST(WITH_BEECRYPT_LIB)
AC_SUBST(WITH_BEECRYPT_INCLUDE)
#=================
+# Check for OpenSSL library.
+# We need evp.h from OpenSSL.
+
+WITH_OPENSSL_INCLUDE=
+WITH_OPENSSL_LIB=
+if test "$with_crypto" = openssl; then
+# If we have pkgconfig make sure CPPFLAGS are setup correctly for the OpenSSL
+# -I include path.
+AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no], [$PATH:/usr/bin:/usr/local/bin])
+if test "x$PKGCONFIG" != "xno"; then
+ CPPFLAGS="$CPPFLAGS $($PKGCONFIG --cflags libcrypto)"
+ WITH_OPENSSL_LIB=$($PKGCONFIG --libs libcrypto)
+else
+ WITH_OPENSSL_LIB=-lcrypto
+fi
+
+AC_CHECK_HEADERS([openssl/evp.h], [], [
+ AC_MSG_ERROR([missing required OpenSSL header])
+])
+AC_CHECK_HEADERS([openssl/rsa.h], [], [
+ AC_MSG_ERROR([missing required OpenSSL header])
+])
+AC_CHECK_HEADERS([openssl/dsa.h], [], [
+ AC_MSG_ERROR([missing required OpenSSL header])
+])
+
+AC_CHECK_LIB(crypto, EVP_DigestInit_ex, [], [
+ AC_MSG_ERROR([required OpenSSL library 'libcrypto' missing or too old])
+])
+
+AC_CHECK_LIB(crypto, EVP_MD_CTX_new, [
+ AC_DEFINE(HAVE_EVP_MD_CTX_NEW, 1, [Define to 1 if OpenSSL has EVP_MD_CTX_new])
+ AC_SUBST(HAVE_EVP_MD_CTX_NEW, [1])
+ ], [
+ AC_CHECK_LIB(crypt, EVP_MD_CTX_create, [], [
+ AC_MSG_ERROR([required OpenSSL library 'libcrypto' missing or too old])
+ ])
+])
+
+AC_CHECK_LIB(crypto, EVP_PKEY_CTX_new, [], [
+ AC_MSG_ERROR([required OpenSSL library 'libcrypto' missing or too old])
+])
+
+AC_CHECK_LIB(crypto, DSA_set0_key, [
+ AC_DEFINE(HAVE_DSA_SET0_KEY, 1, [Define to 1 if OpenSSL has DSA_set0_key])
+ AC_SUBST(HAVE_DSA_SET0_KEY, [1])
+ ], []
+)
+
+AC_CHECK_LIB(crypto, DSA_set0_pqg, [
+ AC_DEFINE(HAVE_DSA_SET0_PQG, 1, [Define to 1 if OpenSSL has DSA_set0_pqg])
+ AC_SUBST(HAVE_DSA_SET0_PQG, [1])
+ ], []
+)
+
+AC_CHECK_LIB(crypto, DSA_SIG_set0, [
+ AC_DEFINE(HAVE_DSA_SIG_SET0, 1, [Define to 1 if OpenSSL has DSA_SIG_set0])
+ AC_SUBST(HAVE_DSA_SIG_SET0, [1])
+ ], []
+)
+
+AC_CHECK_LIB(crypto, RSA_set0_key, [
+ AC_DEFINE(HAVE_RSA_SET0_KEY, 1, [Define to 1 if OpenSSL has RSA_set0_key])
+ AC_SUBST(HAVE_RSA_SET0_KEY, [1])
+ ], []
+)
+
+AC_CHECK_LIB(crypto, BN_bn2binpad, [
+ AC_DEFINE(HAVE_BN2BINPAD, 1, [Define to 1 if OpenSSL has BN_bn2binpad])
+ AC_SUBST(HAVE_BN2BINPAD, [1])
+ ], []
+)
+
+fi
+
+AM_CONDITIONAL([WITH_OPENSSL],[test "$with_crypto" = openssl])
+AC_SUBST(WITH_OPENSSL_INCLUDE)
+AC_SUBST(WITH_OPENSSL_LIB)
+
+#=================
# Check for NSS library.
-# We need nss.h from NSS which needs nspr.h. Unfortunately both glibc and NSS
-# have a header named nss.h... so make extra check for NSS's sechash.h
+# We need nss.h from NSS which needs nspr.h. Unfortunately both glibc and NSS
+# have a header named nss.h... so make extra check for NSS's sechash.h
# which we use too and hopefully is slightly more unique to NSS.
WITH_NSS_INCLUDE=
WITH_NSS_LIB=
-if test "$with_beecrypt" != yes ; then
+if test "$with_crypto" = nss; then
+# If we have pkgconfig make sure CPPFLAGS are setup correctly for the nss
+# -I include path. Otherwise the below checks will fail because nspr.h
+# cannot be found.
+AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no], [$PATH:/usr/bin:/usr/local/bin])
+if test "x$PKGCONFIG" != "xno"; then
+ CPPFLAGS="$CPPFLAGS $($PKGCONFIG --cflags nss)"
+fi
AC_CHECK_HEADERS([nspr.h nss.h sechash.h], [], [
AC_MSG_ERROR([missing required NSPR / NSS header])
])
diff --git a/rpmio/Makefile.am b/rpmio/Makefile.am
index 68a821934..9062f58a5 100644
--- a/rpmio/Makefile.am
+++ b/rpmio/Makefile.am
@@ -5,6 +5,7 @@ include $(top_srcdir)/rpm.am
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_builddir)/include/
AM_CPPFLAGS += @WITH_NSS_INCLUDE@
AM_CPPFLAGS += @WITH_BEECRYPT_INCLUDE@
+AM_CPPFLAGS += @WITH_OPENSSL_INCLUDE@
AM_CPPFLAGS += @WITH_POPT_INCLUDE@
AM_CPPFLAGS += -I$(top_srcdir)/misc
AM_CPPFLAGS += -DRPMCONFIGDIR="\"@RPMCONFIGDIR@\""
@@ -23,8 +24,12 @@ librpmio_la_SOURCES = \
if WITH_BEECRYPT
librpmio_la_SOURCES += digest_beecrypt.c
else
+if WITH_OPENSSL
+librpmio_la_SOURCES += digest_openssl.c
+else
librpmio_la_SOURCES += digest_nss.c
endif
+endif
librpmio_la_LDFLAGS = -version-info $(rpm_version_info)
@@ -32,6 +37,7 @@ librpmio_la_LIBADD = \
../misc/libmisc.la \
@WITH_NSS_LIB@ \
@WITH_BEECRYPT_LIB@ \
+ @WITH_OPENSSL_LIB@ \
@WITH_BZ2_LIB@ \
@WITH_ZLIB_LIB@ \
@WITH_LIBELF_LIB@ \
diff --git a/rpmio/digest_openssl.c b/rpmio/digest_openssl.c
new file mode 100644
index 000000000..aea460e39
--- /dev/null
+++ b/rpmio/digest_openssl.c
@@ -0,0 +1,838 @@
+#include "system.h"
+
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <rpm/rpmpgp.h>
+
+#include "rpmio/digest.h"
+
+
+/* Compatibility functions for OpenSSL 1.0.2 */
+
+#ifndef HAVE_EVP_MD_CTX_NEW
+# define EVP_MD_CTX_new EVP_MD_CTX_create
+# define EVP_MD_CTX_free EVP_MD_CTX_destroy
+#endif
+
+#ifndef HAVE_RSA_SET0_KEY
+int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d);
+int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
+{
+ if (!r) return 0;
+
+ if (n) {
+ r->n = n;
+ }
+
+ if (e) {
+ r->e = e;
+ }
+
+ if (d) {
+ r->d = d;
+ }
+
+ return 1;
+}
+#endif /* HAVE_RSA_SET0_KEY */
+
+#ifndef HAVE_DSA_SET0_KEY
+int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key);
+
+int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
+{
+ if (!d) return 0;
+
+ if (pub_key) {
+ d->pub_key = pub_key;
+ }
+
+ if (priv_key) {
+ d->priv_key = priv_key;
+ }
+
+ return 1;
+}
+#endif /* HAVE_DSA_SET0_KEY */
+
+#ifndef HAVE_DSA_SET0_PQG
+int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g);
+
+int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
+{
+ if (!d) return 0;
+
+ if (p) {
+ d->p = p;
+ }
+
+ if (q) {
+ d->q = q;
+ }
+
+ if (g) {
+ d->g = g;
+ }
+
+ return 1;
+}
+#endif /* HAVE_DSA_SET0_PQG */
+
+#ifndef HAVE_DSA_SIG_SET0
+int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s);
+
+int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+ if (!sig) return 0;
+
+ if (r) {
+ sig->r = r;
+ }
+
+ if (s) {
+ sig->s = s;
+ }
+
+ return 1;
+}
+#endif /* HAVE_DSA_SIG_SET0 */
+
+#ifndef HAVE_BN2BINPAD
+static int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
+{
+ int i;
+
+ i = BN_num_bytes(a);
+ if (tolen < i)
+ return -1;
+
+ /* Add leading zeroes if necessary */
+ if (tolen > i) {
+ memset(to, 0, tolen - i);
+ to += tolen - i;
+ }
+
+ BN_bn2bin(a, to);
+
+ return tolen;
+}
+#endif /* HAVE_BN2BINPAD */
+
+struct DIGEST_CTX_s {
+ rpmDigestFlags flags; /*!< Bit(s) to control digest operation. */
+ int algo; /*!< Used hash algorithm */
+
+ EVP_MD_CTX *md_ctx; /* Digest context (opaque) */
+
+};
+
+/**************************** init ************************************/
+
+int rpmInitCrypto(void) {
+ return 0;
+}
+
+int rpmFreeCrypto(void) {
+ return 0;
+}
+
+/**************************** digest ************************************/
+
+DIGEST_CTX rpmDigestDup(DIGEST_CTX octx)
+{
+ if (!octx) return NULL;
+
+ DIGEST_CTX nctx = NULL;
+ nctx = xcalloc(1, sizeof(*nctx));
+
+ nctx->flags = octx->flags;
+ nctx->algo = octx->algo;
+ nctx->md_ctx = EVP_MD_CTX_new();
+ if (!nctx->md_ctx) {
+ free(nctx);
+ return NULL;
+ }
+
+ if (!EVP_MD_CTX_copy(nctx->md_ctx, octx->md_ctx)) {
+ free(nctx);
+ return NULL;
+ }
+
+ return nctx;
+}
+
+static const EVP_MD *getEVPMD(int hashalgo)
+{
+ switch (hashalgo) {
+
+ case PGPHASHALGO_MD5:
+ return EVP_md5();
+
+ case PGPHASHALGO_SHA1:
+ return EVP_sha1();
+
+ case PGPHASHALGO_RIPEMD160:
+ return EVP_ripemd160();
+
+ case PGPHASHALGO_MD2:
+ return EVP_md2();
+
+ case PGPHASHALGO_SHA256:
+ return EVP_sha256();
+
+ case PGPHASHALGO_SHA384:
+ return EVP_sha384();
+
+ case PGPHASHALGO_SHA512:
+ return EVP_sha512();
+
+ case PGPHASHALGO_SHA224:
+ return EVP_sha224();
+
+ default:
+ return EVP_md_null();
+ }
+}
+
+size_t rpmDigestLength(int hashalgo)
+{
+ return EVP_MD_size(getEVPMD(hashalgo));
+}
+
+DIGEST_CTX rpmDigestInit(int hashalgo, rpmDigestFlags flags)
+{
+ DIGEST_CTX ctx = xcalloc(1, sizeof(*ctx));
+
+ ctx->md_ctx = EVP_MD_CTX_new();
+ if (!ctx->md_ctx) {
+ free(ctx);
+ return NULL;
+ }
+
+ const EVP_MD *md = getEVPMD(hashalgo);
+ if (md == EVP_md_null()) {
+ free(ctx->md_ctx);
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->algo = hashalgo;
+ ctx->flags = flags;
+ if (!EVP_DigestInit_ex(ctx->md_ctx, md, NULL)) {
+ free(ctx->md_ctx);
+ free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+int rpmDigestUpdate(DIGEST_CTX ctx, const void *data, size_t len)
+{
+ if (ctx == NULL) return -1;
+
+ EVP_DigestUpdate(ctx->md_ctx, data, len);
+
+ return 0;
+}
+
+int rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii)
+{
+ int ret;
+ unsigned char *digest = NULL;
+ unsigned int digestlen;
+
+ if (ctx == NULL) return -1;
+
+ digestlen = EVP_MD_CTX_size(ctx->md_ctx);
+ digest = xcalloc(digestlen, sizeof(*digest));
+
+ ret = EVP_DigestFinal_ex(ctx->md_ctx, digest, &digestlen);
+ if (ret != 1) goto done;
+
+ if (!asAscii) {
+ /* Raw data requested */
+ if (lenp) *lenp = digestlen;
+ if (datap) {
+ *datap = digest;
+ digest = NULL;
+ }
+ }
+
+ else {
+ /* ASCII requested */
+ if (lenp) *lenp = (2*digestlen) + 1;
+ if (datap) {
+ const uint8_t * s = (const uint8_t *) digest;
+ *datap = pgpHexStr(s, digestlen);
+ }
+ }
+
+ ret = 1;
+
+done:
+ if (digest) {
+ /* Zero the digest, just in case it's sensitive */
+ memset(digest, 0, digestlen);
+ free(digest);
+ }
+
+ EVP_MD_CTX_free(ctx->md_ctx);
+ free(ctx);
+
+ if (ret != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/****************************** RSA **************************************/
+
+/* Key */
+
+struct pgpDigKeyRSA_s {
+ size_t nbytes; /* Size of modulus */
+
+ BIGNUM *n; /* Common Modulus */
+ BIGNUM *e; /* Public Exponent */
+
+ EVP_PKEY *evp_pkey; /* Fully constructed key */
+};
+
+static int constructRSASigningKey(struct pgpDigKeyRSA_s *key)
+{
+ if (key->evp_pkey) {
+ /* We've already constructed it, so just reuse it */
+ return 1;
+ }
+
+ /* Create the RSA key */
+ RSA *rsa = RSA_new();
+ if (!rsa) return 0;
+
+ if (!RSA_set0_key(rsa, key->n, key->e, NULL)) {
+ RSA_free(rsa);
+ return 0;
+ }
+
+ /* Create an EVP_PKEY container to abstract the key-type. */
+ key->evp_pkey = EVP_PKEY_new();
+ if (!key->evp_pkey) {
+ RSA_free(rsa);
+ return 0;
+ }
+
+ /* Assign the RSA key to the EVP_PKEY structure.
+ This will take over memory management of the RSA key */
+ if (!EVP_PKEY_assign_RSA(key->evp_pkey, rsa)) {
+ EVP_PKEY_free(key->evp_pkey);
+ key->evp_pkey = NULL;
+ RSA_free(rsa);
+ }
+
+ return 1;
+}
+
+static int pgpSetKeyMpiRSA(pgpDigAlg pgpkey, int num, const uint8_t *p)
+{
+ size_t mlen = pgpMpiLen(p) - 2;
+ struct pgpDigKeyRSA_s *key = pgpkey->data;
+
+ if(!key) {
+ key = pgpkey->data = xcalloc(1, sizeof(*key));
+ }
+
+ switch(num) {
+ case 0:
+ /* Modulus */
+ if (key->n) {
+ /* This should only ever happen once per key */
+ return 1;
+ }
+
+ key->nbytes = mlen;
+ /* Create a BIGNUM from the pointer.
+ Note: this assumes big-endian data as required by PGP */
+ key->n = BN_bin2bn(p+2, mlen, NULL);
+ if (!key->n) return 1;
+ break;
+
+ case 1:
+ /* Exponent */
+ if (key->e) {
+ /* This should only ever happen once per key */
+ return 1;
+ }
+
+ /* Create a BIGNUM from the pointer.
+ Note: this assumes big-endian data as required by PGP */
+ key->e = BN_bin2bn(p+2, mlen, NULL);
+ if (!key->e) return 1;
+ break;
+ }
+
+ return 0;
+}
+
+static void pgpFreeKeyRSA(pgpDigAlg pgpkey)
+{
+ struct pgpDigKeyRSA_s *key = pgpkey->data;
+ if (key) {
+ if (key->evp_pkey) {
+ EVP_PKEY_free(key->evp_pkey);
+ } else {
+ /* If key->evp_pkey was constructed,
+ * the memory management of these BNs
+ * are freed with it. */
+ BN_clear_free(key->n);
+ BN_clear_free(key->e);
+ }
+
+ free(key);
+ }
+}
+
+/* Signature */
+
+struct pgpDigSigRSA_s {
+ BIGNUM *bn;
+ size_t len;
+};
+
+static int pgpSetSigMpiRSA(pgpDigAlg pgpsig, int num, const uint8_t *p)
+{
+ BIGNUM *bn = NULL;
+
+ int mlen = pgpMpiLen(p) - 2;
+ int rc = 1;
+
+ struct pgpDigSigRSA_s *sig = pgpsig->data;
+ if (!sig) {
+ sig = xcalloc(1, sizeof(*sig));
+ }
+
+ switch (num) {
+ case 0:
+ if (sig->bn) {
+ /* This should only ever happen once per signature */
+ return 1;
+ }
+
+ bn = sig->bn = BN_new();
+ if (!bn) return 1;
+
+ /* Create a BIGNUM from the signature pointer.
+ Note: this assumes big-endian data as required
+ by the PGP multiprecision integer format
+ (RFC4880, Section 3.2)
+ This will be useful later, as we can
+ retrieve this value with appropriate
+ padding. */
+ bn = BN_bin2bn(p+2, mlen, bn);
+ if (!bn) return 1;
+
+ sig->bn = bn;
+ sig->len = mlen;
+
+ pgpsig->data = sig;
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static void pgpFreeSigRSA(pgpDigAlg pgpsig)
+{
+ struct pgpDigSigRSA_s *sig = pgpsig->data;
+ if (sig) {
+ BN_clear_free(sig->bn);
+ free(pgpsig->data);
+ }
+}
+
+static int pgpVerifySigRSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig,
+ uint8_t *hash, size_t hashlen, int hash_algo)
+{
+ int rc, ret;
+ EVP_PKEY_CTX *pkey_ctx = NULL;
+ struct pgpDigSigRSA_s *sig = pgpsig->data;
+
+ void *padded_sig = NULL;
+
+ struct pgpDigKeyRSA_s *key = pgpkey->data;
+
+ if(!constructRSASigningKey(key)) {
+ rc = 1;
+ goto done;
+ }
+
+ pkey_ctx = EVP_PKEY_CTX_new(key->evp_pkey, NULL);
+ if (!pkey_ctx) {
+ rc = 1;
+ goto done;
+ }
+
+ ret = EVP_PKEY_verify_init(pkey_ctx);
+ if (ret < 0) {
+ rc = 1;
+ goto done;
+ }
+
+ ret = EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PADDING);
+ if (ret < 0) {
+ rc = 1;
+ goto done;
+ }
+
+ ret = EVP_PKEY_CTX_set_signature_md(pkey_ctx, getEVPMD(hash_algo));
+ if (ret < 0) {
+ rc = 1;
+ goto done;
+ }
+
+ int pkey_len = EVP_PKEY_size(key->evp_pkey);
+ padded_sig = xcalloc(1, pkey_len);
+ if (!BN_bn2binpad(sig->bn, padded_sig, pkey_len)) {
+ rc = 1;
+ goto done;
+ }
+
+ ret = EVP_PKEY_verify(pkey_ctx, padded_sig, pkey_len, hash, hashlen);
+ if (ret == 1)
+ {
+ /* Success */
+ rc = 0;
+ }
+ else
+ {
+ /* Failure */
+ rc = 1;
+ }
+
+done:
+ EVP_PKEY_CTX_free(pkey_ctx);
+ free(padded_sig);
+ return rc;
+}
+
+/****************************** DSA ***************************************/
+/* Key */
+
+struct pgpDigKeyDSA_s {
+ BIGNUM *p; /* Prime */
+ BIGNUM *q; /* Subprime */
+ BIGNUM *g; /* Base */
+ BIGNUM *y; /* Public Key */
+
+ DSA *dsa_key; /* Fully constructed key */
+};
+
+static int constructDSASigningKey(struct pgpDigKeyDSA_s *key)
+{
+ int rc;
+
+ if (key->dsa_key) {
+ /* We've already constructed it, so just reuse it */
+ return 1;
+ }
+
+ /* Create the DSA key */
+ DSA *dsa = DSA_new();
+ if (!dsa) return 0;
+
+ if (!DSA_set0_pqg(dsa, key->p, key->q, key->g)) {
+ rc = 0;
+ goto done;
+ }
+
+ if (!DSA_set0_key(dsa, key->y, NULL)) {
+ rc = 0;
+ }
+
+ key->dsa_key = dsa;
+
+ rc = 1;
+done:
+ if (rc == 0) {
+ DSA_free(dsa);
+ }
+ return rc;
+}
+
+
+static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, const uint8_t *p)
+{
+ BIGNUM *bn;
+ size_t mlen = pgpMpiLen(p) - 2;
+ struct pgpDigKeyDSA_s *key = pgpkey->data;
+
+ if(!key) {
+ key = pgpkey->data = xcalloc(1, sizeof(*key));
+ }
+
+ /* Create a BIGNUM from the key pointer.
+ Note: this assumes big-endian data as required
+ by the PGP multiprecision integer format
+ (RFC4880, Section 3.2) */
+ bn = BN_bin2bn(p+2, mlen, NULL);
+ if (!bn) return 1;
+
+ switch(num) {
+ case 0:
+ /* Prime */
+ if (key->p) {
+ /* This should only ever happen once per key */
+ return 1;
+ }
+ key->p = bn;
+ break;
+
+ case 1:
+ /* Subprime */
+ if (key->q) {
+ /* This should only ever happen once per key */
+ return 1;
+ }
+ key->q = bn;
+ break;
+ case 2:
+ /* Base */
+ if (key->g) {
+ /* This should only ever happen once per key */
+ return 1;
+ }
+ key->g = bn;
+ break;
+ case 3:
+ /* Public */
+ if (key->y) {
+ /* This should only ever happen once per key */
+ return 1;
+ }
+ key->y = bn;
+ break;
+ }
+
+ return 0;
+}
+
+static void pgpFreeKeyDSA(pgpDigAlg pgpkey)
+{
+ struct pgpDigKeyDSA_s *key = pgpkey->data;
+ if (key) {
+ if (key->dsa_key) {
+ DSA_free(key->dsa_key);
+ } else {
+ /* If sig->dsa_key was constructed,
+ * the memory management of these BNs
+ * are freed with it. */
+ BN_clear_free(key->p);
+ BN_clear_free(key->q);
+ BN_clear_free(key->g);
+ BN_clear_free(key->y);
+ }
+ free(key);
+ }
+}
+
+/* Signature */
+
+struct pgpDigSigDSA_s {
+ BIGNUM *r;
+ BIGNUM *s;
+
+ DSA_SIG *dsa_sig;
+};
+
+static int constructDSASignature(struct pgpDigSigDSA_s *sig)
+{
+ int rc;
+
+ if (sig->dsa_sig) {
+ /* We've already constructed it, so just reuse it */
+ return 1;
+ }
+
+ /* Create the DSA signature */
+ DSA_SIG *dsa_sig = DSA_SIG_new();
+ if (!dsa_sig) return 0;
+
+ if (!DSA_SIG_set0(dsa_sig, sig->r, sig->s)) {
+ rc = 0;
+ goto done;
+ }
+
+ sig->dsa_sig = dsa_sig;
+
+ rc = 1;
+done:
+ if (rc == 0) {
+ DSA_SIG_free(sig->dsa_sig);
+ }
+ return rc;
+}
+
+static int pgpSetSigMpiDSA(pgpDigAlg pgpsig, int num, const uint8_t *p)
+{
+ BIGNUM *bn = NULL;
+
+ int mlen = pgpMpiLen(p) - 2;
+ int rc = 1;
+
+ struct pgpDigSigDSA_s *sig = pgpsig->data;
+ if (!sig) {
+ sig = xcalloc(1, sizeof(*sig));
+ }
+
+ /* Create a BIGNUM from the signature pointer.
+ Note: this assumes big-endian data as required
+ by the PGP multiprecision integer format
+ (RFC4880, Section 3.2) */
+ bn = BN_bin2bn(p+2, mlen, NULL);
+ if (!bn) return 1;
+
+ switch (num) {
+ case 0:
+ if (sig->r) {
+ /* This should only ever happen once per signature */
+ BN_free(bn);
+ return 1;
+ }
+ sig->r = bn;
+ rc = 0;
+ break;
+ case 1:
+ if (sig->s) {
+ /* This should only ever happen once per signature */
+ BN_free(bn);
+ return 1;
+ }
+ sig->s = bn;
+ rc = 0;
+ break;
+ }
+
+ pgpsig->data = sig;
+
+ return rc;
+}
+
+static void pgpFreeSigDSA(pgpDigAlg pgpsig)
+{
+ struct pgpDigSigDSA_s *sig = pgpsig->data;
+ if (sig) {
+ if (sig->dsa_sig) {
+ DSA_SIG_free(sig->dsa_sig);
+ } else {
+ /* If sig->dsa_sig was constructed,
+ * the memory management of these BNs
+ * are freed with it. */
+ BN_clear_free(sig->r);
+ BN_clear_free(sig->s);
+ }
+ free(pgpsig->data);
+ }
+}
+
+static int pgpVerifySigDSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig,
+ uint8_t *hash, size_t hashlen, int hash_algo)
+{
+ int rc, ret;
+ struct pgpDigSigDSA_s *sig = pgpsig->data;
+
+ struct pgpDigKeyDSA_s *key = pgpkey->data;
+
+ if(!constructDSASigningKey(key)) {
+ rc = 1;
+ goto done;
+ }
+
+ if (!constructDSASignature(sig)) {
+ rc = 1;
+ goto done;
+ }
+
+ ret = DSA_do_verify(hash, hashlen, sig->dsa_sig, key->dsa_key);
+ if (ret == 1)
+ {
+ /* Success */
+ rc = 0;
+ }
+ else
+ {
+ /* Failure */
+ rc = 1;
+ }
+
+done:
+ return rc;
+}
+
+/****************************** NULL **************************************/
+
+static int pgpSetMpiNULL(pgpDigAlg pgpkey, int num, const uint8_t *p)
+{
+ return 1;
+}
+
+static int pgpVerifyNULL(pgpDigAlg pgpkey, pgpDigAlg pgpsig,
+ uint8_t *hash, size_t hashlen, int hash_algo)
+{
+ return 1;
+}
+
+/****************************** PGP **************************************/
+pgpDigAlg pgpPubkeyNew(int algo)
+{
+ pgpDigAlg ka = xcalloc(1, sizeof(*ka));;
+
+ switch (algo) {
+ case PGPPUBKEYALGO_RSA:
+ ka->setmpi = pgpSetKeyMpiRSA;
+ ka->free = pgpFreeKeyRSA;
+ ka->mpis = 2;
+ break;
+ case PGPPUBKEYALGO_DSA:
+ ka->setmpi = pgpSetKeyMpiDSA;
+ ka->free = pgpFreeKeyDSA;
+ ka->mpis = 4;
+ break;
+ default:
+ ka->setmpi = pgpSetMpiNULL;
+ ka->mpis = -1;
+ break;
+ }
+
+ ka->verify = pgpVerifyNULL; /* keys can't be verified */
+
+ return ka;
+}
+
+pgpDigAlg pgpSignatureNew(int algo)
+{
+ pgpDigAlg sa = xcalloc(1, sizeof(*sa));
+
+ switch (algo) {
+ case PGPPUBKEYALGO_RSA:
+ sa->setmpi = pgpSetSigMpiRSA;
+ sa->free = pgpFreeSigRSA;
+ sa->verify = pgpVerifySigRSA;
+ sa->mpis = 1;
+ break;
+ case PGPPUBKEYALGO_DSA:
+ sa->setmpi = pgpSetSigMpiDSA;
+ sa->free = pgpFreeSigDSA;
+ sa->verify = pgpVerifySigDSA;
+ sa->mpis = 2;
+ break;
+ default:
+ sa->setmpi = pgpSetMpiNULL;
+ sa->verify = pgpVerifyNULL;
+ sa->mpis = -1;
+ break;
+ }
+ return sa;
+}