]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnscrypt.cc
Merge pull request #4711 from thusoy/pgsql-extra-connection-args
[thirdparty/pdns.git] / pdns / dnscrypt.cc
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 */
22 #include "config.h"
23 #ifdef HAVE_DNSCRYPT
24 #include <fstream>
25 #include "dolog.hh"
26 #include "dnscrypt.hh"
27 #include "dnswriter.hh"
28
29 DnsCryptPrivateKey::DnsCryptPrivateKey()
30 {
31 sodium_memzero(key, sizeof(key));
32 sodium_mlock(key, sizeof(key));
33 }
34
35 void DnsCryptPrivateKey::loadFromFile(const std::string& keyFile)
36 {
37 ifstream file(keyFile);
38 sodium_memzero(key, sizeof(key));
39 file.read((char*) key, sizeof(key));
40
41 if (file.fail()) {
42 sodium_memzero(key, sizeof(key));
43 file.close();
44 throw std::runtime_error("Invalid DNSCrypt key file " + keyFile);
45 }
46
47 file.close();
48 }
49
50 void DnsCryptPrivateKey::saveToFile(const std::string& keyFile) const
51 {
52 ofstream file(keyFile);
53 file.write((char*) key, sizeof(key));
54 file.close();
55 }
56
57 DnsCryptPrivateKey::~DnsCryptPrivateKey()
58 {
59 sodium_munlock(key, sizeof(key));
60 }
61
62 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
63 DnsCryptQuery::~DnsCryptQuery()
64 {
65 if (sharedKeyComputed) {
66 sodium_munlock(sharedKey, sizeof(sharedKey));
67 }
68 }
69
70 int DnsCryptQuery::computeSharedKey(const DnsCryptPrivateKey& privateKey)
71 {
72 int res = 0;
73
74 if (sharedKeyComputed) {
75 return res;
76 }
77
78 sodium_mlock(sharedKey, sizeof(sharedKey));
79 res = crypto_box_beforenm(sharedKey,
80 header.clientPK,
81 privateKey.key);
82
83 if (res != 0) {
84 sodium_munlock(sharedKey, sizeof(sharedKey));
85 return res;
86 }
87
88 sharedKeyComputed = true;
89 return res;
90 }
91 #else
92 DnsCryptQuery::~DnsCryptQuery()
93 {
94 }
95 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
96
97 void DnsCryptContext::generateProviderKeys(unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE], unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE])
98 {
99 int res = crypto_sign_ed25519_keypair(publicKey, privateKey);
100
101 if (res != 0) {
102 throw std::runtime_error("Error generating DNSCrypt provider keys");
103 }
104 }
105
106 std::string DnsCryptContext::getProviderFingerprint(unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE])
107 {
108 boost::format fmt("%02X%02X");
109 ostringstream ret;
110
111 for (size_t idx = 0; idx < DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE; idx += 2)
112 {
113 ret << (fmt % static_cast<int>(publicKey[idx]) % static_cast<int>(publicKey[idx+1]));
114 if (idx < (DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE - 2)) {
115 ret << ":";
116 }
117 }
118
119 return ret.str();
120 }
121
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)
123 {
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);
129
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);
138
139 unsigned long long signatureSize = 0;
140
141 int res = crypto_sign_ed25519(cert.signature,
142 &signatureSize,
143 (unsigned char*) &cert.signedData,
144 sizeof(cert.signedData),
145 providerPrivateKey);
146
147 if (res == 0) {
148 assert(signatureSize == sizeof(DnsCryptCertSignedData) + DNSCRYPT_SIGNATURE_SIZE);
149 }
150 else {
151 throw std::runtime_error("Error generating DNSCrypt certificate");
152 }
153 }
154
155 void DnsCryptContext::loadCertFromFile(const std::string&filename, DnsCryptCert& dest)
156 {
157 ifstream file(filename);
158 file.read((char *) &dest, sizeof(dest));
159
160 if (file.fail())
161 throw std::runtime_error("Invalid dnscrypt certificate file " + filename);
162
163 file.close();
164 }
165
166 void DnsCryptContext::saveCertFromFile(const DnsCryptCert& cert, const std::string&filename)
167 {
168 ofstream file(filename);
169 file.write((char *) &cert, sizeof(cert));
170 file.close();
171 }
172
173 void DnsCryptContext::generateResolverKeyPair(DnsCryptPrivateKey& privK, unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE])
174 {
175 int res = crypto_box_keypair(pubK, privK.key);
176
177 if (res != 0) {
178 throw std::runtime_error("Error generating DNSCrypt resolver keys");
179 }
180 }
181
182 void DnsCryptContext::computePublicKeyFromPrivate(const DnsCryptPrivateKey& privK, unsigned char* pubK)
183 {
184 int res = crypto_scalarmult_base(pubK,
185 privK.key);
186
187 if (res != 0) {
188 throw std::runtime_error("Error computing dnscrypt public key from the private one");
189 }
190 }
191
192 std::string DnsCryptContext::certificateDateToStr(uint32_t date)
193 {
194 char buf[20];
195 time_t tdate = (time_t) ntohl(date);
196 struct tm date_tm;
197
198 localtime_r(&tdate, &date_tm);
199 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &date_tm);
200
201 return string(buf);
202 }
203
204 void DnsCryptContext::setNewCertificate(const DnsCryptCert& newCert, const DnsCryptPrivateKey& newKey)
205 {
206 // XXX TODO: this could use a lock
207 oldPrivateKey = privateKey;
208 oldCert = cert;
209 hasOldCert = true;
210 privateKey = newKey;
211 cert = newCert;
212 }
213
214 void DnsCryptContext::loadNewCertificate(const std::string& certFile, const std::string& keyFile)
215 {
216 DnsCryptCert newCert;
217 DnsCryptPrivateKey newPrivateKey;
218
219 loadCertFromFile(certFile, newCert);
220 newPrivateKey.loadFromFile(keyFile);
221 setNewCertificate(newCert, newPrivateKey);
222 }
223
224 void DnsCryptContext::parsePlaintextQuery(const char * packet, uint16_t packetSize, std::shared_ptr<DnsCryptQuery> query) const
225 {
226 if (packetSize < sizeof(dnsheader)) {
227 return;
228 }
229
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)
232 return;
233
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)))
238 return;
239
240 if (qtype != QType::TXT || qclass != QClass::IN)
241 return;
242
243 if (qname != DNSName(providerName))
244 return;
245
246 query->qname = qname;
247 query->id = dh->id;
248 query->valid = true;
249 }
250
251 void DnsCryptContext::getCertificateResponse(const std::shared_ptr<DnsCryptQuery> query, vector<uint8_t>& response) const
252 {
253 DNSPacketWriter pw(response, query->qname, QType::TXT, QClass::IN, Opcode::Query);
254 struct dnsheader * dh = pw.getHeader();
255 dh->id = query->id;
256 dh->qr = true;
257 dh->rcode = RCode::NoError;
258 pw.startRecord(query->qname, QType::TXT, (DNSCRYPT_CERTIFICATE_RESPONSE_TTL), QClass::IN, DNSResourceRecord::ANSWER, true);
259 std::string scert;
260 uint8_t certSize = sizeof(cert);
261 scert.assign((const char*) &certSize, sizeof(certSize));
262 scert.append((const char*) &cert, certSize);
263
264 pw.xfrBlob(scert);
265 pw.commit();
266 }
267
268 bool DnsCryptContext::magicMatchesPublicKey(std::shared_ptr<DnsCryptQuery> query) const
269 {
270 const unsigned char* magic = query->header.clientMagic;
271
272 if (memcmp(magic, cert.signedData.clientMagic, DNSCRYPT_CLIENT_MAGIC_SIZE) == 0) {
273 return true;
274 }
275
276 if (hasOldCert == true &&
277 memcmp(magic, oldCert.signedData.clientMagic, DNSCRYPT_CLIENT_MAGIC_SIZE) == 0) {
278 query->useOldCert = true;
279 return true;
280 }
281
282 return false;
283 }
284
285 void DnsCryptContext::isQueryEncrypted(const char * packet, uint16_t packetSize, std::shared_ptr<DnsCryptQuery> query, bool tcp) const
286 {
287 query->encrypted = false;
288
289 if (packetSize < sizeof(DnsCryptQueryHeader)) {
290 return;
291 }
292
293 if (!tcp && packetSize < DnsCryptQuery::minUDPLength) {
294 return;
295 }
296
297 struct DnsCryptQueryHeader* header = (struct DnsCryptQueryHeader*) packet;
298
299 query->header = *(header);
300
301 if (!magicMatchesPublicKey(query)) {
302 return;
303 }
304
305 query->encrypted = true;
306 }
307
308 void DnsCryptContext::getDecryptedQuery(std::shared_ptr<DnsCryptQuery> query, bool tcp, char* packet, uint16_t packetSize, uint16_t* decryptedQueryLen) const
309 {
310 assert(decryptedQueryLen != NULL);
311 assert(query->encrypted);
312 assert(query->valid == false);
313
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);
317 return;
318 }
319 #endif
320
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");
325
326 memcpy(nonce, &query->header.clientNonce, sizeof(query->header.clientNonce));
327 memset(nonce + sizeof(query->header.clientNonce), 0, sizeof(nonce) - sizeof(query->header.clientNonce));
328
329 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
330 int res = query->computeSharedKey(query->useOldCert ? oldPrivateKey : privateKey);
331 if (res != 0) {
332 vinfolog("Dropping encrypted query we can't compute the shared key for");
333 return;
334 }
335
336 res = crypto_box_open_easy_afternm((unsigned char*) packet,
337 (unsigned char*) packet + sizeof(DnsCryptQueryHeader),
338 packetSize - sizeof(DnsCryptQueryHeader),
339 nonce,
340 query->sharedKey);
341 #else
342 int res = crypto_box_open_easy((unsigned char*) packet,
343 (unsigned char*) packet + sizeof(DnsCryptQueryHeader),
344 packetSize - sizeof(DnsCryptQueryHeader),
345 nonce,
346 query->header.clientPK,
347 query->useOldCert ? oldPrivateKey.key : privateKey.key);
348 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
349
350 if (res != 0) {
351 vinfolog("Dropping encrypted query we can't decrypt");
352 return;
353 }
354
355 *decryptedQueryLen = packetSize - sizeof(DnsCryptQueryHeader) - DNSCRYPT_MAC_SIZE;
356 uint16_t pos = *decryptedQueryLen;
357 assert(pos < packetSize);
358 query->paddedLen = *decryptedQueryLen;
359
360 while(pos > 0 && packet[pos - 1] == 0) pos--;
361
362 if (pos == 0 || ((uint8_t) packet[pos - 1]) != 0x80) {
363 vinfolog("Dropping encrypted query with invalid padding value");
364 return;
365 }
366
367 pos--;
368
369 size_t paddingLen = *decryptedQueryLen - pos;
370 *decryptedQueryLen = pos;
371
372 if (tcp && paddingLen > DNSCRYPT_MAX_TCP_PADDING_SIZE) {
373 vinfolog("Dropping encrypted query with too long padding size");
374 return;
375 }
376
377 query->len = pos;
378
379 query->valid = true;
380 }
381
382 void DnsCryptContext::parsePacket(char* packet, uint16_t packetSize, std::shared_ptr<DnsCryptQuery> query, bool tcp, uint16_t* decryptedQueryLen) const
383 {
384 assert(packet != NULL);
385 assert(decryptedQueryLen != NULL);
386
387 query->valid = false;
388
389 /* might be a plaintext certificate request or an authenticated request */
390 isQueryEncrypted(packet, packetSize, query, tcp);
391
392 if (query->encrypted) {
393 getDecryptedQuery(query, tcp, packet, packetSize, decryptedQueryLen);
394 }
395 else {
396 parsePlaintextQuery(packet, packetSize, query);
397 }
398 }
399
400 void DnsCryptContext::fillServerNonce(unsigned char* nonce) const
401 {
402 uint32_t* dest = (uint32_t*) nonce;
403 static const size_t nonceSize = DNSCRYPT_NONCE_SIZE / 2;
404
405 for (size_t pos = 0; pos < (nonceSize / sizeof(*dest)); pos++)
406 {
407 const uint32_t value = randombytes_random();
408 memcpy(dest + pos, &value, sizeof(value));
409 }
410 }
411
412 /*
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."
415 */
416 uint16_t DnsCryptContext::computePaddingSize(uint16_t unpaddedLen, size_t maxLen, const unsigned char* clientNonce) const
417 {
418 size_t paddedLen = 0;
419 uint16_t result = 0;
420 uint32_t rnd = 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);
426
427 paddedLen = unpaddedLen + rnd % (maxLen - unpaddedLen + 1);
428 paddedLen += DNSCRYPT_PADDED_BLOCK_SIZE - (paddedLen % DNSCRYPT_PADDED_BLOCK_SIZE);
429
430 if (paddedLen > maxLen)
431 paddedLen = maxLen;
432
433 result = paddedLen - unpaddedLen;
434
435 return result;
436 }
437
438 int DnsCryptContext::encryptResponse(char* response, uint16_t responseLen, uint16_t responseSize, const std::shared_ptr<DnsCryptQuery> query, bool tcp, uint16_t* encryptedResponseLen) const
439 {
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);
446
447 if (!tcp && query->paddedLen < responseLen) {
448 struct dnsheader* dh = (struct dnsheader*) response;
449 size_t questionSize = 0;
450
451 if (responseLen > sizeof(dnsheader)) {
452 unsigned int consumed = 0;
453 DNSName qname(response, responseLen, sizeof(dnsheader), false, 0, 0, &consumed);
454 if (consumed > 0) {
455 questionSize = consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
456 }
457 }
458
459 responseLen = sizeof(dnsheader) + questionSize;
460
461 if (responseLen > query->paddedLen) {
462 responseLen = query->paddedLen;
463 }
464 dh->ancount = dh->arcount = dh->nscount = 0;
465 dh->tc = 1;
466 }
467
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;
472
473 if (requiredSize > responseSize)
474 return ENOBUFS;
475
476 memcpy(&header.nonce, &query->header.clientNonce, sizeof query->header.clientNonce);
477 fillServerNonce(&(header.nonce[sizeof(query->header.clientNonce)]));
478
479 /* moving the existing response after the header + MAC */
480 memmove(response + sizeof(header) + DNSCRYPT_MAC_SIZE, response, responseLen);
481
482 uint16_t pos = 0;
483 /* copying header */
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 */
491 pos += responseLen;
492 /* padding */
493 response[pos] = (uint8_t) 0x80;
494 pos++;
495 memset(response + pos, 0, paddingSize - 1);
496 pos += (paddingSize - 1);
497
498 /* encrypting */
499 #ifdef HAVE_CRYPTO_BOX_EASY_AFTERNM
500 int res = query->computeSharedKey(query->useOldCert ? oldPrivateKey : privateKey);
501 if (res != 0) {
502 return res;
503 }
504
505 res = crypto_box_easy_afternm((unsigned char*) (response + sizeof(header)),
506 (unsigned char*) (response + toEncryptPos),
507 responseLen + paddingSize,
508 header.nonce,
509 query->sharedKey);
510 #else
511 int res = crypto_box_easy((unsigned char*) (response + sizeof(header)),
512 (unsigned char*) (response + toEncryptPos),
513 responseLen + paddingSize,
514 header.nonce,
515 query->header.clientPK,
516 query->useOldCert ? oldPrivateKey.key : privateKey.key);
517 #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */
518
519 if (res == 0) {
520 assert(pos == requiredSize);
521 *encryptedResponseLen = requiredSize;
522 }
523
524 return res;
525 }
526
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
528 {
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;
539
540 if (!tcp && requiredSize < DnsCryptQuery::minUDPLength) {
541 paddingSize += (DnsCryptQuery::minUDPLength - requiredSize);
542 requiredSize = DnsCryptQuery::minUDPLength;
543 }
544
545 if (requiredSize > querySize)
546 return ENOBUFS;
547
548 /* moving the existing query after the header + MAC */
549 memmove(query + sizeof(DnsCryptQueryHeader) + DNSCRYPT_MAC_SIZE, query, queryLen);
550
551 size_t pos = 0;
552 /* client magic */
553 memcpy(query + pos, cert.signedData.clientMagic, sizeof(cert.signedData.clientMagic));
554 pos += sizeof(cert.signedData.clientMagic);
555
556 /* client PK */
557 memcpy(query + pos, clientPublicKey, DNSCRYPT_PUBLIC_KEY_SIZE);
558 pos += DNSCRYPT_PUBLIC_KEY_SIZE;
559
560 /* client nonce */
561 memcpy(query + pos, clientNonce, DNSCRYPT_NONCE_SIZE / 2);
562 pos += DNSCRYPT_NONCE_SIZE / 2;
563 size_t encryptedPos = pos;
564
565 /* clear the MAC bytes */
566 memset(query + pos, 0, DNSCRYPT_MAC_SIZE);
567 pos += DNSCRYPT_MAC_SIZE;
568
569 /* skipping data */
570 pos += queryLen;
571
572 /* padding */
573 query[pos] = (uint8_t) 0x80;
574 pos++;
575 memset(query + pos, 0, paddingSize - 1);
576 pos += paddingSize - 1;
577
578 memcpy(nonce, clientNonce, DNSCRYPT_NONCE_SIZE / 2);
579 memset(nonce + (DNSCRYPT_NONCE_SIZE / 2), 0, DNSCRYPT_NONCE_SIZE / 2);
580
581 int res = crypto_box_easy((unsigned char*) query + encryptedPos,
582 (unsigned char*) query + encryptedPos + DNSCRYPT_MAC_SIZE,
583 queryLen + paddingSize,
584 nonce,
585 cert.signedData.resolverPK,
586 clientPrivateKey.key);
587
588 if (res == 0) {
589 assert(pos == requiredSize);
590 *encryptedResponseLen = requiredSize;
591 }
592
593 return res;
594 }
595
596 #endif