Discussion:
[PATCH v2 0/4] Use AF_ALG in checksum utilities
Matteo Croce
2018-04-25 11:18:47 UTC
Permalink
Let md5sum and all sha*sum utilities use Linux kernel cryptographic API via the
AF_ALG address family.

Speed gain depends on the CPU type:

Xeon E3-1265L V2:

$ truncate -s 2GB 2g.bin
$ time sha1sum 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m4.829s
user 0m4.437s
sys 0m0.391s
$ time ./sha1sum-afalg 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m3.164s
user 0m0.000s
sys 0m3.162s

Xeon E3-1240 v6:

$ time sha1sum disk.qcow2
47be301f86c71c20eae4ccc5bab4c02e09e729a4 disk.qcow2

real 0m28.390s
user 0m13.352s
sys 0m1.376s
$ time ./sha1sum-afalg disk.qcow2
47be301f86c71c20eae4ccc5bab4c02e09e729a4 disk.qcow2

real 0m8.373s
user 0m0.052s
sys 0m8.308s

Marvell Armada 8040 - MACCHIATOBin Dual shot:

$ dd if=/dev/zero bs=1G count=10 |time -p sha1sum
a0b6e2ca4e28360a929943e8eb966f703a69dc44 2g.bin

real 0m49.390s
user 0m46.852s
sys 0m2.076s
$ dd if=/dev/zero bs=1G count=10 |time -p ./sha1sum-afalg
a0b6e2ca4e28360a929943e8eb966f703a69dc44 2g.bin

real 0m15.104s
user 0m0.052s
sys 0m15.008s

Correctness of the implementation was tested with the following script:

algos='md5sum sha1sum sha224sum sha256sum sha384sum sha512sum'

for alg in $algos; do
echo -n "Checking $alg..."
for bs in 1 31 512 1K 4K 1M; do
for count in 0 1 1234 4096; do
hash1=$(dd status=none if=/dev/zero bs=$bs count=$count |$alg)
hash2=$(dd status=none if=/dev/zero bs=$bs count=$count |src/$alg)
[ "$hash1" = "$hash2" ] || exit 1
done
done
echo " ok"
done

Changes from v2:
* add copyright nore in af_alg.c
* check for AF_ALG in sys/socket.h to avoid build failures when unavailable
* don't include unneeded header files in af_alg.h
* revert wrong Include directive in module description
* revert unneeded 'Hey Emacs!' blocks
* use correct GNU indentation
* prefer size_t over int to denote memory segments
* avoid possible overflow by checking arguments size
* return -EIO if sendfile() returns a short read/write count
* fix a file descriptor leak when bind() returns error

Matteo Croce (4):
sha1sum: use AF_ALG when available
sha256sum: use kernel crypto API
sha512sum: use kernel crypto API
md5sum: use kernel crypto API

lib/af_alg.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/af_alg.h | 51 +++++++++++++++++++++
lib/md5.c | 20 +++++++-
lib/sha1.c | 19 +++++++-
lib/sha256.c | 34 +++++++++++++-
lib/sha512.c | 34 +++++++++++++-
modules/crypto/md5 | 4 +-
modules/crypto/sha1 | 4 +-
modules/crypto/sha256 | 4 +-
modules/crypto/sha512 | 4 +-
10 files changed, 288 insertions(+), 10 deletions(-)
create mode 100644 lib/af_alg.c
create mode 100644 lib/af_alg.h
--
2.14.3
Matteo Croce
2018-04-25 11:26:08 UTC
Permalink
Linux supports accessing kernel crypto API via AF_ALG since
version 2.6.38. Coreutils uses libcrypto when available and fallbacks to
generic C implementation of various hashing functions.

Add a generic afalg_stream() function which uses AF_ALG to calculate the
hash of a stream and use sendfile() when possible (regular file with size
less or equal than 0x7ffff000 (2,147,479,552) bytes, AKA MAX_RW_COUNT).

Use afalg_stream() only in sha1sum for now, but other hashes are possible.
The speed gain really depends on the CPU type, on systems which doesn't use
libcrypto ranges from ~10% to 320%.

This is a test on a Intel(R) Xeon(R) CPU E3-1265L V2 and Debian stretch:

$ truncate -s 2GB 2g.bin
$ time sha1sum 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m4.829s
user 0m4.437s
sys 0m0.391s
$ time ./sha1sum-afalg 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m3.164s
user 0m0.000s
sys 0m3.162s

Signed-off-by: Matteo Croce <***@redhat.com>
---
lib/af_alg.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/af_alg.h | 51 +++++++++++++++++++++
lib/sha1.c | 19 +++++++-
modules/crypto/sha1 | 4 +-
4 files changed, 196 insertions(+), 2 deletions(-)
create mode 100644 lib/af_alg.c
create mode 100644 lib/af_alg.h

