• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 8332866939

18 Mar 2024 08:01PM UTC coverage: 59.243% (+0.03%) from 59.211%
8332866939

push

github

web-flow
Merge pull request #13931 from omoerbeek/rec-build-least

rec ci: also build a rec with all optional stuff disabled

32501 of 87978 branches covered (36.94%)

Branch coverage included in aggregate %.

113639 of 158702 relevant lines covered (71.61%)

3328629.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.2
/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
{
40✔
32
  sodium_memzero(key, sizeof(key));
40✔
33
  sodium_mlock(key, sizeof(key));
40✔
34
}
40✔
35

36
void DNSCryptPrivateKey::loadFromFile(const std::string& keyFile)
37
{
7✔
38
  ifstream file(keyFile);
7✔
39
  sodium_memzero(key, sizeof(key));
7✔
40
  file.read((char*) key, sizeof(key));
7✔
41

42
  if (file.fail()) {
7!
43
    sodium_memzero(key, sizeof(key));
×
44
    file.close();
×
45
    throw std::runtime_error("Invalid DNSCrypt key file " + keyFile);
×
46
  }
×
47

48
  file.close();
7✔
49
}
7✔
50

51
void DNSCryptPrivateKey::saveToFile(const std::string& keyFile) const
52
{
7✔
53
  ofstream file(keyFile);
7✔
54
  file.write(reinterpret_cast<const char*>(key), sizeof(key));
7✔
55
  file.close();
7✔
56
}
7✔
57

58
DNSCryptPrivateKey::~DNSCryptPrivateKey()
59
{
33✔
60
  sodium_munlock(key, sizeof(key));
33✔
61
}
33✔
62

63
DNSCryptExchangeVersion DNSCryptQuery::getVersion() const
64
{
40✔
65
  if (d_pair == nullptr) {
40!
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);
40✔
70
}
40✔
71

72
#ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
73
DNSCryptQuery::~DNSCryptQuery()
74
{
37✔
75
  if (d_sharedKeyComputed) {
37✔
76
    sodium_munlock(d_sharedKey, sizeof(d_sharedKey));
21✔
77
  }
21✔
78
}
37✔
79

80
int DNSCryptQuery::computeSharedKey()
81
{
40✔
82
  assert(d_pair != nullptr);
40✔
83

84
  int res = 0;
×
85

86
  if (d_sharedKeyComputed) {
40✔
87
    return res;
19✔
88
  }
19✔
89

90
  const DNSCryptExchangeVersion version = DNSCryptContext::getExchangeVersion(d_pair->cert);
21✔
91

92
  sodium_mlock(d_sharedKey, sizeof(d_sharedKey));
21✔
93

94
  if (version == DNSCryptExchangeVersion::VERSION1) {
21!
95
    res = crypto_box_beforenm(d_sharedKey,
21✔
96
                              d_header.clientPK,
21✔
97
                              d_pair->privateKey.key);
21✔
98
  }
21✔
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) {
21!
113
    sodium_munlock(d_sharedKey, sizeof(d_sharedKey));
×
114
    return res;
×
115
  }
×
116

117
  d_sharedKeyComputed = true;
21✔
118
  return res;
21✔
119
}
21✔
120
#else
121
DNSCryptQuery::~DNSCryptQuery()
122
{
123
}
124
#endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
125

126

127
DNSCryptContext::~DNSCryptContext() {
7✔
128
}
7✔
129

130
DNSCryptContext::DNSCryptContext(const std::string& pName, const std::vector<CertKeyPaths>& certKeys): d_certKeyPaths(certKeys), providerName(pName)
131
{
6✔
132
  reloadCertificates();
6✔
133
}
6✔
134

135
DNSCryptContext::DNSCryptContext(const std::string& pName, const DNSCryptCert& certificate, const DNSCryptPrivateKey& pKey): providerName(pName)
136
{
7✔
137
  addNewCertificate(certificate, pKey);
7✔
138
}
7✔
139

140
void DNSCryptContext::generateProviderKeys(unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE], unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE])
141
{
7✔
142
  int res = crypto_sign_ed25519_keypair(publicKey, privateKey);
7✔
143

144
  if (res != 0) {
7!
145
    throw std::runtime_error("Error generating DNSCrypt provider keys");
×
146
  }
×
147
}
7✔
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
{
19✔
167
  esVersion[0] = 0x00;
19✔
168

169
  if (version == DNSCryptExchangeVersion::VERSION1) {
19!
170
    esVersion[1] = { 0x01 };
19✔
171
  }
19✔
172
  else if (version == DNSCryptExchangeVersion::VERSION2) {
×
173
    esVersion[1] = { 0x02 };
×
174
  }
×
175
  else {
×
176
    throw std::runtime_error("Unknown DNSCrypt exchange version");
×
177
  }
×
178
}
19✔
179

