Skip to content

Commit

Permalink
Merge pull request #49 from jedisct1/multiplekeys
Browse files Browse the repository at this point in the history
Support for multiple keys / seamless key rotation
  • Loading branch information
cofyc committed Jul 5, 2015
2 parents e2d3645 + 097f09a commit e659173
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 46 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ before_script:
- sudo apt-get install libevent-dev
- git clone git://github.com/jedisct1/libsodium.git
- cd libsodium
- git checkout 0.4.1
- git checkout 1.0.0
- ./autogen.sh
- ./configure --disable-dependency-tracking
- ./configure --disable-dependency-tracking --enable-minimal
- sudo make install
- sudo ldconfig
- cd ..
Expand Down
29 changes: 26 additions & 3 deletions dnscrypt.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
#include "dnscrypt.h"

const KeyPair *
find_keypair(const struct context *c,
const unsigned char magic_query[DNSCRYPT_MAGIC_HEADER_LEN],
const size_t dns_query_len)
{
const KeyPair *keypairs = c->keypairs;
size_t i;

if (dns_query_len <= DNSCRYPT_QUERY_HEADER_SIZE) {
return NULL;
}
for (i = 0U; i < c->keypairs_count; i++) {
if (memcmp(keypairs[i].crypt_publickey, magic_query,
DNSCRYPT_MAGIC_HEADER_LEN) == 0) {
return &keypairs[i];
}
}
if (memcmp(magic_query, CERT_OLD_MAGIC_HEADER, DNSCRYPT_MAGIC_HEADER_LEN) == 0) {
return &keypairs[0];
}
return NULL;
}

