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::string
& certFile
, const std::string
& keyFile
): providerName(pName
)
128 pthread_rwlock_init(&d_lock
, 0);
130 loadNewCertificate(certFile
, keyFile
);
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(const DNSCryptCert
& newCert
, const DNSCryptPrivateKey
& newKey
, bool active
, bool reload
)
288 WriteLock
w(&d_lock
);
290 for (auto pair
: certs
) {
291 if (pair
->cert
.getSerial() == newCert
.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 auto pair
= std::make_shared
<DNSCryptCertificatePair
>();
303 pair
->cert
= newCert
;
304 pair
->privateKey
= newKey
;
305 computePublicKeyFromPrivate(pair
->privateKey
, pair
->publicKey
);
306 pair
->active
= active
;
307 certs
.push_back(pair
);
310 void DNSCryptContext::loadNewCertificate(const std::string
& certFile
, const std::string
& keyFile
, bool active
, bool reload
)
312 DNSCryptCert newCert
;
313 DNSCryptPrivateKey newPrivateKey
;
315 loadCertFromFile(certFile
, newCert
);
316 newPrivateKey
.loadFromFile(keyFile
);
318 addNewCertificate(newCert
, newPrivateKey
, active
, reload
);
319 certificatePath
= certFile
;
323 void DNSCryptContext::reloadCertificate()
325 loadNewCertificate(certificatePath
, keyPath
, true, true);
328 void DNSCryptContext::markActive(uint32_t serial
)
330 WriteLock
w(&d_lock
);
332 for (auto pair
: certs
) {
333 if (pair
->active
== false && pair
->cert
.getSerial() == serial
) {
338 throw std::runtime_error("No inactive certificate found with this serial");
341 void DNSCryptContext::markInactive(uint32_t serial
)
343 WriteLock
w(&d_lock
);
345 for (auto pair
: certs
) {
346 if (pair
->active
== true && pair
->cert
.getSerial() == serial
) {
347 pair
->active
= false;
351 throw std::runtime_error("No active certificate found with this serial");
354 void DNSCryptContext::removeInactiveCertificate(uint32_t serial
)
356 WriteLock
w(&d_lock
);
358 for (auto it
= certs
.begin(); it
!= certs
.end(); ) {
359 if ((*it
)->active
== false && (*it
)->cert
.getSerial() == serial
) {
360 it
= certs
.erase(it
);
366 throw std::runtime_error("No inactive certificate found with this serial");
369 bool DNSCryptQuery::parsePlaintextQuery(const char * packet
, uint16_t packetSize
)
371 assert(d_ctx
!= nullptr);
373 if (packetSize
< sizeof(dnsheader
)) {
377 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
);
378 if (dh
->qr
|| ntohs(dh
->qdcount
) != 1 || dh
->ancount
!= 0 || dh
->nscount
!= 0 || dh
->opcode
!= Opcode::Query
)
381 unsigned int consumed
;
382 uint16_t qtype
, qclass
;
383 DNSName
qname(packet
, packetSize
, sizeof(dnsheader
), false, &qtype
, &qclass
, &consumed
);
384 if ((packetSize
- sizeof(dnsheader
)) < (consumed
+ sizeof(qtype
) + sizeof(qclass
)))
387 if (qtype
!= QType::TXT
|| qclass
!= QClass::IN
)
390 if (qname
!= d_ctx
->getProviderName())
400 void DNSCryptContext::getCertificateResponse(time_t now
, const DNSName
& qname
, uint16_t qid
, std::vector
<uint8_t>& response
)
402 DNSPacketWriter
pw(response
, qname
, QType::TXT
, QClass::IN
, Opcode::Query
);
403 struct dnsheader
* dh
= pw
.getHeader();
406 dh
->rcode
= RCode::NoError
;
409 for (const auto pair
: certs
) {
410 if (!pair
->active
|| !pair
->cert
.isValid(now
)) {
414 pw
.startRecord(qname
, QType::TXT
, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL
), QClass::IN
, DNSResourceRecord::ANSWER
, true);
416 uint8_t certSize
= sizeof(pair
->cert
);
417 scert
.assign((const char*) &certSize
, sizeof(certSize
));
418 scert
.append((const char*) &pair
->cert
, certSize
);
425 bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery
& query
, time_t now
)
427 const unsigned char* magic
= query
.getClientMagic();
430 for (const auto& pair
: certs
) {
431 if (pair
->cert
.isValid(now
) && memcmp(magic
, pair
->cert
.signedData
.clientMagic
, DNSCRYPT_CLIENT_MAGIC_SIZE
) == 0) {
432 query
.setCertificatePair(pair
);
440 bool DNSCryptQuery::isEncryptedQuery(const char * packet
, uint16_t packetSize
, bool tcp
, time_t now
)
442 assert(d_ctx
!= nullptr);
446 if (packetSize
< sizeof(DNSCryptQueryHeader
)) {
450 if (!tcp
&& packetSize
< DNSCryptQuery::s_minUDPLength
) {
454 const struct DNSCryptQueryHeader
* header
= reinterpret_cast<const struct DNSCryptQueryHeader
*>(packet
);
458 if (!d_ctx
->magicMatchesAPublicKey(*this, now
)) {
467 void DNSCryptQuery::getDecrypted(bool tcp
, char* packet
, uint16_t packetSize
, uint16_t* decryptedQueryLen
)
469 assert(decryptedQueryLen
!= nullptr);
471 assert(d_pair
!= nullptr);
472 assert(d_valid
== false);
474 #ifdef DNSCRYPT_STRICT_PADDING_LENGTH
475 if (tcp
&& ((packetSize
- sizeof(DNSCryptQueryHeader
)) % DNSCRYPT_PADDED_BLOCK_SIZE
) != 0) {
476 vinfolog("Dropping encrypted query with invalid size of %d (should be a multiple of %d)", (packetSize
- sizeof(DNSCryptQueryHeader
)), DNSCRYPT_PADDED_BLOCK_SIZE
);
481 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
482 static_assert(sizeof(nonce
) == (2* sizeof(d_header
.clientNonce
)), "Nonce should be larger than clientNonce (half)");
483 static_assert(sizeof(d_header
.clientPK
) == DNSCRYPT_PUBLIC_KEY_SIZE
, "Client Publick key size is not right");
484 static_assert(sizeof(d_pair
->privateKey
.key
) == DNSCRYPT_PRIVATE_KEY_SIZE
, "Private key size is not right");
486 memcpy(nonce
, &d_header
.clientNonce
, sizeof(d_header
.clientNonce
));
487 memset(nonce
+ sizeof(d_header
.clientNonce
), 0, sizeof(nonce
) - sizeof(d_header
.clientNonce
));
489 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
490 int res
= computeSharedKey();
492 vinfolog("Dropping encrypted query we can't compute the shared key for");
496 const DNSCryptExchangeVersion version
= getVersion();
498 if (version
== DNSCryptExchangeVersion::VERSION1
) {
499 res
= crypto_box_open_easy_afternm(reinterpret_cast<unsigned char*>(packet
),
500 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
501 packetSize
- sizeof(DNSCryptQueryHeader
),
505 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
506 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
507 res
= crypto_box_curve25519xchacha20poly1305_open_easy_afternm(reinterpret_cast<unsigned char*>(packet
),
508 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
509 packetSize
- sizeof(DNSCryptQueryHeader
),
512 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
514 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
519 #else /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
520 int res
= crypto_box_open_easy(reinterpret_cast<unsigned char*>(packet
),
521 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
522 packetSize
- sizeof(DNSCryptQueryHeader
),
525 d_pair
->privateKey
.key
);
526 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
529 vinfolog("Dropping encrypted query we can't decrypt");
533 *decryptedQueryLen
= packetSize
- sizeof(DNSCryptQueryHeader
) - DNSCRYPT_MAC_SIZE
;
534 uint16_t pos
= *decryptedQueryLen
;
535 assert(pos
< packetSize
);
536 d_paddedLen
= *decryptedQueryLen
;
538 while(pos
> 0 && packet
[pos
- 1] == 0) pos
--;
540 if (pos
== 0 || static_cast<uint8_t>(packet
[pos
- 1]) != 0x80) {
541 vinfolog("Dropping encrypted query with invalid padding value");
547 size_t paddingLen
= *decryptedQueryLen
- pos
;
548 *decryptedQueryLen
= pos
;
550 if (tcp
&& paddingLen
> DNSCRYPT_MAX_TCP_PADDING_SIZE
) {
551 vinfolog("Dropping encrypted query with too long padding size");
559 void DNSCryptQuery::getCertificateResponse(time_t now
, std::vector
<uint8_t>& response
) const
561 assert(d_ctx
!= nullptr);
562 d_ctx
->getCertificateResponse(now
, d_qname
, d_id
, response
);
565 void DNSCryptQuery::parsePacket(char* packet
, uint16_t packetSize
, bool tcp
, uint16_t* decryptedQueryLen
, time_t now
)
567 assert(packet
!= nullptr);
568 assert(decryptedQueryLen
!= nullptr);
572 /* might be a plaintext certificate request or an authenticated request */
573 if (isEncryptedQuery(packet
, packetSize
, tcp
, now
)) {
574 getDecrypted(tcp
, packet
, packetSize
, decryptedQueryLen
);
577 parsePlaintextQuery(packet
, packetSize
);
581 void DNSCryptQuery::fillServerNonce(unsigned char* nonce
) const
583 uint32_t* dest
= reinterpret_cast<uint32_t*>(nonce
);
584 static const size_t nonceSize
= DNSCRYPT_NONCE_SIZE
/ 2;
586 for (size_t pos
= 0; pos
< (nonceSize
/ sizeof(*dest
)); pos
++)
588 const uint32_t value
= randombytes_random();
589 memcpy(dest
+ pos
, &value
, sizeof(value
));
594 "The length of <resolver-response-pad> must be between 0 and 256 bytes,
595 and must be constant for a given (<resolver-sk>, <client-nonce>) tuple."
597 uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen
, size_t maxLen
) const
599 size_t paddedSize
= 0;
602 assert(d_header
.clientNonce
);
603 assert(d_pair
!= nullptr);
605 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
606 memcpy(nonce
, d_header
.clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
607 memcpy(&(nonce
[DNSCRYPT_NONCE_SIZE
/ 2]), d_header
.clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
608 crypto_stream((unsigned char*) &rnd
, sizeof(rnd
), nonce
, d_pair
->privateKey
.key
);
610 paddedSize
= unpaddedLen
+ rnd
% (maxLen
- unpaddedLen
+ 1);
611 paddedSize
+= DNSCRYPT_PADDED_BLOCK_SIZE
- (paddedSize
% DNSCRYPT_PADDED_BLOCK_SIZE
);
613 if (paddedSize
> maxLen
)
616 result
= paddedSize
- unpaddedLen
;
621 int DNSCryptQuery::encryptResponse(char* response
, uint16_t responseLen
, uint16_t responseSize
, bool tcp
, uint16_t* encryptedResponseLen
)
623 struct DNSCryptResponseHeader responseHeader
;
624 assert(response
!= nullptr);
625 assert(responseLen
> 0);
626 assert(responseSize
>= responseLen
);
627 assert(encryptedResponseLen
!= nullptr);
628 assert(d_encrypted
== true);
629 assert(d_pair
!= nullptr);
631 if (!tcp
&& d_paddedLen
< responseLen
) {
632 struct dnsheader
* dh
= reinterpret_cast<struct dnsheader
*>(response
);
633 size_t questionSize
= 0;
635 if (responseLen
> sizeof(dnsheader
)) {
636 unsigned int consumed
= 0;
637 DNSName
tempQName(response
, responseLen
, sizeof(dnsheader
), false, 0, 0, &consumed
);
639 questionSize
= consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
643 responseLen
= sizeof(dnsheader
) + questionSize
;
645 if (responseLen
> d_paddedLen
) {
646 responseLen
= d_paddedLen
;
648 dh
->ancount
= dh
->arcount
= dh
->nscount
= 0;
652 size_t requiredSize
= sizeof(responseHeader
) + DNSCRYPT_MAC_SIZE
+ responseLen
;
653 size_t maxSize
= (responseSize
> (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
)) ? (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
) : responseSize
;
654 uint16_t paddingSize
= computePaddingSize(requiredSize
, maxSize
);
655 requiredSize
+= paddingSize
;
657 if (requiredSize
> responseSize
)
660 memcpy(&responseHeader
.nonce
, &d_header
.clientNonce
, sizeof d_header
.clientNonce
);
661 fillServerNonce(&(responseHeader
.nonce
[sizeof(d_header
.clientNonce
)]));
663 /* moving the existing response after the header + MAC */
664 memmove(response
+ sizeof(responseHeader
) + DNSCRYPT_MAC_SIZE
, response
, responseLen
);
668 memcpy(response
+ pos
, &responseHeader
, sizeof(responseHeader
));
669 pos
+= sizeof(responseHeader
);
670 /* setting MAC bytes to 0 */
671 memset(response
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
672 pos
+= DNSCRYPT_MAC_SIZE
;
673 uint16_t toEncryptPos
= pos
;
674 /* skipping response */
677 response
[pos
] = static_cast<uint8_t>(0x80);
679 memset(response
+ pos
, 0, paddingSize
- 1);
680 pos
+= (paddingSize
- 1);
683 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
684 int res
= computeSharedKey();
689 const DNSCryptExchangeVersion version
= getVersion();
691 if (version
== DNSCryptExchangeVersion::VERSION1
) {
692 res
= crypto_box_easy_afternm(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
693 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
694 responseLen
+ paddingSize
,
695 responseHeader
.nonce
,
698 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
699 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
700 res
= crypto_box_curve25519xchacha20poly1305_easy_afternm(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
701 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
702 responseLen
+ paddingSize
,
703 responseHeader
.nonce
,
705 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
707 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
713 int res
= crypto_box_easy(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
714 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
715 responseLen
+ paddingSize
,
716 responseHeader
.nonce
,
718 d_pair
->privateKey
.key
);
719 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
722 assert(pos
== requiredSize
);
723 *encryptedResponseLen
= requiredSize
;
729 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
731 assert(query
!= nullptr);
732 assert(queryLen
> 0);
733 assert(querySize
>= queryLen
);
734 assert(encryptedResponseLen
!= nullptr);
735 assert(cert
!= nullptr);
737 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
738 size_t requiredSize
= sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
+ queryLen
;
739 /* this is not optimal, we should compute a random padding size, multiple of DNSCRYPT_PADDED_BLOCK_SIZE,
740 DNSCRYPT_PADDED_BLOCK_SIZE <= padding size <= 4096? */
741 uint16_t paddingSize
= DNSCRYPT_PADDED_BLOCK_SIZE
- (queryLen
% DNSCRYPT_PADDED_BLOCK_SIZE
);
742 requiredSize
+= paddingSize
;
744 if (!tcp
&& requiredSize
< DNSCryptQuery::s_minUDPLength
) {
745 paddingSize
+= (DNSCryptQuery::s_minUDPLength
- requiredSize
);
746 requiredSize
= DNSCryptQuery::s_minUDPLength
;
749 if (requiredSize
> querySize
)
752 /* moving the existing query after the header + MAC */
753 memmove(query
+ sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
, query
, queryLen
);
757 memcpy(query
+ pos
, cert
->signedData
.clientMagic
, sizeof(cert
->signedData
.clientMagic
));
758 pos
+= sizeof(cert
->signedData
.clientMagic
);
761 memcpy(query
+ pos
, clientPublicKey
, DNSCRYPT_PUBLIC_KEY_SIZE
);
762 pos
+= DNSCRYPT_PUBLIC_KEY_SIZE
;
765 memcpy(query
+ pos
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
766 pos
+= DNSCRYPT_NONCE_SIZE
/ 2;
767 size_t encryptedPos
= pos
;
769 /* clear the MAC bytes */
770 memset(query
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
771 pos
+= DNSCRYPT_MAC_SIZE
;
777 query
[pos
] = static_cast<uint8_t>(0x80);
779 memset(query
+ pos
, 0, paddingSize
- 1);
780 pos
+= paddingSize
- 1;
782 memcpy(nonce
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
783 memset(nonce
+ (DNSCRYPT_NONCE_SIZE
/ 2), 0, DNSCRYPT_NONCE_SIZE
/ 2);
785 const DNSCryptExchangeVersion version
= getExchangeVersion(*cert
);
788 if (version
== DNSCryptExchangeVersion::VERSION1
) {
789 res
= crypto_box_easy(reinterpret_cast<unsigned char*>(query
+ encryptedPos
),
790 reinterpret_cast<unsigned char*>(query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
),
791 queryLen
+ paddingSize
,
793 cert
->signedData
.resolverPK
,
794 clientPrivateKey
.key
);
796 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
797 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
798 res
= crypto_box_curve25519xchacha20poly1305_easy(reinterpret_cast<unsigned char*>(query
+ encryptedPos
),
799 reinterpret_cast<unsigned char*>(query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
),
800 queryLen
+ paddingSize
,
802 cert
->signedData
.resolverPK
,
803 clientPrivateKey
.key
);
804 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
807 throw std::runtime_error("Unknown DNSCrypt exchange version");
811 assert(pos
== requiredSize
);
812 *encryptedResponseLen
= requiredSize
;
818 bool generateDNSCryptCertificate(const std::string
& providerPrivateKeyFile
, uint32_t serial
, time_t begin
, time_t end
, DNSCryptExchangeVersion version
, DNSCryptCert
& certOut
, DNSCryptPrivateKey
& keyOut
)
820 bool success
= false;
821 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
822 sodium_mlock(providerPrivateKey
, sizeof(providerPrivateKey
));
823 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
826 ifstream
providerKStream(providerPrivateKeyFile
);
827 providerKStream
.read((char*) providerPrivateKey
, sizeof(providerPrivateKey
));
828 if (providerKStream
.fail()) {
829 providerKStream
.close();
830 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile
);
833 DNSCryptContext::generateCertificate(serial
, begin
, end
, version
, providerPrivateKey
, keyOut
, certOut
);
836 catch(const std::exception
& e
) {
840 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
841 sodium_munlock(providerPrivateKey
, sizeof(providerPrivateKey
));