180
DNSCryptExchangeVersion DNSCryptContext::getExchangeVersion(const unsigned char esVersion[sizeof(DNSCryptCert::esVersion)])
181
{
64✔
182
  if (esVersion[0] != 0x00) {
64!
183
    throw std::runtime_error("Unknown DNSCrypt exchange version");
×
184
  }
×
185

186
  if (esVersion[1] == 0x01) {
64!
187
    return DNSCryptExchangeVersion::VERSION1;
64✔
188
  }
64✔
189
  else if (esVersion[1] == 0x02) {
×
190
    return DNSCryptExchangeVersion::VERSION2;
×
191
  }
×
192

193
  throw std::runtime_error("Unknown DNSCrypt exchange version");
×
194
}
64✔
195

196
DNSCryptExchangeVersion DNSCryptContext::getExchangeVersion(const DNSCryptCert& cert)
197
{
64✔
198
  return getExchangeVersion(cert.esVersion);
64✔
199
}
64✔
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
{
19✔
204
  unsigned char magic[DNSCRYPT_CERT_MAGIC_SIZE] = DNSCRYPT_CERT_MAGIC_VALUE;
19✔
205
  unsigned char protocolMinorVersion[] = DNSCRYPT_CERT_PROTOCOL_MINOR_VERSION_VALUE;
19✔
206
  unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE];
19✔
207
  unsigned char esVersion[sizeof(DNSCryptCert::esVersion)];
19✔
208
  setExchangeVersion(version, esVersion);
19✔
209

210
  generateResolverKeyPair(privateKey, pubK);
19✔
211

212
  memcpy(cert.magic, magic, sizeof(magic));
19✔
213
  memcpy(cert.esVersion, esVersion, sizeof(esVersion));
19✔
214
  memcpy(cert.protocolMinorVersion, protocolMinorVersion, sizeof(protocolMinorVersion));
19✔
215
  memcpy(cert.signedData.resolverPK, pubK, sizeof(cert.signedData.resolverPK));
19✔
216
  memcpy(cert.signedData.clientMagic, pubK, sizeof(cert.signedData.clientMagic));
19✔
217
  cert.signedData.serial = htonl(serial);
19✔
218
  // coverity[store_truncates_time_t]
219
  cert.signedData.tsStart = htonl((uint32_t) begin);
19✔
220
  // coverity[store_truncates_time_t]
221
  cert.signedData.tsEnd = htonl((uint32_t) end);
19✔
222

223
  unsigned long long signatureSize = 0;
19✔
224

225
  int res = crypto_sign_ed25519(cert.signature,
19✔
226
                                &signatureSize,
19✔
227
                                (unsigned char*) &cert.signedData,
19✔
228
                                sizeof(cert.signedData),
19✔
229
                                providerPrivateKey);
19✔
230

231
  if (res == 0) {
19!
232
    assert(signatureSize == sizeof(DNSCryptCertSignedData) + DNSCRYPT_SIGNATURE_SIZE);
19✔
233
  }
19✔
234
  else {
×
235
    throw std::runtime_error("Error generating DNSCrypt certificate");
×
236
  }
×
237
}
19✔
238

239
void DNSCryptContext::loadCertFromFile(const std::string&filename, DNSCryptCert& dest)
240
{
7✔
241
  ifstream file(filename);
7✔
242
  file.read((char *) &dest, sizeof(dest));
7✔
243

244
  if (file.fail())
7!
245
    throw std::runtime_error("Invalid dnscrypt certificate file " + filename);
×
246

247
  file.close();
7✔
248
}
7✔
249

250
void DNSCryptContext::saveCertFromFile(const DNSCryptCert& cert, const std::string&filename)
251
{
7✔
252
  ofstream file(filename);
7✔
253
  file.write(reinterpret_cast<const char *>(&cert), sizeof(cert));
7✔
254
  file.close();
7✔
255
}
7✔
256

257
void DNSCryptContext::generateResolverKeyPair(DNSCryptPrivateKey& privK, unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE])
258
{
23✔
259
  int res = crypto_box_keypair(pubK, privK.key);
23✔
260

261
  if (res != 0) {
23!
262
    throw std::runtime_error("Error generating DNSCrypt resolver keys");
×
263
  }
×
264
}
23✔
265

