Welcome to little lamb

Code » limb » commit 7423739

Add buffer-shldata.h & related functions

author Olivier Brunel
2023-05-03 15:57:01 UTC
committer Olivier Brunel
2023-05-20 18:06:40 UTC
parent 77074df6354d1705672d62cdbf69c8adaf1a304b

Add buffer-shldata.h & related functions

src/doc/buffer-shldata.h.0.md +68 -0
src/doc/buffer-shldata.h/buffer_shldata_getinit.3.md +97 -0
src/doc/buffer-shldata.h/buffer_shldata_putinit.3.md +101 -0
src/doc/shldata.h.0.md +1 -2
src/include/buffer-shldata.h +14 -0
src/liblimb/buffer-shldata.h/buffer_shldata_free.c +10 -0
src/liblimb/buffer-shldata.h/buffer_shldata_get.c +21 -0
src/liblimb/buffer-shldata.h/buffer_shldata_getfinal.c +10 -0
src/liblimb/buffer-shldata.h/buffer_shldata_getfinal_sa.c +18 -0
src/liblimb/buffer-shldata.h/buffer_shldata_getfull.c +46 -0
src/liblimb/buffer-shldata.h/buffer_shldata_getinit.c +10 -0
src/liblimb/buffer-shldata.h/buffer_shldata_getinit_sa.c +25 -0
src/liblimb/buffer-shldata.h/buffer_shldata_put.c +28 -0
src/liblimb/buffer-shldata.h/buffer_shldata_putfinal.c +40 -0
src/liblimb/buffer-shldata.h/buffer_shldata_putinit.c +18 -0
src/liblimb/include/limb/buffer-shldata.h +38 -0

