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.
22 #define BOOST_TEST_DYN_LINK
23 #define BOOST_TEST_NO_MAIN
25 #include <boost/test/unit_test.hpp>
27 #include "dnscrypt.hh"
29 #include "dnsparser.hh"
30 #include "dnswriter.hh"
36 BOOST_AUTO_TEST_SUITE(test_dnscrypt_cc
)
40 // plaintext query for cert
41 BOOST_AUTO_TEST_CASE(DNSCryptPlaintextQuery
) {
42 DNSCryptPrivateKey resolverPrivateKey
;
43 DNSCryptCert resolverCert
;
44 unsigned char providerPublicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
45 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
46 time_t now
= time(nullptr);
47 DNSCryptContext::generateProviderKeys(providerPublicKey
, providerPrivateKey
);
48 DNSCryptContext::generateCertificate(1, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, resolverCert
);
49 auto ctx
= std::make_shared
<DNSCryptContext
>("2.name", resolverCert
, resolverPrivateKey
);
51 DNSName
name("2.name.");
52 vector
<uint8_t> plainQuery
;
53 DNSPacketWriter
pw(plainQuery
, name
, QType::TXT
, QClass::IN
, 0);
54 pw
.getHeader()->rd
= 0;
55 uint16_t len
= plainQuery
.size();
57 std::shared_ptr
<DNSCryptQuery
> query
= std::make_shared
<DNSCryptQuery
>(ctx
);
58 uint16_t decryptedLen
= 0;
60 query
->parsePacket((char*) plainQuery
.data(), len
, false, &decryptedLen
, now
);
62 BOOST_CHECK_EQUAL(query
->isValid(), true);
63 BOOST_CHECK_EQUAL(query
->isEncrypted(), false);
65 std::vector
<uint8_t> response
;
67 query
->getCertificateResponse(now
, response
);
69 MOADNSParser
mdp(false, (char*) response
.data(), response
.size());
71 BOOST_CHECK_EQUAL(mdp
.d_header
.qdcount
, 1);
72 BOOST_CHECK_EQUAL(mdp
.d_header
.ancount
, 1);
73 BOOST_CHECK_EQUAL(mdp
.d_header
.nscount
, 0);
74 BOOST_CHECK_EQUAL(mdp
.d_header
.arcount
, 0);
76 BOOST_CHECK_EQUAL(mdp
.d_qname
.toString(), "2.name.");
77 BOOST_CHECK(mdp
.d_qclass
== QClass::IN
);
78 BOOST_CHECK(mdp
.d_qtype
== QType::TXT
);
81 // invalid plaintext query (A)
82 BOOST_AUTO_TEST_CASE(DNSCryptPlaintextQueryInvalidA
) {
83 DNSCryptPrivateKey resolverPrivateKey
;
84 DNSCryptCert resolverCert
;
85 unsigned char providerPublicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
86 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
87 time_t now
= time(nullptr);
88 DNSCryptContext::generateProviderKeys(providerPublicKey
, providerPrivateKey
);
89 DNSCryptContext::generateCertificate(1, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, resolverCert
);
90 auto ctx
= std::make_shared
<DNSCryptContext
>("2.name", resolverCert
, resolverPrivateKey
);
92 DNSName
name("2.name.");
94 vector
<uint8_t> plainQuery
;
95 DNSPacketWriter
pw(plainQuery
, name
, QType::A
, QClass::IN
, 0);
96 pw
.getHeader()->rd
= 0;
97 uint16_t len
= plainQuery
.size();
99 std::shared_ptr
<DNSCryptQuery
> query
= std::make_shared
<DNSCryptQuery
>(ctx
);
100 uint16_t decryptedLen
= 0;
102 query
->parsePacket((char*) plainQuery
.data(), len
, false, &decryptedLen
, now
);
104 BOOST_CHECK_EQUAL(query
->isValid(), false);
107 // invalid plaintext query (wrong provider name)
108 BOOST_AUTO_TEST_CASE(DNSCryptPlaintextQueryInvalidProviderName
) {
109 DNSCryptPrivateKey resolverPrivateKey
;
110 DNSCryptCert resolverCert
;
111 unsigned char providerPublicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
112 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
113 time_t now
= time(nullptr);
114 DNSCryptContext::generateProviderKeys(providerPublicKey
, providerPrivateKey
);
115 DNSCryptContext::generateCertificate(1, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, resolverCert
);
116 auto ctx
= std::make_shared
<DNSCryptContext
>("2.name", resolverCert
, resolverPrivateKey
);
118 DNSName
name("2.WRONG.name.");
120 vector
<uint8_t> plainQuery
;
121 DNSPacketWriter
pw(plainQuery
, name
, QType::TXT
, QClass::IN
, 0);
122 pw
.getHeader()->rd
= 0;
123 uint16_t len
= plainQuery
.size();
125 std::shared_ptr
<DNSCryptQuery
> query
= std::make_shared
<DNSCryptQuery
>(ctx
);
126 uint16_t decryptedLen
= 0;
128 query
->parsePacket((char*) plainQuery
.data(), len
, false, &decryptedLen
, now
);
130 BOOST_CHECK_EQUAL(query
->isValid(), false);
133 // valid encrypted query
134 BOOST_AUTO_TEST_CASE(DNSCryptEncryptedQueryValid
) {
135 DNSCryptPrivateKey resolverPrivateKey
;
136 DNSCryptCert resolverCert
;
137 unsigned char providerPublicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
138 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
139 time_t now
= time(nullptr);
140 DNSCryptContext::generateProviderKeys(providerPublicKey
, providerPrivateKey
);
141 DNSCryptContext::generateCertificate(1, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, resolverCert
);
142 auto ctx
= std::make_shared
<DNSCryptContext
>("2.name", resolverCert
, resolverPrivateKey
);
144 DNSCryptPrivateKey clientPrivateKey
;
145 unsigned char clientPublicKey
[DNSCRYPT_PUBLIC_KEY_SIZE
];
147 DNSCryptContext::generateResolverKeyPair(clientPrivateKey
, clientPublicKey
);
149 unsigned char clientNonce
[DNSCRYPT_NONCE_SIZE
/ 2] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0A, 0x0B };
151 DNSName
name("www.powerdns.com.");
152 vector
<uint8_t> plainQuery
;
153 DNSPacketWriter
pw(plainQuery
, name
, QType::AAAA
, QClass::IN
, 0);
154 pw
.getHeader()->rd
= 1;
155 size_t requiredSize
= plainQuery
.size() + sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
;
156 if (requiredSize
< DNSCryptQuery::s_minUDPLength
) {
157 requiredSize
= DNSCryptQuery::s_minUDPLength
;
160 plainQuery
.reserve(requiredSize
);
161 uint16_t len
= plainQuery
.size();
162 uint16_t encryptedResponseLen
= 0;
164 int res
= ctx
->encryptQuery((char*) plainQuery
.data(), len
, plainQuery
.capacity(), clientPublicKey
, clientPrivateKey
, clientNonce
, false, &encryptedResponseLen
, std::make_shared
<DNSCryptCert
>(resolverCert
));
166 BOOST_CHECK_EQUAL(res
, 0);
167 BOOST_CHECK(encryptedResponseLen
> len
);
169 std::shared_ptr
<DNSCryptQuery
> query
= std::make_shared
<DNSCryptQuery
>(ctx
);
170 uint16_t decryptedLen
= 0;
172 query
->parsePacket((char*) plainQuery
.data(), encryptedResponseLen
, false, &decryptedLen
, now
);
174 BOOST_CHECK_EQUAL(query
->isValid(), true);
175 BOOST_CHECK_EQUAL(query
->isEncrypted(), true);
177 MOADNSParser
mdp(true, (char*) plainQuery
.data(), decryptedLen
);
179 BOOST_CHECK_EQUAL(mdp
.d_header
.qdcount
, 1);
180 BOOST_CHECK_EQUAL(mdp
.d_header
.ancount
, 0);
181 BOOST_CHECK_EQUAL(mdp
.d_header
.nscount
, 0);
182 BOOST_CHECK_EQUAL(mdp
.d_header
.arcount
, 0);
184 BOOST_CHECK_EQUAL(mdp
.d_qname
, name
);
185 BOOST_CHECK(mdp
.d_qclass
== QClass::IN
);
186 BOOST_CHECK(mdp
.d_qtype
== QType::AAAA
);
189 // valid encrypted query with not enough room
190 BOOST_AUTO_TEST_CASE(DNSCryptEncryptedQueryValidButShort
) {
191 DNSCryptPrivateKey resolverPrivateKey
;
192 DNSCryptCert resolverCert
;
193 unsigned char providerPublicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
194 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
195 time_t now
= time(nullptr);
196 DNSCryptContext::generateProviderKeys(providerPublicKey
, providerPrivateKey
);
197 DNSCryptContext::generateCertificate(1, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, resolverCert
);
198 auto ctx
= std::make_shared
<DNSCryptContext
>("2.name", resolverCert
, resolverPrivateKey
);
200 DNSCryptPrivateKey clientPrivateKey
;
201 unsigned char clientPublicKey
[DNSCRYPT_PUBLIC_KEY_SIZE
];
203 DNSCryptContext::generateResolverKeyPair(clientPrivateKey
, clientPublicKey
);
205 unsigned char clientNonce
[DNSCRYPT_NONCE_SIZE
/ 2] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0A, 0x0B };
207 DNSName
name("www.powerdns.com.");
208 vector
<uint8_t> plainQuery
;
209 DNSPacketWriter
pw(plainQuery
, name
, QType::AAAA
, QClass::IN
, 0);
210 pw
.getHeader()->rd
= 1;
212 uint16_t len
= plainQuery
.size();
213 uint16_t encryptedResponseLen
= 0;
215 int res
= ctx
->encryptQuery((char*) plainQuery
.data(), len
, plainQuery
.capacity(), clientPublicKey
, clientPrivateKey
, clientNonce
, false, &encryptedResponseLen
, std::make_shared
<DNSCryptCert
>(resolverCert
));
217 BOOST_CHECK_EQUAL(res
, ENOBUFS
);
220 // valid encrypted query with old key
221 BOOST_AUTO_TEST_CASE(DNSCryptEncryptedQueryValidWithOldKey
) {
222 DNSCryptPrivateKey resolverPrivateKey
;
223 DNSCryptCert resolverCert
;
224 unsigned char providerPublicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
225 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
226 time_t now
= time(nullptr);
227 DNSCryptContext::generateProviderKeys(providerPublicKey
, providerPrivateKey
);
228 DNSCryptContext::generateCertificate(1, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, resolverCert
);
229 auto ctx
= std::make_shared
<DNSCryptContext
>("2.name", resolverCert
, resolverPrivateKey
);
231 DNSCryptPrivateKey clientPrivateKey
;
232 unsigned char clientPublicKey
[DNSCRYPT_PUBLIC_KEY_SIZE
];
234 DNSCryptContext::generateResolverKeyPair(clientPrivateKey
, clientPublicKey
);
236 unsigned char clientNonce
[DNSCRYPT_NONCE_SIZE
/ 2] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0A, 0x0B };
238 DNSName
name("www.powerdns.com.");
239 vector
<uint8_t> plainQuery
;
240 DNSPacketWriter
pw(plainQuery
, name
, QType::AAAA
, QClass::IN
, 0);
241 pw
.getHeader()->rd
= 1;
243 size_t requiredSize
= plainQuery
.size() + sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
;
244 if (requiredSize
< DNSCryptQuery::s_minUDPLength
) {
245 requiredSize
= DNSCryptQuery::s_minUDPLength
;
248 plainQuery
.reserve(requiredSize
);
250 uint16_t len
= plainQuery
.size();
251 uint16_t encryptedResponseLen
= 0;
253 int res
= ctx
->encryptQuery((char*) plainQuery
.data(), len
, plainQuery
.capacity(), clientPublicKey
, clientPrivateKey
, clientNonce
, false, &encryptedResponseLen
, std::make_shared
<DNSCryptCert
>(resolverCert
));
255 BOOST_CHECK_EQUAL(res
, 0);
256 BOOST_CHECK(encryptedResponseLen
> len
);
258 DNSCryptCert newResolverCert
;
259 DNSCryptContext::generateCertificate(2, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, newResolverCert
);
260 ctx
->addNewCertificate(newResolverCert
, resolverPrivateKey
);
261 ctx
->markInactive(resolverCert
.getSerial());
263 std::shared_ptr
<DNSCryptQuery
> query
= std::make_shared
<DNSCryptQuery
>(ctx
);
264 uint16_t decryptedLen
= 0;
266 query
->parsePacket((char*) plainQuery
.data(), encryptedResponseLen
, false, &decryptedLen
, now
);
268 BOOST_CHECK_EQUAL(query
->isValid(), true);
269 BOOST_CHECK_EQUAL(query
->isEncrypted(), true);
271 MOADNSParser
mdp(true, (char*) plainQuery
.data(), decryptedLen
);
273 BOOST_CHECK_EQUAL(mdp
.d_header
.qdcount
, 1);
274 BOOST_CHECK_EQUAL(mdp
.d_header
.ancount
, 0);
275 BOOST_CHECK_EQUAL(mdp
.d_header
.nscount
, 0);
276 BOOST_CHECK_EQUAL(mdp
.d_header
.arcount
, 0);
278 BOOST_CHECK_EQUAL(mdp
.d_qname
, name
);
279 BOOST_CHECK(mdp
.d_qclass
== QClass::IN
);
280 BOOST_CHECK(mdp
.d_qtype
== QType::AAAA
);
283 // valid encrypted query with wrong key
284 BOOST_AUTO_TEST_CASE(DNSCryptEncryptedQueryInvalidWithWrongKey
) {
285 DNSCryptPrivateKey resolverPrivateKey
;
286 DNSCryptCert resolverCert
;
287 unsigned char providerPublicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
288 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
289 time_t now
= time(nullptr);
290 DNSCryptContext::generateProviderKeys(providerPublicKey
, providerPrivateKey
);
291 DNSCryptContext::generateCertificate(1, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, resolverCert
);
292 auto ctx
= std::make_shared
<DNSCryptContext
>("2.name", resolverCert
, resolverPrivateKey
);
294 DNSCryptPrivateKey clientPrivateKey
;
295 unsigned char clientPublicKey
[DNSCRYPT_PUBLIC_KEY_SIZE
];
297 DNSCryptContext::generateResolverKeyPair(clientPrivateKey
, clientPublicKey
);
299 unsigned char clientNonce
[DNSCRYPT_NONCE_SIZE
/ 2] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0A, 0x0B };
301 DNSName
name("www.powerdns.com.");
302 vector
<uint8_t> plainQuery
;
303 DNSPacketWriter
pw(plainQuery
, name
, QType::AAAA
, QClass::IN
, 0);
304 pw
.getHeader()->rd
= 1;
306 size_t requiredSize
= plainQuery
.size() + sizeof(DNSCryptQueryHeader
) + DNSCRYPT_MAC_SIZE
;
307 if (requiredSize
< DNSCryptQuery::s_minUDPLength
) {
308 requiredSize
= DNSCryptQuery::s_minUDPLength
;
311 plainQuery
.reserve(requiredSize
);
313 uint16_t len
= plainQuery
.size();
314 uint16_t encryptedResponseLen
= 0;
316 int res
= ctx
->encryptQuery((char*) plainQuery
.data(), len
, plainQuery
.capacity(), clientPublicKey
, clientPrivateKey
, clientNonce
, false, &encryptedResponseLen
, std::make_shared
<DNSCryptCert
>(resolverCert
));
318 BOOST_CHECK_EQUAL(res
, 0);
319 BOOST_CHECK(encryptedResponseLen
> len
);
321 DNSCryptCert newResolverCert
;
322 DNSCryptContext::generateCertificate(2, now
, now
+ (24 * 60 * 3600), DNSCryptExchangeVersion::VERSION1
, providerPrivateKey
, resolverPrivateKey
, newResolverCert
);
323 ctx
->addNewCertificate(newResolverCert
, resolverPrivateKey
);
324 ctx
->markInactive(resolverCert
.getSerial());
325 ctx
->removeInactiveCertificate(resolverCert
.getSerial());
327 /* we have removed the old certificate, we can't decrypt this query */
329 std::shared_ptr
<DNSCryptQuery
> query
= std::make_shared
<DNSCryptQuery
>(ctx
);
330 uint16_t decryptedLen
= 0;
332 query
->parsePacket((char*) plainQuery
.data(), encryptedResponseLen
, false, &decryptedLen
, now
);
334 BOOST_CHECK_EQUAL(query
->isValid(), false);
339 BOOST_AUTO_TEST_SUITE_END();