266
void DNSCryptContext::computePublicKeyFromPrivate(const DNSCryptPrivateKey& privK, unsigned char* pubK)
267
{
19✔
268
  int res = crypto_scalarmult_base(pubK,
19✔
269
                                   privK.key);
19✔
270

271
  if (res != 0) {
19!
272
    throw std::runtime_error("Error computing dnscrypt public key from the private one");
×
273
  }
×
274
}
19✔
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
{
13✔
290
  auto certs = d_certs.write_lock();
13✔
291

292
  for (const auto& pair : *certs) {
16✔
293
    if (pair->cert.getSerial() == newCert->cert.getSerial()) {
9!
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
  }
9✔
303

304
  certs->push_back(newCert);
13✔
305
}
13✔
306

307
void DNSCryptContext::addNewCertificate(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, bool active, bool reload)
308
{
12✔
309
  auto pair = std::make_shared<DNSCryptCertificatePair>();
12✔
310
  pair->cert = newCert;
12✔
311
  pair->privateKey = newKey;
12✔
312
  computePublicKeyFromPrivate(pair->privateKey, pair->publicKey);
12✔
313
  pair->active = active;
12✔
314

315
  addNewCertificate(pair, reload);
12✔
316
}
12✔
317

318
std::shared_ptr<DNSCryptCertificatePair> DNSCryptContext::loadCertificatePair(const std::string& certFile, const std::string& keyFile)
319
{
7✔
320
  auto pair = std::make_shared<DNSCryptCertificatePair>();
7✔
321
  loadCertFromFile(certFile, pair->cert);
7✔
322
  pair->privateKey.loadFromFile(keyFile);
7✔
323
  pair->active = true;
7✔
324
  computePublicKeyFromPrivate(pair->privateKey, pair->publicKey);
7✔
325
  return pair;
7✔
326
}
7✔
327

328
void DNSCryptContext::loadNewCertificate(const std::string& certFile, const std::string& keyFile, bool active, bool reload)
329
{
1✔
330
  auto newPair = DNSCryptContext::loadCertificatePair(certFile, keyFile);
1✔
331
  newPair->active = active;
1✔
332
  addNewCertificate(newPair, reload);
1✔
333
  d_certKeyPaths.write_lock()->push_back({certFile, keyFile});
1✔
334
}
1✔
335

336
void DNSCryptContext::reloadCertificates()
337
{
6✔
338
  std::vector<std::shared_ptr<DNSCryptCertificatePair>> newCerts;
6✔
339
  {
6✔
340
    auto paths = d_certKeyPaths.read_lock();
6✔
341
    newCerts.reserve(paths->size());
6✔
342
    for (const auto& pair : *paths) {
6✔
343
      newCerts.push_back(DNSCryptContext::loadCertificatePair(pair.cert, pair.key));
6✔
344
    }
6✔
345
  }
6✔
346
    
347
  {
6✔
348
    *(d_certs.write_lock()) = std::move(newCerts);
6✔
349
  }
6✔
350
}
6✔
351

352
std::vector<std::shared_ptr<DNSCryptCertificatePair>> DNSCryptContext::getCertificates() {
4✔
353
  std::vector<std::shared_ptr<DNSCryptCertificatePair>> ret = *(d_certs.read_lock());
4✔
354
  return ret;
4✔
355
};
4✔
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
{
5✔
370
  for (const auto& pair : *d_certs.write_lock()) {
8!
371
    if (pair->active == true && pair->cert.getSerial() == serial) {
8!
372
      pair->active = false;
5✔
373
      return;
5✔
374
    }
5✔
375
  }
8✔
376
  throw std::runtime_error("No active certificate found with this serial");
×
377
}
5✔
378

379
void DNSCryptContext::removeInactiveCertificate(uint32_t serial)
380
{
4✔
381
  auto certs = d_certs.write_lock();
4✔
382

383
  for (auto it = certs->begin(); it != certs->end(); ) {
4!
384
    if ((*it)->active == false && (*it)->cert.getSerial() == serial) {
4!
385
      it = certs->erase(it);
4✔
386
      return;
4✔
387
    } else {
4✔
388
      it++;
×
389
    }
×
390
  }
4✔
391
  throw std::runtime_error("No inactive certificate found with this serial");
×
392
}
4✔
393

394
bool DNSCryptQuery::parsePlaintextQuery(const PacketBuffer& packet)
395
{
16✔
396
  assert(d_ctx != nullptr);
16✔
397

398
  if (packet.size() < sizeof(dnsheader)) {
16!
399
    return false;
×
400
  }
×
401

402
  const dnsheader_aligned dh(packet.data());
16✔
403
  if (dh->qr || ntohs(dh->qdcount) != 1 || dh->ancount != 0 || dh->nscount != 0 || static_cast<uint8_t>(dh->opcode) != Opcode::Query) {
16!
404
    return false;
2✔
405
  }
2✔
406

407
  unsigned int qnameWireLength;
14✔
408
  uint16_t qtype, qclass;
14✔
409
  DNSName qname(reinterpret_cast<const char*>(packet.data()), packet.size(), sizeof(dnsheader), false, &qtype, &qclass, &qnameWireLength);
14✔
410
  if ((packet.size() - sizeof(dnsheader)) < (qnameWireLength + sizeof(qtype) + sizeof(qclass))) {
14!
411
    return false;
×
412
  }
×
413

414
  if (qtype != QType::TXT || qclass != QClass::IN) {
14!
415
    return false;
1✔
416
  }
1✔
417

418
  if (qname != d_ctx->getProviderName()) {
13✔
419
    return false;
1✔
420
  }
1✔
421

422
  d_qname = std::move(qname);
12✔
423
  d_id = dh->id;
12✔
424
  d_valid = true;
12✔
425

426
  return true;
12✔
427
}
13✔
428

429
void DNSCryptContext::getCertificateResponse(time_t now, const DNSName& qname, uint16_t qid, PacketBuffer& response)
430
{
12✔
431
  GenericDNSPacketWriter<PacketBuffer> pw(response, qname, QType::TXT, QClass::IN, Opcode::Query);
12✔
432
  struct dnsheader * dh = pw.getHeader();
12✔
433
  dh->id = qid;
12✔
434
  dh->qr = true;
12✔
435
  dh->rcode = RCode::NoError;
12✔
436

437
  auto certs = d_certs.read_lock();
12✔
438
  for (const auto& pair : *certs) {
16✔
439
    if (!pair->active || !pair->cert.isValid(now)) {
16!
440
      continue;
×
441
    }
×
442

443
    pw.startRecord(qname, QType::TXT, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL), QClass::IN, DNSResourceRecord::ANSWER, true);
16✔
444
    std::string scert;
16✔
445
    uint8_t certSize = sizeof(pair->cert);
16✔
446
    scert.assign((const char*) &certSize, sizeof(certSize));
16✔
447
    scert.append((const char*) &pair->cert, certSize);
16✔
448

449
    pw.xfrBlob(scert);
16✔
450
    pw.commit();
16✔
451
  }
16✔
452
}
12✔
453

