diff --git a/Knot.files b/Knot.files
index 064df80faac19c7e884767cb8520df027a7d32c5..ae6df4dad4b69539fbcc5037d2259503196c8cd7 100644
--- a/Knot.files
+++ b/Knot.files
@@ -330,10 +330,8 @@ src/libknot/codes.h
 src/libknot/consts.h
 src/libknot/control/control.c
 src/libknot/control/control.h
-src/libknot/cookies/client.c
-src/libknot/cookies/client.h
-src/libknot/cookies/server.c
-src/libknot/cookies/server.h
+src/libknot/cookies.c
+src/libknot/cookies.h
 src/libknot/db/db.h
 src/libknot/db/db_lmdb.c
 src/libknot/db/db_lmdb.h
@@ -377,8 +375,6 @@ src/libknot/rrtype/nsec3.c
 src/libknot/rrtype/nsec3.h
 src/libknot/rrtype/nsec3param.c
 src/libknot/rrtype/nsec3param.h
-src/libknot/rrtype/opt-cookie.c
-src/libknot/rrtype/opt-cookie.h
 src/libknot/rrtype/opt.c
 src/libknot/rrtype/opt.h
 src/libknot/rrtype/rdname.h
@@ -493,9 +489,7 @@ tests/contrib/test_time.c
 tests/contrib/test_wire.c
 tests/contrib/test_wire_ctx.c
 tests/libknot/test_control.c
-tests/libknot/test_cookies-client.c
-tests/libknot/test_cookies-opt.c
-tests/libknot/test_cookies-server.c
+tests/libknot/test_cookies.c
 tests/libknot/test_db.c
 tests/libknot/test_descriptor.c
 tests/libknot/test_dname.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 49f4fc2ac09838e3377997e2fcdc33323a499942..7abb5861b5913bf604ed5c4b057136b1e15859fb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -101,8 +101,7 @@ nobase_libknot_la_HEADERS =			\
 	libknot/codes.h				\
 	libknot/consts.h			\
 	libknot/control/control.h		\
-	libknot/cookies/client.h		\
-	libknot/cookies/server.h		\
+	libknot/cookies.h			\
 	libknot/descriptor.h			\
 	libknot/dname.h				\
 	libknot/errcode.h			\
@@ -129,7 +128,6 @@ nobase_libknot_la_HEADERS =			\
 	libknot/rrtype/nsec3.h			\
 	libknot/rrtype/nsec3param.h		\
 	libknot/rrtype/opt.h			\
-	libknot/rrtype/opt-cookie.h		\
 	libknot/rrtype/rdname.h			\
 	libknot/rrtype/rrsig.h			\
 	libknot/rrtype/soa.h			\
@@ -147,8 +145,7 @@ nobase_libknot_la_HEADERS =			\
 libknot_la_SOURCES =				\
 	libknot/codes.c				\
 	libknot/control/control.c		\
-	libknot/cookies/client.c		\
-	libknot/cookies/server.c		\
+	libknot/cookies.c			\
 	libknot/descriptor.c			\
 	libknot/dname.c				\
 	libknot/error.c				\
@@ -168,7 +165,6 @@ libknot_la_SOURCES =				\
 	libknot/rrtype/nsec3.c			\
 	libknot/rrtype/nsec3param.c		\
 	libknot/rrtype/opt.c			\
-	libknot/rrtype/opt-cookie.c		\
 	libknot/rrtype/rrsig.c			\
 	libknot/rrtype/soa.c			\
 	libknot/rrtype/tsig.c			\