int
dnscrypt_cmp_client_nonce(const uint8_t
client_nonce[crypto_box_HALF_NONCEBYTES],
Expand Down Expand Up @@ -169,7 +192,7 @@ dnscrypt_pad(uint8_t *buf, const size_t len, const size_t max_len,
(DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES)

int
dnscrypt_server_uncurve(struct context *c,
dnscrypt_server_uncurve(struct context *c, const KeyPair *keypair,
uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],
uint8_t nmkey[crypto_box_BEFORENMBYTES],
uint8_t *const buf, size_t * const lenp)
Expand All @@ -183,7 +206,7 @@ dnscrypt_server_uncurve(struct context *c,
struct dnscrypt_query_header *query_header =
(struct dnscrypt_query_header *)buf;
memcpy(nmkey, query_header->publickey, crypto_box_PUBLICKEYBYTES);
if (crypto_box_beforenm(nmkey, nmkey, c->keypair.crypt_secretkey) != 0) {
if (crypto_box_beforenm(nmkey, nmkey, keypair->crypt_secretkey) != 0) {
return -1;
}

Expand Down Expand Up @@ -263,7 +286,7 @@ dnscrypt_server_curve(struct context *c,
len =
dnscrypt_pad(boxed + crypto_box_MACBYTES, len,
max_len - DNSCRYPT_REPLY_HEADER_SIZE, nonce,
c->keypair.crypt_secretkey);
c->keypairs[0].crypt_secretkey);
memset(boxed - crypto_box_BOXZEROBYTES, 0, crypto_box_ZEROBYTES);

// add server nonce extension
Expand Down
14 changes: 12 additions & 2 deletions dnscrypt.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
#include <event2/util.h>
#include <sodium.h>

#if SODIUM_LIBRARY_VERSION_MAJOR < 7
# define sodium_allocarray(C, S) calloc(C, S)
# define sodium_malloc(S) malloc(S)
# define sodium_free(P) free(P)
#endif

#define DNS_QUERY_TIMEOUT 10

#define DNS_MAX_PACKET_SIZE_UDP_RECV (65536U - 20U - 8U)
Expand Down Expand Up @@ -131,11 +137,15 @@ struct context {
uint8_t provider_publickey[crypto_sign_ed25519_PUBLICKEYBYTES];
uint8_t provider_secretkey[crypto_sign_ed25519_SECRETKEYBYTES];
char *crypt_secretkey_file;
KeyPair keypair;
KeyPair *keypairs;
size_t keypairs_count;
uint64_t nonce_ts_last;
unsigned char hash_key[crypto_shorthash_KEYBYTES];
};

const KeyPair * find_keypair(const struct context *c,
const unsigned char magic_query[DNSCRYPT_MAGIC_HEADER_LEN],
const size_t dns_query_len);
int dnscrypt_cmp_client_nonce(const uint8_t
client_nonce[crypto_box_HALF_NONCEBYTES],
const uint8_t *const buf, const size_t len);
Expand Down Expand Up @@ -192,7 +202,7 @@ struct dnscrypt_query_header {
uint8_t mac[crypto_box_MACBYTES];
};

int dnscrypt_server_uncurve(struct context *c,
int dnscrypt_server_uncurve(struct context *c, const KeyPair *keypair,
uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],
uint8_t nmkey[crypto_box_BEFORENMBYTES],
uint8_t *const buf, size_t * const lenp);
Expand Down
64 changes: 51 additions & 13 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ main(int argc, const char **argv)
if (!c.provider_cert_file)
c.provider_cert_file = "dnscrypt.cert";

c.keypairs = NULL;

if (gen_provider_keypair) {
uint8_t provider_publickey[crypto_sign_ed25519_PUBLICKEYBYTES];
uint8_t provider_secretkey[crypto_sign_ed25519_SECRETKEYBYTES];
Expand Down Expand Up @@ -295,16 +297,21 @@ main(int argc, const char **argv)
if (gen_crypt_keypair) {
printf("Generate crypt key pair...");
fflush(stdout);
if (crypto_box_keypair(c.keypair.crypt_publickey,
c.keypair.crypt_secretkey) == 0) {
if ((c.keypairs = sodium_malloc(sizeof *c.keypairs)) == NULL)
exit(1);
if (crypto_box_keypair(c.keypairs->crypt_publickey,
c.keypairs->crypt_secretkey) == 0) {
printf(" ok.\n");
if (write_to_file(c.crypt_secretkey_file,
(char *)c.keypair.crypt_secretkey,
(char *)c.keypairs->crypt_secretkey,
crypto_box_SECRETKEYBYTES) == 0) {
printf("Secret key stored in %s\n",
c.crypt_secretkey_file);
exit(0);
}
logger(LOG_ERR, "The new certificate was not saved - "
"Maybe the %s file already exists - please delete it first.",
c.crypt_secretkey_file);
exit(1);
} else {
printf(" failed.\n");
Expand All @@ -320,21 +327,52 @@ main(int argc, const char **argv)
if (logger_verbosity > LOG_DEBUG)
logger_verbosity = LOG_DEBUG;

if (read_from_file(c.crypt_secretkey_file,
(char *)c.keypair.crypt_secretkey,
crypto_box_SECRETKEYBYTES) != 0) {
char *crypt_secretkey_files, *crypt_secretkey_file;
size_t keypair_id;

c.keypairs_count = 0U;
if ((crypt_secretkey_files = strdup(c.crypt_secretkey_file)) == NULL)
exit(1);
for (crypt_secretkey_file = strtok(crypt_secretkey_files, ",");
crypt_secretkey_file != NULL;
crypt_secretkey_file = strtok(NULL, ",")) {
c.keypairs_count++;
}
if (crypto_scalarmult_base(c.keypair.crypt_publickey,
c.keypair.crypt_secretkey) != 0) {
exit(1);
if (c.keypairs_count <= 0U) {
logger(LOG_ERR, "You must specify --crypt-secretkey-file.\n\n");
argparse_usage(&argparse);
exit(0);
}
char fingerprint[80];
dnscrypt_key_to_fingerprint(fingerprint, c.keypair.crypt_publickey);
logger(LOG_INFO, "Crypt public key fingerprint: %s", fingerprint);
memcpy(crypt_secretkey_files, c.crypt_secretkey_file, strlen(c.crypt_secretkey_file) + 1U);
c.keypairs = sodium_allocarray(c.keypairs_count, sizeof *c.keypairs);
keypair_id = 0U;
for (crypt_secretkey_file = strtok(crypt_secretkey_files, ",");
crypt_secretkey_file != NULL;
crypt_secretkey_file = strtok(NULL, ",")) {
char fingerprint[80];

if (read_from_file(crypt_secretkey_file,
(char *)c.keypairs[keypair_id].crypt_secretkey,
crypto_box_SECRETKEYBYTES) != 0) {
logger(LOG_ERR, "Unable to read %s", crypt_secretkey_file);
exit(1);
}
if (crypto_scalarmult_base(c.keypairs[keypair_id].crypt_publickey,
c.keypairs[keypair_id].crypt_secretkey) != 0)
exit(1);
dnscrypt_key_to_fingerprint(fingerprint, c.keypairs->crypt_publickey);
logger(LOG_INFO, "Crypt public key fingerprint for %s: %s",
crypt_secretkey_file, fingerprint);
keypair_id++;
}
free(crypt_secretkey_files);

// generate signed certificate
if (gen_cert_file) {
if (c.keypairs_count != 1U) {
logger(LOG_ERR, "A certificate can only store a single key");
exit(1);
}
if (read_from_file
(c.provider_publickey_file, (char *)c.provider_publickey,
crypto_sign_ed25519_PUBLICKEYBYTES) == 0
Expand All @@ -346,7 +384,7 @@ main(int argc, const char **argv)
}
logger(LOG_NOTICE, "Generating pre-signed certificate.");
struct SignedCert *signed_cert =
cert_build_cert(c.keypair.crypt_publickey, cert_file_expire_days);
cert_build_cert(c.keypairs->crypt_publickey, cert_file_expire_days);
if (!signed_cert || cert_sign(signed_cert, c.provider_secretkey) != 0) {
logger(LOG_NOTICE, "Failed.");
exit(1);
Expand Down
27 changes: 12 additions & 15 deletions tcp_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ client_proxy_read_cb(struct bufferevent *const client_proxy_bev,
uint8_t dns_query_len_buf[2];
uint8_t dns_curved_query_len_buf[2];
TCPRequest *tcp_request = tcp_request_;
const KeyPair *keypair;
struct context *c = tcp_request->context;
struct evbuffer *input = bufferevent_get_input(client_proxy_bev);
size_t available_size;
Expand Down Expand Up @@ -147,29 +148,25 @@ client_proxy_read_cb(struct bufferevent *const client_proxy_bev,
// decrypt if encrypted
struct dnscrypt_query_header *dnscrypt_header =
(struct dnscrypt_query_header *)dns_query;
debug_assert(sizeof c->keypair.crypt_publickey >= DNSCRYPT_MAGIC_HEADER_LEN);
if (memcmp
(dnscrypt_header->magic_query, c->keypair.crypt_publickey,
DNSCRYPT_MAGIC_HEADER_LEN) == 0
|| memcmp
(dnscrypt_header->magic_query, CERT_OLD_MAGIC_HEADER,
DNSCRYPT_MAGIC_HEADER_LEN) == 0) {
debug_assert(sizeof c->keypairs[0].crypt_publickey >= DNSCRYPT_MAGIC_HEADER_LEN);
if ((keypair =
find_keypair(c, dnscrypt_header->magic_query, dns_query_len)) == NULL) {
if (!c->allow_not_dnscrypted) {
logger(LOG_DEBUG, "Unauthenticated query received over TCP");
tcp_request_kill(tcp_request);
return;
}
tcp_request->is_dnscrypted = false;
} else {
if (dnscrypt_server_uncurve
(c, tcp_request->client_nonce, tcp_request->nmkey, dns_query,
(c, keypair, tcp_request->client_nonce, tcp_request->nmkey, dns_query,
&dns_query_len) != 0) {
logger(LOG_WARNING, "Received a suspicious query from the client");
tcp_request_kill(tcp_request);
return;
}
tcp_request->is_dnscrypted = true;
} else if (!c->allow_not_dnscrypted) {
logger(LOG_DEBUG, "Unauthenticated query received over TCP");
tcp_request_kill(tcp_request);
return;
} else {
tcp_request->is_dnscrypted = false;
}

dns_curved_query_len_buf[0] = (dns_query_len >> 8) & 0xff;
dns_curved_query_len_buf[1] = dns_query_len & 0xff;
if (bufferevent_write(tcp_request->proxy_resolver_bev,
Expand Down
19 changes: 8 additions & 11 deletions udp_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ client_to_proxy_cb(evutil_socket_t client_proxy_handle, short ev_flags,
uint8_t dns_query[DNS_MAX_PACKET_SIZE_UDP];
struct context *c = context;
UDPRequest *udp_request;
const KeyPair *keypair;
ssize_t nread;
size_t dns_query_len = 0;

Expand Down Expand Up @@ -356,24 +357,20 @@ client_to_proxy_cb(evutil_socket_t client_proxy_handle, short ev_flags,
// decrypt if encrypted
struct dnscrypt_query_header *dnscrypt_header =
(struct dnscrypt_query_header *)dns_query;
assert(sizeof c->keypair.crypt_publickey >= DNSCRYPT_MAGIC_HEADER_LEN);

if (memcmp
(dnscrypt_header->magic_query, c->keypair.crypt_publickey,
DNSCRYPT_MAGIC_HEADER_LEN) == 0
|| memcmp
(dnscrypt_header->magic_query, CERT_OLD_MAGIC_HEADER,
DNSCRYPT_MAGIC_HEADER_LEN) == 0) {
assert(sizeof c->keypairs[0].crypt_publickey >= DNSCRYPT_MAGIC_HEADER_LEN);

if ((keypair =
find_keypair(c, dnscrypt_header->magic_query, dns_query_len)) == NULL) {
udp_request->is_dnscrypted = false;
} else {
if (dnscrypt_server_uncurve
(c, udp_request->client_nonce, udp_request->nmkey, dns_query,
(c, keypair, udp_request->client_nonce, udp_request->nmkey, dns_query,
&dns_query_len) != 0) {
logger(LOG_WARNING, "Received a suspicious query from the client");
udp_request_kill(udp_request);
return;
}
udp_request->is_dnscrypted = true;
} else {
udp_request->is_dnscrypted = false;
}

struct dns_header *header = (struct dns_header *)dns_query;
Expand Down

0 comments on commit e659173

Please sign in to comment.