summaryrefslogtreecommitdiff
path: root/src/libknot/tsig-op.c
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-11-02 22:44:12 +0100
committerOndřej Surý <ondrej@sury.org>2011-11-02 22:44:12 +0100
commitc8d5977bb546dae9ed59d81556639c49badd8121 (patch)
tree4c86750db26c1c3502b60f2cd78ca9611cfa01d6 /src/libknot/tsig-op.c
downloadknot-upstream/0.8.0_pre1.tar.gz
Imported Upstream version 0.8.0~pre1upstream/0.8.0_pre1
Diffstat (limited to 'src/libknot/tsig-op.c')
-rw-r--r--src/libknot/tsig-op.c1089
1 files changed, 1089 insertions, 0 deletions
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
new file mode 100644
index 0000000..3178a23
--- /dev/null
+++ b/src/libknot/tsig-op.c
@@ -0,0 +1,1089 @@
+/* Copyright (C) 2011 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 <stdint.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "common.h"
+#include "tsig.h"
+#include "tsig-op.h"
+#include "util/wire.h"
+#include "util/error.h"
+#include "util/debug.h"
+
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+
+static int b64rmap_initialized = 0;
+static uint8_t b64rmap[256];
+
+static const uint8_t b64rmap_special = 0xf0;
+static const uint8_t b64rmap_end = 0xfd;
+static const uint8_t b64rmap_space = 0xfe;
+static const uint8_t b64rmap_invalid = 0xff;
+
+/**
+ * Initializing the reverse map is not thread safe.
+ * Which is fine for NSD. For now...
+ **/
+void b64_initialize_rmap()
+{
+ int i;
+ char ch;
+
+ /* Null: end of string, stop parsing */
+ b64rmap[0] = b64rmap_end;
+
+ for (i = 1; i < 256; ++i) {
+ ch = (char)i;
+ /* Whitespaces */
+ if (isspace(ch)) {
+ b64rmap[i] = b64rmap_space;
+ }
+ /* Padding: stop parsing */
+ else if (ch == Pad64) {
+ b64rmap[i] = b64rmap_end;
+ }
+ /* Non-base64 char */
+ else {
+ b64rmap[i] = b64rmap_invalid;
+ }
+ }
+
+ /* Fill reverse mapping for base64 chars */
+ for (i = 0; Base64[i] != '\0'; ++i) {
+ b64rmap[(uint8_t)Base64[i]] = i;
+ }
+
+ b64rmap_initialized = 1;
+}
+
+int b64_pton_do(char const *src, uint8_t *target, size_t targsize)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] = ofs << 2;
+ state = 1;
+ break;
+ case 1:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 4;
+ target[tarindex+1] = (ofs & 0x0f)
+ << 4 ;
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 2;
+ target[tarindex+1] = (ofs & 0x03)
+ << 6;
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs;
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target[tarindex] != 0) {
+ return (-1);
+ }
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+
+int b64_pton_len(char const *src)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ state = 1;
+ break;
+ case 1:
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+int b64_pton(char const *src, uint8_t *target, size_t targsize)
+{
+ if (!b64rmap_initialized) {
+ b64_initialize_rmap();
+ }
+
+ if (target) {
+ return b64_pton_do(src, target, targsize);
+ } else {
+ return b64_pton_len(src);
+ }
+}
+
+#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
+
+
+
+
+
+
+
+
+
+
+
+
+const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest
+
+
+static int knot_tsig_check_algorithm(const knot_rrset_t *tsig_rr)
+{
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ if (!alg_name) {
+ return KNOT_EMALF;
+ }
+
+ tsig_algorithm_t alg = tsig_alg_from_name(alg_name);
+ if (alg == 0) {
+ /*!< \todo is this error OK? */
+ dbg_tsig("TSIG: unknown algorithm.\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_key(const knot_rrset_t *tsig_rr,
+ const knot_key_t *tsig_key)
+{
+ const knot_dname_t *tsig_name = knot_rrset_owner(tsig_rr);
+ if (!tsig_name) {
+ return KNOT_EMALF;
+ }
+
+ const char *name = knot_dname_to_str(tsig_name);
+ if (!name) {
+ return KNOT_EMALF;
+ }
+
+ if (knot_dname_compare(tsig_name, tsig_key->name) != 0) {
+ /*!< \todo which error. */
+ dbg_tsig("TSIG: unknown key: %s\n", name);
+ return KNOT_TSIG_EBADKEY;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!wire || !digest || !digest_len || !key) {
+ dbg_tsig("TSIG: digest: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (!key->name) {
+ dbg_tsig("TSIG: digest: no algorithm\n");
+ return KNOT_EMALF;
+ }
+
+ tsig_algorithm_t tsig_alg = key->algorithm;
+ if (tsig_alg == 0) {
+ dbg_tsig("TSIG: digest: unknown algorithm\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ /* Create digest, using length of the algorithm. */
+// *digest = malloc(sizeof(uint8_t) * tsig_alg_digest_length(tsig_alg));
+// if (!digest) {
+// ERR_ALLOC_FAILED;
+// return KNOT_ENOMEM;
+// }
+
+ /* Decode key from Base64. */
+ char decoded_key[B64BUFSIZE];
+
+ int decoded_key_size = b64_pton(key->secret, (uint8_t *)decoded_key,
+ B64BUFSIZE);
+ if (decoded_key_size < 0) {
+ dbg_tsig("TSIG: Could not decode Base64\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_tsig("TSIG: decoded key size: %d\n", decoded_key_size);
+ dbg_tsig("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key);
+
+ dbg_tsig("TSIG: using this wire for digest calculation\n");
+
+ //dbg_tsig_hex(wire, wire_len);
+
+ /* Compute digest. */
+ HMAC_CTX ctx;
+
+ switch (tsig_alg) {
+ case KNOT_TSIG_ALG_HMAC_MD5:
+ HMAC_Init(&ctx, decoded_key,
+ decoded_key_size, EVP_md5());
+ break;
+ default:
+ return KNOT_ENOTSUP;
+ } /* switch */
+
+ unsigned tmp_dig_len = *digest_len;
+ HMAC_Update(&ctx, (const unsigned char *)wire, wire_len);
+ HMAC_Final(&ctx, digest, &tmp_dig_len);
+ *digest_len = tmp_dig_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr)
+{
+ if (!tsig_rr) {
+ return KNOT_EBADARG;
+ }
+
+ /* Get the time signed and fudge values. */
+ uint64_t time_signed = tsig_rdata_time_signed(tsig_rr);
+ if (time_signed == 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+ uint16_t fudge = tsig_rdata_fudge(tsig_rr);
+ if (fudge == 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ /* Get the current time. */
+ time_t curr_time = time(NULL);
+
+ /*!< \todo bleeding eyes. */
+ if (difftime(curr_time, (time_t)time_signed) > fudge) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_write_tsig_timers(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ // put time signed
+ knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr));
+
+ // put fudge
+ knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr));
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_write_tsig_variables(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ /* Copy TSIG variables - starting with key name. */
+ const knot_dname_t *tsig_owner = knot_rrset_owner(tsig_rr);
+ if (!tsig_owner) {
+ dbg_tsig("TSIG: write variables: no owner.\n");
+ return KNOT_EBADARG;
+ }
+
+ int offset = 0;
+
+ memcpy(wire + offset, knot_dname_name(tsig_owner),
+ sizeof(uint8_t) * knot_dname_size(tsig_owner));
+ dbg_tsig("TSIG: write variables: written owner (tsig alg): \n");
+ /*knot_rrset_class(tsig_rr));*/
+ dbg_tsig_hex_detail(wire + offset, knot_dname_size(tsig_owner));
+ offset += knot_dname_size(tsig_owner);
+
+ /*!< \todo which order? */
+
+ /* Copy class. */
+ knot_wire_write_u16(wire + offset, knot_rrset_class(tsig_rr));
+ dbg_tsig("TSIG: write variables: written CLASS: %u - ",
+ knot_rrset_class(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset, sizeof(uint16_t));
+ offset += sizeof(uint16_t);
+
+ /* Copy TTL - always 0. */
+ knot_wire_write_u32(wire + offset, knot_rrset_ttl(tsig_rr));
+ dbg_tsig("TSIG: write variables: written TTL: %u - ",
+ knot_rrset_ttl(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset, sizeof(uint32_t));
+ offset += sizeof(uint32_t);
+
+ /* Copy alg name. */
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ if (!alg_name) {
+ dbg_tsig("TSIG: write variables: no algorithm name.\n");
+ return KNOT_EBADARG;
+ }
+// alg_name = knot_dname_new_from_str("HMAC-MD5.SIG-ALG.REG.INT.",
+ //strlen("HMAC-MD5.SIG-ALG.REG.INT."),
+ //NULL);
+
+ memcpy(wire + offset, knot_dname_name(alg_name),
+ sizeof(uint8_t) * knot_dname_size(alg_name));
+ offset += knot_dname_size(alg_name);
+ dbg_tsig_detail("TSIG: write variables: written alg name: %s\n",
+ knot_dname_to_str(alg_name));
+
+ /* Following data are written in network order. */
+ /* Time signed. */
+ knot_wire_write_u48(wire + offset, tsig_rdata_time_signed(tsig_rr));
+ offset += 6;
+ dbg_tsig_detail("TSIG: write variables: time signed: %llu - ",
+ tsig_rdata_time_signed(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset - 6, 6);
+ /* Fudge. */
+ knot_wire_write_u16(wire + offset, tsig_rdata_fudge(tsig_rr));
+ offset += sizeof(uint16_t);
+ dbg_tsig_detail("TSIG: write variables: fudge: %hu\n",
+ tsig_rdata_fudge(tsig_rr));
+ /* TSIG error. */
+ knot_wire_write_u16(wire + offset, tsig_rdata_error(tsig_rr));
+ offset += sizeof(uint16_t);
+ /* Get other data length. */
+ uint16_t other_data_length = tsig_rdata_other_data_length(tsig_rr);
+ /* Get other data. */
+ const uint8_t *other_data = tsig_rdata_other_data(tsig_rr);
+ if (!other_data) {
+ dbg_tsig("TSIG: write variables: no other data.\n");
+ return KNOT_EBADARG;
+ }
+
+ /*
+ * We cannot write the whole other_data, as it contains its length in
+ * machine order.
+ */
+ knot_wire_write_u16(wire + offset, other_data_length);
+ offset += sizeof(uint16_t);
+
+ /* Skip the length. */
+ dbg_tsig_detail("Copying other data.\n");
+ memcpy(wire + offset, other_data, other_data_length);
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_wire_write_timers(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr));
+ knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr));
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
+ /*size_t msg_max_len, */const uint8_t *request_mac,
+ size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_key_t *key)
+{
+ if (!msg || !key || digest_len == NULL) {
+ dbg_tsig("TSIG: create wire: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ /* Create tmp TSIG. */
+ int ret = KNOT_EOK;
+// knot_rrset_t *tmp_tsig =
+// knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+// if (!tmp_tsig) {
+// return KNOT_ENOMEM;
+// }
+
+// tsig_rdata_store_current_time(tmp_tsig);
+
+ /*
+ * Create tmp wire, it should contain message
+ * plus request mac plus tsig varibles.
+ */
+ dbg_tsig("Counting wire size: %zu, %zu, %zu.\n",
+ msg_len, request_mac_len,
+ tsig_rdata_tsig_variables_length(tmp_tsig));
+ size_t wire_len = sizeof(uint8_t) *
+ (msg_len + request_mac_len + ((request_mac_len > 0)
+ ? 2 : 0) +
+ tsig_rdata_tsig_variables_length(tmp_tsig));
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire, 0, wire_len);
+
+ uint8_t *pos = wire;
+
+ /* Copy the request MAC - should work even if NULL. */
+ if (request_mac_len > 0) {
+ dbg_tsig_detail("Copying request MAC size\n");
+ knot_wire_write_u16(pos, request_mac_len);
+ pos += 2;
+ }
+ dbg_tsig("Copying request mac.\n");
+ memcpy(pos, request_mac, sizeof(uint8_t) * request_mac_len);
+ dbg_tsig_detail("TSIG: create wire: request mac: ");
+ dbg_tsig_hex_detail(pos, request_mac_len);
+ pos += request_mac_len;
+ /* Copy the original message. */
+ dbg_tsig("Copying original message.\n");
+ memcpy(pos, msg, msg_len);
+ dbg_tsig_detail("TSIG: create wire: original message: \n");
+ //dbg_tsig_hex_detail(pos, msg_len);
+ pos += msg_len;
+ /* Copy TSIG variables. */
+ dbg_tsig("Writing TSIG variables.\n");
+ ret = knot_tsig_write_tsig_variables(pos, tmp_tsig);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to write TSIG "
+ "variables: %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Compute digest. */
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest, digest_len, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to compute digest: %s\n",
+ knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+// assert(digest_tmp_len > 0);
+ free(wire);
+
+// if (digest_tmp_len > *digest_len) {
+// *digest_len = 0;
+// return KNOT_ESPACE;
+// }
+
+// knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ // everything went ok, save the digest to the output parameter
+// memcpy(digest, digest_tmp, digest_tmp_len);
+// *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
+ const uint8_t *prev_mac,
+ size_t prev_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_key_t *key)
+{
+ if (!msg || !key || digest_len == NULL) {
+ dbg_tsig("TSIG: create wire: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ /* Create tmp TSIG. */
+ int ret = KNOT_EOK;
+
+ /*
+ * Create tmp wire, it should contain message
+ * plus request mac plus tsig varibles.
+ */
+ dbg_tsig("Counting wire size: %zu, %zu, %zu.\n",
+ msg_len, prev_mac_len,
+ tsig_rdata_tsig_timers_length());
+ size_t wire_len = sizeof(uint8_t) *
+ (msg_len + prev_mac_len +
+ tsig_rdata_tsig_timers_length());
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire, 0, wire_len);
+
+ /* Copy the request MAC - should work even if NULL. */
+ dbg_tsig("Copying request mac.\n");
+ memcpy(wire, prev_mac, sizeof(uint8_t) * prev_mac_len);
+ dbg_tsig_detail("TSIG: create wire: request mac: ");
+ dbg_tsig_hex_detail(wire, prev_mac_len);
+ /* Copy the original message. */
+ dbg_tsig("Copying original message.\n");
+ memcpy(wire + prev_mac_len, msg, msg_len);
+ dbg_tsig_detail("TSIG: create wire: original message: \n");
+ //dbg_tsig_hex_detail(wire + prev_mac_len, msg_len);
+ /* Copy TSIG variables. */
+
+ dbg_tsig("Writing TSIG timers.\n");
+ ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len,
+ tmp_tsig);
+// ret = knot_tsig_write_tsig_variables(wire + prev_mac_len + msg_len,
+// tmp_tsig);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to write TSIG "
+ "timers: %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Compute digest. */
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest, digest_len, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to compute digest: %s\n",
+ knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+ free(wire);
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
+ size_t msg_max_len, const uint8_t *request_mac,
+ size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_dname_t *key_name_copy = knot_dname_deep_copy(key->name);
+ if (!key_name_copy) {
+ dbg_tsig_detail("TSIG: key_name_copy = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_t *tmp_tsig =
+ knot_rrset_new(key_name_copy,
+ KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+ if (!tmp_tsig) {
+ dbg_tsig_detail("TSIG: tmp_tsig = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ /* Create rdata for TSIG RR. */
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (!rdata) {
+ dbg_tsig_detail("TSIG: rdata = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_add_rdata(tmp_tsig, rdata);
+
+ /* Create items for TSIG RR. */
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(KNOT_RRTYPE_TSIG);
+ assert(desc);
+
+ knot_rdata_item_t *items =
+ malloc(sizeof(knot_rdata_item_t) * desc->length);
+ if (!items) {
+ dbg_tsig_detail("TSIG: items = NULL\n");
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(items, 0, sizeof(knot_rdata_item_t) * desc->length);
+
+ int ret = knot_rdata_set_items(rdata, items, desc->length);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret));
+ return ret;
+ }
+ free(items);
+
+ tsig_rdata_set_alg(tmp_tsig, key->algorithm);
+ tsig_rdata_store_current_time(tmp_tsig);
+ tsig_rdata_set_fudge(tmp_tsig, 300);
+
+ /* Set original ID */
+ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+
+ /* Set error */
+ /*! \todo [TSIG] Set error and other data if appropriate. */
+ tsig_rdata_set_tsig_error(tmp_tsig, 0);
+
+ /* Set other len. */
+ tsig_rdata_set_other_data(tmp_tsig, 0, 0);
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+
+ dbg_tsig_detail("tmp_tsig before sign_wire():\n");
+ knot_rrset_dump(tmp_tsig, 0);
+
+ ret = knot_tsig_create_sign_wire(msg, *msg_len, /*msg_max_len,*/
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tmp_tsig, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: could not create wire or sign wire: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Set the digest. */
+ size_t tsig_wire_len = msg_max_len - *msg_len;
+ int rr_count = 0;
+ tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
+
+ //knot_rrset_dump(tmp_tsig, 1);
+
+ /* Write RRSet to wire */
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ &tsig_wire_len, &rr_count);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ *msg_len += tsig_wire_len;
+
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ // everything went ok, save the digest to the output parameter
+ memcpy(digest, digest_tmp, digest_tmp_len);
+ *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *prev_digest, size_t prev_digest_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!msg || !msg_len || !key || !key || !digest || !digest_len) {
+ return KNOT_EBADARG;
+ }
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+
+ /* Create tmp TSIG. */
+ knot_rrset_t *tmp_tsig =
+ knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+ if (!tmp_tsig) {
+ return KNOT_ENOMEM;
+ }
+
+ tsig_rdata_store_current_time(tmp_tsig);
+
+ /* Create wire to be signed. */
+ size_t wire_len = prev_digest_len + *msg_len + KNOT_TSIG_TIMERS_LENGTH;
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+ memset(wire, 0, wire_len);
+
+ /* Write previous digest. */
+ memcpy(wire, prev_digest, sizeof(uint8_t) * prev_digest_len);
+ /* Write original message. */
+ memcpy(msg + prev_digest_len, msg, *msg_len);
+ /* Write timers. */
+ knot_tsig_wire_write_timers(msg + prev_digest_len + *msg_len, tmp_tsig);
+
+ int ret = 0;
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest_tmp, &digest_tmp_len, key);
+ if (ret != KNOT_EOK) {
+ *digest_len = 0;
+ return ret;
+ }
+
+ if (digest_tmp_len > *digest_len) {
+ *digest_len = 0;
+ return KNOT_ESPACE;
+ }
+
+ free(wire);
+
+ /* Set the MAC. */
+ tsig_rdata_set_mac(tmp_tsig, *digest_len, digest);
+
+ size_t tsig_wire_size = msg_max_len - *msg_len;
+ int rr_count = 0;
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ &tsig_wire_size, &rr_count);
+ if (ret != KNOT_EOK) {
+ *digest_len = 0;
+ return ret;
+ }
+
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ *msg_len += tsig_wire_size;
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ memcpy(digest, digest_tmp, digest_tmp_len);
+ *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac,
+ size_t request_mac_len,
+ const knot_key_t *tsig_key,
+ int use_times)
+{
+ if (!tsig_rr || !wire || !tsig_key) {
+ return KNOT_EBADARG;
+ }
+
+ /* Check time signed. */
+ int ret = knot_tsig_check_time_signed(tsig_rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: time checked.\n");
+
+ /* Check that libknot knows the algorithm. */
+ ret = knot_tsig_check_algorithm(tsig_rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: algorithm checked.\n");
+
+ /* Check that key is valid, ie. the same as given in args. */
+ ret = knot_tsig_check_key(tsig_rr, tsig_key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: key validity checked.\n");
+
+ /* Time OK algorithm OK, key name OK - do digest. */
+ /* Calculate the size of TSIG RR. */
+ size_t tsig_len = tsig_wire_actsize(tsig_rr);
+
+ dbg_tsig_detail("TSIG: check digest: wire before strip: \n");
+ //dbg_tsig_hex_detail(wire, size);
+
+ /* Strip the TSIG. */
+ size -= tsig_len;
+
+ dbg_tsig_detail("TSIG: check digest: wire after strip (stripped %zu):\n",
+ tsig_len);
+ //dbg_tsig_hex_detail(wire, size);
+
+ uint8_t *wire_to_sign = malloc(sizeof(uint8_t) * size);
+ if (!wire_to_sign) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire_to_sign, 0, sizeof(uint8_t) * size);
+ memcpy(wire_to_sign, wire, size);
+
+ /* Decrease arcount. */
+ knot_wire_set_arcount(wire_to_sign,
+ knot_wire_get_arcount(wire_to_sign) - 1);
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+ assert(tsig_rr->rdata);
+
+ if (use_times) {
+ ret = knot_tsig_create_sign_wire_next(wire_to_sign, size,
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tsig_rr, tsig_key);
+ } else {
+ ret = knot_tsig_create_sign_wire(wire_to_sign, size,
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tsig_rr, tsig_key);
+ }
+
+ assert(tsig_rr->rdata);
+ free(wire_to_sign);
+
+ if (ret != KNOT_EOK) {
+ dbg_tsig("Failed to create wire format for checking: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+// uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+// size_t digest_tmp_len = 0;
+// ret = knot_tsig_compute_digest(wire, size, digest_tmp,
+// &digest_tmp_len, tsig_key);
+// if (ret != KNOT_EOK) {
+// dbg_tsig("TSIG: digest could not be calculated\n");
+// return ret;
+// }
+
+ dbg_tsig("TSIG: digest calculated\n");
+
+ /* Compare MAC from TSIG RR RDATA with just computed digest. */
+
+ /*!< \todo move to function. */
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ tsig_algorithm_t alg = tsig_alg_from_name(alg_name);
+
+ /*! \todo [TSIG] TRUNCATION */
+ uint16_t mac_length = tsig_rdata_mac_length(tsig_rr);
+ const uint8_t *tsig_mac = tsig_rdata_mac(tsig_rr);
+
+ if (mac_length != tsig_alg_digest_length(alg)) {
+ dbg_tsig("TSIG: calculated digest length and given length do not match!\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+// assert(tsig_alg_digest_length(alg) == mac_length);
+
+ dbg_tsig("TSIG: calc digest : ");
+ dbg_tsig_hex(digest_tmp, digest_tmp_len);
+
+ dbg_tsig("TSIG: given digest: ");
+ dbg_tsig_hex(tsig_mac, mac_length);
+
+ if (strncasecmp((char *)(tsig_mac), (char *)digest_tmp,
+ mac_length) != 0) {
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const knot_key_t *tsig_key)
+{
+ dbg_tsig_verb("tsig_server_check()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0);
+}
+
+int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac, size_t request_mac_len,
+ const knot_key_t *tsig_key)
+{
+ dbg_tsig_verb("tsig_client_check()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, request_mac,
+ request_mac_len, tsig_key, 0);
+}
+
+int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *prev_digest,
+ size_t prev_digest_len,
+ const knot_key_t *tsig_key)
+{
+// return knot_tsig_client_check(tsig_rr, wire, size, prev_digest,
+// prev_digest_len, tsig_key);
+ dbg_tsig_verb("tsig_client_check_next()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, prev_digest,
+ prev_digest_len, tsig_key, 1);
+ return KNOT_ENOTSUP;
+}