]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnscrypt.cc
Merge pull request #9070 from rgacogne/boost-173
[thirdparty/pdns.git] / pdns / dnscrypt.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
11e1e08b
RG
22#include "config.h"
23#ifdef HAVE_DNSCRYPT
24#include <fstream>
25#include "dolog.hh"
26#include "dnscrypt.hh"
27#include "dnswriter.hh"
28
43234e76 29DNSCryptPrivateKey::DNSCryptPrivateKey()
11e1e08b
RG
30{
31 sodium_memzero(key, sizeof(key));
32 sodium_mlock(key, sizeof(key));
33}
34
43234e76 35void DNSCryptPrivateKey::loadFromFile(const std::string& keyFile)
11e1e08b
RG
36{
37 ifstream file(keyFile);
38 sodium_memzero(key, sizeof(key));
39 file.read((char*) key, sizeof(key));
40
41 if (file.fail()) {
42 sodium_memzero(key, sizeof(key));
43 file.close();
44 throw std::runtime_error("Invalid DNSCrypt key file " + keyFile);
45 }
46
47 file.close();
48}
49
43234e76 50void DNSCryptPrivateKey::saveToFile(const std::string& keyFile) const
11e1e08b
RG
51{
52 ofstream file(keyFile);
53 file.write((char*) key, sizeof(key));
54 file.close();
55}
56
43234e76 57DNSCryptPrivateKey::~DNSCryptPrivateKey()
11e1e08b 58{
11e1e08b
RG
59 sodium_munlock(key, sizeof(key));
60}
61
43234e76
RG
62DNSCryptExchangeVersion DNSCryptQuery::getVersion() const
63{
64 if (d_pair == nullptr) {
65 throw std::runtime_error("Unable to determine the version of a DNSCrypt query if there is not associated cert");
66 }
67
68 return DNSCryptContext::getExchangeVersion(d_pair->cert);
69}
70
00ffb1c2 71#ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
43234e76 72DNSCryptQuery::~DNSCryptQuery()
332fdc5f 73{
43234e76
RG
74 if (d_sharedKeyComputed) {
75 sodium_munlock(d_sharedKey, sizeof(d_sharedKey));
8089cbe5 76 }
332fdc5f
RG
77}
78
43234e76 79int DNSCryptQuery::computeSharedKey()
332fdc5f 80{
43234e76
RG
81 assert(d_pair != nullptr);
82
8089cbe5
RG
83 int res = 0;
84
43234e76 85 if (d_sharedKeyComputed) {
8089cbe5
RG
86 return res;
87 }
88
43234e76
RG
89 const DNSCryptExchangeVersion version = DNSCryptContext::getExchangeVersion(d_pair->cert);
90
91 sodium_mlock(d_sharedKey, sizeof(d_sharedKey));
92
93 if (version == DNSCryptExchangeVersion::VERSION1) {
94 res = crypto_box_beforenm(d_sharedKey,
95 d_header.clientPK,
96 d_pair->privateKey.key);
97 }
98 else if (version == DNSCryptExchangeVersion::VERSION2) {
99#ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
100 res = crypto_box_curve25519xchacha20poly1305_beforenm(d_sharedKey,
101 d_header.clientPK,
102 d_pair->privateKey.key);
103#else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
104 res = -1;
105#endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
106 }
107 else {
108 res = -1;
109 }
8089cbe5
RG
110
111 if (res != 0) {
43234e76 112 sodium_munlock(d_sharedKey, sizeof(d_sharedKey));
8089cbe5
RG
113 return res;
114 }
115
43234e76 116 d_sharedKeyComputed = true;
8089cbe5 117 return res;
332fdc5f 118}
00ffb1c2 119#else
43234e76 120DNSCryptQuery::~DNSCryptQuery()
00ffb1c2
RG
121{
122}
123#endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
8089cbe5 124
040793d4
OM
125
126DNSCryptContext::~DNSCryptContext() {
040793d4
OM
127}
128
37b6d73d 129DNSCryptContext::DNSCryptContext(const std::string& pName, const std::vector<CertKeyPaths>& certKeys): d_certKeyPaths(certKeys), providerName(pName)
43234e76 130{
37b6d73d 131 reloadCertificates();
43234e76
RG
132}
133
134DNSCryptContext::DNSCryptContext(const std::string& pName, const DNSCryptCert& certificate, const DNSCryptPrivateKey& pKey): providerName(pName)
135{
43234e76
RG
136 addNewCertificate(certificate, pKey);
137}
138
139void DNSCryptContext::generateProviderKeys(unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE], unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE])
11e1e08b
RG
140{
141 int res = crypto_sign_ed25519_keypair(publicKey, privateKey);
142
143 if (res != 0) {
144 throw std::runtime_error("Error generating DNSCrypt provider keys");
145 }
146}
147
43234e76 148std::string DNSCryptContext::getProviderFingerprint(unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE])
11e1e08b
RG
149{
150 boost::format fmt("%02X%02X");
151 ostringstream ret;
152
153 for (size_t idx = 0; idx < DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE; idx += 2)
154 {
155 ret << (fmt % static_cast<int>(publicKey[idx]) % static_cast<int>(publicKey[idx+1]));
156 if (idx < (DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE - 2)) {
157 ret << ":";
158 }
159 }
160
161 return ret.str();
162}
163
43234e76
RG
164void DNSCryptContext::setExchangeVersion(const DNSCryptExchangeVersion& version, unsigned char esVersion[sizeof(DNSCryptCert::esVersion)])
165{
166 esVersion[0] = 0x00;
167
168 if (version == DNSCryptExchangeVersion::VERSION1) {
169 esVersion[1] = { 0x01 };
170 }
171 else if (version == DNSCryptExchangeVersion::VERSION2) {
172 esVersion[1] = { 0x02 };
173 }
174 else {
175 throw std::runtime_error("Unknown DNSCrypt exchange version");
176 }
177}
178
179DNSCryptExchangeVersion DNSCryptContext::getExchangeVersion(const unsigned char esVersion[sizeof(DNSCryptCert::esVersion)])
180{
181 if (esVersion[0] != 0x00) {
182 throw std::runtime_error("Unknown DNSCrypt exchange version");
183 }
184
185 if (esVersion[1] == 0x01) {
186 return DNSCryptExchangeVersion::VERSION1;
187 }
188 else if (esVersion[1] == 0x02) {
189 return DNSCryptExchangeVersion::VERSION2;
190 }
191
192 throw std::runtime_error("Unknown DNSCrypt exchange version");
193}
194
195DNSCryptExchangeVersion DNSCryptContext::getExchangeVersion(const DNSCryptCert& cert)
196{
197 return getExchangeVersion(cert.esVersion);
198}
199
200
201void DNSCryptContext::generateCertificate(uint32_t serial, time_t begin, time_t end, const DNSCryptExchangeVersion& version, const unsigned char providerPrivateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE], DNSCryptPrivateKey& privateKey, DNSCryptCert& cert)
11e1e08b
RG
202{
203 unsigned char magic[DNSCRYPT_CERT_MAGIC_SIZE] = DNSCRYPT_CERT_MAGIC_VALUE;
11e1e08b
RG
204 unsigned char protocolMinorVersion[] = DNSCRYPT_CERT_PROTOCOL_MINOR_VERSION_VALUE;
205 unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE];
43234e76
RG
206 unsigned char esVersion[sizeof(DNSCryptCert::esVersion)];
207 setExchangeVersion(version, esVersion);
208
11e1e08b
RG
209 generateResolverKeyPair(privateKey, pubK);
210
211 memcpy(cert.magic, magic, sizeof(magic));
212 memcpy(cert.esVersion, esVersion, sizeof(esVersion));
213 memcpy(cert.protocolMinorVersion, protocolMinorVersion, sizeof(protocolMinorVersion));
214 memcpy(cert.signedData.resolverPK, pubK, sizeof(cert.signedData.resolverPK));
215 memcpy(cert.signedData.clientMagic, pubK, sizeof(cert.signedData.clientMagic));
b7bd0317 216 cert.signedData.serial = htonl(serial);
11e1e08b
RG
217 cert.signedData.tsStart = htonl((uint32_t) begin);
218 cert.signedData.tsEnd = htonl((uint32_t) end);
219
220 unsigned long long signatureSize = 0;
221
222 int res = crypto_sign_ed25519(cert.signature,
223 &signatureSize,
224 (unsigned char*) &cert.signedData,
225 sizeof(cert.signedData),
226 providerPrivateKey);
227
228 if (res == 0) {
43234e76 229 assert(signatureSize == sizeof(DNSCryptCertSignedData) + DNSCRYPT_SIGNATURE_SIZE);
11e1e08b
RG
230 }
231 else {
232 throw std::runtime_error("Error generating DNSCrypt certificate");
233 }
234}
235
43234e76 236void DNSCryptContext::loadCertFromFile(const std::string&filename, DNSCryptCert& dest)
11e1e08b
RG
237{
238 ifstream file(filename);
239 file.read((char *) &dest, sizeof(dest));
240
241 if (file.fail())
242 throw std::runtime_error("Invalid dnscrypt certificate file " + filename);
243
244 file.close();
245}
246
43234e76 247void DNSCryptContext::saveCertFromFile(const DNSCryptCert& cert, const std::string&filename)
11e1e08b
RG
248{
249 ofstream file(filename);
250 file.write((char *) &cert, sizeof(cert));
251 file.close();
252}
253
43234e76 254void DNSCryptContext::generateResolverKeyPair(DNSCryptPrivateKey& privK, unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE])
11e1e08b
RG
255{
256 int res = crypto_box_keypair(pubK, privK.key);
257
258 if (res != 0) {
259 throw std::runtime_error("Error generating DNSCrypt resolver keys");
260 }
261}
262
43234e76 263void DNSCryptContext::computePublicKeyFromPrivate(const DNSCryptPrivateKey& privK, unsigned char* pubK)
11e1e08b
RG
264{
265 int res = crypto_scalarmult_base(pubK,
266 privK.key);
267
268 if (res != 0) {
269 throw std::runtime_error("Error computing dnscrypt public key from the private one");
270 }
271}
272
43234e76 273std::string DNSCryptContext::certificateDateToStr(uint32_t date)
11e1e08b
RG
274{
275 char buf[20];
43234e76 276 time_t tdate = static_cast<time_t>(ntohl(date));
11e1e08b
RG
277 struct tm date_tm;
278
279 localtime_r(&tdate, &date_tm);
280 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &date_tm);
281
282 return string(buf);
283}
284
37b6d73d 285void DNSCryptContext::addNewCertificate(std::shared_ptr<DNSCryptCertificatePair>& newCert, bool reload)
11e1e08b 286{
43234e76
RG
287 WriteLock w(&d_lock);
288
37b6d73d
RG
289 for (auto pair : d_certs) {
290 if (pair->cert.getSerial() == newCert->cert.getSerial()) {
bcc62bfb
RG
291 if (reload) {
292 /* on reload we just assume that this is the same certificate */
293 return;
294 }
295 else {
296 throw std::runtime_error("Error adding a new certificate: we already have a certificate with the same serial");
297 }
43234e76
RG
298 }
299 }
300
37b6d73d
RG
301 d_certs.push_back(newCert);
302}
303
304void DNSCryptContext::addNewCertificate(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, bool active, bool reload)
305{
43234e76
RG
306 auto pair = std::make_shared<DNSCryptCertificatePair>();
307 pair->cert = newCert;
308 pair->privateKey = newKey;
309 computePublicKeyFromPrivate(pair->privateKey, pair->publicKey);
310 pair->active = active;
37b6d73d
RG
311
312 addNewCertificate(pair, reload);
11e1e08b
RG
313}
314
37b6d73d 315std::shared_ptr<DNSCryptCertificatePair> DNSCryptContext::loadCertificatePair(const std::string& certFile, const std::string& keyFile)
11e1e08b 316{
37b6d73d
RG
317 auto pair = std::make_shared<DNSCryptCertificatePair>();
318 loadCertFromFile(certFile, pair->cert);
319 pair->privateKey.loadFromFile(keyFile);
320 pair->active = true;
321 computePublicKeyFromPrivate(pair->privateKey, pair->publicKey);
322 return pair;
323}
43234e76 324
37b6d73d
RG
325void DNSCryptContext::loadNewCertificate(const std::string& certFile, const std::string& keyFile, bool active, bool reload)
326{
327 auto newPair = DNSCryptContext::loadCertificatePair(certFile, keyFile);
328 newPair->active = active;
329 addNewCertificate(newPair, reload);
330 d_certKeyPaths.push_back({certFile, keyFile});
bcc62bfb
RG
331}
332
37b6d73d 333void DNSCryptContext::reloadCertificates()
bcc62bfb 334{
37b6d73d
RG
335 std::vector<std::shared_ptr<DNSCryptCertificatePair>> newCerts;
336 for (const auto& pair : d_certKeyPaths) {
337 newCerts.push_back(DNSCryptContext::loadCertificatePair(pair.cert, pair.key));
338 }
339
340 {
341 WriteLock w(&d_lock);
342 d_certs = std::move(newCerts);
343 }
43234e76
RG
344}
345
346void DNSCryptContext::markActive(uint32_t serial)
347{
348 WriteLock w(&d_lock);
349
37b6d73d 350 for (auto pair : d_certs) {
43234e76
RG
351 if (pair->active == false && pair->cert.getSerial() == serial) {
352 pair->active = true;
353 return;
354 }
355 }
356 throw std::runtime_error("No inactive certificate found with this serial");
357}
358
359void DNSCryptContext::markInactive(uint32_t serial)
360{
361 WriteLock w(&d_lock);
362
37b6d73d 363 for (auto pair : d_certs) {
43234e76
RG
364 if (pair->active == true && pair->cert.getSerial() == serial) {
365 pair->active = false;
366 return;
367 }
368 }
369 throw std::runtime_error("No active certificate found with this serial");
11e1e08b
RG
370}
371
43234e76 372void DNSCryptContext::removeInactiveCertificate(uint32_t serial)
11e1e08b 373{
43234e76
RG
374 WriteLock w(&d_lock);
375
37b6d73d 376 for (auto it = d_certs.begin(); it != d_certs.end(); ) {
43234e76 377 if ((*it)->active == false && (*it)->cert.getSerial() == serial) {
37b6d73d 378 it = d_certs.erase(it);
43234e76
RG
379 return;
380 } else {
381 it++;
382 }
383 }
384 throw std::runtime_error("No inactive certificate found with this serial");
385}
386
387bool DNSCryptQuery::parsePlaintextQuery(const char * packet, uint16_t packetSize)
388{
389 assert(d_ctx != nullptr);
390
11e1e08b 391 if (packetSize < sizeof(dnsheader)) {
43234e76 392 return false;
11e1e08b
RG
393 }
394
43234e76 395 const struct dnsheader * dh = reinterpret_cast<const struct dnsheader *>(packet);
11e1e08b 396 if (dh->qr || ntohs(dh->qdcount) != 1 || dh->ancount != 0 || dh->nscount != 0 || dh->opcode != Opcode::Query)
43234e76 397 return false;
11e1e08b
RG
398
399 unsigned int consumed;
400 uint16_t qtype, qclass;
401 DNSName qname(packet, packetSize, sizeof(dnsheader), false, &qtype, &qclass, &consumed);
bd910269 402 if ((packetSize - sizeof(dnsheader)) < (consumed + sizeof(qtype) + sizeof(qclass)))
43234e76 403 return false;
11e1e08b
RG
404
405 if (qtype != QType::TXT || qclass != QClass::IN)
43234e76 406 return false;
11e1e08b 407
43234e76
RG
408 if (qname != d_ctx->getProviderName())
409 return false;
410
411 d_qname = qname;
412 d_id = dh->id;
413 d_valid = true;
11e1e08b 414
43234e76 415 return true;
11e1e08b
RG
416}
417
43234e76 418void DNSCryptContext::getCertificateResponse(time_t now, const DNSName& qname, uint16_t qid, std::vector<uint8_t>& response)
11e1e08b 419{
43234e76 420 DNSPacketWriter pw(response, qname, QType::TXT, QClass::IN, Opcode::Query);
11e1e08b 421 struct dnsheader * dh = pw.getHeader();
43234e76 422 dh->id = qid;
11e1e08b
RG
423 dh->qr = true;
424 dh->rcode = RCode::NoError;
11e1e08b 425
43234e76 426 ReadLock r(&d_lock);
ba8ade8e 427 for (const auto& pair : d_certs) {
43234e76
RG
428 if (!pair->active || !pair->cert.isValid(now)) {
429 continue;
430 }
11e1e08b 431
43234e76
RG
432 pw.startRecord(qname, QType::TXT, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL), QClass::IN, DNSResourceRecord::ANSWER, true);
433 std::string scert;
434 uint8_t certSize = sizeof(pair->cert);
435 scert.assign((const char*) &certSize, sizeof(certSize));
436 scert.append((const char*) &pair->cert, certSize);
11e1e08b 437
43234e76
RG
438 pw.xfrBlob(scert);
439 pw.commit();
11e1e08b 440 }
43234e76 441}
11e1e08b 442
43234e76
RG
443bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery& query, time_t now)
444{
445 const unsigned char* magic = query.getClientMagic();
446
447 ReadLock r(&d_lock);
37b6d73d 448 for (const auto& pair : d_certs) {
43234e76
RG
449 if (pair->cert.isValid(now) && memcmp(magic, pair->cert.signedData.clientMagic, DNSCRYPT_CLIENT_MAGIC_SIZE) == 0) {
450 query.setCertificatePair(pair);
451 return true;
452 }
11e1e08b
RG
453 }
454
455 return false;
456}
457
43234e76 458bool DNSCryptQuery::isEncryptedQuery(const char * packet, uint16_t packetSize, bool tcp, time_t now)
11e1e08b 459{
43234e76 460 assert(d_ctx != nullptr);
11e1e08b 461
43234e76
RG
462 d_encrypted = false;
463
464 if (packetSize < sizeof(DNSCryptQueryHeader)) {
465 return false;
11e1e08b
RG
466 }
467
43234e76
RG
468 if (!tcp && packetSize < DNSCryptQuery::s_minUDPLength) {
469 return false;
11e1e08b
RG
470 }
471
43234e76 472 const struct DNSCryptQueryHeader* header = reinterpret_cast<const struct DNSCryptQueryHeader*>(packet);
11e1e08b 473
43234e76 474 d_header = *header;
11e1e08b 475
43234e76
RG
476 if (!d_ctx->magicMatchesAPublicKey(*this, now)) {
477 return false;
11e1e08b
RG
478 }
479
43234e76
RG
480 d_encrypted = true;
481
482 return true;
11e1e08b
RG
483}
484
43234e76 485void DNSCryptQuery::getDecrypted(bool tcp, char* packet, uint16_t packetSize, uint16_t* decryptedQueryLen)
11e1e08b 486{
43234e76
RG
487 assert(decryptedQueryLen != nullptr);
488 assert(d_encrypted);
489 assert(d_pair != nullptr);
490 assert(d_valid == false);
11e1e08b
RG
491
492#ifdef DNSCRYPT_STRICT_PADDING_LENGTH
43234e76
RG
493 if (tcp && ((packetSize - sizeof(DNSCryptQueryHeader)) % DNSCRYPT_PADDED_BLOCK_SIZE) != 0) {
494 vinfolog("Dropping encrypted query with invalid size of %d (should be a multiple of %d)", (packetSize - sizeof(DNSCryptQueryHeader)), DNSCRYPT_PADDED_BLOCK_SIZE);
11e1e08b
RG
495 return;
496 }
497#endif
498
499 unsigned char nonce[DNSCRYPT_NONCE_SIZE];
43234e76 500 static_assert(sizeof(nonce) == (2* sizeof(d_header.clientNonce)), "Nonce should be larger than clientNonce (half)");
ef2ea4bf 501 static_assert(sizeof(d_header.clientPK) == DNSCRYPT_PUBLIC_KEY_SIZE, "Client Public key size is not right");
43234e76 502 static_assert(sizeof(d_pair->privateKey.key) == DNSCRYPT_PRIVATE_KEY_SIZE, "Private key size is not right");
11e1e08b 503
43234e76
RG
504 memcpy(nonce, &d_header.clientNonce, sizeof(d_header.clientNonce));
505 memset(nonce + sizeof(d_header.clientNonce), 0, sizeof(nonce) - sizeof(d_header.clientNonce));
11e1e08b 506
00ffb1c2 507#ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
43234e76 508 int res = computeSharedKey();
8089cbe5
RG
509 if (res != 0) {
510 vinfolog("Dropping encrypted query we can't compute the shared key for");
511 return;
332fdc5f
RG
512 }
513
43234e76
RG
514 const DNSCryptExchangeVersion version = getVersion();
515
516 if (version == DNSCryptExchangeVersion::VERSION1) {
517 res = crypto_box_open_easy_afternm(reinterpret_cast<unsigned char*>(packet),
518 reinterpret_cast<unsigned char*>(packet + sizeof(DNSCryptQueryHeader)),
519 packetSize - sizeof(DNSCryptQueryHeader),
520 nonce,
521 d_sharedKey);
522 }
523 else if (version == DNSCryptExchangeVersion::VERSION2) {
524#ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
525 res = crypto_box_curve25519xchacha20poly1305_open_easy_afternm(reinterpret_cast<unsigned char*>(packet),
526 reinterpret_cast<unsigned char*>(packet + sizeof(DNSCryptQueryHeader)),
527 packetSize - sizeof(DNSCryptQueryHeader),
528 nonce,
529 d_sharedKey);
530#else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
531 res = -1;
532#endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
533 } else {
534 res = -1;
535 }
536
537#else /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
538 int res = crypto_box_open_easy(reinterpret_cast<unsigned char*>(packet),
539 reinterpret_cast<unsigned char*>(packet + sizeof(DNSCryptQueryHeader)),
540 packetSize - sizeof(DNSCryptQueryHeader),
00ffb1c2 541 nonce,
43234e76
RG
542 d_header.clientPK,
543 d_pair->privateKey.key);
00ffb1c2 544#endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
11e1e08b
RG
545
546 if (res != 0) {
547 vinfolog("Dropping encrypted query we can't decrypt");
548 return;
549 }
550
43234e76 551 *decryptedQueryLen = packetSize - sizeof(DNSCryptQueryHeader) - DNSCRYPT_MAC_SIZE;
11e1e08b
RG
552 uint16_t pos = *decryptedQueryLen;
553 assert(pos < packetSize);
43234e76 554 d_paddedLen = *decryptedQueryLen;
11e1e08b
RG
555
556 while(pos > 0 && packet[pos - 1] == 0) pos--;
557
43234e76 558 if (pos == 0 || static_cast<uint8_t>(packet[pos - 1]) != 0x80) {
11e1e08b
RG
559 vinfolog("Dropping encrypted query with invalid padding value");
560 return;
561 }
562
563 pos--;
564
565 size_t paddingLen = *decryptedQueryLen - pos;
566 *decryptedQueryLen = pos;
567
568 if (tcp && paddingLen > DNSCRYPT_MAX_TCP_PADDING_SIZE) {
569 vinfolog("Dropping encrypted query with too long padding size");
570 return;
571 }
572
43234e76
RG
573 d_len = pos;
574 d_valid = true;
575}
11e1e08b 576
43234e76
RG
577void DNSCryptQuery::getCertificateResponse(time_t now, std::vector<uint8_t>& response) const
578{
579 assert(d_ctx != nullptr);
580 d_ctx->getCertificateResponse(now, d_qname, d_id, response);
11e1e08b
RG
581}
582
43234e76 583void DNSCryptQuery::parsePacket(char* packet, uint16_t packetSize, bool tcp, uint16_t* decryptedQueryLen, time_t now)
11e1e08b 584{
43234e76
RG
585 assert(packet != nullptr);
586 assert(decryptedQueryLen != nullptr);
11e1e08b 587
43234e76 588 d_valid = false;
11e1e08b
RG
589
590 /* might be a plaintext certificate request or an authenticated request */
43234e76
RG
591 if (isEncryptedQuery(packet, packetSize, tcp, now)) {
592 getDecrypted(tcp, packet, packetSize, decryptedQueryLen);
11e1e08b
RG
593 }
594 else {
43234e76 595 parsePlaintextQuery(packet, packetSize);
11e1e08b
RG
596 }
597}
598
43234e76 599void DNSCryptQuery::fillServerNonce(unsigned char* nonce) const
11e1e08b 600{
43234e76 601 uint32_t* dest = reinterpret_cast<uint32_t*>(nonce);
11e1e08b
RG
602 static const size_t nonceSize = DNSCRYPT_NONCE_SIZE / 2;
603
604 for (size_t pos = 0; pos < (nonceSize / sizeof(*dest)); pos++)
605 {
606 const uint32_t value = randombytes_random();
607 memcpy(dest + pos, &value, sizeof(value));
608 }
609}
610
611/*
612 "The length of <resolver-response-pad> must be between 0 and 256 bytes,
613 and must be constant for a given (<resolver-sk>, <client-nonce>) tuple."
614*/
43234e76 615uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen, size_t maxLen) const
11e1e08b 616{
43234e76 617 size_t paddedSize = 0;
11e1e08b
RG
618 uint16_t result = 0;
619 uint32_t rnd = 0;
43234e76
RG
620 assert(d_header.clientNonce);
621 assert(d_pair != nullptr);
622
11e1e08b 623 unsigned char nonce[DNSCRYPT_NONCE_SIZE];
43234e76
RG
624 memcpy(nonce, d_header.clientNonce, (DNSCRYPT_NONCE_SIZE / 2));
625 memcpy(&(nonce[DNSCRYPT_NONCE_SIZE / 2]), d_header.clientNonce, (DNSCRYPT_NONCE_SIZE / 2));
626 crypto_stream((unsigned char*) &rnd, sizeof(rnd), nonce, d_pair->privateKey.key);
11e1e08b 627
43234e76
RG
628 paddedSize = unpaddedLen + rnd % (maxLen - unpaddedLen + 1);
629 paddedSize += DNSCRYPT_PADDED_BLOCK_SIZE - (paddedSize % DNSCRYPT_PADDED_BLOCK_SIZE);
11e1e08b 630
43234e76
RG
631 if (paddedSize > maxLen)
632 paddedSize = maxLen;
11e1e08b 633
43234e76 634 result = paddedSize - unpaddedLen;
11e1e08b
RG
635
636 return result;
637}
638
43234e76 639int DNSCryptQuery::encryptResponse(char* response, uint16_t responseLen, uint16_t responseSize, bool tcp, uint16_t* encryptedResponseLen)
11e1e08b 640{
43234e76
RG
641 struct DNSCryptResponseHeader responseHeader;
642 assert(response != nullptr);
11e1e08b
RG
643 assert(responseLen > 0);
644 assert(responseSize >= responseLen);
43234e76
RG
645 assert(encryptedResponseLen != nullptr);
646 assert(d_encrypted == true);
647 assert(d_pair != nullptr);
11e1e08b 648
43234e76
RG
649 if (!tcp && d_paddedLen < responseLen) {
650 struct dnsheader* dh = reinterpret_cast<struct dnsheader*>(response);
bd64cc44
RG
651 size_t questionSize = 0;
652
653 if (responseLen > sizeof(dnsheader)) {
654 unsigned int consumed = 0;
43234e76 655 DNSName tempQName(response, responseLen, sizeof(dnsheader), false, 0, 0, &consumed);
bd64cc44
RG
656 if (consumed > 0) {
657 questionSize = consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
658 }
659 }
660
661 responseLen = sizeof(dnsheader) + questionSize;
662
43234e76
RG
663 if (responseLen > d_paddedLen) {
664 responseLen = d_paddedLen;
bd64cc44
RG
665 }
666 dh->ancount = dh->arcount = dh->nscount = 0;
11e1e08b
RG
667 dh->tc = 1;
668 }
669
43234e76 670 size_t requiredSize = sizeof(responseHeader) + DNSCRYPT_MAC_SIZE + responseLen;
11e1e08b 671 size_t maxSize = (responseSize > (requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE)) ? (requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE) : responseSize;
43234e76 672 uint16_t paddingSize = computePaddingSize(requiredSize, maxSize);
11e1e08b
RG
673 requiredSize += paddingSize;
674
675 if (requiredSize > responseSize)
676 return ENOBUFS;
677
43234e76
RG
678 memcpy(&responseHeader.nonce, &d_header.clientNonce, sizeof d_header.clientNonce);
679 fillServerNonce(&(responseHeader.nonce[sizeof(d_header.clientNonce)]));
11e1e08b
RG
680
681 /* moving the existing response after the header + MAC */
43234e76 682 memmove(response + sizeof(responseHeader) + DNSCRYPT_MAC_SIZE, response, responseLen);
11e1e08b
RG
683
684 uint16_t pos = 0;
685 /* copying header */
43234e76
RG
686 memcpy(response + pos, &responseHeader, sizeof(responseHeader));
687 pos += sizeof(responseHeader);
11e1e08b
RG
688 /* setting MAC bytes to 0 */
689 memset(response + pos, 0, DNSCRYPT_MAC_SIZE);
690 pos += DNSCRYPT_MAC_SIZE;
691 uint16_t toEncryptPos = pos;
692 /* skipping response */
693 pos += responseLen;
694 /* padding */
43234e76 695 response[pos] = static_cast<uint8_t>(0x80);
11e1e08b
RG
696 pos++;
697 memset(response + pos, 0, paddingSize - 1);
698 pos += (paddingSize - 1);
332fdc5f 699
11e1e08b 700 /* encrypting */
00ffb1c2 701#ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
43234e76 702 int res = computeSharedKey();
8089cbe5
RG
703 if (res != 0) {
704 return res;
332fdc5f
RG
705 }
706
43234e76
RG
707 const DNSCryptExchangeVersion version = getVersion();
708
709 if (version == DNSCryptExchangeVersion::VERSION1) {
710 res = crypto_box_easy_afternm(reinterpret_cast<unsigned char*>(response + sizeof(responseHeader)),
711 reinterpret_cast<unsigned char*>(response + toEncryptPos),
712 responseLen + paddingSize,
713 responseHeader.nonce,
714 d_sharedKey);
715 }
716 else if (version == DNSCryptExchangeVersion::VERSION2) {
717#ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
718 res = crypto_box_curve25519xchacha20poly1305_easy_afternm(reinterpret_cast<unsigned char*>(response + sizeof(responseHeader)),
719 reinterpret_cast<unsigned char*>(response + toEncryptPos),
720 responseLen + paddingSize,
721 responseHeader.nonce,
722 d_sharedKey);
723#else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
724 res = -1;
725#endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
726 }
727 else {
728 res = -1;
729 }
00ffb1c2 730#else
43234e76
RG
731 int res = crypto_box_easy(reinterpret_cast<unsigned char*>(response + sizeof(responseHeader)),
732 reinterpret_cast<unsigned char*>(response + toEncryptPos),
00ffb1c2 733 responseLen + paddingSize,
43234e76
RG
734 responseHeader.nonce,
735 d_header.clientPK,
736 d_pair->privateKey.key);
00ffb1c2 737#endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
11e1e08b
RG
738
739 if (res == 0) {
740 assert(pos == requiredSize);
741 *encryptedResponseLen = requiredSize;
742 }
743
744 return res;
745}
746
f3b1a1ef 747int DNSCryptContext::encryptQuery(char* query, uint16_t queryLen, uint16_t querySize, const unsigned char clientPublicKey[DNSCRYPT_PUBLIC_KEY_SIZE], const DNSCryptPrivateKey& clientPrivateKey, const unsigned char clientNonce[DNSCRYPT_NONCE_SIZE / 2], bool tcp, uint16_t* encryptedResponseLen, const std::shared_ptr<DNSCryptCert>& cert) const
11e1e08b 748{
43234e76 749 assert(query != nullptr);
11e1e08b
RG
750 assert(queryLen > 0);
751 assert(querySize >= queryLen);
43234e76
RG
752 assert(encryptedResponseLen != nullptr);
753 assert(cert != nullptr);
754
11e1e08b 755 unsigned char nonce[DNSCRYPT_NONCE_SIZE];
43234e76 756 size_t requiredSize = sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE + queryLen;
11e1e08b
RG
757 /* this is not optimal, we should compute a random padding size, multiple of DNSCRYPT_PADDED_BLOCK_SIZE,
758 DNSCRYPT_PADDED_BLOCK_SIZE <= padding size <= 4096? */
759 uint16_t paddingSize = DNSCRYPT_PADDED_BLOCK_SIZE - (queryLen % DNSCRYPT_PADDED_BLOCK_SIZE);
760 requiredSize += paddingSize;
761
43234e76
RG
762 if (!tcp && requiredSize < DNSCryptQuery::s_minUDPLength) {
763 paddingSize += (DNSCryptQuery::s_minUDPLength - requiredSize);
764 requiredSize = DNSCryptQuery::s_minUDPLength;
11e1e08b
RG
765 }
766
767 if (requiredSize > querySize)
768 return ENOBUFS;
769
770 /* moving the existing query after the header + MAC */
43234e76 771 memmove(query + sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE, query, queryLen);
11e1e08b
RG
772
773 size_t pos = 0;
774 /* client magic */
43234e76
RG
775 memcpy(query + pos, cert->signedData.clientMagic, sizeof(cert->signedData.clientMagic));
776 pos += sizeof(cert->signedData.clientMagic);
11e1e08b
RG
777
778 /* client PK */
779 memcpy(query + pos, clientPublicKey, DNSCRYPT_PUBLIC_KEY_SIZE);
780 pos += DNSCRYPT_PUBLIC_KEY_SIZE;
781
782 /* client nonce */
783 memcpy(query + pos, clientNonce, DNSCRYPT_NONCE_SIZE / 2);
784 pos += DNSCRYPT_NONCE_SIZE / 2;
785 size_t encryptedPos = pos;
786
787 /* clear the MAC bytes */
788 memset(query + pos, 0, DNSCRYPT_MAC_SIZE);
789 pos += DNSCRYPT_MAC_SIZE;
790
791 /* skipping data */
792 pos += queryLen;
793
794 /* padding */
43234e76 795 query[pos] = static_cast<uint8_t>(0x80);
11e1e08b
RG
796 pos++;
797 memset(query + pos, 0, paddingSize - 1);
798 pos += paddingSize - 1;
799
800 memcpy(nonce, clientNonce, DNSCRYPT_NONCE_SIZE / 2);
801 memset(nonce + (DNSCRYPT_NONCE_SIZE / 2), 0, DNSCRYPT_NONCE_SIZE / 2);
802
43234e76
RG
803 const DNSCryptExchangeVersion version = getExchangeVersion(*cert);
804 int res = -1;
805
806 if (version == DNSCryptExchangeVersion::VERSION1) {
807 res = crypto_box_easy(reinterpret_cast<unsigned char*>(query + encryptedPos),
808 reinterpret_cast<unsigned char*>(query + encryptedPos + DNSCRYPT_MAC_SIZE),
809 queryLen + paddingSize,
810 nonce,
811 cert->signedData.resolverPK,
812 clientPrivateKey.key);
813 }
814 else if (version == DNSCryptExchangeVersion::VERSION2) {
815#ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
816 res = crypto_box_curve25519xchacha20poly1305_easy(reinterpret_cast<unsigned char*>(query + encryptedPos),
817 reinterpret_cast<unsigned char*>(query + encryptedPos + DNSCRYPT_MAC_SIZE),
818 queryLen + paddingSize,
819 nonce,
820 cert->signedData.resolverPK,
821 clientPrivateKey.key);
822#endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
823 }
824 else {
825 throw std::runtime_error("Unknown DNSCrypt exchange version");
826 }
11e1e08b
RG
827
828 if (res == 0) {
829 assert(pos == requiredSize);
830 *encryptedResponseLen = requiredSize;
831 }
832
833 return res;
834}
835
43234e76 836bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DNSCryptExchangeVersion version, DNSCryptCert& certOut, DNSCryptPrivateKey& keyOut)
6bb38cd6
RG
837{
838 bool success = false;
839 unsigned char providerPrivateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
840 sodium_mlock(providerPrivateKey, sizeof(providerPrivateKey));
841 sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
842
843 try {
844 ifstream providerKStream(providerPrivateKeyFile);
845 providerKStream.read((char*) providerPrivateKey, sizeof(providerPrivateKey));
846 if (providerKStream.fail()) {
847 providerKStream.close();
848 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile);
849 }
850
43234e76 851 DNSCryptContext::generateCertificate(serial, begin, end, version, providerPrivateKey, keyOut, certOut);
6bb38cd6
RG
852 success = true;
853 }
854 catch(const std::exception& e) {
855 errlog(e.what());
856 }
857
858 sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
859 sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey));
860 return success;
861}
862
11e1e08b 863#endif