From 925a147fc196ff5469e606a34c0bb58ffb367386 Mon Sep 17 00:00:00 2001 From: John McNamara Date: Thu, 27 May 2021 21:08:58 +0100 Subject: [PATCH] Add support for using MD5 functions from OpenSSL. Feature request #335 --- .travis.yml | 2 ++ CMakeLists.txt | 21 +++++++++++-- Makefile | 45 +++++----------------------- examples/Makefile | 3 ++ include/xlsxwriter/third_party/md5.h | 22 +++++++------- src/Makefile | 5 ++++ src/worksheet.c | 15 +++++++--- test/functional/src/Makefile | 4 +++ test/unit/Makefile | 4 +++ test/unit/Makefile.unit | 6 ++++ third_party/md5/md5.c | 24 +++++++-------- third_party/md5/md5.h | 22 +++++++------- 12 files changed, 97 insertions(+), 76 deletions(-) diff --git a/.travis.yml b/.travis.yml index fe52e3c0..b36a3e17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ env: - NO_VALGRIND=1 USE_SYSTEM_MINIZIP=1 CFLAGS='-Werror' - NO_VALGRIND=1 USE_DOUBLE_FUNCTION=1 CFLAGS='-Werror' - NO_VALGRIND=1 USE_NO_MD5=1 CFLAGS='-Werror' + - NO_VALGRIND=1 USE_OPENSSL_MD5=1 CFLAGS='-Werror' - NO_VALGRIND=1 USE_FMEMOPEN=1 CFLAGS='-Werror' addons: @@ -20,6 +21,7 @@ addons: packages: - gcc-multilib - libminizip-dev + - libssl-dev - valgrind - zlib1g-dev - zlib1g-dev:i386 diff --git a/CMakeLists.txt b/CMakeLists.txt index 99224a06..e8816d81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,12 @@ # file. To enable this option pass `-DUSE_NO_MD5=ON` during # configuration. # +# USE_OPENSSL_MD5 Compile with OpenSSL MD5 support. This will link +# against libcrypto for MD5 support rather than using the local MD5 +# support. MD5 support is required to avoid duplicate image files in +# the output xlsx file. To enable this option pass +# `-DUSE_OPENSSL_MD5=ON` during configuration. +# # USE_STATIC_MSVC_RUNTIME # Use the static msvc runtime library when compiling with msvc (default off) # To enable, pass `-DUSE_STATIC_MSVC_RUNTIME` during configuration. @@ -118,6 +124,7 @@ option(BUILD_EXAMPLES "Build libxlsxwriter examples" OFF) option(USE_SYSTEM_MINIZIP "Use system minizip installation" OFF) option(USE_STANDARD_TMPFILE "Use the C standard library's tmpfile()" OFF) option(USE_NO_MD5 "Build libxlsxwriter without third party MD5 lib" OFF) +option(USE_OPENSSL_MD5 "Build libxlsxwriter with the OpenSSL MD5 lib" OFF) option(USE_FMEMOPEN "Use fmemopen() in place of some temporary files" OFF) option(IOAPI_NO_64 "Disable 64-bit filesystem support" OFF) @@ -143,10 +150,14 @@ if(USE_STANDARD_TMPFILE) list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_STANDARD_TMPFILE) endif() -if(USE_NO_MD5) +if(NOT USE_OPENSSL_MD5 AND USE_NO_MD5) list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_NO_MD5) endif() +if(USE_OPENSSL_MD5) + list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_OPENSSL_MD5) +endif() + if(USE_FMEMOPEN) list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_FMEMOPEN) endif() @@ -230,10 +241,14 @@ if (NOT USE_STANDARD_TMPFILE) list(APPEND LXW_SOURCES third_party/tmpfileplus/tmpfileplus.c) endif() -if (NOT USE_NO_MD5) +if(NOT USE_OPENSSL_MD5 AND NOT USE_NO_MD5) list(APPEND LXW_SOURCES third_party/md5/md5.c) endif() +if(USE_OPENSSL_MD5) + set(LIB_CRYPTO "crypto") +endif() + set(LXW_PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(LXW_LIB_DIR "${LXW_PROJECT_DIR}/lib") add_library(${PROJECT_NAME} "") @@ -241,7 +256,7 @@ target_sources(${PROJECT_NAME} PRIVATE ${LXW_SOURCES} PUBLIC ${LXW_HEADERS} ) -target_link_libraries(${PROJECT_NAME} LINK_PUBLIC ${ZLIB_LIBRARIES} ${MINIZIP_LIBRARIES}) +target_link_libraries(${PROJECT_NAME} LINK_PUBLIC ${ZLIB_LIBRARIES} ${MINIZIP_LIBRARIES} ${LIB_CRYPTO}) target_compile_definitions(${PROJECT_NAME} PRIVATE ${LXW_PRIVATE_COMPILE_DEFINITIONS}) # /utf-8 needs VS2015 Update 2 or above. diff --git a/Makefile b/Makefile index e0245aa5..bcb06390 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ PYTESTFILES ?= test VERSION = $(shell sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < include/xlsxwriter.h) + .PHONY: docs tags examples # Build the libs. @@ -29,9 +30,13 @@ endif ifndef USE_STANDARD_TMPFILE $(Q)$(MAKE) -C third_party/tmpfileplus endif + ifndef USE_NO_MD5 +ifndef USE_OPENSSL_MD5 $(Q)$(MAKE) -C third_party/md5 endif +endif + $(Q)$(MAKE) -C src universal_binary : @@ -63,15 +68,9 @@ clean : $(Q)rm -rf test/functional/__pycache__ $(Q)rm -f test/functional/*.pyc $(Q)rm -f lib/* -ifndef USE_SYSTEM_MINIZIP $(Q)$(MAKE) clean -C third_party/minizip -endif -ifndef USE_STANDARD_TMPFILE $(Q)$(MAKE) clean -C third_party/tmpfileplus -endif -ifndef USE_NO_MD5 $(Q)$(MAKE) clean -C third_party/md5 -endif # Run the unit tests. test : all test_unit test_functional @@ -88,17 +87,7 @@ test_functional : all $(Q)$(PYTEST) test/functional -v -k $(PYTESTFILES) # Run all tests. -test_unit : - @echo "Compiling unit tests ..." -ifndef USE_SYSTEM_MINIZIP - $(Q)$(MAKE) -C third_party/minizip -endif -ifndef USE_STANDARD_TMPFILE - $(Q)$(MAKE) -C third_party/tmpfileplus -endif -ifndef USE_NO_MD5 - $(Q)$(MAKE) -C third_party/md5 -endif +test_unit : all $(Q)$(MAKE) -C src test_lib $(Q)$(MAKE) -C test/unit test @@ -165,16 +154,7 @@ strip: $(Q)strip lib/* # Run a coverity static analysis. -coverity: -ifndef USE_SYSTEM_MINIZIP - $(Q)$(MAKE) -C third_party/minizip -endif -ifndef USE_STANDARD_TMPFILE - $(Q)$(MAKE) -C third_party/tmpfileplus -endif -ifndef USE_NO_MD5 - $(Q)$(MAKE) -C third_party/md5 -endif +coverity: all $(Q)$(MAKE) -C src clean $(Q)rm -f lib/* $(Q)rm -rf cov-int @@ -185,16 +165,7 @@ endif $(Q)rm -f lib/* # Run a scan-build static analysis. -scan_build: -ifndef USE_SYSTEM_MINIZIP - $(Q)$(MAKE) -C third_party/minizip -endif -ifndef USE_STANDARD_TMPFILE - $(Q)$(MAKE) -C third_party/tmpfileplus -endif -ifndef USE_NO_MD5 - $(Q)$(MAKE) -C third_party/md5 -endif +scan_build: all $(Q)$(MAKE) -C src clean $(Q)rm -f lib/* $(Q)scan-build make -C src libxlsxwriter.a diff --git a/examples/Makefile b/examples/Makefile index 9cea88b5..c95d40d6 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -27,6 +27,9 @@ LIBS = $(LIBXLSXWRITER) -lz ifdef USE_SYSTEM_MINIZIP LIBS += -lminizip endif +ifdef USE_OPENSSL_MD5 +LIBS += -lcrypto +endif all : $(LIBXLSXWRITER) $(EXES) diff --git a/include/xlsxwriter/third_party/md5.h b/include/xlsxwriter/third_party/md5.h index 8d0844f0..2da44bf3 100644 --- a/include/xlsxwriter/third_party/md5.h +++ b/include/xlsxwriter/third_party/md5.h @@ -23,21 +23,23 @@ * See md5.c for more information. */ -#ifndef __LXW_MD5_H__ -#define __LXW_MD5_H__ +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H /* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int uint32_t; +typedef unsigned int MD5_u32plus; typedef struct { - uint32_t lo, hi; - uint32_t a, b, c, d; + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; unsigned char buffer[64]; - uint32_t block[16]; -} lxw_md5_ctx; + MD5_u32plus block[16]; +} MD5_CTX; -extern void lxw_md5_init(lxw_md5_ctx *ctx); -extern void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size); -extern void lxw_md5_final(unsigned char *result, lxw_md5_ctx *ctx); +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); #endif diff --git a/src/Makefile b/src/Makefile index 835ed7ae..e3ba6663 100644 --- a/src/Makefile +++ b/src/Makefile @@ -45,11 +45,16 @@ ifdef USE_NO_MD5 # Don't use MD5 to avoid duplicate image files. CFLAGS += -DUSE_NO_MD5 else +ifdef USE_OPENSSL_MD5 +CFLAGS += -DUSE_OPENSSL_MD5 +LIBS += -lcrypto +else # Use md5 (the default). MD5_DIR = ../third_party/md5 MD5_OBJ = $(MD5_DIR)/md5.o MD5_SO = $(MD5_DIR)/md5.so endif +endif # Set flag for big endian architecture. ifdef USE_BIG_ENDIAN diff --git a/src/worksheet.c b/src/worksheet.c index 37ea0b57..55a5c87f 100644 --- a/src/worksheet.c +++ b/src/worksheet.c @@ -15,7 +15,14 @@ #include "xlsxwriter/worksheet.h" #include "xlsxwriter/format.h" #include "xlsxwriter/utility.h" + +#ifdef USE_OPENSSL_MD5 +#include +#else +#ifndef USE_NO_MD5 #include "xlsxwriter/third_party/md5.h" +#endif +#endif #define LXW_STR_MAX 32767 #define LXW_BUFFER_SIZE 4096 @@ -3501,7 +3508,7 @@ _get_image_properties(lxw_object_properties *image_props) unsigned char signature[4]; #ifndef USE_NO_MD5 uint8_t i; - lxw_md5_ctx md5_context; + MD5_CTX md5_context; size_t size_read; char buffer[LXW_IMAGE_BUFFER_SIZE]; unsigned char md5_checksum[LXW_MD5_SIZE]; @@ -3543,16 +3550,16 @@ _get_image_properties(lxw_object_properties *image_props) * images to reduce the xlsx file size.*/ rewind(image_props->stream); - lxw_md5_init(&md5_context); + MD5_Init(&md5_context); size_read = fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); while (size_read) { - lxw_md5_update(&md5_context, buffer, size_read); + MD5_Update(&md5_context, buffer, size_read); size_read = fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); } - lxw_md5_final(md5_checksum, &md5_context); + MD5_Final(md5_checksum, &md5_context); /* Create a 32 char hex string buffer for the MD5 checksum. */ image_props->md5 = calloc(1, LXW_MD5_SIZE * 2 + 1); diff --git a/test/functional/src/Makefile b/test/functional/src/Makefile index 4608eba1..afb1015a 100644 --- a/test/functional/src/Makefile +++ b/test/functional/src/Makefile @@ -31,6 +31,10 @@ LIBS += -lminizip CFLAGS += -DUSE_SYSTEM_MINIZIP endif +ifdef USE_OPENSSL_MD5 +LIBS += -lcrypto +endif + # Use a user-defined double number formatting function. ifdef USE_DOUBLE_FUNCTION CFLAGS += -DUSE_DOUBLE_FUNCTION diff --git a/test/unit/Makefile b/test/unit/Makefile index e9a40982..94d6d675 100644 --- a/test/unit/Makefile +++ b/test/unit/Makefile @@ -52,6 +52,10 @@ ifdef USE_SYSTEM_MINIZIP LIBS_O += -lminizip CFLAGS += -DUSE_SYSTEM_MINIZIP endif +ifdef USE_OPENSSL_MD5 +LIBS_O += -lcrypto +endif + # End of LIBS # Use a user-defined double number formatting function. diff --git a/test/unit/Makefile.unit b/test/unit/Makefile.unit index bfd08f1b..47e37edc 100644 --- a/test/unit/Makefile.unit +++ b/test/unit/Makefile.unit @@ -31,6 +31,12 @@ LIBS_O += -lminizip CFLAGS += -DUSE_SYSTEM_MINIZIP endif +# Link libcrypto if needed. +ifdef USE_OPENSSL_MD5 +LIBS_O += -lcrypto +endif + + # Use a user-defined double number formatting function. ifdef USE_DOUBLE_FUNCTION CFLAGS += -DUSE_DOUBLE_FUNCTION diff --git a/third_party/md5/md5.c b/third_party/md5/md5.c index 73f1914b..b235e17a 100644 --- a/third_party/md5/md5.c +++ b/third_party/md5/md5.c @@ -79,16 +79,16 @@ */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) #define SET(n) \ - (*(uint32_t *)&ptr[(n) * 4]) + (*(MD5_u32plus *)&ptr[(n) * 4]) #define GET(n) \ SET(n) #else #define SET(n) \ (ctx->block[(n)] = \ - (uint32_t)ptr[(n) * 4] | \ - ((uint32_t)ptr[(n) * 4 + 1] << 8) | \ - ((uint32_t)ptr[(n) * 4 + 2] << 16) | \ - ((uint32_t)ptr[(n) * 4 + 3] << 24)) + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) #define GET(n) \ (ctx->block[(n)]) #endif @@ -97,11 +97,11 @@ * This processes one or more 64-byte data blocks, but does NOT update the bit * counters. There are no alignment requirements. */ -static const void *body(lxw_md5_ctx *ctx, const void *data, unsigned long size) +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) { const unsigned char *ptr; - uint32_t a, b, c, d; - uint32_t saved_a, saved_b, saved_c, saved_d; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; ptr = (const unsigned char *)data; @@ -204,7 +204,7 @@ static const void *body(lxw_md5_ctx *ctx, const void *data, unsigned long size) return ptr; } -void lxw_md5_init(lxw_md5_ctx *ctx) +void MD5_Init(MD5_CTX *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -215,9 +215,9 @@ void lxw_md5_init(lxw_md5_ctx *ctx) ctx->hi = 0; } -void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size) +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) { - uint32_t saved_lo; + MD5_u32plus saved_lo; unsigned long used, available; saved_lo = ctx->lo; @@ -255,7 +255,7 @@ void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size) (dst)[2] = (unsigned char)((src) >> 16); \ (dst)[3] = (unsigned char)((src) >> 24); -void lxw_md5_final(unsigned char *result, lxw_md5_ctx *ctx) +void MD5_Final(unsigned char *result, MD5_CTX *ctx) { unsigned long used, available; diff --git a/third_party/md5/md5.h b/third_party/md5/md5.h index 8d0844f0..2da44bf3 100644 --- a/third_party/md5/md5.h +++ b/third_party/md5/md5.h @@ -23,21 +23,23 @@ * See md5.c for more information. */ -#ifndef __LXW_MD5_H__ -#define __LXW_MD5_H__ +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H /* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int uint32_t; +typedef unsigned int MD5_u32plus; typedef struct { - uint32_t lo, hi; - uint32_t a, b, c, d; + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; unsigned char buffer[64]; - uint32_t block[16]; -} lxw_md5_ctx; + MD5_u32plus block[16]; +} MD5_CTX; -extern void lxw_md5_init(lxw_md5_ctx *ctx); -extern void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size); -extern void lxw_md5_final(unsigned char *result, lxw_md5_ctx *ctx); +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); #endif