454
bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery& query, time_t now)
455
{
23✔
456
  const unsigned char* magic = query.getClientMagic();
23✔
457

458
  auto certs = d_certs.read_lock();
23✔
459
  for (const auto& pair : *certs) {
31✔
460
    if (pair->cert.isValid(now) && memcmp(magic, pair->cert.signedData.clientMagic, DNSCRYPT_CLIENT_MAGIC_SIZE) == 0) {
31!
461
      query.setCertificatePair(pair);
21✔
462
      return true;
21✔
463
    }
21✔
464
  }
31✔
465

466
  return false;
2✔
467
}
23✔
468

469
bool DNSCryptQuery::isEncryptedQuery(const PacketBuffer& packet, bool tcp, time_t now)
470
{
37✔
471
  assert(d_ctx != nullptr);
37✔
472

473
  d_encrypted = false;
×
474

475
  if (packet.size() < sizeof(DNSCryptQueryHeader)) {
37✔
476
    return false;
14✔
477
  }
14✔
478

479
  if (!tcp && packet.size() < DNSCryptQuery::s_minUDPLength) {
23!
480
    return false;
×
481
  }
×
482

483
  const struct DNSCryptQueryHeader* header = reinterpret_cast<const struct DNSCryptQueryHeader*>(packet.data());
23✔
484

485
  d_header = *header;
23✔
486

487
  if (!d_ctx->magicMatchesAPublicKey(*this, now)) {
23✔
488
    return false;
2✔
489
  }
2✔
490

491
  d_encrypted = true;
21✔
492

493
  return true;
21✔
494
}
23✔
495

