]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
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. | |
8 | * | |
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. | |
12 | * | |
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. | |
17 | * | |
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. | |
21 | */ | |
11e1e08b RG |
22 | #include "config.h" |
23 | #ifdef HAVE_DNSCRYPT | |
24 | #include <fstream> | |
25 | #include "dolog.hh" | |
26 | #include "dnscrypt.hh" | |
27 | #include "dnswriter.hh" | |
43234e76 | 28 | #include "lock.hh" |
11e1e08b | 29 | |
43234e76 | 30 | DNSCryptPrivateKey::DNSCryptPrivateKey() |
11e1e08b RG |
31 | { |
32 | sodium_memzero(key, sizeof(key)); | |
33 | sodium_mlock(key, sizeof(key)); | |
34 | } | |
35 | ||
43234e76 | 36 | void DNSCryptPrivateKey::loadFromFile(const std::string& keyFile) |
11e1e08b RG |
37 | { |
38 | ifstream file(keyFile); | |
39 | sodium_memzero(key, sizeof(key)); | |
40 | file.read((char*) key, sizeof(key)); | |
41 | ||
42 | if (file.fail()) { | |
43 | sodium_memzero(key, sizeof(key)); | |
44 | file.close(); | |
45 | throw std::runtime_error("Invalid DNSCrypt key file " + keyFile); | |
46 | } | |
47 | ||
48 | file.close(); | |
49 | } | |
50 | ||
43234e76 | 51 | void DNSCryptPrivateKey::saveToFile(const std::string& keyFile) const |
11e1e08b RG |
52 | { |
53 | ofstream file(keyFile); | |
54 | file.write((char*) key, sizeof(key)); | |
55 | file.close(); | |
56 | } | |
57 | ||
43234e76 | 58 | DNSCryptPrivateKey::~DNSCryptPrivateKey() |
11e1e08b | 59 | { |
11e1e08b RG |
60 | sodium_munlock(key, sizeof(key)); |
61 | } | |
62 | ||
43234e76 RG |
63 | DNSCryptExchangeVersion DNSCryptQuery::getVersion() const |
64 | { | |
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"); | |
67 | } | |
68 | ||
69 | return DNSCryptContext::getExchangeVersion(d_pair->cert); | |
70 | } | |
71 | ||
00ffb1c2 | 72 | #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM |
43234e76 | 73 | DNSCryptQuery::~DNSCryptQuery() |
332fdc5f | 74 | { |
43234e76 RG |
75 | if (d_sharedKeyComputed) { |
76 | sodium_munlock(d_sharedKey, sizeof(d_sharedKey)); | |
8089cbe5 | 77 | } |
332fdc5f RG |
78 | } |
79 | ||
43234e76 | 80 | int DNSCryptQuery::computeSharedKey() |
332fdc5f | 81 | { |
43234e76 RG |
82 | assert(d_pair != nullptr); |
83 | ||
8089cbe5 RG |
84 | int res = 0; |
85 | ||
43234e76 | 86 | if (d_sharedKeyComputed) { |
8089cbe5 RG |
87 | return res; |
88 | } | |
89 | ||
43234e76 RG |
90 | const DNSCryptExchangeVersion version = DNSCryptContext::getExchangeVersion(d_pair->cert); |
91 | ||
92 | sodium_mlock(d_sharedKey, sizeof(d_sharedKey)); | |
93 | ||
94 | if (version == DNSCryptExchangeVersion::VERSION1) { | |
95 | res = crypto_box_beforenm(d_sharedKey, | |
96 | d_header.clientPK, | |
97 | d_pair->privateKey.key); | |
98 | } | |
99 | else if (version == DNSCryptExchangeVersion::VERSION2) { | |
100 | #ifdef HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY | |
101 | res = crypto_box_curve25519xchacha20poly1305_beforenm(d_sharedKey, | |
102 | d_header.clientPK, | |
103 | d_pair->privateKey.key); | |
104 | #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
105 | res = -1; | |
106 | #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
107 | } | |
108 | else { | |
109 | res = -1; | |
110 | } | |
8089cbe5 RG |
111 | |
112 | if (res != 0) { | |
43234e76 | 113 | sodium_munlock(d_sharedKey, sizeof(d_sharedKey)); |
8089cbe5 RG |
114 | return res; |
115 | } | |
116 | ||
43234e76 | 117 | d_sharedKeyComputed = true; |
8089cbe5 | 118 | return res; |
332fdc5f | 119 | } |
00ffb1c2 | 120 | #else |
43234e76 | 121 | DNSCryptQuery::~DNSCryptQuery() |
00ffb1c2 RG |
122 | { |
123 | } | |
124 | #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */ | |
8089cbe5 | 125 | |
43234e76 RG |
126 | DNSCryptContext::DNSCryptContext(const std::string& pName, const std::string& certFile, const std::string& keyFile): providerName(pName) |
127 | { | |
128 | pthread_rwlock_init(&d_lock, 0); | |
129 | ||
130 | loadNewCertificate(certFile, keyFile); | |
131 | } | |
132 | ||
133 | DNSCryptContext::DNSCryptContext(const std::string& pName, const DNSCryptCert& certificate, const DNSCryptPrivateKey& pKey): providerName(pName) | |
134 | { | |
135 | pthread_rwlock_init(&d_lock, 0); | |
136 | ||
137 | addNewCertificate(certificate, pKey); | |
138 | } | |
139 | ||
140 | void DNSCryptContext::generateProviderKeys(unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE], unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE]) | |
11e1e08b RG |
141 | { |
142 | int res = crypto_sign_ed25519_keypair(publicKey, privateKey); | |
143 | ||
144 | if (res != 0) { | |
145 | throw std::runtime_error("Error generating DNSCrypt provider keys"); | |
146 | } | |
147 | } | |
148 | ||
43234e76 | 149 | std::string DNSCryptContext::getProviderFingerprint(unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE]) |
11e1e08b RG |
150 | { |
151 | boost::format fmt("%02X%02X"); | |
152 | ostringstream ret; | |
153 | ||
154 | for (size_t idx = 0; idx < DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE; idx += 2) | |
155 | { | |
156 | ret << (fmt % static_cast<int>(publicKey[idx]) % static_cast<int>(publicKey[idx+1])); | |
157 | if (idx < (DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE - 2)) { | |
158 | ret << ":"; | |
159 | } | |
160 | } | |
161 | ||
162 | return ret.str(); | |
163 | } | |
164 | ||
43234e76 RG |
165 | void DNSCryptContext::setExchangeVersion(const DNSCryptExchangeVersion& version, unsigned char esVersion[sizeof(DNSCryptCert::esVersion)]) |
166 | { | |
167 | esVersion[0] = 0x00; | |
168 | ||
169 | if (version == DNSCryptExchangeVersion::VERSION1) { | |
170 | esVersion[1] = { 0x01 }; | |
171 | } | |
172 | else if (version == DNSCryptExchangeVersion::VERSION2) { | |
173 | esVersion[1] = { 0x02 }; | |
174 | } | |
175 | else { | |
176 | throw std::runtime_error("Unknown DNSCrypt exchange version"); | |
177 | } | |
178 | } | |
179 | ||
180 | DNSCryptExchangeVersion DNSCryptContext::getExchangeVersion(const unsigned char esVersion[sizeof(DNSCryptCert::esVersion)]) | |
181 | { | |
182 | if (esVersion[0] != 0x00) { | |
183 | throw std::runtime_error("Unknown DNSCrypt exchange version"); | |
184 | } | |
185 | ||
186 | if (esVersion[1] == 0x01) { | |
187 | return DNSCryptExchangeVersion::VERSION1; | |
188 | } | |
189 | else if (esVersion[1] == 0x02) { | |
190 | return DNSCryptExchangeVersion::VERSION2; | |
191 | } | |
192 | ||
193 | throw std::runtime_error("Unknown DNSCrypt exchange version"); | |
194 | } | |
195 | ||
196 | DNSCryptExchangeVersion DNSCryptContext::getExchangeVersion(const DNSCryptCert& cert) | |
197 | { | |
198 | return getExchangeVersion(cert.esVersion); | |
199 | } | |
200 | ||
201 | ||
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) | |
11e1e08b RG |
203 | { |
204 | unsigned char magic[DNSCRYPT_CERT_MAGIC_SIZE] = DNSCRYPT_CERT_MAGIC_VALUE; | |
11e1e08b RG |
205 | unsigned char protocolMinorVersion[] = DNSCRYPT_CERT_PROTOCOL_MINOR_VERSION_VALUE; |
206 | unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE]; | |
43234e76 RG |
207 | unsigned char esVersion[sizeof(DNSCryptCert::esVersion)]; |
208 | setExchangeVersion(version, esVersion); | |
209 | ||
11e1e08b RG |
210 | generateResolverKeyPair(privateKey, pubK); |
211 | ||
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)); | |
b7bd0317 | 217 | cert.signedData.serial = htonl(serial); |
11e1e08b RG |
218 | cert.signedData.tsStart = htonl((uint32_t) begin); |
219 | cert.signedData.tsEnd = htonl((uint32_t) end); | |
220 | ||
221 | unsigned long long signatureSize = 0; | |
222 | ||
223 | int res = crypto_sign_ed25519(cert.signature, | |
224 | &signatureSize, | |
225 | (unsigned char*) &cert.signedData, | |
226 | sizeof(cert.signedData), | |
227 | providerPrivateKey); | |
228 | ||
229 | if (res == 0) { | |
43234e76 | 230 | assert(signatureSize == sizeof(DNSCryptCertSignedData) + DNSCRYPT_SIGNATURE_SIZE); |
11e1e08b RG |
231 | } |
232 | else { | |
233 | throw std::runtime_error("Error generating DNSCrypt certificate"); | |
234 | } | |
235 | } | |
236 | ||
43234e76 | 237 | void DNSCryptContext::loadCertFromFile(const std::string&filename, DNSCryptCert& dest) |
11e1e08b RG |
238 | { |
239 | ifstream file(filename); | |
240 | file.read((char *) &dest, sizeof(dest)); | |
241 | ||
242 | if (file.fail()) | |
243 | throw std::runtime_error("Invalid dnscrypt certificate file " + filename); | |
244 | ||
245 | file.close(); | |
246 | } | |
247 | ||
43234e76 | 248 | void DNSCryptContext::saveCertFromFile(const DNSCryptCert& cert, const std::string&filename) |
11e1e08b RG |
249 | { |
250 | ofstream file(filename); | |
251 | file.write((char *) &cert, sizeof(cert)); | |
252 | file.close(); | |
253 | } | |
254 | ||
43234e76 | 255 | void DNSCryptContext::generateResolverKeyPair(DNSCryptPrivateKey& privK, unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE]) |
11e1e08b RG |
256 | { |
257 | int res = crypto_box_keypair(pubK, privK.key); | |
258 | ||
259 | if (res != 0) { | |
260 | throw std::runtime_error("Error generating DNSCrypt resolver keys"); | |
261 | } | |
262 | } | |
263 | ||
43234e76 | 264 | void DNSCryptContext::computePublicKeyFromPrivate(const DNSCryptPrivateKey& privK, unsigned char* pubK) |
11e1e08b RG |
265 | { |
266 | int res = crypto_scalarmult_base(pubK, | |
267 | privK.key); | |
268 | ||
269 | if (res != 0) { | |
270 | throw std::runtime_error("Error computing dnscrypt public key from the private one"); | |
271 | } | |
272 | } | |
273 | ||
43234e76 | 274 | std::string DNSCryptContext::certificateDateToStr(uint32_t date) |
11e1e08b RG |
275 | { |
276 | char buf[20]; | |
43234e76 | 277 | time_t tdate = static_cast<time_t>(ntohl(date)); |
11e1e08b RG |
278 | struct tm date_tm; |
279 | ||
280 | localtime_r(&tdate, &date_tm); | |
281 | strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &date_tm); | |
282 | ||
283 | return string(buf); | |
284 | } | |
285 | ||
43234e76 | 286 | void DNSCryptContext::addNewCertificate(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, bool active) |
11e1e08b | 287 | { |
43234e76 RG |
288 | WriteLock w(&d_lock); |
289 | ||
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"); | |
293 | } | |
294 | } | |
295 | ||
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); | |
11e1e08b RG |
302 | } |
303 | ||
43234e76 | 304 | void DNSCryptContext::loadNewCertificate(const std::string& certFile, const std::string& keyFile, bool active) |
11e1e08b | 305 | { |
43234e76 RG |
306 | DNSCryptCert newCert; |
307 | DNSCryptPrivateKey newPrivateKey; | |
11e1e08b RG |
308 | |
309 | loadCertFromFile(certFile, newCert); | |
310 | newPrivateKey.loadFromFile(keyFile); | |
43234e76 RG |
311 | |
312 | addNewCertificate(newCert, newPrivateKey, active); | |
313 | } | |
314 | ||
315 | void DNSCryptContext::markActive(uint32_t serial) | |
316 | { | |
317 | WriteLock w(&d_lock); | |
318 | ||
319 | for (auto pair : certs) { | |
320 | if (pair->active == false && pair->cert.getSerial() == serial) { | |
321 | pair->active = true; | |
322 | return; | |
323 | } | |
324 | } | |
325 | throw std::runtime_error("No inactive certificate found with this serial"); | |
326 | } | |
327 | ||
328 | void DNSCryptContext::markInactive(uint32_t serial) | |
329 | { | |
330 | WriteLock w(&d_lock); | |
331 | ||
332 | for (auto pair : certs) { | |
333 | if (pair->active == true && pair->cert.getSerial() == serial) { | |
334 | pair->active = false; | |
335 | return; | |
336 | } | |
337 | } | |
338 | throw std::runtime_error("No active certificate found with this serial"); | |
11e1e08b RG |
339 | } |
340 | ||
43234e76 | 341 | void DNSCryptContext::removeInactiveCertificate(uint32_t serial) |
11e1e08b | 342 | { |
43234e76 RG |
343 | WriteLock w(&d_lock); |
344 | ||
345 | for (auto it = certs.begin(); it != certs.end(); ) { | |
346 | if ((*it)->active == false && (*it)->cert.getSerial() == serial) { | |
347 | it = certs.erase(it); | |
348 | return; | |
349 | } else { | |
350 | it++; | |
351 | } | |
352 | } | |
353 | throw std::runtime_error("No inactive certificate found with this serial"); | |
354 | } | |
355 | ||
356 | bool DNSCryptQuery::parsePlaintextQuery(const char * packet, uint16_t packetSize) | |
357 | { | |
358 | assert(d_ctx != nullptr); | |
359 | ||
11e1e08b | 360 | if (packetSize < sizeof(dnsheader)) { |
43234e76 | 361 | return false; |
11e1e08b RG |
362 | } |
363 | ||
43234e76 | 364 | const struct dnsheader * dh = reinterpret_cast<const struct dnsheader *>(packet); |
11e1e08b | 365 | if (dh->qr || ntohs(dh->qdcount) != 1 || dh->ancount != 0 || dh->nscount != 0 || dh->opcode != Opcode::Query) |
43234e76 | 366 | return false; |
11e1e08b RG |
367 | |
368 | unsigned int consumed; | |
369 | uint16_t qtype, qclass; | |
370 | DNSName qname(packet, packetSize, sizeof(dnsheader), false, &qtype, &qclass, &consumed); | |
bd910269 | 371 | if ((packetSize - sizeof(dnsheader)) < (consumed + sizeof(qtype) + sizeof(qclass))) |
43234e76 | 372 | return false; |
11e1e08b RG |
373 | |
374 | if (qtype != QType::TXT || qclass != QClass::IN) | |
43234e76 | 375 | return false; |
11e1e08b | 376 | |
43234e76 RG |
377 | if (qname != d_ctx->getProviderName()) |
378 | return false; | |
379 | ||
380 | d_qname = qname; | |
381 | d_id = dh->id; | |
382 | d_valid = true; | |
11e1e08b | 383 | |
43234e76 | 384 | return true; |
11e1e08b RG |
385 | } |
386 | ||
43234e76 | 387 | void DNSCryptContext::getCertificateResponse(time_t now, const DNSName& qname, uint16_t qid, std::vector<uint8_t>& response) |
11e1e08b | 388 | { |
43234e76 | 389 | DNSPacketWriter pw(response, qname, QType::TXT, QClass::IN, Opcode::Query); |
11e1e08b | 390 | struct dnsheader * dh = pw.getHeader(); |
43234e76 | 391 | dh->id = qid; |
11e1e08b RG |
392 | dh->qr = true; |
393 | dh->rcode = RCode::NoError; | |
11e1e08b | 394 | |
43234e76 RG |
395 | ReadLock r(&d_lock); |
396 | for (const auto pair : certs) { | |
397 | if (!pair->active || !pair->cert.isValid(now)) { | |
398 | continue; | |
399 | } | |
11e1e08b | 400 | |
43234e76 RG |
401 | pw.startRecord(qname, QType::TXT, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL), QClass::IN, DNSResourceRecord::ANSWER, true); |
402 | std::string scert; | |
403 | uint8_t certSize = sizeof(pair->cert); | |
404 | scert.assign((const char*) &certSize, sizeof(certSize)); | |
405 | scert.append((const char*) &pair->cert, certSize); | |
11e1e08b | 406 | |
43234e76 RG |
407 | pw.xfrBlob(scert); |
408 | pw.commit(); | |
11e1e08b | 409 | } |
43234e76 | 410 | } |
11e1e08b | 411 | |
43234e76 RG |
412 | bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery& query, time_t now) |
413 | { | |
414 | const unsigned char* magic = query.getClientMagic(); | |
415 | ||
416 | ReadLock r(&d_lock); | |
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); | |
420 | return true; | |
421 | } | |
11e1e08b RG |
422 | } |
423 | ||
424 | return false; | |
425 | } | |
426 | ||
43234e76 | 427 | bool DNSCryptQuery::isEncryptedQuery(const char * packet, uint16_t packetSize, bool tcp, time_t now) |
11e1e08b | 428 | { |
43234e76 | 429 | assert(d_ctx != nullptr); |
11e1e08b | 430 | |
43234e76 RG |
431 | d_encrypted = false; |
432 | ||
433 | if (packetSize < sizeof(DNSCryptQueryHeader)) { | |
434 | return false; | |
11e1e08b RG |
435 | } |
436 | ||
43234e76 RG |
437 | if (!tcp && packetSize < DNSCryptQuery::s_minUDPLength) { |
438 | return false; | |
11e1e08b RG |
439 | } |
440 | ||
43234e76 | 441 | const struct DNSCryptQueryHeader* header = reinterpret_cast<const struct DNSCryptQueryHeader*>(packet); |
11e1e08b | 442 | |
43234e76 | 443 | d_header = *header; |
11e1e08b | 444 | |
43234e76 RG |
445 | if (!d_ctx->magicMatchesAPublicKey(*this, now)) { |
446 | return false; | |
11e1e08b RG |
447 | } |
448 | ||
43234e76 RG |
449 | d_encrypted = true; |
450 | ||
451 | return true; | |
11e1e08b RG |
452 | } |
453 | ||
43234e76 | 454 | void DNSCryptQuery::getDecrypted(bool tcp, char* packet, uint16_t packetSize, uint16_t* decryptedQueryLen) |
11e1e08b | 455 | { |
43234e76 RG |
456 | assert(decryptedQueryLen != nullptr); |
457 | assert(d_encrypted); | |
458 | assert(d_pair != nullptr); | |
459 | assert(d_valid == false); | |
11e1e08b RG |
460 | |
461 | #ifdef DNSCRYPT_STRICT_PADDING_LENGTH | |
43234e76 RG |
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); | |
11e1e08b RG |
464 | return; |
465 | } | |
466 | #endif | |
467 | ||
468 | unsigned char nonce[DNSCRYPT_NONCE_SIZE]; | |
43234e76 RG |
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"); | |
11e1e08b | 472 | |
43234e76 RG |
473 | memcpy(nonce, &d_header.clientNonce, sizeof(d_header.clientNonce)); |
474 | memset(nonce + sizeof(d_header.clientNonce), 0, sizeof(nonce) - sizeof(d_header.clientNonce)); | |
11e1e08b | 475 | |
00ffb1c2 | 476 | #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM |
43234e76 | 477 | int res = computeSharedKey(); |
8089cbe5 RG |
478 | if (res != 0) { |
479 | vinfolog("Dropping encrypted query we can't compute the shared key for"); | |
480 | return; | |
332fdc5f RG |
481 | } |
482 | ||
43234e76 RG |
483 | const DNSCryptExchangeVersion version = getVersion(); |
484 | ||
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), | |
489 | nonce, | |
490 | d_sharedKey); | |
491 | } | |
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), | |
497 | nonce, | |
498 | d_sharedKey); | |
499 | #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
500 | res = -1; | |
501 | #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
502 | } else { | |
503 | res = -1; | |
504 | } | |
505 | ||
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), | |
00ffb1c2 | 510 | nonce, |
43234e76 RG |
511 | d_header.clientPK, |
512 | d_pair->privateKey.key); | |
00ffb1c2 | 513 | #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */ |
11e1e08b RG |
514 | |
515 | if (res != 0) { | |
516 | vinfolog("Dropping encrypted query we can't decrypt"); | |
517 | return; | |
518 | } | |
519 | ||
43234e76 | 520 | *decryptedQueryLen = packetSize - sizeof(DNSCryptQueryHeader) - DNSCRYPT_MAC_SIZE; |
11e1e08b RG |
521 | uint16_t pos = *decryptedQueryLen; |
522 | assert(pos < packetSize); | |
43234e76 | 523 | d_paddedLen = *decryptedQueryLen; |
11e1e08b RG |
524 | |
525 | while(pos > 0 && packet[pos - 1] == 0) pos--; | |
526 | ||
43234e76 | 527 | if (pos == 0 || static_cast<uint8_t>(packet[pos - 1]) != 0x80) { |
11e1e08b RG |
528 | vinfolog("Dropping encrypted query with invalid padding value"); |
529 | return; | |
530 | } | |
531 | ||
532 | pos--; | |
533 | ||
534 | size_t paddingLen = *decryptedQueryLen - pos; | |
535 | *decryptedQueryLen = pos; | |
536 | ||
537 | if (tcp && paddingLen > DNSCRYPT_MAX_TCP_PADDING_SIZE) { | |
538 | vinfolog("Dropping encrypted query with too long padding size"); | |
539 | return; | |
540 | } | |
541 | ||
43234e76 RG |
542 | d_len = pos; |
543 | d_valid = true; | |
544 | } | |
11e1e08b | 545 | |
43234e76 RG |
546 | void DNSCryptQuery::getCertificateResponse(time_t now, std::vector<uint8_t>& response) const |
547 | { | |
548 | assert(d_ctx != nullptr); | |
549 | d_ctx->getCertificateResponse(now, d_qname, d_id, response); | |
11e1e08b RG |
550 | } |
551 | ||
43234e76 | 552 | void DNSCryptQuery::parsePacket(char* packet, uint16_t packetSize, bool tcp, uint16_t* decryptedQueryLen, time_t now) |
11e1e08b | 553 | { |
43234e76 RG |
554 | assert(packet != nullptr); |
555 | assert(decryptedQueryLen != nullptr); | |
11e1e08b | 556 | |
43234e76 | 557 | d_valid = false; |
11e1e08b RG |
558 | |
559 | /* might be a plaintext certificate request or an authenticated request */ | |
43234e76 RG |
560 | if (isEncryptedQuery(packet, packetSize, tcp, now)) { |
561 | getDecrypted(tcp, packet, packetSize, decryptedQueryLen); | |
11e1e08b RG |
562 | } |
563 | else { | |
43234e76 | 564 | parsePlaintextQuery(packet, packetSize); |
11e1e08b RG |
565 | } |
566 | } | |
567 | ||
43234e76 | 568 | void DNSCryptQuery::fillServerNonce(unsigned char* nonce) const |
11e1e08b | 569 | { |
43234e76 | 570 | uint32_t* dest = reinterpret_cast<uint32_t*>(nonce); |
11e1e08b RG |
571 | static const size_t nonceSize = DNSCRYPT_NONCE_SIZE / 2; |
572 | ||
573 | for (size_t pos = 0; pos < (nonceSize / sizeof(*dest)); pos++) | |
574 | { | |
575 | const uint32_t value = randombytes_random(); | |
576 | memcpy(dest + pos, &value, sizeof(value)); | |
577 | } | |
578 | } | |
579 | ||
580 | /* | |
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." | |
583 | */ | |
43234e76 | 584 | uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen, size_t maxLen) const |
11e1e08b | 585 | { |
43234e76 | 586 | size_t paddedSize = 0; |
11e1e08b RG |
587 | uint16_t result = 0; |
588 | uint32_t rnd = 0; | |
43234e76 RG |
589 | assert(d_header.clientNonce); |
590 | assert(d_pair != nullptr); | |
591 | ||
11e1e08b | 592 | unsigned char nonce[DNSCRYPT_NONCE_SIZE]; |
43234e76 RG |
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); | |
11e1e08b | 596 | |
43234e76 RG |
597 | paddedSize = unpaddedLen + rnd % (maxLen - unpaddedLen + 1); |
598 | paddedSize += DNSCRYPT_PADDED_BLOCK_SIZE - (paddedSize % DNSCRYPT_PADDED_BLOCK_SIZE); | |
11e1e08b | 599 | |
43234e76 RG |
600 | if (paddedSize > maxLen) |
601 | paddedSize = maxLen; | |
11e1e08b | 602 | |
43234e76 | 603 | result = paddedSize - unpaddedLen; |
11e1e08b RG |
604 | |
605 | return result; | |
606 | } | |
607 | ||
43234e76 | 608 | int DNSCryptQuery::encryptResponse(char* response, uint16_t responseLen, uint16_t responseSize, bool tcp, uint16_t* encryptedResponseLen) |
11e1e08b | 609 | { |
43234e76 RG |
610 | struct DNSCryptResponseHeader responseHeader; |
611 | assert(response != nullptr); | |
11e1e08b RG |
612 | assert(responseLen > 0); |
613 | assert(responseSize >= responseLen); | |
43234e76 RG |
614 | assert(encryptedResponseLen != nullptr); |
615 | assert(d_encrypted == true); | |
616 | assert(d_pair != nullptr); | |
11e1e08b | 617 | |
43234e76 RG |
618 | if (!tcp && d_paddedLen < responseLen) { |
619 | struct dnsheader* dh = reinterpret_cast<struct dnsheader*>(response); | |
bd64cc44 RG |
620 | size_t questionSize = 0; |
621 | ||
622 | if (responseLen > sizeof(dnsheader)) { | |
623 | unsigned int consumed = 0; | |
43234e76 | 624 | DNSName tempQName(response, responseLen, sizeof(dnsheader), false, 0, 0, &consumed); |
bd64cc44 RG |
625 | if (consumed > 0) { |
626 | questionSize = consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE; | |
627 | } | |
628 | } | |
629 | ||
630 | responseLen = sizeof(dnsheader) + questionSize; | |
631 | ||
43234e76 RG |
632 | if (responseLen > d_paddedLen) { |
633 | responseLen = d_paddedLen; | |
bd64cc44 RG |
634 | } |
635 | dh->ancount = dh->arcount = dh->nscount = 0; | |
11e1e08b RG |
636 | dh->tc = 1; |
637 | } | |
638 | ||
43234e76 | 639 | size_t requiredSize = sizeof(responseHeader) + DNSCRYPT_MAC_SIZE + responseLen; |
11e1e08b | 640 | size_t maxSize = (responseSize > (requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE)) ? (requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE) : responseSize; |
43234e76 | 641 | uint16_t paddingSize = computePaddingSize(requiredSize, maxSize); |
11e1e08b RG |
642 | requiredSize += paddingSize; |
643 | ||
644 | if (requiredSize > responseSize) | |
645 | return ENOBUFS; | |
646 | ||
43234e76 RG |
647 | memcpy(&responseHeader.nonce, &d_header.clientNonce, sizeof d_header.clientNonce); |
648 | fillServerNonce(&(responseHeader.nonce[sizeof(d_header.clientNonce)])); | |
11e1e08b RG |
649 | |
650 | /* moving the existing response after the header + MAC */ | |
43234e76 | 651 | memmove(response + sizeof(responseHeader) + DNSCRYPT_MAC_SIZE, response, responseLen); |
11e1e08b RG |
652 | |
653 | uint16_t pos = 0; | |
654 | /* copying header */ | |
43234e76 RG |
655 | memcpy(response + pos, &responseHeader, sizeof(responseHeader)); |
656 | pos += sizeof(responseHeader); | |
11e1e08b RG |
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 */ | |
662 | pos += responseLen; | |
663 | /* padding */ | |
43234e76 | 664 | response[pos] = static_cast<uint8_t>(0x80); |
11e1e08b RG |
665 | pos++; |
666 | memset(response + pos, 0, paddingSize - 1); | |
667 | pos += (paddingSize - 1); | |
332fdc5f | 668 | |
11e1e08b | 669 | /* encrypting */ |
00ffb1c2 | 670 | #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM |
43234e76 | 671 | int res = computeSharedKey(); |
8089cbe5 RG |
672 | if (res != 0) { |
673 | return res; | |
332fdc5f RG |
674 | } |
675 | ||
43234e76 RG |
676 | const DNSCryptExchangeVersion version = getVersion(); |
677 | ||
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, | |
683 | d_sharedKey); | |
684 | } | |
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, | |
691 | d_sharedKey); | |
692 | #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
693 | res = -1; | |
694 | #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
695 | } | |
696 | else { | |
697 | res = -1; | |
698 | } | |
00ffb1c2 | 699 | #else |
43234e76 RG |
700 | int res = crypto_box_easy(reinterpret_cast<unsigned char*>(response + sizeof(responseHeader)), |
701 | reinterpret_cast<unsigned char*>(response + toEncryptPos), | |
00ffb1c2 | 702 | responseLen + paddingSize, |
43234e76 RG |
703 | responseHeader.nonce, |
704 | d_header.clientPK, | |
705 | d_pair->privateKey.key); | |
00ffb1c2 | 706 | #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */ |
11e1e08b RG |
707 | |
708 | if (res == 0) { | |
709 | assert(pos == requiredSize); | |
710 | *encryptedResponseLen = requiredSize; | |
711 | } | |
712 | ||
713 | return res; | |
714 | } | |
715 | ||
43234e76 | 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 |
11e1e08b | 717 | { |
43234e76 | 718 | assert(query != nullptr); |
11e1e08b RG |
719 | assert(queryLen > 0); |
720 | assert(querySize >= queryLen); | |
43234e76 RG |
721 | assert(encryptedResponseLen != nullptr); |
722 | assert(cert != nullptr); | |
723 | ||
11e1e08b | 724 | unsigned char nonce[DNSCRYPT_NONCE_SIZE]; |
43234e76 | 725 | size_t requiredSize = sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE + queryLen; |
11e1e08b RG |
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; | |
730 | ||
43234e76 RG |
731 | if (!tcp && requiredSize < DNSCryptQuery::s_minUDPLength) { |
732 | paddingSize += (DNSCryptQuery::s_minUDPLength - requiredSize); | |
733 | requiredSize = DNSCryptQuery::s_minUDPLength; | |
11e1e08b RG |
734 | } |
735 | ||
736 | if (requiredSize > querySize) | |
737 | return ENOBUFS; | |
738 | ||
739 | /* moving the existing query after the header + MAC */ | |
43234e76 | 740 | memmove(query + sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE, query, queryLen); |
11e1e08b RG |
741 | |
742 | size_t pos = 0; | |
743 | /* client magic */ | |
43234e76 RG |
744 | memcpy(query + pos, cert->signedData.clientMagic, sizeof(cert->signedData.clientMagic)); |
745 | pos += sizeof(cert->signedData.clientMagic); | |
11e1e08b RG |
746 | |
747 | /* client PK */ | |
748 | memcpy(query + pos, clientPublicKey, DNSCRYPT_PUBLIC_KEY_SIZE); | |
749 | pos += DNSCRYPT_PUBLIC_KEY_SIZE; | |
750 | ||
751 | /* client nonce */ | |
752 | memcpy(query + pos, clientNonce, DNSCRYPT_NONCE_SIZE / 2); | |
753 | pos += DNSCRYPT_NONCE_SIZE / 2; | |
754 | size_t encryptedPos = pos; | |
755 | ||
756 | /* clear the MAC bytes */ | |
757 | memset(query + pos, 0, DNSCRYPT_MAC_SIZE); | |
758 | pos += DNSCRYPT_MAC_SIZE; | |
759 | ||
760 | /* skipping data */ | |
761 | pos += queryLen; | |
762 | ||
763 | /* padding */ | |
43234e76 | 764 | query[pos] = static_cast<uint8_t>(0x80); |
11e1e08b RG |
765 | pos++; |
766 | memset(query + pos, 0, paddingSize - 1); | |
767 | pos += paddingSize - 1; | |
768 | ||
769 | memcpy(nonce, clientNonce, DNSCRYPT_NONCE_SIZE / 2); | |
770 | memset(nonce + (DNSCRYPT_NONCE_SIZE / 2), 0, DNSCRYPT_NONCE_SIZE / 2); | |
771 | ||
43234e76 RG |
772 | const DNSCryptExchangeVersion version = getExchangeVersion(*cert); |
773 | int res = -1; | |
774 | ||
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, | |
779 | nonce, | |
780 | cert->signedData.resolverPK, | |
781 | clientPrivateKey.key); | |
782 | } | |
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, | |
788 | nonce, | |
789 | cert->signedData.resolverPK, | |
790 | clientPrivateKey.key); | |
791 | #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
792 | } | |
793 | else { | |
794 | throw std::runtime_error("Unknown DNSCrypt exchange version"); | |
795 | } | |
11e1e08b RG |
796 | |
797 | if (res == 0) { | |
798 | assert(pos == requiredSize); | |
799 | *encryptedResponseLen = requiredSize; | |
800 | } | |
801 | ||
802 | return res; | |
803 | } | |
804 | ||
43234e76 | 805 | bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DNSCryptExchangeVersion version, DNSCryptCert& certOut, DNSCryptPrivateKey& keyOut) |
6bb38cd6 RG |
806 | { |
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)); | |
811 | ||
812 | try { | |
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); | |
818 | } | |
819 | ||
43234e76 | 820 | DNSCryptContext::generateCertificate(serial, begin, end, version, providerPrivateKey, keyOut, certOut); |
6bb38cd6 RG |
821 | success = true; |
822 | } | |
823 | catch(const std::exception& e) { | |
824 | errlog(e.what()); | |
825 | } | |
826 | ||
827 | sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey)); | |
828 | sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey)); | |
829 | return success; | |
830 | } | |
831 | ||
11e1e08b | 832 | #endif |