summaryrefslogtreecommitdiffstats
path: root/src/libknot/cookies.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/cookies.c')
-rw-r--r--src/libknot/cookies.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/libknot/cookies.c b/src/libknot/cookies.c
new file mode 100644
index 0000000..e7b3354
--- /dev/null
+++ b/src/libknot/cookies.c
@@ -0,0 +1,170 @@
+/* Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+
+#include "libknot/attribute.h"
+#include "libknot/cookies.h"
+#include "libknot/endian.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->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->server_addr, &addr_len);
+ assert(addr);
+ 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;
+}
+
+static int cookie_server_generate(knot_edns_cookie_t *out,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_params_t *params)
+{
+ assert(out && params);
+
+ if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE ||
+ params->client_addr == NULL) {
+ return KNOT_EINVAL;
+ } else if (out->data[0] != KNOT_EDNS_COOKIE_VERSION) {
+ return KNOT_ENOTSUP;
+ }
+
+ SIPHASH_CTX ctx;
+ assert(sizeof(params->secret) == sizeof(SIPHASH_KEY));
+ SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret);
+
+ SipHash24_Update(&ctx, cc->data, cc->len);
+ SipHash24_Update(&ctx, out->data, out->len);
+
+ size_t addr_len = 0;
+ void *addr = sockaddr_raw(params->client_addr, &addr_len);
+ assert(addr);
+ SipHash24_Update(&ctx, addr, addr_len);
+
+ uint64_t hash = SipHash24_End(&ctx);
+ memcpy(out->data + out->len, &hash, sizeof(hash));
+ out->len += sizeof(hash);
+
+ 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 || params == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ out->data[0] = params->version;
+ out->data[1] = 0; /* reserved */
+ out->data[2] = 0; /* reserved */
+ out->data[3] = 0; /* reserved */
+ out->len = 4;
+
+ uint32_t now = htobe32(params->timestamp);
+ memcpy(&out->data[out->len], &now, sizeof(now));
+ out->len += sizeof(now);
+
+ return cookie_server_generate(out, cc, params);
+}
+
+_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 || sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE || params == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint32_t cookie_time;
+ memcpy(&cookie_time, &sc->data[4], sizeof(cookie_time));
+ cookie_time = be32toh(cookie_time);
+
+ uint32_t min_time = params->timestamp - params->lifetime_before;
+ uint32_t max_time = params->timestamp + params->lifetime_after;
+ if (cookie_time < min_time || cookie_time > max_time) {
+ return KNOT_ERANGE;
+ }
+
+ const int fixed_len = 8;
+ knot_edns_cookie_t ref;
+ memcpy(ref.data, sc->data, fixed_len);
+ ref.len = fixed_len;
+
+ int ret = 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 + fixed_len, ref.data + fixed_len,
+ sc->len - fixed_len);
+ if (ret != 0) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}