diff --git a/src/doc/buffer-shldata.h.0.md b/src/doc/buffer-shldata.h.0.md
new file mode 100644
index 0000000..a7a4f68
--- /dev/null
+++ b/src/doc/buffer-shldata.h.0.md
@@ -0,0 +1,68 @@
+% limb manual
+% buffer-shldata.h(0)
+
+# NAME
+
+buffer-shldata.h - buffer interface for shield data protocol
+
+# SYNOPSIS
+
+    #include <limb/buffer-shldata.h>
+
+# DESCRIPTION
+
+The header defines functions used to encrypt/decrypt data using the "shielded
+data" protocol through the [buffer.h](0) interface.
+
+Refer to [shldata](5) for more about shielded data protocol.
+
+## Types
+
+The following types are defined :
+
+: *buffer_shldata_ctx*
+:: An opaque structure to e given to the functions below.
+
+## Macros
+
+The following macros are defined :
+
+: *BUFFER_SHLDATA_ZERO*
+:: Value to initialize a *buffer_shldata_ctx* to.
+
+## Functions
+
+The following functions are defined :
+
+: [buffer_shldata_putinit](3)
+:: To initialize encryption of data through the shielded data protocol.
+
+: [buffer_shldata_put](3)
+:: To encrypt data through the shielded data protocol.
+
+: [buffer_shldata_putfinal](3)
+:: To finalize encryption of data through the shielded data protocol.
+
+: [buffer_shldata_getinit_sa](3)
+:: Similar to [buffer_shldata_getinit](3) but using the specified *stralloc* as
+:: heap-allocated temporary space.
+
+: [buffer_shldata_getinit](3)
+:: To initialize decryption of data through the shielded data protocol.
+
+: [buffer_shldata_datasize](3)
+:: Returns the length of encrypted data to be decrypted through the shielded data
+:: protocol.
+
+: [buffer_shldata_get](3)
+:: To decrypt data through the shielded data protocol.
+
+: [buffer_shldata_getfinal_sa](3)
+:: Similar to [buffer_shldata_getfinal](3) but using the specified *stralloc* as
+:: heap-allocated temporary space.
+
+: [buffer_shldata_getfinal](3)
+:: To finalize decryption of data through the shielded data protocol.
+
+: [buffer_shldata_free](3)
+:: To free memory associated with the given opaque structure.
diff --git a/src/doc/buffer-shldata.h/buffer_shldata_getinit.3.md b/src/doc/buffer-shldata.h/buffer_shldata_getinit.3.md
new file mode 100644
index 0000000..a23e1dc
--- /dev/null
+++ b/src/doc/buffer-shldata.h/buffer_shldata_getinit.3.md
@@ -0,0 +1,97 @@
+% limb manual
+% buffer_shldata_getinit(3)
+
+# NAME
+
+buffer\_shldata\_getinit\_sa, buffer\_shldata\_getinit, buffer\_shldata\_get,
+buffer\_shldata\_getfinal\_sa, buffer\_shldata\_getfinal - decrypt data using
+the shielded data protocol through buffer interface
+
+# SYNOPSIS
+
+    #include <limb/buffer-shldata.h>
+
+```pre hl
+int buffer_shldata_getinit_sa(buffer *<em>buf</em>, const char *<em>pwd</em>, size_t <em>plen</em>, stralloc *<em>sa</em>, buffer_shldata_ctx *<em>ctx</em>)
+int buffer_shldata_getinit(buffer *<em>buf</em>, const char *<em>pwd</em>, size_t <em>plen</em>, buffer_shldata_ctx *<em>ctx</em>)
+size_t buffer_shldata_datasize(buffer_shldata_ctx *<em>ctx</em>)
+ssize_t buffer_shldata_get(buffer *<em>buf</em>, char *<em>dst</em>, size_t <em>dlen</em>, buffer_shldata_ctx *<em>ctx</em>)
+int buffer_shldata_getfinal_sa(buffer *<em>buf</em>, stralloc *<em>sa</em>, buffer_shldata_ctx *<em>ctx</em>)
+int buffer_shldata_getfinal(buffer *<em>buf</em>, buffer_shldata_ctx *<em>ctx</em>)
+```
+
+# DESCRIPTION
+
+All those functions are used to read data through the [buffer.h](0) interface
+and decrypt it with a user-supplied password, as per the shielded data protocol
+described in [shldata](5).
+
+The `buffer_shldata_getinit`() function reads data from the buffer pointed by
+`buf` for derivation parameters, derives a secret key from the password pointed
+by `pwd` of length `plen` and initializes the opaque structure pointed by `ctx`
+for decryption.
+
+The `buffer_shldata_getinit_sa`() function is similar but using the *stralloc*
+`sa` as head-allocated temporary space.
+
+The `buffer_shldata_datasize`() macro returns the length of the encrypted data,
+and therefore decrypted data.
+
+The `buffer_shldata_get`() function will read `dlen` bytes of data from the
+buffer pointed by `buf` and decrypt it into the memory pointed by `dst` (which
+must be able to store at least `dlen` bytes).
+
+It is possible to call this function as many times as needed.
+
+! INFO:
+! If trying to decrypt more than there is encrypted data (i.e. total amount
+! requested to `buffer_shldata_get`() gets larger than the value returned by
+! `buffer_shldata_datasize`()) only as much as there is left will be processed.
+
+The `buffer_shldata_getfinal`() function will read data from the buffer pointed
+by `buf` to authenticate both the derivation parameters used during
+`buffer_shldata_getinit`() and the encrypted data.
+
+Lastly, the [buffer_shldata_free](3) function must be called to free memory
+associated with the given `ctx`.
+
+# RETURN VALUE
+
+The `buffer_shldata_getinit_sa`(), `buffer_shldata_getinit`(),
+`buffer_shldata_getfinal_sa`() and `buffer_shldata_getfinal`() functions return
+1 on success. Otherwise they return 0 and set `errno` to indicate the error.
+
+The `buffer_shldata_datasize`() macro returns the length of the data to be
+decrypted.
+
+The `buffer_shldata_get`() function returns the length of data decrypted and
+written into `dst`, which might be less than requested (even zero) if trying to
+decrypt more than there is encrypted data.
+
+# ERRORS
+
+The `buffer_shldata_getinit_sa`(), `buffer_shldata_getinit`(),
+`buffer_shldata_getfinal_sa`() and `buffer_shldata_getfinal`() functions may
+fail if :
+
+: *EINVAL*
+:: Invalid data from the buffer.
+
+: *ENOMEM*
+:: Out of memory.
+
+: *EPIPE*
+:: Premature reach of end-of-file.
+
+They may also fail for any of the errors described for [buffer_fill](3).
+
+The `buffer_shldata_getfinal_sa`() and `buffer_shldata_getfinal`() functions may
+also fail if :
+
+: *EBADMSG*
+:: Authentication of the message (derivation parameters and encrypted data)
+:: failed. (This could be caused by a wrong password being used.)
+
+# SEE ALSO
+
+[buffer_shldata_putinit](3), [shldata_initr](3)
diff --git a/src/doc/buffer-shldata.h/buffer_shldata_putinit.3.md b/src/doc/buffer-shldata.h/buffer_shldata_putinit.3.md
new file mode 100644
index 0000000..667b424
--- /dev/null
+++ b/src/doc/buffer-shldata.h/buffer_shldata_putinit.3.md
@@ -0,0 +1,101 @@
+% limb manual
+% buffer_shldata_putinit(3)
+
+# NAME
+
+buffer\_shldata\_putinit, buffer\_shldata\_put, buffer\_shldata\_putfinal -
+encrypt data using the shielded data protocol through buffer interface
+
+# SYNOPSIS
+
+    #include <limb/buffer-shldata.h>
+
+```pre hl
+ssize_t buffer_shldata_putinit(buffer *<em>buf</em>, const char *<em>pwd</em>, size_t <em>plen</em>, unsigned <em>algo</em>, unsigned <em>iter</em>,
+                               size_t <em>len</em>, buffer_shldata_ctx *<em>ctx</em>)
+ssize_t buffer_shldata_put(buffer *<em>buf</em>, char *<em>data</em>, size_t <em>dlen</em>, int <em>inplace</em>, buffer_shldata_ctx *<em>ctx</em>)
+ssize_t buffer_shldata_putfinal(buffer *<em>buf</em>, buffer_shldata_ctx *<em>ctx</em>)
+
+void buffer_shldata_free(buffer_shldata_ctx *<em>ctx</em>)
+```
+
+# DESCRIPTION
+
+All those functions are used to protect data with a user-supplied password using
+the shielded data protocol, as described in [shldata](5), through the
+[buffer.h](0) interface.
+
+First, the `buffer_shldata_putinit`() function must be called to both write to
+the buffer pointed by `buf` the necessary parameters that will be needed for
+decryption, and to prepare the opaque structure pointed by `ctx` (which must
+have been initialized to *BUFFER_SHLDATA_ZERO*) in order to perform data
+encryption.
+
+Key derivation will be performed using PBKDF2 from the password pointed by `pwd`
+of length `plen`, with the HMAC based on the algorithm indicated in `algo` -
+which must be a valid index/constant as defined in [hasher.h](0) - and the
+number of iterations indicated in `iter`.
+
+It is recommended, if possible, to indicate the exact length of data to be
+encrypted in `len`. It is however possible to use zero if not yet known.
+
+The `buffer_shldata_put`() function can then be used to encrypt data pointed by
+`data` of length `dlen` and write the encrypted data into the buffer pointed by
+`buf`, using the specified `ctx`.
+
+In `inplace` is non-zero the data might be encrypted in-place before being
+written to the buffer, meaning that afterwards the memory pointed by `data` will
+contain the /encrypted/ data. Otherwise, memory might be heap-allocated before
+encrypting data.
+
+! INFO:
+! Note that even with `inplace` set to a non-zero value, the data might be
+! encrypted elsewhere in memory, and stored to be written later on - i.e. during
+! the call to `buffer_shldata_putfinal`(). This will happen if the length of
+! data to encrypt wasn't specified to `buffer_shldata_init`().
+! Refer to [shldata_initw](3) for more.
+
+It is possible to call this function as many times as needed.
+
+Then the `buffer_shldata_putfinal`() function must be called to write to the
+buffer pointed by `buf` the final additional data (MAC). As described above, if
+the length of the data to encrypt wasn't known/given to
+`buffer_shldata_putinit`() then encrypted data will also be written then, before
+the MAC.
+
+Lastly, the `buffer_shldata_free`() function must be called to free memory
+associated with the given `ctx`.
+
+# RETURN VALUE
+
+These functions (except `buffer_shldata_free`()) return the number of bytes
+written to the buffer pointed by `buf` on success. Otherwise they return -1 and
+set `errno` to indicate the error.
+
+# ERRORS
+
+The `buffer_shldata_putinit`() function may fail for any of the errors described
+for [shldata_initw](3), save for *ENOBUFS*.
+
+The `buffer_shldata_put`() and `buffer_shldata_putfinal`() functions may fail
+if :
+
+: *EINVAL*
+:: The total amount of data given to encrypt was larger than that specified to
+:: `buffer_shldata_putinit`().
+
+The `buffer_shldata_put`() function may also fail if :
+
+: *ENOMEM*
+:: Out of memory.
+
+The `buffer_shldata_put`() function may also fail for any of the errors
+described for [buffer_put](3).
+
+The `buffer_shldata_putfinal`() function may fail for any of the errors
+described for [shldata_finalw](3), save for *ENOBUFS*, as well as any of the
+errors described for [buffer_putv](3).
+
+# SEE ALSO
+
+[buffer_shldata_getinit](3), [shldata_initw](3)
diff --git a/src/doc/shldata.h.0.md b/src/doc/shldata.h.0.md
index f23575c..c38cbf9 100644
--- a/src/doc/shldata.h.0.md
+++ b/src/doc/shldata.h.0.md
@@ -19,8 +19,7 @@ data" protocol. Refer to [shldata](5) for more.
 The following types are defined :
 
 : *shldata_ctx*
