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