diff --git a/src/libknot/cookies.c b/src/libknot/cookies.c
new file mode 100644
index 0000000000000000000000000000000000000000..dd024d5c96843cb59c5284cbb62279bfc76a8326
--- /dev/null
+++ b/src/libknot/cookies.c
@@ -0,0 +1,130 @@
+/*  Copyright (C) 2017 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 <assert.h>
+#include <stdbool.h>
+
+#include "libknot/attribute.h"
+#include "libknot/cookies.h"
+#include "libknot/errcode.h"
+#include "contrib/string.h"
+#include "contrib/sockaddr.h"
+#include "contrib/openbsd/siphash.h"
+
+_public_
+int knot_edns_cookie_client_generate(knot_edns_cookie_t *out,
+                                     const knot_edns_cookie_params_t *params)
+{
+	if (out == NULL || params == NULL || params->client_addr == NULL ||
+	    params->server_addr == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	SIPHASH_CTX ctx;
+	assert(sizeof(params->secret) == sizeof(SIPHASH_KEY));
+	SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret);
+
+	size_t addr_len = 0;
+	void *addr = sockaddr_raw(params->client_addr, &addr_len);
+	SipHash24_Update(&ctx, addr, addr_len);
+
+	addr_len = 0;
+	addr = sockaddr_raw(params->server_addr, &addr_len);
+	SipHash24_Update(&ctx, addr, addr_len);
+
+	uint64_t hash = SipHash24_End(&ctx);
+	memcpy(out->data, &hash, sizeof(hash));
+	out->len = sizeof(hash);
+
+	return KNOT_EOK;
+}
+
+_public_
+int knot_edns_cookie_client_check(const knot_edns_cookie_t *cc,
+                                  const knot_edns_cookie_params_t *params)
+{
+	if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) {
+		return KNOT_EINVAL;
+	}
+
+	knot_edns_cookie_t ref;
+	int ret = knot_edns_cookie_client_generate(&ref, params);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	assert(ref.len == KNOT_EDNS_COOKIE_CLNT_SIZE);
+
+	ret = const_time_memcmp(cc->data, ref.data, KNOT_EDNS_COOKIE_CLNT_SIZE);
+	if (ret != 0) {
+		return KNOT_EINVAL;
+	}
+
+	return KNOT_EOK;
+}
+
+_public_
+int knot_edns_cookie_server_generate(knot_edns_cookie_t *out,
+                                     const knot_edns_cookie_t *cc,
+                                     const knot_edns_cookie_params_t *params)
+{
+	if (out == NULL || cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE ||
+	    params == NULL || params->client_addr == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	SIPHASH_CTX ctx;
+	assert(sizeof(params->secret) == sizeof(SIPHASH_KEY));
+	SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret);
+
+	size_t addr_len = 0;
+	void *addr = sockaddr_raw(params->client_addr, &addr_len);
+	SipHash24_Update(&ctx, addr, addr_len);
+
+	SipHash24_Update(&ctx, cc->data, cc->len);
+
+	uint64_t hash = SipHash24_End(&ctx);
+	memcpy(out->data, &hash, sizeof(hash));
+	out->len = sizeof(hash);
+
+	return KNOT_EOK;
+}
+
+_public_
+int knot_edns_cookie_server_check(const knot_edns_cookie_t *sc,
+                                  const knot_edns_cookie_t *cc,
+                                  const knot_edns_cookie_params_t *params)
+{
+	if (sc == NULL || cc == NULL || params == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	knot_edns_cookie_t ref;
+	int ret = knot_edns_cookie_server_generate(&ref, cc, params);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+
+	if (sc->len != ref.len) {
+		return KNOT_EINVAL;
+	}
+
+	ret = const_time_memcmp(sc->data, ref.data, sc->len);
+	if (ret != 0) {
+		return KNOT_EINVAL;
+	}
+
+	return KNOT_EOK;
+}
diff --git a/src/libknot/cookies.h b/src/libknot/cookies.h
new file mode 100644
index 0000000000000000000000000000000000000000..5bdfb68f727496501189979ac5ea30fcfd9e0aef
--- /dev/null
+++ b/src/libknot/cookies.h
@@ -0,0 +1,97 @@
+/*  Copyright (C) 2017 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/>.
+ */
+/*!
+* \file
+*
+* \brief DNS cookies processing.
+*
+* \addtogroup libknot
+* @{
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/socket.h>
+
+#include "libknot/rrtype/opt.h"
+
+#define KNOT_EDNS_COOKIE_SECRET_SIZE 16
+
+/*!
+ * \brief DNS Cookie parameters needed to compute the cookie value.
+ *
+ * \note Server address is not used for the server cookie check.
+ */
+typedef struct {
+	const struct sockaddr *client_addr; /*!< Client socket address. */
+	const struct sockaddr *server_addr; /*!< Server socket address. */
+	uint8_t secret[KNOT_EDNS_COOKIE_SECRET_SIZE]; /*!< Cookie secret data. */
+} knot_edns_cookie_params_t;
+
+/*!
+ * \brief Generate a client cookie using given parameters.
+ *
+ * \param out     Generated client cookie.
+ * \param params  Client cookie parameters.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int knot_edns_cookie_client_generate(knot_edns_cookie_t *out,
+                                     const knot_edns_cookie_params_t *params);
+
+/*!
+ * \brief Check whether client cookie was generated using given parameters.
+ *
+ * \param cc      Client cookie that should be checked.
+ * \param params  Client cookie parameters.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int knot_edns_cookie_client_check(const knot_edns_cookie_t *cc,
+                                  const knot_edns_cookie_params_t *params);
+
+/*!
+ * \brief Generate a server cookie using given parameters.
+ *
+ * \param out     Generated server cookie.
+ * \param cc      Client cookie parameter.
+ * \param params  Server cookie parameters.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int knot_edns_cookie_server_generate(knot_edns_cookie_t *out,
+                                     const knot_edns_cookie_t *cc,
+                                     const knot_edns_cookie_params_t *params);
+
+/*!
+ * \brief Check whether server cookie was generated using given parameters.
+ *
+ * \param sc      Server cookie that should be checked.
+ * \param cc      Client cookie parameter.
+ * \param params  Server cookie parameters.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int knot_edns_cookie_server_check(const knot_edns_cookie_t *sc,
+                                  const knot_edns_cookie_t *cc,
+                                  const knot_edns_cookie_params_t *params);
+
+/*! @} */
diff --git a/src/libknot/cookies/client.c b/src/libknot/cookies/client.c
deleted file mode 100644
index e0b347d3ba0bdf774bcadbe5462a99790f01f674..0000000000000000000000000000000000000000
--- a/src/libknot/cookies/client.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*  Copyright (C) 2017 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 <assert.h>
-#include <string.h>
-
-#include "libknot/attribute.h"
-#include "libknot/cookies/client.h"
-#include "libknot/errcode.h"
-#include "libknot/rrtype/opt-cookie.h"
-#include "contrib/string.h"
-#include "contrib/sockaddr.h"
-
-_public_
-bool knot_cc_input_is_valid(const struct knot_cc_input *input)
-{
-	/*
-	 * RFC7873 4.1 -- Client cookie should be generated from
-	 * client IP address, server IP address and a secret quantity.
-	 */
-
-	return input && (input->clnt_sockaddr || input->srvr_sockaddr);
-}
-
-static uint64_t generate_client_cookie(const struct knot_cc_input *input)
-{
-	SIPHASH_CTX ctx;
-	SipHash24_Init(&ctx, &input->secret);
-
-	if (input->clnt_sockaddr) {
-		size_t addr_len = 0;
-		void *addr = sockaddr_raw(input->clnt_sockaddr, &addr_len);
-		SipHash24_Update(&ctx, addr, addr_len);
-	}
-
-	if (input->srvr_sockaddr) {
-		size_t addr_len = 0;
-		void *addr = sockaddr_raw(input->srvr_sockaddr, &addr_len);
-		SipHash24_Update(&ctx, addr, addr_len);
-	}
-
-	return SipHash24_End(&ctx);
-}
-
-_public_
-int knot_cc_check(const uint8_t *cc, uint16_t cc_len,
-                  const struct knot_cc_input *input)
-{
-	if (!cc || cc_len != KNOT_OPT_COOKIE_CLNT || !knot_cc_input_is_valid(input)) {
-		return KNOT_EINVAL;
-	}
-
-	uint64_t generated_cc = generate_client_cookie(input);
-	assert(sizeof(generated_cc) == KNOT_OPT_COOKIE_CLNT);
-
-	int ret = const_time_memcmp(cc, &generated_cc, KNOT_OPT_COOKIE_CLNT);
-	if (ret != 0) {
-		return KNOT_EINVAL;
-	}
-
-	return KNOT_EOK;
-}
diff --git a/src/libknot/cookies/client.h b/src/libknot/cookies/client.h
deleted file mode 100644
index e345009f95ba74d1a7d56cfb06bfbaf157631de0..0000000000000000000000000000000000000000
--- a/src/libknot/cookies/client.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*  Copyright (C) 2017 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 <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "contrib/openbsd/siphash.h"
-
-/*!
- * \brief Input data needed to compute the client cookie value.
- */
-struct knot_cc_input {
-	const struct sockaddr *clnt_sockaddr; /*!< Client (local) socket address. */
-	const struct sockaddr *srvr_sockaddr; /*!< Server (remote) socket address. */
-	SIPHASH_KEY secret; /*!< Client secret data. */
-};
-
-/*!
- * \brief Check client cookie input data for basic sanity.
- *
- * \param input  Data which to generate the cookie from.
- *
- * \retval true if input contains at least one socket and secret data
- * \retval false if input is insufficient or NULL pointer passed
- */
-bool knot_cc_input_is_valid(const struct knot_cc_input *input);
-
-/*!
- * \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.
- * \param cc_len  Cookie buffer size.
- *
- * \retval non-zero size of written data on successful return
- * \retval 0 on error
- */
-typedef uint16_t (knot_cc_gen_t)(const struct knot_cc_input *input,
-                                 uint8_t *cc_out, uint16_t cc_len);
-
-/*!
- * \brief Holds description of the client cookie algorithm.
- */
-struct knot_cc_alg {
-	const uint16_t cc_size;  /*!< Cookie size the algorithm operates with. */
-	knot_cc_gen_t *gen_func; /*!< Cookie generator function. */
-};
-
-/*!
- * \brief Check whether client cookie \a cc was generated from given \a input.
- *
- * \param cc      Client cookie that should be checked.
- * \param cc_len  Client cookie size.
- * \param input   Client cookie input algorithm parameters.
- *
- * \retval KNOT_EOK
- * \retval KNOT_EINVAL
- */
-int knot_cc_check(const uint8_t *cc, uint16_t cc_len,
-                  const struct knot_cc_input *input);
diff --git a/src/libknot/cookies/server.c b/src/libknot/cookies/server.c
deleted file mode 100644
index 61dae82d353b9b7624e0dae286e47ddff527496d..0000000000000000000000000000000000000000
--- a/src/libknot/cookies/server.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*  Copyright (C) 2017 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 <assert.h>
-#include <string.h>
-
-#include "libknot/attribute.h"
-#include "libknot/cookies/server.h"
-#include "libknot/errcode.h"
-#include "libknot/rrtype/opt-cookie.h"
-#include "contrib/string.h"
-#include "contrib/sockaddr.h"
-
-#define SERVER_HASH_LEN 8
-
-_public_
-bool knot_sc_input_is_valid(const struct knot_sc_input *input)
-{
-	/*
-	 * RFC7873 4.2 -- Server cookie should be generated from request
-	 * source IP address, a secret quantity and request client cookie.
-	 */
-
-	return input && input->cc && input->cc_len > 0 && input->srvr_data;
-}
-
-_public_
-int knot_sc_parse(uint16_t nonce_len, const uint8_t *sc, uint16_t sc_len,
-                  struct knot_sc_content *content)
-{
-	if (!sc || !sc_len || !content) {
-		return KNOT_EINVAL;
-	}
-
-	if (nonce_len >= sc_len) {
-		return KNOT_EINVAL;
-	}
-
-	content->nonce = nonce_len ? sc : NULL;
-	content->nonce_len = nonce_len;
-	/* Rest of server cookie contains hash. */
-	content->hash = sc + nonce_len;
-	content->hash_len = sc_len - nonce_len;
-
-	return KNOT_EOK;
-}
-
-static uint64_t generate_server_cookie(const struct knot_sc_input *input)
-{
-	SIPHASH_CTX ctx;
-	SipHash24_Init(&ctx, &input->srvr_data->secret);
-
-	if (input->srvr_data->clnt_sockaddr) {
-		size_t addr_len = 0;
-		void *addr = sockaddr_raw(input->srvr_data->clnt_sockaddr, &addr_len);
-		if (addr) {
-			SipHash24_Update(&ctx, addr, addr_len);
-		}
-	}
-
-	if (input->nonce && input->nonce_len > 0) {
-		SipHash24_Update(&ctx, input->nonce, input->nonce_len);
-	}
-
-	if (input->cc && input->cc_len == KNOT_OPT_COOKIE_CLNT) {
-		SipHash24_Update(&ctx, input->cc, input->cc_len);
-	}
-
-	return SipHash24_End(&ctx);
-}
-
-_public_
-int knot_sc_check(uint16_t nonce_len, const struct knot_dns_cookies *cookies,
-                  const struct knot_sc_private *srvr_data)
-{
-	if (!cookies || !srvr_data) {
-		return KNOT_EINVAL;
-	}
-
-	if (!cookies->cc || !cookies->cc_len ||
-	    !cookies->sc || !cookies->sc_len) {
-		return KNOT_EINVAL;
-	}
-
-	if (!srvr_data->clnt_sockaddr) {
-		return KNOT_EINVAL;
-	}
-
-	if ((nonce_len + SERVER_HASH_LEN) > KNOT_OPT_COOKIE_SRVR_MAX) {
-		return KNOT_EINVAL;
-	}
-
-	if (cookies->sc_len != (nonce_len + SERVER_HASH_LEN)) {
-		return KNOT_EINVAL;
-	}
-
-	struct knot_sc_content content = { 0 };
-
-	/* Obtain data from received server cookie. */
-	int ret = knot_sc_parse(nonce_len, cookies->sc, cookies->sc_len, &content);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-	if (content.hash_len != SERVER_HASH_LEN) {
-		return KNOT_EINVAL;
-	}
-
-	struct knot_sc_input sc_input = {
-		.cc = cookies->cc,
-		.cc_len = cookies->cc_len,
-		.nonce = content.nonce,
-		.nonce_len = content.nonce_len,
-		.srvr_data = srvr_data
-	};
-
-	if (!knot_sc_input_is_valid(&sc_input)) {
-		return KNOT_EINVAL;
-	}
-
-	/* Generate a new hash. */
-	uint64_t generated_hash = generate_server_cookie(&sc_input);
-	assert(sizeof(generated_hash) == SERVER_HASH_LEN);
-
-	/* Compare hashes. */
-	ret = const_time_memcmp(content.hash, &generated_hash, SERVER_HASH_LEN);
-	if (ret != 0) {
-		return KNOT_EINVAL;
-	}
-
-	return KNOT_EOK;
-}
diff --git a/src/libknot/cookies/server.h b/src/libknot/cookies/server.h
deleted file mode 100644
index 825eaa76c0a281096d238b18b879f44818b865d6..0000000000000000000000000000000000000000
--- a/src/libknot/cookies/server.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*  Copyright (C) 2017 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 <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "contrib/openbsd/siphash.h"
-
-/*!
- * \brief Convenience structure holding both, server and client, cookies.
- */
-struct knot_dns_cookies {
-	const uint8_t *cc; /*!< Client cookie. */
-	uint16_t cc_len;   /*!< Client cookie size. */
-	const uint8_t *sc; /*!< Server cookie. */
-	uint16_t sc_len;   /*!< Server cookie size. */
-};
-
-/*!
- * \brief Private data known to the server.
- *
- * \note Contains data needed to check the inbound server cookie and to
- *       generate a new one.
- */
-struct knot_sc_private {
-	const struct sockaddr *clnt_sockaddr; /*!< Client (remote) socket address. */
-	SIPHASH_KEY secret; /*!< Server secret data. */
-};
-
-/*!
- * \brief Inbound server cookie content structure.
- *
- * \note These data are obtained from an incoming server cookie.
- */
-struct knot_sc_content {
-	const uint8_t *nonce; /*!< Some value prefixed to the hash. */
-	uint16_t nonce_len;   /*!< Nonce data length. */
-	const uint8_t *hash;  /*!< Hash data. */
-	uint16_t hash_len;    /*!< Hash data length. */
-};
-
-/*!
- * \brief Input data needed to compute the server cookie value.
- *
- * \note All these data are needed to generate a new server cookie hash.
- */
-struct knot_sc_input {
-	const uint8_t *cc;    /*!< Client cookie. */
-	uint16_t cc_len;      /*!< Client cookie size. */
-	const uint8_t *nonce; /*!< Some value prefixed before the hash. */
-	uint16_t nonce_len;   /*!< Nonce data length. */
-	const struct knot_sc_private *srvr_data; /*!< Private data known to the server. */
-};
-
-/*!
- * \brief Check server cookie input data for basic sanity.
- *
- * \param input  Data which to generate the cookie from.
- *
- * \retval true if input contains client cookie and server secret data
- * \retval false if input is insufficient or NULL pointer passed
- */
-bool knot_sc_input_is_valid(const struct knot_sc_input *input);
-
-/*!
- * \brief Reads a server cookie that contains \a nonce_len bytes of data
- *        prefixed before the actual hash.
- *
- * \see DNS Cookies, RFC 7873, Appendix B.1 and B.2
- *
- * \param nonce_len  Expected nonce data size.
- * \param sc         Server cookie.
- * \param sc_len     Server cookie length.
- * \param content    Server cookie content structure to be set.
- *
- * \retval KNOT_EOK
- * \retval KNOT_EINVAL
- */
-int knot_sc_parse(uint16_t nonce_len, const uint8_t *sc, uint16_t sc_len,
-                  struct knot_sc_content *content);
-
-/*!
- * \brief Hash generator function type.
- *
- * \note The function writes only the hash value. It does not write any nonce
- *       data prefixed before the actual hash value. Nonce data must be written
- *       by an external function into the server cookie.
- *
- * \param input     Data which to generate the cookie from.
- * \param hash_out  Buffer to write the resulting hash data into.
- * \param hash_len  Hash buffer size.
- *
- * \retval non-zero size of written data on successful return
- * \retval 0 on error
- */
-typedef uint16_t (knot_sc_hash_t)(const struct knot_sc_input *input,
-                                  uint8_t *hash_out, uint16_t hash_len);
-
-/*!
- * \brief Holds description of the server cookie algorithm.
- */
-struct knot_sc_alg {
-	const uint16_t hash_size;  /*!< Hash size the algorithm operates with. */
-	knot_sc_hash_t *hash_func; /*!< Cookie generator function. */
-};
-
-/*!
- * \brief Check whether supplied client and server cookies match.
- *
- * \param nonce_len  Expected nonce data size.
- * \param cookies    Cookie data.
- * \param srvr_data  Data known to the server needed for cookie validation.
- *
- * \retval KNOT_EOK
- * \retval KNOT_EINVAL
- */
-int knot_sc_check(uint16_t nonce_len, const struct knot_dns_cookies *cookies,
-                  const struct knot_sc_private *srvr_data);
diff --git a/src/libknot/rrtype/opt-cookie.c b/src/libknot/rrtype/opt-cookie.c
deleted file mode 100644
index d678dbb322a5504ae2cdc49dd1913521c157ebab..0000000000000000000000000000000000000000
--- a/src/libknot/rrtype/opt-cookie.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/*  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 <stdlib.h>
-
-#include "contrib/wire_ctx.h"
-#include "libknot/errcode.h"
-#include "libknot/rrtype/opt.h"
-#include "libknot/rrtype/opt-cookie.h"
-
-#define cookie_len_ok(clen) \
-	(((clen) == KNOT_OPT_COOKIE_CLNT) || \
-	 ((clen) >= (KNOT_OPT_COOKIE_CLNT + KNOT_OPT_COOKIE_SRVR_MIN) && \
-	  (clen) <= (KNOT_OPT_COOKIE_CLNT + KNOT_OPT_COOKIE_SRVR_MAX)))
-
-#define ccookie_len_ok(cclen) \
-	((cclen) == KNOT_OPT_COOKIE_CLNT)
-
-#define scookie_len_ok(sclen) \
-	(((sclen) == 0) || \
-	 ((sclen) >= KNOT_OPT_COOKIE_SRVR_MIN && \
-	  (sclen) <= KNOT_OPT_COOKIE_SRVR_MAX))
-
-_public_
-uint16_t knot_edns_opt_cookie_data_len(uint16_t clen, uint16_t slen)
-{
-	return (ccookie_len_ok(clen) && scookie_len_ok(slen)) ? (clen + slen) : 0;
-}
-
-_public_
-uint16_t knot_edns_opt_cookie_write(const uint8_t *cc, uint16_t cc_len,
-                                    const uint8_t *sc, uint16_t sc_len,
-                                    uint8_t *data, uint16_t data_len)
-{
-	if ((cc == NULL && cc_len > 0) || (sc == NULL && sc_len > 0)) {
-		return 0;
-	}
-
-	if (data == NULL || data_len == 0) {
-		return 0;
-	}
-
-	uint16_t cookies_size = knot_edns_opt_cookie_data_len(cc_len, sc_len);
-	if (cookies_size == 0 || data_len < cookies_size) {
-		return 0;
-	}
-
-	wire_ctx_t wire = wire_ctx_init(data, data_len);
-	wire_ctx_write(&wire, cc, cc_len);
-	if (sc_len > 0) {
-		wire_ctx_write(&wire, sc, sc_len);
-	}
-
-	if (wire.error != KNOT_EOK) {
-		return 0;
-	}
-
-	return wire_ctx_offset(&wire);
-}
-
-_public_
-int knot_edns_opt_cookie_parse(const uint8_t *data, uint16_t data_len,
-                               const uint8_t **cc, uint16_t *cc_len,
-                               const uint8_t **sc, uint16_t *sc_len)
-{
-	if (data == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	if (!cookie_len_ok(data_len)) {
-		return KNOT_EMALF;
-	}
-
-	if (cc != NULL && cc_len != NULL) {
-		*cc_len = KNOT_OPT_COOKIE_CLNT;
-		*cc = data;
-	}
-
-	if (sc != NULL && sc_len != NULL) {
-		*sc_len = data_len - KNOT_OPT_COOKIE_CLNT;
-		*sc = (*sc_len == 0) ? NULL : (data + KNOT_OPT_COOKIE_CLNT);
-	}
-
-	return KNOT_EOK;
-}
diff --git a/src/libknot/rrtype/opt-cookie.h b/src/libknot/rrtype/opt-cookie.h
deleted file mode 100644
index 750630553cc616e63e8f97d7b24b14a00674454a..0000000000000000000000000000000000000000
--- a/src/libknot/rrtype/opt-cookie.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*  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 <stdint.h>
-
-#define KNOT_OPT_COOKIE_MIN  8
-#define KNOT_OPT_COOKIE_CLNT KNOT_OPT_COOKIE_MIN
-#define KNOT_OPT_COOKIE_SRVR_MIN 8
-#define KNOT_OPT_COOKIE_SRVR_MAX 32
-
-/*!
- * \brief Returns the size of the buffer required to store the cookie.
- *
- * \note The value of \a clen and \a slen must be within defined limits.
- *
- * \param clen  Client cookie portion length.
- * \param slen  Server cookie portion length.
- *
- * \retval <> 0 if the supplied arguments are within limits
- * \retval 0 if the supplied parameters violate the requirements
- */
-uint16_t knot_edns_opt_cookie_data_len(uint16_t clen, uint16_t slen);
-
-/*!
- * \brief Write cookie wire data.
- *
- * \param cc        Client cookie.
- * \param cc_len    Client cookie size.
- * \param sc        Server cookie.
- * \param sc_len    Server cookie size.
- * \param data      Output data buffer.
- * \param data_len  Size of output data buffer.
- *
- * \retval non-zero size of written data on successful return
- * \retval 0 on error
- */
-uint16_t knot_edns_opt_cookie_write(const uint8_t *cc, uint16_t cc_len,
-                                    const uint8_t *sc, uint16_t sc_len,
-                                    uint8_t *data, uint16_t data_len);
-
-/*!
- * \brief Parse cookie wire data.
- *
- * \note The function only sets the pointers into the buffer. It does not
- * copy any data.
- *
- * \param data      Input data buffer containing whole cookie option.
- * \param data_len  Length of input data buffer.
- * \param cc        Client cookie.
- * \param cc_len    Client cookie size.
- * \param sc        Server cookie.
- * \param sc_len    Server cookie size.
- *
- * \retval KNOT_EOK
- * \retval KNOT_EINVAL
- * \retval KNOT_EMALF
- */
-int knot_edns_opt_cookie_parse(const uint8_t *data, uint16_t data_len,
-                               const uint8_t **cc, uint16_t *cc_len,
-                               const uint8_t **sc, uint16_t *sc_len);
diff --git a/src/libknot/rrtype/opt.c b/src/libknot/rrtype/opt.c
index ec0ea3465bb485f6b823cfad351f83748d71bc72..9de8ce060db8a54e7148e6a996540f2c96153728 100644
--- a/src/libknot/rrtype/opt.c
+++ b/src/libknot/rrtype/opt.c
@@ -835,3 +835,71 @@ int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option,
 
 	return KNOT_EOK;
 }
