aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/dnscrypt
diff options
context:
space:
mode:
authorErik de Castro Lopo <erikd@mega-nerd.com>2017-06-16 20:16:05 +1000
committerErik de Castro Lopo <erikd@mega-nerd.com>2017-06-17 23:04:00 +1000
commita85b5759f34c0c4110a479a8b5fa606f15ed9b23 (patch)
tree518cb8346249a42fd2aa8a78c09c3631e14db6aa /external/unbound/dnscrypt
parentMerge pull request #2059 (diff)
downloadmonero-a85b5759f34c0c4110a479a8b5fa606f15ed9b23.tar.xz
Upgrade unbound library
These files were pulled from the 1.6.3 release tarball. This new version builds against OpenSSL version 1.1 which will be the default in the new Debian Stable which is due to be released RealSoonNow (tm).
Diffstat (limited to 'external/unbound/dnscrypt')
-rw-r--r--external/unbound/dnscrypt/cert.h32
-rw-r--r--external/unbound/dnscrypt/dnscrypt.c531
-rw-r--r--external/unbound/dnscrypt/dnscrypt.h102
-rw-r--r--external/unbound/dnscrypt/dnscrypt.m425
-rw-r--r--external/unbound/dnscrypt/dnscrypt_config.h17
5 files changed, 707 insertions, 0 deletions
diff --git a/external/unbound/dnscrypt/cert.h b/external/unbound/dnscrypt/cert.h
new file mode 100644
index 000000000..044f49f26
--- /dev/null
+++ b/external/unbound/dnscrypt/cert.h
@@ -0,0 +1,32 @@
+#ifndef UNBOUND_DNSCRYPT_CERT_H
+#define UNBOUND_DNSCRYPT_CERT_H
+
+/**
+ * \file
+ * certificate type for dnscrypt for use in other header files
+ */
+
+#include <sodium.h>
+#define CERT_MAGIC_CERT "DNSC"
+#define CERT_MAJOR_VERSION 1
+#define CERT_MINOR_VERSION 0
+#define CERT_OLD_MAGIC_HEADER "7PYqwfzt"
+
+#define CERT_FILE_EXPIRE_DAYS 365
+
+struct SignedCert {
+ uint8_t magic_cert[4];
+ uint8_t version_major[2];
+ uint8_t version_minor[2];
+
+ // Signed Content
+ uint8_t server_publickey[crypto_box_PUBLICKEYBYTES];
+ uint8_t magic_query[8];
+ uint8_t serial[4];
+ uint8_t ts_begin[4];
+ uint8_t ts_end[4];
+ uint8_t end[64];
+};
+
+
+#endif
diff --git a/external/unbound/dnscrypt/dnscrypt.c b/external/unbound/dnscrypt/dnscrypt.c
new file mode 100644
index 000000000..56903e651
--- /dev/null
+++ b/external/unbound/dnscrypt/dnscrypt.c
@@ -0,0 +1,531 @@
+
+#include "config.h"
+#include <stdlib.h>
+#include <fcntl.h>
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h>
+#include "sldns/sbuffer.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+#include "util/netevent.h"
+#include "util/log.h"
+
+#include "dnscrypt/cert.h"
+#include "dnscrypt/dnscrypt.h"
+
+#include <ctype.h>
+
+/**
+ * \file
+ * dnscrypt functions for encrypting DNS packets.
+ */
+
+#define DNSCRYPT_QUERY_BOX_OFFSET \
+ (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES)
+
+// 8 bytes: magic header (CERT_MAGIC_HEADER)
+// 12 bytes: the client's nonce
+// 12 bytes: server nonce extension
+// 16 bytes: Poly1305 MAC (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES)
+
+#define DNSCRYPT_REPLY_BOX_OFFSET \
+ (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES + crypto_box_HALF_NONCEBYTES)
+
+/**
+ * Decrypt a query using the keypair that was found using dnsc_find_keypair.
+ * The client nonce will be extracted from the encrypted query and stored in
+ * client_nonce, a shared secret will be computed and stored in nmkey and the
+ * buffer will be decrypted inplace.
+ * \param[in] keypair the keypair that matches this encrypted query.
+ * \param[in] client_nonce where the client nonce will be stored.
+ * \param[in] nmkey where the shared secret key will be written.
+ * \param[in] buffer the encrypted buffer.
+ * \return 0 on success.
+ */
+static int
+dnscrypt_server_uncurve(const KeyPair *keypair,
+ uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],
+ uint8_t nmkey[crypto_box_BEFORENMBYTES],
+ struct sldns_buffer* buffer)
+{
+ size_t len = sldns_buffer_limit(buffer);
+ uint8_t *const buf = sldns_buffer_begin(buffer);
+ uint8_t nonce[crypto_box_NONCEBYTES];
+ struct dnscrypt_query_header *query_header;
+
+ if (len <= DNSCRYPT_QUERY_HEADER_SIZE) {
+ return -1;
+ }
+
+ query_header = (struct dnscrypt_query_header *)buf;
+ memcpy(nmkey, query_header->publickey, crypto_box_PUBLICKEYBYTES);
+ if (crypto_box_beforenm(nmkey, nmkey, keypair->crypt_secretkey) != 0) {
+ return -1;
+ }
+
+ memcpy(nonce, query_header->nonce, crypto_box_HALF_NONCEBYTES);
+ memset(nonce + crypto_box_HALF_NONCEBYTES, 0, crypto_box_HALF_NONCEBYTES);
+
+ sldns_buffer_set_at(buffer,
+ DNSCRYPT_QUERY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
+ 0, crypto_box_BOXZEROBYTES);
+
+ if (crypto_box_open_afternm
+ (buf + DNSCRYPT_QUERY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
+ buf + DNSCRYPT_QUERY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
+ len - DNSCRYPT_QUERY_BOX_OFFSET + crypto_box_BOXZEROBYTES, nonce,
+ nmkey) != 0) {
+ return -1;
+ }
+
+ while (*sldns_buffer_at(buffer, --len) == 0)
+ ;
+
+ if (*sldns_buffer_at(buffer, len) != 0x80) {
+ return -1;
+ }
+
+ memcpy(client_nonce, nonce, crypto_box_HALF_NONCEBYTES);
+ memmove(sldns_buffer_begin(buffer),
+ sldns_buffer_at(buffer, DNSCRYPT_QUERY_HEADER_SIZE),
+ len - DNSCRYPT_QUERY_HEADER_SIZE);
+
+ sldns_buffer_set_position(buffer, 0);
+ sldns_buffer_set_limit(buffer, len - DNSCRYPT_QUERY_HEADER_SIZE);
+
+ return 0;
+}
+
+
+/**
+ * Add random padding to a buffer, according to a client nonce.
+ * The length has to depend on the query in order to avoid reply attacks.
+ *
+ * @param buf a buffer
+ * @param len the initial size of the buffer
+ * @param max_len the maximum size
+ * @param nonce a nonce, made of the client nonce repeated twice
+ * @param secretkey
+ * @return the new size, after padding
+ */
+size_t
+dnscrypt_pad(uint8_t *buf, const size_t len, const size_t max_len,
+ const uint8_t *nonce, const uint8_t *secretkey)
+{
+ uint8_t *buf_padding_area = buf + len;
+ size_t padded_len;
+ uint32_t rnd;
+
+ // no padding
+ if (max_len < len + DNSCRYPT_MIN_PAD_LEN)
+ return len;
+
+ assert(nonce[crypto_box_HALF_NONCEBYTES] == nonce[0]);
+
+ crypto_stream((unsigned char *)&rnd, (unsigned long long)sizeof(rnd), nonce,
+ secretkey);
+ padded_len =
+ len + DNSCRYPT_MIN_PAD_LEN + rnd % (max_len - len -
+ DNSCRYPT_MIN_PAD_LEN + 1);
+ padded_len += DNSCRYPT_BLOCK_SIZE - padded_len % DNSCRYPT_BLOCK_SIZE;
+ if (padded_len > max_len)
+ padded_len = max_len;
+
+ memset(buf_padding_area, 0, padded_len - len);
+ *buf_padding_area = 0x80;
+
+ return padded_len;
+}
+
+uint64_t
+dnscrypt_hrtime(void)
+{
+ struct timeval tv;
+ uint64_t ts = (uint64_t)0U;
+ int ret;
+
+ ret = gettimeofday(&tv, NULL);
+ if (ret == 0) {
+ ts = (uint64_t)tv.tv_sec * 1000000U + (uint64_t)tv.tv_usec;
+ } else {
+ log_err("gettimeofday: %s", strerror(errno));
+ }
+ return ts;
+}
+
+/**
+ * Add the server nonce part to once.
+ * The nonce is made half of client nonce and the seconf half of the server
+ * nonce, both of them of size crypto_box_HALF_NONCEBYTES.
+ * \param[in] nonce: a uint8_t* of size crypto_box_NONCEBYTES
+ */
+static void
+add_server_nonce(uint8_t *nonce)
+{
+ uint64_t ts;
+ uint64_t tsn;
+ uint32_t suffix;
+ ts = dnscrypt_hrtime();
+ // TODO? dnscrypt-wrapper does some logic with context->nonce_ts_last
+ // unclear if we really need it, so skipping it for now.
+ tsn = (ts << 10) | (randombytes_random() & 0x3ff);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tsn =
+ (((uint64_t)htonl((uint32_t)tsn)) << 32) | htonl((uint32_t)(tsn >> 32));
+#endif
+ memcpy(nonce + crypto_box_HALF_NONCEBYTES, &tsn, 8);
+ suffix = randombytes_random();
+ memcpy(nonce + crypto_box_HALF_NONCEBYTES + 8, &suffix, 4);
+}
+
+/**
+ * Encrypt a reply using the keypair that was used with the query.
+ * The client nonce will be extracted from the encrypted query and stored in
+ * The buffer will be encrypted inplace.
+ * \param[in] keypair the keypair that matches this encrypted query.
+ * \param[in] client_nonce client nonce used during the query
+ * \param[in] nmkey shared secret key used during the query.
+ * \param[in] buffer the buffer where to encrypt the reply.
+ * \param[in] udp if whether or not it is a UDP query.
+ * \param[in] max_udp_size configured max udp size.
+ * \return 0 on success.
+ */
+static int
+dnscrypt_server_curve(const KeyPair *keypair,
+ uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],
+ uint8_t nmkey[crypto_box_BEFORENMBYTES],
+ struct sldns_buffer* buffer,
+ uint8_t udp,
+ size_t max_udp_size)
+{
+ size_t dns_reply_len = sldns_buffer_limit(buffer);
+ size_t max_len = dns_reply_len + DNSCRYPT_MAX_PADDING + DNSCRYPT_REPLY_HEADER_SIZE;
+ size_t max_reply_size = max_udp_size - 20U - 8U;
+ uint8_t nonce[crypto_box_NONCEBYTES];
+ uint8_t *boxed;
+ uint8_t *const buf = sldns_buffer_begin(buffer);
+ size_t len = sldns_buffer_limit(buffer);
+
+ if(udp){
+ if (max_len > max_reply_size)
+ max_len = max_reply_size;
+ }
+
+
+ memcpy(nonce, client_nonce, crypto_box_HALF_NONCEBYTES);
+ memcpy(nonce + crypto_box_HALF_NONCEBYTES, client_nonce,
+ crypto_box_HALF_NONCEBYTES);
+
+ boxed = buf + DNSCRYPT_REPLY_BOX_OFFSET;
+ memmove(boxed + crypto_box_MACBYTES, buf, len);
+ len = dnscrypt_pad(boxed + crypto_box_MACBYTES, len,
+ max_len - DNSCRYPT_REPLY_HEADER_SIZE, nonce,
+ keypair->crypt_secretkey);
+ sldns_buffer_set_at(buffer,
+ DNSCRYPT_REPLY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
+ 0, crypto_box_ZEROBYTES);
+
+ // add server nonce extension
+ add_server_nonce(nonce);
+
+ if (crypto_box_afternm
+ (boxed - crypto_box_BOXZEROBYTES, boxed - crypto_box_BOXZEROBYTES,
+ len + crypto_box_ZEROBYTES, nonce, nmkey) != 0) {
+ return -1;
+ }
+
+ sldns_buffer_write_at(buffer, 0, DNSCRYPT_MAGIC_RESPONSE, DNSCRYPT_MAGIC_HEADER_LEN);
+ sldns_buffer_write_at(buffer, DNSCRYPT_MAGIC_HEADER_LEN, nonce, crypto_box_NONCEBYTES);
+ sldns_buffer_set_limit(buffer, len + DNSCRYPT_REPLY_HEADER_SIZE);
+ return 0;
+}
+
+/**
+ * Read the content of fname into buf.
+ * \param[in] fname name of the file to read.
+ * \param[in] buf the buffer in which to read the content of the file.
+ * \param[in] count number of bytes to read.
+ * \return 0 on success.
+ */
+static int
+dnsc_read_from_file(char *fname, char *buf, size_t count)
+{
+ int fd;
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ return -1;
+ }
+ if (read(fd, buf, count) != (ssize_t)count) {
+ close(fd);
+ return -2;
+ }
+ close(fd);
+ return 0;
+}
+
+/**
+ * Parse certificates files provided by the configuration and load them into
+ * dnsc_env.
+ * \param[in] env the dnsc_env structure to load the certs into.
+ * \param[in] cfg the configuration.
+ * \return the number of certificates loaded.
+ */
+static int
+dnsc_parse_certs(struct dnsc_env *env, struct config_file *cfg)
+{
+ struct config_strlist *head;
+ size_t signed_cert_id;
+
+ env->signed_certs_count = 0U;
+ for (head = cfg->dnscrypt_provider_cert; head; head = head->next) {
+ env->signed_certs_count++;
+ }
+ env->signed_certs = sodium_allocarray(env->signed_certs_count,
+ sizeof *env->signed_certs);
+
+ signed_cert_id = 0U;
+ for(head = cfg->dnscrypt_provider_cert; head; head = head->next, signed_cert_id++) {
+ if(dnsc_read_from_file(
+ head->str,
+ (char *)(env->signed_certs + signed_cert_id),
+ sizeof(struct SignedCert)) != 0) {
+ fatal_exit("dnsc_parse_certs: failed to load %s: %s", head->str, strerror(errno));
+ }
+ verbose(VERB_OPS, "Loaded cert %s", head->str);
+ }
+ return signed_cert_id;
+}
+
+/**
+ * Helper function to convert a binary key into a printable fingerprint.
+ * \param[in] fingerprint the buffer in which to write the printable key.
+ * \param[in] key the key to convert.
+ */
+void
+dnsc_key_to_fingerprint(char fingerprint[80U], const uint8_t * const key)
+{
+ const size_t fingerprint_size = 80U;
+ size_t fingerprint_pos = (size_t) 0U;
+ size_t key_pos = (size_t) 0U;
+
+ for (;;) {
+ assert(fingerprint_size > fingerprint_pos);
+ snprintf(&fingerprint[fingerprint_pos],
+ fingerprint_size - fingerprint_pos, "%02X%02X",
+ key[key_pos], key[key_pos + 1U]);
+ key_pos += 2U;
+ if (key_pos >= crypto_box_PUBLICKEYBYTES) {
+ break;
+ }
+ fingerprint[fingerprint_pos + 4U] = ':';
+ fingerprint_pos += 5U;
+ }
+}
+
+/**
+ * Find the keypair matching a DNSCrypt query.
+ * \param[in] dnscenv The DNSCrypt enviroment, which contains the list of keys
+ * supported by the server.
+ * \param[in] buffer The encrypted DNS query.
+ * \return a KeyPair * if we found a key pair matching the query, NULL otherwise.
+ */
+static const KeyPair *
+dnsc_find_keypair(struct dnsc_env* dnscenv, struct sldns_buffer* buffer)
+{
+ const KeyPair *keypairs = dnscenv->keypairs;
+ struct dnscrypt_query_header *dnscrypt_header;
+ size_t i;
+
+ if (sldns_buffer_limit(buffer) < DNSCRYPT_QUERY_HEADER_SIZE) {
+ return NULL;
+ }
+ dnscrypt_header = (struct dnscrypt_query_header *)sldns_buffer_begin(buffer);
+ for (i = 0U; i < dnscenv->keypairs_count; i++) {
+ if (memcmp(keypairs[i].crypt_publickey, dnscrypt_header->magic_query,
+ DNSCRYPT_MAGIC_HEADER_LEN) == 0) {
+ return &keypairs[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Insert local-zone and local-data into configuration.
+ * In order to be able to serve certs over TXT, we can reuse the local-zone and
+ * local-data config option. The zone and qname are infered from the
+ * provider_name and the content of the TXT record from the certificate content.
+ * returns the number of certtificate TXT record that were loaded.
+ * < 0 in case of error.
+ */
+static int
+dnsc_load_local_data(struct dnsc_env* dnscenv, struct config_file *cfg)
+{
+ size_t i, j;
+ // Insert 'local-zone: "2.dnscrypt-cert.example.com" deny'
+ if(!cfg_str2list_insert(&cfg->local_zones,
+ strdup(dnscenv->provider_name),
+ strdup("deny"))) {
+ log_err("Could not load dnscrypt local-zone: %s deny",
+ dnscenv->provider_name);
+ return -1;
+ }
+
+ // Add local data entry of type:
+ // 2.dnscrypt-cert.example.com 86400 IN TXT "DNSC......"
+ for(i=0; i<dnscenv->signed_certs_count; i++) {
+ const char *ttl_class_type = " 86400 IN TXT \"";
+ struct SignedCert *cert = dnscenv->signed_certs + i;
+ uint16_t rrlen = strlen(dnscenv->provider_name) +
+ strlen(ttl_class_type) +
+ 4 * sizeof(struct SignedCert) + // worst case scenario
+ 1 + // trailing double quote
+ 1;
+ char *rr = malloc(rrlen);
+ if(!rr) {
+ log_err("Could not allocate memory");
+ return -2;
+ }
+ snprintf(rr, rrlen - 1, "%s 86400 IN TXT \"", dnscenv->provider_name);
+ for(j=0; j<sizeof(struct SignedCert); j++) {
+ int c = (int)*((const uint8_t *) cert + j);
+ if (isprint(c) && c != '"' && c != '\\') {
+ snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "%c", c);
+ } else {
+ snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "\\%03d", c);
+ }
+ }
+ snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "\"");
+ cfg_strlist_insert(&cfg->local_data, strdup(rr));
+ free(rr);
+ }
+ return dnscenv->signed_certs_count;
+}
+
+/**
+ * Parse the secret key files from `dnscrypt-secret-key` config and populates
+ * a list of secret/public keys supported by dnscrypt listener.
+ * \param[in] env The dnsc_env structure which will hold the keypairs.
+ * \param[in] cfg The config with the secret key file paths.
+ */
+static int
+dnsc_parse_keys(struct dnsc_env *env, struct config_file *cfg)
+{
+ struct config_strlist *head;
+ size_t keypair_id;
+
+ env->keypairs_count = 0U;
+ for (head = cfg->dnscrypt_secret_key; head; head = head->next) {
+ env->keypairs_count++;
+ }
+ env->keypairs = sodium_allocarray(env->keypairs_count,
+ sizeof *env->keypairs);
+
+ keypair_id = 0U;
+ for(head = cfg->dnscrypt_secret_key; head; head = head->next, keypair_id++) {
+ char fingerprint[80];
+ if(dnsc_read_from_file(
+ head->str,
+ (char *)(env->keypairs[keypair_id].crypt_secretkey),
+ crypto_box_SECRETKEYBYTES) != 0) {
+ fatal_exit("dnsc_parse_keys: failed to load %s: %s", head->str, strerror(errno));
+ }
+ verbose(VERB_OPS, "Loaded key %s", head->str);
+ if (crypto_scalarmult_base(env->keypairs[keypair_id].crypt_publickey,
+ env->keypairs[keypair_id].crypt_secretkey) != 0) {
+ fatal_exit("dnsc_parse_keys: could not generate public key from %s", head->str);
+ }
+ dnsc_key_to_fingerprint(fingerprint, env->keypairs[keypair_id].crypt_publickey);
+ verbose(VERB_OPS, "Crypt public key fingerprint for %s: %s", head->str, fingerprint);
+ }
+ return keypair_id;
+}
+
+
+/**
+ * #########################################################
+ * ############# Publicly accessible functions #############
+ * #########################################################
+ */
+
+int
+dnsc_handle_curved_request(struct dnsc_env* dnscenv,
+ struct comm_reply* repinfo)
+{
+ struct comm_point* c = repinfo->c;
+
+ repinfo->is_dnscrypted = 0;
+ if( !c->dnscrypt ) {
+ return 1;
+ }
+ // Attempt to decrypt the query. If it is not crypted, we may still need
+ // to serve the certificate.
+ verbose(VERB_ALGO, "handle request called on DNSCrypt socket");
+ if ((repinfo->keypair = dnsc_find_keypair(dnscenv, c->buffer)) != NULL) {
+ if(dnscrypt_server_uncurve(repinfo->keypair,
+ repinfo->client_nonce,
+ repinfo->nmkey,
+ c->buffer) != 0){
+ verbose(VERB_ALGO, "dnscrypt: Failed to uncurve");
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
+ repinfo->is_dnscrypted = 1;
+ sldns_buffer_rewind(c->buffer);
+ }
+ return 1;
+}
+
+int
+dnsc_handle_uncurved_request(struct comm_reply *repinfo)
+{
+ if(!repinfo->c->dnscrypt) {
+ return 1;
+ }
+ sldns_buffer_copy(repinfo->c->dnscrypt_buffer, repinfo->c->buffer);
+ if(!repinfo->is_dnscrypted) {
+ return 1;
+ }
+ if(dnscrypt_server_curve(repinfo->keypair,
+ repinfo->client_nonce,
+ repinfo->nmkey,
+ repinfo->c->dnscrypt_buffer,
+ repinfo->c->type == comm_udp,
+ repinfo->max_udp_size) != 0){
+ verbose(VERB_ALGO, "dnscrypt: Failed to curve cached missed answer");
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
+ return 1;
+}
+
+struct dnsc_env *
+dnsc_create(void)
+{
+ struct dnsc_env *env;
+ if (sodium_init() == -1) {
+ fatal_exit("dnsc_create: could not initialize libsodium.");
+ }
+ env = (struct dnsc_env *) calloc(1, sizeof(struct dnsc_env));
+ return env;
+}
+
+int
+dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg)
+{
+ if(dnsc_parse_certs(env, cfg) <= 0) {
+ fatal_exit("dnsc_apply_cfg: no cert file loaded");
+ }
+ if(dnsc_parse_keys(env, cfg) <= 0) {
+ fatal_exit("dnsc_apply_cfg: no key file loaded");
+ }
+ randombytes_buf(env->hash_key, sizeof env->hash_key);
+ env->provider_name = cfg->dnscrypt_provider;
+
+ if(dnsc_load_local_data(env, cfg) <= 0) {
+ fatal_exit("dnsc_apply_cfg: could not load local data");
+ }
+ return 0;
+}
diff --git a/external/unbound/dnscrypt/dnscrypt.h b/external/unbound/dnscrypt/dnscrypt.h
new file mode 100644
index 000000000..dac611b05
--- /dev/null
+++ b/external/unbound/dnscrypt/dnscrypt.h
@@ -0,0 +1,102 @@
+#ifndef UNBOUND_DNSCRYPT_H
+#define UNBOUND_DNSCRYPT_H
+
+/**
+ * \file
+ * dnscrypt functions for encrypting DNS packets.
+ */
+
+#include "dnscrypt/dnscrypt_config.h"
+#ifdef USE_DNSCRYPT
+
+#define DNSCRYPT_MAGIC_HEADER_LEN 8U
+#define DNSCRYPT_MAGIC_RESPONSE "r6fnvWj8"
+
+#ifndef DNSCRYPT_MAX_PADDING
+# define DNSCRYPT_MAX_PADDING 256U
+#endif
+#ifndef DNSCRYPT_BLOCK_SIZE
+# define DNSCRYPT_BLOCK_SIZE 64U
+#endif
+#ifndef DNSCRYPT_MIN_PAD_LEN
+# define DNSCRYPT_MIN_PAD_LEN 8U
+#endif
+
+#define crypto_box_HALF_NONCEBYTES (crypto_box_NONCEBYTES / 2U)
+
+#include "config.h"
+#include "dnscrypt/cert.h"
+
+#define DNSCRYPT_QUERY_HEADER_SIZE \
+ (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES + crypto_box_MACBYTES)
+#define DNSCRYPT_RESPONSE_HEADER_SIZE \
+ (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_NONCEBYTES + crypto_box_MACBYTES)
+
+#define DNSCRYPT_REPLY_HEADER_SIZE \
+ (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES * 2 + crypto_box_MACBYTES)
+
+struct sldns_buffer;
+struct config_file;
+struct comm_reply;
+
+typedef struct KeyPair_ {
+ uint8_t crypt_publickey[crypto_box_PUBLICKEYBYTES];
+ uint8_t crypt_secretkey[crypto_box_SECRETKEYBYTES];
+} KeyPair;
+
+struct dnsc_env {
+ struct SignedCert *signed_certs;
+ size_t signed_certs_count;
+ uint8_t provider_publickey[crypto_sign_ed25519_PUBLICKEYBYTES];
+ uint8_t provider_secretkey[crypto_sign_ed25519_SECRETKEYBYTES];
+ KeyPair *keypairs;
+ size_t keypairs_count;
+ uint64_t nonce_ts_last;
+ unsigned char hash_key[crypto_shorthash_KEYBYTES];
+ char * provider_name;
+};
+
+struct dnscrypt_query_header {
+ uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN];
+ uint8_t publickey[crypto_box_PUBLICKEYBYTES];
+ uint8_t nonce[crypto_box_HALF_NONCEBYTES];
+ uint8_t mac[crypto_box_MACBYTES];
+};
+
+/**
+ * Initialize DNSCrypt enviroment.
+ * Initialize sodium library and allocate the dnsc_env structure.
+ * \return an uninitialized struct dnsc_env.
+ */
+struct dnsc_env * dnsc_create(void);
+
+/**
+ * Apply configuration.
+ * Read certificates and secret keys from configuration. Initialize hashkey and
+ * provider name as well as loading cert TXT records.
+ * In case of issue applying configuration, this function fatals.
+ * \param[in] env the struct dnsc_env to populate.
+ * \param[in] cfg the config_file struct with dnscrypt options.
+ * \return 0 on success.
+ */
+int dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg);
+
+/**
+ * handle a crypted dnscrypt request.
+ * Determine wether or not a query is coming over the dnscrypt listener and
+ * attempt to uncurve it or detect if it is a certificate query.
+ * return 0 in case of failure.
+ */
+int dnsc_handle_curved_request(struct dnsc_env* dnscenv,
+ struct comm_reply* repinfo);
+/**
+ * handle an unencrypted dnscrypt request.
+ * Determine wether or not a query is going over the dnscrypt channel and
+ * attempt to curve it unless it was not crypted like when it is a
+ * certificate query.
+ * \return 0 in case of failure.
+ */
+
+int dnsc_handle_uncurved_request(struct comm_reply *repinfo);
+#endif /* USE_DNSCRYPT */
+#endif
diff --git a/external/unbound/dnscrypt/dnscrypt.m4 b/external/unbound/dnscrypt/dnscrypt.m4
new file mode 100644
index 000000000..077d28221
--- /dev/null
+++ b/external/unbound/dnscrypt/dnscrypt.m4
@@ -0,0 +1,25 @@
+# dnscrypt.m4
+
+# dnsc_DNSCRYPT([action-if-true], [action-if-false])
+# --------------------------------------------------------------------------
+# Check for required dnscrypt libraries and add dnscrypt configure args.
+AC_DEFUN([dnsc_DNSCRYPT],
+[
+ AC_ARG_ENABLE([dnscrypt],
+ AS_HELP_STRING([--enable-dnscrypt],
+ [Enable dnscrypt support (requires libsodium)]),
+ [opt_dnscrypt=$enableval], [opt_dnscrypt=no])
+
+ if test "x$opt_dnscrypt" != "xno"; then
+ AC_ARG_WITH([libsodium], AC_HELP_STRING([--with-libsodium=path],
+ [Path where libsodium is installed, for dnscrypt]), [
+ CFLAGS="$CFLAGS -I$withval/include"
+ LDFLAGS="$LDFLAGS -L$withval/lib"
+ ])
+ AC_SEARCH_LIBS([sodium_init], [sodium], [],
+ AC_MSG_ERROR([The sodium library was not found. Please install sodium!]))
+ $1
+ else
+ $2
+ fi
+])
diff --git a/external/unbound/dnscrypt/dnscrypt_config.h b/external/unbound/dnscrypt/dnscrypt_config.h
new file mode 100644
index 000000000..0d77a0c91
--- /dev/null
+++ b/external/unbound/dnscrypt/dnscrypt_config.h
@@ -0,0 +1,17 @@
+#ifndef UNBOUND_DNSCRYPT_CONFIG_H
+#define UNBOUND_DNSCRYPT_CONFIG_H
+
+/*
+ * Process this file (dnscrypt_config.h.in) with AC_CONFIG_FILES to generate
+ * dnscrypt_config.h.
+ *
+ * This file exists so that USE_DNSCRYPT can be used without including config.h.
+ */
+
+#if 0 /* ENABLE_DNSCRYPT */
+# ifndef USE_DNSCRYPT
+# define USE_DNSCRYPT 1
+# endif
+#endif
+
+#endif /* UNBOUND_DNSCRYPT_CONFIG_H */