]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/test-signers.cc
rec: Update new b-root-server.net addresses in built-in hints.
[thirdparty/pdns.git] / pdns / test-signers.cc
1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 #include <boost/test/unit_test.hpp>
7 #include <boost/assign/list_of.hpp>
8
9 #include "base32.hh"
10 #include "base64.hh"
11 #include "dnsseckeeper.hh"
12 #include "dnssecinfra.hh"
13 #include "misc.hh"
14
15 #include <cstdio>
16 #include <unordered_map>
17
18 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables): Boost stuff.
19 BOOST_AUTO_TEST_SUITE(test_signers)
20
21 struct SignerParams
22 {
23 std::string iscMap;
24 std::string dsSHA1;
25 std::string dsSHA256;
26 std::string dsSHA384;
27 std::vector<uint8_t> signature;
28 std::string zoneRepresentation;
29 std::string name;
30 std::string rfcMsgDump;
31 std::string rfcB64Signature;
32 int bits;
33 uint16_t flags;
34 uint16_t rfcFlags;
35 uint8_t algorithm;
36 bool isDeterministic;
37 std::string pem;
38 };
39
40 // clang-format off
41 static const SignerParams rsaSha256SignerParams = SignerParams
42 {
43 .iscMap = "Algorithm: 8\n"
44 "Modulus: qtunSiHnYq4XRLBehKAw1Glxb+48oIpAC7w3Jhpj570bb2uHt6orWGqnuyRtK8oqUi2ABoV0PFm8+IPgDMEdCQ==\n"
45 "PublicExponent: AQAB\n"
46 "PrivateExponent: MiItniUAngXzMeaGdWgDq/AcpvlCtOCcFlVt4TJRKkfp8DNRSxIxG53NNlOFkp1W00iLHqYC2GrH1qkKgT9l+Q==\n"
47 "Prime1: 3sZmM+5FKFy5xaRt0n2ZQOZ2C+CoKzVil6/al9LmYVs=\n"
48 "Prime2: xFcNWSIW6v8dDL2JQ1kxFDm/8RVeUSs1BNXXnvCjBGs=\n"
49 "Exponent1: WuUwhjfN1+4djlrMxHmisixWNfpwI1Eg7Ss/UXsnrMk=\n"
50 "Exponent2: vfMqas1cNsXRqP3Fym6D2Pl2BRuTQBv5E1B/ZrmQPTk=\n"
51 "Coefficient: Q10z43cA3hkwOkKsj5T0W5jrX97LBwZoY5lIjDCa4+M=\n",
52
53 .dsSHA1 = "1506 8 1 "
54 "172a500b374158d1a64ba3073cdbbc319b2fdf2c",
55
56 .dsSHA256 = "1506 8 2 "
57 "253b099ff47b02c6ffa52695a30a94c6681c56befe0e71a5077d6f79514972f9",
58
59 .dsSHA384 = "1506 8 4 "
60 "22ea940600dc2d9a98b1126c26ac0dc5c91b31eb50fe784b"
61 "36ad675e9eecfe6573c1f85c53b6bc94580f3ac443d13c4c",
62
63 /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
64 .signature = {
65 0x93, 0x93, 0x5f, 0xd8, 0xa1, 0x2b, 0x4c, 0x0b, 0xf3, 0x67, 0x42, 0x13, 0x52,
66 0x00, 0x35, 0xdc, 0x09, 0xe0, 0xdf, 0xe0, 0x3e, 0xc2, 0xcf, 0x64, 0xab, 0x9f,
67 0x9f, 0x51, 0x5f, 0x5c, 0x27, 0xbe, 0x13, 0xd6, 0x17, 0x07, 0xa6, 0xe4, 0x3b,
68 0x63, 0x44, 0x85, 0x06, 0x13, 0xaa, 0x01, 0x3c, 0x58, 0x52, 0xa3, 0x98, 0x20,
69 0x65, 0x03, 0xd0, 0x40, 0xc8, 0xa0, 0xe9, 0xd2, 0xc0, 0x03, 0x5a, 0xab
70 },
71
72 .zoneRepresentation = "256 3 8 "
73 "AwEAAarbp0oh52KuF0SwXoSgMNRpcW/uPKCKQAu8NyYaY+"
74 "e9G29rh7eqK1hqp7skbSvKKlItgAaFdDxZvPiD4AzBHQk=",
75
76 .name = "rsa.",
77
78 .rfcMsgDump = "",
79 .rfcB64Signature = "",
80
81 .bits = 512,
82 .flags = 256,
83 .rfcFlags = 0,
84
85 .algorithm = DNSSECKeeper::RSASHA256,
86 .isDeterministic = true,
87
88 #if OPENSSL_VERSION_MAJOR >= 3
89 // OpenSSL 3.0.0 uses a generic key interface which stores the key PKCS#8-encoded.
90 .pem = "-----BEGIN PRIVATE KEY-----\n"
91 "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqtunSiHnYq4XRLBe\n"
92 "hKAw1Glxb+48oIpAC7w3Jhpj570bb2uHt6orWGqnuyRtK8oqUi2ABoV0PFm8+IPg\n"
93 "DMEdCQIDAQABAkAyIi2eJQCeBfMx5oZ1aAOr8Bym+UK04JwWVW3hMlEqR+nwM1FL\n"
94 "EjEbnc02U4WSnVbTSIsepgLYasfWqQqBP2X5AiEA3sZmM+5FKFy5xaRt0n2ZQOZ2\n"
95 "C+CoKzVil6/al9LmYVsCIQDEVw1ZIhbq/x0MvYlDWTEUOb/xFV5RKzUE1dee8KME\n"
96 "awIgWuUwhjfN1+4djlrMxHmisixWNfpwI1Eg7Ss/UXsnrMkCIQC98ypqzVw2xdGo\n"
97 "/cXKboPY+XYFG5NAG/kTUH9muZA9OQIgQ10z43cA3hkwOkKsj5T0W5jrX97LBwZo\n"
98 "Y5lIjDCa4+M=\n"
99 "-----END PRIVATE KEY-----\n"
100 #else
101 .pem = "-----BEGIN RSA PRIVATE KEY-----\n"
102 "MIIBOgIBAAJBAKrbp0oh52KuF0SwXoSgMNRpcW/uPKCKQAu8NyYaY+e9G29rh7eq\n"
103 "K1hqp7skbSvKKlItgAaFdDxZvPiD4AzBHQkCAwEAAQJAMiItniUAngXzMeaGdWgD\n"
104 "q/AcpvlCtOCcFlVt4TJRKkfp8DNRSxIxG53NNlOFkp1W00iLHqYC2GrH1qkKgT9l\n"
105 "+QIhAN7GZjPuRShcucWkbdJ9mUDmdgvgqCs1Ypev2pfS5mFbAiEAxFcNWSIW6v8d\n"
106 "DL2JQ1kxFDm/8RVeUSs1BNXXnvCjBGsCIFrlMIY3zdfuHY5azMR5orIsVjX6cCNR\n"
107 "IO0rP1F7J6zJAiEAvfMqas1cNsXRqP3Fym6D2Pl2BRuTQBv5E1B/ZrmQPTkCIENd\n"
108 "M+N3AN4ZMDpCrI+U9FuY61/eywcGaGOZSIwwmuPj\n"
109 "-----END RSA PRIVATE KEY-----\n"
110 #endif
111 };
112 // clang-format on
113
114 /* ECDSA-P256-SHA256 from
115 * https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h
116 */
117 // clang-format off
118 static const SignerParams ecdsaSha256 = SignerParams
119 {
120 .iscMap = "Algorithm: 13\n"
121 "PrivateKey: iyLIPdk3DOIxVmmSYlmTstbtUPiVlEyDX46psyCwNVQ=\n",
122
123 .dsSHA1 = "5345 13 1 "
124 "954103ac7c43810ce9f414e80f30ab1cbe49b236",
125
126 .dsSHA256 = "5345 13 2 "
127 "bac2107036e735b50f85006ce409a19a3438cab272e70769ebda032239a3d0ca",
128
129 .dsSHA384 = "5345 13 4 "
130 "a0ac6790483872be72a258314200a88ab75cdd70f66a18a0"
131 "9f0f414c074df0989fdb1df0e67d82d4312cda67b93a76c1",
132
133 /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
134 .signature = {
135 0xa2, 0x95, 0x76, 0xb5, 0xf5, 0x7e, 0xbd, 0xdd, 0xf5, 0x62, 0xa2, 0xc3, 0xa4,
136 0x8d, 0xd4, 0x53, 0x5c, 0xba, 0x29, 0x71, 0x8c, 0xcc, 0x28, 0x7b, 0x58, 0xf3,
137 0x1e, 0x4e, 0x58, 0xe2, 0x36, 0x7e, 0xa0, 0x1a, 0xb6, 0xe6, 0x29, 0x71, 0x1b,
138 0xd3, 0x8c, 0x88, 0xc3, 0xee, 0x12, 0x0e, 0x69, 0x70, 0x55, 0x99, 0xec, 0xd5,
139 0xf6, 0x4f, 0x4b, 0xe2, 0x41, 0xd9, 0x10, 0x7e, 0x67, 0xe5, 0xad, 0x2f
140 },
141
142 .zoneRepresentation = "256 3 13 "
143 "8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZ"
144 "rSLBubLPiBw7gbvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==",
145
146 .name = "ecdsa.",
147
148 .rfcMsgDump = "",
149 .rfcB64Signature = "",
150
151 .bits = 256,
152 .flags = 256,
153 .rfcFlags = 0,
154
155 .algorithm = DNSSECKeeper::ECDSA256,
156 .isDeterministic = false,
157
158 #if OPENSSL_VERSION_MAJOR >= 3
159 // OpenSSL 3.0.0 uses a generic key interface which stores the key PKCS#8-encoded.
160 .pem = "-----BEGIN PRIVATE KEY-----\n"
161 "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiyLIPdk3DOIxVmmS\n"
162 "YlmTstbtUPiVlEyDX46psyCwNVShRANCAATy4PsLhMdMz/Du6GvJFJOh4T+MpPvz\n"
163 "+nzndFfQvkTCtmtIsG5ss+IHDuBu9Q/pKwiBrllDgJIDE2ZgD+Bmy5fM\n"
164 "-----END PRIVATE KEY-----\n"
165 #else
166 .pem = "-----BEGIN EC PRIVATE KEY-----\n"
167 "MHcCAQEEIIsiyD3ZNwziMVZpkmJZk7LW7VD4lZRMg1+OqbMgsDVUoAoGCCqGSM49\n"
168 "AwEHoUQDQgAE8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPi\n"
169 "Bw7gbvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==\n"
170 "-----END EC PRIVATE KEY-----\n"
171 #endif
172 };
173 // clang-format on
174
175 /* Ed25519 from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h,
176 * also from rfc8080 section 6.1
177 */
178 // clang-format off
179 static const SignerParams ed25519 = SignerParams{
180 .iscMap = "Algorithm: 15\n"
181 "PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=\n",
182
183 .dsSHA1 = "3612 15 1 "
184 "501249721e1f09a79d30d5c6c4dca1dc1da4ed5d",
185
186 .dsSHA256 = "3612 15 2 "
187 "1b1c8766b2a96566ff196f77c0c4194af86aaa109c5346ff60231a27d2b07ac0",
188
189 .dsSHA384 = "3612 15 4 "
190 "d11831153af4985efbd0ae792c967eb4aff3c35488db95f7"
191 "e2f85dcec74ae8f59f9a72641798c91c67c675db1d710c18",
192
193 /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
194 .signature = {
195 0x0a, 0x9e, 0x51, 0x5f, 0x16, 0x89, 0x49, 0x27, 0x0e, 0x98, 0x34, 0xd3, 0x48,
196 0xef, 0x5a, 0x6e, 0x85, 0x2f, 0x7c, 0xd6, 0xd7, 0xc8, 0xd0, 0xf4, 0x2c, 0x68,
197 0x8c, 0x1f, 0xf7, 0xdf, 0xeb, 0x7c, 0x25, 0xd6, 0x1a, 0x76, 0x3e, 0xaf, 0x28,
198 0x1f, 0x1d, 0x08, 0x10, 0x20, 0x1c, 0x01, 0x77, 0x1b, 0x5a, 0x48, 0xd6, 0xe5,
199 0x1c, 0xf9, 0xe3, 0xe0, 0x70, 0x34, 0x5e, 0x02, 0x49, 0xfb, 0x9e, 0x05
200 },
201
202 .zoneRepresentation = "256 3 15 l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4=",
203
204 .name = "ed25519.",
205
206 // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev
207 // 476d6ded) by printing signature_data
208 .rfcMsgDump = "00 0f 0f 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 0e 1d 07 65 78 "
209 "61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f "
210 "6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 "
211 "78 61 6d 70 6c 65 03 63 6f 6d 00 ",
212
213 // vector verified from dnskey.py as above, and confirmed with
214 // https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
215 .rfcB64Signature = "oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeR"
216 "AvTdszaPD+QLs3fx8A4M3e23mRZ9VrbpMngwcrqNAg==",
217
218 .bits = 256,
219 .flags = 256,
220 .rfcFlags = 257,
221
222 .algorithm = DNSSECKeeper::ED25519,
223 .isDeterministic = true,
224
225 .pem = "-----BEGIN PRIVATE KEY-----\n"
226 "MC4CAQAwBQYDK2VwBCIEIDgyMjYwMzg0NjI4MDgwMTIyNjQ1MTkwMjA0MTQyMjYy\n"
227 "-----END PRIVATE KEY-----\n"
228 };
229 // clang-format on
230
231 /* Ed448.
232 */
233 // clang-format off
234 static const SignerParams ed448 = SignerParams{
235 .iscMap = "Private-key-format: v1.2\n"
236 "Algorithm: 16 (ED448)\n"
237 "PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA\n",
238
239 .dsSHA1 = "9712 16 1 "
240 "2873e800eb2d784cdd1802f884b3c540b573eaa0",
241
242 .dsSHA256 = "9712 16 2 "
243 "9aa27306f8a04a0a6fae8affd65d6f35875dcb134c05bd7c7b61bd0dc44009cd",
244
245 .dsSHA384 = "9712 16 4 "
246 "3876e5d892d3f31725f9964a332f9b9afd791171833480f2"
247 "e71af78efb985cde9900ba95315287123a5908ca8f334369",
248
249 .signature = {
250 0xb5, 0xcc, 0x21, 0x5a, 0x52, 0x21, 0x60, 0xa3, 0xb8, 0xd9, 0x3a, 0xd7, 0x05,
251 0xdd, 0x4a, 0x32, 0x96, 0xce, 0x08, 0xde, 0x74, 0x5f, 0xdb, 0xde, 0x54, 0x95,
252 0x97, 0x93, 0x6f, 0x3a, 0x4a, 0x34, 0x41, 0x14, 0xba, 0x99, 0x86, 0x0d, 0xe2,
253 0x99, 0xf1, 0x14, 0x6a, 0x1b, 0x7a, 0xfa, 0xef, 0xab, 0x62, 0xd2, 0x71, 0x85,
254 0xae, 0xd1, 0x84, 0x80, 0x00, 0x50, 0x03, 0x9e, 0x73, 0x53, 0xe8, 0x9e, 0x19,
255 0xb8, 0xc0, 0xdb, 0xd4, 0xf0, 0x1e, 0x44, 0x4c, 0xb7, 0x32, 0x07, 0xda, 0x0b,
256 0x64, 0x22, 0xa8, 0x63, 0xaa, 0x7a, 0x12, 0x73, 0xc9, 0x29, 0xfd, 0x50, 0x85,
257 0x0f, 0x43, 0x72, 0x77, 0x86, 0xec, 0x88, 0x1a, 0x96, 0x95, 0x4a, 0x01, 0xfe,
258 0xf2, 0xe6, 0x77, 0x4a, 0x2e, 0x43, 0xdd, 0x60, 0x29, 0x00,
259 },
260
261 .zoneRepresentation = "256 3 16 "
262 "3kgROaDjrh0H2iuixWBrc8g2EpBBLCdGzHmn+"
263 "G2MpTPhpj/OiBVHHSfPodx1FYYUcJKm1MDpJtIA",
264
265 .name = "ed448.",
266
267 // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev
268 // 476d6ded) by printing signature_data
269 .rfcMsgDump = "00 0f 10 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 25 f1 07 65 78 "
270 "61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f "
271 "6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 "
272 "78 61 6d 70 6c 65 03 63 6f 6d 00 ",
273
274 // vector verified from dnskey.py as above, and confirmed with
275 // https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
276 .rfcB64Signature = "3cPAHkmlnxcDHMyg7vFC34l0blBhuG1qpwLmjInI8w1CMB29FkEA"
277 "IJUA0amxWndkmnBZ6SKiwZSAxGILn/NBtOXft0+Gj7FSvOKxE/07"
278 "+4RQvE581N3Aj/JtIyaiYVdnYtyMWbSNyGEY2213WKsJlwEA",
279
280 .bits = 456,
281 .flags = 256,
282 .rfcFlags = 257,
283
284 .algorithm = DNSSECKeeper::ED448,
285 .isDeterministic = true,
286
287 .pem = "-----BEGIN PRIVATE KEY-----\n"
288 "MEcCAQAwBQYDK2VxBDsEOcWfuQoJuOt8boLZGOQdCcenqxU16dd62HoLLdKkbTbD\n"
289 "mJ2laTMdGf3ORIgPcfMFmww3Lf1NxYWFgA==\n"
290 "-----END PRIVATE KEY-----\n"
291 };
292 // clang-format on
293
294 struct Fixture
295 {
296 Fixture()
297 {
298 BOOST_TEST_MESSAGE("All available/supported algorithms:");
299 auto pairs = DNSCryptoKeyEngine::listAllAlgosWithBackend();
300 for (auto const& pair : pairs) {
301 BOOST_TEST_MESSAGE(" " + std::to_string(pair.first) + ": " + pair.second);
302 }
303
304 BOOST_TEST_MESSAGE("Setting up signer params:");
305
306 addSignerParams(DNSSECKeeper::RSASHA256, "RSA SHA256", rsaSha256SignerParams);
307
308 #ifdef HAVE_LIBCRYPTO_ECDSA
309 addSignerParams(DNSSECKeeper::ECDSA256, "ECDSA SHA256", ecdsaSha256);
310 #endif
311
312 // We need to have HAVE_LIBCRYPTO_ED25519 for the PEM reader/writer.
313 #if defined(HAVE_LIBCRYPTO_ED25519)
314 addSignerParams(DNSSECKeeper::ED25519, "ED25519", ed25519);
315 #endif
316
317 #if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
318 addSignerParams(DNSSECKeeper::ED448, "ED448", ed448);
319 #endif
320 }
321
322 void addSignerParams(const uint8_t algorithm, const std::string& name, const SignerParams& params)
323 {
324 BOOST_TEST_MESSAGE(" " + std::to_string(algorithm) + ": " + name + " (" + params.name + ")");
325 signerParams.insert_or_assign(algorithm, params);
326 }
327
328 const std::string message{"Very good, young padawan."};
329 std::unordered_map<uint8_t, struct SignerParams> signerParams;
330 };
331
332 static void checkRR(const SignerParams& signer)
333 {
334 DNSKEYRecordContent drc;
335 auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(drc, signer.iscMap));
336 DNSSECPrivateKey dpk;
337 dpk.setKey(dcke, signer.rfcFlags);
338
339 sortedRecords_t rrs;
340 /* values taken from rfc8080 for ed25519 and ed448, rfc5933 for gost */
341 DNSName qname(dpk.getAlgorithm() == DNSSECKeeper::ECCGOST ? "www.example.net." : "example.com.");
342
343 reportBasicTypes();
344
345 RRSIGRecordContent rrc;
346 uint32_t expire = 1440021600;
347 uint32_t inception = 1438207200;
348
349 if (dpk.getAlgorithm() == DNSSECKeeper::ECCGOST) {
350 rrc.d_signer = DNSName("example.net.");
351 inception = 946684800;
352 expire = 1893456000;
353 rrs.insert(DNSRecordContent::mastermake(QType::A, QClass::IN, "192.0.2.1"));
354 }
355 else {
356 rrc.d_signer = qname;
357 rrs.insert(DNSRecordContent::mastermake(QType::MX, QClass::IN, "10 mail.example.com."));
358 }
359
360 rrc.d_originalttl = 3600;
361 rrc.d_sigexpire = expire;
362 rrc.d_siginception = inception;
363 rrc.d_type = (*rrs.cbegin())->getType();
364 rrc.d_labels = qname.countLabels();
365 rrc.d_tag = dpk.getTag();
366 rrc.d_algorithm = dpk.getAlgorithm();
367
368 string msg = getMessageForRRSET(qname, rrc, rrs, false);
369
370 BOOST_CHECK_EQUAL(makeHexDump(msg), signer.rfcMsgDump);
371
372 string signature = dcke->sign(msg);
373
374 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): Boost stuff.
375 BOOST_CHECK(dcke->verify(msg, signature));
376
377 if (signer.isDeterministic) {
378 string b64 = Base64Encode(signature);
379 BOOST_CHECK_EQUAL(b64, signer.rfcB64Signature);
380 }
381 else {
382 std::string raw;
383 B64Decode(signer.rfcB64Signature, raw);
384 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): Boost stuff.
385 BOOST_CHECK(dcke->verify(msg, raw));
386 }
387 }
388
389 static void test_generic_signer(std::shared_ptr<DNSCryptoKeyEngine> dcke, DNSKEYRecordContent& drc, const SignerParams& signer, const std::string& message)
390 {
391 BOOST_CHECK_EQUAL(dcke->getAlgorithm(), signer.algorithm);
392 BOOST_CHECK_EQUAL(dcke->getBits(), signer.bits);
393
394 vector<string> errorMessages{};
395 BOOST_CHECK_EQUAL(dcke->checkKey(errorMessages), true);
396 if (!errorMessages.empty()) {
397 BOOST_TEST_MESSAGE("Errors from " + dcke->getName() + " checkKey()");
398 for (auto& errorMessage : errorMessages) {
399 BOOST_TEST_MESSAGE(" " + errorMessage);
400 }
401 }
402
403 BOOST_CHECK_EQUAL(drc.d_algorithm, signer.algorithm);
404
405 DNSSECPrivateKey dpk;
406 dpk.setKey(dcke, signer.flags);
407 drc = dpk.getDNSKEY();
408
409 BOOST_CHECK_EQUAL(drc.d_algorithm, signer.algorithm);
410 BOOST_CHECK_EQUAL(drc.d_protocol, 3);
411 BOOST_CHECK_EQUAL(drc.getZoneRepresentation(), signer.zoneRepresentation);
412
413 DNSName name(signer.name);
414 auto ds1 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA1);
415 if (!signer.dsSHA1.empty()) {
416 BOOST_CHECK_EQUAL(ds1.getZoneRepresentation(), signer.dsSHA1);
417 }
418
419 auto ds2 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA256);
420 if (!signer.dsSHA256.empty()) {
421 BOOST_CHECK_EQUAL(ds2.getZoneRepresentation(), signer.dsSHA256);
422 }
423
424 auto ds4 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA384);
425 if (!signer.dsSHA384.empty()) {
426 BOOST_CHECK_EQUAL(ds4.getZoneRepresentation(), signer.dsSHA384);
427 }
428
429 auto signature = dcke->sign(message);
430 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): Boost stuff.
431 BOOST_CHECK(dcke->verify(message, signature));
432
433 auto signerSignature = std::string(signer.signature.begin(), signer.signature.end());
434 if (signer.isDeterministic) {
435 auto signatureBase64 = Base64Encode(signature);
436 auto signerSignatureBase64 = Base64Encode(signerSignature);
437 BOOST_CHECK_EQUAL(signatureBase64, signerSignatureBase64);
438 }
439 else {
440 /* since the signing process is not deterministic, we can't directly compare our signature
441 with the one we have. Still the one we have should also validate correctly. */
442 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): Boost stuff.
443 BOOST_CHECK(dcke->verify(message, signerSignature));
444 }
445
446 if (!signer.rfcMsgDump.empty() && !signer.rfcB64Signature.empty()) {
447 checkRR(signer);
448 }
449 }
450
451 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables,readability-identifier-length): Boost stuff.
452 BOOST_FIXTURE_TEST_CASE(test_generic_signers, Fixture)
453 {
454 for (const auto& algoSignerPair : signerParams) {
455 auto signer = algoSignerPair.second;
456
457 DNSKEYRecordContent drc;
458 auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(drc, signer.iscMap));
459 test_generic_signer(dcke, drc, signer, message);
460
461 DNSKEYRecordContent pemDRC;
462 shared_ptr<DNSCryptoKeyEngine> pemKey{DNSCryptoKeyEngine::makeFromPEMString(pemDRC, signer.algorithm, signer.pem)};
463
464 BOOST_CHECK_EQUAL(pemKey->convertToISC(), dcke->convertToISC());
465
466 test_generic_signer(pemKey, pemDRC, signer, message);
467
468 auto dckePEMOutput = dcke->convertToPEMString();
469 BOOST_CHECK_EQUAL(dckePEMOutput, signer.pem);
470
471 auto pemKeyOutput = pemKey->convertToPEMString();
472 BOOST_CHECK_EQUAL(pemKeyOutput, signer.pem);
473 }
474 }
475
476 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables,readability-identifier-length): Boost stuff.
477 BOOST_AUTO_TEST_CASE(test_hash_qname_with_salt)
478 {
479 {
480 // rfc5155 appendix A
481 const unsigned char salt[] = {0xaa, 0xbb, 0xcc, 0xdd};
482 const unsigned int iterations{12};
483 const std::vector<std::pair<std::string, std::string>> namesToHashes = {
484 {"example", "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom"},
485 {"a.example", "35mthgpgcu1qg68fab165klnsnk3dpvl"},
486 {"ai.example", "gjeqe526plbf1g8mklp59enfd789njgi"},
487 {"ns1.example", "2t7b4g4vsa5smi47k61mv5bv1a22bojr"},
488 {"ns2.example", "q04jkcevqvmu85r014c7dkba38o0ji5r"},
489 {"w.example", "k8udemvp1j2f7eg6jebps17vp3n8i58h"},
490 {"*.w.example", "r53bq7cc2uvmubfu5ocmm6pers9tk9en"},
491 {"x.w.example", "b4um86eghhds6nea196smvmlo4ors995"},
492 {"y.w.example", "ji6neoaepv8b5o6k4ev33abha8ht9fgc"},
493 {"x.y.w.example", "2vptu5timamqttgl4luu9kg21e0aor3s"},
494 {"xx.example", "t644ebqk9bibcna874givr6joj62mlhv"},
495 {"2t7b4g4vsa5smi47k61mv5bv1a22bojr.example", "kohar7mbb8dc2ce8a9qvl8hon4k53uhi"},
496 };
497
498 for (const auto& [name, expectedHash] : namesToHashes) {
499 auto hash = hashQNameWithSalt(std::string(reinterpret_cast<const char*>(salt), sizeof(salt)), iterations, DNSName(name));
500 BOOST_CHECK_EQUAL(toBase32Hex(hash), expectedHash);
501 }
502 }
503
504 {
505 /* no additional iterations, very short salt */
506 const unsigned char salt[] = {0xFF};
507 const unsigned int iterations{0};
508 const std::vector<std::pair<std::string, std::string>> namesToHashes = {
509 {"example", "s9dp8o2l6jgqgg26ecobtjooe7p019cs"},
510 };
511
512 for (const auto& [name, expectedHash] : namesToHashes) {
513 auto hash = hashQNameWithSalt(std::string(reinterpret_cast<const char*>(salt), sizeof(salt)), iterations, DNSName(name));
514 BOOST_CHECK_EQUAL(toBase32Hex(hash), expectedHash);
515 }
516 }
517
518 {
519 /* only one iteration */
520 const unsigned char salt[] = {0xaa, 0xbb, 0xcc, 0xdd};
521 const unsigned int iterations{1};
522 const std::vector<std::pair<std::string, std::string>> namesToHashes = {
523 {"example", "ulddquehrj5jpf50ga76vgqr1oq40133"},
524 };
525
526 for (const auto& [name, expectedHash] : namesToHashes) {
527 auto hash = hashQNameWithSalt(std::string(reinterpret_cast<const char*>(salt), sizeof(salt)), iterations, DNSName(name));
528 BOOST_CHECK_EQUAL(toBase32Hex(hash), expectedHash);
529 }
530 }
531
532 {
533 /* 65535 iterations, long salt */
534 unsigned char salt[255];
535 for (unsigned char idx = 0; idx < 255; idx++) {
536 salt[idx] = idx;
537 };
538 const unsigned int iterations{65535};
539 const std::vector<std::pair<std::string, std::string>> namesToHashes = {
540 {"example", "no95j4cfile8avstr7bn4aj9he18trri"},
541 };
542
543 for (const auto& [name, expectedHash] : namesToHashes) {
544 auto hash = hashQNameWithSalt(std::string(reinterpret_cast<const char*>(salt), sizeof(salt)), iterations, DNSName(name));
545 BOOST_CHECK_EQUAL(toBase32Hex(hash), expectedHash);
546 }
547 }
548 }
549
550 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables): Boost stuff.
551 BOOST_AUTO_TEST_SUITE_END()