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