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 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
63 DnsCryptQuery::~DnsCryptQuery()
65 if (sharedKeyComputed
) {
66 sodium_munlock(sharedKey
, sizeof(sharedKey
));
70 int DnsCryptQuery::computeSharedKey(const DnsCryptPrivateKey
& privateKey
)
74 if (sharedKeyComputed
) {
78 sodium_mlock(sharedKey
, sizeof(sharedKey
));
79 res
= crypto_box_beforenm(sharedKey
,
84 sodium_munlock(sharedKey
, sizeof(sharedKey
));
88 sharedKeyComputed
= true;
92 DnsCryptQuery::~DnsCryptQuery()
95 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
97 void DnsCryptContext::generateProviderKeys(unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
], unsigned char privateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
])
99 int res
= crypto_sign_ed25519_keypair(publicKey
, privateKey
);
102 throw std::runtime_error("Error generating DNSCrypt provider keys");
106 std::string
DnsCryptContext::getProviderFingerprint(unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
])
108 boost::format
fmt("%02X%02X");
111 for (size_t idx
= 0; idx
< DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
; idx
+= 2)
113 ret
<< (fmt
% static_cast<int>(publicKey
[idx
]) % static_cast<int>(publicKey
[idx
+1]));
114 if (idx
< (DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
- 2)) {
122 void DnsCryptContext::generateCertificate(uint32_t serial
, time_t begin
, time_t end
, const unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
], DnsCryptPrivateKey
& privateKey
, DnsCryptCert
& cert
)
124 unsigned char magic
[DNSCRYPT_CERT_MAGIC_SIZE
] = DNSCRYPT_CERT_MAGIC_VALUE
;
125 unsigned char esVersion
[] = DNSCRYPT_CERT_ES_VERSION_VALUE
;
126 unsigned char protocolMinorVersion
[] = DNSCRYPT_CERT_PROTOCOL_MINOR_VERSION_VALUE
;
127 unsigned char pubK
[DNSCRYPT_PUBLIC_KEY_SIZE
];
128 generateResolverKeyPair(privateKey
, pubK
);
130 memcpy(cert
.magic
, magic
, sizeof(magic
));
131 memcpy(cert
.esVersion
, esVersion
, sizeof(esVersion
));
132 memcpy(cert
.protocolMinorVersion
, protocolMinorVersion
, sizeof(protocolMinorVersion
));
133 memcpy(cert
.signedData
.resolverPK
, pubK
, sizeof(cert
.signedData
.resolverPK
));
134 memcpy(cert
.signedData
.clientMagic
, pubK
, sizeof(cert
.signedData
.clientMagic
));
135 cert
.signedData
.serial
= serial
;
136 cert
.signedData
.tsStart
= htonl((uint32_t) begin
);
137 cert
.signedData
.tsEnd
= htonl((uint32_t) end
);
139 unsigned long long signatureSize
= 0;
141 int res
= crypto_sign_ed25519(cert
.signature
,
143 (unsigned char*) &cert
.signedData
,
144 sizeof(cert
.signedData
),
148 assert(signatureSize
== sizeof(DnsCryptCertSignedData
) + DNSCRYPT_SIGNATURE_SIZE
);
151 throw std::runtime_error("Error generating DNSCrypt certificate");
155 void DnsCryptContext::loadCertFromFile(const std::string
&filename
, DnsCryptCert
& dest
)
157 ifstream
file(filename
);
158 file
.read((char *) &dest
, sizeof(dest
));
161 throw std::runtime_error("Invalid dnscrypt certificate file " + filename
);
166 void DnsCryptContext::saveCertFromFile(const DnsCryptCert
& cert
, const std::string
&filename
)
168 ofstream
file(filename
);
169 file
.write((char *) &cert
, sizeof(cert
));
173 void DnsCryptContext::generateResolverKeyPair(DnsCryptPrivateKey
& privK
, unsigned char pubK
[DNSCRYPT_PUBLIC_KEY_SIZE
])
175 int res
= crypto_box_keypair(pubK
, privK
.key
);
178 throw std::runtime_error("Error generating DNSCrypt resolver keys");
182 void DnsCryptContext::computePublicKeyFromPrivate(const DnsCryptPrivateKey
& privK
, unsigned char* pubK
)
184 int res
= crypto_scalarmult_base(pubK
,
188 throw std::runtime_error("Error computing dnscrypt public key from the private one");
192 std::string
DnsCryptContext::certificateDateToStr(uint32_t date
)
195 time_t tdate
= (time_t) ntohl(date
);
198 localtime_r(&tdate
, &date_tm
);
199 strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S", &date_tm
);
204 void DnsCryptContext::setNewCertificate(const DnsCryptCert
& newCert
, const DnsCryptPrivateKey
& newKey
)
206 // XXX TODO: this could use a lock
207 oldPrivateKey
= privateKey
;
214 void DnsCryptContext::loadNewCertificate(const std::string
& certFile
, const std::string
& keyFile
)
216 DnsCryptCert newCert
;
217 DnsCryptPrivateKey newPrivateKey
;
219 loadCertFromFile(certFile
, newCert
);
220 newPrivateKey
.loadFromFile(keyFile
);
221 setNewCertificate(newCert
, newPrivateKey
);
224 void DnsCryptContext::parsePlaintextQuery(const char * packet
, uint16_t packetSize
, std::shared_ptr
<DnsCryptQuery
> query
) const
226 if (packetSize
< sizeof(dnsheader
)) {
230 struct dnsheader
* dh
= (struct dnsheader
*) packet
;
231 if (dh
->qr
|| ntohs(dh
->qdcount
) != 1 || dh
->ancount
!= 0 || dh
->nscount
!= 0 || dh
->opcode
!= Opcode::Query
)
234 unsigned int consumed
;
235 uint16_t qtype
, qclass
;
236 DNSName
qname(packet
, packetSize
, sizeof(dnsheader
), false, &qtype
, &qclass
, &consumed
);
237 if ((packetSize
- sizeof(dnsheader
)) < (consumed
+ sizeof(qtype
) + sizeof(qclass
)))
240 if (qtype
!= QType::TXT
|| qclass
!= QClass::IN
)
243 if (qname
!= DNSName(providerName
))
246 query
->qname
= qname
;
251 void DnsCryptContext::getCertificateResponse(const std::shared_ptr
<DnsCryptQuery
> query
, vector
<uint8_t>& response
) const
253 DNSPacketWriter
pw(response
, query
->qname
, QType::TXT
, QClass::IN
, Opcode::Query
);
254 struct dnsheader
* dh
= pw
.getHeader();
257 dh
->rcode
= RCode::NoError
;
258 pw
.startRecord(query
->qname
, QType::TXT
, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL
), QClass::IN
, DNSResourceRecord::ANSWER
, true);
260 uint8_t certSize
= sizeof(cert
);
261 scert
.assign((const char*) &certSize
, sizeof(certSize
));
262 scert
.append((const char*) &cert
, certSize
);
268 bool DnsCryptContext::magicMatchesPublicKey(std::shared_ptr
<DnsCryptQuery
> query
) const
270 const unsigned char* magic
= query
->header
.clientMagic
;
272 if (memcmp(magic
, cert
.signedData
.clientMagic
, DNSCRYPT_CLIENT_MAGIC_SIZE
) == 0) {
276 if (hasOldCert
== true &&
277 memcmp(magic
, oldCert
.signedData
.clientMagic
, DNSCRYPT_CLIENT_MAGIC_SIZE
) == 0) {
278 query
->useOldCert
= true;
285 void DnsCryptContext::isQueryEncrypted(const char * packet
, uint16_t packetSize
, std::shared_ptr
<DnsCryptQuery
> query
, bool tcp
) const
287 query
->encrypted
= false;
289 if (packetSize
< sizeof(DnsCryptQueryHeader
)) {
293 if (!tcp
&& packetSize
< DnsCryptQuery::minUDPLength
) {
297 struct DnsCryptQueryHeader
* header
= (struct DnsCryptQueryHeader
*) packet
;
299 query
->header
= *(header
);
301 if (!magicMatchesPublicKey(query
)) {
305 query
->encrypted
= true;
308 void DnsCryptContext::getDecryptedQuery(std::shared_ptr
<DnsCryptQuery
> query
, bool tcp
, char* packet
, uint16_t packetSize
, uint16_t* decryptedQueryLen
) const
310 assert(decryptedQueryLen
!= NULL
);
311 assert(query
->encrypted
);
312 assert(query
->valid
== false);
314 #ifdef DNSCRYPT_STRICT_PADDING_LENGTH
315 if (tcp
&& ((packetSize
- sizeof(DnsCryptQueryHeader
)) % DNSCRYPT_PADDED_BLOCK_SIZE
) != 0) {
316 vinfolog("Dropping encrypted query with invalid size of %d (should be a multiple of %d)", (packetSize
- sizeof(DnsCryptQueryHeader
)), DNSCRYPT_PADDED_BLOCK_SIZE
);
321 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
322 static_assert(sizeof(nonce
) == (2* sizeof(query
->header
.clientNonce
)), "Nonce should be larger than clientNonce (half)");
323 static_assert(sizeof(query
->header
.clientPK
) == DNSCRYPT_PUBLIC_KEY_SIZE
, "Client Publick key size is not right");
324 static_assert(sizeof(privateKey
.key
) == DNSCRYPT_PRIVATE_KEY_SIZE
, "Private key size is not right");
326 memcpy(nonce
, &query
->header
.clientNonce
, sizeof(query
->header
.clientNonce
));
327 memset(nonce
+ sizeof(query
->header
.clientNonce
), 0, sizeof(nonce
) - sizeof(query
->header
.clientNonce
));
329 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
330 int res
= query
->computeSharedKey(query
->useOldCert
? oldPrivateKey
: privateKey
);
332 vinfolog("Dropping encrypted query we can't compute the shared key for");
336 res
= crypto_box_open_easy_afternm((unsigned char*) packet
,
337 (unsigned char*) packet
+ sizeof(DnsCryptQueryHeader
),
338 packetSize
- sizeof(DnsCryptQueryHeader
),
342 int res
= crypto_box_open_easy((unsigned char*) packet
,
343 (unsigned char*) packet
+ sizeof(DnsCryptQueryHeader
),
344 packetSize
- sizeof(DnsCryptQueryHeader
),
346 query
->header
.clientPK
,
347 query
->useOldCert
? oldPrivateKey
.key
: privateKey
.key
);
348 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
351 vinfolog("Dropping encrypted query we can't decrypt");
355 *decryptedQueryLen
= packetSize
- sizeof(DnsCryptQueryHeader
) - DNSCRYPT_MAC_SIZE
;
356 uint16_t pos
= *decryptedQueryLen
;
357 assert(pos
< packetSize
);
358 query
->paddedLen
= *decryptedQueryLen
;
360 while(pos
> 0 && packet
[pos
- 1] == 0) pos
--;
362 if (pos
== 0 || ((uint8_t) packet
[pos
- 1]) != 0x80) {
363 vinfolog("Dropping encrypted query with invalid padding value");
369 size_t paddingLen
= *decryptedQueryLen
- pos
;
370 *decryptedQueryLen
= pos
;
372 if (tcp
&& paddingLen
> DNSCRYPT_MAX_TCP_PADDING_SIZE
) {
373 vinfolog("Dropping encrypted query with too long padding size");
382 void DnsCryptContext::parsePacket(char* packet
, uint16_t packetSize
, std::shared_ptr
<DnsCryptQuery
> query
, bool tcp
, uint16_t* decryptedQueryLen
) const
384 assert(packet
!= NULL
);
385 assert(decryptedQueryLen
!= NULL
);
387 query
->valid
= false;
389 /* might be a plaintext certificate request or an authenticated request */
390 isQueryEncrypted(packet
, packetSize
, query
, tcp
);
392 if (query
->encrypted
) {
393 getDecryptedQuery(query
, tcp
, packet
, packetSize
, decryptedQueryLen
);
396 parsePlaintextQuery(packet
, packetSize
, query
);
400 void DnsCryptContext::fillServerNonce(unsigned char* nonce
) const
402 uint32_t* dest
= (uint32_t*) nonce
;
403 static const size_t nonceSize
= DNSCRYPT_NONCE_SIZE
/ 2;
405 for (size_t pos
= 0; pos
< (nonceSize
/ sizeof(*dest
)); pos
++)
407 const uint32_t value
= randombytes_random();
408 memcpy(dest
+ pos
, &value
, sizeof(value
));
413 "The length of <resolver-response-pad> must be between 0 and 256 bytes,
414 and must be constant for a given (<resolver-sk>, <client-nonce>) tuple."
416 uint16_t DnsCryptContext::computePaddingSize(uint16_t unpaddedLen
, size_t maxLen
, const unsigned char* clientNonce
) const
418 size_t paddedLen
= 0;
421 assert(clientNonce
!= NULL
);
422 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
423 memcpy(nonce
, clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
424 memcpy(&(nonce
[DNSCRYPT_NONCE_SIZE
/ 2]), clientNonce
, (DNSCRYPT_NONCE_SIZE
/ 2));
425 crypto_stream((unsigned char*) &rnd
, sizeof(rnd
), nonce
, privateKey
.key
);
427 paddedLen
= unpaddedLen
+ rnd
% (maxLen
- unpaddedLen
+ 1);
428 paddedLen
+= DNSCRYPT_PADDED_BLOCK_SIZE
- (paddedLen
% DNSCRYPT_PADDED_BLOCK_SIZE
);
430 if (paddedLen
> maxLen
)
433 result
= paddedLen
- unpaddedLen
;
438 int DnsCryptContext::encryptResponse(char* response
, uint16_t responseLen
, uint16_t responseSize
, const std::shared_ptr
<DnsCryptQuery
> query
, bool tcp
, uint16_t* encryptedResponseLen
) const
440 struct DnsCryptResponseHeader header
;
441 assert(response
!= NULL
);
442 assert(responseLen
> 0);
443 assert(responseSize
>= responseLen
);
444 assert(encryptedResponseLen
!= NULL
);
445 assert(query
->encrypted
== true);
447 if (!tcp
&& query
->paddedLen
< responseLen
) {
448 struct dnsheader
* dh
= (struct dnsheader
*) response
;
449 size_t questionSize
= 0;
451 if (responseLen
> sizeof(dnsheader
)) {
452 unsigned int consumed
= 0;
453 DNSName
qname(response
, responseLen
, sizeof(dnsheader
), false, 0, 0, &consumed
);
455 questionSize
= consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
459 responseLen
= sizeof(dnsheader
) + questionSize
;
461 if (responseLen
> query
->paddedLen
) {
462 responseLen
= query
->paddedLen
;
464 dh
->ancount
= dh
->arcount
= dh
->nscount
= 0;
468 size_t requiredSize
= sizeof(header
) + DNSCRYPT_MAC_SIZE
+ responseLen
;
469 size_t maxSize
= (responseSize
> (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
)) ? (requiredSize
+ DNSCRYPT_MAX_RESPONSE_PADDING_SIZE
) : responseSize
;
470 uint16_t paddingSize
= computePaddingSize(requiredSize
, maxSize
, query
->header
.clientNonce
);
471 requiredSize
+= paddingSize
;
473 if (requiredSize
> responseSize
)
476 memcpy(&header
.nonce
, &query
->header
.clientNonce
, sizeof query
->header
.clientNonce
);
477 fillServerNonce(&(header
.nonce
[sizeof(query
->header
.clientNonce
)]));
479 /* moving the existing response after the header + MAC */
480 memmove(response
+ sizeof(header
) + DNSCRYPT_MAC_SIZE
, response
, responseLen
);
484 memcpy(response
+ pos
, &header
, sizeof(header
));
485 pos
+= sizeof(header
);
486 /* setting MAC bytes to 0 */
487 memset(response
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
488 pos
+= DNSCRYPT_MAC_SIZE
;
489 uint16_t toEncryptPos
= pos
;
490 /* skipping response */
493 response
[pos
] = (uint8_t) 0x80;
495 memset(response
+ pos
, 0, paddingSize
- 1);
496 pos
+= (paddingSize
- 1);
499 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
500 int res
= query
->computeSharedKey(query
->useOldCert
? oldPrivateKey
: privateKey
);
505 res
= crypto_box_easy_afternm((unsigned char*) (response
+ sizeof(header
)),
506 (unsigned char*) (response
+ toEncryptPos
),
507 responseLen
+ paddingSize
,
511 int res
= crypto_box_easy((unsigned char*) (response
+ sizeof(header
)),
512 (unsigned char*) (response
+ toEncryptPos
),
513 responseLen
+ paddingSize
,
515 query
->header
.clientPK
,
516 query
->useOldCert
? oldPrivateKey
.key
: privateKey
.key
);
517 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
520 assert(pos
== requiredSize
);
521 *encryptedResponseLen
= requiredSize
;
527 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
529 assert(query
!= NULL
);
530 assert(queryLen
> 0);
531 assert(querySize
>= queryLen
);
532 assert(encryptedResponseLen
!= NULL
);
533 unsigned char nonce
[DNSCRYPT_NONCE_SIZE
];
534 size_t requiredSize
= sizeof(DnsCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
+ queryLen
;
535 /* this is not optimal, we should compute a random padding size, multiple of DNSCRYPT_PADDED_BLOCK_SIZE,
536 DNSCRYPT_PADDED_BLOCK_SIZE <= padding size <= 4096? */
537 uint16_t paddingSize
= DNSCRYPT_PADDED_BLOCK_SIZE
- (queryLen
% DNSCRYPT_PADDED_BLOCK_SIZE
);
538 requiredSize
+= paddingSize
;
540 if (!tcp
&& requiredSize
< DnsCryptQuery::minUDPLength
) {
541 paddingSize
+= (DnsCryptQuery::minUDPLength
- requiredSize
);
542 requiredSize
= DnsCryptQuery::minUDPLength
;
545 if (requiredSize
> querySize
)
548 /* moving the existing query after the header + MAC */
549 memmove(query
+ sizeof(DnsCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
, query
, queryLen
);
553 memcpy(query
+ pos
, cert
.signedData
.clientMagic
, sizeof(cert
.signedData
.clientMagic
));
554 pos
+= sizeof(cert
.signedData
.clientMagic
);
557 memcpy(query
+ pos
, clientPublicKey
, DNSCRYPT_PUBLIC_KEY_SIZE
);
558 pos
+= DNSCRYPT_PUBLIC_KEY_SIZE
;
561 memcpy(query
+ pos
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
562 pos
+= DNSCRYPT_NONCE_SIZE
/ 2;
563 size_t encryptedPos
= pos
;
565 /* clear the MAC bytes */
566 memset(query
+ pos
, 0, DNSCRYPT_MAC_SIZE
);
567 pos
+= DNSCRYPT_MAC_SIZE
;
573 query
[pos
] = (uint8_t) 0x80;
575 memset(query
+ pos
, 0, paddingSize
- 1);
576 pos
+= paddingSize
- 1;
578 memcpy(nonce
, clientNonce
, DNSCRYPT_NONCE_SIZE
/ 2);
579 memset(nonce
+ (DNSCRYPT_NONCE_SIZE
/ 2), 0, DNSCRYPT_NONCE_SIZE
/ 2);
581 int res
= crypto_box_easy((unsigned char*) query
+ encryptedPos
,
582 (unsigned char*) query
+ encryptedPos
+ DNSCRYPT_MAC_SIZE
,
583 queryLen
+ paddingSize
,
585 cert
.signedData
.resolverPK
,
586 clientPrivateKey
.key
);
589 assert(pos
== requiredSize
);
590 *encryptedResponseLen
= requiredSize
;