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"
29 DNSCryptPrivateKey::DNSCryptPrivateKey()
31 sodium_memzero(key
, sizeof(key
));
32 sodium_mlock(key
, sizeof(key
));
35 void DNSCryptPrivateKey::loadFromFile(const std::string
& keyFile
)
37 ifstream
file(keyFile
);
38 sodium_memzero(key
, sizeof(key
));
39 file
.read((char*) key
, sizeof(key
));
42 sodium_memzero(key
, sizeof(key
));
44 throw std::runtime_error("Invalid DNSCrypt key file " + keyFile
);
50 void DNSCryptPrivateKey::saveToFile(const std::string
& keyFile
) const
52 ofstream
file(keyFile
);
53 file
.write((char*) key
, sizeof(key
));
57 DNSCryptPrivateKey::~DNSCryptPrivateKey()
59 sodium_munlock(key
, sizeof(key
));
62 DNSCryptExchangeVersion
DNSCryptQuery::getVersion() const
64 if (d_pair
== nullptr) {
65 throw std::runtime_error("Unable to determine the version of a DNSCrypt query if there is not associated cert");
68 return DNSCryptContext::getExchangeVersion(d_pair
->cert
);
71 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
72 DNSCryptQuery::~DNSCryptQuery()
74 if (d_sharedKeyComputed
) {
75 sodium_munlock(d_sharedKey
, sizeof(d_sharedKey
));
79 int DNSCryptQuery::computeSharedKey()
81 assert(d_pair
!= nullptr);
85 if (d_sharedKeyComputed
) {
89 const DNSCryptExchangeVersion version
= DNSCryptContext::getExchangeVersion(d_pair
->cert
);
91 sodium_mlock(d_sharedKey
, sizeof(d_sharedKey
));
93 if (version
== DNSCryptExchangeVersion::VERSION1
) {
94 res
= crypto_box_beforenm(d_sharedKey
,
96 d_pair
->privateKey
.key
);
98 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
99 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
100 res
= crypto_box_curve25519xchacha20poly1305_beforenm(d_sharedKey
,
102 d_pair
->privateKey
.key
);
103 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
105 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
112 sodium_munlock(d_sharedKey
, sizeof(d_sharedKey
));
116 d_sharedKeyComputed
= true;
120 DNSCryptQuery::~DNSCryptQuery()
123 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
126 DNSCryptContext::~DNSCryptContext() {
129 DNSCryptContext::DNSCryptContext(const std::string
& pName
, const std::vector
<CertKeyPaths
>& certKeys
): d_certKeyPaths(certKeys
), providerName(pName
)
131 reloadCertificates();
134 DNSCryptContext::DNSCryptContext(const std::string
& pName
, const DNSCryptCert
& certificate
, const DNSCryptPrivateKey
& pKey
): providerName(pName
)
136 addNewCertificate(certificate
, pKey
);
139 void DNSCryptContext::generateProviderKeys(unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
], unsigned char privateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
])
141 int res
= crypto_sign_ed25519_keypair(publicKey
, privateKey
);
144 throw std::runtime_error("Error generating DNSCrypt provider keys");
148 std::string
DNSCryptContext::getProviderFingerprint(unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
])
150 boost::format
fmt("%02X%02X");
153 for (size_t idx
= 0; idx
< DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
; idx
+= 2)
155 ret
<< (fmt
% static_cast<int>(publicKey
[idx
]) % static_cast<int>(publicKey
[idx
+1]));
156 if (idx
< (DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
- 2)) {
164 void DNSCryptContext::setExchangeVersion(const DNSCryptExchangeVersion
& version
, unsigned char esVersion
[sizeof(DNSCryptCert::esVersion
)])
168 if (version
== DNSCryptExchangeVersion::VERSION1
) {
169 esVersion
[1] = { 0x01 };
171 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
172 esVersion
[1] = { 0x02 };
175 throw std::runtime_error("Unknown DNSCrypt exchange version");
179 DNSCryptExchangeVersion
DNSCryptContext::getExchangeVersion(const unsigned char esVersion
[sizeof(DNSCryptCert::esVersion
)])
181 if (esVersion
[0] != 0x00) {
182 throw std::runtime_error("Unknown DNSCrypt exchange version");
185 if (esVersion
[1] == 0x01) {
186 return DNSCryptExchangeVersion::VERSION1
;
188 else if (esVersion
[1] == 0x02) {
189 return DNSCryptExchangeVersion::VERSION2
;
192 throw std::runtime_error("Unknown DNSCrypt exchange version");
195 DNSCryptExchangeVersion
DNSCryptContext::getExchangeVersion(const DNSCryptCert
& cert
)
197 return getExchangeVersion(cert
.esVersion
);
201 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 unsigned char magic
[DNSCRYPT_CERT_MAGIC_SIZE
] = DNSCRYPT_CERT_MAGIC_VALUE
;
204 unsigned char protocolMinorVersion
[] = DNSCRYPT_CERT_PROTOCOL_MINOR_VERSION_VALUE
;
205 unsigned char pubK
[DNSCRYPT_PUBLIC_KEY_SIZE
];
206 unsigned char esVersion
[sizeof(DNSCryptCert::esVersion
)];
207 setExchangeVersion(version
, esVersion
);
209 generateResolverKeyPair(privateKey
, pubK
);
211 memcpy(cert
.magic
, magic
, sizeof(magic
));
212 memcpy(cert
.esVersion
, esVersion
, sizeof(esVersion
));
213 memcpy(cert
.protocolMinorVersion
, protocolMinorVersion
, sizeof(protocolMinorVersion
));
214 memcpy(cert
.signedData
.resolverPK
, pubK
, sizeof(cert
.signedData
.resolverPK
));
215 memcpy(cert
.signedData
.clientMagic
, pubK
, sizeof(cert
.signedData
.clientMagic
));
216 cert
.signedData
.serial
= htonl(serial
);
217 cert
.signedData
.tsStart
= htonl((uint32_t) begin
);
218 cert
.signedData
.tsEnd
= htonl((uint32_t) end
);
220 unsigned long long signatureSize
= 0;
222 int res
= crypto_sign_ed25519(cert
.signature
,
224 (unsigned char*) &cert
.signedData
,
225 sizeof(cert
.signedData
),
229 assert(signatureSize
== sizeof(DNSCryptCertSignedData
) + DNSCRYPT_SIGNATURE_SIZE
);
232 throw std::runtime_error("Error generating DNSCrypt certificate");
236 void DNSCryptContext::loadCertFromFile(const std::string
&filename
, DNSCryptCert
& dest
)
238 ifstream
file(filename
);
239 file
.read((char *) &dest
, sizeof(dest
));
242 throw std::runtime_error("Invalid dnscrypt certificate file " + filename
);
247 void DNSCryptContext::saveCertFromFile(const DNSCryptCert
& cert
, const std::string
&filename
)
249 ofstream
file(filename
);
250 file
.write((char *) &cert
, sizeof(cert
));
254 void DNSCryptContext::generateResolverKeyPair(DNSCryptPrivateKey
& privK
, unsigned char pubK
[DNSCRYPT_PUBLIC_KEY_SIZE
])
256 int res
= crypto_box_keypair(pubK
, privK
.key
);
259 throw std::runtime_error("Error generating DNSCrypt resolver keys");
263 void DNSCryptContext::computePublicKeyFromPrivate(const DNSCryptPrivateKey
& privK
, unsigned char* pubK
)
265 int res
= crypto_scalarmult_base(pubK
,
269 throw std::runtime_error("Error computing dnscrypt public key from the private one");
273 std::string
DNSCryptContext::certificateDateToStr(uint32_t date
)
276 time_t tdate
= static_cast<time_t>(ntohl(date
));
279 localtime_r(&tdate
, &date_tm
);
280 strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S", &date_tm
);
285 void DNSCryptContext::addNewCertificate(std::shared_ptr
<DNSCryptCertificatePair
>& newCert
, bool reload
)
287 WriteLock
w(&d_lock
);
289 for (auto pair
: d_certs
) {
290 if (pair
->cert
.getSerial() == newCert
->cert
.getSerial()) {
292 /* on reload we just assume that this is the same certificate */
296 throw std::runtime_error("Error adding a new certificate: we already have a certificate with the same serial");
301 d_certs
.push_back(newCert
);
304 void DNSCryptContext::addNewCertificate(const DNSCryptCert
& newCert
, const DNSCryptPrivateKey
& newKey
, bool active
, bool reload
)
306 auto pair
= std::make_shared
<DNSCryptCertificatePair
>();
307 pair
->cert
= newCert
;
308 pair
->privateKey
= newKey
;
309 computePublicKeyFromPrivate(pair
->privateKey
, pair
->publicKey
);
310 pair
->active
= active
;
312 addNewCertificate(pair
, reload
);
315 std::shared_ptr
<DNSCryptCertificatePair
> DNSCryptContext::loadCertificatePair(const std::string
& certFile
, const std::string
& keyFile
)
317 auto pair
= std::make_shared
<DNSCryptCertificatePair
>();
318 loadCertFromFile(certFile
, pair
->cert
);
319 pair
->privateKey
.loadFromFile(keyFile
);
321 computePublicKeyFromPrivate(pair
->privateKey
, pair
->publicKey
);
325 void DNSCryptContext::loadNewCertificate(const std::string
& certFile
, const std::string
& keyFile
, bool active
, bool reload
)
327 auto newPair
= DNSCryptContext::loadCertificatePair(certFile
, keyFile
);
328 newPair
->active
= active
;
329 addNewCertificate(newPair
, reload
);
330 d_certKeyPaths
.push_back({certFile
, keyFile
});
333 void DNSCryptContext::reloadCertificates()
335 std::vector
<std::shared_ptr
<DNSCryptCertificatePair
>> newCerts
;
336 for (const auto& pair
: d_certKeyPaths
) {
337 newCerts
.push_back(DNSCryptContext::loadCertificatePair(pair
.cert
, pair
.key
));
341 WriteLock
w(&d_lock
);
342 d_certs
= std::move(newCerts
);
346 void DNSCryptContext::markActive(uint32_t serial
)
348 WriteLock
w(&d_lock
);
350 for (auto pair
: d_certs
) {
351 if (pair
->active
== false && pair
->cert
.getSerial() == serial
) {
356 throw std::runtime_error("No inactive certificate found with this serial");
359 void DNSCryptContext::markInactive(uint32_t serial
)
361 WriteLock
w(&d_lock
);
363 for (auto pair
: d_certs
) {
364 if (pair
->active
== true && pair
->cert
.getSerial() == serial
) {
365 pair
->active
= false;
369 throw std::runtime_error("No active certificate found with this serial");
372 void DNSCryptContext::removeInactiveCertificate(uint32_t serial
)
374 WriteLock
w(&d_lock
);
376 for (auto it
= d_certs
.begin(); it
!= d_certs
.end(); ) {
377 if ((*it
)->active
== false && (*it
)->cert
.getSerial() == serial
) {
378 it
= d_certs
.erase(it
);
384 throw std::runtime_error("No inactive certificate found with this serial");
387 bool DNSCryptQuery::parsePlaintextQuery(const char * packet
, uint16_t packetSize
)
389 assert(d_ctx
!= nullptr);
391 if (packetSize
< sizeof(dnsheader
)) {
395 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
);
396 if (dh
->qr
|| ntohs(dh
->qdcount
) != 1 || dh
->ancount
!= 0 || dh
->nscount
!= 0 || dh
->opcode
!= Opcode::Query
)
399 unsigned int consumed
;
400 uint16_t qtype
, qclass
;
401 DNSName
qname(packet
, packetSize
, sizeof(dnsheader
), false, &qtype
, &qclass
, &consumed
);
402 if ((packetSize
- sizeof(dnsheader
)) < (consumed
+ sizeof(qtype
) + sizeof(qclass
)))
405 if (qtype
!= QType::TXT
|| qclass
!= QClass::IN
)
408 if (qname
!= d_ctx
->getProviderName())
418 void DNSCryptContext::getCertificateResponse(time_t now
, const DNSName
& qname
, uint16_t qid
, std::vector
<uint8_t>& response
)
420 DNSPacketWriter
pw(response
, qname
, QType::TXT
, QClass::IN
, Opcode::Query
);
421 struct dnsheader
* dh
= pw
.getHeader();
424 dh
->rcode
= RCode::NoError
;
427 for (const auto& pair
: d_certs
) {
428 if (!pair
->active
|| !pair
->cert
.isValid(now
)) {
432 pw
.startRecord(qname
, QType::TXT
, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL
), QClass::IN
, DNSResourceRecord::ANSWER
, true);
434 uint8_t certSize
= sizeof(pair
->cert
);
435 scert
.assign((const char*) &certSize
, sizeof(certSize
));
436 scert
.append((const char*) &pair
->cert
, certSize
);
443 bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery
& query
, time_t now
)
445 const unsigned char* magic
= query
.getClientMagic();
448 for (const auto& pair
: d_certs
) {
449 if (pair
->cert
.isValid(now
) && memcmp(magic
, pair
->cert
.signedData
.clientMagic
, DNSCRYPT_CLIENT_MAGIC_SIZE
) == 0) {
450 query
.setCertificatePair(pair
);
458 bool DNSCryptQuery::isEncryptedQuery(const char * packet
, uint16_t packetSize
, bool tcp
, time_t now
)
460 assert(d_ctx
!= nullptr);
464 if (packetSize
< sizeof(DNSCryptQueryHeader
)) {
468 if (!tcp
&& packetSize
< DNSCryptQuery::s_minUDPLength
) {
472 const struct DNSCryptQueryHeader
* header
= reinterpret_cast<const struct DNSCryptQueryHeader
*>(packet
);
476 if (!d_ctx
->magicMatchesAPublicKey(*this, now
)) {
485 void DNSCryptQuery::getDecrypted(bool tcp
, char* packet
, uint16_t packetSize
, uint16_t* decryptedQueryLen
)
487 assert(decryptedQueryLen
!= nullptr);
489 assert(d_pair
!= nullptr);
490 assert(d_valid
== false);
492 #ifdef DNSCRYPT_STRICT_PADDING_LENGTH
493 if (tcp
&& ((packetSize
- sizeof(DNSCryptQueryHeader
)) % DNSCRYPT_PADDED_BLOCK_SIZE
) != 0) {
494 vinfolog("Dropping encrypted query with invalid size of %d (should be a multiple of %d)", (packetSize
- sizeof(DNSCryptQueryHeader
)), DNSCRYPT_PADDED_BLOCK_SIZE
);
499 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
500 static_assert(sizeof(nonce
) == (2* sizeof(d_header
.clientNonce
)), "Nonce should be larger than clientNonce (half)");
501 static_assert(sizeof(d_header
.clientPK
) == DNSCRYPT_PUBLIC_KEY_SIZE
, "Client Public key size is not right");
502 static_assert(sizeof(d_pair
->privateKey
.key
) == DNSCRYPT_PRIVATE_KEY_SIZE
, "Private key size is not right");
504 memcpy(nonce
, &d_header
.clientNonce
, sizeof(d_header
.clientNonce
));
505 memset(nonce
+ sizeof(d_header
.clientNonce
), 0, sizeof(nonce
) - sizeof(d_header
.clientNonce
));
507 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
508 int res
= computeSharedKey();
510 vinfolog("Dropping encrypted query we can't compute the shared key for");
514 const DNSCryptExchangeVersion version
= getVersion();
516 if (version
== DNSCryptExchangeVersion::VERSION1
) {
517 res
= crypto_box_open_easy_afternm(reinterpret_cast<unsigned char*>(packet
),
518 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
519 packetSize
- sizeof(DNSCryptQueryHeader
),
523 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
524 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
525 res
= crypto_box_curve25519xchacha20poly1305_open_easy_afternm(reinterpret_cast<unsigned char*>(packet
),
526 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
527 packetSize
- sizeof(DNSCryptQueryHeader
),
530 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
532 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
537 #else /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
538 int res
= crypto_box_open_easy(reinterpret_cast<unsigned char*>(packet
),
539 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
540 packetSize
- sizeof(DNSCryptQueryHeader
),
543 d_pair
->privateKey
.key
);
544 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
547 vinfolog("Dropping encrypted query we can't decrypt");
551 *decryptedQueryLen
= packetSize
- sizeof(DNSCryptQueryHeader
) - DNSCRYPT_MAC_SIZE
;
552 uint16_t pos
= *decryptedQueryLen
;
553 assert(pos
< packetSize
);
554 d_paddedLen
= *decryptedQueryLen
;
556 while(pos
> 0 && packet
[pos
- 1] == 0) pos
--;
558 if (pos
== 0 || static_cast<uint8_t>(packet
[pos
- 1]) != 0x80) {
559 vinfolog("Dropping encrypted query with invalid padding value");
565 size_t paddingLen
= *decryptedQueryLen
- pos
;
566 *decryptedQueryLen
= pos
;
568 if (tcp
&& paddingLen
> DNSCRYPT_MAX_TCP_PADDING_SIZE
) {
569 vinfolog("Dropping encrypted query with too long padding size");
577 void DNSCryptQuery::getCertificateResponse(time_t now
, std::vector
<uint8_t>& response
) const
579 assert(d_ctx
!= nullptr);
580 d_ctx
->getCertificateResponse(now
, d_qname
, d_id
, response
);
583 void DNSCryptQuery::parsePacket(char* packet
, uint16_t packetSize
, bool tcp
, uint16_t* decryptedQueryLen
, time_t now
)
585 assert(packet
!= nullptr);
586 assert(decryptedQueryLen
!= nullptr);
590 /* might be a plaintext certificate request or an authenticated request */
591 if (isEncryptedQuery(packet
, packetSize
, tcp
, now
)) {
592 getDecrypted(tcp
, packet
, packetSize
, decryptedQueryLen
);
595 parsePlaintextQuery(packet
, packetSize
);
599 void DNSCryptQuery::fillServerNonce(unsigned char* nonce
) const
601 uint32_t* dest
= reinterpret_cast<uint32_t*>(nonce
);
602 static const size_t nonceSize
= DNSCRYPT_NONCE_SIZE
/ 2;
604 for (size_t pos
= 0; pos
< (nonceSize
/ sizeof(*dest
)); pos
++)
606 const uint32_t value
= randombytes_random();
607 memcpy(dest
+ pos
, &value
, sizeof(value
));
612 "The length of <resolver-response-pad> must be between 0 and 256 bytes,
613 and must be constant for a given (<resolver-sk>, <client-nonce>) tuple."
615 uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen
, size_t maxLen
) const
617 size_t paddedSize
= 0;
620 assert(d_header
.clientNonce
);
621 assert(d_pair
!= nullptr);
623 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
624 memcpy(nonce
, d_header
.clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
625 memcpy(&(nonce
[DNSCRYPT_NONCE_SIZE
/ 2]), d_header
.clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
626 crypto_stream((unsigned char*) &rnd
, sizeof(rnd
), nonce
, d_pair
->privateKey
.key
);
628 paddedSize
= unpaddedLen
+ rnd
% (maxLen
- unpaddedLen
+ 1);
629 paddedSize
+= DNSCRYPT_PADDED_BLOCK_SIZE
- (paddedSize
% DNSCRYPT_PADDED_BLOCK_SIZE
);
631 if (paddedSize
> maxLen
)
634 result
= paddedSize
- unpaddedLen
;
639 int DNSCryptQuery::encryptResponse(char* response
, uint16_t responseLen
, uint16_t responseSize
, bool tcp
, uint16_t* encryptedResponseLen
)
641 struct DNSCryptResponseHeader responseHeader
;
642 assert(response
!= nullptr);
643 assert(responseLen
> 0);
644 assert(responseSize
>= responseLen
);
645 assert(encryptedResponseLen
!= nullptr);
646 assert(d_encrypted
== true);
647 assert(d_pair
!= nullptr);
649 if (!tcp
&& d_paddedLen
< responseLen
) {
650 struct dnsheader
* dh
= reinterpret_cast<struct dnsheader
*>(response
);
651 size_t questionSize
= 0;
653 if (responseLen
> sizeof(dnsheader
)) {
654 unsigned int consumed
= 0;
655 DNSName
tempQName(response
, responseLen
, sizeof(dnsheader
), false, 0, 0, &consumed
);
657 questionSize
= consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
661 responseLen
= sizeof(dnsheader
) + questionSize
;
663 if (responseLen
> d_paddedLen
) {
664 responseLen
= d_paddedLen
;
666 dh
->ancount
= dh
->arcount
= dh
->nscount
= 0;
670 size_t requiredSize
= sizeof(responseHeader
) + DNSCRYPT_MAC_SIZE
+ responseLen
;
671 size_t maxSize
= (responseSize
> (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
)) ? (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
) : responseSize
;
672 uint16_t paddingSize
= computePaddingSize(requiredSize
, maxSize
);
673 requiredSize
+= paddingSize
;
675 if (requiredSize
> responseSize
)
678 memcpy(&responseHeader
.nonce
, &d_header
.clientNonce
, sizeof d_header
.clientNonce
);
679 fillServerNonce(&(responseHeader
.nonce
[sizeof(d_header
.clientNonce
)]));
681 /* moving the existing response after the header + MAC */
682 memmove(response
+ sizeof(responseHeader
) + DNSCRYPT_MAC_SIZE
, response
, responseLen
);
686 memcpy(response
+ pos
, &responseHeader
, sizeof(responseHeader
));
687 pos
+= sizeof(responseHeader
);
688 /* setting MAC bytes to 0 */
689 memset(response
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
690 pos
+= DNSCRYPT_MAC_SIZE
;
691 uint16_t toEncryptPos
= pos
;
692 /* skipping response */
695 response
[pos
] = static_cast<uint8_t>(0x80);
697 memset(response
+ pos
, 0, paddingSize
- 1);
698 pos
+= (paddingSize
- 1);
701 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
702 int res
= computeSharedKey();
707 const DNSCryptExchangeVersion version
= getVersion();
709 if (version
== DNSCryptExchangeVersion::VERSION1
) {
710 res
= crypto_box_easy_afternm(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
711 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
712 responseLen
+ paddingSize
,
713 responseHeader
.nonce
,
716 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
717 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
718 res
= crypto_box_curve25519xchacha20poly1305_easy_afternm(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
719 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
720 responseLen
+ paddingSize
,
721 responseHeader
.nonce
,
723 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
725 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
731 int res
= crypto_box_easy(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
732 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
733 responseLen
+ paddingSize
,
734 responseHeader
.nonce
,
736 d_pair
->privateKey
.key
);
737 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
740 assert(pos
== requiredSize
);
741 *encryptedResponseLen
= requiredSize
;
747 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
749 assert(query
!= nullptr);
750 assert(queryLen
> 0);
751 assert(querySize
>= queryLen
);
752 assert(encryptedResponseLen
!= nullptr);
753 assert(cert
!= nullptr);
755 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
756 size_t requiredSize
= sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
+ queryLen
;
757 /* this is not optimal, we should compute a random padding size, multiple of DNSCRYPT_PADDED_BLOCK_SIZE,
758 DNSCRYPT_PADDED_BLOCK_SIZE <= padding size <= 4096? */
759 uint16_t paddingSize
= DNSCRYPT_PADDED_BLOCK_SIZE
- (queryLen
% DNSCRYPT_PADDED_BLOCK_SIZE
);
760 requiredSize
+= paddingSize
;
762 if (!tcp
&& requiredSize
< DNSCryptQuery::s_minUDPLength
) {
763 paddingSize
+= (DNSCryptQuery::s_minUDPLength
- requiredSize
);
764 requiredSize
= DNSCryptQuery::s_minUDPLength
;
767 if (requiredSize
> querySize
)
770 /* moving the existing query after the header + MAC */
771 memmove(query
+ sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
, query
, queryLen
);
775 memcpy(query
+ pos
, cert
->signedData
.clientMagic
, sizeof(cert
->signedData
.clientMagic
));
776 pos
+= sizeof(cert
->signedData
.clientMagic
);
779 memcpy(query
+ pos
, clientPublicKey
, DNSCRYPT_PUBLIC_KEY_SIZE
);
780 pos
+= DNSCRYPT_PUBLIC_KEY_SIZE
;
783 memcpy(query
+ pos
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
784 pos
+= DNSCRYPT_NONCE_SIZE
/ 2;
785 size_t encryptedPos
= pos
;
787 /* clear the MAC bytes */
788 memset(query
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
789 pos
+= DNSCRYPT_MAC_SIZE
;
795 query
[pos
] = static_cast<uint8_t>(0x80);
797 memset(query
+ pos
, 0, paddingSize
- 1);
798 pos
+= paddingSize
- 1;
800 memcpy(nonce
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
801 memset(nonce
+ (DNSCRYPT_NONCE_SIZE
/ 2), 0, DNSCRYPT_NONCE_SIZE
/ 2);
803 const DNSCryptExchangeVersion version
= getExchangeVersion(*cert
);
806 if (version
== DNSCryptExchangeVersion::VERSION1
) {
807 res
= crypto_box_easy(reinterpret_cast<unsigned char*>(query
+ encryptedPos
),
808 reinterpret_cast<unsigned char*>(query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
),
809 queryLen
+ paddingSize
,
811 cert
->signedData
.resolverPK
,
812 clientPrivateKey
.key
);
814 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
815 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
816 res
= crypto_box_curve25519xchacha20poly1305_easy(reinterpret_cast<unsigned char*>(query
+ encryptedPos
),
817 reinterpret_cast<unsigned char*>(query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
),
818 queryLen
+ paddingSize
,
820 cert
->signedData
.resolverPK
,
821 clientPrivateKey
.key
);
822 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
825 throw std::runtime_error("Unknown DNSCrypt exchange version");
829 assert(pos
== requiredSize
);
830 *encryptedResponseLen
= requiredSize
;
836 bool generateDNSCryptCertificate(const std::string
& providerPrivateKeyFile
, uint32_t serial
, time_t begin
, time_t end
, DNSCryptExchangeVersion version
, DNSCryptCert
& certOut
, DNSCryptPrivateKey
& keyOut
)
838 bool success
= false;
839 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
840 sodium_mlock(providerPrivateKey
, sizeof(providerPrivateKey
));
841 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
844 ifstream
providerKStream(providerPrivateKeyFile
);
845 providerKStream
.read((char*) providerPrivateKey
, sizeof(providerPrivateKey
));
846 if (providerKStream
.fail()) {
847 providerKStream
.close();
848 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile
);
851 DNSCryptContext::generateCertificate(serial
, begin
, end
, version
, providerPrivateKey
, keyOut
, certOut
);
854 catch(const std::exception
& e
) {
858 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
859 sodium_munlock(providerPrivateKey
, sizeof(providerPrivateKey
));