2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
25 #include <boost/format.hpp>
27 #include "dnscrypt.hh"
28 #include "dnswriter.hh"
30 DNSCryptPrivateKey::DNSCryptPrivateKey()
32 sodium_memzero(key
, sizeof(key
));
33 sodium_mlock(key
, sizeof(key
));
36 void DNSCryptPrivateKey::loadFromFile(const std::string
& keyFile
)
38 ifstream
file(keyFile
);
39 sodium_memzero(key
, sizeof(key
));
40 file
.read((char*) key
, sizeof(key
));
43 sodium_memzero(key
, sizeof(key
));
45 throw std::runtime_error("Invalid DNSCrypt key file " + keyFile
);
51 void DNSCryptPrivateKey::saveToFile(const std::string
& keyFile
) const
53 ofstream
file(keyFile
);
54 file
.write(reinterpret_cast<const char*>(key
), sizeof(key
));
58 DNSCryptPrivateKey::~DNSCryptPrivateKey()
60 sodium_munlock(key
, sizeof(key
));
63 DNSCryptExchangeVersion
DNSCryptQuery::getVersion() const
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");
69 return DNSCryptContext::getExchangeVersion(d_pair
->cert
);
72 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
73 DNSCryptQuery::~DNSCryptQuery()
75 if (d_sharedKeyComputed
) {
76 sodium_munlock(d_sharedKey
, sizeof(d_sharedKey
));
80 int DNSCryptQuery::computeSharedKey()
82 assert(d_pair
!= nullptr);
86 if (d_sharedKeyComputed
) {
90 const DNSCryptExchangeVersion version
= DNSCryptContext::getExchangeVersion(d_pair
->cert
);
92 sodium_mlock(d_sharedKey
, sizeof(d_sharedKey
));
94 if (version
== DNSCryptExchangeVersion::VERSION1
) {
95 res
= crypto_box_beforenm(d_sharedKey
,
97 d_pair
->privateKey
.key
);
99 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
100 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
101 res
= crypto_box_curve25519xchacha20poly1305_beforenm(d_sharedKey
,
103 d_pair
->privateKey
.key
);
104 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
106 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
113 sodium_munlock(d_sharedKey
, sizeof(d_sharedKey
));
117 d_sharedKeyComputed
= true;
121 DNSCryptQuery::~DNSCryptQuery()
124 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
127 DNSCryptContext::~DNSCryptContext() {
130 DNSCryptContext::DNSCryptContext(const std::string
& pName
, const std::vector
<CertKeyPaths
>& certKeys
): d_certKeyPaths(certKeys
), providerName(pName
)
132 reloadCertificates();
135 DNSCryptContext::DNSCryptContext(const std::string
& pName
, const DNSCryptCert
& certificate
, const DNSCryptPrivateKey
& pKey
): providerName(pName
)
137 addNewCertificate(certificate
, pKey
);
140 void DNSCryptContext::generateProviderKeys(unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
], unsigned char privateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
])
142 int res
= crypto_sign_ed25519_keypair(publicKey
, privateKey
);
145 throw std::runtime_error("Error generating DNSCrypt provider keys");
149 std::string
DNSCryptContext::getProviderFingerprint(unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
])
151 boost::format
fmt("%02X%02X");
154 for (size_t idx
= 0; idx
< DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
; idx
+= 2)
156 ret
<< (fmt
% static_cast<int>(publicKey
[idx
]) % static_cast<int>(publicKey
[idx
+1]));
157 if (idx
< (DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
- 2)) {
165 void DNSCryptContext::setExchangeVersion(const DNSCryptExchangeVersion
& version
, unsigned char esVersion
[sizeof(DNSCryptCert::esVersion
)])
169 if (version
== DNSCryptExchangeVersion::VERSION1
) {
170 esVersion
[1] = { 0x01 };
172 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
173 esVersion
[1] = { 0x02 };
176 throw std::runtime_error("Unknown DNSCrypt exchange version");
180 DNSCryptExchangeVersion
DNSCryptContext::getExchangeVersion(const unsigned char esVersion
[sizeof(DNSCryptCert::esVersion
)])
182 if (esVersion
[0] != 0x00) {
183 throw std::runtime_error("Unknown DNSCrypt exchange version");
186 if (esVersion
[1] == 0x01) {
187 return DNSCryptExchangeVersion::VERSION1
;
189 else if (esVersion
[1] == 0x02) {
190 return DNSCryptExchangeVersion::VERSION2
;
193 throw std::runtime_error("Unknown DNSCrypt exchange version");
196 DNSCryptExchangeVersion
DNSCryptContext::getExchangeVersion(const DNSCryptCert
& cert
)
198 return getExchangeVersion(cert
.esVersion
);
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
)
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
);
210 generateResolverKeyPair(privateKey
, pubK
);
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
);
223 unsigned long long signatureSize
= 0;
225 int res
= crypto_sign_ed25519(cert
.signature
,
227 (unsigned char*) &cert
.signedData
,
228 sizeof(cert
.signedData
),
232 assert(signatureSize
== sizeof(DNSCryptCertSignedData
) + DNSCRYPT_SIGNATURE_SIZE
);
235 throw std::runtime_error("Error generating DNSCrypt certificate");
239 void DNSCryptContext::loadCertFromFile(const std::string
&filename
, DNSCryptCert
& dest
)
241 ifstream
file(filename
);
242 file
.read((char *) &dest
, sizeof(dest
));
245 throw std::runtime_error("Invalid dnscrypt certificate file " + filename
);
250 void DNSCryptContext::saveCertFromFile(const DNSCryptCert
& cert
, const std::string
&filename
)
252 ofstream
file(filename
);
253 file
.write(reinterpret_cast<const char *>(&cert
), sizeof(cert
));
257 void DNSCryptContext::generateResolverKeyPair(DNSCryptPrivateKey
& privK
, unsigned char pubK
[DNSCRYPT_PUBLIC_KEY_SIZE
])
259 int res
= crypto_box_keypair(pubK
, privK
.key
);
262 throw std::runtime_error("Error generating DNSCrypt resolver keys");
266 void DNSCryptContext::computePublicKeyFromPrivate(const DNSCryptPrivateKey
& privK
, unsigned char* pubK
)
268 int res
= crypto_scalarmult_base(pubK
,
272 throw std::runtime_error("Error computing dnscrypt public key from the private one");
276 std::string
DNSCryptContext::certificateDateToStr(uint32_t date
)
279 time_t tdate
= static_cast<time_t>(ntohl(date
));
282 localtime_r(&tdate
, &date_tm
);
283 strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S", &date_tm
);
288 void DNSCryptContext::addNewCertificate(std::shared_ptr
<DNSCryptCertificatePair
>& newCert
, bool reload
)
290 auto certs
= d_certs
.write_lock();
292 for (const auto& pair
: *certs
) {
293 if (pair
->cert
.getSerial() == newCert
->cert
.getSerial()) {
295 /* on reload we just assume that this is the same certificate */
299 throw std::runtime_error("Error adding a new certificate: we already have a certificate with the same serial");
304 certs
->push_back(newCert
);
307 void DNSCryptContext::addNewCertificate(const DNSCryptCert
& newCert
, const DNSCryptPrivateKey
& newKey
, bool active
, bool reload
)
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
;
315 addNewCertificate(pair
, reload
);
318 std::shared_ptr
<DNSCryptCertificatePair
> DNSCryptContext::loadCertificatePair(const std::string
& certFile
, const std::string
& keyFile
)
320 auto pair
= std::make_shared
<DNSCryptCertificatePair
>();
321 loadCertFromFile(certFile
, pair
->cert
);
322 pair
->privateKey
.loadFromFile(keyFile
);
324 computePublicKeyFromPrivate(pair
->privateKey
, pair
->publicKey
);
328 void DNSCryptContext::loadNewCertificate(const std::string
& certFile
, const std::string
& keyFile
, bool active
, bool reload
)
330 auto newPair
= DNSCryptContext::loadCertificatePair(certFile
, keyFile
);
331 newPair
->active
= active
;
332 addNewCertificate(newPair
, reload
);
333 d_certKeyPaths
.write_lock()->push_back({certFile
, keyFile
});
336 void DNSCryptContext::reloadCertificates()
338 std::vector
<std::shared_ptr
<DNSCryptCertificatePair
>> newCerts
;
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
));
348 *(d_certs
.write_lock()) = std::move(newCerts
);
352 std::vector
<std::shared_ptr
<DNSCryptCertificatePair
>> DNSCryptContext::getCertificates() {
353 std::vector
<std::shared_ptr
<DNSCryptCertificatePair
>> ret
= *(d_certs
.read_lock());
357 void DNSCryptContext::markActive(uint32_t serial
)
359 for (const auto& pair
: *d_certs
.write_lock()) {
360 if (pair
->active
== false && pair
->cert
.getSerial() == serial
) {
365 throw std::runtime_error("No inactive certificate found with this serial");
368 void DNSCryptContext::markInactive(uint32_t serial
)
370 for (const auto& pair
: *d_certs
.write_lock()) {
371 if (pair
->active
== true && pair
->cert
.getSerial() == serial
) {
372 pair
->active
= false;
376 throw std::runtime_error("No active certificate found with this serial");
379 void DNSCryptContext::removeInactiveCertificate(uint32_t serial
)
381 auto certs
= d_certs
.write_lock();
383 for (auto it
= certs
->begin(); it
!= certs
->end(); ) {
384 if ((*it
)->active
== false && (*it
)->cert
.getSerial() == serial
) {
385 it
= certs
->erase(it
);
391 throw std::runtime_error("No inactive certificate found with this serial");
394 bool DNSCryptQuery::parsePlaintextQuery(const PacketBuffer
& packet
)
396 assert(d_ctx
!= nullptr);
398 if (packet
.size() < sizeof(dnsheader
)) {
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
)
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
))) {
413 if (qtype
!= QType::TXT
|| qclass
!= QClass::IN
) {
417 if (qname
!= d_ctx
->getProviderName()) {
428 void DNSCryptContext::getCertificateResponse(time_t now
, const DNSName
& qname
, uint16_t qid
, PacketBuffer
& response
)
430 GenericDNSPacketWriter
<PacketBuffer
> pw(response
, qname
, QType::TXT
, QClass::IN
, Opcode::Query
);
431 struct dnsheader
* dh
= pw
.getHeader();
434 dh
->rcode
= RCode::NoError
;
436 auto certs
= d_certs
.read_lock();
437 for (const auto& pair
: *certs
) {
438 if (!pair
->active
|| !pair
->cert
.isValid(now
)) {
442 pw
.startRecord(qname
, QType::TXT
, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL
), QClass::IN
, DNSResourceRecord::ANSWER
, true);
444 uint8_t certSize
= sizeof(pair
->cert
);
445 scert
.assign((const char*) &certSize
, sizeof(certSize
));
446 scert
.append((const char*) &pair
->cert
, certSize
);
453 bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery
& query
, time_t now
)
455 const unsigned char* magic
= query
.getClientMagic();
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
);
468 bool DNSCryptQuery::isEncryptedQuery(const PacketBuffer
& packet
, bool tcp
, time_t now
)
470 assert(d_ctx
!= nullptr);
474 if (packet
.size() < sizeof(DNSCryptQueryHeader
)) {
478 if (!tcp
&& packet
.size() < DNSCryptQuery::s_minUDPLength
) {
482 const struct DNSCryptQueryHeader
* header
= reinterpret_cast<const struct DNSCryptQueryHeader
*>(packet
.data());
486 if (!d_ctx
->magicMatchesAPublicKey(*this, now
)) {
495 void DNSCryptQuery::getDecrypted(bool tcp
, PacketBuffer
& packet
)
498 assert(d_pair
!= nullptr);
499 assert(d_valid
== false);
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
);
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");
513 memcpy(nonce
, &d_header
.clientNonce
, sizeof(d_header
.clientNonce
));
514 memset(nonce
+ sizeof(d_header
.clientNonce
), 0, sizeof(nonce
) - sizeof(d_header
.clientNonce
));
516 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
517 int res
= computeSharedKey();
519 vinfolog("Dropping encrypted query we can't compute the shared key for");
523 const DNSCryptExchangeVersion version
= getVersion();
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
),
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
),
539 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
541 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
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
),
552 d_pair
->privateKey
.key
);
553 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
556 vinfolog("Dropping encrypted query we can't decrypt");
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
;
565 while (pos
> 0 && packet
.at(pos
- 1) == 0) pos
--;
567 if (pos
== 0 || packet
.at(pos
- 1) != 0x80) {
568 vinfolog("Dropping encrypted query with invalid padding value");
574 size_t paddingLen
= decryptedQueryLen
- pos
;
577 if (tcp
&& paddingLen
> DNSCRYPT_MAX_TCP_PADDING_SIZE
) {
578 vinfolog("Dropping encrypted query with too long padding size");
586 void DNSCryptQuery::getCertificateResponse(time_t now
, PacketBuffer
& response
) const
588 assert(d_ctx
!= nullptr);
589 d_ctx
->getCertificateResponse(now
, d_qname
, d_id
, response
);
592 void DNSCryptQuery::parsePacket(PacketBuffer
& packet
, bool tcp
, time_t now
)
596 /* might be a plaintext certificate request or an authenticated request */
597 if (isEncryptedQuery(packet
, tcp
, now
)) {
598 getDecrypted(tcp
, packet
);
601 parsePlaintextQuery(packet
);
605 void DNSCryptQuery::fillServerNonce(unsigned char* nonce
) const
607 uint32_t* dest
= reinterpret_cast<uint32_t*>(nonce
);
608 static const size_t nonceSize
= DNSCRYPT_NONCE_SIZE
/ 2;
610 for (size_t pos
= 0; pos
< (nonceSize
/ sizeof(*dest
)); pos
++)
612 const uint32_t value
= randombytes_random();
613 memcpy(dest
+ pos
, &value
, sizeof(value
));
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."
621 uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen
, size_t maxLen
) const
623 size_t paddedSize
= 0;
626 assert(d_header
.clientNonce
);
627 assert(d_pair
!= nullptr);
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
);
634 paddedSize
= unpaddedLen
+ rnd
% (maxLen
- unpaddedLen
+ 1);
635 paddedSize
+= DNSCRYPT_PADDED_BLOCK_SIZE
- (paddedSize
% DNSCRYPT_PADDED_BLOCK_SIZE
);
637 if (paddedSize
> maxLen
)
640 result
= paddedSize
- unpaddedLen
;
645 int DNSCryptQuery::encryptResponse(PacketBuffer
& response
, size_t maxResponseSize
, bool tcp
)
647 struct DNSCryptResponseHeader responseHeader
;
648 assert(response
.size() > 0);
649 assert(maxResponseSize
>= response
.size());
650 assert(d_encrypted
== true);
651 assert(d_pair
!= nullptr);
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;
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
;
666 response
.resize(sizeof(dnsheader
) + questionSize
);
668 if (response
.size() > d_paddedLen
) {
669 /* that does not seem right but let's truncate even more */
670 response
.resize(d_paddedLen
);
672 struct dnsheader
* dh
= reinterpret_cast<struct dnsheader
*>(response
.data());
673 dh
->ancount
= dh
->arcount
= dh
->nscount
= 0;
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
;
682 if (requiredSize
> maxResponseSize
) {
686 memcpy(&responseHeader
.nonce
, &d_header
.clientNonce
, sizeof d_header
.clientNonce
);
687 fillServerNonce(&(responseHeader
.nonce
[sizeof(d_header
.clientNonce
)]));
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
);
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 */
705 response
.at(pos
) = static_cast<uint8_t>(0x80);
707 memset(&response
.at(pos
), 0, paddingSize
- 1);
708 pos
+= (paddingSize
- 1);
711 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
712 int res
= computeSharedKey();
717 const DNSCryptExchangeVersion version
= getVersion();
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
,
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
,
733 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
735 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
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
,
746 d_pair
->privateKey
.key
);
747 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
750 assert(pos
== requiredSize
);
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
758 assert(packet
.size() > 0);
759 assert(cert
!= nullptr);
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
;
769 if (!tcp
&& requiredSize
< DNSCryptQuery::s_minUDPLength
) {
770 paddingSize
+= (DNSCryptQuery::s_minUDPLength
- requiredSize
);
771 requiredSize
= DNSCryptQuery::s_minUDPLength
;
774 if (requiredSize
> maximumSize
) {
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
);
784 memcpy(&packet
.at(pos
), cert
->signedData
.clientMagic
, sizeof(cert
->signedData
.clientMagic
));
785 pos
+= sizeof(cert
->signedData
.clientMagic
);
788 memcpy(&packet
.at(pos
), clientPublicKey
, DNSCRYPT_PUBLIC_KEY_SIZE
);
789 pos
+= DNSCRYPT_PUBLIC_KEY_SIZE
;
792 memcpy(&packet
.at(pos
), clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
793 pos
+= DNSCRYPT_NONCE_SIZE
/ 2;
794 size_t encryptedPos
= pos
;
796 /* clear the MAC bytes */
797 memset(&packet
.at(pos
), 0, DNSCRYPT_MAC_SIZE
);
798 pos
+= DNSCRYPT_MAC_SIZE
;
804 packet
.at(pos
) = static_cast<uint8_t>(0x80);
806 memset(&packet
.at(pos
), 0, paddingSize
- 1);
807 pos
+= paddingSize
- 1;
809 memcpy(nonce
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
810 memset(nonce
+ (DNSCRYPT_NONCE_SIZE
/ 2), 0, DNSCRYPT_NONCE_SIZE
/ 2);
812 const DNSCryptExchangeVersion version
= getExchangeVersion(*cert
);
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
,
820 cert
->signedData
.resolverPK
,
821 clientPrivateKey
.key
);
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
,
829 cert
->signedData
.resolverPK
,
830 clientPrivateKey
.key
);
831 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
834 throw std::runtime_error("Unknown DNSCrypt exchange version");
838 assert(pos
== requiredSize
);
844 bool generateDNSCryptCertificate(const std::string
& providerPrivateKeyFile
, uint32_t serial
, time_t begin
, time_t end
, DNSCryptExchangeVersion version
, DNSCryptCert
& certOut
, DNSCryptPrivateKey
& keyOut
)
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
));
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
);
859 DNSCryptContext::generateCertificate(serial
, begin
, end
, version
, providerPrivateKey
, keyOut
, certOut
);
862 catch(const std::exception
& e
) {
866 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
867 sodium_munlock(providerPrivateKey
, sizeof(providerPrivateKey
));