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
)
288 WriteLock
w(&d_lock
);
290 for (auto pair
: certs
) {
291 if (pair
->cert
.getSerial() == newCert
.getSerial()) {
292 throw std::runtime_error("Error adding a new certificate: we already have a certificate with the same serial");
296 auto pair
= std::make_shared
<DNSCryptCertificatePair
>();
297 pair
->cert
= newCert
;
298 pair
->privateKey
= newKey
;
299 computePublicKeyFromPrivate(pair
->privateKey
, pair
->publicKey
);
300 pair
->active
= active
;
301 certs
.push_back(pair
);
304 void DNSCryptContext::loadNewCertificate(const std::string
& certFile
, const std::string
& keyFile
, bool active
)
306 DNSCryptCert newCert
;
307 DNSCryptPrivateKey newPrivateKey
;
309 loadCertFromFile(certFile
, newCert
);
310 newPrivateKey
.loadFromFile(keyFile
);
312 addNewCertificate(newCert
, newPrivateKey
, active
);
315 void DNSCryptContext::markActive(uint32_t serial
)
317 WriteLock
w(&d_lock
);
319 for (auto pair
: certs
) {
320 if (pair
->active
== false && pair
->cert
.getSerial() == serial
) {
325 throw std::runtime_error("No inactive certificate found with this serial");
328 void DNSCryptContext::markInactive(uint32_t serial
)
330 WriteLock
w(&d_lock
);
332 for (auto pair
: certs
) {
333 if (pair
->active
== true && pair
->cert
.getSerial() == serial
) {
334 pair
->active
= false;
338 throw std::runtime_error("No active certificate found with this serial");
341 void DNSCryptContext::removeInactiveCertificate(uint32_t serial
)
343 WriteLock
w(&d_lock
);
345 for (auto it
= certs
.begin(); it
!= certs
.end(); ) {
346 if ((*it
)->active
== false && (*it
)->cert
.getSerial() == serial
) {
347 it
= certs
.erase(it
);
353 throw std::runtime_error("No inactive certificate found with this serial");
356 bool DNSCryptQuery::parsePlaintextQuery(const char * packet
, uint16_t packetSize
)
358 assert(d_ctx
!= nullptr);
360 if (packetSize
< sizeof(dnsheader
)) {
364 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
);
365 if (dh
->qr
|| ntohs(dh
->qdcount
) != 1 || dh
->ancount
!= 0 || dh
->nscount
!= 0 || dh
->opcode
!= Opcode::Query
)
368 unsigned int consumed
;
369 uint16_t qtype
, qclass
;
370 DNSName
qname(packet
, packetSize
, sizeof(dnsheader
), false, &qtype
, &qclass
, &consumed
);
371 if ((packetSize
- sizeof(dnsheader
)) < (consumed
+ sizeof(qtype
) + sizeof(qclass
)))
374 if (qtype
!= QType::TXT
|| qclass
!= QClass::IN
)
377 if (qname
!= d_ctx
->getProviderName())
387 void DNSCryptContext::getCertificateResponse(time_t now
, const DNSName
& qname
, uint16_t qid
, std::vector
<uint8_t>& response
)
389 DNSPacketWriter
pw(response
, qname
, QType::TXT
, QClass::IN
, Opcode::Query
);
390 struct dnsheader
* dh
= pw
.getHeader();
393 dh
->rcode
= RCode::NoError
;
396 for (const auto pair
: certs
) {
397 if (!pair
->active
|| !pair
->cert
.isValid(now
)) {
401 pw
.startRecord(qname
, QType::TXT
, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL
), QClass::IN
, DNSResourceRecord::ANSWER
, true);
403 uint8_t certSize
= sizeof(pair
->cert
);
404 scert
.assign((const char*) &certSize
, sizeof(certSize
));
405 scert
.append((const char*) &pair
->cert
, certSize
);
412 bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery
& query
, time_t now
)
414 const unsigned char* magic
= query
.getClientMagic();
417 for (const auto& pair
: certs
) {
418 if (pair
->cert
.isValid(now
) && memcmp(magic
, pair
->cert
.signedData
.clientMagic
, DNSCRYPT_CLIENT_MAGIC_SIZE
) == 0) {
419 query
.setCertificatePair(pair
);
427 bool DNSCryptQuery::isEncryptedQuery(const char * packet
, uint16_t packetSize
, bool tcp
, time_t now
)
429 assert(d_ctx
!= nullptr);
433 if (packetSize
< sizeof(DNSCryptQueryHeader
)) {
437 if (!tcp
&& packetSize
< DNSCryptQuery::s_minUDPLength
) {
441 const struct DNSCryptQueryHeader
* header
= reinterpret_cast<const struct DNSCryptQueryHeader
*>(packet
);
445 if (!d_ctx
->magicMatchesAPublicKey(*this, now
)) {
454 void DNSCryptQuery::getDecrypted(bool tcp
, char* packet
, uint16_t packetSize
, uint16_t* decryptedQueryLen
)
456 assert(decryptedQueryLen
!= nullptr);
458 assert(d_pair
!= nullptr);
459 assert(d_valid
== false);
461 #ifdef DNSCRYPT_STRICT_PADDING_LENGTH
462 if (tcp
&& ((packetSize
- sizeof(DNSCryptQueryHeader
)) % DNSCRYPT_PADDED_BLOCK_SIZE
) != 0) {
463 vinfolog("Dropping encrypted query with invalid size of %d (should be a multiple of %d)", (packetSize
- sizeof(DNSCryptQueryHeader
)), DNSCRYPT_PADDED_BLOCK_SIZE
);
468 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
469 static_assert(sizeof(nonce
) == (2* sizeof(d_header
.clientNonce
)), "Nonce should be larger than clientNonce (half)");
470 static_assert(sizeof(d_header
.clientPK
) == DNSCRYPT_PUBLIC_KEY_SIZE
, "Client Publick key size is not right");
471 static_assert(sizeof(d_pair
->privateKey
.key
) == DNSCRYPT_PRIVATE_KEY_SIZE
, "Private key size is not right");
473 memcpy(nonce
, &d_header
.clientNonce
, sizeof(d_header
.clientNonce
));
474 memset(nonce
+ sizeof(d_header
.clientNonce
), 0, sizeof(nonce
) - sizeof(d_header
.clientNonce
));
476 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
477 int res
= computeSharedKey();
479 vinfolog("Dropping encrypted query we can't compute the shared key for");
483 const DNSCryptExchangeVersion version
= getVersion();
485 if (version
== DNSCryptExchangeVersion::VERSION1
) {
486 res
= crypto_box_open_easy_afternm(reinterpret_cast<unsigned char*>(packet
),
487 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
488 packetSize
- sizeof(DNSCryptQueryHeader
),
492 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
493 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
494 res
= crypto_box_curve25519xchacha20poly1305_open_easy_afternm(reinterpret_cast<unsigned char*>(packet
),
495 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
496 packetSize
- sizeof(DNSCryptQueryHeader
),
499 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
501 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
506 #else /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
507 int res
= crypto_box_open_easy(reinterpret_cast<unsigned char*>(packet
),
508 reinterpret_cast<unsigned char*>(packet
+ sizeof(DNSCryptQueryHeader
)),
509 packetSize
- sizeof(DNSCryptQueryHeader
),
512 d_pair
->privateKey
.key
);
513 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
516 vinfolog("Dropping encrypted query we can't decrypt");
520 *decryptedQueryLen
= packetSize
- sizeof(DNSCryptQueryHeader
) - DNSCRYPT_MAC_SIZE
;
521 uint16_t pos
= *decryptedQueryLen
;
522 assert(pos
< packetSize
);
523 d_paddedLen
= *decryptedQueryLen
;
525 while(pos
> 0 && packet
[pos
- 1] == 0) pos
--;
527 if (pos
== 0 || static_cast<uint8_t>(packet
[pos
- 1]) != 0x80) {
528 vinfolog("Dropping encrypted query with invalid padding value");
534 size_t paddingLen
= *decryptedQueryLen
- pos
;
535 *decryptedQueryLen
= pos
;
537 if (tcp
&& paddingLen
> DNSCRYPT_MAX_TCP_PADDING_SIZE
) {
538 vinfolog("Dropping encrypted query with too long padding size");
546 void DNSCryptQuery::getCertificateResponse(time_t now
, std::vector
<uint8_t>& response
) const
548 assert(d_ctx
!= nullptr);
549 d_ctx
->getCertificateResponse(now
, d_qname
, d_id
, response
);
552 void DNSCryptQuery::parsePacket(char* packet
, uint16_t packetSize
, bool tcp
, uint16_t* decryptedQueryLen
, time_t now
)
554 assert(packet
!= nullptr);
555 assert(decryptedQueryLen
!= nullptr);
559 /* might be a plaintext certificate request or an authenticated request */
560 if (isEncryptedQuery(packet
, packetSize
, tcp
, now
)) {
561 getDecrypted(tcp
, packet
, packetSize
, decryptedQueryLen
);
564 parsePlaintextQuery(packet
, packetSize
);
568 void DNSCryptQuery::fillServerNonce(unsigned char* nonce
) const
570 uint32_t* dest
= reinterpret_cast<uint32_t*>(nonce
);
571 static const size_t nonceSize
= DNSCRYPT_NONCE_SIZE
/ 2;
573 for (size_t pos
= 0; pos
< (nonceSize
/ sizeof(*dest
)); pos
++)
575 const uint32_t value
= randombytes_random();
576 memcpy(dest
+ pos
, &value
, sizeof(value
));
581 "The length of <resolver-response-pad> must be between 0 and 256 bytes,
582 and must be constant for a given (<resolver-sk>, <client-nonce>) tuple."
584 uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen
, size_t maxLen
) const
586 size_t paddedSize
= 0;
589 assert(d_header
.clientNonce
);
590 assert(d_pair
!= nullptr);
592 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
593 memcpy(nonce
, d_header
.clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
594 memcpy(&(nonce
[DNSCRYPT_NONCE_SIZE
/ 2]), d_header
.clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
595 crypto_stream((unsigned char*) &rnd
, sizeof(rnd
), nonce
, d_pair
->privateKey
.key
);
597 paddedSize
= unpaddedLen
+ rnd
% (maxLen
- unpaddedLen
+ 1);
598 paddedSize
+= DNSCRYPT_PADDED_BLOCK_SIZE
- (paddedSize
% DNSCRYPT_PADDED_BLOCK_SIZE
);
600 if (paddedSize
> maxLen
)
603 result
= paddedSize
- unpaddedLen
;
608 int DNSCryptQuery::encryptResponse(char* response
, uint16_t responseLen
, uint16_t responseSize
, bool tcp
, uint16_t* encryptedResponseLen
)
610 struct DNSCryptResponseHeader responseHeader
;
611 assert(response
!= nullptr);
612 assert(responseLen
> 0);
613 assert(responseSize
>= responseLen
);
614 assert(encryptedResponseLen
!= nullptr);
615 assert(d_encrypted
== true);
616 assert(d_pair
!= nullptr);
618 if (!tcp
&& d_paddedLen
< responseLen
) {
619 struct dnsheader
* dh
= reinterpret_cast<struct dnsheader
*>(response
);
620 size_t questionSize
= 0;
622 if (responseLen
> sizeof(dnsheader
)) {
623 unsigned int consumed
= 0;
624 DNSName
tempQName(response
, responseLen
, sizeof(dnsheader
), false, 0, 0, &consumed
);
626 questionSize
= consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
630 responseLen
= sizeof(dnsheader
) + questionSize
;
632 if (responseLen
> d_paddedLen
) {
633 responseLen
= d_paddedLen
;
635 dh
->ancount
= dh
->arcount
= dh
->nscount
= 0;
639 size_t requiredSize
= sizeof(responseHeader
) + DNSCRYPT_MAC_SIZE
+ responseLen
;
640 size_t maxSize
= (responseSize
> (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
)) ? (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
) : responseSize
;
641 uint16_t paddingSize
= computePaddingSize(requiredSize
, maxSize
);
642 requiredSize
+= paddingSize
;
644 if (requiredSize
> responseSize
)
647 memcpy(&responseHeader
.nonce
, &d_header
.clientNonce
, sizeof d_header
.clientNonce
);
648 fillServerNonce(&(responseHeader
.nonce
[sizeof(d_header
.clientNonce
)]));
650 /* moving the existing response after the header + MAC */
651 memmove(response
+ sizeof(responseHeader
) + DNSCRYPT_MAC_SIZE
, response
, responseLen
);
655 memcpy(response
+ pos
, &responseHeader
, sizeof(responseHeader
));
656 pos
+= sizeof(responseHeader
);
657 /* setting MAC bytes to 0 */
658 memset(response
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
659 pos
+= DNSCRYPT_MAC_SIZE
;
660 uint16_t toEncryptPos
= pos
;
661 /* skipping response */
664 response
[pos
] = static_cast<uint8_t>(0x80);
666 memset(response
+ pos
, 0, paddingSize
- 1);
667 pos
+= (paddingSize
- 1);
670 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
671 int res
= computeSharedKey();
676 const DNSCryptExchangeVersion version
= getVersion();
678 if (version
== DNSCryptExchangeVersion::VERSION1
) {
679 res
= crypto_box_easy_afternm(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
680 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
681 responseLen
+ paddingSize
,
682 responseHeader
.nonce
,
685 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
686 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
687 res
= crypto_box_curve25519xchacha20poly1305_easy_afternm(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
688 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
689 responseLen
+ paddingSize
,
690 responseHeader
.nonce
,
692 #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
694 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
700 int res
= crypto_box_easy(reinterpret_cast<unsigned char*>(response
+ sizeof(responseHeader
)),
701 reinterpret_cast<unsigned char*>(response
+ toEncryptPos
),
702 responseLen
+ paddingSize
,
703 responseHeader
.nonce
,
705 d_pair
->privateKey
.key
);
706 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
709 assert(pos
== requiredSize
);
710 *encryptedResponseLen
= requiredSize
;
716 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
718 assert(query
!= nullptr);
719 assert(queryLen
> 0);
720 assert(querySize
>= queryLen
);
721 assert(encryptedResponseLen
!= nullptr);
722 assert(cert
!= nullptr);
724 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
725 size_t requiredSize
= sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
+ queryLen
;
726 /* this is not optimal, we should compute a random padding size, multiple of DNSCRYPT_PADDED_BLOCK_SIZE,
727 DNSCRYPT_PADDED_BLOCK_SIZE <= padding size <= 4096? */
728 uint16_t paddingSize
= DNSCRYPT_PADDED_BLOCK_SIZE
- (queryLen
% DNSCRYPT_PADDED_BLOCK_SIZE
);
729 requiredSize
+= paddingSize
;
731 if (!tcp
&& requiredSize
< DNSCryptQuery::s_minUDPLength
) {
732 paddingSize
+= (DNSCryptQuery::s_minUDPLength
- requiredSize
);
733 requiredSize
= DNSCryptQuery::s_minUDPLength
;
736 if (requiredSize
> querySize
)
739 /* moving the existing query after the header + MAC */
740 memmove(query
+ sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
, query
, queryLen
);
744 memcpy(query
+ pos
, cert
->signedData
.clientMagic
, sizeof(cert
->signedData
.clientMagic
));
745 pos
+= sizeof(cert
->signedData
.clientMagic
);
748 memcpy(query
+ pos
, clientPublicKey
, DNSCRYPT_PUBLIC_KEY_SIZE
);
749 pos
+= DNSCRYPT_PUBLIC_KEY_SIZE
;
752 memcpy(query
+ pos
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
753 pos
+= DNSCRYPT_NONCE_SIZE
/ 2;
754 size_t encryptedPos
= pos
;
756 /* clear the MAC bytes */
757 memset(query
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
758 pos
+= DNSCRYPT_MAC_SIZE
;
764 query
[pos
] = static_cast<uint8_t>(0x80);
766 memset(query
+ pos
, 0, paddingSize
- 1);
767 pos
+= paddingSize
- 1;
769 memcpy(nonce
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
770 memset(nonce
+ (DNSCRYPT_NONCE_SIZE
/ 2), 0, DNSCRYPT_NONCE_SIZE
/ 2);
772 const DNSCryptExchangeVersion version
= getExchangeVersion(*cert
);
775 if (version
== DNSCryptExchangeVersion::VERSION1
) {
776 res
= crypto_box_easy(reinterpret_cast<unsigned char*>(query
+ encryptedPos
),
777 reinterpret_cast<unsigned char*>(query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
),
778 queryLen
+ paddingSize
,
780 cert
->signedData
.resolverPK
,
781 clientPrivateKey
.key
);
783 else if (version
== DNSCryptExchangeVersion::VERSION2
) {
784 #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY
785 res
= crypto_box_curve25519xchacha20poly1305_easy(reinterpret_cast<unsigned char*>(query
+ encryptedPos
),
786 reinterpret_cast<unsigned char*>(query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
),
787 queryLen
+ paddingSize
,
789 cert
->signedData
.resolverPK
,
790 clientPrivateKey
.key
);
791 #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */
794 throw std::runtime_error("Unknown DNSCrypt exchange version");
798 assert(pos
== requiredSize
);
799 *encryptedResponseLen
= requiredSize
;
805 bool generateDNSCryptCertificate(const std::string
& providerPrivateKeyFile
, uint32_t serial
, time_t begin
, time_t end
, DNSCryptExchangeVersion version
, DNSCryptCert
& certOut
, DNSCryptPrivateKey
& keyOut
)
807 bool success
= false;
808 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
809 sodium_mlock(providerPrivateKey
, sizeof(providerPrivateKey
));
810 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
813 ifstream
providerKStream(providerPrivateKeyFile
);
814 providerKStream
.read((char*) providerPrivateKey
, sizeof(providerPrivateKey
));
815 if (providerKStream
.fail()) {
816 providerKStream
.close();
817 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile
);
820 DNSCryptContext::generateCertificate(serial
, begin
, end
, version
, providerPrivateKey
, keyOut
, certOut
);
823 catch(const std::exception
& e
) {
827 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
828 sodium_munlock(providerPrivateKey
, sizeof(providerPrivateKey
));