diff --git a/lib/af_alg.c b/lib/af_alg.c
new file mode 100644
index 000000000..cb6086b4d
--- /dev/null
+++ b/lib/af_alg.c
@@ -0,0 +1,124 @@
+/* af_alg.c - Functions to compute message digest from file streams using
+ Linux kernel crypto API.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Matteo Croce <***@redhat.com>, 2018. */
+
+#include <config.h>
+
+#include <sys/socket.h>
+
+#ifdef AF_ALG
+
+#include <unistd.h>
+#include <string.h>
+#include <linux/if_alg.h>
+#include <sys/stat.h>
+#include <sys/sendfile.h>
+
+#include "af_alg.h"
+
+/* from linux/include/linux/fs.h: (INT_MAX & PAGE_MASK) */
+#define MAX_RW_COUNT 0x7FFFF000
+#define BLOCKSIZE 32768
+
+int
+afalg_stream (FILE * stream, void *resblock, const char *alg, ssize_t hashlen)
+{
+ struct sockaddr_alg salg = {
+ .salg_family = AF_ALG,
+ .salg_type = "hash",
+ };
+ int ret, cfd, ofd;
+ char buf[BLOCKSIZE];
+ ssize_t size;
+ struct stat st;
+
+ if (strlen (alg) >= sizeof(salg.salg_name))
+ return -EINVAL;
+
+ cfd = socket (AF_ALG, SOCK_SEQPACKET, 0);
+ if (cfd < 0)
+ return -EAFNOSUPPORT;
+
+ strcpy ((char *) salg.salg_name, alg);
+
+ ret = bind (cfd, (struct sockaddr *) &salg, sizeof (salg));
+ if (ret < 0)
+ {
+ ret = -EAFNOSUPPORT;
+ goto out_cfd;
+ }
+
+ ofd = accept (cfd, NULL, 0);
+ if (ofd < 0)
+ {
+ ret = -EAFNOSUPPORT;
+ goto out_cfd;
+ }
+
+ /* if file is a regular file, attempt sendfile() to pipe the data */
+ if (!fstat (fileno (stream), &st) && S_ISREG (st.st_mode) &&
+ st.st_size <= MAX_RW_COUNT)
+ {
+ if (sendfile (ofd, fileno (stream), NULL, st.st_size) != st.st_size)
+ {
+ ret = -EIO;
+ goto out_ofd;
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+ else
+ {
+ /* sendfile() not possible, do a classic read-write loop */
+ while ((size = fread (buf, 1, sizeof (buf), stream)))
+ {
+ if (send (ofd, buf, size, size == sizeof (buf) ? MSG_MORE : 0) == -1)
+ {
+ ret = -EIO;
+ goto out_ofd;
+ }
+ }
+ if (ferror (stream))
+ {
+ ret = -EIO;
+ goto out_ofd;
+ }
+ }
+
+ size = read (ofd, resblock, hashlen);
+ if (size != hashlen)
+ {
+ fprintf (stderr, "Error from read (%zd vs %zd bytes): %s\n",
+ size, hashlen, strerror (errno));
+ ret = -EIO;
+ }
+ else
+ {
+ ret = 0;
+ }
+out_ofd:
+ close (ofd);
+out_cfd:
+ close (cfd);
+ return ret;
+}
+
+#endif
diff --git a/lib/af_alg.h b/lib/af_alg.h
new file mode 100644
index 000000000..36a95f4bc
--- /dev/null
+++ b/lib/af_alg.h
@@ -0,0 +1,51 @@
+/* af_alg.h - Declaration of functions to compute message digest from
+ file streams using Linux kernel crypto API.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Matteo Croce <***@redhat.com>, 2018. */
+
+#ifndef AF_ALG_H
+# define AF_ALG_H 1
+
+# include <stdio.h>
+# include <errno.h>
+# include <sys/socket.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+#ifdef AF_ALG
+
+int
+afalg_stream (FILE * stream, void *resblock, const char *alg, ssize_t hashlen);
+
+#else
+
+static int
+afalg_stream (FILE * stream, void *resblock, const char *alg, ssize_t hashlen)
+{
+ return -EAFNOSUPPORT;
+}
+
+#endif
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
diff --git a/lib/sha1.c b/lib/sha1.c
index 37d46b68e..21b4b323a 100644
--- a/lib/sha1.c
+++ b/lib/sha1.c
@@ -33,6 +33,12 @@
#include <stdlib.h>
#include <string.h>

+#include <sys/socket.h>
+
+#ifdef AF_ALG
+# include "af_alg.h"
+#endif
+
#if USE_UNLOCKED_IO
# include "unlocked-io.h"
#endif
@@ -130,8 +136,19 @@ sha1_stream (FILE *stream, void *resblock)
{
struct sha1_ctx ctx;
size_t sum;
+ char *buffer;
+#ifdef AF_ALG
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "sha1", SHA1_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif

- char *buffer = malloc (BLOCKSIZE + 72);
+ buffer = malloc (BLOCKSIZE + 72);
if (!buffer)
return 1;

diff --git a/modules/crypto/sha1 b/modules/crypto/sha1
index d65f99418..30b7153e2 100644
--- a/modules/crypto/sha1
+++ b/modules/crypto/sha1
@@ -5,6 +5,8 @@ Files:
lib/gl_openssl.h
lib/sha1.h
lib/sha1.c
+lib/af_alg.h
+lib/af_alg.c
m4/gl-openssl.m4
m4/sha1.m4

@@ -17,7 +19,7 @@ configure.ac:
gl_SHA1

Makefile.am:
-lib_SOURCES += sha1.c
+lib_SOURCES += sha1.c af_alg.c

Include:
"sha1.h"
--
2.14.3
Dmitry V. Levin
2018-04-25 12:25:13 UTC
Permalink
On Wed, Apr 25, 2018 at 01:26:08PM +0200, Matteo Croce wrote:
[...]
Post by Matteo Croce
+#include <config.h>
+
+#include <sys/socket.h>
+
+#ifdef AF_ALG
+
+#include <unistd.h>
+#include <string.h>
+#include <linux/if_alg.h>
+#include <sys/stat.h>
+#include <sys/sendfile.h>
+
+#include "af_alg.h"
+
+/* from linux/include/linux/fs.h: (INT_MAX & PAGE_MASK) */
+#define MAX_RW_COUNT 0x7FFFF000
+#define BLOCKSIZE 32768
+
+int
+afalg_stream (FILE * stream, void *resblock, const char *alg, ssize_t hashlen)
+{
+ struct sockaddr_alg salg = {
As struct sockaddr_alg is not provided by glibc,
you'd have to check for HAVE_LINUX_IF_ALG_H after all.

[...]
Post by Matteo Croce
+ /* if file is a regular file, attempt sendfile() to pipe the data */
+ if (!fstat (fileno (stream), &st) && S_ISREG (st.st_mode) &&
+ st.st_size <= MAX_RW_COUNT)
+ {
+ if (sendfile (ofd, fileno (stream), NULL, st.st_size) != st.st_size)
Given that sendfile has offset argument, wouldn't it be better
to use a sendfile loop for handling large regular files?
--
ldv
Matteo Croce
2018-04-25 22:14:49 UTC
Permalink
Post by Dmitry V. Levin
[...]
Post by Matteo Croce
+#include <config.h>
+
+#include <sys/socket.h>
+
+#ifdef AF_ALG
+
+#include <unistd.h>
+#include <string.h>
+#include <linux/if_alg.h>
+#include <sys/stat.h>
+#include <sys/sendfile.h>
+
+#include "af_alg.h"
+
+/* from linux/include/linux/fs.h: (INT_MAX & PAGE_MASK) */
+#define MAX_RW_COUNT 0x7FFFF000
+#define BLOCKSIZE 32768
+
+int
+afalg_stream (FILE * stream, void *resblock, const char *alg, ssize_t hashlen)
+{
+ struct sockaddr_alg salg = {
As struct sockaddr_alg is not provided by glibc,
you'd have to check for HAVE_LINUX_IF_ALG_H after all.
Did you mean check for HAVE_LINUX_IF_ALG_H instead of AF_ALG?
That would work without even including sys/socket.h.
Post by Dmitry V. Levin
[...]
Post by Matteo Croce
+ /* if file is a regular file, attempt sendfile() to pipe the data */
+ if (!fstat (fileno (stream), &st) && S_ISREG (st.st_mode) &&
+ st.st_size <= MAX_RW_COUNT)
+ {
+ if (sendfile (ofd, fileno (stream), NULL, st.st_size) != st.st_size)
Given that sendfile has offset argument, wouldn't it be better
to use a sendfile loop for handling large regular files?
It would be nice to call sendfile multiple times, unfortunately after
every write the kernel calculates the hash unless you set the MSG_MORE
flag in the send(), but sendfile doesn't allow to inform the kernel
that we're sending more data later. From
Documentation/crypto/userspace-if.rst

Using the send() system call, the application provides the data that
should be processed with the message digest. The send system call allows
the following flags to be specified:

- MSG_MORE: If this flag is set, the send system call acts like a
message digest update function where the final hash is not yet
calculated. If the flag is not set, the send system call calculates
the final message digest immediately.

Cheers,
--
Matteo Croce
per aspera ad upstream
Matteo Croce
2018-04-27 15:50:30 UTC
Permalink
Post by Dmitry V. Levin
As struct sockaddr_alg is not provided by glibc,
you'd have to check for HAVE_LINUX_IF_ALG_H after all.
Does this require a change in the coreutils project configure.ac too?

Regards,
--
Matteo Croce
per aspera ad upstream
Paul Eggert
2018-04-27 16:05:51 UTC
Permalink
Post by Matteo Croce
Does this require a change in the coreutils project configure.ac too?
I don't see why. Only Gnulib-supplied code needs to worry about
HAVE_LINUX_IF_ALG_H, right? So the configury can all be done in the
Gnulib-supplied lib/*.m4 file. Although 'configure' will change,
configure.ac shouldn't need any changes.
Assaf Gordon
2018-04-25 17:34:54 UTC
Permalink
Hello Matteo,
Post by Matteo Croce
Linux supports accessing kernel crypto API via AF_ALG since
version 2.6.38. Coreutils uses libcrypto when available and fallbacks to
generic C implementation of various hashing functions.
Not exactly: coreutils (and every project which uses gnulib's md5/sha* modules)
defaults to using the generic C implementation.
gnulib automatically adds the option "--with-openssl" to "./configure",
which can be set explicitly to 'yes' or 'auto'.

To be conservative, I would suggest following the same method:
Add a "configure" flag instead of enabling a completely new interface
by default for every downstream project,
especially that it is tied to external components (the kernel / modules / etc).

If a distribution wants to enable by default, they can do it
for their package (and ensure it works well with their kernel).
Post by Matteo Croce
Use afalg_stream() only in sha1sum for now, but other hashes are possible.
The speed gain really depends on the CPU type, on systems which doesn't use
libcrypto ranges from ~10% to 320%.
It would be beneficial to know the improvements against coreutils+libcrypto
on the same CPUs, because if users build with "--openssl=yes" (which is currently
considered the "fast" implementation), they would not benefit from your patch.
So they'll need to decide which one is preferable (and OpenSSL/libreSSL is faster
on more CPUs and OSs, not just linux on a subset of CPUs).
Post by Matteo Croce
$ truncate -s 2GB 2g.bin
$ time sha1sum 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin
real 0m4.829s
user 0m4.437s
sys 0m0.391s
$ time ./sha1sum-afalg 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin
real 0m3.164s
user 0m0.000s
sys 0m3.162s
Regarding the above measurement:
On most modern filesystems "truncate -s" would create a sparse file,
requiring very little I/O.
And if the measurements above are indeed mostly CPU-bound
and not IO-bound, it indicates that hasing in the kernel
is only somewhat faster then in user-mode, but not significantly so
if you exclude I/O (not as dramatic difference as measured in
your previous email).

This also brings the question: in the previous emails you report
the improved *-afalg versions after the default versions - did
you clear the cache to exclude I/O effects?
(e.g. "sync; echo 3 > /proc/sys/vm/drop_caches")
Post by Matteo Croce
+ {
+ /* sendfile() not possible, do a classic read-write loop */
+ while ((size = fread (buf, 1, sizeof (buf), stream)))
+ {
+ if (send (ofd, buf, size, size == sizeof (buf) ? MSG_MORE : 0) == -1)
+ {
Few comments:
1. In the above loop, wouldn't a file that's an exact multiple of BLOCKSIZE
never be sent 0 as the 4th paramater?
And if so, assuming hashing still works because "MSG_MORE" is just a hint,
why not always send MSG_MORE ?

2. The code for sha*_stream() functions is a bit more elaborate,
and checks ferror/feof to accomodate EAGAIN - it might be useful
to replicate it here.
Post by Matteo Croce
+ if (size != hashlen)
+ {
+ fprintf (stderr, "Error from read (%zd vs %zd bytes): %s\n",
+ size, hashlen, strerror (errno));
+ ret = -EIO;
The fprintf might be problematic:
First, this is a cryptic error message, in the sense that
it indicate an I/O error (hinted by the word "read"),
but is actually a kernel crypto/driver issue.
If a user sees this message, there's no chance they'll know
what the problem is. It's better to be more explicit that
this relates to kernel crypto API problem.

Second, all other sha*_stream functions never output anything
to STDERR. Their interface is returning 0 for success, 1 for failure,
and setting errno for failure. Not sure if this is an official API
agreement or just de-facto, but it means that users of sha*_stream
functions should now be aware there might be output.

Lastly, if the decision is to keep the error message,
it's likely better to use gnulib's error() function, like so:

error (0, errno, _("some error message %zd"), size);

Which will take care of strerror, and also enable translation.

----

On more general note, while the Crypto API is available since version 2.6.38,
There are still some issues popping here and there.
for example:
https://security-tracker.debian.org/tracker/CVE-2016-8646
https://bugzilla.redhat.com/show_bug.cgi?id=1395896

Another example, the libkcapi [1] library (referenced from the kernel crypto api documentation)
contains a work-around for a bug in kernels pre-4.9 [2].
I don't know if this is relevant for your implementation or not,
but if it is, it's worth checking for this and other issues.
[1] http://www.chronox.de/libkcapi.html
[2] https://github.com/smuellerDD/libkcapi/commit/b8d5941addb15fe5a716eef24060fbd306c06ec9

It also seems OpenSSL version 1.1.0 added support for AF_ALG.
Perhaps it's better to leave it to them (since gnulib can already
use openssl easily)?

regards,
- assaf
Matteo Croce
2018-04-26 01:24:25 UTC
Permalink
Post by Assaf Gordon
Hello Matteo,
Post by Matteo Croce
Linux supports accessing kernel crypto API via AF_ALG since
version 2.6.38. Coreutils uses libcrypto when available and fallbacks to
generic C implementation of various hashing functions.
Not exactly: coreutils (and every project which uses gnulib's md5/sha* modules)
defaults to using the generic C implementation.
gnulib automatically adds the option "--with-openssl" to "./configure",
which can be set explicitly to 'yes' or 'auto'.
Add a "configure" flag instead of enabling a completely new interface
by default for every downstream project,
especially that it is tied to external components (the kernel / modules / etc).
If a distribution wants to enable by default, they can do it
for their package (and ensure it works well with their kernel).
Hi Assaf,
I will have a look at the autotools to add such option to configure
and check for something like HAVE_LINUX_CRYPTO
Post by Assaf Gordon
Post by Matteo Croce
Use afalg_stream() only in sha1sum for now, but other hashes are possible.
The speed gain really depends on the CPU type, on systems which doesn't use
libcrypto ranges from ~10% to 320%.
It would be beneficial to know the improvements against coreutils+libcrypto
on the same CPUs, because if users build with "--openssl=yes" (which is currently
considered the "fast" implementation), they would not benefit from your patch.
So they'll need to decide which one is preferable (and OpenSSL/libreSSL is faster
on more CPUs and OSs, not just linux on a subset of CPUs).
I did comparations against libcrypto too, the results depends on the
CPU and file size but generally libcrypto and af_alg are paragonable.
Of course they are way faster than the generic C implementation.
on x86_64 and file smaller than 2 GB af_alg is slightly faster than
libcrypto, probably because of the sendfile usage:

$ grep -m1 '^model name' /proc/cpuinfo
model name : Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
$ uname -a
Linux turbo 4.16.2 #71 SMP Sat Apr 21 20:14:08 CEST 2018 x86_64 x86_64
x86_64 GNU/Linux

$ time ./sha1sum-plain 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m2,718s
user 0m2,548s
sys 0m0,170s

$ time ./sha1sum-libcrypto 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m1,707s
user 0m1,538s
sys 0m0,170s

$ time ./sha1sum-afalg 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m1,680s
user 0m0,000s
sys 0m1,668s

on x86_64 and huge files, af_alg is slightly slower than libcrypto,
probably because two syscalls are needed per block:

$ time ./sha1sum-plain 16g.bin
6b9df4760e7308efe6d9def1293b1653ff24ace1 16g.bin

real 0m22,410s
user 0m20,159s
sys 0m2,250s
$ time ./sha1sum-libcrypto 16g.bin
6b9df4760e7308efe6d9def1293b1653ff24ace1 16g.bin

real 0m13,873s
user 0m12,263s
sys 0m1,610s
$ time ./sha1sum-afalg 16g.bin
6b9df4760e7308efe6d9def1293b1653ff24ace1 16g.bin

real 0m14,124s
user 0m0,080s
sys 0m14,044s


I have similar results on an arm64 board running a quad core Cortex
A72, small file:

$ cat /proc/cpuinfo
processor : 0
BogoMIPS : 50.00
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 1

$ uname -a
Linux macchiatobin 4.16.0 #8 SMP Sat Apr 21 17:55:12 UTC 2018 aarch64
aarch64 aarch64 GNU/Linux

$ time ./sha1sum-plain 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m9.348s
user 0m8.176s
sys 0m1.171s
$ time ./sha1sum-libcrypto 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m2.232s
user 0m1.721s
sys 0m0.510s
$ time ./sha1sum-afalg 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin

real 0m2.219s
user 0m0.000s
sys 0m2.219s

and a bigger one:

$ time ./sha1sum-plain 16g.bin
6b9df4760e7308efe6d9def1293b1653ff24ace1 16g.bin

real 1m14.968s
user 1m5.860s
sys 0m9.106s

$ time ./sha1sum-libcrypto 16g.bin
6b9df4760e7308efe6d9def1293b1653ff24ace1 16g.bin

real 0m20.706s
user 0m14.674s
sys 0m6.001s

$ time ./sha1sum-afalg 16g.bin
6b9df4760e7308efe6d9def1293b1653ff24ace1 16g.bin

real 0m21.608s
user 0m0.142s
sys 0m21.462s

Unfortunately I don't have a board with a crypto HW engine available,
but given the speed of such chips, the results will vary a lot in
favour of af_alg.
Also, keep in mind that some distribution don't want or can't use
libssl for some reasons (licensing, firmware footprint, etc.)
Post by Assaf Gordon
Post by Matteo Croce
$ truncate -s 2GB 2g.bin
$ time sha1sum 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin
real 0m4.829s
user 0m4.437s
sys 0m0.391s
$ time ./sha1sum-afalg 2g.bin
752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin
real 0m3.164s
user 0m0.000s
sys 0m3.162s
On most modern filesystems "truncate -s" would create a sparse file,
requiring very little I/O.
And if the measurements above are indeed mostly CPU-bound
and not IO-bound, it indicates that hasing in the kernel
is only somewhat faster then in user-mode, but not significantly so
if you exclude I/O (not as dramatic difference as measured in
your previous email).
That was intended behaviour. Reading from a slow disk would have
reported similar times between versions.
Post by Assaf Gordon
This also brings the question: in the previous emails you report
the improved *-afalg versions after the default versions - did
you clear the cache to exclude I/O effects?
(e.g. "sync; echo 3 > /proc/sys/vm/drop_caches")
as before, that would clear the block cache and the I/O could be a
bottleneck, probably not what we want.
Post by Assaf Gordon
Post by Matteo Croce
+ {
+ /* sendfile() not possible, do a classic read-write loop */
+ while ((size = fread (buf, 1, sizeof (buf), stream)))
+ {
+ if (send (ofd, buf, size, size == sizeof (buf) ? MSG_MORE : 0) == -1)
+ {
1. In the above loop, wouldn't a file that's an exact multiple of BLOCKSIZE
never be sent 0 as the 4th paramater?
And if so, assuming hashing still works because "MSG_MORE" is just a hint,
why not always send MSG_MORE ?
I tried it before posting the patch, it seems that doing the last
write with MSG_MORE still generates a valid hash. As you guessed, it's
just an hint:

$ dd status=none if=/dev/zero bs=32k count=4 |sha1sum
67dfd19f3eb3649d6f3f6631e44d0bd36b8d8d19 -
$ dd status=none if=/dev/zero bs=32k count=4 |strace -e trace=%network
./sha1sum-afalg
socket(AF_ALG, SOCK_SEQPACKET, 0) = 3
bind(3, {sa_family=AF_ALG,
sa_data="hash\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0sha1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"},
88) = 0
accept(3, NULL, NULL) = 4
sendto(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
32768, MSG_MORE, NULL, 0) = 32768
sendto(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
32768, MSG_MORE, NULL, 0) = 32768
sendto(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
32768, MSG_MORE, NULL, 0) = 32768
sendto(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
32768, MSG_MORE, NULL, 0) = 32768
67dfd19f3eb3649d6f3f6631e44d0bd36b8d8d19 -
+++ exited with 0 +++

Yes, we can always pass MSG_MORE to the kernel, the hash will
correctly generated when calling read()
Post by Assaf Gordon
2. The code for sha*_stream() functions is a bit more elaborate,
and checks ferror/feof to accomodate EAGAIN - it might be useful
to replicate it here.
Post by Matteo Croce
+ if (size != hashlen)
+ {
+ fprintf (stderr, "Error from read (%zd vs %zd bytes): %s\n",
+ size, hashlen, strerror (errno));
+ ret = -EIO;
First, this is a cryptic error message, in the sense that
it indicate an I/O error (hinted by the word "read"),
but is actually a kernel crypto/driver issue.
If a user sees this message, there's no chance they'll know
what the problem is. It's better to be more explicit that
this relates to kernel crypto API problem.
Indeed, it should be reworded better.
Post by Assaf Gordon
Second, all other sha*_stream functions never output anything
to STDERR. Their interface is returning 0 for success, 1 for failure,
and setting errno for failure. Not sure if this is an official API
agreement or just de-facto, but it means that users of sha*_stream
functions should now be aware there might be output.
Lastly, if the decision is to keep the error message,
error (0, errno, _("some error message %zd"), size);
Which will take care of strerror, and also enable translation.
I wasn't sure if put the error message or not. I think that a library
should not clobber the output with text, probably I did a mistake,
returning 1 and set errno seems a better idea.
Post by Assaf Gordon
----
On more general note, while the Crypto API is available since version 2.6.38,
There are still some issues popping here and there.
https://security-tracker.debian.org/tracker/CVE-2016-8646
https://bugzilla.redhat.com/show_bug.cgi?id=1395896
That's bad. I'm not a security expert at all, but I did a quick search
and I've found that also libcrypt had some CVEs.
Nobody's perfect :)
Post by Assaf Gordon
Another example, the libkcapi [1] library (referenced from the kernel crypto api documentation)
contains a work-around for a bug in kernels pre-4.9 [2].
I don't know if this is relevant for your implementation or not,
but if it is, it's worth checking for this and other issues.
[1] http://www.chronox.de/libkcapi.html
[2] https://github.com/smuellerDD/libkcapi/commit/b8d5941addb15fe5a716eef24060fbd306c06ec9
That's bad too. In this case probably the distribution kernel
maintainers should backport such fix anyway, regardless of af_alg
support in coreutils.
Post by Assaf Gordon
It also seems OpenSSL version 1.1.0 added support for AF_ALG.
Perhaps it's better to leave it to them (since gnulib can already
use openssl easily)?
I've just checked the openssl version on my two reference systems
which are an arm64 Ubuntu 18.04:

$ dpkg -l libssl1.1 |grep ^ii
ii libssl1.1:arm64 1.1.0g-2ubuntu4 arm64

and an x86_64 Fedora 27:

$ rpm -q openssl-libs
openssl-libs-1.1.0h-3.fc27.x86_64

strace revealed that none of them seems to use af_alg, at least in my
tests. Probably the package maintainer had to enable it?

Regards,
--
Matteo Croce
per aspera ad upstream
Bruno Haible
2018-05-06 00:42:58 UTC
Permalink
Post by Assaf Gordon
On more general note, while the Crypto API is available since version 2.6.38,
There are still some issues popping here and there.
https://security-tracker.debian.org/tracker/CVE-2016-8646
https://bugzilla.redhat.com/show_bug.cgi?id=1395896
Another example, the libkcapi [1] library (referenced from the kernel crypto api documentation)
contains a work-around for a bug in kernels pre-4.9 [2].
I don't know if this is relevant for your implementation or not,
but if it is, it's worth checking for this and other issues.
[1] http://www.chronox.de/libkcapi.html
[2] https://github.com/smuellerDD/libkcapi/commit/b8d5941addb15fe5a716eef24060fbd306c06ec9
To me, it looks like all these bugs occur in the same case: namely,
when zero-sized input is given. That is, precisely the bug that my unit
tests found.


2018-05-05 Bruno Haible <***@clisp.org>

af_alg: Improve comments.
* lib/af_alg.c (afalg_stream): Improve comment about kernel bug.

diff --git a/lib/af_alg.c b/lib/af_alg.c
index dc3ac6a..3b35e01 100644
--- a/lib/af_alg.c
+++ b/lib/af_alg.c
@@ -96,8 +96,8 @@ afalg_stream (FILE *stream, const char *alg, void *resblock, ssize_t hashlen)
ret = -EIO;
goto out_ofd;
}
- /* On Linux 4.4.0 at least, the value for an empty stream is wrong
- (all zeroes). */
+ /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes).
+ See <https://patchwork.kernel.org/patch/9434741/>. */
if (!non_empty)
{
ret = -EAFNOSUPPORT;

Bernhard Voelker
2018-04-25 17:41:29 UTC
Permalink
Post by Matteo Croce
+#define BLOCKSIZE 32768
Did you try different buffer sizes?

In coreutils, we have a little script to determine the optimal BLOCKSIZE
- which is 128*1024 since 2014:

https://git.sv.gnu.org/cgit/coreutils.git/tree/src/ioblksize.h?id=22424dde74d0
Post by Matteo Croce
+#ifdef AF_ALG
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "sha1", SHA1_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif
afalg_stream also returns other errors, namely EAFNOSUPPORT and EINVAL,
which should be handled as well, right?

Thanks & have a nice day,
Berny
Matteo Croce
2018-04-26 01:55:33 UTC
Permalink
On Wed, Apr 25, 2018 at 7:41 PM, Bernhard Voelker
Post by Bernhard Voelker
Post by Matteo Croce
+#define BLOCKSIZE 32768
Did you try different buffer sizes?
In coreutils, we have a little script to determine the optimal BLOCKSIZE
https://git.sv.gnu.org/cgit/coreutils.git/tree/src/ioblksize.h?id=22424dde74d0
Hi Bernhard,

I knew it and I tried different blocksizes too, but for some odd
reason there are no improvements with block size past 16k, so I left
the old value which was 32k. These are
the timings of sha1sum on a 16g file:

1024 22.991s
2048 18.609s
4096 16.397s
8192 15.255s
16384 14.544s
32768 14.126s
65536 15.396s
131072 15.247s
262144 13.713s
524288 15.141s
1048576 15.139s

I have an Intel i7 i7-6700K CPU and a recent kernel with mitigations
enabled for Meltdown, which should give a penality on every context
switch, this is why I say "for some odd reason" :)

Keep in mind that with files smaller than 2 GB the whole hash will be
calculated with a single call to sendfile()
Post by Bernhard Voelker
Post by Matteo Croce
+#ifdef AF_ALG
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "sha1", SHA1_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif
afalg_stream also returns other errors, namely EAFNOSUPPORT and EINVAL,
which should be handled as well, right?
EAFNOSUPPORT means that af_alg is not supported, hence the generic C
code is used as fallback.
EINVAL means that an string longer than
sizeof(sockaddr_alg.sockaddr_alg) is passed to afalg_stream().
This is impossible as we pass the constant value "sha1" to afalg_stream().
Post by Bernhard Voelker
Thanks & have a nice day,
Berny
Thanks, have a nice day too.
--
Matteo Croce
per aspera ad upstream
Paul Eggert
2018-04-25 19:07:27 UTC
Permalink
Post by Matteo Croce
+ This file is part of the GNU C Library.
Is it really part of glibc? If not, please remove that comment.
Post by Matteo Croce
+ if (strlen (alg) >= sizeof(salg.salg_name))
+ return -EINVAL;
...
Post by Matteo Croce
+ strcpy ((char *) salg.salg_name, alg);
Space before paren is the usual glibc and gnulib style, for both
functions and sizeof. Please check your code for this elsewhere, as
there are other instances.

Better, sizeof need not use parens when its argument is an lvalue, as is
the case here (and elsewhere).

Better yet, for this particular case, copy and check at the same time so
that your algorithm doesn't have unbounded CPU time when alg is very
long, plus this avoids a cast. Something like this:

  for (int i = 0; (salg.salg_name[i] = alg[i]); i++)
    if (i == sizeof salg.salg_name - 1)
      return -EINVAL;
Post by Matteo Croce
+ /* if file is a regular file, attempt sendfile() to pipe the data */
+ if (!fstat (fileno (stream), &st) && S_ISREG (st.st_mode) &&
+ st.st_size <= MAX_RW_COUNT)
The comment should end with "data.  */" with two spaces after the
period. Similarly for other comments.

"&&" should be at the beginning of the next line, not at the end of the
previous one.

POSIX says st_size is also valid if S_TYPEISSHM (&st) || S_TYPEISTMO
(&st); perhaps test for that too?

Even if st_size is greater than MAX_RW_COUNT, the code can still use
sendfile in a loop; why not do that and get rid of the MAX_RW_COUNT
here? Surely that would be more efficient for large files.
Post by Matteo Croce
+ fprintf (stderr, "Error from read (%zd vs %zd bytes): %s\n",
+ size, hashlen, strerror (errno));
This is not right; this low-level function should not output to stderr
when there is a read error. Instead, it should simply return an error
value, as it does when there is a write error.
Matteo Croce
2018-04-26 16:02:44 UTC
Permalink
Thanks for the review!
Post by Paul Eggert
Post by Matteo Croce
+ This file is part of the GNU C Library.
Is it really part of glibc? If not, please remove that comment.
Right, I get the header from md5.c which actually comes from GNU C
Library. Will remove it.
Post by Paul Eggert
Post by Matteo Croce
+ if (strlen (alg) >= sizeof(salg.salg_name))
+ return -EINVAL;
...
Post by Matteo Croce
+ strcpy ((char *) salg.salg_name, alg);
Space before paren is the usual glibc and gnulib style, for both functions
and sizeof. Please check your code for this elsewhere, as there are other
instances.
Better, sizeof need not use parens when its argument is an lvalue, as is the
case here (and elsewhere).
To be compliant with the GNU coding style, I indented the code with
`indent -gnu -nut`, which removed space after sizeof. In future
iterations I'll add --blank-before-sizeof too.
Will fix in the v3
Post by Paul Eggert
Better yet, for this particular case, copy and check at the same time so
that your algorithm doesn't have unbounded CPU time when alg is very long,
for (int i = 0; (salg.salg_name[i] = alg[i]); i++)
if (i == sizeof salg.salg_name - 1)
return -EINVAL;
I assume that C99 is safe to use in gnulib, good to know.
Post by Paul Eggert
Post by Matteo Croce
+ /* if file is a regular file, attempt sendfile() to pipe the data */
+ if (!fstat (fileno (stream), &st) && S_ISREG (st.st_mode) &&
+ st.st_size <= MAX_RW_COUNT)
The comment should end with "data. */" with two spaces after the period.
Similarly for other comments.
"&&" should be at the beginning of the next line, not at the end of the
previous one.
POSIX says st_size is also valid if S_TYPEISSHM (&st) || S_TYPEISTMO (&st);
perhaps test for that too?
I have to check if sendfile supports piping data from a shared memory
object, while I can't find a definition for S_TYPEISTMO in my compiler
headers (gcc 7.3.1). Is it for a typed memory object?
Anyway, the size must be known in advance to use sendfile, is the cas
for SHM and STMO?
Post by Paul Eggert
Even if st_size is greater than MAX_RW_COUNT, the code can still use
sendfile in a loop; why not do that and get rid of the MAX_RW_COUNT here?
Surely that would be more efficient for large files.
Post by Matteo Croce
+ fprintf (stderr, "Error from read (%zd vs %zd bytes): %s\n",
+ size, hashlen, strerror (errno));
This is not right; this low-level function should not output to stderr when
there is a read error. Instead, it should simply return an error value, as
it does when there is a write error.
I agree, generating output from a library is not a good idea. I will
just return an error value.

Regards,
--
Matteo Croce
per aspera ad upstream
Paul Eggert
2018-04-26 16:31:40 UTC
Permalink
Post by Matteo Croce
I assume that C99 is safe to use in gnulib, good to know
Yes. Until recently we did require C89 in Gnulib because Awk still
wanted C89. However, that last holdout has finally given up the ghost so
C99isms are OK if they are portable in practice. I noticed some other
C99isms in your patch so I assumed you didn't mind C99 either.
Post by Matteo Croce
I can't find a definition for S_TYPEISTMO in my compiler
headers (gcc 7.3.1). Is it for a typed memory object?
Anyway, the size must be known in advance to use sendfile, is the cas
for SHM and STMO?
Yes, S_TYPEISTMO is defined by POSIX and st_size is required to work for
it too. See
<http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html>.
It's not in GNU/Linux (it's intended for real-time use which is not
Linux's forte; a quick Google search suggests that it is supported by
QNX) but presumably it would work if added, and in the meantime Gnulib
defines a substitute S_TYPEISTMO that is always zero so your code should
compile and run the same as if you didn't use it.
Matteo Croce
2018-04-25 11:27:02 UTC
Permalink
Use AF_ALG for sha224 and sha256 too

Signed-off-by: Matteo Croce <***@redhat.com>
---
lib/sha256.c | 34 ++++++++++++++++++++++++++++++++--
modules/crypto/sha256 | 4 +++-
2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/lib/sha256.c b/lib/sha256.c
index 85405b20f..7924040cd 100644
--- a/lib/sha256.c
+++ b/lib/sha256.c
@@ -32,6 +32,12 @@
#include <stdlib.h>
#include <string.h>

+#include <sys/socket.h>
+
+#ifdef AF_ALG
+# include "af_alg.h"
+#endif
+
#if USE_UNLOCKED_IO
# include "unlocked-io.h"
#endif
@@ -177,8 +183,20 @@ sha256_stream (FILE *stream, void *resblock)
{
struct sha256_ctx ctx;
size_t sum;
+ char *buffer;

- char *buffer = malloc (BLOCKSIZE + 72);
+#ifdef AF_ALG
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "sha256", SHA256_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif
+
+ buffer = malloc (BLOCKSIZE + 72);
if (!buffer)
return 1;

@@ -248,8 +266,20 @@ sha224_stream (FILE *stream, void *resblock)
{
struct sha256_ctx ctx;
size_t sum;
+ char *buffer;
+
+#ifdef AF_ALG
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "sha224", SHA224_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif

- char *buffer = malloc (BLOCKSIZE + 72);
+ buffer = malloc (BLOCKSIZE + 72);
if (!buffer)
return 1;

diff --git a/modules/crypto/sha256 b/modules/crypto/sha256
index 37fabfd90..412cbabfd 100644
--- a/modules/crypto/sha256
+++ b/modules/crypto/sha256
@@ -5,6 +5,8 @@ Files:
lib/gl_openssl.h
lib/sha256.h
lib/sha256.c
+lib/af_alg.h
+lib/af_alg.c
m4/gl-openssl.m4
m4/sha256.m4

@@ -17,7 +19,7 @@ configure.ac:
gl_SHA256

Makefile.am:
-lib_SOURCES += sha256.c
+lib_SOURCES += sha256.c af_alg.c

Include:
"sha256.h"
--
2.14.3
Matteo Croce
2018-04-25 11:27:31 UTC
Permalink
Use AF_ALG for sha384 and sha512 too

Signed-off-by: Matteo Croce <***@redhat.com>
---
lib/sha512.c | 34 ++++++++++++++++++++++++++++++++--
modules/crypto/sha512 | 4 +++-
2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/lib/sha512.c b/lib/sha512.c
index 8a6dd4e83..1d3471f20 100644
--- a/lib/sha512.c
+++ b/lib/sha512.c
@@ -32,6 +32,12 @@
#include <stdlib.h>
#include <string.h>

+#include <sys/socket.h>
+
+#ifdef AF_ALG
+# include "af_alg.h"
+#endif
+
#if USE_UNLOCKED_IO
# include "unlocked-io.h"
#endif
@@ -185,8 +191,20 @@ sha512_stream (FILE *stream, void *resblock)
{
struct sha512_ctx ctx;
size_t sum;
+ char *buffer;

- char *buffer = malloc (BLOCKSIZE + 72);
+#ifdef AF_ALG
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "sha512", SHA512_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif
+
+ buffer = malloc (BLOCKSIZE + 72);
if (!buffer)
return 1;

@@ -256,8 +274,20 @@ sha384_stream (FILE *stream, void *resblock)
{
struct sha512_ctx ctx;
size_t sum;
+ char *buffer;
+
+#ifdef AF_ALG
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "sha384", SHA384_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif

- char *buffer = malloc (BLOCKSIZE + 72);
+ buffer = malloc (BLOCKSIZE + 72);
if (!buffer)
return 1;

diff --git a/modules/crypto/sha512 b/modules/crypto/sha512
index 4c97604cd..feefbf8f8 100644
--- a/modules/crypto/sha512
+++ b/modules/crypto/sha512
@@ -5,6 +5,8 @@ Files:
lib/gl_openssl.h
lib/sha512.h
lib/sha512.c
+lib/af_alg.h
+lib/af_alg.c
m4/gl-openssl.m4
m4/sha512.m4

@@ -18,7 +20,7 @@ configure.ac:
gl_SHA512

Makefile.am:
-lib_SOURCES += sha512.c
+lib_SOURCES += sha512.c af_alg.c

Include:
"sha512.h"
--
2.14.3
Matteo Croce
2018-04-25 11:28:04 UTC
Permalink
Use AF_ALG for md5 too

Signed-off-by: Matteo Croce <***@redhat.com>
---
lib/md5.c | 20 +++++++++++++++++++-
modules/crypto/md5 | 4 +++-
2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/lib/md5.c b/lib/md5.c
index 68d00a6c7..8e60a549f 100644
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -32,6 +32,12 @@
#include <string.h>
#include <sys/types.h>

+#include <sys/socket.h>
+
+#ifdef AF_ALG
+# include "af_alg.h"
+#endif
+
#if USE_UNLOCKED_IO
# include "unlocked-io.h"
#endif
@@ -142,8 +148,20 @@ md5_stream (FILE *stream, void *resblock)
{
struct md5_ctx ctx;
size_t sum;
+ char *buffer;
+
+#ifdef AF_ALG
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "md5", MD5_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif

- char *buffer = malloc (BLOCKSIZE + 72);
+ buffer = malloc (BLOCKSIZE + 72);
if (!buffer)
return 1;

diff --git a/modules/crypto/md5 b/modules/crypto/md5
index e5fb39ced..ef9ec9916 100644
--- a/modules/crypto/md5
+++ b/modules/crypto/md5
@@ -5,6 +5,8 @@ Files:
lib/gl_openssl.h
lib/md5.h
lib/md5.c
+lib/af_alg.h
+lib/af_alg.c
m4/gl-openssl.m4
m4/md5.m4

@@ -17,7 +19,7 @@ configure.ac:
gl_MD5

Makefile.am:
-lib_SOURCES += md5.c
+lib_SOURCES += md5.c af_alg.c

Include:
"md5.h"
--
2.14.3
Loading...