496
void DNSCryptQuery::getDecrypted(bool tcp, PacketBuffer& packet)
497
{
21✔
498
  assert(d_encrypted);
21✔
499
  assert(d_pair != nullptr);
×
500
  assert(d_valid == false);
×
501

502
#ifdef DNSCRYPT_STRICT_PADDING_LENGTH
503
  if (tcp && ((packet.size() - sizeof(DNSCryptQueryHeader)) % DNSCRYPT_PADDED_BLOCK_SIZE) != 0) {
504
    vinfolog("Dropping encrypted query with invalid size of %d (should be a multiple of %d)", (packet.size() - sizeof(DNSCryptQueryHeader)), DNSCRYPT_PADDED_BLOCK_SIZE);
505
    return;
506
  }
507
#endif
508

509
  unsigned char nonce[DNSCRYPT_NONCE_SIZE];
×
510
  static_assert(sizeof(nonce) == (2* sizeof(d_header.clientNonce)), "Nonce should be larger than clientNonce (half)");
21✔
511
  static_assert(sizeof(d_header.clientPK) == DNSCRYPT_PUBLIC_KEY_SIZE, "Client Public key size is not right");
21✔
512
  static_assert(sizeof(d_pair->privateKey.key) == DNSCRYPT_PRIVATE_KEY_SIZE, "Private key size is not right");
21✔
513

514
  memcpy(nonce, &d_header.clientNonce, sizeof(d_header.clientNonce));
21✔
515
  memset(nonce + sizeof(d_header.clientNonce), 0, sizeof(nonce) - sizeof(d_header.clientNonce));
21✔
516

517
#ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
21✔
518
  int res = computeSharedKey();
21✔
519
  if (res != 0) {
21!
520
    vinfolog("Dropping encrypted query we can't compute the shared key for");
×
521
    return;
×
522
  }
×
523

524
  const DNSCryptExchangeVersion version = getVersion();
21✔
525

526
  if (version == DNSCryptExchangeVersion::VERSION1) {
21!
527
    res = crypto_box_open_easy_afternm(reinterpret_cast<unsigned char*>(packet.data()),
21✔
528
                                       reinterpret_cast<unsigned char*>(&packet.at(sizeof(DNSCryptQueryHeader))),
21✔
529
                                       packet.size() - sizeof(DNSCryptQueryHeader),
21✔
530
                                       nonce,
21✔
531
                                       d_sharedKey);
21✔
532
  }
21✔
533
  else if (version == DNSCryptExchangeVersion::VERSION2) {
×
534
#ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
×
535
    res = crypto_box_curve25519xchacha20poly1305_open_easy_afternm(reinterpret_cast<unsigned char*>(packet.data()),
×
536
                                                                   reinterpret_cast<unsigned char*>(&packet.at(sizeof(DNSCryptQueryHeader))),
×
537
                                                                   packet.size() - sizeof(DNSCryptQueryHeader),
×
538
                                                                   nonce,
×
539
                                                                   d_sharedKey);
×
540
#else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
541
    res = -1;
542
#endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
543
  } else {
×
544
    res = -1;
×
545
  }
×
546

547
#else /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
548
  int res = crypto_box_open_easy(reinterpret_cast<unsigned char*>(packet.data()),
549
                                 reinterpret_cast<unsigned char*>(&packet.at(sizeof(DNSCryptQueryHeader))),
550
                                 packet.size() - sizeof(DNSCryptQueryHeader),
551
                                 nonce,
552
                                 d_header.clientPK,
553
                                 d_pair->privateKey.key);
554
#endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
555

556
  if (res != 0) {
21!
557
    vinfolog("Dropping encrypted query we can't decrypt");
×
558
    return;
×
559
  }
×
560

561
  uint16_t decryptedQueryLen = packet.size() - sizeof(DNSCryptQueryHeader) - DNSCRYPT_MAC_SIZE;
21✔
562
  uint16_t pos = decryptedQueryLen;
21✔
563
  assert(pos < packet.size());
21✔
564
  d_paddedLen = decryptedQueryLen;
×
565

566
  while (pos > 0 && packet.at(pos - 1) == 0) pos--;
2,092!
567

568
  if (pos == 0 || packet.at(pos - 1) != 0x80) {
21!
569
    vinfolog("Dropping encrypted query with invalid padding value");
×
570
    return;
×
571
  }
×
572

573
  pos--;
21✔
574

575
  size_t paddingLen = decryptedQueryLen - pos;
21✔
576
  packet.resize(pos);
21✔
577

578
  if (tcp && paddingLen > DNSCRYPT_MAX_TCP_PADDING_SIZE) {
21!
579
    vinfolog("Dropping encrypted query with too long padding size");
×
580
    return;
×
581
  }
×
582

583
  d_len = pos;
21✔
584
  d_valid = true;
21✔
585
}
21✔
586

587
void DNSCryptQuery::getCertificateResponse(time_t now, PacketBuffer& response) const
588
{
12✔
589
  assert(d_ctx != nullptr);
12✔
590
  d_ctx->getCertificateResponse(now, d_qname, d_id, response);
×
591
}
12✔
592