+
+_public_
+uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc,
+                               const knot_edns_cookie_t *sc)
+{
+	if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) {
+		return 0;
+	} else if (sc == NULL || sc->len == 0) {
+		return KNOT_EDNS_COOKIE_CLNT_SIZE;
+	} else if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+	           sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) {
+		return 0;
+	} else {
+		return cc->len + sc->len;
+	}
+}
+
+_public_
+int knot_edns_cookie_write(uint8_t *option, size_t option_len,
+                           const knot_edns_cookie_t *cc,
+                           const knot_edns_cookie_t *sc)
+{
+	if (option == NULL || cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) {
+		return KNOT_EINVAL;
+	}
+
+	wire_ctx_t wire = wire_ctx_init(option, option_len);
+	wire_ctx_write(&wire, cc->data, cc->len);
+
+	if (sc != NULL && sc->len > 0) {
+		if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+		    sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) {
+			return KNOT_EINVAL;
+		}
+		wire_ctx_write(&wire, sc->data, sc->len);
+	}
+
+	return wire.error;
+}
+
+_public_
+int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc,
+                           const uint8_t *option, uint16_t option_len)
+{
+	if (cc == NULL || sc == NULL || option == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	if (option_len != KNOT_EDNS_COOKIE_CLNT_SIZE &&
+	    (option_len < KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+	     option_len > KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MAX_SIZE)) {
+		return KNOT_EMALF;
+	}
+	assert(option_len >= KNOT_EDNS_COOKIE_CLNT_SIZE);
+
+	memcpy(cc->data, option, KNOT_EDNS_COOKIE_CLNT_SIZE);
+	cc->len = KNOT_EDNS_COOKIE_CLNT_SIZE;
+
+	size_t sc_len = option_len - KNOT_EDNS_COOKIE_CLNT_SIZE;
+	if (sc_len == 0) {
+		sc->len = 0;
+	} else {
+		memcpy(sc->data, option + KNOT_EDNS_COOKIE_CLNT_SIZE, sc_len);
+		sc->len = sc_len;
+	}
+
+	return KNOT_EOK;
+}
diff --git a/src/libknot/rrtype/opt.h b/src/libknot/rrtype/opt.h
index 8ebd72e2eaa03ed254d681c760fac1869fccce6b..ba5889c9b2934d6d99af286e17cf53679cf93b28 100644
--- a/src/libknot/rrtype/opt.h
+++ b/src/libknot/rrtype/opt.h
@@ -55,9 +55,16 @@ enum knot_edns_const {
 	/*! \brief Maximal size of EDNS client subnet address in bytes (IPv6). */
 	KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN = 16,
 
+	/*! \brief EDNS client cookie size. */
+	KNOT_EDNS_COOKIE_CLNT_SIZE     = 8,
+	/*! \brief EDNS minimum server cookie size. */
+	KNOT_EDNS_COOKIE_SRVR_MIN_SIZE = 8,
+	/*! \brief EDNS maximum server cookie size. */
+	KNOT_EDNS_COOKIE_SRVR_MAX_SIZE = 32,
+
 	/*! \brief NSID option code. */
 	KNOT_EDNS_OPTION_NSID          = 3,
-	/*! \brief EDNS client subnet option code. */
+	/*! \brief EDNS Client subnet option code. */
 	KNOT_EDNS_OPTION_CLIENT_SUBNET = 8,
 	/*! \brief EDNS DNS Cookie option code. */
 	KNOT_EDNS_OPTION_COOKIE        = 10,
@@ -534,4 +541,50 @@ int knot_edns_chain_write(uint8_t *option, size_t option_len,
 int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option,
                           uint16_t option_len);
 
+/*!
+ * \brief DNS Cookie content.
+ */
+typedef struct {
+	uint8_t data[KNOT_EDNS_COOKIE_SRVR_MAX_SIZE]; /*!< Cookie data. */
+	uint16_t len; /*!< Cookie length. */
+} knot_edns_cookie_t;
+
+/*!
+ * \brief Get size of the EDNS Cookie option wire size.
+ *
+ * \param[in] cc  Client cookie.
+ * \param[in] sc  Server cookie (can be NULL).
+ *
+ * \return Size of the EDNS option data or 0 if invalid input.
+ */
+uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc,
+                               const knot_edns_cookie_t *sc);
+
+/*!
+ * \brief Writes EDNS cookie wire data.
+ *
+ * \param[out] option      EDNS option data buffer.
+ * \param[in]  option_len  EDNS option data buffer size.
+ * \param[in]  cc          EDNS client cookie.
+ * \param[in]  sc          EDNS server cookie (can be NULL).
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_cookie_write(uint8_t *option, size_t option_len,
+                           const knot_edns_cookie_t *cc,
+                           const knot_edns_cookie_t *sc);
+
+/*!
+ * \brief Parses EDNS Cookie wire data.
+ *
+ * \param[out] cc          EDNS client cookie.
+ * \param[out] sc          EDNS server cookie.
+ * \param[in]  option      EDNS option data.
+ * \param[in]  option_len  EDNS option size.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc,
+                           const uint8_t *option, uint16_t option_len);
+
 /*! @} */
diff --git a/tests/.gitignore b/tests/.gitignore
index 2926e6405eeebcdfb84a20cbf734f3e952136976..a62ff73e30770c5e3770b69eb4404455ef0a321f 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -19,9 +19,7 @@
 /contrib/test_wire_ctx
 
 /libknot/test_control
-/libknot/test_cookies-client
-/libknot/test_cookies-opt
-/libknot/test_cookies-server
+/libknot/test_cookies
 /libknot/test_db
 /libknot/test_descriptor
 /libknot/test_dname
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 322aedbbd0a7fbfe3e2d250b4d2ea2d1ab273419..4b12dfa5c63e3e0b946647f3ca0bac794b48de66 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -30,9 +30,7 @@ check_PROGRAMS = \
 
 check_PROGRAMS += \
 	libknot/test_control		\
-	libknot/test_cookies-client	\
-	libknot/test_cookies-opt   	\
-	libknot/test_cookies-server	\
+	libknot/test_cookies		\
 	libknot/test_db			\
 	libknot/test_descriptor		\
 	libknot/test_dname		\
diff --git a/tests/libknot/test_cookies-client.c b/tests/libknot/test_cookies-client.c
deleted file mode 100644
index 7c9e726466a9fb57f8f069ddd69c49f716d6aceb..0000000000000000000000000000000000000000
--- a/tests/libknot/test_cookies-client.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*  Copyright (C) 2017 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 <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <tap/basic.h>
-
-#include "contrib/endian.h"
-#include "contrib/openbsd/siphash.h"
-#include "contrib/sockaddr.h"
-#include "libknot/consts.h"
-#include "libknot/cookies/client.h"
-#include "libknot/cookies/client.c"
-#include "libknot/errcode.h"
-
-int main(int argc, char *argv[])
-{
-	plan_lazy();
-
-	int ret;
-
-	SIPHASH_KEY secret = {
-		.k0 = htole64(0xDEADBEEFFACE),
-		.k1 = htole64(0xFACEDEADBEEF)
-	};
-
-	uint64_t hash;
-
-
-	struct knot_cc_input cc_in = { 0 };
-
-	struct sockaddr_storage unspec_sa = { 0 };
-
-	struct sockaddr_storage c4_sa = { 0 };
-	struct sockaddr_storage s4_sa = { 0 };
-	sockaddr_set(&c4_sa, AF_INET, "127.0.0.1", 0);
-	sockaddr_set(&s4_sa, AF_INET, "10.0.0.1", 0);
-
-	struct sockaddr_storage c6_sa = { 0 };
-	struct sockaddr_storage s6_sa = { 0 };
-	sockaddr_set(&c6_sa, AF_INET6, "2001:db8:8714:3a90::12", 0);
-	sockaddr_set(&s6_sa, AF_INET6, "::1", 0);
-
-	/* Client cookie hash algorithm. */
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = (struct sockaddr *)&c4_sa;
-	cc_in.srvr_sockaddr = (struct sockaddr *)&s4_sa;
-	cc_in.secret = secret;
-	hash = generate_client_cookie(&cc_in);
-	{
-		uint64_t expected = 0xde3832f4f59bf5ab;
-		ok(hash == expected, "cookies: SipHash client cookie content");
-	}
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = NULL;
-	cc_in.srvr_sockaddr = (struct sockaddr *)&s4_sa;
-	cc_in.secret = secret;
-	hash = generate_client_cookie(&cc_in);
-	{
-		uint64_t expected = 0x6b636ff225a1b340;
-		ok(hash == expected, "cookies: SipHash client cookie content");
-	}
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = (struct sockaddr *)&unspec_sa;
-	cc_in.srvr_sockaddr = (struct sockaddr *)&s4_sa;
-	cc_in.secret = secret;
-	hash = generate_client_cookie(&cc_in);
-	{
-		uint64_t expected = 0x6b636ff225a1b340;
-		ok(hash == expected, "cookies: SipHash client cookie content");
-	}
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = (struct sockaddr *)&c4_sa;
-	cc_in.srvr_sockaddr = NULL;
-	cc_in.secret = secret;
-	hash = generate_client_cookie(&cc_in);
-	{
-		uint64_t expected = 0xd713ab1a81179bb3;
-		ok(hash == expected, "cookies: SipHash client cookie content");
-	}
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = (struct sockaddr *)&c4_sa;
-	cc_in.srvr_sockaddr = (struct sockaddr *)&unspec_sa;
-	cc_in.secret = secret;
-	hash = generate_client_cookie(&cc_in);
-	{
-		uint64_t expected = 0xd713ab1a81179bb3;
-		ok(hash == expected, "cookies: SipHash client cookie content");
-	}
-
-	/* Client cookie check. */
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	cc_in.srvr_sockaddr = (struct sockaddr *)&s6_sa;
-	cc_in.secret = secret;
-	{
-		ret = knot_cc_check(NULL, 0, &cc_in);
-		is_int(KNOT_EINVAL, ret, "cookies: SipHash client cookie check no cookie");
-	}
-
-	{
-		uint8_t cookie[] = { 0xaf, 0xe5, 0x17, 0x94, 0x80, 0xa6, 0x0c, 0x33 };
-		ret = knot_cc_check(cookie, sizeof(cookie), NULL);
-		is_int(KNOT_EINVAL, ret, "cookies: SipHash client cookie check no input");
-	}
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	cc_in.srvr_sockaddr = (struct sockaddr *)&s6_sa;
-	cc_in.secret = secret;
-	{
-		uint64_t cookie = 0xf99dbd02b69ab3c2;
-		ret = knot_cc_check((const uint8_t *)&cookie, sizeof(cookie), &cc_in);
-		is_int(KNOT_EOK, ret, "cookies: SipHash client good cookie check");
-	}
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	cc_in.srvr_sockaddr = (struct sockaddr *)&s6_sa;
-	cc_in.secret = secret;
-	{
-		uint64_t cookie = 0xf99dbd02b69ab3c2;
-		ret = knot_cc_check((const uint8_t *)&cookie, sizeof(cookie) - 1, &cc_in);
-		is_int(KNOT_EINVAL, ret, "cookies: SipHash client cookie check invalid length");
-	}
-
-	memset(&cc_in, 0, sizeof(cc_in));
-	cc_in.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	cc_in.srvr_sockaddr = (struct sockaddr *)&s6_sa;
-	cc_in.secret = secret;
-	{
-		uint8_t cookie[] = { 0xaf, 0xe5, 0x17, 0x94, 0x80, 0xa6, 0x0c, 0x32 };
-		ret = knot_cc_check((const uint8_t *)&cookie, sizeof(cookie), &cc_in);
-		is_int(KNOT_EINVAL, ret, "cookies: SipHash client cookie check invalid cookie");
-	}
-}
diff --git a/tests/libknot/test_cookies-opt.c b/tests/libknot/test_cookies-opt.c
deleted file mode 100644
index 8cc0dfe5bf7d59161bf2d97b439cde6f6e6ef2a8..0000000000000000000000000000000000000000
--- a/tests/libknot/test_cookies-opt.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*  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 <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <tap/basic.h>
-
-#include "libknot/consts.h"
-#include "libknot/errcode.h"
-#include "libknot/rrtype/opt.h"
-#include "libknot/rrtype/opt-cookie.h"
-
-const char *cookie_opts[] = {
-	"\x00\x0a" "\x00\x00", /* Zero length cookie. */
-	"\x00\x0a" "\x00\x01" "\x00", /* Short client cookie. */
-	"\x00\x0a" "\x00\x07" "\x00\x01\x02\x03\x04\x05\x06", /* Short client cookie. */
-	"\x00\x0a" "\x00\x09" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08", /* Short server cookie. */
-	"\x00\x0a" "\x00\x0f" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e", /* Short server cookie. */
-	"\x00\x0a" "\x00\x29" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28", /* Long server cookie. */
-	"\x00\x0a" "\x00\x08" "\x00\x01\x02\x03\x04\x05\x06\x07", /* Only client cookie. */
-	"\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", /* 8 octets long server cookie. */
-	"\x00\x0a" "\x00\x28" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" /* 32 octets long server cookie. */
-};
-
-#define ROPT(i) ((const uint8_t *)cookie_opts[(i)])
-
-static void get_opt_data(const uint8_t *opt,
-                         const uint8_t **data, uint16_t *data_len)
-{
-	if (opt == NULL) {
-		*data = NULL;
-		*data_len = 0;
-	}
-
-	*data = knot_edns_opt_get_data((uint8_t *)opt);
-	*data_len = knot_edns_opt_get_length((uint8_t *)opt);
-}
-
-int main(int argc, char *argv[])
-{
-	plan_lazy();
-
-	uint16_t code;
-	uint16_t data_len;
-	const uint8_t *data;
-	int ret;
-
-	const uint8_t *cc, *sc;
-	uint16_t cc_len, sc_len;
-
-	code = knot_edns_opt_get_code(ROPT(0));
-	ok(code == KNOT_EDNS_OPTION_COOKIE, "cookies: EDNS OPT code");
-
-	data_len = knot_edns_opt_get_length(ROPT(1));
-	ok(data_len == 1, "cookies: EDNS OPT length");
-
-	/* Should return pointer to data, although option has zero length. */
-	data = knot_edns_opt_get_data((uint8_t *)ROPT(0));
-	ok(data != NULL, "cookies: EDNS OPT zero data");
-
-	data = knot_edns_opt_get_data((uint8_t *)ROPT(1));
-	ok(data != NULL, "cookies: EDNS OPT data");
-
-	ret = knot_edns_opt_cookie_parse(NULL, 0, NULL, NULL, NULL, NULL);
-	is_int(KNOT_EINVAL, ret, "cookies: EDNS OPT parse NULL");
-
-	/* Malformed cookies. */
-
-	get_opt_data(ROPT(0), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, NULL, NULL, NULL, NULL);
-	is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse zero length");
-
-	get_opt_data(ROPT(1), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 1B (short) cookie");
-
-	get_opt_data(ROPT(2), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 7B (short) cookie");
-
-	get_opt_data(ROPT(3), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 9B (short) cookie");
-
-	get_opt_data(ROPT(4), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 15B (short) cookie");
-
-	get_opt_data(ROPT(5), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 41B (long) cookie");
-
-	get_opt_data(ROPT(5), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 41B (long) cookie");
-
-	/* Testing combination of output parameters. */
-
-	cc = sc = NULL;
-	cc_len = sc_len = 0;
-	get_opt_data(ROPT(7), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, NULL, NULL);
-	ok(ret == KNOT_EOK && cc != NULL && cc_len == 8, "cookies: EDNS OPT parse client cookie");
-
-	cc = sc = NULL;
-	cc_len = sc_len = 0;
-	get_opt_data(ROPT(7), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, NULL, NULL, &sc, &sc_len);
-	ok(ret == KNOT_EOK && sc != NULL && sc_len == 8, "cookies: EDNS OPT parse server cookie");
-
-	/* Valid cookies. */
-
-	const void *DUMMYPTR = (void *)1;
-	const int DUMMYVAL = 1;
-
-	cc = sc = DUMMYPTR;
-	cc_len = sc_len = DUMMYVAL;
-	get_opt_data(ROPT(6), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	ok(ret == KNOT_EOK &&
-	   cc != NULL && cc != DUMMYPTR && cc_len == 8 &&
-	   sc == NULL && sc_len == 0, "cookies: EDNS OPT parse 8B cookie");
-
-	cc = sc = DUMMYPTR;
-	cc_len = sc_len = DUMMYVAL;
-	get_opt_data(ROPT(7), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	ok(ret == KNOT_EOK &&
-	    cc != NULL && cc != DUMMYPTR && cc_len == 8 &&
-	    sc != NULL && sc != DUMMYPTR && sc_len == 8, "cookies: EDNS OPT parse 16B cookie");
-
-	cc = sc = DUMMYPTR;
-	cc_len = sc_len = DUMMYVAL;
-	get_opt_data(ROPT(8), &data, &data_len);
-	ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len);
-	ok(ret == KNOT_EOK &&
-	   cc != NULL && cc != DUMMYPTR && cc_len == 8 &&
-	   sc != NULL && sc != DUMMYPTR && sc_len == 32, "cookies: EDNS OPT parse 40B cookie");
-
-	return 0;
-}
diff --git a/tests/libknot/test_cookies-server.c b/tests/libknot/test_cookies-server.c
deleted file mode 100644
index a235f7cb21f5f9076245a4ffd172ffbebfacf458..0000000000000000000000000000000000000000
--- a/tests/libknot/test_cookies-server.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*  Copyright (C) 2017 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 <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <tap/basic.h>
-
-#include "contrib/endian.h"
-#include "contrib/openbsd/siphash.h"
-#include "contrib/sockaddr.h"
-#include "libknot/consts.h"
-#include "libknot/cookies/server.h"
-#include "libknot/cookies/server.c"
-#include "libknot/errcode.h"
-#include "libknot/rrtype/opt.h"
-#include "libknot/rrtype/opt-cookie.h"
-
-const char *cookie_opts[] = {
-	"\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x19\x99\xc3\xbc\x4e\x95\xd9\xdf", /* 8 octets long wrong server cookie. */
-	"\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x69\xf4\xac\x05\x90\x6c\xac\x33", /* 8 octets long OK server cookie. */
-	"\x00\x0a" "\x00\x18" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x39\x88\x78\x19\xec\xdb\xbd\xbf", /* 8B nonce 8B hash long wrong server cookie. */
-	"\x00\x0a" "\x00\x18" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\xc5\xe0\x47\x89\xe9\x77\x32\xd2"/* 8B nonce 8B hash long OK server cookie. */
-};
-
-#define ROPT(i) ((const uint8_t *)cookie_opts[(i)])
-
-static void get_opt_cookies(const uint8_t *opt, struct knot_dns_cookies *cookies)
-{
-	memset(cookies, 0, sizeof(*cookies));
-
-	if (opt == NULL) {
-		return;
-	}
-
-	const uint8_t *data = knot_edns_opt_get_data((uint8_t *)opt);
-	uint16_t data_len = knot_edns_opt_get_length((uint8_t *)opt);
-
-	knot_edns_opt_cookie_parse(data, data_len,
-	                           &cookies->cc, &cookies->cc_len,
-	                           &cookies->sc, &cookies->sc_len);
-}
-
-int main(int argc, char *argv[])
-{
-	plan_lazy();
-
-	int ret;
-
-	const uint8_t sc0[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
-	const uint8_t sc1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17 };
-
-	const uint8_t cc[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
-
-	SIPHASH_KEY secret = {
-		.k0 = htole64(0xDEADBEEFFACE),
-		.k1 = htole64(0xFACEDEADBEEF)
-	};
-
-	SIPHASH_KEY empty = { 0 };
-
-	const uint8_t nonce[] = { 10, 11, 12, 13, 14, 15, 16, 17 };
-
-	uint64_t hash;
-
-	struct knot_sc_content sc_content;
-
-	struct knot_sc_private srvr_data = { 0 };
-	struct knot_sc_input sc_in = { 0 };
-
-	struct sockaddr_storage unspec_sa = { 0 };
-
-	struct sockaddr_storage c4_sa = { 0 };
-	struct sockaddr_storage s4_sa = { 0 };
-	sockaddr_set(&c4_sa, AF_INET, "127.0.0.1", 0);
-	sockaddr_set(&s4_sa, AF_INET, "10.0.0.1", 0);
-
-	struct sockaddr_storage c6_sa = { 0 };
-	struct sockaddr_storage s6_sa = { 0 };
-	sockaddr_set(&c6_sa, AF_INET6, "2001:db8:8714:3a90::12", 0);
-	sockaddr_set(&s6_sa, AF_INET6, "::1", 0);
-
-	struct knot_dns_cookies cookies;
-
-	/* Server cookie hash algorithm. */
-
-	memset(&sc_in, 0, sizeof(sc_in));
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c4_sa;
-	srvr_data.secret = secret;
-	sc_in.cc = cc;
-	sc_in.cc_len = sizeof(cc);
-	sc_in.nonce = nonce;
-	sc_in.nonce_len = sizeof(nonce);
-	sc_in.srvr_data = &srvr_data;
-	hash = generate_server_cookie(&sc_in);
-	{
-		uint64_t expected = 0xbb08ce797dade217;
-		ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content");
-	}
-
-	memset(&sc_in, 0, sizeof(sc_in));
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	sc_in.cc = cc;
-	sc_in.cc_len = sizeof(cc);
-	sc_in.nonce = nonce;
-	sc_in.nonce_len = sizeof(nonce);
-	sc_in.srvr_data = &srvr_data;
-	hash = generate_server_cookie(&sc_in);
-	{
-		uint64_t expected = 0xd23277e98947e0c5;
-		ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content");
-	}
-
-	memset(&sc_in, 0, sizeof(sc_in));
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	sc_in.cc = cc;
-	sc_in.cc_len = sizeof(cc);
-	sc_in.nonce = NULL;
-	sc_in.nonce_len = 0;
-	sc_in.srvr_data = &srvr_data;
-	hash = generate_server_cookie(&sc_in);
-	{
-		uint64_t expected = 0x33ac6c9005acf469;
-		ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content");
-	}
-
-	memset(&sc_in, 0, sizeof(sc_in));
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&unspec_sa;
-	srvr_data.secret = secret;
-	sc_in.cc = cc;
-	sc_in.cc_len = sizeof(cc);
-	sc_in.nonce = nonce;
-	sc_in.nonce_len = sizeof(nonce);
-	sc_in.srvr_data = &srvr_data;
-	hash = generate_server_cookie(&sc_in);
-	{
-		uint64_t expected = 0x5732e9d1127281e9;
-		ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content");
-	}
-
-	memset(&sc_in, 0, sizeof(sc_in));
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&unspec_sa;
-	srvr_data.secret = secret;
-	sc_in.cc = cc;
-	sc_in.cc_len = sizeof(cc) - 1;
-	sc_in.nonce = nonce;
-	sc_in.nonce_len = sizeof(nonce);
-	sc_in.srvr_data = &srvr_data;
-	hash = generate_server_cookie(&sc_in);
-	{
-		uint64_t expected = 0x98a4bf0c9fb53340;
-		ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content");
-	}
-
-	memset(&sc_in, 0, sizeof(sc_in));
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&unspec_sa;
-	srvr_data.secret = secret;
-	sc_in.cc = cc;
-	sc_in.cc_len = sizeof(cc);
-	sc_in.nonce = nonce;
-	sc_in.nonce_len = sizeof(nonce) - 1;
-	sc_in.srvr_data = &srvr_data;
-	hash = generate_server_cookie(&sc_in);
-	{
-		uint64_t expected = 0xdb33fa2bb43d9ac5;
-		ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content");
-	}
-
-	/* Server cookie parse. */
-
-	const void *DUMMYPTR = (void *)1;
-	const int DUMMYVAL = 1;
-
-	ret = knot_sc_parse(0, NULL, 0, &sc_content);
-	is_int(KNOT_EINVAL, ret, "cookies: parse server cookie no cookie");
-
-	ret = knot_sc_parse(0, sc0, sizeof(sc0), NULL);
-	is_int(KNOT_EINVAL, ret, "cookies: parse server cookie no content");
-
-	ret = knot_sc_parse(sizeof(sc0), sc0, sizeof(sc0), &sc_content);
-	is_int(KNOT_EINVAL, ret, "cookies: parse server cookie too large nonce");
-
-	sc_content.nonce = sc_content.hash = DUMMYPTR;
-	sc_content.nonce_len = sc_content.hash_len = DUMMYVAL;
-	ret = knot_sc_parse(0, sc0, sizeof(sc0), &sc_content);
-	ok(ret == KNOT_EOK &&
-	   sc_content.nonce == NULL && sc_content.nonce_len == 0 &&
-	   sc_content.hash == sc0 && sc_content.hash_len == sizeof(sc0), "cookies: parse server cookie 0B nonce");
-
-	sc_content.nonce = sc_content.hash = DUMMYPTR;
-	sc_content.nonce_len = sc_content.hash_len = DUMMYVAL;
-	ret = knot_sc_parse(8, sc1, sizeof(sc1), &sc_content);
-	ok(ret == KNOT_EOK &&
-	   sc_content.nonce == sc1 && sc_content.nonce_len == 8 &&
-	   sc_content.hash == (sc1 + 8) && sc_content.hash_len == 8, "cookies: parse server cookie 8B nonce");
-
-	/* Server cookie check. */
-
-	get_opt_cookies(ROPT(1), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(1, &cookies, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - wrong nonce length");
-
-	get_opt_cookies(ROPT(1), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(17, &cookies, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - too long nonce");
-
-	get_opt_cookies(ROPT(1), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(0, NULL, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no cookies");
-
-	get_opt_cookies(ROPT(1), &cookies);
-	cookies.cc = NULL;
-	cookies.cc_len = 0;
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(0, NULL, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no client cookie");
-
-	get_opt_cookies(ROPT(1), &cookies);
-	cookies.sc = NULL;
-	cookies.sc_len = 0;
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(0, NULL, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no server cookie");
-
-	get_opt_cookies(ROPT(1), &cookies);
-	srvr_data.clnt_sockaddr = NULL;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(0, NULL, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no socket address");
-
-	get_opt_cookies(ROPT(1), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = empty;
-	ret = knot_sc_check(0, &cookies, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no secret");
-
-	get_opt_cookies(ROPT(0), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(0, &cookies, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - bad server cookie");
-
-	get_opt_cookies(ROPT(1), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&unspec_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(0, &cookies, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - bad socket");
-
-	get_opt_cookies(ROPT(1), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(0, &cookies, &srvr_data);
-	is_int(KNOT_EOK, ret, "cookies: SipHash server cookie check");
-
-	get_opt_cookies(ROPT(2), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(8, &cookies, &srvr_data);
-	is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - bad server cookie");
-
-	get_opt_cookies(ROPT(3), &cookies);
-	srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa;
-	srvr_data.secret = secret;
-	ret = knot_sc_check(8, &cookies, &srvr_data);
-	is_int(KNOT_EOK, ret, "cookies: SipHash server cookie check");
-
-	return 0;
-}
diff --git a/tests/libknot/test_cookies.c b/tests/libknot/test_cookies.c
new file mode 100644
index 0000000000000000000000000000000000000000..98e8cafa0506d038f509d5cf3378b367ffd97570
--- /dev/null
+++ b/tests/libknot/test_cookies.c
@@ -0,0 +1,176 @@
+/*  Copyright (C) 2017 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 <stdlib.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/cookies.h"
+#include "libknot/errcode.h"
+#include "contrib/sockaddr.h"
+
+static void client_generate(struct sockaddr_storage *c_addr, struct sockaddr_storage *s_addr,
+                            const uint8_t *secret, const char *msg, int code, uint64_t le_cc)
+{
+	knot_edns_cookie_params_t params = {
+		.client_addr = (struct sockaddr *)c_addr,
+		.server_addr = (struct sockaddr *)s_addr,
+	};
+	memcpy(params.secret, secret, sizeof(params.secret));
+
+	knot_edns_cookie_t cc;
+	int ret = knot_edns_cookie_client_generate(&cc, &params);
+	is_int(ret, code, "client_generate ret: %s", msg);
+	if (ret == KNOT_EOK) {
+		uint64_t ref = le64toh(le_cc);
+		ok(cc.len == sizeof(ref) && memcmp(cc.data, &ref, cc.len) == 0,
+		   "client_generate value: %s", msg);
+	}
+}
+
+static void server_generate(struct sockaddr_storage *c_addr, const uint8_t *secret,
+                            const knot_edns_cookie_t *cc, const char *msg, int code,
+                            uint64_t le_sc)
+{
+	knot_edns_cookie_params_t params = {
+		.client_addr = (struct sockaddr *)c_addr,
+	};
+	memcpy(params.secret, secret, sizeof(params.secret));
+
+	knot_edns_cookie_t sc;
+	int ret = knot_edns_cookie_server_generate(&sc, cc, &params);
+	is_int(ret, code, "server_generate ret: %s", msg);
+	if (ret == KNOT_EOK) {
+		uint64_t ref = le64toh(le_sc);
+		ok(sc.len == sizeof(ref) && memcmp(sc.data, &ref, sc.len) == 0,
+		   "server_generate value: %s", msg);
+	}
+}
+
+static void client_check(struct sockaddr_storage *c_addr, struct sockaddr_storage *s_addr,
+                         const uint8_t *secret, const char *msg, uint16_t le_cc_len,
+                         uint64_t le_cc, int code)
+{
+	knot_edns_cookie_params_t params = {
+		.client_addr = (struct sockaddr *)c_addr,
+		.server_addr = (struct sockaddr *)s_addr,
+	};
+	if (secret != NULL) {
+		memcpy(params.secret, secret, sizeof(params.secret));
+	}
+
+	uint64_t ref = le64toh(le_cc);
+	knot_edns_cookie_t cc = {
+		.len = le_cc_len
+	};
+	memcpy(cc.data, &ref, le_cc_len);
+
+	int ret = knot_edns_cookie_client_check(&cc, &params);
+	is_int(ret, code, "client_check ret: %s", msg);
+}
+
+static void server_check(struct sockaddr_storage *c_addr, const uint8_t *secret,
+                         const char *msg, uint16_t le_cc_len, uint64_t le_cc,
+                         uint16_t le_sc_len, uint64_t le_sc, int code)
+{
+	knot_edns_cookie_params_t params = {
+		.client_addr = (struct sockaddr *)c_addr,
+	};
+	if (secret != NULL) {
+		memcpy(params.secret, secret, sizeof(params.secret));
+	}
+
+	uint64_t ref = le64toh(le_cc);
+	knot_edns_cookie_t cc = {
+		.len = le_cc_len
+	};
+	memcpy(cc.data, &ref, le_cc_len);
+
+	ref = le64toh(le_sc);
+	knot_edns_cookie_t sc = {
+		.len = le_sc_len
+	};
+	memcpy(sc.data, &ref, le_sc_len);
+
+	int ret = knot_edns_cookie_server_check(&sc, &cc, &params);
+	is_int(ret, code, "server_check ret: %s", msg);
+}
+
+int main(int argc, char *argv[])
+{
+	plan_lazy();
+
+	const uint8_t secret[] = "\xCE\xFA\xEF\xBE\xAD\xDE\x00\x00\xEF\xBE\xAD\xDE\xCE\xFA\x00\x00";
+
+	struct sockaddr_storage unspec_sa = { 0 };
+
+	struct sockaddr_storage c4_sa = { 0 };
+	struct sockaddr_storage s4_sa = { 0 };
+	sockaddr_set(&c4_sa, AF_INET, "127.0.0.1", 0);
+	sockaddr_set(&s4_sa, AF_INET, "10.0.0.1", 0);
+
+	struct sockaddr_storage c6_sa = { 0 };
+	struct sockaddr_storage s6_sa = { 0 };
+	sockaddr_set(&c6_sa, AF_INET6, "2001:db8:8714:3a90::12", 0);
+	sockaddr_set(&s6_sa, AF_INET6, "::1", 0);
+
+	/* Client cookie generate. */
+	client_generate(NULL,       &s4_sa,     secret, "NULL, IPv4",   KNOT_EINVAL, 0);
+	client_generate(&c4_sa,     NULL,       secret, "IPv4, NULL",   KNOT_EINVAL, 0);
+	client_generate(&c4_sa,     &s4_sa,     secret, "IPv4, IPv4",   KNOT_EOK, 0xde3832f4f59bf5ab);
+	client_generate(&unspec_sa, &s4_sa,     secret, "unspec, IPv4", KNOT_EOK, 0x6b636ff225a1b340);
+	client_generate(&c4_sa,     &unspec_sa, secret, "IPv4, unspec", KNOT_EOK, 0xd713ab1a81179bb3);
+
+	/* Client cookie check. */
+	client_check(NULL,   &s6_sa, secret, "no client addr",    8, 0xf99dbd02b69ab3c2, KNOT_EINVAL);
+	client_check(&c6_sa, NULL,   secret, "no server addr",    8, 0xf99dbd02b69ab3c2, KNOT_EINVAL);
+	client_check(&c6_sa, &s6_sa, NULL,   "no secret",         8, 0xf99dbd02b69ab3c2, KNOT_EINVAL);
+	client_check(&c6_sa, &s6_sa, secret, "no cookie",         0, 0,                  KNOT_EINVAL);
+	client_check(&c6_sa, &s6_sa, secret, "bad cookie length", 7, 0xf99dbd02b69ab3c2, KNOT_EINVAL);
+	client_check(&c6_sa, &s6_sa, secret, "invalid cookie",    8, 0,                  KNOT_EINVAL);
+	client_check(&c6_sa, &s6_sa, secret, "good cookie",       8, 0xf99dbd02b69ab3c2, KNOT_EOK);
+
+	const knot_edns_cookie_t cc = {
+		.data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+		.len = 8
+	};
+
+	/* Server cookie generate. */
+	knot_edns_cookie_t cc_part = cc; cc_part.len--;
+	server_generate(NULL,       secret, &cc,      "NULL",        KNOT_EINVAL, 0);
+	server_generate(&c4_sa,     secret, &cc_part, "cookie part", KNOT_EINVAL, 0);
+	server_generate(&c4_sa,     secret, &cc,      "IPv4",        KNOT_EOK, 0x52f86bfcc98ded6);
+	server_generate(&c6_sa,     secret, &cc,      "IPv6",        KNOT_EOK, 0x33ac6c9005acf469);
+	server_generate(&unspec_sa, secret, &cc,      "unspec",      KNOT_EOK, 0x96df9dbf28f0f59e);
+
+	/* Server cookie check. */
+	server_check(NULL,   secret, "no addr",           8, 0x0706050403020100,
+	                                                  8, 0x33ac6c9005acf469, KNOT_EINVAL);
+	server_check(&c6_sa, NULL,   "no secret",         8, 0x0706050403020100,
+	                                                  8, 0x33ac6c9005acf469, KNOT_EINVAL);
+	server_check(&c6_sa, secret, "no client cookie",  0, 0,
+	                                                  8, 0x33ac6c9005acf469, KNOT_EINVAL);
+	server_check(&c6_sa, secret, "no server cookie",  8, 0x0706050403020100,
+	                                                  0, 0,                  KNOT_EINVAL);
+	server_check(&c6_sa, secret, "bad client cookie", 8, 0,
+	                                                  8, 0x33ac6c9005acf469, KNOT_EINVAL);
+	server_check(&c6_sa, secret, "bad server cookie", 8, 0x0706050403020100,
+	                                                  8, 0,                  KNOT_EINVAL);
+	server_check(&c6_sa, secret, "good cookie 1",     8, 0x0706050403020100,
+	                                                  8, 0x33ac6c9005acf469, KNOT_EOK);
+
+	return 0;
+}
diff --git a/tests/libknot/test_edns.c b/tests/libknot/test_edns.c
index 5106a4c87b8e6134cb58a48f2d64f797b55338ae..28dfa12accddd5effb5ee3e00bea0bf6df880475 100644
--- a/tests/libknot/test_edns.c
+++ b/tests/libknot/test_edns.c
@@ -201,6 +201,9 @@ static void test_getters(knot_rrset_t *opt_rr)
 
 	check = knot_edns_has_option(opt_rr, E_OPT4_CODE);
 	ok(check, "OPT RR getters: empty option 2");
+
+	uint16_t code = knot_edns_opt_get_code((const uint8_t *)"\x00\x0a" "\x00\x00");
+	ok(code == KNOT_EDNS_OPTION_COOKIE, "OPT RR getters: EDNS OPT code");
 }
 
 static void test_setters(knot_rrset_t *opt_rr)
@@ -845,6 +848,72 @@ static void test_chain(void)
 	   "%s: parse, malformed", __func__);
 }
 
+static void check_cookie_parse(const char *opt, knot_edns_cookie_t *cc,
+                               knot_edns_cookie_t *sc, int code, const char *msg)
+{
+	const uint8_t *data = NULL;
+	uint16_t data_len = 0;
+	if (opt != NULL) {
+		data = knot_edns_opt_get_data((uint8_t *)opt);
+		data_len = knot_edns_opt_get_length((uint8_t *)opt);
+	}
+
+	int ret = knot_edns_cookie_parse(cc, sc, data, data_len);
+	is_int(code, ret, "cookie parse ret: %s", msg);
+}
+
+static void ok_cookie_check(const char *opt, knot_edns_cookie_t *cc,
+                            knot_edns_cookie_t *sc, uint16_t cc_len, uint16_t sc_len,
+                            const char *msg)
+{
+	check_cookie_parse(opt, cc, sc, KNOT_EOK, msg);
+
+	is_int(cc->len, cc_len, "cookie parse cc len: %s", msg);
+	is_int(sc->len, sc_len, "cookie parse cc len: %s", msg);
+
+	uint16_t size = knot_edns_cookie_size(cc, sc);
+	is_int(size, cc_len + sc_len, "cookie len: %s", msg);
+
+	uint8_t buf[64];
+	int ret = knot_edns_cookie_write(buf, sizeof(buf), cc, sc);
+	is_int(KNOT_EOK, ret, "cookie write ret: %s", msg);
+}
+
+static void test_cookie(void)
+{
+	const char *good[] = {
+		"\x00\x0a" "\x00\x08" "\x00\x01\x02\x03\x04\x05\x06\x07", /* Only client cookie. */
+		"\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", /* 8 octets long server cookie. */
+		"\x00\x0a" "\x00\x28" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" /* 32 octets long server cookie. */
+	};
+
+	const char *bad[] = {
+		"\x00\x0a" "\x00\x00", /* Zero length cookie. */
+		"\x00\x0a" "\x00\x01" "\x00", /* Short client cookie. */
+		"\x00\x0a" "\x00\x07" "\x00\x01\x02\x03\x04\x05\x06", /* Short client cookie. */
+		"\x00\x0a" "\x00\x09" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08", /* Short server cookie. */
+		"\x00\x0a" "\x00\x0f" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e", /* Short server cookie. */
+		"\x00\x0a" "\x00\x29" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28", /* Long server cookie. */
+	};
+
+	knot_edns_cookie_t cc, sc;
+
+	ok_cookie_check(good[0], &cc,  &sc, 8, 0,  "good cookie 0");
+	ok_cookie_check(good[1], &cc,  &sc, 8, 8,  "good cookie 1");
+	ok_cookie_check(good[2], &cc,  &sc, 8, 32, "good cookie 2");
+
+	check_cookie_parse(NULL,    &cc,  &sc,  KNOT_EINVAL, "no data");
+	check_cookie_parse(good[0], NULL, &sc,  KNOT_EINVAL, "no client cookie");
+	check_cookie_parse(good[1], &cc,  NULL, KNOT_EINVAL, "no server cookie");
+
+	check_cookie_parse(bad[0],  &cc,  &sc,  KNOT_EMALF,  "bad cookie 0");
+	check_cookie_parse(bad[1],  &cc,  &sc,  KNOT_EMALF,  "bad cookie 1");
+	check_cookie_parse(bad[2],  &cc,  &sc,  KNOT_EMALF,  "bad cookie 2");
+	check_cookie_parse(bad[3],  &cc,  &sc,  KNOT_EMALF,  "bad cookie 3");
+	check_cookie_parse(bad[4],  &cc,  &sc,  KNOT_EMALF,  "bad cookie 4");
+	check_cookie_parse(bad[5],  &cc,  &sc,  KNOT_EMALF,  "bad cookie 5");
+}
+
 int main(int argc, char *argv[])
 {
 	plan_lazy();
@@ -863,6 +932,7 @@ int main(int argc, char *argv[])
 	test_alignment();
 	test_keepalive();
 	test_chain();
+	test_cookie();
 
 	knot_rrset_clear(&opt_rr, NULL);