-:: An opaque structure used to perform encryption or decryption through the
-:: shield data protocol.
+:: An opaque structure to be given to the function below.
 
 ## Functions
 
diff --git a/src/include/buffer-shldata.h b/src/include/buffer-shldata.h
new file mode 100644
index 0000000..56c95b2
--- /dev/null
+++ b/src/include/buffer-shldata.h
@@ -0,0 +1,14 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LIMB_LIMB_BUFFER_SHLDATA_h
+#define LIMB_LIMB_BUFFER_SHLDATA_h
+
+#include <skalibs/functypes.h>
+#include <limb/buffer-shldata.h>
+
+typedef ssize_t (*shldata_getfull) (const char *s, size_t l, void *args, buffer_shldata_ctx *ctx);
+
+extern int buffer_shldata_getfull(buffer *b, shldata_getfull fn, void *args, stralloc *sa, buffer_shldata_ctx *ctx);
+
+#endif /* LIMB_LIMB_BUFFER_SHLDATA_h */
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_free.c b/src/liblimb/buffer-shldata.h/buffer_shldata_free.c
new file mode 100644
index 0000000..00fe24f
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_free.c
@@ -0,0 +1,10 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/buffer-shldata.h>
+
+void
+buffer_shldata_free(buffer_shldata_ctx *ctx)
+{
+    stralloc_free(&ctx->sa);
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_get.c b/src/liblimb/buffer-shldata.h/buffer_shldata_get.c
new file mode 100644
index 0000000..72a9493
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_get.c
@@ -0,0 +1,21 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/buffer-shldata.h>
+#include <limb/shldata.h>
+#include <limb/siovec.h>
+
+ssize_t
+buffer_shldata_get(buffer *b, char *dst, size_t dlen, buffer_shldata_ctx *ctx)
+{
+    if (ctx->sd.done + dlen > ctx->sd.len)
+        dlen = ctx->sd.len - ctx->sd.done;
+
+    if (dlen) {
+        if (buffer_get(b, dst, dlen) < 0)
+            return -1;
+        shldata_decrypt(dst, dst, dlen, &ctx->sd);
+    }
+
+    return dlen;
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_getfinal.c b/src/liblimb/buffer-shldata.h/buffer_shldata_getfinal.c
new file mode 100644
index 0000000..8976fd5
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_getfinal.c
@@ -0,0 +1,10 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/buffer-shldata.h>
+
+int
+buffer_shldata_getfinal(buffer *b, buffer_shldata_ctx *ctx)
+{
+    return buffer_shldata_getfinal_sa(b, &ctx->sa, ctx);
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_getfinal_sa.c b/src/liblimb/buffer-shldata.h/buffer_shldata_getfinal_sa.c
new file mode 100644
index 0000000..22931ce
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_getfinal_sa.c
@@ -0,0 +1,18 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/buffer-shldata.h>
+#include <limb/gccattributes.h>
+#include "buffer-shldata.h"
+
+static ssize_t
+fn(const char *s, size_t l, void *args gccattr_unused, buffer_shldata_ctx *ctx)
+{
+    return shldata_finalr(s, l, &ctx->sd);
+}
+
+int
+buffer_shldata_getfinal_sa(buffer *b, stralloc *sa, buffer_shldata_ctx *ctx)
+{
+    return buffer_shldata_getfull(b, fn, NULL, sa, ctx);
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_getfull.c b/src/liblimb/buffer-shldata.h/buffer_shldata_getfull.c
new file mode 100644
index 0000000..1254ccf
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_getfull.c
@@ -0,0 +1,46 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <errno.h>
+#include <limb/buffer-shldata.h>
+#include <limb/shldata.h>
+#include <limb/siovec.h>
+#include "buffer-shldata.h"
+
+int
+buffer_shldata_getfull(buffer *b, shldata_getfull fn, void *args,
+                       stralloc *sa, buffer_shldata_ctx *ctx)
+{
+    struct iovec v[2];
+    ssize_t o;
+    size_t salen, l;
+
+    salen = sa->len;
+    for(;;) {
+        buffer_rpeek(b, v);
+        /* make sure we can put the whole buffer into our sa */
+        if (!stralloc_readyplus(sa, siov_len(v, 2)))
+            return (sa->len = salen, 0);
+        /* and gather it */
+        l = siov_gather(sa->s + sa->len, sa->a - sa->len, v, 2);
+        /* try the getfull fn() */
+        o = fn(sa->s + salen, sa->len - salen + l, args, ctx);
+        /* in case of ENODATA we "appropriate" the buffer's content & refill */
+        if (o < 0 && errno == ENODATA) {
+            sa->len += l;
+            buffer_rseek(b, l);
+            o = buffer_fill(b);
+            if (o > 0)
+                continue;
+        }
+        break;
+    }
+    if (o <= 0) {
+        sa->len = salen;
+        return (!o) ? (errno = EPIPE, 0) : 0;
+    }
+
+    buffer_rseek(b, o - (sa->len - salen));
+    sa->len = salen;
+    return 1;
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_getinit.c b/src/liblimb/buffer-shldata.h/buffer_shldata_getinit.c
new file mode 100644
index 0000000..e8a5f75
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_getinit.c
@@ -0,0 +1,10 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/buffer-shldata.h>
+
+int
+buffer_shldata_getinit(buffer *b, const char *pwd, size_t plen, buffer_shldata_ctx *ctx)
+{
+    return buffer_shldata_getinit_sa(b, pwd, plen, &ctx->sa, ctx);
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_getinit_sa.c b/src/liblimb/buffer-shldata.h/buffer_shldata_getinit_sa.c
new file mode 100644
index 0000000..c656529
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_getinit_sa.c
@@ -0,0 +1,25 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/buffer-shldata.h>
+#include "buffer-shldata.h"
+
+struct args {
+    const char *pwd;
+    size_t plen;
+};
+
+static ssize_t
+fn(const char *s, size_t l, void *args_, buffer_shldata_ctx *ctx)
+{
+    struct args *args = args_;
+    return shldata_initr(s, l, args->pwd, args->plen, &ctx->sd);
+}
+
+int
+buffer_shldata_getinit_sa(buffer *b, const char *pwd, size_t plen,
+                          stralloc *sa, buffer_shldata_ctx *ctx)
+{
+    struct args args = { pwd, plen };
+    return buffer_shldata_getfull(b, fn, &args, sa, ctx);
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_put.c b/src/liblimb/buffer-shldata.h/buffer_shldata_put.c
new file mode 100644
index 0000000..161f48e
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_put.c
@@ -0,0 +1,28 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <errno.h>
+#include <limb/buffer-shldata.h>
+#include <limb/shldata.h>
+
+ssize_t
+buffer_shldata_put(buffer *b, char *data, size_t dlen, int inplace, buffer_shldata_ctx *ctx)
+{
+    if (ctx->sd.len && inplace) {
+        if (!shldata_encrypt(data, data, dlen, &ctx->sd))
+            return (errno = EINVAL, -1);
+    } else {
+        if (!stralloc_readyplus(&ctx->sa, dlen)
+                || !shldata_encrypt(ctx->sa.s + ctx->sa.len, data, dlen, &ctx->sd))
+            return (errno = (errno == ENOMEM) ? ENOMEM : EINVAL, -1);
+        /* if full data length wasn't given to init we need to store the
+         * encrypted data until we're done, as the predata shall be written
+         * first (with the data length) */
+        if (!ctx->sd.len) {
+            ctx->sa.len += dlen;
+            return 0;
+        }
+    }
+
+    return buffer_put(b, (inplace) ? data : ctx->sa.s, dlen);
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_putfinal.c b/src/liblimb/buffer-shldata.h/buffer_shldata_putfinal.c
new file mode 100644
index 0000000..025d4be
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_putfinal.c
@@ -0,0 +1,40 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/buffer-shldata.h>
+#include <limb/shldata.h>
+#include <limb/siovec.h>
+
+ssize_t
+buffer_shldata_putfinal(buffer *b, buffer_shldata_ctx *ctx)
+{
+    char buf[28];
+    struct iovec v[3];
+    int n = 0;
+
+    /* did we store encrypted data? i.e. length wasn't given to init */
+    if (ctx->sa.len) {
+        int r;
+        r = shldata_predata(buf, sizeof(buf), &ctx->sd);
+        if (r < 0) return -1;
+
+        v[n].iov_base = buf;
+        v[n].iov_len = r;
+        ++n;
+
+        v[n].iov_base = ctx->sa.s;
+        v[n].iov_len = ctx->sa.len;
+        ++n;
+    }
+
+    ssize_t l;
+    l = shldata_finalw(buf + 10, sizeof(buf) - 10, &ctx->sd);
+    if (l < 0) return -1;
+
+    v[n].iov_base = buf + 10;
+    v[n].iov_len = l;
+    ++n;
+
+    ctx->sa.len = 0;
+    return buffer_putv(b, v, n);
+}
diff --git a/src/liblimb/buffer-shldata.h/buffer_shldata_putinit.c b/src/liblimb/buffer-shldata.h/buffer_shldata_putinit.c
new file mode 100644
index 0000000..72c6700
--- /dev/null
+++ b/src/liblimb/buffer-shldata.h/buffer_shldata_putinit.c
@@ -0,0 +1,18 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <limb/buffer-shldata.h>
+#include <limb/shldata.h>
+
+ssize_t
+buffer_shldata_putinit(buffer *b, const char *pwd, size_t plen, unsigned algo,
+                       unsigned iter, size_t len, buffer_shldata_ctx *ctx)
+{
+    char buf[64];
+    ssize_t l;
+
+    l = shldata_initw(buf, sizeof(buf), pwd, plen, algo, iter, len, &ctx->sd);
+    if (l < 0) return l;
+
+    return buffer_put(b, buf, l);
+}
diff --git a/src/liblimb/include/limb/buffer-shldata.h b/src/liblimb/include/limb/buffer-shldata.h
new file mode 100644
index 0000000..84f2110
--- /dev/null
+++ b/src/liblimb/include/limb/buffer-shldata.h
@@ -0,0 +1,38 @@
+/* This file is part of limb                           https://lila.oss/limb
+ * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LIMB_BUFFER_SHLDATA_H
+#define LIMB_BUFFER_SHLDATA_H
+
+#include <sys/types.h>
+#include <skalibs/stralloc.h>
+#include <limb/buffer.h>
+#include <limb/shldata.h>
+
+struct buffer_shldata_ctx {
+    stralloc sa;
+    shldata_ctx sd;
+};
+
+#define BUFFER_SHLDATA_ZERO     { .sa = STRALLOC_ZERO }
+
+typedef struct buffer_shldata_ctx buffer_shldata_ctx;
+
+extern void buffer_shldata_free(buffer_shldata_ctx *ctx);
+
+/* Writing */
+
+extern ssize_t buffer_shldata_putinit(buffer *b, const char *pwd, size_t plen, unsigned algo, unsigned iter, size_t len, buffer_shldata_ctx *ctx);
+extern ssize_t buffer_shldata_put(buffer *b, char *data, size_t dlen, int inplace, buffer_shldata_ctx *ctx);
+extern ssize_t buffer_shldata_putfinal(buffer *b, buffer_shldata_ctx *ctx);
+
+/* Reading */
+
+extern int buffer_shldata_getinit_sa(buffer *b, const char *pwd, size_t plen, stralloc *sa, buffer_shldata_ctx *ctx);
+extern int buffer_shldata_getinit(buffer *b, const char *pwd, size_t plen, buffer_shldata_ctx *ctx);
+#define buffer_shldata_datasize(ctx)     shldata_datasize(&(ctx)->sd)
+extern ssize_t buffer_shldata_get(buffer *b, char *dst, size_t dlen, buffer_shldata_ctx *ctx);
+extern int buffer_shldata_getfinal_sa(buffer *b, stralloc *sa, buffer_shldata_ctx *ctx);
+extern int buffer_shldata_getfinal(buffer *b, buffer_shldata_ctx *ctx);
+
+#endif /* LIMB_BUFFER_SHLDATA_H */