From 639a486f7a6bc8d1d141628f7a3b7231400b2b84 Mon Sep 17 00:00:00 2001
From: Karel Slany <karel.slany@nic.cz>
Date: Mon, 13 Jun 2016 19:05:15 +0200
Subject: [PATCH] Started working on 'server' cookie code.

---
 lib/cookies/{algorithm.c => alg_clnt.c} | 172 +++++++-----
 lib/cookies/{algorithm.h => alg_clnt.h} |  60 ++--
 lib/cookies/alg_srvr.c                  | 355 ++++++++++++++++++++++++
 lib/cookies/alg_srvr.h                  |  92 ++++++
 lib/cookies/control.c                   |   4 +-
 lib/cookies/control.h                   |   4 +-
 lib/layer/cookiemonster.c               |  50 +---
 lib/lib.mk                              |   6 +-
 modules/cookiectl/cookiectl.c           |  18 +-
 9 files changed, 590 insertions(+), 171 deletions(-)
 rename lib/cookies/{algorithm.c => alg_clnt.c} (73%)
 rename lib/cookies/{algorithm.h => alg_clnt.h} (54%)
 create mode 100644 lib/cookies/alg_srvr.c
 create mode 100644 lib/cookies/alg_srvr.h

diff --git a/lib/cookies/algorithm.c b/lib/cookies/alg_clnt.c
similarity index 73%
rename from lib/cookies/algorithm.c
rename to lib/cookies/alg_clnt.c
index 5724c9b17..d441ed2be 100644
--- a/lib/cookies/algorithm.c
+++ b/lib/cookies/alg_clnt.c
@@ -24,84 +24,19 @@
 #include <openssl/sha.h>
 
 #include "contrib/fnv/fnv.h"
-#include "lib/cookies/algorithm.h"
+#include "lib/cookies/alg_clnt.h"
 
 //#define CC_HASH_USE_CLIENT_ADDRESS /* When defined, client address will be used when generating client cookie. */
 
