]>
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 | ||
bcc62bfb | 286 | void DNSCryptContext::addNewCertificate(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, bool active, bool reload) |
11e1e08b | 287 | { |
43234e76 RG |
288 | WriteLock w(&d_lock); |
289 | ||
290 | for (auto pair : certs) { | |
291 | if (pair->cert.getSerial() == newCert.getSerial()) { | |
bcc62bfb RG |
292 | if (reload) { |
293 | /* on reload we just assume that this is the same certificate */ | |
294 | return; | |
295 | } | |
296 | else { | |
297 | throw std::runtime_error("Error adding a new certificate: we already have a certificate with the same serial"); | |
298 | } | |
43234e76 RG |
299 | } |
300 | } | |
301 | ||
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); | |
11e1e08b RG |
308 | } |
309 | ||
bcc62bfb | 310 | void DNSCryptContext::loadNewCertificate(const std::string& certFile, const std::string& keyFile, bool active, bool reload) |
11e1e08b | 311 | { |
43234e76 RG |
312 | DNSCryptCert newCert; |
313 | DNSCryptPrivateKey newPrivateKey; | |
11e1e08b RG |
314 | |
315 | loadCertFromFile(certFile, newCert); | |
316 | newPrivateKey.loadFromFile(keyFile); | |
43234e76 | 317 | |
bcc62bfb RG |
318 | addNewCertificate(newCert, newPrivateKey, active, reload); |
319 | certificatePath = certFile; | |
320 | keyPath = keyFile; | |
321 | } | |
322 | ||
323 | void DNSCryptContext::reloadCertificate() | |
324 | { | |
325 | loadNewCertificate(certificatePath, keyPath, true, true); | |
43234e76 RG |
326 | } |
327 | ||
328 | void DNSCryptContext::markActive(uint32_t serial) | |
329 | { | |
330 | WriteLock w(&d_lock); | |
331 | ||
332 | for (auto pair : certs) { | |
333 | if (pair->active == false && pair->cert.getSerial() == serial) { | |
334 | pair->active = true; | |
335 | return; | |
336 | } | |
337 | } | |
338 | throw std::runtime_error("No inactive certificate found with this serial"); | |
339 | } | |
340 | ||
341 | void DNSCryptContext::markInactive(uint32_t serial) | |
342 | { | |
343 | WriteLock w(&d_lock); | |
344 | ||
345 | for (auto pair : certs) { | |
346 | if (pair->active == true && pair->cert.getSerial() == serial) { | |
347 | pair->active = false; | |
348 | return; | |
349 | } | |
350 | } | |
351 | throw std::runtime_error("No active certificate found with this serial"); | |
11e1e08b RG |
352 | } |
353 | ||
43234e76 | 354 | void DNSCryptContext::removeInactiveCertificate(uint32_t serial) |
11e1e08b | 355 | { |
43234e76 RG |
356 | WriteLock w(&d_lock); |
357 | ||
358 | for (auto it = certs.begin(); it != certs.end(); ) { | |
359 | if ((*it)->active == false && (*it)->cert.getSerial() == serial) { | |
360 | it = certs.erase(it); | |
361 | return; | |
362 | } else { | |
363 | it++; | |
364 | } | |
365 | } | |
366 | throw std::runtime_error("No inactive certificate found with this serial"); | |
367 | } | |
368 | ||
369 | bool DNSCryptQuery::parsePlaintextQuery(const char * packet, uint16_t packetSize) | |
370 | { | |
371 | assert(d_ctx != nullptr); | |
372 | ||
11e1e08b | 373 | if (packetSize < sizeof(dnsheader)) { |
43234e76 | 374 | return false; |
11e1e08b RG |
375 | } |
376 | ||
43234e76 | 377 | const struct dnsheader * dh = reinterpret_cast<const struct dnsheader *>(packet); |
11e1e08b | 378 | if (dh->qr || ntohs(dh->qdcount) != 1 || dh->ancount != 0 || dh->nscount != 0 || dh->opcode != Opcode::Query) |
43234e76 | 379 | return false; |
11e1e08b RG |
380 | |
381 | unsigned int consumed; | |
382 | uint16_t qtype, qclass; | |
383 | DNSName qname(packet, packetSize, sizeof(dnsheader), false, &qtype, &qclass, &consumed); | |
bd910269 | 384 | if ((packetSize - sizeof(dnsheader)) < (consumed + sizeof(qtype) + sizeof(qclass))) |
43234e76 | 385 | return false; |
11e1e08b RG |
386 | |
387 | if (qtype != QType::TXT || qclass != QClass::IN) | |
43234e76 | 388 | return false; |
11e1e08b | 389 | |
43234e76 RG |
390 | if (qname != d_ctx->getProviderName()) |
391 | return false; | |
392 | ||
393 | d_qname = qname; | |
394 | d_id = dh->id; | |
395 | d_valid = true; | |
11e1e08b | 396 | |
43234e76 | 397 | return true; |
11e1e08b RG |
398 | } |
399 | ||
43234e76 | 400 | void DNSCryptContext::getCertificateResponse(time_t now, const DNSName& qname, uint16_t qid, std::vector<uint8_t>& response) |
11e1e08b | 401 | { |
43234e76 | 402 | DNSPacketWriter pw(response, qname, QType::TXT, QClass::IN, Opcode::Query); |
11e1e08b | 403 | struct dnsheader * dh = pw.getHeader(); |
43234e76 | 404 | dh->id = qid; |
11e1e08b RG |
405 | dh->qr = true; |
406 | dh->rcode = RCode::NoError; | |
11e1e08b | 407 | |
43234e76 RG |
408 | ReadLock r(&d_lock); |
409 | for (const auto pair : certs) { | |
410 | if (!pair->active || !pair->cert.isValid(now)) { | |
411 | continue; | |
412 | } | |
11e1e08b | 413 | |
43234e76 RG |
414 | pw.startRecord(qname, QType::TXT, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL), QClass::IN, DNSResourceRecord::ANSWER, true); |
415 | std::string scert; | |
416 | uint8_t certSize = sizeof(pair->cert); | |
417 | scert.assign((const char*) &certSize, sizeof(certSize)); | |
418 | scert.append((const char*) &pair->cert, certSize); | |
11e1e08b | 419 | |
43234e76 RG |
420 | pw.xfrBlob(scert); |
421 | pw.commit(); | |
11e1e08b | 422 | } |
43234e76 | 423 | } |
11e1e08b | 424 | |
43234e76 RG |
425 | bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery& query, time_t now) |
426 | { | |
427 | const unsigned char* magic = query.getClientMagic(); | |
428 | ||
429 | ReadLock r(&d_lock); | |
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); | |
433 | return true; | |
434 | } | |
11e1e08b RG |
435 | } |
436 | ||
437 | return false; | |
438 | } | |
439 | ||
43234e76 | 440 | bool DNSCryptQuery::isEncryptedQuery(const char * packet, uint16_t packetSize, bool tcp, time_t now) |
11e1e08b | 441 | { |
43234e76 | 442 | assert(d_ctx != nullptr); |
11e1e08b | 443 | |
43234e76 RG |
444 | d_encrypted = false; |
445 | ||
446 | if (packetSize < sizeof(DNSCryptQueryHeader)) { | |
447 | return false; | |
11e1e08b RG |
448 | } |
449 | ||
43234e76 RG |
450 | if (!tcp && packetSize < DNSCryptQuery::s_minUDPLength) { |
451 | return false; | |
11e1e08b RG |
452 | } |
453 | ||
43234e76 | 454 | const struct DNSCryptQueryHeader* header = reinterpret_cast<const struct DNSCryptQueryHeader*>(packet); |
11e1e08b | 455 | |
43234e76 | 456 | d_header = *header; |
11e1e08b | 457 | |
43234e76 RG |
458 | if (!d_ctx->magicMatchesAPublicKey(*this, now)) { |
459 | return false; | |
11e1e08b RG |
460 | } |
461 | ||
43234e76 RG |
462 | d_encrypted = true; |
463 | ||
464 | return true; | |
11e1e08b RG |
465 | } |
466 | ||
43234e76 | 467 | void DNSCryptQuery::getDecrypted(bool tcp, char* packet, uint16_t packetSize, uint16_t* decryptedQueryLen) |
11e1e08b | 468 | { |
43234e76 RG |
469 | assert(decryptedQueryLen != nullptr); |
470 | assert(d_encrypted); | |
471 | assert(d_pair != nullptr); | |
472 | assert(d_valid == false); | |
11e1e08b RG |
473 | |
474 | #ifdef DNSCRYPT_STRICT_PADDING_LENGTH | |
43234e76 RG |
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); | |
11e1e08b RG |
477 | return; |
478 | } | |
479 | #endif | |
480 | ||
481 | unsigned char nonce[DNSCRYPT_NONCE_SIZE]; | |
43234e76 RG |
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"); | |
11e1e08b | 485 | |
43234e76 RG |
486 | memcpy(nonce, &d_header.clientNonce, sizeof(d_header.clientNonce)); |
487 | memset(nonce + sizeof(d_header.clientNonce), 0, sizeof(nonce) - sizeof(d_header.clientNonce)); | |
11e1e08b | 488 | |
00ffb1c2 | 489 | #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM |
43234e76 | 490 | int res = computeSharedKey(); |
8089cbe5 RG |
491 | if (res != 0) { |
492 | vinfolog("Dropping encrypted query we can't compute the shared key for"); | |
493 | return; | |
332fdc5f RG |
494 | } |
495 | ||
43234e76 RG |
496 | const DNSCryptExchangeVersion version = getVersion(); |
497 | ||
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), | |
502 | nonce, | |
503 | d_sharedKey); | |
504 | } | |
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), | |
510 | nonce, | |
511 | d_sharedKey); | |
512 | #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
513 | res = -1; | |
514 | #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
515 | } else { | |
516 | res = -1; | |
517 | } | |
518 | ||
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), | |
00ffb1c2 | 523 | nonce, |
43234e76 RG |
524 | d_header.clientPK, |
525 | d_pair->privateKey.key); | |
00ffb1c2 | 526 | #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */ |
11e1e08b RG |
527 | |
528 | if (res != 0) { | |
529 | vinfolog("Dropping encrypted query we can't decrypt"); | |
530 | return; | |
531 | } | |
532 | ||
43234e76 | 533 | *decryptedQueryLen = packetSize - sizeof(DNSCryptQueryHeader) - DNSCRYPT_MAC_SIZE; |
11e1e08b RG |
534 | uint16_t pos = *decryptedQueryLen; |
535 | assert(pos < packetSize); | |
43234e76 | 536 | d_paddedLen = *decryptedQueryLen; |
11e1e08b RG |
537 | |
538 | while(pos > 0 && packet[pos - 1] == 0) pos--; | |
539 | ||
43234e76 | 540 | if (pos == 0 || static_cast<uint8_t>(packet[pos - 1]) != 0x80) { |
11e1e08b RG |
541 | vinfolog("Dropping encrypted query with invalid padding value"); |
542 | return; | |
543 | } | |
544 | ||
545 | pos--; | |
546 | ||
547 | size_t paddingLen = *decryptedQueryLen - pos; | |
548 | *decryptedQueryLen = pos; | |
549 | ||
550 | if (tcp && paddingLen > DNSCRYPT_MAX_TCP_PADDING_SIZE) { | |
551 | vinfolog("Dropping encrypted query with too long padding size"); | |
552 | return; | |
553 | } | |
554 | ||
43234e76 RG |
555 | d_len = pos; |
556 | d_valid = true; | |
557 | } | |
11e1e08b | 558 | |
43234e76 RG |
559 | void DNSCryptQuery::getCertificateResponse(time_t now, std::vector<uint8_t>& response) const |
560 | { | |
561 | assert(d_ctx != nullptr); | |
562 | d_ctx->getCertificateResponse(now, d_qname, d_id, response); | |
11e1e08b RG |
563 | } |
564 | ||
43234e76 | 565 | void DNSCryptQuery::parsePacket(char* packet, uint16_t packetSize, bool tcp, uint16_t* decryptedQueryLen, time_t now) |
11e1e08b | 566 | { |
43234e76 RG |
567 | assert(packet != nullptr); |
568 | assert(decryptedQueryLen != nullptr); | |
11e1e08b | 569 | |
43234e76 | 570 | d_valid = false; |
11e1e08b RG |
571 | |
572 | /* might be a plaintext certificate request or an authenticated request */ | |
43234e76 RG |
573 | if (isEncryptedQuery(packet, packetSize, tcp, now)) { |
574 | getDecrypted(tcp, packet, packetSize, decryptedQueryLen); | |
11e1e08b RG |
575 | } |
576 | else { | |
43234e76 | 577 | parsePlaintextQuery(packet, packetSize); |
11e1e08b RG |
578 | } |
579 | } | |
580 | ||
43234e76 | 581 | void DNSCryptQuery::fillServerNonce(unsigned char* nonce) const |
11e1e08b | 582 | { |
43234e76 | 583 | uint32_t* dest = reinterpret_cast<uint32_t*>(nonce); |
11e1e08b RG |
584 | static const size_t nonceSize = DNSCRYPT_NONCE_SIZE / 2; |
585 | ||
586 | for (size_t pos = 0; pos < (nonceSize / sizeof(*dest)); pos++) | |
587 | { | |
588 | const uint32_t value = randombytes_random(); | |
589 | memcpy(dest + pos, &value, sizeof(value)); | |
590 | } | |
591 | } | |
592 | ||
593 | /* | |
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." | |
596 | */ | |
43234e76 | 597 | uint16_t DNSCryptQuery::computePaddingSize(uint16_t unpaddedLen, size_t maxLen) const |
11e1e08b | 598 | { |
43234e76 | 599 | size_t paddedSize = 0; |
11e1e08b RG |
600 | uint16_t result = 0; |
601 | uint32_t rnd = 0; | |
43234e76 RG |
602 | assert(d_header.clientNonce); |
603 | assert(d_pair != nullptr); | |
604 | ||
11e1e08b | 605 | unsigned char nonce[DNSCRYPT_NONCE_SIZE]; |
43234e76 RG |
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); | |
11e1e08b | 609 | |
43234e76 RG |
610 | paddedSize = unpaddedLen + rnd % (maxLen - unpaddedLen + 1); |
611 | paddedSize += DNSCRYPT_PADDED_BLOCK_SIZE - (paddedSize % DNSCRYPT_PADDED_BLOCK_SIZE); | |
11e1e08b | 612 | |
43234e76 RG |
613 | if (paddedSize > maxLen) |
614 | paddedSize = maxLen; | |
11e1e08b | 615 | |
43234e76 | 616 | result = paddedSize - unpaddedLen; |
11e1e08b RG |
617 | |
618 | return result; | |
619 | } | |
620 | ||
43234e76 | 621 | int DNSCryptQuery::encryptResponse(char* response, uint16_t responseLen, uint16_t responseSize, bool tcp, uint16_t* encryptedResponseLen) |
11e1e08b | 622 | { |
43234e76 RG |
623 | struct DNSCryptResponseHeader responseHeader; |
624 | assert(response != nullptr); | |
11e1e08b RG |
625 | assert(responseLen > 0); |
626 | assert(responseSize >= responseLen); | |
43234e76 RG |
627 | assert(encryptedResponseLen != nullptr); |
628 | assert(d_encrypted == true); | |
629 | assert(d_pair != nullptr); | |
11e1e08b | 630 | |
43234e76 RG |
631 | if (!tcp && d_paddedLen < responseLen) { |
632 | struct dnsheader* dh = reinterpret_cast<struct dnsheader*>(response); | |
bd64cc44 RG |
633 | size_t questionSize = 0; |
634 | ||
635 | if (responseLen > sizeof(dnsheader)) { | |
636 | unsigned int consumed = 0; | |
43234e76 | 637 | DNSName tempQName(response, responseLen, sizeof(dnsheader), false, 0, 0, &consumed); |
bd64cc44 RG |
638 | if (consumed > 0) { |
639 | questionSize = consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE; | |
640 | } | |
641 | } | |
642 | ||
643 | responseLen = sizeof(dnsheader) + questionSize; | |
644 | ||
43234e76 RG |
645 | if (responseLen > d_paddedLen) { |
646 | responseLen = d_paddedLen; | |
bd64cc44 RG |
647 | } |
648 | dh->ancount = dh->arcount = dh->nscount = 0; | |
11e1e08b RG |
649 | dh->tc = 1; |
650 | } | |
651 | ||
43234e76 | 652 | size_t requiredSize = sizeof(responseHeader) + DNSCRYPT_MAC_SIZE + responseLen; |
11e1e08b | 653 | size_t maxSize = (responseSize > (requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE)) ? (requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE) : responseSize; |
43234e76 | 654 | uint16_t paddingSize = computePaddingSize(requiredSize, maxSize); |
11e1e08b RG |
655 | requiredSize += paddingSize; |
656 | ||
657 | if (requiredSize > responseSize) | |
658 | return ENOBUFS; | |
659 | ||
43234e76 RG |
660 | memcpy(&responseHeader.nonce, &d_header.clientNonce, sizeof d_header.clientNonce); |
661 | fillServerNonce(&(responseHeader.nonce[sizeof(d_header.clientNonce)])); | |
11e1e08b RG |
662 | |
663 | /* moving the existing response after the header + MAC */ | |
43234e76 | 664 | memmove(response + sizeof(responseHeader) + DNSCRYPT_MAC_SIZE, response, responseLen); |
11e1e08b RG |
665 | |
666 | uint16_t pos = 0; | |
667 | /* copying header */ | |
43234e76 RG |
668 | memcpy(response + pos, &responseHeader, sizeof(responseHeader)); |
669 | pos += sizeof(responseHeader); | |
11e1e08b RG |
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 */ | |
675 | pos += responseLen; | |
676 | /* padding */ | |
43234e76 | 677 | response[pos] = static_cast<uint8_t>(0x80); |
11e1e08b RG |
678 | pos++; |
679 | memset(response + pos, 0, paddingSize - 1); | |
680 | pos += (paddingSize - 1); | |
332fdc5f | 681 | |
11e1e08b | 682 | /* encrypting */ |
00ffb1c2 | 683 | #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM |
43234e76 | 684 | int res = computeSharedKey(); |
8089cbe5 RG |
685 | if (res != 0) { |
686 | return res; | |
332fdc5f RG |
687 | } |
688 | ||
43234e76 RG |
689 | const DNSCryptExchangeVersion version = getVersion(); |
690 | ||
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, | |
696 | d_sharedKey); | |
697 | } | |
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, | |
704 | d_sharedKey); | |
705 | #else /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
706 | res = -1; | |
707 | #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
708 | } | |
709 | else { | |
710 | res = -1; | |
711 | } | |
00ffb1c2 | 712 | #else |
43234e76 RG |
713 | int res = crypto_box_easy(reinterpret_cast<unsigned char*>(response + sizeof(responseHeader)), |
714 | reinterpret_cast<unsigned char*>(response + toEncryptPos), | |
00ffb1c2 | 715 | responseLen + paddingSize, |
43234e76 RG |
716 | responseHeader.nonce, |
717 | d_header.clientPK, | |
718 | d_pair->privateKey.key); | |
00ffb1c2 | 719 | #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */ |
11e1e08b RG |
720 | |
721 | if (res == 0) { | |
722 | assert(pos == requiredSize); | |
723 | *encryptedResponseLen = requiredSize; | |
724 | } | |
725 | ||
726 | return res; | |
727 | } | |
728 | ||
f3b1a1ef | 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 |
11e1e08b | 730 | { |
43234e76 | 731 | assert(query != nullptr); |
11e1e08b RG |
732 | assert(queryLen > 0); |
733 | assert(querySize >= queryLen); | |
43234e76 RG |
734 | assert(encryptedResponseLen != nullptr); |
735 | assert(cert != nullptr); | |
736 | ||
11e1e08b | 737 | unsigned char nonce[DNSCRYPT_NONCE_SIZE]; |
43234e76 | 738 | size_t requiredSize = sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE + queryLen; |
11e1e08b RG |
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; | |
743 | ||
43234e76 RG |
744 | if (!tcp && requiredSize < DNSCryptQuery::s_minUDPLength) { |
745 | paddingSize += (DNSCryptQuery::s_minUDPLength - requiredSize); | |
746 | requiredSize = DNSCryptQuery::s_minUDPLength; | |
11e1e08b RG |
747 | } |
748 | ||
749 | if (requiredSize > querySize) | |
750 | return ENOBUFS; | |
751 | ||
752 | /* moving the existing query after the header + MAC */ | |
43234e76 | 753 | memmove(query + sizeof(DNSCryptQueryHeader) + DNSCRYPT_MAC_SIZE, query, queryLen); |
11e1e08b RG |
754 | |
755 | size_t pos = 0; | |
756 | /* client magic */ | |
43234e76 RG |
757 | memcpy(query + pos, cert->signedData.clientMagic, sizeof(cert->signedData.clientMagic)); |
758 | pos += sizeof(cert->signedData.clientMagic); | |
11e1e08b RG |
759 | |
760 | /* client PK */ | |
761 | memcpy(query + pos, clientPublicKey, DNSCRYPT_PUBLIC_KEY_SIZE); | |
762 | pos += DNSCRYPT_PUBLIC_KEY_SIZE; | |
763 | ||
764 | /* client nonce */ | |
765 | memcpy(query + pos, clientNonce, DNSCRYPT_NONCE_SIZE / 2); | |
766 | pos += DNSCRYPT_NONCE_SIZE / 2; | |
767 | size_t encryptedPos = pos; | |
768 | ||
769 | /* clear the MAC bytes */ | |
770 | memset(query + pos, 0, DNSCRYPT_MAC_SIZE); | |
771 | pos += DNSCRYPT_MAC_SIZE; | |
772 | ||
773 | /* skipping data */ | |
774 | pos += queryLen; | |
775 | ||
776 | /* padding */ | |
43234e76 | 777 | query[pos] = static_cast<uint8_t>(0x80); |
11e1e08b RG |
778 | pos++; |
779 | memset(query + pos, 0, paddingSize - 1); | |
780 | pos += paddingSize - 1; | |
781 | ||
782 | memcpy(nonce, clientNonce, DNSCRYPT_NONCE_SIZE / 2); | |
783 | memset(nonce + (DNSCRYPT_NONCE_SIZE / 2), 0, DNSCRYPT_NONCE_SIZE / 2); | |
784 | ||
43234e76 RG |
785 | const DNSCryptExchangeVersion version = getExchangeVersion(*cert); |
786 | int res = -1; | |
787 | ||
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, | |
792 | nonce, | |
793 | cert->signedData.resolverPK, | |
794 | clientPrivateKey.key); | |
795 | } | |
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, | |
801 | nonce, | |
802 | cert->signedData.resolverPK, | |
803 | clientPrivateKey.key); | |
804 | #endif /* HAVE_CRYPTO_BOX_CURVE25519XCHACHA20POLY1305_EASY */ | |
805 | } | |
806 | else { | |
807 | throw std::runtime_error("Unknown DNSCrypt exchange version"); | |
808 | } | |
11e1e08b RG |
809 | |
810 | if (res == 0) { | |
811 | assert(pos == requiredSize); | |
812 | *encryptedResponseLen = requiredSize; | |
813 | } | |
814 | ||
815 | return res; | |
816 | } | |
817 | ||
43234e76 | 818 | bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DNSCryptExchangeVersion version, DNSCryptCert& certOut, DNSCryptPrivateKey& keyOut) |
6bb38cd6 RG |
819 | { |
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)); | |
824 | ||
825 | try { | |
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); | |
831 | } | |
832 | ||
43234e76 | 833 | DNSCryptContext::generateCertificate(serial, begin, end, version, providerPrivateKey, keyOut, certOut); |
6bb38cd6 RG |
834 | success = true; |
835 | } | |
836 | catch(const std::exception& e) { | |
837 | errlog(e.what()); | |
838 | } | |
839 | ||
840 | sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey)); | |
841 | sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey)); | |
842 | return success; | |
843 | } | |
844 | ||
11e1e08b | 845 | #endif |