# HG changeset patch # User Bruno Haible # Date 1525619065 -7200 # Node ID b50fdee87a365dd495874395d965d21dfd74dd30 # Parent 70fea0441bbe7f01e6ec70e3e7115eaddf406a8d af_alg: Add ability to use Linux kernel crypto API on data in memory. * lib/af_alg.h (afalg_buffer): New declaration. * lib/af_alg.c (afalg_buffer): New function. diff -r 70fea0441bbe -r b50fdee87a36 ChangeLog --- a/ChangeLog Sun May 06 17:00:56 2018 +0200 +++ b/ChangeLog Sun May 06 17:04:25 2018 +0200 @@ -1,3 +1,9 @@ +2018-05-06 Bruno Haible + + af_alg: Add ability to use Linux kernel crypto API on data in memory. + * lib/af_alg.h (afalg_buffer): New declaration. + * lib/af_alg.c (afalg_buffer): New function. + 2018-05-06 Bruno Haible af_alg: Avoid warnings. diff -r 70fea0441bbe -r b50fdee87a36 lib/af_alg.c --- a/lib/af_alg.c Sun May 06 17:00:56 2018 +0200 +++ b/lib/af_alg.c Sun May 06 17:04:25 2018 +0200 @@ -37,7 +37,73 @@ #define BLOCKSIZE 32768 int -afalg_stream (FILE *stream, const char *alg, void *resblock, ssize_t hashlen) +afalg_buffer (const char *buffer, size_t len, const char *alg, + void *resblock, ssize_t hashlen) +{ + /* On Linux < 4.9, the value for an empty stream is wrong (all zeroes). + See . */ + if (len == 0) + return -EAFNOSUPPORT; + + int cfd = socket (AF_ALG, SOCK_SEQPACKET, 0); + if (cfd < 0) + return -EAFNOSUPPORT; + + int result; + struct sockaddr_alg salg = { + .salg_family = AF_ALG, + .salg_type = "hash", + }; + /* Avoid calling both strcpy and strlen. */ + for (int i = 0; (salg.salg_name[i] = alg[i]); i++) + if (i == sizeof salg.salg_name - 1) + { + result = -EINVAL; + goto out_cfd; + } + + int ret = bind (cfd, (struct sockaddr *) &salg, sizeof salg); + if (ret != 0) + { + result = -EAFNOSUPPORT; + goto out_cfd; + } + + int ofd = accept (cfd, NULL, 0); + if (ofd < 0) + { + result = -EAFNOSUPPORT; + goto out_cfd; + } + + do + { + ssize_t size = (len > BLOCKSIZE ? BLOCKSIZE : len); + if (send (ofd, buffer, size, MSG_MORE) != size) + { + result = -EIO; + goto out_ofd; + } + buffer += size; + len -= size; + } + while (len > 0); + + if (read (ofd, resblock, hashlen) != hashlen) + result = -EIO; + else + result = 0; + +out_ofd: + close (ofd); +out_cfd: + close (cfd); + return result; +} + +int +afalg_stream (FILE *stream, const char *alg, + void *resblock, ssize_t hashlen) { int cfd = socket (AF_ALG, SOCK_SEQPACKET, 0); if (cfd < 0) diff -r 70fea0441bbe -r b50fdee87a36 lib/af_alg.h --- a/lib/af_alg.h Sun May 06 17:00:56 2018 +0200 +++ b/lib/af_alg.h Sun May 06 17:04:25 2018 +0200 @@ -37,6 +37,30 @@ # if USE_LINUX_CRYPTO_API +/* Compute a message digest of a memory region. + + The memory region starts at BUFFER and is LEN bytes long. + + ALG is the message digest algorithm; see the file /proc/crypto. + + RESBLOCK points to a block of HASHLEN bytes, for the result. + HASHLEN must be the length of the message digest, in bytes, in particular: + + alg | hashlen + -------+-------- + md5 | 16 + sha1 | 20 + sha224 | 28 + sha256 | 32 + sha384 | 48 + sha512 | 64 + + If successful, fill RESBLOCK and return 0. + Upon failure, return a negated error number. */ +int +afalg_buffer (const char *buffer, size_t len, const char *alg, + void *resblock, ssize_t hashlen); + /* Compute a message digest of the contents of a file. STREAM is an open file stream. Regular files are handled more efficiently. @@ -60,12 +84,21 @@ If successful, fill RESBLOCK and return 0. Upon failure, return a negated error number. */ int -afalg_stream (FILE *stream, const char *alg, void *resblock, ssize_t hashlen); +afalg_stream (FILE *stream, const char *alg, + void *resblock, ssize_t hashlen); # else static inline int -afalg_stream (FILE *stream, const char *alg, void *resblock, ssize_t hashlen) +afalg_buffer (const char *buffer, size_t len, const char *alg, + void *resblock, ssize_t hashlen) +{ + return -EAFNOSUPPORT; +} + +static inline int +afalg_stream (FILE *stream, const char *alg, + void *resblock, ssize_t hashlen) { return -EAFNOSUPPORT; }