Blob Blame History Raw
From e016a42a204bd6ee26982975011b8a243a4096e6 Mon Sep 17 00:00:00 2001
From: Sergio Correia <scorreia@redhat.com>
Date: Mon, 30 Mar 2020 18:12:20 -0300
Subject: [PATCH] Move key generation to tang

---
 Makefile.am                   |   10 +-
 src/keys.c                    | 1043 +++++++++++++++++++++++++++++++++++++++++
 src/keys.h                    |   84 ++++
 src/tangd-update              |   83 ----
 src/tangd.c                   |   60 ++-
 src/util.c                    |  141 ++++++
 src/util.h                    |   33 ++
 tests/adv                     |    4 +-
 tests/nagios                  |    3 +-
 tests/rec                     |    4 +-
 units/tangd-keygen.service.in |    8 -
 units/tangd-update.path.in    |    4 -
 units/tangd-update.service.in |    6 -
 units/tangd.socket.in         |    2 -
 units/tangd@.service.in       |    4 +-
 15 files changed, 1343 insertions(+), 146 deletions(-)
 create mode 100644 src/keys.c
 create mode 100644 src/keys.h
 delete mode 100755 src/tangd-update
 create mode 100644 src/util.c
 create mode 100644 src/util.h
 delete mode 100644 units/tangd-keygen.service.in
 delete mode 100644 units/tangd-update.path.in
 delete mode 100644 units/tangd-update.service.in

diff --git a/Makefile.am b/Makefile.am
index 14bf91d..6d769ea 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,18 +9,15 @@ jwkdir = $(localstatedir)/db/$(PACKAGE_NAME)
 
 nodist_systemdsystemunit_DATA = \
     units/tangd@.service \
-    units/tangd.socket \
-    units/tangd-update.path \
-    units/tangd-update.service \
-    units/tangd-keygen.service
+    units/tangd.socket
 
-dist_libexec_SCRIPTS = src/tangd-update src/tangd-keygen
+dist_libexec_SCRIPTS = src/tangd-keygen
 libexec_PROGRAMS = src/tangd
 nagios_PROGRAMS = src/tang
 man1_MANS = doc/tang-nagios.1
 man8_MANS = doc/tang.8
 
-src_tangd_SOURCES = src/http.c src/http.h src/tangd.c
+src_tangd_SOURCES = src/util.c src/util.h src/keys.c src/keys.h src/http.c src/http.h src/tangd.c
 src_tang_SOURCES = src/nagios.c
 
 %: %.in
@@ -28,7 +25,6 @@ src_tang_SOURCES = src/nagios.c
 	$(AM_V_GEN)$(SED) \
 		-e 's,@libexecdir\@,$(libexecdir),g' \
 		-e 's,@jwkdir\@,$(jwkdir),g' \
-		-e 's,@cachedir\@,$(cachedir),g' \
 		$(srcdir)/$@.in > $@
 
 AM_TESTS_ENVIRONMENT = SD_ACTIVATE="@SD_ACTIVATE@" PATH=$(srcdir)/src:$(builddir)/src:$(PATH)
