Blob Blame History Raw
From 64028f9a1c25ada8ffc7a48775f526600edcbf85 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
---
 INSTALL                |  27 +-
 Makefile.am            |  17 +-
 configure.ac           | 108 ++++++-
 rpmio/Makefile.am      |   6 +
 rpmio/digest_openssl.c | 838 +++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 975 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 b99da12ec..56aa485f7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -43,6 +43,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/lib
 AM_CPPFLAGS += -I$(top_srcdir)/rpmio
 AM_CPPFLAGS += @WITH_BEECRYPT_INCLUDE@
 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)\""
@@ -111,45 +112,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_BEECRYPT_LIB@ @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpm_LDADD +=		@WITH_BEECRYPT_LIB@ @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_BEECRYPT_LIB@ @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmdb_LDADD +=		@WITH_BEECRYPT_LIB@ @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_BEECRYPT_LIB@ @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmkeys_LDADD +=	@WITH_BEECRYPT_LIB@ @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_BEECRYPT_LIB@ @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmsign_LDADD +=	@WITH_BEECRYPT_LIB@ @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_BEECRYPT_LIB@ @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmbuild_LDADD += 	@WITH_BEECRYPT_LIB@ @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_BEECRYPT_LIB@ @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpmspec_LDADD +=	@WITH_BEECRYPT_LIB@ @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_BEECRYPT_LIB@ @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@
+rpm2cpio_LDADD +=	@WITH_BEECRYPT_LIB@ @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_BEECRYPT_LIB@ @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@ @WITH_ARCHIVE_LIB@
+rpm2archive_LDADD +=	@WITH_BEECRYPT_LIB@ @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 4f3be8770..9ecef95d1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -245,18 +245,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"
@@ -265,7 +277,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.])
     ])
@@ -275,13 +287,93 @@ 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.
diff --git a/rpmio/Makefile.am b/rpmio/Makefile.am
index 5ad4d6818..3ac1c715d 100644
--- a/rpmio/Makefile.am
+++ b/rpmio/Makefile.am
@@ -6,6 +6,7 @@ AM_CFLAGS = @RPMCFLAGS@
 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@\""
@@ -24,8 +25,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)
@@ -33,6 +38,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;
+}
-- 
2.12.0