tang/0002-Move-key-handling-to-tang-itself.patch
DistroBaker 90caa841a9 Merged update from upstream sources
This is an automated DistroBaker update from upstream sources.
If you do not know what this is about or would like to opt out,
contact the OSCI team.

Source: https://src.fedoraproject.org/rpms/tang.git#2cc81c4c4236830bd3d81eb1ea71ed276a16353d
2020-12-01 19:47:14 +00:00

1457 lines
45 KiB
Diff

From 7961ab6175829c6a71796f34cbe0b8d247245ca9 Mon Sep 17 00:00:00 2001
From: rpm-build <rpm-build>
Date: Tue, 1 Dec 2020 15:30:19 -0300
Subject: [PATCH 2/2] Move key handling to tang itself
Upstream commits:
- https://github.com/latchset/tang/commit/6090505
- https://github.com/latchset/tang/commit/c71df1d
- https://github.com/latchset/tang/commit/7119454
---
meson.build | 2 -
src/keys.c | 455 ++++++++++++++++++++
src/keys.h | 45 ++
src/meson.build | 3 +-
src/tangd-update | 83 ----
src/tangd.c | 52 +--
tests/adv | 6 +-
tests/keys/-bWkGaJi0Zdvxaj4DCp28umLcRA.jwk | 1 +
tests/keys/.r4E2wG1u_YyKUo0N0rIK7jJF5Xg.jwk | 1 +
tests/keys/.uZ0s8YTXcGcuWduWWBSiR2OjOVg.jwk | 1 +
tests/keys/another-bad-file | 1 +
tests/keys/empty.jwk | 0
tests/keys/invalid.jwk | 1 +
tests/keys/qgmqJSo6AEEuVQY7zVlklqdTMqY.jwk | 1 +
tests/meson.build | 38 +-
tests/rec | 4 +-
tests/test-keys.c.in | 257 +++++++++++
tests/test-util.c | 75 ++++
tests/test-util.h | 46 ++
units/meson.build | 21 -
units/tangd-keygen.service.in | 8 -
units/tangd-update.path.in | 4 -
units/tangd-update.service.in | 6 -
units/tangd.socket | 5 -
units/tangd@.service.in | 2 +-
25 files changed, 939 insertions(+), 179 deletions(-)
create mode 100644 src/keys.c
create mode 100644 src/keys.h
delete mode 100755 src/tangd-update
create mode 100644 tests/keys/-bWkGaJi0Zdvxaj4DCp28umLcRA.jwk
create mode 100644 tests/keys/.r4E2wG1u_YyKUo0N0rIK7jJF5Xg.jwk
create mode 100644 tests/keys/.uZ0s8YTXcGcuWduWWBSiR2OjOVg.jwk
create mode 100644 tests/keys/another-bad-file
create mode 100644 tests/keys/empty.jwk
create mode 100644 tests/keys/invalid.jwk
create mode 100644 tests/keys/qgmqJSo6AEEuVQY7zVlklqdTMqY.jwk
create mode 100644 tests/test-keys.c.in
create mode 100644 tests/test-util.c
create mode 100644 tests/test-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/meson.build b/meson.build
index d91f485..0343fa7 100644
--- a/meson.build
+++ b/meson.build
@@ -16,14 +16,12 @@ sysconfdir = join_paths(get_option('prefix'), get_option('sysconfdir'))
bindir = join_paths(get_option('prefix'), get_option('bindir'))
systemunitdir = join_paths(get_option('prefix'), 'lib/systemd/system')
licensedir = join_paths(get_option('prefix'), 'share', 'licenses', meson.project_name())
-cachedir = join_paths(get_option('localstatedir'), 'cache', meson.project_name())
jwkdir = join_paths(get_option('localstatedir'), 'db', meson.project_name())
data = configuration_data()
data.set('libexecdir', libexecdir)
data.set('sysconfdir', sysconfdir)
data.set('systemunitdir', systemunitdir)
-data.set('cachedir', cachedir)
data.set('jwkdir', jwkdir)
add_project_arguments(
diff --git a/src/keys.c b/src/keys.c
new file mode 100644
index 0000000..e79be8d
--- /dev/null
+++ b/src/keys.c
@@ -0,0 +1,455 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2020 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 <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <stdio.h>
+
+#include <jose/b64.h>
+#include <jose/jwk.h>
+#include <jose/jws.h>
+
+#include "keys.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+static const char**
+supported_hashes(void)
+{
+ /* TODO: check if jose has a way to export the hash algorithms it
+ * supports. */
+ static const char* hashes[] = {"S1", "S224", "S256", "S384", "S512", NULL};
+ return hashes;
+}
+
+static int
+is_hash(const char* alg)
+{
+ if (!alg) {
+ return 0;
+ }
+
+ const char** algs = supported_hashes();
+ for (size_t a = 0; algs[a]; a++) {
+ if (strcmp(alg, algs[a]) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static json_t*
+jwk_generate(const char* alg)
+{
+ json_auto_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);
+ return NULL;
+ }
+
+ return json_incref(jalg);
+}
+
+static char*
+jwk_thumbprint(const json_t* jwk, const char* alg)
+{
+ size_t elen = 0;
+ size_t dlen = 0;
+
+ if (!jwk) {
+ fprintf(stderr, "Invalid JWK\n");
+ return NULL;
+ }
+
+ if (!alg || !is_hash(alg)) {
+ fprintf(stderr, "Invalid hash algorithm (%s)\n", alg);
+ return NULL;
+ }
+
+ dlen = jose_jwk_thp_buf(NULL, NULL, alg, NULL, 0);
+ if (dlen == SIZE_MAX) {
+ fprintf(stderr, "Error determining hash size for %s\n", alg);
+ return NULL;
+ }
+
+ elen = jose_b64_enc_buf(NULL, dlen, NULL, 0);
+ if (elen == SIZE_MAX) {
+ fprintf(stderr, "Error determining encoded size for %s\n", alg);
+ return NULL;
+ }
+
+ uint8_t dec[dlen];
+ char enc[elen];
+
+ if (!jose_jwk_thp_buf(NULL, jwk, alg, 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;
+ }
+
+ return strndup(enc, elen);
+}
+
+void
+free_tang_keys_info(struct tang_keys_info* tki)
+{
+ if (!tki) {
+ return;
+ }
+
+ json_t* to_free[] = {tki->m_keys, tki->m_rotated_keys,
+ tki->m_payload, tki->m_sign
+ };
+ size_t len = sizeof(to_free) / sizeof(to_free[0]);
+
+ for (size_t i = 0; i < len; i++) {
+ if (to_free[i] == NULL) {
+ continue;
+ }
+ json_decref(to_free[i]);
+ }
+ free(tki);
+}
+
+void
+cleanup_tang_keys_info(struct tang_keys_info** tki)
+{
+ if (!tki || !*tki) {
+ return;
+ }
+ free_tang_keys_info(*tki);
+ *tki = NULL;
+}
+
+static struct tang_keys_info*
+new_tang_keys_info(void)
+{
+ struct tang_keys_info* tki = calloc(1, sizeof(*tki));
+ if (!tki) {
+ return NULL;
+ }
+
+ tki->m_keys = json_array();
+ tki->m_rotated_keys = json_array();
+ tki->m_payload = json_array();
+ tki->m_sign = json_array();
+
+ if (!tki->m_keys || !tki->m_rotated_keys ||
+ !tki->m_payload || !tki->m_sign) {
+ free_tang_keys_info(tki);
+ return NULL;
+ }
+ tki->m_keys_count = 0;
+ return tki;
+}
+
+static int
+jwk_valid_for(const json_t* jwk, const char* use)
+{
+ if (!jwk || !use) {
+ return 0;
+ }
+ return jose_jwk_prm(NULL, jwk, false, use);
+}
+
+static int
+jwk_valid_for_signing_and_verifying(const json_t* jwk)
+{
+ const char* uses[] = {"sign", "verify", NULL};
+ int ret = 1;
+ for (int i = 0; uses[i]; i++) {
+ if (!jwk_valid_for(jwk, uses[i])) {
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int
+jwk_valid_for_signing(const json_t* jwk)
+{
+ return jwk_valid_for(jwk, "sign");
+}
+
+static int
+jwk_valid_for_deriving_keys(const json_t* jwk)
+{
+ return jwk_valid_for(jwk, "deriveKey");
+}
+
+static void
+cleanup_str(char** str)
+{
+ if (!str || !*str) {
+ return;
+ }
+ free(*str);
+ *str = NULL;
+}
+
+static json_t*
+jwk_sign(const json_t* to_sign, const json_t* sig_keys)
+{
+ if (!sig_keys || !json_is_array(sig_keys) || !json_is_array(to_sign)) {
+ return NULL;
+ }
+
+ json_auto_t* to_sign_copy = json_deep_copy(to_sign);
+ if (!jose_jwk_pub(NULL, to_sign_copy)) {
+ fprintf(stderr, "Error removing private material from data to sign\n");
+ }
+
+ json_auto_t* payload = json_pack("{s:O}", "keys", to_sign_copy);
+ json_auto_t* sig_template = json_pack("{s:{s:s}}",
+ "protected", "cty", "jwk-set+json");
+
+ /* Use the template with the signing keys. */
+ json_auto_t* sig_template_arr = json_array();
+ size_t arr_size = json_array_size(sig_keys);
+ for (size_t i = 0; i < arr_size; i++) {
+ if (json_array_append(sig_template_arr, sig_template) == -1) {
+ fprintf(stderr, "Unable to append sig template to array\n");
+ return NULL;
+ }
+ }
+
+ __attribute__ ((__cleanup__(cleanup_str))) char* data_to_sign = json_dumps(payload, 0);
+ json_auto_t* jws = json_pack("{s:o}", "payload",
+ jose_b64_enc(data_to_sign, strlen(data_to_sign)));
+
+ if (!jose_jws_sig(NULL, jws, sig_template_arr, sig_keys)) {
+ fprintf(stderr, "Error trying to jose_jws_sign\n");
+ return NULL;
+ }
+ return json_incref(jws);
+}
+
+static json_t*
+find_by_thp(struct tang_keys_info* tki, const char* target)
+{
+ if (!tki) {
+ return NULL;
+ }
+
+ json_auto_t* keys = json_deep_copy(tki->m_keys);
+ json_array_extend(keys, tki->m_rotated_keys);
+
+ size_t idx;
+ json_t* jwk;
+ const char** hashes = supported_hashes();
+ json_array_foreach(keys, idx, jwk) {
+ for (int i = 0; hashes[i]; i++) {
+ __attribute__ ((__cleanup__(cleanup_str))) char* thumbprint = jwk_thumbprint(jwk, hashes[i]);
+ if (strcmp(thumbprint, target) != 0) {
+ continue;
+ }
+
+ if (jwk_valid_for_deriving_keys(jwk)) {
+ return json_incref(jwk);
+ } else if (jwk_valid_for_signing(jwk)) {
+ json_auto_t* sign = json_deep_copy(tki->m_sign);
+ if (json_array_append(sign, jwk) == -1) {
+ return NULL;
+ }
+ json_auto_t* jws = jwk_sign(tki->m_payload, sign);
+ if (!jws) {
+ return NULL;
+ }
+ return json_incref(jws);
+ }
+ }
+ }
+ return NULL;
+}
+
+static int
+prepare_payload_and_sign(struct tang_keys_info* tki)
+{
+ if (!tki) {
+ return 0;
+ }
+
+ size_t idx;
+ json_t* jwk;
+ json_array_foreach(tki->m_keys, idx, jwk) {
+ if (jwk_valid_for_signing_and_verifying(jwk)) {
+ if (json_array_append(tki->m_sign, jwk) == -1) {
+ continue;
+ }
+ if (json_array_append(tki->m_payload, jwk) == -1) {
+ continue;
+ }
+ } else if (jwk_valid_for_deriving_keys(jwk)) {
+ if (json_array_append(tki->m_payload, jwk) == -1) {
+ continue;
+ }
+ }
+ }
+ if (json_array_size(tki->m_sign) == 0 || json_array_size(tki->m_payload) == 0) {
+ return 0;
+ }
+ return 1;
+}
+
+static int
+create_new_keys(const char* jwkdir)
+{
+ const char** hashes = supported_hashes();
+ const char* alg[] = {"ES512", "ECMR", NULL};
+ char path[PATH_MAX];
+ for (int i = 0; alg[i] != NULL; i++) {
+ json_auto_t* jwk = jwk_generate(alg[i]);
+ if (!jwk) {
+ return 0;
+ }
+ __attribute__ ((__cleanup__(cleanup_str))) char* thp = jwk_thumbprint(jwk, hashes[0]);
+ if (!thp) {
+ return 0;
+ }
+ if (snprintf(path, PATH_MAX, "%s/%s.jwk", jwkdir, thp) < 0) {
+ fprintf(stderr, "Unable to prepare variable with file full path (%s)\n", thp);
+ return 0;
+ }
+ path[sizeof(path) - 1] = '\0';
+ if (json_dump_file(jwk, path, 0) == -1) {
+ fprintf(stderr, "Error saving JWK to file (%s)\n", path);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static struct tang_keys_info*
+load_keys(const char* jwkdir)
+{
+ struct tang_keys_info* tki = new_tang_keys_info();
+ if (!tki) {
+ return NULL;
+ }
+
+ struct dirent* d;
+ DIR* dir = opendir(jwkdir);
+ if (!dir) {
+ free_tang_keys_info(tki);
+ return NULL;
+ }
+
+ char filepath[PATH_MAX];
+ const char* pattern = ".jwk";
+ while ((d = readdir(dir)) != NULL) {
+ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
+ continue;
+ }
+
+ char* dot = strrchr(d->d_name, '.');
+ if (!dot) {
+ continue;
+ }
+
+ if (strcmp(dot, pattern) == 0) {
+ /* Found a file with .jwk extension. */
+ if (snprintf(filepath, PATH_MAX, "%s/%s", jwkdir, d->d_name) < 0) {
+ fprintf(stderr, "Unable to prepare variable with file full path (%s); skipping\n", d->d_name);
+ continue;
+ }
+ filepath[sizeof(filepath) - 1] = '\0';
+ json_auto_t* json = json_load_file(filepath, 0, NULL);
+ if (!json) {
+ fprintf(stderr, "Invalid JSON file (%s); skipping\n", filepath);
+ continue;
+ }
+
+ json_t* arr = tki->m_keys;
+ if (d->d_name[0] == '.') {
+ arr = tki->m_rotated_keys;
+ }
+ if (json_array_append(arr, json) == -1) {
+ fprintf(stderr, "Unable to append JSON (%s) to array; skipping\n", d->d_name);
+ continue;
+ }
+ tki->m_keys_count++;
+ }
+ }
+ closedir(dir);
+ return tki;
+}
+
+struct tang_keys_info*
+read_keys(const char* jwkdir)
+{
+ struct tang_keys_info* tki = load_keys(jwkdir);
+ if (!tki) {
+ return NULL;
+ }
+
+ if (tki->m_keys_count == 0) {
+ /* Let's attempt to create a new pair of keys. */
+ free_tang_keys_info(tki);
+ if (!create_new_keys(jwkdir)) {
+ return NULL;
+ }
+ tki = load_keys(jwkdir);
+ }
+
+ if (!prepare_payload_and_sign(tki)) {
+ free_tang_keys_info(tki);
+ return NULL;
+ }
+ return tki;
+}
+
+json_t*
+find_jws(struct tang_keys_info* tki, const char* thp)
+{
+ if (!tki) {
+ return NULL;
+ }
+
+ if (thp == NULL) {
+ /* Default advertisement. */
+ json_auto_t* jws = jwk_sign(tki->m_payload, tki->m_sign);
+ if (!jws) {
+ return NULL;
+ }
+ return json_incref(jws);
+ }
+ return find_by_thp(tki, thp);
+}
+
+json_t*
+find_jwk(struct tang_keys_info* tki, const char* thp)
+{
+ if (!tki || !thp) {
+ return NULL;
+ }
+ return find_by_thp(tki, thp);
+}
diff --git a/src/keys.h b/src/keys.h
new file mode 100644
index 0000000..5bae23c
--- /dev/null
+++ b/src/keys.h
@@ -0,0 +1,45 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2020 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 <stddef.h>
+
+struct tang_keys_info {
+ /* Arrays. */
+ json_t* m_keys; /* Regular keys. */
+ json_t* m_rotated_keys; /* Rotated keys. */
+
+ json_t* m_payload; /* Payload made of regular keys capable of
+ * either signing+verifying or deriving new
+ * keys. */
+
+ json_t* m_sign; /* Set of signing keys made from regular
+ keys. */
+
+ size_t m_keys_count; /* Number of keys (regular + rotated). */
+
+};
+
+void cleanup_tang_keys_info(struct tang_keys_info**);
+void free_tang_keys_info(struct tang_keys_info*);
+struct tang_keys_info* read_keys(const char* /* jwkdir */);
+json_t* find_jws(struct tang_keys_info* /* tki */, const char* /* thp */);
+json_t* find_jwk(struct tang_keys_info* /* tki */, const char* /* thp */);
diff --git a/src/meson.build b/src/meson.build
index 1eb8baf..fd14fcb 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,6 +1,6 @@
tangd = executable('tangd',
- 'http.h',
'http.c',
+ 'keys.c',
'tangd.c',
dependencies: [jose, http_parser],
install: true,
@@ -9,6 +9,5 @@ tangd = executable('tangd',
bins += join_paths(meson.current_source_dir(), 'tang-show-keys')
libexecbins += join_paths(meson.current_source_dir(), 'tangd-keygen')
-libexecbins += join_paths(meson.current_source_dir(), 'tangd-update')
# vim:set ts=2 sw=2 et:
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..cd1488e 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)
@@ -36,23 +37,20 @@ str_cleanup(char **str)
free(*str);
}
-static void
-FILE_cleanup(FILE **file)
-{
- if (file && *file)
- fclose(*file);
-}
-
static int
adv(enum http_method method, const char *path, const char *body,
regmatch_t matches[], void *misc)
{
- __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;
+ json_auto_t *jws = NULL;
+ const char *jwkdir = misc;
+
+ tki = read_keys(jwkdir);
+ if (!tki || tki->m_keys_count == 0) {
+ return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
+ }
if (matches[1].rm_so < matches[1].rm_eo) {
size_t size = matches[1].rm_eo - matches[1].rm_so;
@@ -61,24 +59,15 @@ 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)
+ jws = find_jws(tki, thp);
+ if (!jws) {
return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
+ }
- if (fstat(fileno(file), &st) != 0)
- return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
-
- adv = calloc(st.st_size + 1, 1);
+ adv = json_dumps(jws, 0);
if (!adv)
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);
-
return http_reply(HTTP_STATUS_OK,
"Content-Type: application/jose+json\r\n"
"Content-Length: %zu\r\n"
@@ -91,9 +80,9 @@ 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;
size_t size = matches[1].rm_eo - matches[1].rm_so;
- char filename[PATH_MAX] = {};
- const char *cachedir = misc;
+ const char *jwkdir = misc;
json_auto_t *jwk = NULL;
json_auto_t *req = NULL;
json_auto_t *rep = NULL;
@@ -124,15 +113,16 @@ rec(enum http_method method, const char *path, const char *body,
/*
* Parse and validate the server-side JWK
*/
+ tki = read_keys(jwkdir);
+ if (!tki || tki->m_keys_count == 0) {
+ return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
+ }
thp = strndup(&path[matches[1].rm_so], size);
if (!thp)
return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- if (snprintf(filename, sizeof(filename), "%s/%s.jwk", cachedir, thp) < 0)
- return http_reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
-
- jwk = json_load_file(filename, 0, NULL);
+ jwk = find_jwk(tki, thp);
if (!jwk)
return http_reply(HTTP_STATUS_NOT_FOUND, NULL);
@@ -188,7 +178,7 @@ main(int argc, char *argv[])
http_parser_init(&parser, HTTP_REQUEST);
if (argc != 2) {
- fprintf(stderr, "Usage: %s <cachedir>\n", argv[0]);
+ fprintf(stderr, "Usage: %s <jwkdir>\n", argv[0]);
return EXIT_FAILURE;
}
diff --git a/tests/adv b/tests/adv
index 357d097..06d1330 100755
--- a/tests/adv
+++ b/tests/adv
@@ -36,15 +36,13 @@ 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
export 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
@@ -84,4 +82,4 @@ fetch /adv/`jose jwk thp -i $TMP/db/.sig.jwk` \
-g 0 -Og protected -SyOg cty -Sq "jwk-set+json" -EUUUUU \
-g 1 -Og protected -SyOg cty -Sq "jwk-set+json" -EUUUUU
-test $(tang-show-keys $PORT) == $(jose jwk thp -i $TMP/db/sig.jwk)
+test "$(tang-show-keys $PORT)" == "$(jose jwk thp -i $TMP/db/sig.jwk)"
diff --git a/tests/keys/-bWkGaJi0Zdvxaj4DCp28umLcRA.jwk b/tests/keys/-bWkGaJi0Zdvxaj4DCp28umLcRA.jwk
new file mode 100644
index 0000000..d51ccbd
--- /dev/null
+++ b/tests/keys/-bWkGaJi0Zdvxaj4DCp28umLcRA.jwk
@@ -0,0 +1 @@
+{"alg":"ECMR","crv":"P-521","d":"AQ3u1g0L__GIGSJRX1LtjSArwJxxQz0kWXIi-X4PqwoheoeY57cw36pmWmyVsn43jDEZ6SAsiNeIw9sHDkFZe1VV","key_ops":["deriveKey"],"kty":"EC","x":"AcfowrKEKteg_jKX1CiR2RQfbUGJ73KXlcl8AgIDAgN7R6yNKWpKhZNBmV2tAxxMCQcIksqQl17UXwemvH2j2fem","y":"ACrb-y4ZhLIGX-41QYgJhniiZ85qkjILbkVUcC8gBYxOAnKWIpMGLsjrT3AYhM6jk6puwnNYbEM28s2caAEogUcA"}
diff --git a/tests/keys/.r4E2wG1u_YyKUo0N0rIK7jJF5Xg.jwk b/tests/keys/.r4E2wG1u_YyKUo0N0rIK7jJF5Xg.jwk
new file mode 100644
index 0000000..5602a0c
--- /dev/null
+++ b/tests/keys/.r4E2wG1u_YyKUo0N0rIK7jJF5Xg.jwk
@@ -0,0 +1 @@
+{"alg":"ES512","crv":"P-521","d":"Af5SfYh_4LmBDF9dCGDQRDA52yzqnzDeo-GZU05hpnLr6bsIBTFpc8rdcCm95mhZ59ngC-6WNtmAF_nLCDcHKg0A","key_ops":["sign","verify"],"kty":"EC","x":"AUxS3DXdoONUB-6-nyzdd3V42iD7obGTJ1m40t3V6jzXfABWp_gtTidwiKyDJQXxhEzMSToo-V0RGq6Qz2XTOgPe","y":"AFrkJfkLGJz_2v-k3-wdydckVcBXql2NR66HaF0U9NlcfGLezQau7XihArm4GE3-sHoQLsRa-HvYET9zyd9Syh5y"}
diff --git a/tests/keys/.uZ0s8YTXcGcuWduWWBSiR2OjOVg.jwk b/tests/keys/.uZ0s8YTXcGcuWduWWBSiR2OjOVg.jwk
new file mode 100644
index 0000000..5088e1c
--- /dev/null
+++ b/tests/keys/.uZ0s8YTXcGcuWduWWBSiR2OjOVg.jwk
@@ -0,0 +1 @@
+{"alg":"ECMR","crv":"P-521","d":"Acuk66sHvSS5TN-p0XhwHhVKyifYr-ecur-7zfgQZMzSq9SeBJjuX6Ttav-7AbnVvXU_S55BgtGL5iymXGuMguCp","key_ops":["deriveKey"],"kty":"EC","x":"AEz9EwBOIrLeV4in6M1oWVnOWVR7ubkFB0R0-AwyIL7u5-7G3u2tvPIJRQY-l1Wttn7Ar4DhflcMhnb3rk5hT5yh","y":"AZt35wqOhNsnEi-GLAgyCaiW_c6h6Zyo4xwjuvXzmQMDwh9MtdaUigFuBOTlfRj1uri_YBqdpI09nYrqqgx97Ca6"}
diff --git a/tests/keys/another-bad-file b/tests/keys/another-bad-file
new file mode 100644
index 0000000..323fae0
--- /dev/null
+++ b/tests/keys/another-bad-file
@@ -0,0 +1 @@
+foobar
diff --git a/tests/keys/empty.jwk b/tests/keys/empty.jwk
new file mode 100644
index 0000000..e69de29
diff --git a/tests/keys/invalid.jwk b/tests/keys/invalid.jwk
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/tests/keys/invalid.jwk
@@ -0,0 +1 @@
+foo
diff --git a/tests/keys/qgmqJSo6AEEuVQY7zVlklqdTMqY.jwk b/tests/keys/qgmqJSo6AEEuVQY7zVlklqdTMqY.jwk
new file mode 100644
index 0000000..107a545
--- /dev/null
+++ b/tests/keys/qgmqJSo6AEEuVQY7zVlklqdTMqY.jwk
@@ -0,0 +1 @@
+{"alg":"ES512","crv":"P-521","d":"AUQpuWtoSqURl0OuWQ-I9i4X1F3sDak0Hbf9Ixj7uwjA20A0ABJdCHbai1Ai0t3yoxWKPYi6t2XjjeRzHIKyhXbf","key_ops":["sign","verify"],"kty":"EC","x":"ACbYnvh1EtLePkM5jCtuxLpMroOUNRfv-wQdXgT5AQ5bhSLv6wkrBzh1rwymo-fmCNWzrcTflzqWf-wXAd00lokM","y":"AEaDByxXfbee4TlPXgPRg5S4MVOqgObjX6_JJkySTudSfOygcx7dujsf32dOEI25d8bNKUgdjQt8lc5XtHeWXH3a"}
diff --git a/tests/meson.build b/tests/meson.build
index b03531d..9fe656e 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,22 +1,42 @@
+incdir = include_directories(
+ join_paths('..', 'src')
+)
+
+test_data = configuration_data()
+test_data.set('testjwkdir', join_paths(meson.source_root(), 'tests','keys'))
+
+test_keys_c = configure_file(
+ input: 'test-keys.c.in',
+ output: 'test-keys.c',
+ configuration: test_data
+)
+
+test_keys = executable('test-keys',
+ test_keys_c,
+ 'test-util.c',
+ dependencies: [jose],
+ include_directories: incdir
+)
+
sd_activate = find_program(
'systemd-socket-activate',
'systemd-activate',
required: false
)
+env = environment()
+env.prepend('PATH',
+ join_paths(meson.source_root(), 'src'),
+ join_paths(meson.build_root(), 'src'),
+ separator: ':'
+)
+
if sd_activate.found()
- env = environment()
- env.prepend('PATH',
- join_paths(meson.source_root(), 'src'),
- join_paths(meson.build_root(), 'src'),
- separator: ':'
- )
env.set('SD_ACTIVATE', sd_activate.path() + ' --inetd')
- test('adv', find_program('adv'), env: env)
+ test('adv', find_program('adv'), env: env, timeout: 60)
test('rec', find_program('rec'), env: env)
-else
- warning('Will not run the tests due to missing dependencies!')
endif
+test('test-keys', test_keys, env: env, timeout: 60)
# vim:set ts=2 sw=2 et:
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/tests/test-keys.c.in b/tests/test-keys.c.in
new file mode 100644
index 0000000..2071b9f
--- /dev/null
+++ b/tests/test-keys.c.in
@@ -0,0 +1,257 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2020 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 "keys.c"
+#include "test-util.h"
+
+const char* jwkdir = "@testjwkdir@";
+
+struct thp_result {
+ const char* thp;
+ int valid;
+};
+
+struct test_result_int {
+ const char* data;
+ int expected;
+};
+
+static void
+test_create_new_keys(void)
+{
+ __attribute__((cleanup(cleanup_str))) char* newdir = create_tempdir();
+ ASSERT(newdir);
+ __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki = read_keys(newdir);
+ ASSERT(tki);
+ ASSERT(tki->m_keys_count == 2);
+ remove_tempdir(newdir);
+}
+
+
+static void
+test_is_hash(void)
+{
+ const struct test_result_int test_data[] = {
+ {NULL, 0},
+ {"", 0},
+ {"ES512", 0},
+ {"ECMR", 0},
+ {"foobar", 0},
+ {"{", 0},
+ {"[}", 0},
+ {"[]", 0},
+ {"S1", 1},
+ {"S224", 1},
+ {"S256", 1},
+ {"S384", 1},
+ {"S512", 1},
+ {"S42", 0}
+ };
+ for (int i = 0, len = ARRAY_COUNT(test_data); i < len; i++) {
+ int ret = is_hash(test_data[i].data);
+ ASSERT_WITH_MSG(ret == test_data[i].expected, "i = %d, alg = %s", i, test_data[i].data);
+ };
+
+}
+
+static void
+test_jwk_generate(void)
+{
+ const struct test_result_int test_data[] = {
+ {NULL, 0},
+ {"", 0},
+ {"ES512", 1},
+ {"ECMR", 1},
+ {"foobar", 0},
+ {"{", 0},
+ {"[}", 0},
+ {"[]", 0}
+ };
+
+ for (int i = 0, len = ARRAY_COUNT(test_data); i < len; i++) {
+ json_auto_t* jwk = jwk_generate(test_data[i].data);
+ ASSERT_WITH_MSG(!!jwk == test_data[i].expected, "i = %d, alg = %s", i, test_data[i].data);
+ };
+}
+
+static void
+test_find_jws(void)
+{
+ const struct thp_result test_data[] = {
+ {"00BUQM4A7NYxbOrBR9QDfkzGVGj3k57Fs4jCbJxcLYAgRFHu5B7jtbL97x1T7stQ", 1},
+ {"dd5qbN1lQ6UWdZszbfx2oIcH34ShklzFL1SUQg", 1},
+ {"dOZkUtZ_gLDUP53GIlyAxHMNuyrk8vdY-XXND32GccqNbT_MKpqGC-13-GNEye48", 1},
+ {"DZrlBQvfvlwPQlvH_IieBdc_KpesEramLygVL_rFr7g", 1},
+ {"FL_Zt5fFadUL4syeMMpUnss8aKdCrPGFy3102JGR3EE", 1},
+ {"qgmqJSo6AEEuVQY7zVlklqdTMqY", 1},
+ {"r4E2wG1u_YyKUo0N0rIK7jJF5Xg", 1},
+ {"ugJ4Ula-YABQIiJ-0g3B_jpFpF2nl3W-DNpfLdXArhTusV0QCcd1vtgDeGHEPzpm7jEsyC7VYYSSOkZicK22mw", 1},
+ {"up0Z4fRhpd4O5QwBaMCXDTlrvxCmZacU0MD8kw", 1},
+ {"vllHS-M0aQFCo2yUCcAahMU4TAtXACyeuRf-zbmmTPBg7V0Pb-RRFGo5C6MnpzdirK8B3ORLOsN8RyXClvtjxA", 1},
+ {NULL, 1},
+ {"a", 0},
+ {"foo", 0},
+ {"bar", 0},
+ {"XXXXXXXXXXXXXXXXXX", 0}
+ };
+
+ __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki = read_keys(jwkdir);
+ for (int i = 0, len = ARRAY_COUNT(test_data); i < len; i++) {
+ json_auto_t* jws = find_jws(tki, test_data[i].thp);
+ ASSERT_WITH_MSG(!!jws == test_data[i].valid, "i = %d, thp = %s", i, test_data[i].thp);
+ }
+
+ /* Passing NULL to find_jws should return the default advertisement */
+ json_auto_t* adv = find_jws(tki, NULL);
+ ASSERT(adv);
+
+
+ /*
+ * The default set of signing keys are the signing keys that are not
+ * rotated. The payload is made of deriving keys that are also not
+ * rotated. The default advertisement should be signed by this set of
+ * default signing keys.
+ */
+ ASSERT(jose_jws_ver(NULL, adv, NULL, tki->m_sign, 1));
+
+ /* find_jws should be able to respond to thumbprints of keys using any
+ * of jose supported hash algorithms. */
+ const char** hashes = supported_hashes();
+ size_t idx;
+ json_t* jwk;
+
+ /* Let's put together all the keys, including rotated ones. */
+ json_auto_t* keys = json_deep_copy(tki->m_keys);
+ ASSERT(keys);
+ ASSERT(json_array_extend(keys, tki->m_rotated_keys) == 0);
+ ASSERT(json_array_size(keys) == (size_t)tki->m_keys_count);
+
+ for (int i = 0; hashes[i]; i++) {
+ json_array_foreach(keys, idx, jwk) {
+ if (!jwk_valid_for_signing(jwk)) {
+ continue;
+ }
+ __attribute__((cleanup(cleanup_str))) char* thp = jwk_thumbprint(jwk, hashes[i]);
+ ASSERT_WITH_MSG(thp, "i = %d, hash = %s, key idx = %d", i, hashes[i], idx);
+ json_auto_t* jws = find_jws(tki, thp);
+ ASSERT_WITH_MSG(jws, "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
+
+ /* Signing keys should sign the payload, in addition to the
+ * default set of signing keys. */
+ json_auto_t* sign = json_deep_copy(tki->m_sign);
+ ASSERT_WITH_MSG(sign, "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
+ ASSERT_WITH_MSG(json_array_append(sign, jwk) == 0, "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
+ ASSERT_WITH_MSG(jose_jws_ver(NULL, jws, NULL, sign, 1), "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
+ }
+ }
+}
+
+static void
+test_find_jwk(void)
+{
+ const struct thp_result test_data[] = {
+ {"1HdF3XKRSsuZdkpXNurBPoL_pvxdvCOlHuhB4DP-4xWFqbZ51zo29kR4fSiT3BGy9UrHVJ26JMBLOA1vKq3lxA", 1},
+ {"9U8qgy_YjyY6Isuq6QuiKEiYZgNJShcGgJx5FJzCu6m3N6zFaIPy_HDkxkVqAZ9E", 1},
+ {"-bWkGaJi0Zdvxaj4DCp28umLcRA", 1},
+ {"Cy73glFjs6B6RU7wy6vWxAc-2bJy5VJOT9LyK80eKgZ8k27wXZ-3rjsuNU5tua_yHWtluyoSYtjoKXfI0E8ESw", 1},
+ {"kfjbqx_b3BsgPC87HwlOWL9daGMMHBzxcFLClw", 1},
+ {"L4xg2tZXTEVbsK39bzOZM1jGWn3HtOxF5gh6F9YVf5Q", 1},
+ {"LsVAV2ig5LlfstM8TRSf-c7IAkLpNYbIysNuRCVlxocRCGqAh6-f9PklM4nU4N-J", 1},
+ {"OkAcDxYHNlo7-tul8OubYuWXB8CPEhAkcacCmhTclMU", 1},
+ {"uZ0s8YTXcGcuWduWWBSiR2OjOVg", 1},
+ {"WEpfFyeoNKkE2-TosN_bP-gd9UgRvQCZpVasZQ", 1},
+ {NULL, 0},
+ {"a", 0},
+ {"foo", 0},
+ {"bar", 0},
+ {"XXXXXXXXXXXXXXXXXX", 0},
+ };
+
+ __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki = read_keys(jwkdir);
+
+ for (int i = 0, len = ARRAY_COUNT(test_data); i < len; i++) {
+ json_auto_t* tjwk = find_jwk(tki, test_data[i].thp);
+ ASSERT_WITH_MSG(!!tjwk == test_data[i].valid, "i = %d, thp = %s", i, test_data[i].thp);
+ }
+ /* Passing NULL to find_jwk should fail */
+ json_auto_t* bad_jwk = find_jwk(tki, NULL);
+ ASSERT(bad_jwk == NULL);
+
+ /* find_jwk should be able to respond to thumbprints of keys using any
+ * of jose supported hash algorithms. */
+ const char** hashes = supported_hashes();
+ size_t idx;
+ json_t* jwk;
+
+ /* Let's put together all the keys, including rotated ones. */
+ json_auto_t* keys = json_deep_copy(tki->m_keys);
+ ASSERT(keys);
+ ASSERT(json_array_extend(keys, tki->m_rotated_keys) == 0);
+ ASSERT(json_array_size(keys) == (size_t)tki->m_keys_count);
+
+ for (int i = 0; hashes[i]; i++) {
+ json_array_foreach(keys, idx, jwk) {
+ if (!jwk_valid_for_deriving_keys(jwk)) {
+ continue;
+ }
+ __attribute__((cleanup(cleanup_str))) char* thp = jwk_thumbprint(jwk, hashes[i]);
+ json_auto_t* tjwk = find_jwk(tki, thp);
+ ASSERT_WITH_MSG(tjwk, "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
+ }
+ }
+}
+
+static void
+test_read_keys(void)
+{
+ __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki = read_keys(jwkdir);
+ ASSERT(tki);
+
+ /*
+ * Keys in tests/keys:
+ * - .uZ0s8YTXcGcuWduWWBSiR2OjOVg.jwk
+ * - .r4E2wG1u_YyKUo0N0rIK7jJF5Xg.jwk
+ * - qgmqJSo6AEEuVQY7zVlklqdTMqY.jwk
+ * - -bWkGaJi0Zdvxaj4DCp28umLcRA.jwk
+ */
+ ASSERT(tki->m_keys_count == 4);
+ ASSERT(json_array_size(tki->m_keys) == 2);
+ ASSERT(json_array_size(tki->m_rotated_keys) == 2);
+
+ const char* invalid_jwkdir = "foobar";
+ __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki2 = read_keys(invalid_jwkdir);
+ ASSERT(tki2 == NULL);
+}
+
+static void
+run_tests(void)
+{
+ test_read_keys();
+ test_find_jwk();
+ test_find_jws();
+ test_jwk_generate();
+ test_is_hash();
+ test_create_new_keys();
+}
+
+int main(int argc, char** argv)
+{
+ run_tests();
+ return 0;
+}
diff --git a/tests/test-util.c b/tests/test-util.c
new file mode 100644
index 0000000..bc1299b
--- /dev/null
+++ b/tests/test-util.c
@@ -0,0 +1,75 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2020 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/>.
+ */
+
+#define _XOPEN_SOURCE 500L
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ftw.h>
+
+#include "test-util.h"
+
+void
+assert_func(const char* filename,
+ int lineno,
+ const char* funcname,
+ const char* expr,
+ const char* fmt,
+ ...)
+{
+ char buffer[MAX_BUF_LEN] = {};
+ if (fmt) {
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buffer, MAX_BUF_LEN, fmt, ap);
+ va_end(ap);
+ buffer[strcspn(buffer, "\r\n")] = '\0';
+ }
+ fprintf(stderr, "%s:%d: assertion '%s' failed in %s(). %s\n", filename,
+ lineno,
+ expr,
+ funcname,
+ buffer);
+ abort();
+}
+
+static int
+nftw_remove_callback(const char* path, const struct stat* stat,
+ int type, struct FTW* ftw)
+{
+ return remove(path);
+}
+
+char*
+create_tempdir(void)
+{
+ char template[] = "/tmp/tang.test.XXXXXX";
+ char *tmpdir = mkdtemp(template);
+ return strdup(tmpdir);
+}
+
+int
+remove_tempdir(const char* path)
+{
+ return nftw(path, nftw_remove_callback, FOPEN_MAX, FTW_DEPTH|FTW_MOUNT|FTW_PHYS);
+}
+
diff --git a/tests/test-util.h b/tests/test-util.h
new file mode 100644
index 0000000..eae9a71
--- /dev/null
+++ b/tests/test-util.h
@@ -0,0 +1,46 @@
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
+/*
+ * Copyright (c) 2020 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 <stdarg.h>
+
+#define ARRAY_COUNT(arr) (sizeof(arr)/sizeof(0[arr]))
+#define MAX_BUF_LEN 2048
+
+void
+assert_func(const char* /* filename */,
+ int /* line number */,
+ const char* /* function name */,
+ const char* /* expression */,
+ const char* /* format */,
+ ...);
+
+#define ASSERT_WITH_MSG(expr, fmt, ...) \
+ if (!(expr)) \
+ assert_func(__FILE__, __LINE__, __FUNCTION__, #expr, fmt, ##__VA_ARGS__)
+
+#define ASSERT(expr) \
+ ASSERT_WITH_MSG(expr, NULL)
+
+char*
+create_tempdir(void);
+
+int
+remove_tempdir(const char* /* path */);
diff --git a/units/meson.build b/units/meson.build
index ada6dd0..6e66af9 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -1,31 +1,10 @@
-tangd_keygen_service = configure_file(
- input: 'tangd-keygen.service.in',
- output: 'tangd-keygen.service',
- configuration: data
-)
-
tangd_service = configure_file(
input: 'tangd@.service.in',
output: 'tangd@.service',
configuration: data
)
-tangd_update_path = configure_file(
- input: 'tangd-update.path.in',
- output: 'tangd-update.path',
- configuration: data
-)
-
-tangd_update_service = configure_file(
- input: 'tangd-update.service.in',
- output: 'tangd-update.service',
- configuration: data
-)
-
units += join_paths(meson.current_source_dir(), 'tangd.socket')
-units += tangd_keygen_service
units += tangd_service
-units += tangd_update_path
-units += tangd_update_service
# vim:set ts=2 sw=2 et:
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 b/units/tangd.socket
index 22474ea..0a3e239 100644
--- a/units/tangd.socket
+++ b/units/tangd.socket
@@ -1,10 +1,5 @@
[Unit]
Description=Tang Server socket
-Requires=tangd-keygen.service
-Requires=tangd-update.service
-Requires=tangd-update.path
-After=tangd-keygen.service
-After=tangd-update.service
[Socket]
ListenStream=80
diff --git a/units/tangd@.service.in b/units/tangd@.service.in
index c4b9597..f1db261 100644
--- a/units/tangd@.service.in
+++ b/units/tangd@.service.in
@@ -5,4 +5,4 @@ Description=Tang Server
StandardInput=socket
StandardOutput=socket
StandardError=journal
-ExecStart=@libexecdir@/tangd @cachedir@
+ExecStart=@libexecdir@/tangd @jwkdir@
--
2.28.0