593
void DNSCryptQuery::parsePacket(PacketBuffer& packet, bool tcp, time_t now)
594
{
37✔
595
  d_valid = false;
37✔
596

597
  /* might be a plaintext certificate request or an authenticated request */
598
  if (isEncryptedQuery(packet, tcp, now)) {
37✔
599
    getDecrypted(tcp, packet);
21✔
600
  }
21✔
601
  else {
16✔
602
    parsePlaintextQuery(packet);
16✔
603
  }
16✔
604
}
37✔
605

606
void DNSCryptQuery::fillServerNonce(unsigned char* nonce) const
607
{
19✔
608
  uint32_t* dest = reinterpret_cast<uint32_t*>(nonce);
19✔
609
  static const size_t nonceSize = DNSCRYPT_NONCE_SIZE / 2;
19✔
610

611
  for (size_t pos = 0; pos < (nonceSize / sizeof(*dest)); pos++)
76✔
612
  {
57✔
613
    const uint32_t value = randombytes_random();
57✔
614
    memcpy(dest + pos, &value, sizeof(value));
57✔
615
  }
57✔
616
}
19✔
617

618
/*
619
   "The length of <resolver-response-pad> must be between 0 and 256 bytes,
620
   and must be constant for a given (<resolver-sk>, <client-nonce>) tuple."
621
*/
622
uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen, size_t maxLen) const
623
{
19✔
624
  size_t paddedSize = 0;
19✔
625
  uint16_t result = 0;
19✔
626
  uint32_t rnd = 0;
19✔
627
  assert(d_header.clientNonce);
19✔
628
  assert(d_pair != nullptr);
×
629

630
  unsigned char nonce[DNSCRYPT_NONCE_SIZE];
×
631
  memcpy(nonce, d_header.clientNonce, (DNSCRYPT_NONCE_SIZE / 2));
19✔
632
  memcpy(&(nonce[DNSCRYPT_NONCE_SIZE / 2]), d_header.clientNonce, (DNSCRYPT_NONCE_SIZE / 2));
19✔
633
  crypto_stream((unsigned char*) &rnd, sizeof(rnd), nonce, d_pair->privateKey.key);
19✔
634

635
  paddedSize = unpaddedLen + rnd % (maxLen - unpaddedLen + 1);
19✔
636
  paddedSize += DNSCRYPT_PADDED_BLOCK_SIZE - (paddedSize % DNSCRYPT_PADDED_BLOCK_SIZE);
19✔
637

638
  if (paddedSize > maxLen)
19!
639
    paddedSize = maxLen;
×
640

641
  result = paddedSize - unpaddedLen;
19✔
642

643
  return result;
19✔
644
}
19✔
645

