]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnscrypt.cc
Merge pull request #7653 from pieterlexis/docker-ignore
[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 "dolog.hh"
26 #include "dnscrypt.hh"
27 #include "dnswriter.hh"
28 #include "lock.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((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 DNSCryptContext::DNSCryptContext(const std::string& pName, const std::string& certFile, const std::string& keyFile): providerName(pName)
127 {
128 pthread_rwlock_init(&d_lock, 0);
129
130 loadNewCertificate(certFile, keyFile);
131 }
132
133 DNSCryptContext::DNSCryptContext(const std::string& pName, const DNSCryptCert& certificate, const DNSCryptPrivateKey& pKey): providerName(pName)
134 {
135 pthread_rwlock_init(&d_lock, 0);
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 cert.signedData.tsStart = htonl((uint32_t) begin);
219 cert.signedData.tsEnd = htonl((uint32_t) end);
220
221 unsigned long long signatureSize = 0;
222
223 int res = crypto_sign_ed25519(cert.signature,
224 &signatureSize,
225 (unsigned char*) &cert.signedData,
226 sizeof(cert.signedData),
227 providerPrivateKey);
228
229 if (res == 0) {
230 assert(signatureSize == sizeof(DNSCryptCertSignedData) + DNSCRYPT_SIGNATURE_SIZE);
231 }
232 else {
233 throw std::runtime_error("Error generating DNSCrypt certificate");
234 }
235 }
236
237 void DNSCryptContext::loadCertFromFile(const std::string&filename, DNSCryptCert& dest)
238 {
239 ifstream file(filename);
240 file.read((char *) &dest, sizeof(dest));
241
242 if (file.fail())
243 throw std::runtime_error("Invalid dnscrypt certificate file " + filename);
244
245 file.close();
246 }
247
248 void DNSCryptContext::saveCertFromFile(const DNSCryptCert& cert, const std::string&filename)
249 {
250 ofstream file(filename);
251 file.write((char *) &cert, sizeof(cert));
252 file.close();
253 }
254
255 void DNSCryptContext::generateResolverKeyPair(DNSCryptPrivateKey& privK, unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE])
256 {
257 int res = crypto_box_keypair(pubK, privK.key);
258
259 if (res != 0) {
260 throw std::runtime_error("Error generating DNSCrypt resolver keys");
261 }
262 }
263
264 void DNSCryptContext::computePublicKeyFromPrivate(const DNSCryptPrivateKey& privK, unsigned char* pubK)
265 {
266 int res = crypto_scalarmult_base(pubK,
267 privK.key);
268
269 if (res != 0) {
270 throw std::runtime_error("Error computing dnscrypt public key from the private one");
271 }
272 }
273
274 std::string DNSCryptContext::certificateDateToStr(uint32_t date)
275 {
276 char buf[20];
277 time_t tdate = static_cast<time_t>(ntohl(date));
278 struct tm date_tm;
279
280 localtime_r(&tdate, &date_tm);
281 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &date_tm);
282
283 return string(buf);
284 }
285
286 void DNSCryptContext::addNewCertificate(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, bool active, bool reload)
287 {
288 WriteLock w(&d_lock);
289
290 for (auto pair : certs) {
291 if (pair->cert.getSerial() == newCert.getSerial()) {
292 if (reload) {
293 /* on reload we just assume that this is the same certificate */
294 return;
295 }
296 else {
297 throw std::runtime_error("Error adding a new certificate: we already have a certificate with the same serial");
298 }
299 }
300 }
301
302 auto pair = std::make_shared<DNSCryptCertificatePair>();
303 pair->cert = newCert;
304 pair->privateKey = newKey;
305 computePublicKeyFromPrivate(pair->privateKey, pair->publicKey);
306 pair->active = active;
307 certs.push_back(pair);
308 }
309
310 void DNSCryptContext::loadNewCertificate(const std::string& certFile, const std::string& keyFile, bool active, bool reload)
311 {
312 DNSCryptCert newCert;
313 DNSCryptPrivateKey newPrivateKey;
314
315 loadCertFromFile(certFile, newCert);
316 newPrivateKey.loadFromFile(keyFile);
317
318 addNewCertificate(newCert, newPrivateKey, active, reload);
319 certificatePath = certFile;
320 keyPath = keyFile;
321 }
322
323 void DNSCryptContext::reloadCertificate()
324 {
325 loadNewCertificate(certificatePath, keyPath, true, true);
326 }
327
328 void DNSCryptContext::markActive(uint32_t serial)
329 {
330 WriteLock w(&d_lock);
331
332 for (auto pair : certs) {
333 if (pair->active == false && pair->cert.getSerial() == serial) {
334 pair->active = true;
335 return;
336 }
337 }
338 throw std::runtime_error("No inactive certificate found with this serial");
339 }
340
341 void DNSCryptContext::markInactive(uint32_t serial)
342 {
343 WriteLock w(&d_lock);
344
345 for (auto pair : certs) {
346 if (pair->active == true && pair->cert.getSerial() == serial) {
347 pair->active = false;
348 return;
349 }
350 }
351 throw std::runtime_error("No active certificate found with this serial");
352 }
353
354 void DNSCryptContext::removeInactiveCertificate(uint32_t serial)
355 {
356 WriteLock w(&d_lock);
357
358 for (auto it = certs.begin(); it != certs.end(); ) {
359 if ((*it)->active == false && (*it)->cert.getSerial() == serial) {
360 it = certs.erase(it);
361 return;
362 } else {
363 it++;
364 }
365 }
366 throw std::runtime_error("No inactive certificate found with this serial");
367 }
368
369 bool DNSCryptQuery::parsePlaintextQuery(const char * packet, uint16_t packetSize)
370 {
371 assert(d_ctx != nullptr);
372
373 if (packetSize < sizeof(dnsheader)) {
374 return false;
375 }
376
377 const struct dnsheader * dh = reinterpret_cast<const struct dnsheader *>(packet);
378 if (dh->qr || ntohs(dh->qdcount) != 1 || dh->ancount != 0 || dh->nscount != 0 || dh->opcode != Opcode::Query)
379 return false;
380
381 unsigned int consumed;
382 uint16_t qtype, qclass;
383 DNSName qname(packet, packetSize, sizeof(dnsheader), false, &qtype, &qclass, &consumed);
384 if ((packetSize - sizeof(dnsheader)) < (consumed + sizeof(qtype) + sizeof(qclass)))
385 return false;
386
387 if (qtype != QType::TXT || qclass != QClass::IN)
388 return false;
389
390 if (qname != d_ctx->getProviderName())
391 return false;
392
393 d_qname = qname;
394 d_id = dh->id;
395 d_valid = true;
396
397 return true;
398 }
399
400 void DNSCryptContext::getCertificateResponse(time_t now, const DNSName& qname, uint16_t qid, std::vector<uint8_t>& response)
401 {
402 DNSPacketWriter pw(response, qname, QType::TXT, QClass::IN, Opcode::Query);
403 struct dnsheader * dh = pw.getHeader();
404 dh->id = qid;
405 dh->qr = true;
406 dh->rcode = RCode::NoError;
407
408 ReadLock r(&d_lock);
409 for (const auto pair : certs) {
410 if (!pair->active || !pair->cert.isValid(now)) {
411 continue;
412 }
413
414 pw.startRecord(qname, QType::TXT, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL), QClass::IN, DNSResourceRecord::ANSWER, true);
415 std::string scert;
416 uint8_t certSize = sizeof(pair->cert);
417 scert.assign((const char*) &certSize, sizeof(certSize));
418 scert.append((const char*) &pair->cert, certSize);
419
420 pw.xfrBlob(scert);
421 pw.commit();
422 }
423 }
424
425 bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery& query, time_t now)
426 {
427 const unsigned char* magic = query.getClientMagic();
428
429 ReadLock r(&d_lock);
430 for (const auto& pair : certs) {
431 if (pair->cert.isValid(now) && memcmp(magic, pair->cert.signedData.clientMagic, DNSCRYPT_CLIENT_MAGIC_SIZE) == 0) {
432 query.setCertificatePair(pair);
433 return true;
434 }
435 }
436
437 return false;
438 }
439
440 bool DNSCryptQuery::isEncryptedQuery(const char * packet, uint16_t packetSize, bool tcp, time_t now)
441 {
442 assert(d_ctx != nullptr);
443
444 d_encrypted = false;
445
446 if (packetSize < sizeof(DNSCryptQueryHeader)) {
447 return false;
448 }
449
450 if (!tcp && packetSize < DNSCryptQuery::s_minUDPLength) {
451 return false;
452 }
453
454 const struct DNSCryptQueryHeader* header = reinterpret_cast<const struct DNSCryptQueryHeader*>(packet);
455
456 d_header = *header;
457
458 if (!d_ctx->magicMatchesAPublicKey(*this, now)) {
459 return false;
460 }
461
462 d_encrypted = true;
463
464 return true;
465 }
466
467 void DNSCryptQuery::getDecrypted(bool tcp, char* packet, uint16_t packetSize, uint16_t* decryptedQueryLen)
468 {
469 assert(decryptedQueryLen != nullptr);
470 assert(d_encrypted);
471 assert(d_pair != nullptr);
472 assert(d_valid == false);
473
474 #ifdef DNSCRYPT_STRICT_PADDING_LENGTH
475 if (tcp && ((packetSize - sizeof(DNSCryptQueryHeader)) % DNSCRYPT_PADDED_BLOCK_SIZE) != 0) {
476 vinfolog("Dropping encrypted query with invalid size of %d (should be a multiple of %d)", (packetSize - sizeof(DNSCryptQueryHeader)), DNSCRYPT_PADDED_BLOCK_SIZE);
477 return;
478 }
479 #endif
480
481 unsigned char nonce[DNSCRYPT_NONCE_SIZE];
482 static_assert(sizeof(nonce) == (2* sizeof(d_header.clientNonce)), "Nonce should be larger than clientNonce (half)");
483 static_assert(sizeof(d_header.clientPK) == DNSCRYPT_PUBLIC_KEY_SIZE, "Client Publick key size is not right");
484 static_assert(sizeof(d_pair->privateKey.key) == DNSCRYPT_PRIVATE_KEY_SIZE, "Private key size is not right");
485
486 memcpy(nonce, &d_header.clientNonce, sizeof(d_header.clientNonce));
487 memset(nonce + sizeof(d_header.clientNonce), 0, sizeof(nonce) - sizeof(d_header.clientNonce));
488
489 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
490 int res = computeSharedKey();
491 if (res != 0) {
492 vinfolog("Dropping encrypted query we can't compute the shared key for");
493 return;
494 }
495
496 const DNSCryptExchangeVersion version = getVersion();
497
498 if (version == DNSCryptExchangeVersion::VERSION1) {
499 res = crypto_box_open_easy_afternm(reinterpret_cast<unsigned char*>(packet),
500 reinterpret_cast<unsigned char*>(packet + sizeof(DNSCryptQueryHeader)),
501 packetSize - sizeof(DNSCryptQueryHeader),
502 nonce,
503 d_sharedKey);
504 }
505 else if (version == DNSCryptExchangeVersion::VERSION2) {
506 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
507 res = crypto_box_curve25519xchacha20poly1305_open_easy_afternm(reinterpret_cast<unsigned char*>(packet),
508 reinterpret_cast<unsigned char*>(packet + sizeof(DNSCryptQueryHeader)),
509 packetSize - sizeof(DNSCryptQueryHeader),
510 nonce,
511 d_sharedKey);
512 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
513 res = -1;
514 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
515 } else {
516 res = -1;
517 }
518
519 #else /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
520 int res = crypto_box_open_easy(reinterpret_cast<unsigned char*>(packet),
521 reinterpret_cast<unsigned char*>(packet + sizeof(DNSCryptQueryHeader)),
522 packetSize - sizeof(DNSCryptQueryHeader),
523 nonce,
524 d_header.clientPK,
525 d_pair->privateKey.key);
526 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
527
528 if (res != 0) {
529 vinfolog("Dropping encrypted query we can't decrypt");
530 return;
531 }
532
533 *decryptedQueryLen = packetSize - sizeof(DNSCryptQueryHeader) - DNSCRYPT_MAC_SIZE;
534 uint16_t pos = *decryptedQueryLen;
535 assert(pos < packetSize);
536 d_paddedLen = *decryptedQueryLen;
537
538 while(pos > 0 && packet[pos - 1] == 0) pos--;
539
540 if (pos == 0 || static_cast<uint8_t>(packet[pos - 1]) != 0x80) {
541 vinfolog("Dropping encrypted query with invalid padding value");
542 return;
543 }
544
545 pos--;
546
547 size_t paddingLen = *decryptedQueryLen - pos;
548 *decryptedQueryLen = pos;
549
550 if (tcp && paddingLen > DNSCRYPT_MAX_TCP_PADDING_SIZE) {
551 vinfolog("Dropping encrypted query with too long padding size");
552 return;
553 }
554
555 d_len = pos;
556 d_valid = true;
557 }
558
559 void DNSCryptQuery::getCertificateResponse(time_t now, std::vector<uint8_t>& response) const
560 {
561 assert(d_ctx != nullptr);
562 d_ctx->getCertificateResponse(now, d_qname, d_id, response);
563 }
564
565 void DNSCryptQuery::parsePacket(char* packet, uint16_t packetSize, bool tcp, uint16_t* decryptedQueryLen, time_t now)
566 {
567 assert(packet != nullptr);
568 assert(decryptedQueryLen != nullptr);
569
570 d_valid = false;
571
572 /* might be a plaintext certificate request or an authenticated request */
573 if (isEncryptedQuery(packet, packetSize, tcp, now)) {
574 getDecrypted(tcp, packet, packetSize, decryptedQueryLen);
575 }
576 else {
577 parsePlaintextQuery(packet, packetSize);
578 }
579 }
580
581 void DNSCryptQuery::fillServerNonce(unsigned char* nonce) const
582 {
583 uint32_t* dest = reinterpret_cast<uint32_t*>(nonce);
584 static const size_t nonceSize = DNSCRYPT_NONCE_SIZE / 2;
585
586 for (size_t pos = 0; pos < (nonceSize / sizeof(*dest)); pos++)
587 {
588 const uint32_t value = randombytes_random();
589 memcpy(dest + pos, &value, sizeof(value));
590 }
591 }
592
593 /*
594 "The length of <resolver-response-pad> must be between 0 and 256 bytes,
595 and must be constant for a given (<resolver-sk>, <client-nonce>) tuple."
596 */
597 uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen, size_t maxLen) const
598 {
599 size_t paddedSize = 0;
600 uint16_t result = 0;
601 uint32_t rnd = 0;
602 assert(d_header.clientNonce);
603 assert(d_pair != nullptr);
604
605 unsigned char nonce[DNSCRYPT_NONCE_SIZE];
606 memcpy(nonce, d_header.clientNonce, (DNSCRYPT_NONCE_SIZE / 2));
607 memcpy(&(nonce[DNSCRYPT_NONCE_SIZE / 2]), d_header.clientNonce, (DNSCRYPT_NONCE_SIZE / 2));
608 crypto_stream((unsigned char*) &rnd, sizeof(rnd), nonce, d_pair->privateKey.key);
609
610 paddedSize = unpaddedLen + rnd % (maxLen - unpaddedLen + 1);
611 paddedSize += DNSCRYPT_PADDED_BLOCK_SIZE - (paddedSize % DNSCRYPT_PADDED_BLOCK_SIZE);
612
613 if (paddedSize > maxLen)
614 paddedSize = maxLen;
615
616 result = paddedSize - unpaddedLen;
617
618 return result;
619 }
620
621 int DNSCryptQuery::encryptResponse(char* response, uint16_t responseLen, uint16_t responseSize, bool tcp, uint16_t* encryptedResponseLen)
622 {
623 struct DNSCryptResponseHeader responseHeader;
624 assert(response != nullptr);
625 assert(responseLen > 0);
626 assert(responseSize >= responseLen);
627 assert(encryptedResponseLen != nullptr);
628 assert(d_encrypted == true);
629 assert(d_pair != nullptr);
630
631 if (!tcp && d_paddedLen < responseLen) {
632 struct dnsheader* dh = reinterpret_cast<struct dnsheader*>(response);
633 size_t questionSize = 0;
634
635 if (responseLen > sizeof(dnsheader)) {
636 unsigned int consumed = 0;
637 DNSName tempQName(response, responseLen, sizeof(dnsheader), false, 0, 0, &consumed);
638 if (consumed > 0) {
639 questionSize = consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
640 }
641 }
642
643 responseLen = sizeof(dnsheader) + questionSize;
644
645 if (responseLen > d_paddedLen) {
646 responseLen = d_paddedLen;
647 }
648 dh->ancount = dh->arcount = dh->nscount = 0;
649 dh->tc = 1;
650 }
651
652 size_t requiredSize = sizeof(responseHeader) + DNSCRYPT_MAC_SIZE + responseLen;
653 size_t maxSize = (responseSize > (requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE)) ? (requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE) : responseSize;
654 uint16_t paddingSize = computePaddingSize(requiredSize, maxSize);
655 requiredSize += paddingSize;
656
657 if (requiredSize > responseSize)
658 return ENOBUFS;
659
660 memcpy(&responseHeader.nonce, &d_header.clientNonce, sizeof d_header.clientNonce);
661 fillServerNonce(&(responseHeader.nonce[sizeof(d_header.clientNonce)]));
662
663 /* moving the existing response after the header + MAC */
664 memmove(response + sizeof(responseHeader) + DNSCRYPT_MAC_SIZE, response, responseLen);
665
666 uint16_t pos = 0;
667 /* copying header */
668 memcpy(response + pos, &responseHeader, sizeof(responseHeader));
669 pos += sizeof(responseHeader);
670 /* setting MAC bytes to 0 */
671 memset(response + pos, 0, DNSCRYPT_MAC_SIZE);
672 pos += DNSCRYPT_MAC_SIZE;
673 uint16_t toEncryptPos = pos;
674 /* skipping response */
675 pos += responseLen;
676 /* padding */
677 response[pos] = static_cast<uint8_t>(0x80);
678 pos++;
679 memset(response + pos, 0, paddingSize - 1);
680 pos += (paddingSize - 1);
681
682 /* encrypting */
683 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
684 int res = computeSharedKey();
685 if (res != 0) {
686 return res;
687 }
688
689 const DNSCryptExchangeVersion version = getVersion();
690
691 if (version == DNSCryptExchangeVersion::VERSION1) {
692 res = crypto_box_easy_afternm(reinterpret_cast<unsigned char*>(response + sizeof(responseHeader)),
693 reinterpret_cast<unsigned char*>(response + toEncryptPos),
694 responseLen + paddingSize,
695 responseHeader.nonce,
696 d_sharedKey);
697 }
698 else if (version == DNSCryptExchangeVersion::VERSION2) {
699 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
700 res = crypto_box_curve25519xchacha20poly1305_easy_afternm(reinterpret_cast<unsigned char*>(response + sizeof(responseHeader)),
701 reinterpret_cast<unsigned char*>(response + toEncryptPos),
702 responseLen + paddingSize,
703 responseHeader.nonce,
704 d_sharedKey);
705 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
706 res = -1;
707 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
708 }
709 else {
710 res = -1;
711 }
712 #else
713 int res = crypto_box_easy(reinterpret_cast<unsigned char*>(response + sizeof(responseHeader)),
714 reinterpret_cast<unsigned char*>(response + toEncryptPos),
715 responseLen + paddingSize,
716 responseHeader.nonce,
717 d_header.clientPK,
718 d_pair->privateKey.key);
719 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
720
721 if (res == 0) {
722 assert(pos == requiredSize);
723 *encryptedResponseLen = requiredSize;
724 }
725
726 return res;
727 }
728
729 int 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
730 {
731 assert(query != nullptr);
732 assert(queryLen > 0);
733 assert(querySize >= queryLen);
734 assert(encryptedResponseLen != nullptr);
735 assert(cert != nullptr);
736
737 unsigned char nonce[DNSCRYPT_NONCE_SIZE];
738 size_t requiredSize = sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE + queryLen;
739 /* this is not optimal, we should compute a random padding size, multiple of DNSCRYPT_PADDED_BLOCK_SIZE,
740 DNSCRYPT_PADDED_BLOCK_SIZE <= padding size <= 4096? */
741 uint16_t paddingSize = DNSCRYPT_PADDED_BLOCK_SIZE - (queryLen % DNSCRYPT_PADDED_BLOCK_SIZE);
742 requiredSize += paddingSize;
743
744 if (!tcp && requiredSize < DNSCryptQuery::s_minUDPLength) {
745 paddingSize += (DNSCryptQuery::s_minUDPLength - requiredSize);
746 requiredSize = DNSCryptQuery::s_minUDPLength;
747 }
748
749 if (requiredSize > querySize)
750 return ENOBUFS;
751
752 /* moving the existing query after the header + MAC */
753 memmove(query + sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE, query, queryLen);
754
755 size_t pos = 0;
756 /* client magic */
757 memcpy(query + pos, cert->signedData.clientMagic, sizeof(cert->signedData.clientMagic));
758 pos += sizeof(cert->signedData.clientMagic);
759
760 /* client PK */
761 memcpy(query + pos, clientPublicKey, DNSCRYPT_PUBLIC_KEY_SIZE);
762 pos += DNSCRYPT_PUBLIC_KEY_SIZE;
763
764 /* client nonce */
765 memcpy(query + pos, clientNonce, DNSCRYPT_NONCE_SIZE / 2);
766 pos += DNSCRYPT_NONCE_SIZE / 2;
767 size_t encryptedPos = pos;
768
769 /* clear the MAC bytes */
770 memset(query + pos, 0, DNSCRYPT_MAC_SIZE);
771 pos += DNSCRYPT_MAC_SIZE;
772
773 /* skipping data */
774 pos += queryLen;
775
776 /* padding */
777 query[pos] = static_cast<uint8_t>(0x80);
778 pos++;
779 memset(query + pos, 0, paddingSize - 1);
780 pos += paddingSize - 1;
781
782 memcpy(nonce, clientNonce, DNSCRYPT_NONCE_SIZE / 2);
783 memset(nonce + (DNSCRYPT_NONCE_SIZE / 2), 0, DNSCRYPT_NONCE_SIZE / 2);
784
785 const DNSCryptExchangeVersion version = getExchangeVersion(*cert);
786 int res = -1;
787
788 if (version == DNSCryptExchangeVersion::VERSION1) {
789 res = crypto_box_easy(reinterpret_cast<unsigned char*>(query + encryptedPos),
790 reinterpret_cast<unsigned char*>(query + encryptedPos + DNSCRYPT_MAC_SIZE),
791 queryLen + paddingSize,
792 nonce,
793 cert->signedData.resolverPK,
794 clientPrivateKey.key);
795 }
796 else if (version == DNSCryptExchangeVersion::VERSION2) {
797 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
798 res = crypto_box_curve25519xchacha20poly1305_easy(reinterpret_cast<unsigned char*>(query + encryptedPos),
799 reinterpret_cast<unsigned char*>(query + encryptedPos + DNSCRYPT_MAC_SIZE),
800 queryLen + paddingSize,
801 nonce,
802 cert->signedData.resolverPK,
803 clientPrivateKey.key);
804 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
805 }
806 else {
807 throw std::runtime_error("Unknown DNSCrypt exchange version");
808 }
809
810 if (res == 0) {
811 assert(pos == requiredSize);
812 *encryptedResponseLen = requiredSize;
813 }
814
815 return res;
816 }
817
818 bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DNSCryptExchangeVersion version, DNSCryptCert& certOut, DNSCryptPrivateKey& keyOut)
819 {
820 bool success = false;
821 unsigned char providerPrivateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
822 sodium_mlock(providerPrivateKey, sizeof(providerPrivateKey));
823 sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
824
825 try {
826 ifstream providerKStream(providerPrivateKeyFile);
827 providerKStream.read((char*) providerPrivateKey, sizeof(providerPrivateKey));
828 if (providerKStream.fail()) {
829 providerKStream.close();
830 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile);
831 }
832
833 DNSCryptContext::generateCertificate(serial, begin, end, version, providerPrivateKey, keyOut, certOut);
834 success = true;
835 }
836 catch(const std::exception& e) {
837 errlog(e.what());
838 }
839
840 sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
841 sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey));
842 return success;
843 }
844
845 #endif