summaryrefslogtreecommitdiffstats
path: root/src/libdnssec/nsec
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libdnssec/nsec.h155
-rw-r--r--src/libdnssec/nsec/bitmap.c142
-rw-r--r--src/libdnssec/nsec/hash.c125
-rw-r--r--src/libdnssec/nsec/nsec.c130
4 files changed, 552 insertions, 0 deletions
diff --git a/src/libdnssec/nsec.h b/src/libdnssec/nsec.h
new file mode 100644
index 0000000..19808b0
--- /dev/null
+++ b/src/libdnssec/nsec.h
@@ -0,0 +1,155 @@
+/* Copyright (C) 2020 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup nsec
+ *
+ * \brief NSEC bitmap and NSEC3 hash computation API.
+ *
+ * The module provides interface for computation of NSEC3 hashes and for
+ * construction of bit maps used in NSEC and NSEC3 records.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <libdnssec/binary.h>
+
+/*!
+ * DNSSEC NSEC3 algorithm numbers.
+ */
+typedef enum dnssec_nsec_algorithm {
+ DNSSEC_NSEC3_ALGORITHM_UNKNOWN = 0,
+ DNSSEC_NSEC3_ALGORITHM_SHA1 = 1,
+} dnssec_nsec3_algorithm_t;
+
+/*!
+ * DNSSEC NSEC3 parameters.
+ */
+typedef struct dnssec_nsec3_params {
+ dnssec_nsec3_algorithm_t algorithm; /*!< NSEC3 algorithm. */
+ uint8_t flags; /*!< NSEC3 flags. */
+ uint16_t iterations; /*!< NSEC3 iterations count. */
+ dnssec_binary_t salt; /*!< NSEC3 salt. */
+} dnssec_nsec3_params_t;
+
+/*!
+ * Free NSEC3 parameters.
+ */
+void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params);
+
+/*!
+ * Parse NSEC3 parameters from NSEC3PARAM RDATA.
+ *
+ * \param params Output parameters.
+ * \param rdata NSEC3PARAM RDATA.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params,
+ const dnssec_binary_t *rdata);
+
+/*!
+ * Check if NSEC3 parameters match.
+ *
+ * \param params1 NSEC3 parameters 1.
+ * \param params2 NSEC3 parameters 2.
+ *
+ * \return True if match or if both NULL.
+ */
+bool dnssec_nsec3_params_match(const dnssec_nsec3_params_t *params1,
+ const dnssec_nsec3_params_t *params2);
+
+/*!
+ * Check whether a given NSEC bitmap contains a given RR type.
+ *
+ * \param bitmap Bitmap of an NSEC record.
+ * \param size Size of the bitmap.
+ * \param type RR type to check for.
+ *
+ * \return true if bitmap contains type, false otherwise.
+ */
+bool dnssec_nsec_bitmap_contains(const uint8_t *bitmap, uint16_t size, uint16_t type);
+
+/*!
+ * Compute NSEC3 hash for given data.
+ *
+ * \todo Input data must be converted to lowercase!
+ *
+ * \param[in] data Data to be hashed (usually domain name).
+ * \param[in] params NSEC3 parameters.
+ * \param[out] hash Computed hash (will be allocated or resized).
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_nsec3_hash(const dnssec_binary_t *data,
+ const dnssec_nsec3_params_t *params,
+ dnssec_binary_t *hash);
+
+/*!
+ * Get length of raw NSEC3 hash for a given algorithm.
+ *
+ * \param algorithm NSEC3 algorithm number.
+ *
+ * \return Length of raw NSEC3 hash, zero on error.
+ */
+size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm);
+
+struct dnssec_nsec_bitmap;
+
+/*!
+ * Context for encoding of RR types bitmap used in NSEC/NSEC3.
+ */
+typedef struct dnssec_nsec_bitmap dnssec_nsec_bitmap_t;
+
+/*!
+ * Allocate new bit map encoding context.
+ */
+dnssec_nsec_bitmap_t *dnssec_nsec_bitmap_new(void);
+
+/*!
+ * Clear existing bit map encoding context.
+ */
+void dnssec_nsec_bitmap_clear(dnssec_nsec_bitmap_t *bitmap);
+
+/*!
+ * Free bit map encoding context.
+ */
+void dnssec_nsec_bitmap_free(dnssec_nsec_bitmap_t *bitmap);
+
+/*!
+ * Add one RR type into the bitmap.
+ */
+void dnssec_nsec_bitmap_add(dnssec_nsec_bitmap_t *bitmap, uint16_t type);
+
+/*!
+ * Compute the size of the encoded bitmap.
+ */
+size_t dnssec_nsec_bitmap_size(const dnssec_nsec_bitmap_t *bitmap);
+
+/*!
+ * Write encoded bitmap into the given buffer.
+ */
+void dnssec_nsec_bitmap_write(const dnssec_nsec_bitmap_t *bitmap, uint8_t *output);
+
+/*! @} */
diff --git a/src/libdnssec/nsec/bitmap.c b/src/libdnssec/nsec/bitmap.c
new file mode 100644
index 0000000..05f8cfa
--- /dev/null
+++ b/src/libdnssec/nsec/bitmap.c
@@ -0,0 +1,142 @@
+/* Copyright (C) 2018 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 <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libdnssec/nsec.h"
+#include "libdnssec/shared/shared.h"
+
+#define BITMAP_WINDOW_SIZE 256
+#define BITMAP_WINDOW_BYTES (BITMAP_WINDOW_SIZE/CHAR_BIT)
+#define BITMAP_WINDOW_COUNT 256
+
+/*!
+ * One window of an NSEC bitmap.
+ */
+typedef struct window {
+ uint8_t used;
+ uint8_t data[BITMAP_WINDOW_BYTES];
+} window_t;
+
+struct dnssec_nsec_bitmap {
+ int used;
+ window_t windows[BITMAP_WINDOW_COUNT];
+};
+
+/* -- public API ----------------------------------------------------------- */
+
+/*!
+ * Allocate new bit map encoding context.
+ */
+_public_
+dnssec_nsec_bitmap_t *dnssec_nsec_bitmap_new(void)
+{
+ dnssec_nsec_bitmap_t *bitmap = malloc(sizeof(*bitmap));
+ if (!bitmap) {
+ return NULL;
+ }
+
+ dnssec_nsec_bitmap_clear(bitmap);
+
+ return bitmap;
+}
+
+/*!
+ * Clear existing bit map encoding context.
+ */
+_public_
+void dnssec_nsec_bitmap_clear(dnssec_nsec_bitmap_t *bitmap)
+{
+ clear_struct(bitmap);
+}
+
+/*!
+ * Free bit map encoding context.
+ */
+_public_
+void dnssec_nsec_bitmap_free(dnssec_nsec_bitmap_t *bitmap)
+{
+ free(bitmap);
+}
+
+/*!
+ * Add one RR type into the bitmap.
+ */
+_public_
+void dnssec_nsec_bitmap_add(dnssec_nsec_bitmap_t *bitmap, uint16_t type)
+{
+ int win = type / BITMAP_WINDOW_SIZE;
+ int bit = type % BITMAP_WINDOW_SIZE;
+
+ if (bitmap->used <= win) {
+ bitmap->used = win + 1;
+ }
+
+ int win_byte = bit / CHAR_BIT;
+ int win_bit = bit % CHAR_BIT;
+
+ window_t *window = &bitmap->windows[win];
+ window->data[win_byte] |= 0x80 >> win_bit;
+ if (window->used <= win_byte) {
+ window->used = win_byte + 1;
+ }
+}
+
+/*!
+ * Compute the size of the encoded bitmap.
+ */
+_public_
+size_t dnssec_nsec_bitmap_size(const dnssec_nsec_bitmap_t *bitmap)
+{
+ size_t result = 0;
+
+ for (int i = 0; i < bitmap->used; i++) {
+ int used = bitmap->windows[i].used;
+ if (used == 0) {
+ continue;
+ }
+
+ result += 2 + used; // windows number, window size, data
+ }
+
+ return result;
+}
+
+/*!
+ * Write encoded bitmap into the given buffer.
+ */
+_public_
+void dnssec_nsec_bitmap_write(const dnssec_nsec_bitmap_t *bitmap, uint8_t *output)
+{
+ uint8_t *write_ptr = output;
+ for (int win = 0; win < bitmap->used; win++) {
+ int used = bitmap->windows[win].used;
+ if (used == 0) {
+ continue;
+ }
+
+ *write_ptr = (uint8_t)win;
+ write_ptr += 1;
+
+ *write_ptr = (uint8_t)used;
+ write_ptr += 1;
+
+ memmove(write_ptr, bitmap->windows[win].data, used);
+ write_ptr += used;
+ }
+}
diff --git a/src/libdnssec/nsec/hash.c b/src/libdnssec/nsec/hash.c
new file mode 100644
index 0000000..b5d46ab
--- /dev/null
+++ b/src/libdnssec/nsec/hash.c
@@ -0,0 +1,125 @@
+/* Copyright (C) 2018 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 <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <string.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/nsec.h"
+#include "libdnssec/shared/shared.h"
+
+/*!
+ * Compute NSEC3 hash for given data and algorithm.
+ *
+ * \see RFC 5155
+ *
+ * \todo Input data should be converted to lowercase.
+ */
+static int nsec3_hash(gnutls_digest_algorithm_t algorithm, int iterations,
+ const dnssec_binary_t *salt, const dnssec_binary_t *data,
+ dnssec_binary_t *hash)
+{
+ assert(salt);
+ assert(data);
+ assert(hash);
+
+ int hash_size = gnutls_hash_get_len(algorithm);
+ if (hash_size <= 0) {
+ return DNSSEC_NSEC3_HASHING_ERROR;
+ }
+
+ int result = dnssec_binary_resize(hash, hash_size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ _cleanup_hash_ gnutls_hash_hd_t digest = NULL;
+ result = gnutls_hash_init(&digest, algorithm);
+ if (result < 0) {
+ return DNSSEC_NSEC3_HASHING_ERROR;
+ }
+
+ const uint8_t *in = data->data;
+ size_t in_size = data->size;
+
+ for (int i = 0; i <= iterations; i++) {
+ result = gnutls_hash(digest, in, in_size);
+ if (result < 0) {
+ return DNSSEC_NSEC3_HASHING_ERROR;
+ }
+
+ result = gnutls_hash(digest, salt->data, salt->size);
+ if (result < 0) {
+ return DNSSEC_NSEC3_HASHING_ERROR;
+ }
+
+ gnutls_hash_output(digest, hash->data);
+
+ in = hash->data;
+ in_size = hash->size;
+ }
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Get GnuTLS digest algorithm from DNSSEC algorithm number.
+ */
+static gnutls_digest_algorithm_t algorithm_d2g(dnssec_nsec3_algorithm_t dnssec)
+{
+ switch (dnssec) {
+ case DNSSEC_NSEC3_ALGORITHM_SHA1: return GNUTLS_DIG_SHA1;
+ default: return GNUTLS_DIG_UNKNOWN;
+ }
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+/*!
+ * Compute NSEC3 hash for given data.
+ */
+_public_
+int dnssec_nsec3_hash(const dnssec_binary_t *data,
+ const dnssec_nsec3_params_t *params,
+ dnssec_binary_t *hash)
+{
+ if (!data || !params || !hash) {
+ return DNSSEC_EINVAL;
+ }
+
+ gnutls_digest_algorithm_t algorithm = algorithm_d2g(params->algorithm);
+ if (algorithm == GNUTLS_DIG_UNKNOWN) {
+ return DNSSEC_INVALID_NSEC3_ALGORITHM;
+ }
+
+ return nsec3_hash(algorithm, params->iterations, &params->salt, data, hash);
+}
+
+/*!
+ * Get length of raw NSEC3 hash for a given algorithm.
+ */
+_public_
+size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm)
+{
+ gnutls_digest_algorithm_t gnutls = algorithm_d2g(algorithm);
+ if (gnutls == GNUTLS_DIG_UNKNOWN) {
+ return 0;
+ }
+
+ return gnutls_hash_get_len(gnutls);
+}
diff --git a/src/libdnssec/nsec/nsec.c b/src/libdnssec/nsec/nsec.c
new file mode 100644
index 0000000..2e71598
--- /dev/null
+++ b/src/libdnssec/nsec/nsec.c
@@ -0,0 +1,130 @@
+/* Copyright (C) 2018 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 "libdnssec/nsec.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/shared/binary_wire.h"
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+
+/*!
+ * Free NSEC3 parameters.
+ */
+_public_
+void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params)
+{
+ if (!params) {
+ return;
+ }
+
+ dnssec_binary_free(&params->salt);
+ clear_struct(params);
+}
+
+/*!
+ * Parse NSEC3 parameters from NSEC3PARAM RDATA.
+ *
+ * \see RFC 5155 (section 4.2)
+ */
+_public_
+int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params,
+ const dnssec_binary_t *rdata)
+{
+ if (!params || !rdata || !rdata->data) {
+ return DNSSEC_EINVAL;
+ }
+
+ dnssec_nsec3_params_t new_params = { 0 };
+
+ wire_ctx_t wire = binary_init(rdata);
+
+ if (wire_ctx_available(&wire) < 5) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ new_params.algorithm = wire_ctx_read_u8(&wire);
+ new_params.flags = wire_ctx_read_u8(&wire);
+ new_params.iterations = wire_ctx_read_u16(&wire);
+ new_params.salt.size = wire_ctx_read_u8(&wire);
+
+ if (wire_ctx_available(&wire) != new_params.salt.size) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ new_params.salt.data = malloc(new_params.salt.size);
+ if (new_params.salt.data == NULL) {
+ return DNSSEC_ENOMEM;
+ }
+
+ binary_read(&wire, &new_params.salt);
+ assert(wire_ctx_offset(&wire) == rdata->size);
+
+ *params = new_params;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+bool dnssec_nsec_bitmap_contains(const uint8_t *bitmap, uint16_t size, uint16_t type)
+{
+ if (!bitmap || size == 0) {
+ return false;
+ }
+
+ const uint8_t type_hi = (type >> 8); // Which window block contains type.
+ const uint8_t type_lo = (type & 0xff);
+ const uint8_t bitmap_idx = (type_lo >> 3); // Which byte in the window block contains type.
+ const uint8_t bit_mask = 1 << (7 - (type_lo & 0x07)); // Which bit in the byte represents type.
+
+ size_t bitmap_pos = 0;
+ while (bitmap_pos + 3 <= size) {
+ uint8_t block_idx = bitmap[bitmap_pos++]; // Skip window block No.
+ uint8_t block_size = bitmap[bitmap_pos++]; // Skip window block size.
+
+ // Size checks.
+ if (block_size == 0 || bitmap_pos + block_size > size) {
+ return false;
+ }
+
+ // Check whether we found the correct window block.
+ if (block_idx == type_hi) {
+ if (bitmap_idx < block_size) {
+ // Check if the bit for type is set.
+ return bitmap[bitmap_pos + bitmap_idx] & bit_mask;
+ }
+ return false;
+ } else {
+ bitmap_pos += block_size;
+ }
+ }
+
+ return false;
+}
+
+_public_
+bool dnssec_nsec3_params_match(const dnssec_nsec3_params_t *params1,
+ const dnssec_nsec3_params_t *params2)
+{
+ if (params1 != NULL && params2 != NULL) {
+ return (params1->algorithm == params2->algorithm &&
+ params1->flags == params2->flags &&
+ params1->iterations == params2->iterations &&
+ dnssec_binary_cmp(&params1->salt, &params2->salt) == 0);
+ } else {
+ return (params1 == NULL && params2 == NULL);
+ }
+}