646
int DNSCryptQuery::encryptResponse(PacketBuffer& response, size_t maxResponseSize, bool tcp)
647
{
19✔
648
  struct DNSCryptResponseHeader responseHeader;
19✔
649
  assert(response.size() > 0);
19✔
650
  assert(maxResponseSize >= response.size());
×
651
  assert(d_encrypted == true);
×
652
  assert(d_pair != nullptr);
×
653

654
  /* a DNSCrypt UDP response can't be larger than the (padded) DNSCrypt query */
655
  if (!tcp && d_paddedLen < response.size()) {
19✔
656
    /* so we need to truncate it */
657
    size_t questionSize = 0;
1✔
658

659
    if (response.size() > sizeof(dnsheader)) {
1!
660
      unsigned int qnameWireLength = 0;
1✔
661
      DNSName tempQName(reinterpret_cast<const char*>(response.data()), response.size(), sizeof(dnsheader), false, 0, 0, &qnameWireLength);
1✔
662
      if (qnameWireLength > 0) {
1!
663
        questionSize = qnameWireLength + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
1✔
664
      }
1✔
665
    }
1✔
666

667
    response.resize(sizeof(dnsheader) + questionSize);
1✔
668

669
    if (response.size() > d_paddedLen) {
1!
670
      /* that does not seem right but let's truncate even more */
671
      response.resize(d_paddedLen);
×
672
    }
×
673
    struct dnsheader* dh = reinterpret_cast<struct dnsheader*>(response.data());
1✔
674
    dh->ancount = dh->arcount = dh->nscount = 0;
1✔
675
    dh->tc = 1;
1✔
676
  }
1✔
677

678
  size_t requiredSize = sizeof(responseHeader) + DNSCRYPT_MAC_SIZE + response.size();
19✔
679
  size_t maxSize = std::min(maxResponseSize, requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE);
19✔
680
  uint16_t paddingSize = computePaddingSize(requiredSize, maxSize);
19✔
681
  requiredSize += paddingSize;
19✔
682

683
  if (requiredSize > maxResponseSize) {
19!
684
    return ENOBUFS;
×
685
  }
×
686

687
  memcpy(&responseHeader.nonce, &d_header.clientNonce, sizeof d_header.clientNonce);
19✔
688
  fillServerNonce(&(responseHeader.nonce[sizeof(d_header.clientNonce)]));
19✔
689

690
  size_t responseLen = response.size();
19✔
691
  /* moving the existing response after the header + MAC */
692
  response.resize(requiredSize);
19✔
693
  std::copy_backward(response.begin(), response.begin() + responseLen, response.begin() + responseLen + sizeof(responseHeader) + DNSCRYPT_MAC_SIZE);
19✔
694

695
  uint16_t pos = 0;
19✔
696
  /* copying header */
697
  memcpy(&response.at(pos), &responseHeader, sizeof(responseHeader));
19✔
698
  pos += sizeof(responseHeader);
19✔
699
  /* setting MAC bytes to 0 */
700
  memset(&response.at(pos), 0, DNSCRYPT_MAC_SIZE);
19✔
701
  pos += DNSCRYPT_MAC_SIZE;
19✔
702
  uint16_t toEncryptPos = pos;
19✔
703
  /* skipping response */
704
  pos += responseLen;
19✔
705
  /* padding */
706
  response.at(pos) = static_cast<uint8_t>(0x80);
19✔
707
  pos++;
19✔
708
  memset(&response.at(pos), 0, paddingSize - 1);
19✔
709
  pos += (paddingSize - 1);
19✔
710

711
  /* encrypting */
712
#ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
19✔
713
  int res = computeSharedKey();
19✔
714
  if (res != 0) {
19!
715
    return res;
×
716
  }
×
717

718
  const DNSCryptExchangeVersion version = getVersion();
19✔
719

720
  if (version == DNSCryptExchangeVersion::VERSION1) {
19!
721
    res = crypto_box_easy_afternm(reinterpret_cast<unsigned char*>(&response.at(sizeof(responseHeader))),
19✔
722
                                  reinterpret_cast<unsigned char*>(&response.at(toEncryptPos)),
19✔
723
                                  responseLen + paddingSize,
19✔
724
                                  responseHeader.nonce,
19✔
725
                                  d_sharedKey);
19✔
726
  }
19✔
727
  else if (version == DNSCryptExchangeVersion::VERSION2) {
×
728
#ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
×
729
    res = crypto_box_curve25519xchacha20poly1305_easy_afternm(reinterpret_cast<unsigned char*>(&response.at(sizeof(responseHeader))),
×
730
                                                              reinterpret_cast<unsigned char*>(&response.at(toEncryptPos)),
×
731
                                                              responseLen + paddingSize,
×
732
                                                              responseHeader.nonce,
×
733
                                                              d_sharedKey);
×
734
#else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
735
    res = -1;
736
#endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
737
  }
×
738
  else {
×
739
    res = -1;
×
740
  }
×
741
#else
742
  int res = crypto_box_easy(reinterpret_cast<unsigned char*>(&response.at(sizeof(responseHeader))),
743
                            reinterpret_cast<unsigned char*>(&response.at(toEncryptPos)),
744
                            responseLen + paddingSize,
745
                            responseHeader.nonce,
746
                            d_header.clientPK,
747
                            d_pair->privateKey.key);
748
#endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
749

750
  if (res == 0) {
19!
751
    assert(pos == requiredSize);
19✔
752
  }
19✔
753

754
  return res;
×
755
}
19✔
756

