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.
26 #include "dnscrypt.hh"
27 #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((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 */
126 DNSCryptContext::DNSCryptContext(const std::string
& pName
, const std::vector
<CertKeyPaths
>& certKeys
): d_certKeyPaths(certKeys
), providerName(pName
)
128 pthread_rwlock_init(&d_lock
, 0);
130 reloadCertificates();
133 DNSCryptContext::DNSCryptContext(const std::string
& pName
, const DNSCryptCert
& certificate
, const DNSCryptPrivateKey
& pKey
): providerName(pName
)
135 pthread_rwlock_init(&d_lock
, 0);
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 cert
.signedData
.tsStart
= htonl((uint32_t) begin
);
219 cert
.signedData
.tsEnd
= htonl((uint32_t) end
);
221 unsigned long long signatureSize
= 0;
223 int res
= crypto_sign_ed25519(cert
.signature
,
225 (unsigned char*) &cert
.signedData
,
226 sizeof(cert
.signedData
),
230 assert(signatureSize
== sizeof(DNSCryptCertSignedData
) + DNSCRYPT_SIGNATURE_SIZE
);
233 throw std::runtime_error("Error generating DNSCrypt certificate");
237 void DNSCryptContext::loadCertFromFile(const std::string
&filename
, DNSCryptCert
& dest
)
239 ifstream
file(filename
);
240 file
.read((char *) &dest
, sizeof(dest
));
243 throw std::runtime_error("Invalid dnscrypt certificate file " + filename
);
248 void DNSCryptContext::saveCertFromFile(const DNSCryptCert
& cert
, const std::string
&filename
)
250 ofstream
file(filename
);
251 file
.write((char *) &cert
, sizeof(cert
));
255 void DNSCryptContext::generateResolverKeyPair(DNSCryptPrivateKey
& privK
, unsigned char pubK
[DNSCRYPT_PUBLIC_KEY_SIZE
])
257 int res
= crypto_box_keypair(pubK
, privK
.key
);
260 throw std::runtime_error("Error generating DNSCrypt resolver keys");
264 void DNSCryptContext::computePublicKeyFromPrivate(const DNSCryptPrivateKey
& privK
, unsigned char* pubK
)
266 int res
= crypto_scalarmult_base(pubK
,
270 throw std::runtime_error("Error computing dnscrypt public key from the private one");
274 std::string
DNSCryptContext::certificateDateToStr(uint32_t date
)
277 time_t tdate
= static_cast<time_t>(ntohl(date
));
280 localtime_r(&tdate
, &date_tm
);
281 strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S", &date_tm
);
286 void DNSCryptContext::addNewCertificate(std::shared_ptr
<DNSCryptCertificatePair
>& newCert
, bool reload
)
288 WriteLock
w(&d_lock
);
290 for (auto pair
: d_certs
) {
291 if (pair
->cert
.getSerial() == newCert
->cert
.getSerial()) {
293 /* on reload we just assume that this is the same certificate */
297 throw std::runtime_error("Error adding a new certificate: we already have a certificate with the same serial");
302 d_certs
.push_back(newCert
);
305 void DNSCryptContext::addNewCertificate(const DNSCryptCert
& newCert
, const DNSCryptPrivateKey
& newKey
, bool active
, bool reload
)
307 auto pair
= std::make_shared
<DNSCryptCertificatePair
>();
308 pair
->cert
= newCert
;
309 pair
->privateKey
= newKey
;
310 computePublicKeyFromPrivate(pair
->privateKey
, pair
->publicKey
);
311 pair
->active
= active
;
313 addNewCertificate(pair
, reload
);
316 std::shared_ptr
<DNSCryptCertificatePair
> DNSCryptContext::loadCertificatePair(const std::string
& certFile
, const std::string
& keyFile
)
318 auto pair
= std::make_shared
<DNSCryptCertificatePair
>();
319 loadCertFromFile(certFile
, pair
->cert
);
320 pair
->privateKey
.loadFromFile(keyFile
);
322 computePublicKeyFromPrivate(pair
->privateKey
, pair
->publicKey
);
326 void DNSCryptContext::loadNewCertificate(const std::string
& certFile
, const std::string
& keyFile
, bool active
, bool reload
)
328 auto newPair
= DNSCryptContext::loadCertificatePair(certFile
, keyFile
);
329 newPair
->active
= active
;
330 addNewCertificate(newPair
, reload
);
331 d_certKeyPaths
.push_back({certFile
, keyFile
});
334 void DNSCryptContext::reloadCertificates()
336 std::vector
<std::shared_ptr
<DNSCryptCertificatePair
>> newCerts
;
337 for (const auto& pair
: d_certKeyPaths
) {
338 newCerts
.push_back(DNSCryptContext::loadCertificatePair(pair
.cert
, pair
.key
));
342 WriteLock
w(&d_lock
);
343 d_certs
= std::move(newCerts
);
347 void DNSCryptContext::markActive(uint32_t serial
)
349 WriteLock
w(&d_lock
);
351 for (auto pair
: d_certs
) {
352 if (pair
->active
== false && pair
->cert
.getSerial() == serial
) {
357 throw std::runtime_error("No inactive certificate found with this serial");
360 void DNSCryptContext::markInactive(uint32_t serial
)
362 WriteLock
w(&d_lock
);
364 for (auto pair
: d_certs
) {
365 if (pair
->active
== true && pair
->cert
.getSerial() == serial
) {
366 pair
->active
= false;
370 throw std::runtime_error("No active certificate found with this serial");
373 void DNSCryptContext::removeInactiveCertificate(uint32_t serial
)
375 WriteLock
w(&d_lock
);
377 for (auto it
= d_certs
.begin(); it
!= d_certs
.end(); ) {
378 if ((*it
)->active
== false && (*it
)->cert
.getSerial() == serial
) {
379 it
= d_certs
.erase(it
);
385 throw std::runtime_error("No inactive certificate found with this serial");
388 bool DNSCryptQuery::parsePlaintextQuery(const char * packet
, uint16_t packetSize
)
390 assert(d_ctx
!= nullptr);
392 if (packetSize
< sizeof(dnsheader
)) {
396 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
);
397 if (dh
->qr
|| ntohs(dh
->qdcount
) != 1 || dh
->ancount
!= 0 || dh
->nscount
!= 0 || dh
->opcode
!= Opcode::Query
)
400 unsigned int consumed
;
401 uint16_t qtype
, qclass
;
402 DNSName
qname(packet
, packetSize
, sizeof(dnsheader
), false, &qtype
, &qclass
, &consumed
);
403 if ((packetSize
- sizeof(dnsheader
)) < (consumed
+ sizeof(qtype
) + sizeof(qclass
)))
406 if (qtype
!= QType::TXT
|| qclass
!= QClass::IN
)
409 if (qname
!= d_ctx
->getProviderName())
419 void DNSCryptContext::getCertificateResponse(time_t now
, const DNSName
& qname
, uint16_t qid
, std::vector
<uint8_t>& response
)
421 DNSPacketWriter
pw(response
, qname
, QType::TXT
, QClass::IN
, Opcode::Query
);
422 struct dnsheader
* dh
= pw
.getHeader();
425 dh
->rcode
= RCode::NoError
;
428 for (const auto pair
: d_certs
) {
429 if (!pair
->active
|| !pair
->cert
.isValid(now
)) {
433 pw
.startRecord(qname
, QType::TXT
, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL
), QClass::IN
, DNSResourceRecord::ANSWER
, true);
435 uint8_t certSize
= sizeof(pair
->cert
);
436 scert
.assign((const char*) &certSize
, sizeof(certSize
));
437 scert
.append((const char*) &pair
->cert
, certSize
);
444 bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery
& query
, time_t now
)
446 const unsigned char* magic
= query
.getClientMagic();
449 for (const auto& pair
: d_certs
) {
450 if (pair
->cert
.isValid(now
) && memcmp(magic
, pair
->cert
.signedData
.clientMagic
, DNSCRYPT_CLIENT_MAGIC_SIZE
) == 0) {
451 query
.setCertificatePair(pair
);
459 bool DNSCryptQuery::isEncryptedQuery(const char * packet
, uint16_t packetSize
, bool tcp
, time_t now
)
461 assert(d_ctx
!= nullptr);
465 if (packetSize
< sizeof(DNSCryptQueryHeader
)) {
469 if (!tcp
&& packetSize
< DNSCryptQuery::s_minUDPLength
) {
473 const struct DNSCryptQueryHeader
* header
= reinterpret_cast<const struct DNSCryptQueryHeader
*>(packet
);
477 if (!d_ctx
->magicMatchesAPublicKey(*this, now
)) {
486 void DNSCryptQuery::getDecrypted(bool tcp
, char* packet
, uint16_t packetSize
, uint16_t* decryptedQueryLen
)
488 assert(decryptedQueryLen
!= nullptr);
490 assert(d_pair
!= nullptr);
491 assert(d_valid
== false);
493 #ifdef DNSCRYPT_STRICT_PADDING_LENGTH
494 if (tcp
&& ((packetSize
- sizeof(DNSCryptQueryHeader
)) % DNSCRYPT_PADDED_BLOCK_SIZE
) != 0) {
495 vinfolog("Dropping encrypted query with invalid size of %d (should be a multiple of %d)", (packetSize
- sizeof(DNSCryptQueryHeader
)), DNSCRYPT_PADDED_BLOCK_SIZE
);
500 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
501 static_assert(sizeof(nonce
) == (2* sizeof(d_header
.clientNonce
)), "Nonce should be larger than clientNonce (half)");
502 static_assert(sizeof(d_header
.clientPK
) == DNSCRYPT_PUBLIC_KEY_SIZE
, "Client Publick key size is not right");
503 static_assert(sizeof(d_pair
->privateKey
.key
) == DNSCRYPT_PRIVATE_KEY_SIZE
, "Private key size is not right");
505 memcpy(nonce
, &d_header
.clientNonce
, sizeof(d_header
.clientNonce
));
506 memset(nonce
+ sizeof(d_header
.clientNonce
), 0, sizeof(nonce
) - sizeof(d_header
.clientNonce
));
508 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
509 int res
= computeSharedKey();
511 vinfolog("Dropping encrypted query we can't compute the shared key for");
515 const DNSCryptExchangeVersion version
= getVersion();
517 if (version
== DNSCryptExchangeVersion::VERSION1
) {
518 res
= crypto_box_open_easy_afternm(reinterpret_cast<unsigned char*>(packet
),
519 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
520 packetSize
- sizeof(DNSCryptQueryHeader
),
524 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
525 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
526 res
= crypto_box_curve25519xchacha20poly1305_open_easy_afternm(reinterpret_cast<unsigned char*>(packet
),
527 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
528 packetSize
- sizeof(DNSCryptQueryHeader
),
531 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
533 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
538 #else /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
539 int res
= crypto_box_open_easy(reinterpret_cast<unsigned char*>(packet
),
540 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
541 packetSize
- sizeof(DNSCryptQueryHeader
),
544 d_pair
->privateKey
.key
);
545 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
548 vinfolog("Dropping encrypted query we can't decrypt");
552 *decryptedQueryLen
= packetSize
- sizeof(DNSCryptQueryHeader
) - DNSCRYPT_MAC_SIZE
;
553 uint16_t pos
= *decryptedQueryLen
;
554 assert(pos
< packetSize
);
555 d_paddedLen
= *decryptedQueryLen
;
557 while(pos
> 0 && packet
[pos
- 1] == 0) pos
--;
559 if (pos
== 0 || static_cast<uint8_t>(packet
[pos
- 1]) != 0x80) {
560 vinfolog("Dropping encrypted query with invalid padding value");
566 size_t paddingLen
= *decryptedQueryLen
- pos
;
567 *decryptedQueryLen
= pos
;
569 if (tcp
&& paddingLen
> DNSCRYPT_MAX_TCP_PADDING_SIZE
) {
570 vinfolog("Dropping encrypted query with too long padding size");
578 void DNSCryptQuery::getCertificateResponse(time_t now
, std::vector
<uint8_t>& response
) const
580 assert(d_ctx
!= nullptr);
581 d_ctx
->getCertificateResponse(now
, d_qname
, d_id
, response
);
584 void DNSCryptQuery::parsePacket(char* packet
, uint16_t packetSize
, bool tcp
, uint16_t* decryptedQueryLen
, time_t now
)
586 assert(packet
!= nullptr);
587 assert(decryptedQueryLen
!= nullptr);
591 /* might be a plaintext certificate request or an authenticated request */
592 if (isEncryptedQuery(packet
, packetSize
, tcp
, now
)) {
593 getDecrypted(tcp
, packet
, packetSize
, decryptedQueryLen
);
596 parsePlaintextQuery(packet
, packetSize
);
600 void DNSCryptQuery::fillServerNonce(unsigned char* nonce
) const
602 uint32_t* dest
= reinterpret_cast<uint32_t*>(nonce
);
603 static const size_t nonceSize
= DNSCRYPT_NONCE_SIZE
/ 2;
605 for (size_t pos
= 0; pos
< (nonceSize
/ sizeof(*dest
)); pos
++)
607 const uint32_t value
= randombytes_random();
608 memcpy(dest
+ pos
, &value
, sizeof(value
));
613 "The length of <resolver-response-pad> must be between 0 and 256 bytes,
614 and must be constant for a given (<resolver-sk>, <client-nonce>) tuple."
616 uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen
, size_t maxLen
) const
618 size_t paddedSize
= 0;
621 assert(d_header
.clientNonce
);
622 assert(d_pair
!= nullptr);
624 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
625 memcpy(nonce
, d_header
.clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
626 memcpy(&(nonce
[DNSCRYPT_NONCE_SIZE
/ 2]), d_header
.clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
627 crypto_stream((unsigned char*) &rnd
, sizeof(rnd
), nonce
, d_pair
->privateKey
.key
);
629 paddedSize
= unpaddedLen
+ rnd
% (maxLen
- unpaddedLen
+ 1);
630 paddedSize
+= DNSCRYPT_PADDED_BLOCK_SIZE
- (paddedSize
% DNSCRYPT_PADDED_BLOCK_SIZE
);
632 if (paddedSize
> maxLen
)
635 result
= paddedSize
- unpaddedLen
;
640 int DNSCryptQuery::encryptResponse(char* response
, uint16_t responseLen
, uint16_t responseSize
, bool tcp
, uint16_t* encryptedResponseLen
)
642 struct DNSCryptResponseHeader responseHeader
;
643 assert(response
!= nullptr);
644 assert(responseLen
> 0);
645 assert(responseSize
>= responseLen
);
646 assert(encryptedResponseLen
!= nullptr);
647 assert(d_encrypted
== true);
648 assert(d_pair
!= nullptr);
650 if (!tcp
&& d_paddedLen
< responseLen
) {
651 struct dnsheader
* dh
= reinterpret_cast<struct dnsheader
*>(response
);
652 size_t questionSize
= 0;
654 if (responseLen
> sizeof(dnsheader
)) {
655 unsigned int consumed
= 0;
656 DNSName
tempQName(response
, responseLen
, sizeof(dnsheader
), false, 0, 0, &consumed
);
658 questionSize
= consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
662 responseLen
= sizeof(dnsheader
) + questionSize
;
664 if (responseLen
> d_paddedLen
) {
665 responseLen
= d_paddedLen
;
667 dh
->ancount
= dh
->arcount
= dh
->nscount
= 0;
671 size_t requiredSize
= sizeof(responseHeader
) + DNSCRYPT_MAC_SIZE
+ responseLen
;
672 size_t maxSize
= (responseSize
> (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
)) ? (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
) : responseSize
;
673 uint16_t paddingSize
= computePaddingSize(requiredSize
, maxSize
);
674 requiredSize
+= paddingSize
;
676 if (requiredSize
> responseSize
)
679 memcpy(&responseHeader
.nonce
, &d_header
.clientNonce
, sizeof d_header
.clientNonce
);
680 fillServerNonce(&(responseHeader
.nonce
[sizeof(d_header
.clientNonce
)]));
682 /* moving the existing response after the header + MAC */
683 memmove(response
+ sizeof(responseHeader
) + DNSCRYPT_MAC_SIZE
, response
, responseLen
);
687 memcpy(response
+ pos
, &responseHeader
, sizeof(responseHeader
));
688 pos
+= sizeof(responseHeader
);
689 /* setting MAC bytes to 0 */
690 memset(response
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
691 pos
+= DNSCRYPT_MAC_SIZE
;
692 uint16_t toEncryptPos
= pos
;
693 /* skipping response */
696 response
[pos
] = static_cast<uint8_t>(0x80);
698 memset(response
+ pos
, 0, paddingSize
- 1);
699 pos
+= (paddingSize
- 1);
702 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
703 int res
= computeSharedKey();
708 const DNSCryptExchangeVersion version
= getVersion();
710 if (version
== DNSCryptExchangeVersion::VERSION1
) {
711 res
= crypto_box_easy_afternm(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
712 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
713 responseLen
+ paddingSize
,
714 responseHeader
.nonce
,
717 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
718 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
719 res
= crypto_box_curve25519xchacha20poly1305_easy_afternm(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
720 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
721 responseLen
+ paddingSize
,
722 responseHeader
.nonce
,
724 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
726 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
732 int res
= crypto_box_easy(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
733 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
734 responseLen
+ paddingSize
,
735 responseHeader
.nonce
,
737 d_pair
->privateKey
.key
);
738 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
741 assert(pos
== requiredSize
);
742 *encryptedResponseLen
= requiredSize
;
748 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
750 assert(query
!= nullptr);
751 assert(queryLen
> 0);
752 assert(querySize
>= queryLen
);
753 assert(encryptedResponseLen
!= nullptr);
754 assert(cert
!= nullptr);
756 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
757 size_t requiredSize
= sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
+ queryLen
;
758 /* this is not optimal, we should compute a random padding size, multiple of DNSCRYPT_PADDED_BLOCK_SIZE,
759 DNSCRYPT_PADDED_BLOCK_SIZE <= padding size <= 4096? */
760 uint16_t paddingSize
= DNSCRYPT_PADDED_BLOCK_SIZE
- (queryLen
% DNSCRYPT_PADDED_BLOCK_SIZE
);
761 requiredSize
+= paddingSize
;
763 if (!tcp
&& requiredSize
< DNSCryptQuery::s_minUDPLength
) {
764 paddingSize
+= (DNSCryptQuery::s_minUDPLength
- requiredSize
);
765 requiredSize
= DNSCryptQuery::s_minUDPLength
;
768 if (requiredSize
> querySize
)
771 /* moving the existing query after the header + MAC */
772 memmove(query
+ sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
, query
, queryLen
);
776 memcpy(query
+ pos
, cert
->signedData
.clientMagic
, sizeof(cert
->signedData
.clientMagic
));
777 pos
+= sizeof(cert
->signedData
.clientMagic
);
780 memcpy(query
+ pos
, clientPublicKey
, DNSCRYPT_PUBLIC_KEY_SIZE
);
781 pos
+= DNSCRYPT_PUBLIC_KEY_SIZE
;
784 memcpy(query
+ pos
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
785 pos
+= DNSCRYPT_NONCE_SIZE
/ 2;
786 size_t encryptedPos
= pos
;
788 /* clear the MAC bytes */
789 memset(query
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
790 pos
+= DNSCRYPT_MAC_SIZE
;
796 query
[pos
] = static_cast<uint8_t>(0x80);
798 memset(query
+ pos
, 0, paddingSize
- 1);
799 pos
+= paddingSize
- 1;
801 memcpy(nonce
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
802 memset(nonce
+ (DNSCRYPT_NONCE_SIZE
/ 2), 0, DNSCRYPT_NONCE_SIZE
/ 2);
804 const DNSCryptExchangeVersion version
= getExchangeVersion(*cert
);
807 if (version
== DNSCryptExchangeVersion::VERSION1
) {
808 res
= crypto_box_easy(reinterpret_cast<unsigned char*>(query
+ encryptedPos
),
809 reinterpret_cast<unsigned char*>(query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
),
810 queryLen
+ paddingSize
,
812 cert
->signedData
.resolverPK
,
813 clientPrivateKey
.key
);
815 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
816 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
817 res
= crypto_box_curve25519xchacha20poly1305_easy(reinterpret_cast<unsigned char*>(query
+ encryptedPos
),
818 reinterpret_cast<unsigned char*>(query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
),
819 queryLen
+ paddingSize
,
821 cert
->signedData
.resolverPK
,
822 clientPrivateKey
.key
);
823 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
826 throw std::runtime_error("Unknown DNSCrypt exchange version");
830 assert(pos
== requiredSize
);
831 *encryptedResponseLen
= requiredSize
;
837 bool generateDNSCryptCertificate(const std::string
& providerPrivateKeyFile
, uint32_t serial
, time_t begin
, time_t end
, DNSCryptExchangeVersion version
, DNSCryptCert
& certOut
, DNSCryptPrivateKey
& keyOut
)
839 bool success
= false;
840 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
841 sodium_mlock(providerPrivateKey
, sizeof(providerPrivateKey
));
842 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
845 ifstream
providerKStream(providerPrivateKeyFile
);
846 providerKStream
.read((char*) providerPrivateKey
, sizeof(providerPrivateKey
));
847 if (providerKStream
.fail()) {
848 providerKStream
.close();
849 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile
);
852 DNSCryptContext::generateCertificate(serial
, begin
, end
, version
, providerPrivateKey
, keyOut
, certOut
);
855 catch(const std::exception
& e
) {
859 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
860 sodium_munlock(providerPrivateKey
, sizeof(providerPrivateKey
));