Skip to content

Commit

Permalink
pythongh-99108: Add HACL* Blake2 implementation to hashlib (pythonGH-…
Browse files Browse the repository at this point in the history
…119316)

This replaces the existing hashlib Blake2 module with a single implementation that uses HACL\*'s Blake2b/Blake2s implementations. We added support for all the modes exposed by the Python API, including tree hashing, leaf nodes, and so on. We ported and merged all of these changes upstream in HACL\*, added test vectors based on Python's existing implementation, and exposed everything needed for hashlib.

This was joint work done with @R1kM.

See the PR for much discussion and benchmarking details.   TL;DR: On many systems, 8-50% faster (!) than `libb2`, on some systems it appeared 10-20% slower than `libb2`.
  • Loading branch information
msprotz authored and blhsing committed Aug 22, 2024
1 parent 8c46cb4 commit ceaf4d1
Show file tree
Hide file tree
Showing 55 changed files with 11,594 additions and 5,133 deletions.
11 changes: 11 additions & 0 deletions Lib/test/test_hashlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,17 @@ def test_sha3_256_update_over_4gb(self):
h.update(b"hello world")
self.assertEqual(h.hexdigest(), "e2d4535e3b613135c14f2fe4e026d7ad8d569db44901740beffa30d430acb038")

@requires_resource('cpu')
def test_blake2_update_over_4gb(self):
# blake2s or blake2b doesn't matter based on how our C code is structured, this tests the
# common loop macro logic.
zero_1mb = b"\0" * 1024 * 1024
h = hashlib.blake2s()
for i in range(0, 4096):
h.update(zero_1mb)
h.update(b"hello world")
self.assertEqual(h.hexdigest(), "8a268e83dd30528bc0907fa2008c91de8f090a0b6e0e60a5ff0d999d8485526f")

def check(self, name, data, hexdigest, shake=False, **kwargs):
length = len(hexdigest)//2
hexdigest = hexdigest.lower()
Expand Down
66 changes: 53 additions & 13 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ ENSUREPIP= @ENSUREPIP@
LIBMPDEC_A= Modules/_decimal/libmpdec/libmpdec.a
LIBEXPAT_A= Modules/expat/libexpat.a
LIBHACL_SHA2_A= Modules/_hacl/libHacl_Hash_SHA2.a
LIBHACL_BLAKE2_A= Modules/_hacl/libHacl_Hash_Blake2.a
LIBHACL_SIMD128_FLAGS=@LIBHACL_SIMD128_FLAGS@
LIBHACL_SIMD256_FLAGS=@LIBHACL_SIMD256_FLAGS@
LIBHACL_SIMD128_OBJS=@LIBHACL_SIMD128_OBJS@
LIBHACL_SIMD256_OBJS=@LIBHACL_SIMD256_OBJS@

# Module state, compiler flags and linker flags
# Empty CFLAGS and LDFLAGS are omitted.
Expand Down Expand Up @@ -646,6 +651,13 @@ LIBEXPAT_HEADERS= \
LIBHACL_SHA2_OBJS= \
Modules/_hacl/Hacl_Hash_SHA2.o

LIBHACL_BLAKE2_OBJS= \
Modules/_hacl/Hacl_Hash_Blake2s.o \
Modules/_hacl/Hacl_Hash_Blake2b.o \
Modules/_hacl/Lib_Memzero0.o \
$(LIBHACL_SIMD128_OBJS) \
$(LIBHACL_SIMD256_OBJS)

LIBHACL_HEADERS= \
Modules/_hacl/include/krml/FStar_UInt128_Verified.h \
Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h \
Expand All @@ -661,6 +673,18 @@ LIBHACL_SHA2_HEADERS= \
Modules/_hacl/internal/Hacl_Hash_SHA2.h \
$(LIBHACL_HEADERS)

LIBHACL_BLAKE2_HEADERS= \
Modules/_hacl/Hacl_Hash_Blake2b.h \
Modules/_hacl/Hacl_Hash_Blake2s.h \
Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h \
Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h \
Modules/_hacl/internal/Hacl_Hash_Blake2b.h \
Modules/_hacl/internal/Hacl_Hash_Blake2s.h \
Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h \
Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h \
Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h \
$(LIBHACL_HEADERS)

#########################################################################
# Rules

Expand Down Expand Up @@ -840,7 +864,7 @@ coverage-lcov:
@ # remove 3rd party modules, system headers and internal files with
@ # debug, test or dummy functions.
@lcov $(COVERAGE_LCOV_OPTIONS) --remove $(COVERAGE_INFO) \
'*/Modules/_blake2/impl/*' \
'*/Modules/_hacl/*' \
'*/Modules/_ctypes/libffi*/*' \
'*/Modules/_decimal/libmpdec/*' \
'*/Modules/expat/*' \
Expand Down Expand Up @@ -870,7 +894,7 @@ coverage-report: regen-token regen-frozen