757
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
758
{
4✔
759
  assert(packet.size() > 0);
4✔
760
  assert(cert != nullptr);
×
761

762
  size_t queryLen = packet.size();
×
763
  unsigned char nonce[DNSCRYPT_NONCE_SIZE];
4✔
764
  size_t requiredSize = sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE + queryLen;
4✔
765
  /* this is not optimal, we should compute a random padding size, multiple of DNSCRYPT_PADDED_BLOCK_SIZE,
766
     DNSCRYPT_PADDED_BLOCK_SIZE <= padding size <= 4096? */
767
  uint16_t paddingSize = DNSCRYPT_PADDED_BLOCK_SIZE - (queryLen % DNSCRYPT_PADDED_BLOCK_SIZE);
4✔
768
  requiredSize += paddingSize;
4✔
769

770
  if (!tcp && requiredSize < DNSCryptQuery::s_minUDPLength) {
4!
771
    paddingSize += (DNSCryptQuery::s_minUDPLength - requiredSize);
4✔
772
    requiredSize = DNSCryptQuery::s_minUDPLength;
4✔
773
  }
4✔
774

775
  if (requiredSize > maximumSize) {
4✔
776
    return ENOBUFS;
1✔
777
  }
1✔
778

779
  /* moving the existing query after the header + MAC */
780
  packet.resize(requiredSize);
3✔
781
  std::copy_backward(packet.begin(), packet.begin() + queryLen, packet.begin() + queryLen + sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE);
3✔
782

783
  size_t pos = 0;
3✔
784
  /* client magic */
785
  memcpy(&packet.at(pos), cert->signedData.clientMagic, sizeof(cert->signedData.clientMagic));
3✔
786
  pos += sizeof(cert->signedData.clientMagic);
3✔
787

788
  /* client PK */
789
  memcpy(&packet.at(pos), clientPublicKey, DNSCRYPT_PUBLIC_KEY_SIZE);
3✔
790
  pos += DNSCRYPT_PUBLIC_KEY_SIZE;
3✔
791

792
  /* client nonce */
793
  memcpy(&packet.at(pos), clientNonce, DNSCRYPT_NONCE_SIZE / 2);
3✔
794
  pos += DNSCRYPT_NONCE_SIZE / 2;
3✔
795
  size_t encryptedPos = pos;
3✔
796

797
  /* clear the MAC bytes */
798
  memset(&packet.at(pos), 0, DNSCRYPT_MAC_SIZE);
3✔
799
  pos += DNSCRYPT_MAC_SIZE;
3✔
800

801
  /* skipping data */
802
  pos += queryLen;
3✔
803

804
  /* padding */
805
  packet.at(pos) = static_cast<uint8_t>(0x80);
3✔
806
  pos++;
3✔
807
  memset(&packet.at(pos), 0, paddingSize - 1);
3✔
808
  pos += paddingSize - 1;
3✔
809

810
  memcpy(nonce, clientNonce, DNSCRYPT_NONCE_SIZE / 2);
3✔
811
  memset(nonce + (DNSCRYPT_NONCE_SIZE / 2), 0, DNSCRYPT_NONCE_SIZE / 2);
3✔
812

813
  const DNSCryptExchangeVersion version = getExchangeVersion(*cert);
3✔
814
  int res = -1;
3✔
815

816
  if (version == DNSCryptExchangeVersion::VERSION1) {
3!
817
    res = crypto_box_easy(reinterpret_cast<unsigned char*>(&packet.at(encryptedPos)),
3✔
818
                          reinterpret_cast<unsigned char*>(&packet.at(encryptedPos + DNSCRYPT_MAC_SIZE)),
3✔
819
                          queryLen + paddingSize,
3✔
820
                          nonce,
3✔
821
                          cert->signedData.resolverPK,
3✔
822
                          clientPrivateKey.key);
3✔
823
  }
3✔
824
  else if (version == DNSCryptExchangeVersion::VERSION2) {
×
825
#ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
×
826
    res = crypto_box_curve25519xchacha20poly1305_easy(reinterpret_cast<unsigned char*>(&packet.at(encryptedPos)),
×
827
                                                      reinterpret_cast<unsigned char*>(&packet.at(encryptedPos + DNSCRYPT_MAC_SIZE)),
×
828
                                                      queryLen + paddingSize,
×
829
                                                      nonce,
×
830
                                                      cert->signedData.resolverPK,
×
831
                                                      clientPrivateKey.key);
×
832
#endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
×
833
  }
×
834
  else {
×
835
    throw std::runtime_error("Unknown DNSCrypt exchange version");
×
836
  }
×
837

838
  if (res == 0) {
3!
839
    assert(pos == requiredSize);
3✔
840
  }
3✔
841

842
  return res;
×
843
}
3✔
844

845
bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DNSCryptExchangeVersion version, DNSCryptCert& certOut, DNSCryptPrivateKey& keyOut)
846
{
10✔
847
  bool success = false;
10✔
848
  unsigned char providerPrivateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
10✔
849
  sodium_mlock(providerPrivateKey, sizeof(providerPrivateKey));
10✔
850
  sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
10✔
851

852
  try {
10✔
853
    ifstream providerKStream(providerPrivateKeyFile);
10✔
854
    providerKStream.read((char*) providerPrivateKey, sizeof(providerPrivateKey));
10✔
855
    if (providerKStream.fail()) {
10!
856
      providerKStream.close();
×
857
      throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile);
×
858
    }
×
859

860
    DNSCryptContext::generateCertificate(serial, begin, end, version, providerPrivateKey, keyOut, certOut);
10✔
861
    success = true;
10✔
862
  }
10✔
863
  catch(const std::exception& e) {
10✔
864
    errlog(e.what());
×
865
  }
×
866

867
  sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
10✔
868
  sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey));
10✔
869
  return success;
10✔
870
}
10✔
871

872
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc