Add support for using MD5 functions from OpenSSL.

Feature request #335
This commit is contained in:
John McNamara 2021-05-27 21:08:58 +01:00
parent acc0640237
commit 925a147fc1
12 changed files with 97 additions and 76 deletions

View file

@ -12,6 +12,7 @@ env:
- NO_VALGRIND=1 USE_SYSTEM_MINIZIP=1 CFLAGS='-Werror' - NO_VALGRIND=1 USE_SYSTEM_MINIZIP=1 CFLAGS='-Werror'
- NO_VALGRIND=1 USE_DOUBLE_FUNCTION=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_NO_MD5=1 CFLAGS='-Werror'
- NO_VALGRIND=1 USE_OPENSSL_MD5=1 CFLAGS='-Werror'
- NO_VALGRIND=1 USE_FMEMOPEN=1 CFLAGS='-Werror' - NO_VALGRIND=1 USE_FMEMOPEN=1 CFLAGS='-Werror'
addons: addons:
@ -20,6 +21,7 @@ addons:
packages: packages:
- gcc-multilib - gcc-multilib
- libminizip-dev - libminizip-dev
- libssl-dev
- valgrind - valgrind
- zlib1g-dev - zlib1g-dev
- zlib1g-dev:i386 - zlib1g-dev:i386

View file

@ -53,6 +53,12 @@
# file. To enable this option pass `-DUSE_NO_MD5=ON` during # file. To enable this option pass `-DUSE_NO_MD5=ON` during
# configuration. # 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_STATIC_MSVC_RUNTIME
# Use the static msvc runtime library when compiling with msvc (default off) # Use the static msvc runtime library when compiling with msvc (default off)
# To enable, pass `-DUSE_STATIC_MSVC_RUNTIME` during configuration. # 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_SYSTEM_MINIZIP "Use system minizip installation" OFF)
option(USE_STANDARD_TMPFILE "Use the C standard library's tmpfile()" 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_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(USE_FMEMOPEN "Use fmemopen() in place of some temporary files" OFF)
option(IOAPI_NO_64 "Disable 64-bit filesystem support" 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) list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_STANDARD_TMPFILE)
endif() endif()
if(USE_NO_MD5) if(NOT USE_OPENSSL_MD5 AND USE_NO_MD5)
list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_NO_MD5) list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_NO_MD5)
endif() endif()
if(USE_OPENSSL_MD5)
list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_OPENSSL_MD5)
endif()
if(USE_FMEMOPEN) if(USE_FMEMOPEN)
list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_FMEMOPEN) list(APPEND LXW_PRIVATE_COMPILE_DEFINITIONS USE_FMEMOPEN)
endif() endif()
@ -230,10 +241,14 @@ if (NOT USE_STANDARD_TMPFILE)
list(APPEND LXW_SOURCES third_party/tmpfileplus/tmpfileplus.c) list(APPEND LXW_SOURCES third_party/tmpfileplus/tmpfileplus.c)
endif() 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) list(APPEND LXW_SOURCES third_party/md5/md5.c)
endif() endif()
if(USE_OPENSSL_MD5)
set(LIB_CRYPTO "crypto")
endif()
set(LXW_PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(LXW_PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(LXW_LIB_DIR "${LXW_PROJECT_DIR}/lib") set(LXW_LIB_DIR "${LXW_PROJECT_DIR}/lib")
add_library(${PROJECT_NAME} "") add_library(${PROJECT_NAME} "")
@ -241,7 +256,7 @@ target_sources(${PROJECT_NAME}
PRIVATE ${LXW_SOURCES} PRIVATE ${LXW_SOURCES}
PUBLIC ${LXW_HEADERS} 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}) target_compile_definitions(${PROJECT_NAME} PRIVATE ${LXW_PRIVATE_COMPILE_DEFINITIONS})
# /utf-8 needs VS2015 Update 2 or above. # /utf-8 needs VS2015 Update 2 or above.

View file

@ -19,6 +19,7 @@ PYTESTFILES ?= test
VERSION = $(shell sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < include/xlsxwriter.h) VERSION = $(shell sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < include/xlsxwriter.h)
.PHONY: docs tags examples .PHONY: docs tags examples
# Build the libs. # Build the libs.
@ -29,9 +30,13 @@ endif
ifndef USE_STANDARD_TMPFILE ifndef USE_STANDARD_TMPFILE
$(Q)$(MAKE) -C third_party/tmpfileplus $(Q)$(MAKE) -C third_party/tmpfileplus
endif endif
ifndef USE_NO_MD5 ifndef USE_NO_MD5
ifndef USE_OPENSSL_MD5
$(Q)$(MAKE) -C third_party/md5 $(Q)$(MAKE) -C third_party/md5
endif endif
endif
$(Q)$(MAKE) -C src $(Q)$(MAKE) -C src
universal_binary : universal_binary :
@ -63,15 +68,9 @@ clean :
$(Q)rm -rf test/functional/__pycache__ $(Q)rm -rf test/functional/__pycache__
$(Q)rm -f test/functional/*.pyc $(Q)rm -f test/functional/*.pyc
$(Q)rm -f lib/* $(Q)rm -f lib/*
ifndef USE_SYSTEM_MINIZIP
$(Q)$(MAKE) clean -C third_party/minizip $(Q)$(MAKE) clean -C third_party/minizip
endif
ifndef USE_STANDARD_TMPFILE
$(Q)$(MAKE) clean -C third_party/tmpfileplus $(Q)$(MAKE) clean -C third_party/tmpfileplus
endif
ifndef USE_NO_MD5
$(Q)$(MAKE) clean -C third_party/md5 $(Q)$(MAKE) clean -C third_party/md5
endif
# Run the unit tests. # Run the unit tests.
test : all test_unit test_functional test : all test_unit test_functional
@ -88,17 +87,7 @@ test_functional : all
$(Q)$(PYTEST) test/functional -v -k $(PYTESTFILES) $(Q)$(PYTEST) test/functional -v -k $(PYTESTFILES)
# Run all tests. # Run all tests.
test_unit : test_unit : all
@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
$(Q)$(MAKE) -C src test_lib $(Q)$(MAKE) -C src test_lib
$(Q)$(MAKE) -C test/unit test $(Q)$(MAKE) -C test/unit test
@ -165,16 +154,7 @@ strip:
$(Q)strip lib/* $(Q)strip lib/*
# Run a coverity static analysis. # Run a coverity static analysis.
coverity: coverity: all
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
$(Q)$(MAKE) -C src clean $(Q)$(MAKE) -C src clean
$(Q)rm -f lib/* $(Q)rm -f lib/*
$(Q)rm -rf cov-int $(Q)rm -rf cov-int
@ -185,16 +165,7 @@ endif
$(Q)rm -f lib/* $(Q)rm -f lib/*
# Run a scan-build static analysis. # Run a scan-build static analysis.
scan_build: scan_build: all
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
$(Q)$(MAKE) -C src clean $(Q)$(MAKE) -C src clean
$(Q)rm -f lib/* $(Q)rm -f lib/*
$(Q)scan-build make -C src libxlsxwriter.a $(Q)scan-build make -C src libxlsxwriter.a

View file

@ -27,6 +27,9 @@ LIBS = $(LIBXLSXWRITER) -lz
ifdef USE_SYSTEM_MINIZIP ifdef USE_SYSTEM_MINIZIP
LIBS += -lminizip LIBS += -lminizip
endif endif
ifdef USE_OPENSSL_MD5
LIBS += -lcrypto
endif
all : $(LIBXLSXWRITER) $(EXES) all : $(LIBXLSXWRITER) $(EXES)

View file

@ -23,21 +23,23 @@
* See md5.c for more information. * See md5.c for more information.
*/ */
#ifndef __LXW_MD5_H__ #ifdef HAVE_OPENSSL
#define __LXW_MD5_H__ #include <openssl/md5.h>
#elif !defined(_MD5_H)
#define _MD5_H
/* Any 32-bit or wider unsigned integer data type will do */ /* Any 32-bit or wider unsigned integer data type will do */
typedef unsigned int uint32_t; typedef unsigned int MD5_u32plus;
typedef struct { typedef struct {
uint32_t lo, hi; MD5_u32plus lo, hi;
uint32_t a, b, c, d; MD5_u32plus a, b, c, d;
unsigned char buffer[64]; unsigned char buffer[64];
uint32_t block[16]; MD5_u32plus block[16];
} lxw_md5_ctx; } MD5_CTX;
extern void lxw_md5_init(lxw_md5_ctx *ctx); extern void MD5_Init(MD5_CTX *ctx);
extern void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size); extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
extern void lxw_md5_final(unsigned char *result, lxw_md5_ctx *ctx); extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
#endif #endif

