changeset 39342:b50fdee87a36

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.
author Bruno Haible <bruno@clisp.org>
date Sun, 06 May 2018 17:04:25 +0200
parents 70fea0441bbe
children 3603cbcfab21
files ChangeLog lib/af_alg.c lib/af_alg.h
diffstat 3 files changed, 108 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- 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  <bruno@clisp.org>
+
+	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  <bruno@clisp.org>
 
 	af_alg: Avoid warnings.
--- 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 <https://patchwork.kernel.org/patch/9434741/>.  */
+  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)
--- 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;
 }