| From 64c75ba68118e47f65eedf15855d8a50deeebcd1 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) |
| |
| 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 |
| |
| |
| @@ -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 6b37b5898..42a77c0d4 100644 |
| |
| |
| @@ -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 91fadbade..d21b67220 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |