Blame SOURCES/0001-Move-key-generation-to-tang.patch

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