diff --git a/src/keys.c b/src/keys.c
new file mode 100644
index 0000000..77f5d3c
--- /dev/null
+++ b/src/keys.c
@@ -0,0 +1,1043 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ * Author: Sergio Correia <scorreia@redhat.com>
+ *
+ * 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 3 of the License, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <jose/b64.h>
+#include <jose/jwk.h>
+#include <jose/jws.h>
+#include <jose/io.h>
+#include <jansson.h>
+#include <string.h>
+
+#include "util.h"
+#include "keys.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define TANG_MAXBUFLEN (1024 * 1024)
+
+#define DEFAULT_HASH_ALG "S1"
+
+/* TODO: check if jose has a way to export the hash algorithms it supports. */
+const char *hash_alg[] = {"S1", "S224", "S256", "S384", "S512", NULL};
+
+size_t
+hash_alg_size(void)
+{
+    size_t count = 0;
+    for (size_t i = 0; hash_alg[i]; i++) {
+        count++;
+    }
+    return count;
+}
+
+int
+is_hash(const char *alg)
+{
+    for (size_t a = 0, size = hash_alg_size(); a < size; a++) {
+        if (strcmp(alg, hash_alg[a]) == 0) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Generates a JWK and returns a json_t*, which must be released with
+ * json_decref().
+ */
+static json_t*
+jwk_generate(const char* alg)
+{
+    json_t *jalg = json_pack("{s:s}", "alg", alg);
+    if (!jalg) {
+        fprintf(stderr, "Error packing JSON with alg %s\n", alg);
+        return NULL;
+    }
+
+    if (!jose_jwk_gen(NULL, jalg)) {
+        fprintf(stderr, "Error generating JWK with alg %s\n", alg);
+        json_decref(jalg);
+        return NULL;
+    }
+
+    return jalg;
+}
+
+/*
+ * Returns a thumbprint from a JWK and a given algorithm, which must be
+ * released with free().
+ */
+char*
+jwk_thumbprint(const json_t* jwk, const char* alg)
+{
+    size_t elen = 0;
+    size_t dlen = 0;
+
+    const char* hash = alg;
+    if (!is_hash(alg)) {
+        hash = DEFAULT_HASH_ALG;
+    }
+
+    dlen = jose_jwk_thp_buf(NULL, NULL, hash, NULL, 0);
+    if (dlen == SIZE_MAX) {
+        fprintf(stderr, "Error determining hash size for %s\n", hash);
+        return NULL;
+    }
+
+    elen = jose_b64_enc_buf(NULL, dlen, NULL, 0);
+    if (elen == SIZE_MAX) {
+        fprintf(stderr, "Error determining encoded size for %s\n", hash);
+        return NULL;
+    }
+
+    uint8_t dec[dlen];
+    char enc[elen];
+
+    if (!jose_jwk_thp_buf(NULL, jwk, hash, dec, sizeof(dec))) {
+        fprintf(stderr, "Error making thumbprint\n");
+        return NULL;
+    }
+
+    if (jose_b64_enc_buf(dec, dlen, enc, sizeof(enc)) != elen) {
+        fprintf(stderr, "Error encoding data Base64\n");
+        return NULL;
+    }
+
+    char *thp = malloc(elen + 1);
+    if (!thp) {
+        fprintf(stderr, "Error allocating string for thumbprint\n");
+        return NULL;
+    }
+
+    if (!strncpy(thp, enc, elen)) {
+        fprintf(stderr, "Error copying thumbprint to string\n");
+        free(thp);
+        return NULL;
+    }
+
+    thp[elen] = '\0';
+    return thp;
+}
+
+char*
+jwk_thumbprint_from_file(const char *file, const char *alg)
+{
+    json_auto_t *jwk = json_load_file(file, 0, NULL);
+    if (!jwk) {
+        return 0;
+    }
+    return jwk_thumbprint(jwk, alg);
+}
+
+/*
+ * Releases the allocated memory by struct tang_jwk*.
+ */
+void
+free_tang_jwk(struct tang_jwk* tjwk)
+{
+    if (!tjwk) {
+        return;
+    }
+
+    if (tjwk->m_json) {
+        json_decref(tjwk->m_json);
+    }
+    free(tjwk->m_from_file);
+    free(tjwk->m_thp);
+    free(tjwk->m_alg);
+    free(tjwk->m_str);
+    free(tjwk);
+}
+
+void
+cleanup_tang_jwk(struct tang_jwk **jwk)
+{
+    if (!jwk || !*jwk) {
+        return;
+    }
+    free_tang_jwk(*jwk);
+}
+
+struct tang_jwk*
+new_tang_jwk_from_args(json_t *jwk, const char* thp, const char* alg)
+{
+    if (!jwk) {
+        fprintf(stderr, "Invalid JWK (%p) \n", jwk);
+        return NULL;
+    }
+
+    struct tang_jwk *tjwk = calloc(1, sizeof(*tjwk));
+    if (!tjwk) {
+        fprintf(stderr, "Error allocating new struct tang_jwk.\n");
+        return NULL;
+    }
+
+    tjwk->m_json = json_incref(jwk);
+    tjwk->m_from_file = NULL;
+
+    if (alg) {
+        tjwk->m_alg = strdup(alg);
+        if (!tjwk->m_alg) {
+            fprintf(stderr, "Unable to copy algorithm (%s) to tang_jwk.\n", alg);
+            free_tang_jwk(tjwk);
+            return NULL;
+        }
+    }
+
+    if (thp) {
+        tjwk->m_thp = strdup(thp);
+        if (!tjwk->m_thp) {
+            fprintf(stderr, "Unable to copy thumbprint (%s).\n", thp);
+            free_tang_jwk(tjwk);
+            return NULL;
+        }
+    }
+
+    tjwk->m_str = json_dumps(tjwk->m_json, JSON_SORT_KEYS | JSON_COMPACT);
+    if (!tjwk->m_str) {
+        fprintf(stderr, "Unable to get string version from JWK.\n");
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+
+    return tjwk;
+}
+
+struct tang_jwk*
+tang_jwk_dup(const struct tang_jwk *jwk)
+{
+    if (!jwk) {
+        return NULL;
+    }
+
+    struct tang_jwk *new_jwk = new_tang_jwk_from_args(jwk->m_json, jwk->m_thp, jwk->m_alg);
+    if (!new_jwk) {
+        return NULL;
+    }
+
+    if (jwk->m_from_file) {
+        new_jwk->m_from_file = strdup(jwk->m_from_file);
+        if (!new_jwk->m_from_file) {
+            free_tang_jwk(new_jwk);
+            return NULL;
+        }
+    }
+
+    return new_jwk;
+}
+
+struct tang_jwk*
+new_tang_jwk(const char* file, const char* alg)
+{
+    if (!file || !alg) {
+        fprintf(stderr, "Invalid file (%s) or algorithm (%s).\n", file, alg);
+        return NULL;
+    }
+
+    json_auto_t *jwk = json_load_file(file, 0, NULL);
+    if (!jwk) {
+        fprintf(stderr, "Unable to parse JSON from %s.\n", file);
+        return NULL;
+    }
+
+    struct tang_jwk *tjwk = calloc(1, sizeof(*tjwk));
+    if (!tjwk) {
+        fprintf(stderr, "Error allocating new struct tang_jwk.\n");
+        return NULL;
+    }
+
+    tjwk->m_json = json_incref(jwk);
+    tjwk->m_from_file = strdup(file);
+    if (!tjwk->m_from_file) {
+        fprintf(stderr, "Unable to copy file name (%s) to m_from_file.\n", file);
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+    tjwk->m_alg = strdup(alg);
+    if (!tjwk->m_alg) {
+        fprintf(stderr, "Unable to copy algorithm (%s) to tang_jwk.\n", alg);
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+
+    tjwk->m_thp = jwk_thumbprint(tjwk->m_json, tjwk->m_alg);
+    if (!tjwk->m_thp) {
+        fprintf(stderr, "Unable to get thumbprint using alg (%s).\n", alg);
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+
+    tjwk->m_str = json_dumps(tjwk->m_json, JSON_SORT_KEYS | JSON_COMPACT);
+    if (!tjwk->m_str) {
+        fprintf(stderr, "Unable to get string version from JWK.\n");
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+
+    return tjwk;
+}
+
+/*
+ * Builds a new struct tang_jwk*, which should be destructed by calling
+ * free_tang_jwk().
+ */
+struct tang_jwk*
+generate_new_tang_jwk(const char* alg)
+{
+    if (!alg) {
+        fprintf(stderr, "Invalid algorithm.\n");
+        return NULL;
+    }
+
+    struct tang_jwk *tjwk = calloc(1, sizeof(*tjwk));
+    if (!tjwk) {
+        fprintf(stderr, "Error allocating new struct tang_jwk.\n");
+        return NULL;
+    }
+
+    tjwk->m_alg = strdup(alg);
+    if (!tjwk->m_alg) {
+        fprintf(stderr, "Unable to copy algorithm (%s) to tang_jwk.\n", alg);
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+
+    tjwk->m_json = jwk_generate(alg);
+    if (!tjwk->m_json) {
+        fprintf(stderr, "Unable to generate new JWK using alg (%s).\n", alg);
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+    tjwk->m_thp = jwk_thumbprint(tjwk->m_json, tjwk->m_alg);
+    if (!tjwk->m_thp) {
+        fprintf(stderr, "Unable to get thumbprint using alg (%s).\n", alg);
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+
+    tjwk->m_str = json_dumps(tjwk->m_json, JSON_SORT_KEYS | JSON_COMPACT);
+    if (!tjwk->m_str) {
+        fprintf(stderr, "Unable to get string version from JWK.\n");
+        free_tang_jwk(tjwk);
+        return NULL;
+    }
+    return tjwk;
+}
+
+static int
+file_valid_for(const char *file, const char *use)
+{
+    json_auto_t *jwk = json_load_file(file, 0, NULL);
+    if (!jwk) {
+        return 0;
+    }
+
+    return jose_jwk_prm(NULL, jwk, false, use);
+}
+
+static int
+jwk_valid_for(const json_t *jwk, const char *use)
+{
+    return jose_jwk_prm(NULL, jwk, false, use);
+}
+
+
+int valid_for_signing_and_verifying(const char *file)
+{
+    json_auto_t *jwk = json_load_file(file, 0, NULL);
+    if (!jwk) {
+        return 0;
+    }
+
+    const char *use[] = {"sign", "verify", NULL};
+    int ret = 1;
+    for (int i = 0; use[i] != NULL; i++) {
+        if (!jwk_valid_for(jwk, use[i])) {
+            ret = 0;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+int valid_for_signing(const char *file)
+{
+    return file_valid_for(file, "sign");
+}
+
+int valid_for_deriving_keys(const char *file)
+{
+    return file_valid_for(file, "deriveKey");
+}
+
+struct tang_jwk_list*
+new_tang_jwk_list(void)
+{
+    struct tang_jwk_list *tjl = malloc(sizeof(*tjl));
+    if (!tjl) {
+        return NULL;
+    }
+    tjl->m_jwk = NULL;
+    tjl->m_size = 0;
+    return tjl;
+}
+
+void
+free_tang_jwk_list(struct tang_jwk_list *tjl)
+{
+    if (!tjl) {
+        return;
+    }
+
+    for (size_t i = 0, size = tjl->m_size; i < size; i++) {
+        free_tang_jwk(tjl->m_jwk[i]);
+    }
+    free(tjl->m_jwk);
+    free(tjl);
+}
+
+int
+tang_jwk_list_add(struct tang_jwk_list *tjl, struct tang_jwk *jwk_to_add)
+{
+    if (!tjl || !jwk_to_add) {
+        return 0;
+    }
+
+    struct tang_jwk *jwk = tang_jwk_dup(jwk_to_add);
+    if (!jwk) {
+        return 0;
+    }
+
+    struct tang_jwk **new_jwk = realloc(tjl->m_jwk, sizeof(struct tang_jwk*) * (tjl->m_size + 1));
+    if (!new_jwk) {
+        fprintf(stderr, "Error reallocating memory for the new JWK.\n");
+        free_tang_jwk(jwk);
+        return 0;
+    }
+
+    tjl->m_jwk = new_jwk;
+    tjl->m_jwk[tjl->m_size++] = jwk;
+    return 1;
+}
+
+static int
+tang_jwk_thp_bsearch_cmp_func(const void *a, const void *b)
+{
+    const char *key = (const char*)a;
+    const struct tang_jwk *jwk = *(const struct tang_jwk**)b;
+    return strcmp(key, jwk->m_thp);
+}
+
+struct tang_jwk*
+tang_jwk_list_find_thp(const struct tang_jwk_list *tjl, const char *thp)
+{
+    if (!tjl || !thp) {
+        return NULL;
+    }
+
+    if (tjl->m_size == 0) {
+        return NULL;
+    }
+
+    struct tang_jwk **item = bsearch(thp, tjl->m_jwk, tjl->m_size, sizeof(struct tang_jwk*), tang_jwk_thp_bsearch_cmp_func);
+    if (!item) {
+        return NULL;
+    }
+    return *item;
+}
+
+void
+free_tang_keys_info(struct tang_keys_info *tki)
+{
+    if (!tki) {
+        return;
+    }
+
+    free(tki->m_jwkdir);
+    free_file_list(tki->m_payload_keys);
+    free_file_list(tki->m_sign_keys);
+    free_tang_jwk_list(tki->m_derive);
+    free_tang_jwk_list(tki->m_adv);
+    free_tang_jwk(tki->m_default_adv);
+    free(tki);
+}
+
+struct tang_keys_info*
+new_tang_keys_info(const char* jwkdir)
+{
+    if (!jwkdir) {
+        fprintf(stderr, "Invalid JWK dir.\n");
+        return NULL;
+    }
+
+    struct tang_keys_info *tki = calloc(1, sizeof(struct tang_keys_info));
+    if (!tki) {
+        fprintf(stderr, "Error allocating tang_keys_info struct.\n");
+        return NULL;
+    }
+
+    tki->m_jwkdir = strdup(jwkdir);
+    if (!tki->m_jwkdir) {
+        fprintf(stderr, "Error copying JWK dir to tang_keys_info struct.\n");
+        free_tang_keys_info(tki);
+        return NULL;
+    }
+
+    tki->m_payload_keys = new_file_list();
+    if (!tki->m_payload_keys) {
+        fprintf(stderr, "Error allocating payload keys.\n");
+        free_tang_keys_info(tki);
+        return NULL;
+    }
+
+    tki->m_sign_keys = new_file_list();
+    if (!tki->m_sign_keys) {
+        fprintf(stderr, "Error allocating signing keys.\n");
+        free_tang_keys_info(tki);
+        return NULL;
+    }
+
+    tki->m_derive = new_tang_jwk_list();
+    if (!tki->m_derive) {
+        fprintf(stderr, "Error allocating list of deriving keys.\n");
+        free_tang_keys_info(tki);
+        return NULL;
+    }
+
+    tki->m_adv = new_tang_jwk_list();
+    if (!tki->m_adv) {
+        fprintf(stderr, "Error allocating list adv.\n");
+        free_tang_keys_info(tki);
+        return NULL;
+    }
+
+    tki->m_default_adv = NULL;
+
+    return tki;
+}
+
+int
+check_keys(const char* jwkdir)
+{
+    if (!jwkdir) {
+        return 0;
+    }
+
+    /* We ignore hidden files in here because we only care about
+     * advertised keys. */
+    struct file_list *fl __attribute__ ((__cleanup__(cleanup_file_list))) = list_files(jwkdir, ".jwk", 1 /* ignore hidden */);
+    if (!fl) {
+        return 0;
+    }
+
+    if (fl->m_size > 0) {
+        /* There are already keys in the JWKdir, so let's leave it as is. */
+        return 1;
+    }
+
+    /* At this point, there are no keys, so let's create them. */
+    const char *alg[] = {"ES512", "ECMR", NULL};
+    char path[PATH_MAX];
+    for (int i = 0; alg[i] != NULL; i++) {
+        struct tang_jwk *jwk __attribute__((cleanup(cleanup_tang_jwk))) = generate_new_tang_jwk(alg[i]);
+        if (!jwk) {
+            fprintf(stderr, "Error generating JWK using %s\n", alg[i]);
+            return 0;
+        }
+
+        snprintf(path, PATH_MAX, "%s/%s.jwk", jwkdir, jwk->m_thp);
+        path[sizeof(path) - 1] = '\0';
+
+        FILE *fp = fopen(path, "w+");
+        if (!fp) {
+            fprintf(stderr, "Error creating JWK file to %s\n", path);
+            return 0;
+        }
+        fprintf(fp, "%s", jwk->m_str);
+        fclose(fp);
+    }
+    return 1;
+}
+
+static int
+tang_jwk_thp_cmp_func(const void *a, const void *b)
+{
+    const struct tang_jwk *ta = *(const struct tang_jwk**)a;
+    const struct tang_jwk *tb = *(const struct tang_jwk**)b;
+    return strcmp(ta->m_thp, tb->m_thp);
+}
+
+void
+cleanup_tang_keys_info(struct tang_keys_info **tki)
+{
+    if (!tki || !*tki) {
+        return;
+    }
+    free_tang_keys_info(*tki);
+}
+
+static void
+cleanup_buffer(char **buffer)
+{
+    if (!buffer || !*buffer) {
+        return;
+    }
+    free(*buffer);
+}
+
+static void
+cleanup_jose_io_t(jose_io_t ***iosp)
+{
+    jose_io_t **ios = *iosp;
+    for (size_t i = 0; ios && ios[i]; i++) {
+        jose_io_auto(&ios[i]);
+    }
+}
+
+static json_t*
+build_json_array(const char **files, size_t total_files)
+{
+    if (!files || total_files == 0) {
+        return NULL;
+    }
+
+    json_t *arr = json_array();
+    if (!arr) {
+        fprintf(stderr, "Unable to create json array\n");
+        return NULL;
+    }
+
+    for (size_t i = 0; i < total_files; i++) {
+        json_t *jwk = json_load_file(files[i], 0, NULL);
+        if (!jwk) {
+            fprintf(stderr, "Unable to load JSON from %s; skipping\n", files[i]);
+            continue;
+        }
+
+        if (json_array_append_new(arr, jwk) != 0) {
+            fprintf(stderr, "Unable to append JSON %s to array; skipping\n", files[i]);
+            continue;
+        }
+    }
+    return arr;
+}
+
+static json_t*
+remove_private_keys(const struct file_list *fl)
+{
+    if (!fl) {
+        fprintf(stderr, "Invalid file list for cleaning private keys.\n");
+        return NULL;
+    }
+
+    json_auto_t *array = build_json_array((const char**)fl->m_files, fl->m_size);
+    if (!array || json_array_size(array) == 0) {
+        fprintf(stderr, "Empty array %p.\n", array);
+        return NULL;
+    }
+
+    for (size_t i = 0, size = json_array_size(array); i < size; i++) {
+        if (!jose_jwk_pub(NULL, json_array_get(array, i))) {
+            fprintf(stderr, "Error removing private keys.\n");
+            return NULL;
+        }
+    }
+
+    return json_pack("{s:O}", "keys", array);
+}
+
+static json_t*
+prepare_template_sigs(size_t total)
+{
+    json_t *arr = json_array();
+    if (!arr) {
+        fprintf(stderr, "Unable to create JSON sigs array\n");
+        return NULL;
+    }
+
+    for (size_t i = 0; i < total; i++) {
+        json_t *cty = json_pack("{s:{s:s}}", "protected", "cty", "jwk-set+json");
+        if (!cty) {
+            fprintf(stderr, "Unable to create item %zu/%zu; skipping\n", i, total);
+            continue;
+        }
+
+        if (json_array_append_new(arr, cty) != 0) {
+            fprintf(stderr, "Unable to append item %zu/%zu to array; skipping\n", i, total);
+            continue;
+        }
+    }
+    return arr;
+}
+
+static jose_io_t*
+tang_prep_io(jose_io_t *io, uint8_t *buffer, size_t *buflen)
+{
+    if (!io || !buffer || !buflen) {
+        fprintf(stderr, "Either io (%p) the buffer (%p) or the buffer len (%p) are NULL\n", io, buffer, buflen);
+    }
+
+    jose_io_t **ios __attribute__((cleanup(cleanup_jose_io_t))) = NULL;
+    size_t i = 0;
+
+    ios = alloca(sizeof(*ios) * 3);
+    memset(ios, 0, sizeof(*ios) * 3);
+
+    if (io) {
+        ios[i++] = io;
+    }
+
+    ios[i] = jose_io_buffer(NULL, buffer, buflen);
+    if (!ios[i]) {
+            return NULL;
+    }
+
+    for (i = 0; ios[i]; i++) {
+        jose_io_auto_t *b64 = NULL;
+
+        b64 = jose_b64_enc_io(ios[i]);
+        if (!b64) {
+            return NULL;
+        }
+
+        jose_io_decref(ios[i]);
+        ios[i] = jose_io_incref(b64);
+    }
+
+    return jose_io_multiplex(NULL, ios, true);
+}
+
+static int
+prepare_deriving_jwk(struct tang_keys_info *tki, const struct file_list *fl)
+{
+    const size_t hash_alg_count = hash_alg_size();
+    for (size_t a = 0; a < hash_alg_count; a++) {
+        for (size_t i = 0; i < fl->m_size; i++) {
+            struct tang_jwk *jwk __attribute__((cleanup(cleanup_tang_jwk))) = new_tang_jwk(fl->m_files[i], hash_alg[a]);
+            if (!jwk) {
+                fprintf(stderr, "Unable to create tang_jwk from %s with alg %s; skipping.\n", fl->m_files[i], hash_alg[a]);
+                continue;
+            }
+            if (!tang_jwk_list_add(tki->m_derive, jwk)) {
+                fprintf(stderr, "Unable to add JWK from %s with alg %s to list of deriving keys; skipping.\n", fl->m_files[i], hash_alg[a]);
+                continue;
+            }
+        }
+    }
+
+    if (tki->m_derive->m_size > 1) {
+        qsort(tki->m_derive->m_jwk, tki->m_derive->m_size, sizeof(struct tang_jwk*), tang_jwk_thp_cmp_func);
+    }
+    return 1;
+}
+
+static int
+prepare_adv_jwk(struct tang_keys_info *tki, const struct file_list *fl)
+{
+    const size_t hash_alg_count = hash_alg_size();
+    json_auto_t *keys = build_json_array((const char**)tki->m_sign_keys->m_files, tki->m_sign_keys->m_size);
+    json_auto_t *pub = remove_private_keys(tki->m_payload_keys);
+
+    /*
+     * Adding dummy element in the first position. We will be be replacing
+     * it with the actual ones and then creating the JWS data.
+     */
+    json_auto_t *dummy = json_object();
+    if (json_array_insert(keys, 0, dummy) != 0) {
+        fprintf(stderr, "Error preparing adv JWKs.\n");
+        return 0;
+    }
+
+    json_auto_t *sigs = prepare_template_sigs(json_array_size(keys));
+
+    for (size_t i = 0; i < fl->m_size; i++) {
+        json_auto_t *jwk = json_load_file(fl->m_files[i], 0, NULL);
+        if (!jwk) {
+            fprintf(stderr, "Unable to load JWK from %s; skipping.\n", fl->m_files[i]);
+            continue;
+        }
+        if (json_array_set(keys, 0, jwk) != 0) {
+            fprintf(stderr, "Unable to add JWK from file (%s) to array; skipping.\n", fl->m_files[i]);
+            continue;
+        }
+
+        char *jws_data __attribute__((cleanup(cleanup_buffer))) = process_adv(keys, sigs, pub);
+        if (!jws_data) {
+            fprintf(stderr, "Unable to obtain JWS from %s; skipping.\n", fl->m_files[i]);
+            continue;
+        }
+        json_auto_t *jws_json = json_loads(jws_data, 0, NULL);
+        if (!jws_json) {
+            fprintf(stderr, "Unable to convert string to JSON; skipping.\n");
+            continue;
+        }
+        for (size_t a = 0; a < hash_alg_count; a++) {
+            char *thp __attribute__((cleanup(cleanup_buffer))) = jwk_thumbprint(jwk, hash_alg[a]);
+            if (!thp) {
+                fprintf(stderr, "Unable to obtain thumbprint from file (%s) and alg (%s); skipping.\n", fl->m_files[i], hash_alg[a]);
+                continue;
+            }
+            struct tang_jwk *jws __attribute__((cleanup(cleanup_tang_jwk)))= new_tang_jwk_from_args(jws_json, thp, hash_alg[a]);
+            if (!jws) {
+                fprintf(stderr, "Error creating tang_jwk with JWS data from file (%s) and alg (%s); skipping.\n", fl->m_files[i], hash_alg[a]);
+                continue;
+            }
+            if (!tang_jwk_list_add(tki->m_adv, jws)) {
+                fprintf(stderr, "Error adding JWS data from file (%s) and alg (%s) to adv list; skipping.\n", fl->m_files[i], hash_alg[a]);
+                continue;
+            }
+        }
+    }
+
+    if (tki->m_adv->m_size > 1) {
+        qsort(tki->m_adv->m_jwk, tki->m_adv->m_size, sizeof(struct tang_jwk*), tang_jwk_thp_cmp_func);
+    }
+    return 1;
+}
+
+static int
+prepare_deriving_and_adv(struct tang_keys_info *tki)
+{
+    if (!tki) {
+        return 0;
+    }
+
+    struct file_list *fl __attribute__ ((__cleanup__(cleanup_file_list))) = list_files(tki->m_jwkdir, ".jwk", 0 /* ignore hidden */);
+    if (!fl || fl->m_size == 0) {
+        fprintf(stderr, "No JWK keys available.\n");
+        return 0;
+    }
+
+    struct file_list *derive_key __attribute__((__cleanup__(cleanup_file_list))) = new_file_list();
+    struct file_list *jws __attribute__((__cleanup__(cleanup_file_list))) = new_file_list();
+
+    char filepath[PATH_MAX] = {};
+    for (size_t i = 0, size = fl->m_size; i < size; i++) {
+        snprintf(filepath, sizeof(filepath), "%s/%s", tki->m_jwkdir, fl->m_files[i]);
+        filepath[sizeof(filepath) - 1] = '\0';
+        if (valid_for_deriving_keys(filepath)) {
+            if (!file_list_add(derive_key, filepath)) {
+                fprintf(stderr, "Error adding %s to file list of keys valid for deriving keys; skipping.\n", filepath);
+            }
+        } else if (valid_for_signing(filepath)) {
+            if (!file_list_add(jws, filepath)) {
+                fprintf(stderr, "Error adding %s to file list of keys valid for signing; skipping.\n", filepath);
+            }
+        }
+    }
+
+    if (derive_key->m_size == 0 || jws->m_size == 0) {
+        fprintf(stderr, "Either the number of keys able to derive keys (%zu) or to sign keys (%zu) is zero.\n", derive_key->m_size, jws->m_size);
+        return 0;
+    }
+
+    if (!prepare_deriving_jwk(tki, derive_key)) {
+        fprintf(stderr, "Error preparing deriving keys JWK.\n");
+        return 0;
+    }
+
+    if (!prepare_adv_jwk(tki, jws)) {
+        fprintf(stderr, "Error preparing advertising JWK.\n");
+        return 0;
+    }
+
+    return 1;
+}
+
+struct tang_jwk*
+find_adv(const struct tang_keys_info *tki, const char* thp)
+{
+    if (!tki) {
+        fprintf(stderr, "Invalid tang_keys_info (%p).\n", tki);
+        return NULL;
+    }
+    return tang_jwk_list_find_thp(tki->m_adv, thp);
+}
+
+struct tang_jwk*
+find_deriving_key(const struct tang_keys_info *tki, const char* thp)
+{
+    if (!tki) {
+        fprintf(stderr, "Invalid tang_keys_info (%p).\n", tki);
+        return NULL;
+    }
+    return tang_jwk_list_find_thp(tki->m_derive, thp);
+}
+
+struct tang_keys_info*
+read_keys(const char *jwkdir)
+{
+    struct tang_keys_info *tki = new_tang_keys_info(jwkdir);
+    if (!tki) {
+        fprintf(stderr, "Unable to create tang_keys_info\n");
+        return NULL;
+    }
+
+    struct file_list *fl __attribute__ ((__cleanup__(cleanup_file_list))) = list_files(jwkdir, ".jwk", 1 /* ignore hidden */);
+    if (!fl || fl->m_size == 0) {
+        fprintf(stderr, "No JWK keys available.\n");
+        free_tang_keys_info(tki);
+        return NULL;
+    }
+
+    char filepath[PATH_MAX] = {};
+    for (size_t i = 0, size = fl->m_size; i < size; i++) {
+        snprintf(filepath, sizeof(filepath), "%s/%s", jwkdir, fl->m_files[i]);
+        filepath[sizeof(filepath) - 1] = '\0';
+        if (valid_for_signing_and_verifying(filepath)) {
+            if (!file_list_add(tki->m_sign_keys, filepath)) {
+                fprintf(stderr, "Error adding %s to the list of signing keys\n", filepath);
+                free_tang_keys_info(tki);
+                return NULL;
+            }
+            if (!file_list_add(tki->m_payload_keys, filepath)) {
+                fprintf(stderr, "Error adding %s to the list of payload keys\n", filepath);
+                free_tang_keys_info(tki);
+                return NULL;
+            }
+        } else if (valid_for_deriving_keys(filepath)) {
+            if (!file_list_add(tki->m_payload_keys, filepath)) {
+                fprintf(stderr, "Error adding %s to the list of payload keys\n", filepath);
+                free_tang_keys_info(tki);
+                return NULL;
+            }
+        }
+    }
+
+    if (!prepare_deriving_and_adv(tki)) {
+        fprintf(stderr, "Unable to prepare deriving and advcertising JWKs.\n");
+        free_tang_keys_info(tki);
+        return NULL;
+    }
+
+    if (!prepare_default_adv(tki)) {
+        fprintf(stderr, "Unable to prepare the default adv.\n");
+        free_tang_keys_info(tki);
+        return NULL;
+    }
+
+    return tki;
+}
+
+int
+process_payload(const json_t *jwk, jose_io_t *io)
+{
+    char *payload __attribute__((__cleanup__(cleanup_buffer))) = json_dumps(jwk, JSON_SORT_KEYS | JSON_COMPACT);
+    if (!payload) {
+        fprintf(stderr, "Error converting JSON to char*.\n");
+        return 0;
+    }
+
+    for (size_t i = 0, size = strlen(payload); i < size; i++) {
+        uint8_t b = payload[i];
+        if (!io->feed(io, &b, sizeof(b))) {
+            fprintf(stderr, "Error calling io-feed with b = [%c].\n", b);
+            return 0;
+        }
+    }
+
+    if (!io->done(io)) {
+        fprintf(stderr, "Error calling io-done.\n");
+        return 0;
+    }
+    return 1;
+}
+
+char*
+process_adv(const json_t *keys, json_t* sigs, const json_t *pub)
+{
+    /* For the IO data, we need to have an own buffer. */
+    uint8_t buffer[TANG_MAXBUFLEN] = {};
+    size_t buflen = sizeof(buffer);
+    json_auto_t *io_data = json_object();
+
+    jose_io_auto_t *io = jose_jws_sig_io(NULL, io_data, sigs, keys);
+    if (!io) {
+        fprintf(stderr, "jose_jws_sig_io() failed.\n");
+        return NULL;
+    }
+
+    io = tang_prep_io(io, buffer, &buflen);
+    if (!io) {
+        fprintf(stderr, "tang_prep_io() failed.\n");
+        return NULL;
+    }
+
+    if (!process_payload(pub, io)) {
+        fprintf(stderr, "Error processing payload.\n");
+        return NULL;
+    }
+
+    const char *preamble = "{\"payload\":\"";
+    const char *separator = "\",";
+    const char *postamble = "}";
+    char *data __attribute__ ((__cleanup__(cleanup_buffer))) = json_dumps(io_data, JSON_EMBED | JSON_COMPACT | JSON_SORT_KEYS);
+    if (!data) {
+        fprintf(stderr, "Error obtaining signing data.\n");
+        return NULL;
+    }
+
+    size_t adv_len = strlen(preamble) + buflen + strlen(separator) + strlen(data) + strlen(postamble) + 1;
+    char *adv_data = malloc(adv_len);
+    snprintf(adv_data, adv_len, "%s%s%s%s%s", preamble, buffer, separator, data, postamble);
+    adv_data[adv_len - 1] = '\0';
+    return adv_data;
+}
+
+int
+prepare_default_adv(struct tang_keys_info *tki)
+{
+    if (!tki) {
+        fprintf(stderr, "Invalid tang_keys_info\n");
+        return 0;
+    }
+
+    if (!tki->m_sign_keys || tki->m_sign_keys->m_size == 0) {
+        fprintf(stderr, "No valid signing keys.\n");
+        return 0;
+    }
+
+    if (!tki->m_payload_keys || tki->m_payload_keys->m_size <= tki->m_sign_keys->m_size) {
+        fprintf(stderr, "Invalid payload keys.\n");
+        return 0;
+    }
+
+    json_auto_t *keys = build_json_array((const char**)tki->m_sign_keys->m_files, tki->m_sign_keys->m_size);
+    json_auto_t *sigs = prepare_template_sigs(tki->m_sign_keys->m_size);
+    json_auto_t *pub = remove_private_keys(tki->m_payload_keys);
+
+    char *adv_str __attribute__((cleanup(cleanup_buffer))) = process_adv(keys, sigs, pub);
+    if (!adv_str) {
+        return 0;
+    }
+    json_auto_t *json = json_loads(adv_str, 0, NULL);
+    if (!json) {
+        return 0;
+    }
+
+    struct tang_jwk *jwk = new_tang_jwk_from_args(json, NULL, NULL);
+    if (!jwk) {
+        return 0;
+    }
+    tki->m_default_adv = jwk;
+    return 1;
+}
diff --git a/src/keys.h b/src/keys.h
new file mode 100644
index 0000000..150b881
--- /dev/null
+++ b/src/keys.h
@@ -0,0 +1,84 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ * Author: Sergio Correia <scorreia@redhat.com>
+ *
+ * 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 3 of the License, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <jansson.h>
+#include <jose/io.h>
+
+struct tang_jwk {
+    json_t* m_json;
+    char* m_from_file;
+    char* m_str;
+    char* m_thp;
+    char* m_alg;
+};
+
+struct tang_jwk_list {
+    struct tang_jwk **m_jwk;
+    size_t m_size;
+};
+
+struct tang_keys_info {
+    char *m_jwkdir;
+    struct file_list *m_payload_keys;
+    struct file_list *m_sign_keys;
+
+    struct tang_jwk_list *m_derive;
+    struct tang_jwk_list *m_adv;
+    struct tang_jwk *m_default_adv;
+};
+
+/* struct tang_jwk. */
+struct tang_jwk *new_tang_jwk(const char* /* file */, const char* /* alg */);
+struct tang_jwk* new_tang_jwk_from_args(json_t*, const char* /* thp */, const char* /* alg */);
+struct tang_jwk* tang_jwk_dup(const struct tang_jwk*);
+struct tang_jwk *generate_new_tang_jwk(const char* /* alg */);
+void free_tang_jwk(struct tang_jwk*);
+void cleanup_tang_jwk(struct tang_jwk **jwk);
+
+/* struct tang_jwk_list. */
+struct tang_jwk_list* new_tang_jwk_list(void);
+void free_tang_jwk_list(struct tang_jwk_list*);
+int tang_jwk_list_add(struct tang_jwk_list*, struct tang_jwk*);
+struct tang_jwk* tang_jwk_list_find_thp(const struct tang_jwk_list*, const char*);
+
+char *jwk_thumbprint(const json_t* /* jwk */, const char* /* alg */);
+char *jwk_thumbprint_from_file(const char* /* file */, const char* /* alg */);
+int valid_for_signing(const char* /* file */);
+int valid_for_signing_and_verifying(const char* /* file */);
+int valid_for_deriving_keys(const char* /* file */);
+
+struct tang_keys_info* new_tang_keys_info(const char*);
+void free_tang_keys_info(struct tang_keys_info*);
+void cleanup_tang_keys_info(struct tang_keys_info**);
+
+
+struct tang_keys_info* read_keys(const char*);
+
+int process_payload(const json_t*, jose_io_t*);
+char* process_adv(const json_t*, json_t*, const json_t*);
+int prepare_default_adv(struct tang_keys_info*);
+
+size_t hash_alg_size(void);
+int check_keys(const char*);
+struct tang_jwk* find_adv(const struct tang_keys_info*, const char*);
+struct tang_jwk* find_deriving_key(const struct tang_keys_info*, const char*);
+int is_hash(const char*);
+
diff --git a/src/tangd-update b/src/tangd-update
deleted file mode 100755
index 652dbef..0000000
--- a/src/tangd-update
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/bash
-# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
-#
-# Copyright (c) 2016 Red Hat, Inc.
-# Author: Nathaniel McCallum <npmccallum@redhat.com>
-#
-# 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 3 of the License, 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 <http://www.gnu.org/licenses/>.
-#
-
-TMP='{"protected":{"cty":"jwk-set+json"}}'
-
-trap 'exit' ERR
-
-shopt -s nullglob
-
-HASHES=`jose alg -k hash`
-
-if [ $# -ne 2 ] || [ ! -d "$1" ]; then
-    echo "Usage: $0 <jwkdir> <cachedir>" >&2
-    exit 1
-fi
-
-[ ! -d "$2" ] && mkdir -p -m 0700 "$2"
-
-src=`realpath "$1"`
-dst=`realpath "$2"`
-
-payl=()
-sign=()
-
-for jwk in $src/*.jwk; do
-    if jose jwk use -i "$jwk" -r -u sign -u verify; then
-        sign+=("-s" "$TMP" "-k" "$jwk")
-        payl+=("-i" "$jwk")
-    elif jose jwk use -i "$jwk" -r -u deriveKey; then
-        payl+=("-i" "$jwk")
-    else
-        echo "Skipping invalid key: $jwk" >&2
-    fi
-done
-
-if [ ${#sign[@]} -gt 0 ]; then
-    jose jwk pub -s "${payl[@]}" \
-        | jose jws sig -I- "${sign[@]}" -o "$dst/.default.jws"
-    mv -f "$dst/.default.jws" "$dst/default.jws"
-    new=default.jws
-fi
-
-shopt -s dotglob
-
-for jwk in $src/*.jwk; do
-    for hsh in $HASHES; do
-        thp=`jose jwk thp -i "$jwk" -a $hsh`
-
-        if jose jwk use -i "$jwk" -r -u deriveKey; then
-            ln -sf "$jwk" "$dst/.$thp.jwk"
-            mv -f "$dst/.$thp.jwk" "$dst/$thp.jwk"
-            new="$new\n$thp.jwk"
-        elif jose jwk use -i "$jwk" -r -u sign; then
-            keys=("${sign[@]}" -s "$TMP" -k "$jwk")
-            jose jwk pub -s "${payl[@]}" \
-                | jose jws sig -I- "${keys[@]}" -o "$dst/.$thp.jws"
-            mv -f "$dst/.$thp.jws" "$dst/$thp.jws"
-            new="$new\n$thp.jws"
-        fi
-    done
-done
-
-for f in "$dst"/*; do
-    b=`basename "$f"`
-    echo -e "$new" | grep -q "^$b\$" || rm -f "$f"
-done
diff --git a/src/tangd.c b/src/tangd.c
index dc45a90..b569f38 100644
--- a/src/tangd.c
+++ b/src/tangd.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include <jose/jose.h>
+#include "keys.h"
 
 static void
 str_cleanup(char **str)
@@ -50,9 +51,8 @@ adv(enum http_method method, const char *path, const char *body,
     __attribute__((cleanup(FILE_cleanup))) FILE *file = NULL;
     __attribute__((cleanup(str_cleanup))) char *adv = NULL;
     __attribute__((cleanup(str_cleanup))) char *thp = NULL;
-    char filename[PATH_MAX] = {};
-    const char *cachedir = misc;
-    struct stat st = {};
+    __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info *tki = NULL;
+    const char *jwkdir = misc;
 
     if (matches[1].rm_so < matches[1].rm_eo) {
         size_t size = matches[1].rm_eo - matches[1].rm_so;
@@ -61,23 +61,25 @@ adv(enum http_method method, const char *path, const char *body,
             return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
     }
 
-    if (snprintf(filename, sizeof(filename),
-                 "%s/%s.jws", cachedir, thp ? thp : "default") < 0)
-        return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
-
-    file = fopen(filename, "r");
-    if (!file)
-        return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
-
-    if (fstat(fileno(file), &st) != 0)
+    if (!check_keys(jwkdir)) {
         return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
+    }
 
-    adv = calloc(st.st_size + 1, 1);
-    if (!adv)
+    tki = read_keys(jwkdir);
+    if (!tki) {
         return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
+    }
 
-    if (fread(adv, st.st_size, 1, file) != 1)
-        return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
+    if (thp) {
+        const struct tang_jwk *jwk = find_adv(tki, thp);
+        if (!jwk) {
+            return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
+        }
+        adv = strdup(jwk->m_str);
+    } else {
+        /* Default adv. */
+        adv = strdup(tki->m_default_adv->m_str);
+    }
 
     return http_reply(HTTP_STATUS_OK,
                       "Content-Type: application/jose+json\r\n"
@@ -91,10 +93,11 @@ rec(enum http_method method, const char *path, const char *body,
 {
     __attribute__((cleanup(str_cleanup))) char *enc = NULL;
     __attribute__((cleanup(str_cleanup))) char *thp = NULL;
+    __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info *tki = NULL;
+    const struct tang_jwk *jwk = NULL;
+
     size_t size = matches[1].rm_eo - matches[1].rm_so;
-    char filename[PATH_MAX] = {};
-    const char *cachedir = misc;
-    json_auto_t *jwk = NULL;
+    const char *jwkdir = misc;
     json_auto_t *req = NULL;
     json_auto_t *rep = NULL;
     const char *alg = NULL;
@@ -129,17 +132,24 @@ rec(enum http_method method, const char *path, const char *body,
     if (!thp)
         return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
 
-    if (snprintf(filename, sizeof(filename), "%s/%s.jwk", cachedir, thp) < 0)
+    if (!check_keys(jwkdir)) {
+        return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
+    }
+
+    tki = read_keys(jwkdir);
+    if (!tki) {
         return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
+    }
 
-    jwk = json_load_file(filename, 0, NULL);
-    if (!jwk)
+    jwk = find_deriving_key(tki, thp);
+    if (!jwk) {
         return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
+    }
 
-    if (!jose_jwk_prm(NULL, jwk, true, "deriveKey"))
+    if (!jose_jwk_prm(NULL, jwk->m_json, true, "deriveKey"))
         return http_reply(HTTP_STATUS_FORBIDDEN, NULL);
 
-    if (json_unpack(jwk, "{s:s,s?s}", "d", &d, "alg", &alg) < 0)
+    if (json_unpack(jwk->m_json, "{s:s,s?s}", "d", &d, "alg", &alg) < 0)
         return http_reply(HTTP_STATUS_FORBIDDEN, NULL);
 
     if (alg && strcmp(alg, "ECMR") != 0)
@@ -148,7 +158,7 @@ rec(enum http_method method, const char *path, const char *body,
     /*
      * Perform the exchange and return
      */
-    rep = jose_jwk_exc(NULL, jwk, req);
+    rep = jose_jwk_exc(NULL, jwk->m_json, req);
     if (!rep)
         return http_reply(HTTP_STATUS_BAD_REQUEST, NULL);
 
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..b8e3756
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,141 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ * Author: Sergio Correia <scorreia@redhat.com>
+ *
+ * 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 3 of the License, 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "util.h"
+
+struct file_list*
+new_file_list(void)
+{
+    struct file_list *fl = malloc(sizeof(*fl));
+    if (!fl) {
+        return NULL;
+    }
+    fl->m_files = NULL;
+    fl->m_size = 0;
+
+    return fl;
+}
+
+void
+free_file_list(struct file_list *fl)
+{
+    if (!fl) {
+        return;
+    }
+
+    for (size_t i = 0, size = fl->m_size; i < size; i++) {
+        free(fl->m_files[i]);
+    }
+    free(fl->m_files);
+    fl->m_size = 0;
+    free(fl);
+}
+
+void
+cleanup_file_list(struct file_list **fl)
+{
+    if (!fl || !*fl) {
+        return;
+    }
+    free_file_list(*fl);
+}
+
+int
+file_list_add(struct file_list *fl, const char *filepath)
+{
+    if (!fl || !filepath) {
+        return 0;
+    }
+
+    char **new_files = realloc(fl->m_files, sizeof(char *) * (fl->m_size + 1));
+    if (!new_files) {
+        fprintf(stderr, "Error reallocating memory for the new file\n");
+        return 0;
+    }
+
+    fl->m_files = new_files;
+    fl->m_files[fl->m_size++] = strdup(filepath);
+    return 1;
+}
+
+int
+match_file(const char *file, const char *pattern)
+{
+    if (!file) {
+        return 0;
+    }
+
+    if (!pattern) {
+        return 1;
+    }
+
+    return strstr(file, pattern) != NULL;
+}
+
+int
+list_files_cmp_func(const void *a, const void *b)
+{
+    const char *sa = *(const char**)a;
+    const char *sb = *(const char**)b;
+    return strcmp(sa, sb);
+}
+
+struct file_list*
+list_files(const char *path, const char *pattern, int ignore_hidden)
+{
+    struct file_list *fl = new_file_list();
+    struct dirent *d;
+    DIR *dir = opendir(path);
+
+    if (dir == NULL) {
+        return fl;
+    }
+
+    while ((d = readdir(dir)) != NULL) {
+        if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
+            continue;
+        }
+
+        if (ignore_hidden && d->d_name[0] == '.') {
+            continue;
+        }
+
+        if (match_file(d->d_name, pattern)) {
+            if (!file_list_add(fl, d->d_name)) {
+                fprintf(stderr, "Unable to add file %s to file list.\n", d->d_name);
+                continue;
+            }
+        }
+    }
+
+    if (fl->m_size > 1) {
+        qsort(fl->m_files, fl->m_size, sizeof(char*), list_files_cmp_func);
+    }
+
+    closedir(dir);
+    return fl;
+}
+
+
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..c3af014
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,33 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ * Author: Sergio Correia <scorreia@redhat.com>
+ *
+ * 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 3 of the License, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+struct file_list {
+    char **m_files;
+    size_t m_size;
+};
+
+struct file_list* new_file_list(void);
+void free_file_list(struct file_list*);
+void cleanup_file_list(struct file_list**);
+int file_list_add(struct file_list* /* fl */, const char* /* filepath */);
+int match_file(const char* /* file */, const char* /* pattern */);
+int list_files_cmp_func(const void*, const void*);
+struct file_list* list_files(const char* /* path */, const char* /* match */, int /* ignore_hidden */);
diff --git a/tests/adv b/tests/adv
index e2092ca..2266d20 100755
--- a/tests/adv
+++ b/tests/adv
@@ -28,16 +28,14 @@ trap 'exit' ERR
 
 export TMP=`mktemp -d`
 mkdir -p $TMP/db
-mkdir -p $TMP/cache
 
 tangd-keygen $TMP/db sig exc
 jose jwk gen -i '{"alg": "ES512"}' -o $TMP/db/.sig.jwk
 jose jwk gen -i '{"alg": "ES512"}' -o $TMP/db/.oth.jwk
-tangd-update $TMP/db $TMP/cache
 
 for addr in "127.0.0.1" "[::1]"; do
   port=`shuf -i 1024-65536 -n 1`
-  $SD_ACTIVATE -l "$addr:$port" -a $VALGRIND tangd $TMP/cache &
+  $SD_ACTIVATE -l "$addr:$port" -a $VALGRIND tangd $TMP/db &
   export PID=$!
   sleep 0.5
 
diff --git a/tests/nagios b/tests/nagios
index f0ea1eb..dbac3de 100755
--- a/tests/nagios
+++ b/tests/nagios
@@ -32,11 +32,10 @@ mkdir -p $TMP/cache
 
 # Generate the server keys
 tangd-keygen $TMP/db
-tangd-update $TMP/db $TMP/cache
 
 # Start the server
 port=`shuf -i 1024-65536 -n 1`
-$SD_ACTIVATE -l 127.0.0.1:$port -a $VALGRIND tangd $TMP/cache &
+$SD_ACTIVATE -l 127.0.0.1:$port -a $VALGRIND tangd $TMP/db &
 export PID=$!
 sleep 0.5
 
diff --git a/tests/rec b/tests/rec
index d1dfe97..3316565 100755
--- a/tests/rec
+++ b/tests/rec
@@ -28,11 +28,9 @@ trap 'exit' ERR
 
 export TMP=`mktemp -d`
 mkdir -p $TMP/db
-mkdir -p $TMP/cache
 
 # Generate the server keys
 tangd-keygen $TMP/db sig exc
-tangd-update $TMP/db $TMP/cache
 
 # Generate the client keys
 exc_kid=`jose jwk thp -i $TMP/db/exc.jwk`
@@ -42,7 +40,7 @@ jose jwk pub -i $TMP/exc.jwk -o $TMP/exc.pub.jwk
 
 # Start the server
 port=`shuf -i 1024-65536 -n 1`
-$SD_ACTIVATE -l 127.0.0.1:$port -a $VALGRIND tangd $TMP/cache &
+$SD_ACTIVATE -l 127.0.0.1:$port -a $VALGRIND tangd $TMP/db &
 export PID=$!
 sleep 0.5
 
diff --git a/units/tangd-keygen.service.in b/units/tangd-keygen.service.in
deleted file mode 100644
index 2f80cd8..0000000
--- a/units/tangd-keygen.service.in
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=Tang Server key generation script
-ConditionDirectoryNotEmpty=|!@jwkdir@
-Requires=tangd-update.path
-
-[Service]
-Type=oneshot
-ExecStart=@libexecdir@/tangd-keygen @jwkdir@
diff --git a/units/tangd-update.path.in b/units/tangd-update.path.in
deleted file mode 100644
index ee9005d..0000000
--- a/units/tangd-update.path.in
+++ /dev/null
@@ -1,4 +0,0 @@
-[Path]
-PathChanged=@jwkdir@
-MakeDirectory=true
-DirectoryMode=0700
diff --git a/units/tangd-update.service.in b/units/tangd-update.service.in
deleted file mode 100644
index 11a4cb2..0000000
--- a/units/tangd-update.service.in
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Tang Server key update script
-
-[Service]
-Type=oneshot
-ExecStart=@libexecdir@/tangd-update @jwkdir@ @cachedir@
diff --git a/units/tangd.socket.in b/units/tangd.socket.in
index 30b0f98..0a3e239 100644
--- a/units/tangd.socket.in
+++ b/units/tangd.socket.in
@@ -1,7 +1,5 @@
 [Unit]
 Description=Tang Server socket
-Requires=tangd-update.path
-Requires=tangd-keygen.service
 
 [Socket]
 ListenStream=80
diff --git a/units/tangd@.service.in b/units/tangd@.service.in
index 5b792a4..f1db261 100644
--- a/units/tangd@.service.in
+++ b/units/tangd@.service.in
@@ -1,10 +1,8 @@
 [Unit]
 Description=Tang Server
-Requires=tangd-update.path
-Requires=tangd-keygen.service
 
 [Service]
 StandardInput=socket
 StandardOutput=socket
 StandardError=journal
-ExecStart=@libexecdir@/tangd @cachedir@
+ExecStart=@libexecdir@/tangd @jwkdir@
-- 
1.8.3.1