-const struct kr_clnt_cookie_alg_descr kr_clnt_cookie_algs[] = {
-	{ kr_clnt_cookie_alg_fnv64, "FNV-64" },
-	{ kr_clnt_cookie_alg_hmac_sha256_64, "HMAC-SHA256-64" },
-	{ NULL, NULL }
-};
-
-clnt_cookie_alg_t *kr_clnt_cookie_alg_func(const struct kr_clnt_cookie_alg_descr cc_algs[],
-                                           const char *name)
-{
-	if (!cc_algs || !name) {
-		return NULL;
-	}
-
-	const struct kr_clnt_cookie_alg_descr *aux_ptr = cc_algs;
-	while (aux_ptr && aux_ptr->func) {
-		assert(aux_ptr->name);
-		if (strcmp(aux_ptr->name, name) == 0) {
-			return aux_ptr->func;
-		}
-		++aux_ptr;
-	}
-
-	return NULL;
-}
-
-const char *kr_clnt_cookie_alg_name(const struct kr_clnt_cookie_alg_descr cc_algs[],
-                                    clnt_cookie_alg_t *func)
-{
-	if (!cc_algs || !func) {
-		return NULL;
-	}
-
-	const struct kr_clnt_cookie_alg_descr *aux_ptr = cc_algs;
-	while (aux_ptr && aux_ptr->func) {
-		assert(aux_ptr->name);
-		if (aux_ptr->func == func) {
-			return aux_ptr->name;
-		}
-		++aux_ptr;
-	}
-
-	return NULL;
-}
-
-int kr_address_bytes(const void *sockaddr, const uint8_t **addr, size_t *len)
-{
-	if (!sockaddr || !addr || !len) {
-		return kr_error(EINVAL);
-	}
-
-	int addr_family = ((struct sockaddr *) sockaddr)->sa_family;
-
-	switch (addr_family) {
-	case AF_INET:
-		*addr = (uint8_t *) &((struct sockaddr_in *) sockaddr)->sin_addr;
-		*len = 4;
-		break;
-	case AF_INET6:
-		*addr = (uint8_t *) &((struct sockaddr_in6 *) sockaddr)->sin6_addr;
-		*len = 16;
-		break;
-	default:
-		*addr = NULL;
-		*len = 0;
-		addr_family = AF_UNSPEC;
-		return kr_error(EINVAL);
-		break;
-	}
-
-	return kr_ok();
-}
-
-int kr_clnt_cookie_alg_fnv64(const struct kr_clnt_cookie_input *input,
-                             uint8_t cc_out[KNOT_OPT_COOKIE_CLNT])
+/**
+ * Compute client cookie using FNV-64.
+ * @note At least one of the arguments must be non-null.
+ * @param input  Input parameters.
+ * @param cc_out Buffer for computed client cookie.
+ * @return kr_ok() on success, error code else.
+ */
+static int kr_clnt_cookie_alg_fnv64(const struct kr_clnt_cookie_input *input,
+                                    uint8_t cc_out[KNOT_OPT_COOKIE_CLNT])
 {
 	if (!input || !cc_out) {
 		return kr_error(EINVAL);
@@ -145,8 +80,15 @@ int kr_clnt_cookie_alg_fnv64(const struct kr_clnt_cookie_input *input,
 	return kr_ok();
 }
 
-int kr_clnt_cookie_alg_hmac_sha256_64(const struct kr_clnt_cookie_input *input,
-                                      uint8_t cc_out[KNOT_OPT_COOKIE_CLNT])
+/**
+ * Compute client cookie using HMAC_SHA256-64.
+ * @note At least one of the arguments must be non-null.
+ * @param input  Input parameters.
+ * @param cc_out Buffer for computed client cookie.
+ * @return kr_ok() on success, error code else.
+ */
+static int kr_clnt_cookie_alg_hmac_sha256_64(const struct kr_clnt_cookie_input *input,
+                                             uint8_t cc_out[KNOT_OPT_COOKIE_CLNT])
 {
 	if (!input || !cc_out) {
 		return kr_error(EINVAL);
@@ -216,3 +158,79 @@ fail:
 	HMAC_CTX_cleanup(&ctx);
 	return ret;
 }
+
+const struct kr_clnt_cookie_alg_descr kr_clnt_cookie_algs[] = {
+	{ "FNV-64",         kr_clnt_cookie_alg_fnv64 },
+	{ "HMAC-SHA256-64", kr_clnt_cookie_alg_hmac_sha256_64 },
+	{ NULL, NULL }
+};
+
+const struct kr_clnt_cookie_alg_descr *kr_clnt_cookie_alg(const struct kr_clnt_cookie_alg_descr cc_algs[],
+                                                          const char *name)
+{
+	if (!cc_algs || !name) {
+		return NULL;
+	}
+
+	const struct kr_clnt_cookie_alg_descr *aux_ptr = cc_algs;
+	while (aux_ptr && aux_ptr->func) {
+		assert(aux_ptr->name);
+		if (strcmp(aux_ptr->name, name) == 0) {
+			return aux_ptr;
+		}
+		++aux_ptr;
+	}
+
+	return NULL;
+}
+
+int kr_address_bytes(const void *sockaddr, const uint8_t **addr, size_t *len)
+{
+	if (!sockaddr || !addr || !len) {
+		return kr_error(EINVAL);
+	}
+
+	int addr_family = ((struct sockaddr *) sockaddr)->sa_family;
+
+	switch (addr_family) {
+	case AF_INET:
+		*addr = (uint8_t *) &((struct sockaddr_in *) sockaddr)->sin_addr;
+		*len = 4;
+		break;
+	case AF_INET6:
+		*addr = (uint8_t *) &((struct sockaddr_in6 *) sockaddr)->sin6_addr;
+		*len = 16;
+		break;
+	default:
+		*addr = NULL;
+		*len = 0;
+		addr_family = AF_UNSPEC;
+		return kr_error(EINVAL);
+		break;
+	}
+
+	return kr_ok();
+}
+
+int kr_clnt_cookie_check(const uint8_t cc[KNOT_OPT_COOKIE_CLNT],
+                         const struct kr_clnt_cookie_input *input,
+                         const struct kr_clnt_cookie_alg_descr *cc_alg)
+{
+	if (!cc || !input || !cc_alg || !cc_alg->func) {
+		return kr_error(EINVAL);
+	}
+
+	uint8_t generated_cc[KNOT_OPT_COOKIE_CLNT] = {0, };
+
+	int ret = cc_alg->func(input, generated_cc);
+	if (ret != kr_ok()) {
+		return ret;
+	}
+
+	ret = memcmp(cc, generated_cc, KNOT_OPT_COOKIE_CLNT);
+	if (ret == 0) {
+		return kr_ok();
+	}
+
+	return kr_error(EINVAL);
+}
diff --git a/lib/cookies/algorithm.h b/lib/cookies/alg_clnt.h
similarity index 54%
rename from lib/cookies/algorithm.h
rename to lib/cookies/alg_clnt.h
index e3fc53138..97cf5d174 100644
--- a/lib/cookies/algorithm.h
+++ b/lib/cookies/alg_clnt.h
@@ -28,17 +28,22 @@ struct kr_clnt_cookie_input {
 	const void *clnt_sockaddr; /**< Client (local) socket address. */
 	const void *srvr_sockaddr; /**< Server (remote) socket address. */
 	const uint8_t *secret_data; /**< Client secret data. */
-	size_t secret_len;
+	size_t secret_len; /**< Secret data length. */
 };
 
-/** Client cookie algorithm type. */
+/**
+ * @brief Client cookie generator function type.
+ * @param input Data which to generate the cookie from.
+ * @param cc_out Buffer to write the resulting client cookie data into.
+ * @return kr_ok() or error code
+ */
 typedef int (clnt_cookie_alg_t)(const struct kr_clnt_cookie_input *input,
-                                uint8_t *);
+                                uint8_t *cc_out);
 
 /** Holds description of client cookie hashing algorithms. */
 struct kr_clnt_cookie_alg_descr {
-	clnt_cookie_alg_t *func; /**< Pointer to has function. */
 	const char *name; /**< Hash function name. */
+	clnt_cookie_alg_t *func; /**< Pointer to hash function. */
 };
 
 /**
@@ -50,27 +55,17 @@ KR_EXPORT
 extern const struct kr_clnt_cookie_alg_descr kr_clnt_cookie_algs[];
 
 /**
- * @brief Return pointer to client cookie hash function with given name.
+ * @brief Return pointer to client cookie algorithm with given name.
  * @param cc_algs List of available algorithms.
  * @param name    Algorithm name.
- * @return pointer to function or NULL if not found.
- */
-KR_EXPORT
-clnt_cookie_alg_t *kr_clnt_cookie_alg_func(const struct kr_clnt_cookie_alg_descr cc_algs[],
-                                           const char *name);
-
-/**
- * @brief Return name of given client cookie hash function.
- * @param cc_algs List of available algorithms.
- * @param func    Sought algorithm function.
- * @return pointer to string or NULL if not found.
+ * @return pointer to algorithm or NULL if not found.
  */
 KR_EXPORT
-const char *kr_clnt_cookie_alg_name(const struct kr_clnt_cookie_alg_descr cc_algs[],
-                                    clnt_cookie_alg_t *func);
+const struct kr_clnt_cookie_alg_descr *kr_clnt_cookie_alg(const struct kr_clnt_cookie_alg_descr cc_algs[],
+                                                          const char *name);
 
 /**
- * Get pointers to IP address bytes.
+ * @brief Get pointers to IP address bytes.
  * @param sockaddr socket address
  * @param addr pointer to address
  * @param len address length
@@ -79,23 +74,14 @@ const char *kr_clnt_cookie_alg_name(const struct kr_clnt_cookie_alg_descr cc_alg
 int kr_address_bytes(const void *sockaddr, const uint8_t **addr, size_t *len);
 
 /**
- * Compute client cookie using FNV-64.
- * @note At least one of the arguments must be non-null.
- * @param input  Input parameters.
- * @param cc_out Buffer for computed client cookie.
- * @return kr_ok() on success, error code else.
- */
-KR_EXPORT
-int kr_clnt_cookie_alg_fnv64(const struct kr_clnt_cookie_input *input,
-                             uint8_t cc_out[KNOT_OPT_COOKIE_CLNT]);
-
-/**
- * Compute client cookie using HMAC_SHA256-64.
- * @note At least one of the arguments must be non-null.
- * @param input  Input parameters.
- * @param cc_out Buffer for computed client cookie.
- * @return kr_ok() on success, error code else.
+ * @brief Check whether supplied client cookie was generated from given client
+ * secret and address.
+ * @param cc     Client cookie that should be checked.
+ * @param input  Input cookie algorithm parameters.
+ * @param cc_alg Client cookie algorithm.
+ * @return kr_ok() or error code
  */
 KR_EXPORT
-int kr_clnt_cookie_alg_hmac_sha256_64(const struct kr_clnt_cookie_input *input,
-                                      uint8_t cc_buf[KNOT_OPT_COOKIE_CLNT]);
+int kr_clnt_cookie_check(const uint8_t cc[KNOT_OPT_COOKIE_CLNT],
+                         const struct kr_clnt_cookie_input *input,
+                         const struct kr_clnt_cookie_alg_descr *cc_alg);
diff --git a/lib/cookies/alg_srvr.c b/lib/cookies/alg_srvr.c
new file mode 100644
index 000000000..884dbb174
--- /dev/null
+++ b/lib/cookies/alg_srvr.c
@@ -0,0 +1,355 @@
+/*  Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    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 <arpa/inet.h> /* ntohl(), ... */
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#include "contrib/fnv/fnv.h"
+#include "lib/cookies/alg_clnt.h" /* kr_address_bytes() */
+#include "lib/cookies/alg_srvr.h"
+
+/**
+ * @brief Server cookie contains only hash value.
+ * @note DNS Cookies -- Appendix B.1
+ */
+static int srvr_cookie_parse_simple(const uint8_t *cookie_data, uint16_t data_len,
+                                    struct kr_srvr_cookie_inbound *inbound)
+{
+	if (!cookie_data || !inbound) {
+		return kr_error(EINVAL);
+	}
+
+	const uint8_t *cc = NULL, *sc = NULL;
+        uint16_t cc_len = 0, sc_len = 0;
+	int ret = knot_edns_opt_cookie_parse(cookie_data, data_len,
+                                             &cc, &cc_len, &sc, &sc_len);
+	if (ret != KNOT_EOK || !sc) {
+		kr_error(EINVAL); /* Server cookie missing. */
+	}
+	assert(cc_len == KNOT_OPT_COOKIE_CLNT);
+
+	//memset(inbound, 0, sizeof(*inbound));
+	inbound->clnt_cookie = cc;
+	inbound->hash_data = sc; /* Entire server cookie contains data. */
+	inbound->hash_len = sc_len;
+
+	return kr_ok();
+}
+
+/**
+ * @brief Server cookie contains also additional values.
+ * @note DNS Cookies -- Appendix B.2
+ */
+static int srvr_cookie_parse(const uint8_t *cookie_data, uint16_t data_len,
+                             struct kr_srvr_cookie_inbound *inbound)
+{
+	if (!cookie_data || !inbound) {
+		return kr_error(EINVAL);
+	}
+
+	const uint8_t *cc = NULL, *sc = NULL;
+        uint16_t cc_len = 0, sc_len = 0;
+	int ret = knot_edns_opt_cookie_parse(cookie_data, data_len,
+                                             &cc, &cc_len, &sc, &sc_len);
+	if (ret != KNOT_EOK || !sc) {
+		kr_error(EINVAL); /* Server cookie missing. */
+	}
+	assert(cc_len == KNOT_OPT_COOKIE_CLNT);
+
+	if (sc_len <= (2 * sizeof(uint32_t))) { /* nonce + time */
+		return kr_error(EINVAL);
+	}
+
+	uint32_t aux;
+
+	inbound->clnt_cookie = cc;
+	memcpy(&aux, sc, sizeof(aux));
+	inbound->nonce = ntohl(aux);
+	memcpy(&aux, sc + sizeof(aux), sizeof(aux));
+	inbound->time = ntohl(aux);
+	inbound->hash_data = sc + (2 * sizeof(aux));
+	inbound->hash_len = sc_len - (2 * sizeof(aux));
+
+	return kr_ok();
+}
+
+#define SRVR_FNV64_SIMPLE_HASH_SIZE 8
+
+/**
+ * @brief Compute server cookie using FNV-64 (hash only).
+ * @note Server cookie = FNV-64( client IP | client cookie | server secret )
+ */
+static int kr_srvr_cookie_alg_fnv64_simple(const struct kr_srvr_cookie_input *input,
+                                           uint8_t sc_out[KNOT_OPT_COOKIE_SRVR_MAX],
+                                           size_t *sc_size)
+{
+	if (!input || !sc_out ||
+	    !sc_size || (*sc_size < SRVR_FNV64_SIMPLE_HASH_SIZE)) {
+		return kr_error(EINVAL);
+	}
+
+	if (!input->clnt_cookie ||
+	    !input->srvr_data.secret_data || !input->srvr_data.secret_len) {
+		return kr_error(EINVAL);
+	}
+
+	const uint8_t *addr = NULL;
+	size_t alen = 0; /* Address length. */
+
+	Fnv64_t hash_val = FNV1A_64_INIT;
+
+	if (kr_ok() == kr_address_bytes(input->srvr_data.clnt_sockaddr, &addr,
+	                                &alen)) {
+		assert(addr && alen);
+		hash_val = fnv_64a_buf((void *) addr, alen, hash_val);
+	}
+
+	hash_val = fnv_64a_buf((void *) input->clnt_cookie,
+	                       KNOT_OPT_COOKIE_CLNT, hash_val);
+
+	hash_val = fnv_64a_buf((void *) input->srvr_data.secret_data,
+	                       input->srvr_data.secret_len, hash_val);
+
+	memcpy(sc_out, &hash_val, sizeof(hash_val));
+	*sc_size = sizeof(hash_val);
+	assert(SRVR_FNV64_SIMPLE_HASH_SIZE == *sc_size);
+
+	return kr_ok();
+}
+
+#define SRVR_FNV64_SIZE 16
+
+/**
+ * @brief Compute server cookie using FNV-64.
+ * @note Server cookie = nonce | time | FNV-64( client IP | nonce| time | client cookie | server secret )
+ */
+static int kr_srvr_cookie_alg_fnv64(const struct kr_srvr_cookie_input *input,
+                                    uint8_t sc_out[KNOT_OPT_COOKIE_SRVR_MAX],
+                                    size_t *sc_size)
+{
+	if (!input || !sc_out ||
+	    !sc_size || (*sc_size < SRVR_FNV64_SIMPLE_HASH_SIZE)) {
+		return kr_error(EINVAL);
+	}
+
+	if (!input->clnt_cookie ||
+	    !input->srvr_data.secret_data || !input->srvr_data.secret_len) {
+		return kr_error(EINVAL);
+	}
+
+	const uint8_t *addr = NULL;
+	size_t alen = 0; /* Address length. */
+
+	Fnv64_t hash_val = FNV1A_64_INIT;
+
+	if (input->srvr_data.clnt_sockaddr) {
+		if (kr_ok() == kr_address_bytes(input->srvr_data.clnt_sockaddr,
+		                                &addr, &alen)) {
+			assert(addr && alen);
+			hash_val = fnv_64a_buf((void *) addr, alen, hash_val);
+		}
+	}
+
+	hash_val = fnv_64a_buf((void *) &input->nonce, sizeof(input->nonce),
+	                       hash_val);
+
+	hash_val = fnv_64a_buf((void *) &input->time, sizeof(input->time),
+	                       hash_val);
+
+	hash_val = fnv_64a_buf((void *) input->clnt_cookie,
+	                       KNOT_OPT_COOKIE_CLNT, hash_val);
+
+	hash_val = fnv_64a_buf((void *) input->srvr_data.secret_data,
+	                       input->srvr_data.secret_len, hash_val);
+
+	uint32_t aux = htonl(input->nonce);
+	memcpy(sc_out, &aux, sizeof(aux));
+	aux = htonl(input->time);
+	memcpy(sc_out + sizeof(aux), &aux, sizeof(aux));
+
+	memcpy(sc_out + (2 * sizeof(aux)), &hash_val, sizeof(hash_val));
+	*sc_size = (2 * sizeof(aux)) + sizeof(hash_val);
+	assert(SRVR_FNV64_SIZE == *sc_size);
+
+	return kr_ok();
+}
+
+#define SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE 8
+
+/**
+ * @brief Compute server cookie using HMAC-SHA256-64 (hash only).
+ * @note Server cookie = HMAC-SHA256-64( server secret, client cookie | client IP )
+ */
+static int kr_srvr_cookie_alg_hmac_sha256_64_simple(const struct kr_srvr_cookie_input *input,
+                                                    uint8_t sc_out[KNOT_OPT_COOKIE_SRVR_MAX],
+                                                    size_t *sc_size)
+{
+	if (!input || !sc_out ||
+	    !sc_size || (*sc_size < SRVR_FNV64_SIMPLE_HASH_SIZE)) {
+		return kr_error(EINVAL);
+	}
+
+	if (!input->clnt_cookie ||
+	    !input->srvr_data.secret_data || !input->srvr_data.secret_len) {
+		return kr_error(EINVAL);
+	}
+
+	const uint8_t *addr = NULL;
+	size_t alen = 0; /* Address length. */
+
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	unsigned int digest_len = SHA256_DIGEST_LENGTH;
+
+	HMAC_CTX ctx;
+	HMAC_CTX_init(&ctx);
+
+	int ret = HMAC_Init_ex(&ctx, input->srvr_data.secret_data,
+	                       input->srvr_data.secret_len,
+	                       EVP_sha256(), NULL);
+	if (ret != 1) {
+		ret = kr_error(EINVAL);
+		goto fail;
+	}
+
+	ret = HMAC_Update(&ctx, input->clnt_cookie, KNOT_OPT_COOKIE_CLNT);
+	if (ret != 1) {
+		ret = kr_error(EINVAL);
+		goto fail;
+	}
+
+	if (input->srvr_data.clnt_sockaddr) {
+		if (kr_ok() == kr_address_bytes(input->srvr_data.clnt_sockaddr,
+		                                &addr, &alen)) {
+			assert(addr && alen);
+			ret = HMAC_Update(&ctx, addr, alen);
+			if (ret != 1) {
+				ret = kr_error(EINVAL);
+				goto fail;
+			}
+		}
+	}
+
+	if (1 != HMAC_Final(&ctx, digest, &digest_len)) {
+		ret = kr_error(EINVAL);
+		goto fail;
+	}
+
+	assert(SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE <= SHA256_DIGEST_LENGTH);
+
+	memcpy(sc_out, digest, SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE);
+	*sc_size = SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE;
+
+	ret = kr_ok();
+
+fail:
+	HMAC_CTX_cleanup(&ctx);
+	return ret;
+}
+
+#define SRVR_HMAC_SHA256_64_SIZE 16
+
+/**
+ * @brief Compute server cookie using HMAC-SHA256-64).
+ * @note Server cookie = nonce | time | HMAC-SHA256-64( server secret, client cookie | nonce| time | client IP )
+ */
+static int kr_srvr_cookie_alg_hmac_sha256_64(const struct kr_srvr_cookie_input *input,
+                                             uint8_t sc_out[KNOT_OPT_COOKIE_SRVR_MAX],
+                                             size_t *sc_size)
+{
+	if (!input || !sc_out ||
+	    !sc_size || (*sc_size < SRVR_FNV64_SIMPLE_HASH_SIZE)) {
+		return kr_error(EINVAL);
+	}
+
+	if (!input->clnt_cookie ||
+	    !input->srvr_data.secret_data || !input->srvr_data.secret_len) {
+		return kr_error(EINVAL);
+	}
+
+	const uint8_t *addr = NULL;
+	size_t alen = 0; /* Address length. */
+
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	unsigned int digest_len = SHA256_DIGEST_LENGTH;
+
+	HMAC_CTX ctx;
+	HMAC_CTX_init(&ctx);
+
+	int ret = HMAC_Init_ex(&ctx, input->srvr_data.secret_data,
+	                       input->srvr_data.secret_len,
+	                       EVP_sha256(), NULL);
+	if (ret != 1) {
+		ret = kr_error(EINVAL);
+		goto fail;
+	}
+
+	ret = HMAC_Update(&ctx, input->clnt_cookie, KNOT_OPT_COOKIE_CLNT);
+	if (ret != 1) {
+		ret = kr_error(EINVAL);
+		goto fail;
+	}
+
+	ret = HMAC_Update(&ctx, (void *) &input->nonce, sizeof(input->nonce));
+	if (ret != 1) {
+		ret = kr_error(EINVAL);
+		goto fail;
+	}
+
+	ret = HMAC_Update(&ctx, (void *) &input->time, sizeof(input->time));
+	if (ret != 1) {
+		ret = kr_error(EINVAL);
+		goto fail;
+	}
+
+	if (input->srvr_data.clnt_sockaddr) {
+		if (kr_ok() == kr_address_bytes(input->srvr_data.clnt_sockaddr,
+		                                &addr, &alen)) {
+			assert(addr && alen);
+			ret = HMAC_Update(&ctx, addr, alen);
+			if (ret != 1) {
+				ret = kr_error(EINVAL);
+				goto fail;
+			}
+		}
+	}
+
+	if (1 != HMAC_Final(&ctx, digest, &digest_len)) {
+		ret = kr_error(EINVAL);
+		goto fail;
+	}
+
+	uint32_t aux = htonl(input->nonce);
+	memcpy(sc_out, &aux, sizeof(aux));
+	aux = htonl(input->time);
+	memcpy(sc_out + sizeof(aux), &aux, sizeof(aux));
+
+	assert(SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE <= SHA256_DIGEST_LENGTH);
+
+	memcpy(sc_out + (2 * sizeof(aux)), digest,
+	       SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE);
+	*sc_size = (2 * sizeof(aux)) + SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE;
+	assert(SRVR_HMAC_SHA256_64_SIZE == *sc_size);
+
+	ret = kr_ok();
+
+fail:
+	HMAC_CTX_cleanup(&ctx);
+	return ret;
+}
diff --git a/lib/cookies/alg_srvr.h b/lib/cookies/alg_srvr.h
new file mode 100644
index 000000000..64e425c93
--- /dev/null
+++ b/lib/cookies/alg_srvr.h
@@ -0,0 +1,92 @@
+/*  Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    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 <libknot/rrtype/opt_cookie.h>
+
+#include "lib/defines.h"
+
+/** Inbound server cookie checking context. */
+struct kr_srvr_cookie_check_ctx {
+	const void *clnt_sockaddr; /**< Client (remote) socket address. */
+	const uint8_t *secret_data; /**< Server secret data. */
+	size_t secret_len; /**< Server secret data length. */
+};
+
+/** Inbound server cookie content structure. */
+struct kr_srvr_cookie_inbound {
+	const uint8_t *clnt_cookie; /**< Client cookie, `KNOT_OPT_COOKIE_CLNT` bytes long. */
+	uint32_t nonce; /**< Some value. */
+	uint32_t time; /**< Time stamp. */
+	const uint8_t *hash_data; /**< Hash data. */
+	uint16_t hash_len; /**< Hash data length. */
+};
+
+/** Server cookie creation context. */
+struct kr_srvr_cookie_input {
+	const uint8_t *clnt_cookie; /**< Client cookie, must be `KNOT_OPT_COOKIE_CLNT` bytes long. */
+	uint32_t nonce; /**< Some generated value. */
+	uint32_t time; /**< Cookie time stamp. */
+	struct kr_srvr_cookie_check_ctx srvr_data; /**< Data known to the server. */
+};
+
+/**
+ * @brief Server cookie parser function type.
+ * @param cookie_data Entire cookie option data (without option header).
+ * @param data_len    Cookie data length.
+ * @param inbound     Inbound cookie structure to be set.
+ * @return kr_ok() or error code.
+ */
+typedef int (srvr_cookie_parse_t)(const uint8_t *cookie_data, uint16_t data_len,
+                                  struct kr_srvr_cookie_inbound *inbound);
+/**
+ * @brief Server cookie generator function type.
+ * @param input   Data which to generate the cookie from.
+ * @param sc_out  Buffer to write the resulting client cookie data into.
+ * @param sc_size On input must contain size of the buffer, on successful return contains size of actual written data.
+ * @return kr_ok() or error code
+ */
+typedef int (srvr_cookie_gen_t)(const struct kr_srvr_cookie_input *input,
+                                uint8_t *sc_out, size_t *sc_size);
+
+/** Holds description of server cookie hashing algorithms. */
+struct kr_srvr_cookie_alg_descr {
+	const char *name; /** Server cookie algorithm name. */
+	const uint16_t srvr_cookie_size; /**< Size of the generated server cookie. */
+	const srvr_cookie_parse_t *opt_parse_func; /**< Cookie option parser function. */
+	const srvr_cookie_gen_t *gen_func; /*< Cookie generator function. */
+};
+
+/**
+ * List of available server cookie algorithms.
+ *
+ * Last element contains all null entries.
+ */
+KR_EXPORT
+extern const struct kr_srvr_cookie_alg_descr kr_srvr_cookie_algs[];
+
+/**
+ * @brief Check whether supplied client and server cookie match.
+ * @param cookie_opt Entire cookie option, must contain server cookie.
+ * @param check_ctx  Data known to the server needed for cookie validation.
+ * @param sc_alg     Server cookie algorithm.
+ * @return kr_ok() if check OK, error code else.
+ */
+KR_EXPORT
+int kr_srvr_cookie_check(const uint8_t *cookie_opt,
+                         const struct kr_srvr_cookie_check_ctx *check_ctx,
+                         const struct kr_srvr_cookie_alg_descr *sc_alg);
diff --git a/lib/cookies/control.c b/lib/cookies/control.c
index 778c51f27..9afd81368 100644
--- a/lib/cookies/control.c
+++ b/lib/cookies/control.c
@@ -152,8 +152,8 @@ int kr_request_put_cookie(const struct kr_cookie_ctx *cntrl,
 		.secret_len = cntrl->current_cs->size
 	};
 	uint8_t cc[KNOT_OPT_COOKIE_CLNT];
-	assert(cntrl->cc_alg_func);
-	int ret = cntrl->cc_alg_func(&input, cc);
+	assert(cntrl->cc_alg && cntrl->cc_alg->func);
+	int ret = cntrl->cc_alg->func(&input, cc);
 	if (ret != kr_ok()) {
 		return ret;
 	}
diff --git a/lib/cookies/control.h b/lib/cookies/control.h
index 98727d99e..99c43db34 100644
--- a/lib/cookies/control.h
+++ b/lib/cookies/control.h
@@ -20,7 +20,7 @@
 #include <libknot/rrtype/opt_cookie.h>
 #include <stdbool.h>
 
-#include "lib/cookies/algorithm.h"
+#include "lib/cookies/alg_clnt.h"
 #include "lib/cache.h"
 #include "lib/defines.h"
 
@@ -46,7 +46,7 @@ struct kr_cookie_ctx {
 
 	uint32_t cache_ttl; /**< TTL used when caching cookies */
 
-	clnt_cookie_alg_t *cc_alg_func; /**< Client cookie hash computation callback. */
+	const struct kr_clnt_cookie_alg_descr *cc_alg; /**< Client cookie algorithm. */
 };
 
 /** Global cookie control context. */
diff --git a/lib/layer/cookiemonster.c b/lib/layer/cookiemonster.c
index 4bed370b1..89c34732a 100644
--- a/lib/layer/cookiemonster.c
+++ b/lib/layer/cookiemonster.c
@@ -25,7 +25,7 @@
 #include <string.h>
 
 #include "daemon/engine.h"
-#include "lib/cookies/algorithm.h"
+#include "lib/cookies/alg_clnt.h"
 #include "lib/cookies/cache.h"
 #include "lib/cookies/control.h"
 #include "lib/module.h"
@@ -38,37 +38,6 @@
 /* TODO -- The context must store sent cookies and server addresses in order
  * to make the process more reliable. */
 
-/**
- * Check whether supplied client cookie was generated from given client secret
- * and address.
- * @param cc client cookie
- * @param input input cookie algorithm parameters
- * @param cc_alg_func function generating client cookie
- * @return kr_ok() or error code
- */
-static int check_client_cookie(const uint8_t cc[KNOT_OPT_COOKIE_CLNT],
-                               const struct kr_clnt_cookie_input *input,
-                               clnt_cookie_alg_t *cc_alg_func)
-{
-	if (!cc || !input || !cc_alg_func) {
-		return kr_error(EINVAL);
-	}
-
-	uint8_t generated_cc[KNOT_OPT_COOKIE_CLNT] = {0, };
-
-	int ret = cc_alg_func(input, generated_cc);
-	if (ret != kr_ok()) {
-		return ret;
-	}
-
-	ret = memcmp(cc, generated_cc, KNOT_OPT_COOKIE_CLNT);
-	if (ret == 0) {
-		return kr_ok();
-	}
-
-	return kr_error(EINVAL);
-}
-
 /**
  * Obtain address from query/response context if if can be obtained.
  * @param qry query context
@@ -92,15 +61,15 @@ static const struct sockaddr *passed_server_sockaddr(const struct kr_query *qry)
  * @param nsrep name server reputation context
  * @param cc client cookie data
  * @param csecr client secret
- * @param cc_alg_func function generating client cookie
+ * @param cc_alg client cookie algorithm
  * @return pointer to address if a matching found, NULL if none matches
  */
 static const struct sockaddr *guess_server_addr(const struct kr_nsrep *nsrep,
                                                 const uint8_t cc[KNOT_OPT_COOKIE_CLNT],
                                                 const struct kr_cookie_secret *csecr,
-                                                clnt_cookie_alg_t *cc_alg_func)
+                                                const struct kr_clnt_cookie_alg_descr *cc_alg)
 {
-	assert(nsrep && cc && csecr && cc_alg_func);
+	assert(nsrep && cc && csecr && cc_alg);
 
 	const struct sockaddr *sockaddr = NULL;
 
@@ -118,7 +87,7 @@ static const struct sockaddr *guess_server_addr(const struct kr_nsrep *nsrep,
 		}
 
 		input.srvr_sockaddr = &nsrep->addr[i];
-		int ret = check_client_cookie(cc, &input, cc_alg_func);
+		int ret = kr_clnt_cookie_check(cc, &input, cc_alg);
 		if (ret == kr_ok()) {
 			sockaddr = (struct sockaddr *) &nsrep->addr[i];
 			break;
@@ -155,13 +124,12 @@ static int srvr_sockaddr_cc_check(const struct sockaddr **sockaddr,
 			.secret_data = cntrl->current_cs->data,
 			.secret_len = cntrl->current_cs->size
 		};
-		int ret = check_client_cookie(cc, &input, cntrl->cc_alg_func);
+		int ret = kr_clnt_cookie_check(cc, &input, cntrl->cc_alg);
 		bool have_current = (ret == kr_ok());
 		if ((ret != kr_ok()) && cntrl->recent_cs) {
 			input.secret_data = cntrl->recent_cs->data;
 			input.secret_len = cntrl->recent_cs->size;
-			ret = check_client_cookie(cc, &input,
-			                          cntrl->cc_alg_func);
+			ret = kr_clnt_cookie_check(cc, &input, cntrl->cc_alg);
 		}
 		if (ret == kr_ok()) {
 			*sockaddr = tmp_sockaddr;
@@ -180,12 +148,12 @@ static int srvr_sockaddr_cc_check(const struct sockaddr **sockaddr,
 	/* Abusing name server reputation mechanism to guess IP addresses. */
 	const struct kr_nsrep *ns = &qry->ns;
 	tmp_sockaddr = guess_server_addr(ns, cc, cntrl->current_cs,
-	                                 cntrl->cc_alg_func);
+	                                 cntrl->cc_alg);
 	bool have_current = (tmp_sockaddr != NULL);
 	if (!tmp_sockaddr && cntrl->recent_cs) {
 		/* Try recent client secret to check obtained cookie. */
 		tmp_sockaddr = guess_server_addr(ns, cc, cntrl->recent_cs,
-		                                 cntrl->cc_alg_func);
+		                                 cntrl->cc_alg);
 	}
 	if (tmp_sockaddr) {
 		*sockaddr = tmp_sockaddr;
diff --git a/lib/lib.mk b/lib/lib.mk
index ac5e53827..33bdc7a3b 100644
--- a/lib/lib.mk
+++ b/lib/lib.mk
@@ -48,12 +48,14 @@ ifeq ($(HAS_libcrypto),yes)
 libkres_SOURCES += \
 	contrib/fnv/hash_64a.c \
 	lib/layer/cookiemonster.c \
-	lib/cookies/algorithm.c \
+	lib/cookies/alg_clnt.c \
+	lib/cookies/alg_srvr.c \
 	lib/cookies/cache.c    \
 	lib/cookies/control.c
 
 libkres_HEADERS += \
-	lib/cookies/algorithm.h \
+	lib/cookies/alg_clnt.h \
+	lib/cookies/alg_srvr.h \
 	lib/cookies/cache.h    \
 	lib/cookies/control.h
 
diff --git a/modules/cookiectl/cookiectl.c b/modules/cookiectl/cookiectl.c
index ce7fda084..156b9ea7e 100644
--- a/modules/cookiectl/cookiectl.c
+++ b/modules/cookiectl/cookiectl.c
@@ -21,7 +21,7 @@
 #include <string.h>
 
 #include "daemon/engine.h"
-#include "lib/cookies/algorithm.h"
+#include "lib/cookies/alg_clnt.h"
 #include "lib/cookies/control.h"
 #include "lib/layer.h"
 
@@ -133,12 +133,12 @@ static bool apply_client_hash_func(struct kr_cookie_ctx *cntrl,
                                    const JsonNode *node)
 {
 	if (node->tag == JSON_STRING) {
-		clnt_cookie_alg_t *cc_alg_func = kr_clnt_cookie_alg_func(kr_clnt_cookie_algs,
-		                                                         node->string_);
-		if (!cc_alg_func) {
+		const struct kr_clnt_cookie_alg_descr *cc_alg = kr_clnt_cookie_alg(kr_clnt_cookie_algs,
+		                                                                   node->string_);
+		if (!cc_alg) {
 			return false;
 		}
-		cntrl->cc_alg_func = cc_alg_func;
+		cntrl->cc_alg = cc_alg;
 		return true;
 	}
 
@@ -263,11 +263,9 @@ static char *cookiectl_config(void *env, struct kr_module *module, const char *a
 
 	read_secret(root_node, &kr_glob_cookie_ctx);
 
-	const char *name = kr_clnt_cookie_alg_name(kr_clnt_cookie_algs,
-	                                           kr_glob_cookie_ctx.cc_alg_func);
-	assert(name);
+	assert(kr_glob_cookie_ctx.cc_alg->name);
 	json_append_member(root_node, NAME_CLIENT_COOKIE_ALG,
-	                   json_mkstring(name));
+	                   json_mkstring(kr_glob_cookie_ctx.cc_alg->name));
 
 	read_available_cc_hashes(root_node, &kr_glob_cookie_ctx);
 
@@ -293,7 +291,7 @@ int cookiectl_init(struct kr_module *module)
 	kr_glob_cookie_ctx.enabled = false;
 	kr_glob_cookie_ctx.current_cs = &dflt_cs;
 	kr_glob_cookie_ctx.cache_ttl = DFLT_COOKIE_TTL;
-	kr_glob_cookie_ctx.cc_alg_func = kr_clnt_cookie_alg_fnv64;
+	kr_glob_cookie_ctx.cc_alg = kr_clnt_cookie_alg(kr_clnt_cookie_algs, "FNV-64");
 
 	module->data = NULL;
 
-- 
GitLab