# Run "Argument Clinic" over all source files
.PHONY: clinic
clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c
clinic: check-clean-src
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py --make --exclude Lib/test/clinic.test.c --srcdir $(srcdir)

.PHONY: clinic-tests
Expand Down Expand Up @@ -900,11 +924,6 @@ pybuilddir.txt: $(PYTHON_FOR_BUILD_DEPS)
exit 1 ; \
fi

# blake2s is auto-generated from blake2b
$(srcdir)/Modules/_blake2/blake2s_impl.c: $(srcdir)/Modules/_blake2/blake2b_impl.c $(srcdir)/Modules/_blake2/blake2b2s.py
$(PYTHON_FOR_REGEN) $(srcdir)/Modules/_blake2/blake2b2s.py
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py -f $@

# Build static library
$(LIBRARY): $(LIBRARY_OBJS)
-rm -f $@
Expand Down Expand Up @@ -1346,8 +1365,10 @@ $(LIBEXPAT_A): $(LIBEXPAT_OBJS)
$(AR) $(ARFLAGS) $@ $(LIBEXPAT_OBJS)

##########################################################################
# Build HACL* static libraries for hashlib: libHacl_Hash_SHA2.a
LIBHACL_CFLAGS=-I$(srcdir)/Modules/_hacl/include -D_BSD_SOURCE -D_DEFAULT_SOURCE $(PY_STDMODULE_CFLAGS) $(CCSHARED)
# Build HACL* static libraries for hashlib: libHacl_Hash_SHA2.a, and
# libHacl_Blake2.a -- the contents of the latter vary depending on whether we
# have the ability to compile vectorized versions
LIBHACL_CFLAGS=-I$(srcdir)/Modules/_hacl -I$(srcdir)/Modules/_hacl/include -D_BSD_SOURCE -D_DEFAULT_SOURCE $(PY_STDMODULE_CFLAGS) $(CCSHARED)

Modules/_hacl/Hacl_Hash_SHA2.o: $(srcdir)/Modules/_hacl/Hacl_Hash_SHA2.c $(LIBHACL_SHA2_HEADERS)
$(CC) -c $(LIBHACL_CFLAGS) -o $@ $(srcdir)/Modules/_hacl/Hacl_Hash_SHA2.c
Expand All @@ -1356,6 +1377,25 @@ $(LIBHACL_SHA2_A): $(LIBHACL_SHA2_OBJS)
-rm -f $@
$(AR) $(ARFLAGS) $@ $(LIBHACL_SHA2_OBJS)

Modules/_hacl/Hacl_Hash_Blake2s.o: $(srcdir)/Modules/_hacl/Hacl_Hash_Blake2s.c $(LIBHACL_BLAKE2_HEADERS)
$(CC) -c $(LIBHACL_CFLAGS) -o $@ $(srcdir)/Modules/_hacl/Hacl_Hash_Blake2s.c

Modules/_hacl/Hacl_Hash_Blake2b.o: $(srcdir)/Modules/_hacl/Hacl_Hash_Blake2b.c $(LIBHACL_BLAKE2_HEADERS)
$(CC) -c $(LIBHACL_CFLAGS) -o $@ $(srcdir)/Modules/_hacl/Hacl_Hash_Blake2b.c

Modules/_hacl/Hacl_Hash_Blake2s_Simd128.o: $(srcdir)/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.c $(LIBHACL_BLAKE2_HEADERS)
$(CC) -c $(LIBHACL_CFLAGS) $(LIBHACL_SIMD128_FLAGS) -DHACL_CAN_COMPILE_VEC128 -o $@ $(srcdir)/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.c

Modules/_hacl/Hacl_Hash_Blake2b_Simd256.o: $(srcdir)/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.c $(LIBHACL_BLAKE2_HEADERS)
$(CC) -c $(LIBHACL_CFLAGS) $(LIBHACL_SIMD256_FLAGS) -DHACL_CAN_COMPILE_VEC256 -o $@ $(srcdir)/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.c

Modules/_hacl/Lib_Memzero0.o: $(srcdir)/Modules/_hacl/Lib_Memzero0.c $(LIBHACL_BLAKE2_HEADERS)
$(CC) -c $(LIBHACL_CFLAGS) -o $@ $(srcdir)/Modules/_hacl/Lib_Memzero0.c