View file

@ -45,11 +45,16 @@ ifdef USE_NO_MD5
# Don't use MD5 to avoid duplicate image files. # Don't use MD5 to avoid duplicate image files.
CFLAGS += -DUSE_NO_MD5 CFLAGS += -DUSE_NO_MD5
else else
ifdef USE_OPENSSL_MD5
CFLAGS += -DUSE_OPENSSL_MD5
LIBS += -lcrypto
else
# Use md5 (the default). # Use md5 (the default).
MD5_DIR = ../third_party/md5 MD5_DIR = ../third_party/md5
MD5_OBJ = $(MD5_DIR)/md5.o MD5_OBJ = $(MD5_DIR)/md5.o
MD5_SO = $(MD5_DIR)/md5.so MD5_SO = $(MD5_DIR)/md5.so
endif endif
endif
# Set flag for big endian architecture. # Set flag for big endian architecture.
ifdef USE_BIG_ENDIAN ifdef USE_BIG_ENDIAN

View file

@ -15,7 +15,14 @@
#include "xlsxwriter/worksheet.h" #include "xlsxwriter/worksheet.h"
#include "xlsxwriter/format.h" #include "xlsxwriter/format.h"
#include "xlsxwriter/utility.h" #include "xlsxwriter/utility.h"
#ifdef USE_OPENSSL_MD5
#include <openssl/md5.h>
#else
#ifndef USE_NO_MD5
#include "xlsxwriter/third_party/md5.h" #include "xlsxwriter/third_party/md5.h"
#endif
#endif
#define LXW_STR_MAX 32767 #define LXW_STR_MAX 32767
#define LXW_BUFFER_SIZE 4096 #define LXW_BUFFER_SIZE 4096
@ -3501,7 +3508,7 @@ _get_image_properties(lxw_object_properties *image_props)
unsigned char signature[4]; unsigned char signature[4];
#ifndef USE_NO_MD5 #ifndef USE_NO_MD5
uint8_t i; uint8_t i;
lxw_md5_ctx md5_context; MD5_CTX md5_context;
size_t size_read; size_t size_read;
char buffer[LXW_IMAGE_BUFFER_SIZE]; char buffer[LXW_IMAGE_BUFFER_SIZE];
unsigned char md5_checksum[LXW_MD5_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.*/ * images to reduce the xlsx file size.*/
rewind(image_props->stream); 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); size_read = fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream);
while (size_read) { while (size_read) {
lxw_md5_update(&md5_context, buffer, size_read); MD5_Update(&md5_context, buffer, size_read);
size_read = size_read =
fread(buffer, 1, LXW_IMAGE_BUFFER_SIZE, image_props->stream); 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. */ /* Create a 32 char hex string buffer for the MD5 checksum. */
image_props->md5 = calloc(1, LXW_MD5_SIZE * 2 + 1); image_props->md5 = calloc(1, LXW_MD5_SIZE * 2 + 1);

View file

@ -31,6 +31,10 @@ LIBS += -lminizip
CFLAGS += -DUSE_SYSTEM_MINIZIP CFLAGS += -DUSE_SYSTEM_MINIZIP
endif endif
ifdef USE_OPENSSL_MD5
LIBS += -lcrypto
endif
# Use a user-defined double number formatting function. # Use a user-defined double number formatting function.
ifdef USE_DOUBLE_FUNCTION ifdef USE_DOUBLE_FUNCTION
CFLAGS += -DUSE_DOUBLE_FUNCTION CFLAGS += -DUSE_DOUBLE_FUNCTION

View file

@ -52,6 +52,10 @@ ifdef USE_SYSTEM_MINIZIP
LIBS_O += -lminizip LIBS_O += -lminizip
CFLAGS += -DUSE_SYSTEM_MINIZIP CFLAGS += -DUSE_SYSTEM_MINIZIP
endif endif
ifdef USE_OPENSSL_MD5
LIBS_O += -lcrypto
endif
# End of LIBS # End of LIBS
# Use a user-defined double number formatting function. # Use a user-defined double number formatting function.

View file

@ -31,6 +31,12 @@ LIBS_O += -lminizip
CFLAGS += -DUSE_SYSTEM_MINIZIP CFLAGS += -DUSE_SYSTEM_MINIZIP
endif endif
# Link libcrypto if needed.
ifdef USE_OPENSSL_MD5
LIBS_O += -lcrypto
endif
# Use a user-defined double number formatting function. # Use a user-defined double number formatting function.
ifdef USE_DOUBLE_FUNCTION ifdef USE_DOUBLE_FUNCTION
CFLAGS += -DUSE_DOUBLE_FUNCTION CFLAGS += -DUSE_DOUBLE_FUNCTION

24
third_party/md5/md5.c vendored
View file

@ -79,16 +79,16 @@
*/ */
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) #if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
#define SET(n) \ #define SET(n) \
(*(uint32_t *)&ptr[(n) * 4]) (*(MD5_u32plus *)&ptr[(n) * 4])
#define GET(n) \ #define GET(n) \
SET(n) SET(n)
#else #else
#define SET(n) \ #define SET(n) \
(ctx->block[(n)] = \ (ctx->block[(n)] = \
(uint32_t)ptr[(n) * 4] | \ (MD5_u32plus)ptr[(n) * 4] | \
((uint32_t)ptr[(n) * 4 + 1] << 8) | \ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
((uint32_t)ptr[(n) * 4 + 2] << 16) | \ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
((uint32_t)ptr[(n) * 4 + 3] << 24)) ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
#define GET(n) \ #define GET(n) \
(ctx->block[(n)]) (ctx->block[(n)])
#endif #endif
@ -97,11 +97,11 @@
* This processes one or more 64-byte data blocks, but does NOT update the bit * This processes one or more 64-byte data blocks, but does NOT update the bit
* counters. There are no alignment requirements. * 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; const unsigned char *ptr;
uint32_t a, b, c, d; MD5_u32plus a, b, c, d;
uint32_t saved_a, saved_b, saved_c, saved_d; MD5_u32plus saved_a, saved_b, saved_c, saved_d;
ptr = (const unsigned char *)data; 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; return ptr;
} }
void lxw_md5_init(lxw_md5_ctx *ctx) void MD5_Init(MD5_CTX *ctx)
{ {
ctx->a = 0x67452301; ctx->a = 0x67452301;
ctx->b = 0xefcdab89; ctx->b = 0xefcdab89;
@ -215,9 +215,9 @@ void lxw_md5_init(lxw_md5_ctx *ctx)
ctx->hi = 0; 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; unsigned long used, available;
saved_lo = ctx->lo; 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)[2] = (unsigned char)((src) >> 16); \
(dst)[3] = (unsigned char)((src) >> 24); (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; unsigned long used, available;

22
third_party/md5/md5.h vendored
View file

@ -23,21 +23,23 @@
* See md5.c for more information. * See md5.c for more information.
*/ */
#ifndef __LXW_MD5_H__ #ifdef HAVE_OPENSSL
#define __LXW_MD5_H__ #include <openssl/md5.h>
#elif !defined(_MD5_H)
#define _MD5_H
/* Any 32-bit or wider unsigned integer data type will do */ /* Any 32-bit or wider unsigned integer data type will do */
typedef unsigned int uint32_t; typedef unsigned int MD5_u32plus;
typedef struct { typedef struct {
uint32_t lo, hi; MD5_u32plus lo, hi;
uint32_t a, b, c, d; MD5_u32plus a, b, c, d;
unsigned char buffer[64]; unsigned char buffer[64];
uint32_t block[16]; MD5_u32plus block[16];
} lxw_md5_ctx; } MD5_CTX;
extern void lxw_md5_init(lxw_md5_ctx *ctx); extern void MD5_Init(MD5_CTX *ctx);
extern void lxw_md5_update(lxw_md5_ctx *ctx, const void *data, unsigned long size); extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
extern void lxw_md5_final(unsigned char *result, lxw_md5_ctx *ctx); extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
#endif #endif