$(LIBHACL_BLAKE2_A): $(LIBHACL_BLAKE2_OBJS)
-rm -f $@
$(AR) $(ARFLAGS) $@ $(LIBHACL_BLAKE2_OBJS)

# create relative links from build/lib.platform/egg.so to Modules/egg.so
# pybuilddir.txt is created too late. We cannot use it in Makefile
# targets. ln --relative is not portable.
Expand Down Expand Up @@ -3136,18 +3176,18 @@ MODULE_CMATH_DEPS=$(srcdir)/Modules/_math.h
MODULE_MATH_DEPS=$(srcdir)/Modules/_math.h
MODULE_PYEXPAT_DEPS=@LIBEXPAT_INTERNAL@
MODULE_UNICODEDATA_DEPS=$(srcdir)/Modules/unicodedata_db.h $(srcdir)/Modules/unicodename_db.h
MODULE__BLAKE2_DEPS=$(srcdir)/Modules/_blake2/impl/blake2-config.h $(srcdir)/Modules/_blake2/impl/blake2-impl.h $(srcdir)/Modules/_blake2/impl/blake2.h $(srcdir)/Modules/_blake2/impl/blake2b-load-sse2.h $(srcdir)/Modules/_blake2/impl/blake2b-load-sse41.h $(srcdir)/Modules/_blake2/impl/blake2b-ref.c $(srcdir)/Modules/_blake2/impl/blake2b-round.h $(srcdir)/Modules/_blake2/impl/blake2b.c $(srcdir)/Modules/_blake2/impl/blake2s-load-sse2.h $(srcdir)/Modules/_blake2/impl/blake2s-load-sse41.h $(srcdir)/Modules/_blake2/impl/blake2s-load-xop.h $(srcdir)/Modules/_blake2/impl/blake2s-ref.c $(srcdir)/Modules/_blake2/impl/blake2s-round.h $(srcdir)/Modules/_blake2/impl/blake2s.c $(srcdir)/Modules/_blake2/blake2module.h $(srcdir)/Modules/hashlib.h
MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h $(srcdir)/Modules/_complex.h
MODULE__CTYPES_TEST_DEPS=$(srcdir)/Modules/_ctypes/_ctypes_test_generated.c.h
MODULE__CTYPES_MALLOC_CLOSURE=@MODULE__CTYPES_MALLOC_CLOSURE@
MODULE__DECIMAL_DEPS=$(srcdir)/Modules/_decimal/docstrings.h @LIBMPDEC_INTERNAL@
MODULE__ELEMENTTREE_DEPS=$(srcdir)/Modules/pyexpat.c @LIBEXPAT_INTERNAL@
MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h
MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h
MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) Modules/_hacl/Hacl_Hash_MD5.h Modules/_hacl/Hacl_Hash_MD5.c
MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) Modules/_hacl/Hacl_Hash_SHA1.h Modules/_hacl/Hacl_Hash_SHA1.c
MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) Modules/_hacl/Hacl_Hash_MD5.h Modules/_hacl/internal/Hacl_Hash_MD5.h Modules/_hacl/Hacl_Hash_MD5.c
MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) Modules/_hacl/Hacl_Hash_SHA1.h Modules/_hacl/internal/Hacl_Hash_SHA1.h Modules/_hacl/Hacl_Hash_SHA1.c
MODULE__SHA2_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_SHA2_HEADERS) $(LIBHACL_SHA2_A)
MODULE__SHA3_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) Modules/_hacl/Hacl_Hash_SHA3.h Modules/_hacl/Hacl_Hash_SHA3.c
MODULE__SHA3_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) Modules/_hacl/Hacl_Hash_SHA3.h Modules/_hacl/internal/Hacl_Hash_SHA3.h Modules/_hacl/Hacl_Hash_SHA3.c
MODULE__BLAKE2_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_BLAKE2_HEADERS) $(LIBHACL_BLAKE2_A)
MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c
MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h
MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/parts.h $(srcdir)/Modules/_testcapi/util.h
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Python's hashlib now unconditionally uses the vendored HACL* library for
Blake2. Python no longer accepts libb2 as an optional dependency for Blake2.

We refreshed HACL* to the latest version, and now vendor HACL*'s 128-bit and
256-bit wide vector implementations for Blake2, which are used on x86/x64
toolchains when the required CPU features are available at runtime.

HACL*'s 128-bit wide vector implementation of Blake2 can also run on ARM
NEON and Power8, but lacking evidence of a performance gain, these are not
enabled (yet).
Loading

0 comments on commit ceaf4d1

Please sign in to comment.