]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/test-signers.cc
auth: Fix 'loop variable [...] creates a copy' warnings
[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 <boost/tuple/tuple.hpp>
10
11 #include "base64.hh"
12 #include "dnsseckeeper.hh"
13 #include "dnssecinfra.hh"
14 #include "misc.hh"
15 BOOST_AUTO_TEST_SUITE(test_signers)
16
17 static const std::string message = "Very good, young padawan.";
18
19 static const struct signerParams
20 {
21 std::string iscMap;
22 std::string dsSHA1;
23 std::string dsSHA256;
24 std::string dsSHA384;
25 std::vector<uint8_t> signature;
26 std::string zoneRepresentation;
27 std::string name;
28 std::string rfcMsgDump;
29 std::string rfcB64Signature;
30 int bits;
31 uint16_t flags;
32 uint16_t rfcFlags;
33 uint8_t algorithm;
34 bool isDeterministic;
35 } signers[] = {
36 /* RSA from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h */
37 { "Algorithm: 8\n"
38 "Modulus: qtunSiHnYq4XRLBehKAw1Glxb+48oIpAC7w3Jhpj570bb2uHt6orWGqnuyRtK8oqUi2ABoV0PFm8+IPgDMEdCQ==\n"
39 "PublicExponent: AQAB\n"
40 "PrivateExponent: MiItniUAngXzMeaGdWgDq/AcpvlCtOCcFlVt4TJRKkfp8DNRSxIxG53NNlOFkp1W00iLHqYC2GrH1qkKgT9l+Q==\n"
41 "Prime1: 3sZmM+5FKFy5xaRt0n2ZQOZ2C+CoKzVil6/al9LmYVs=\n"
42 "Prime2: xFcNWSIW6v8dDL2JQ1kxFDm/8RVeUSs1BNXXnvCjBGs=\n"
43 "Exponent1: WuUwhjfN1+4djlrMxHmisixWNfpwI1Eg7Ss/UXsnrMk=\n"
44 "Exponent2: vfMqas1cNsXRqP3Fym6D2Pl2BRuTQBv5E1B/ZrmQPTk=\n"
45 "Coefficient: Q10z43cA3hkwOkKsj5T0W5jrX97LBwZoY5lIjDCa4+M=\n",
46 "1506 8 1 172a500b374158d1a64ba3073cdbbc319b2fdf2c",
47 "1506 8 2 253b099ff47b02c6ffa52695a30a94c6681c56befe0e71a5077d6f79514972f9",
48 "1506 8 4 22ea940600dc2d9a98b1126c26ac0dc5c91b31eb50fe784b36ad675e9eecfe6573c1f85c53b6bc94580f3ac443d13c4c",
49 /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
50 { 0x93, 0x93, 0x5f, 0xd8, 0xa1, 0x2b, 0x4c, 0x0b, 0xf3, 0x67, 0x42, 0x13, 0x52, 0x00, 0x35, 0xdc, 0x09, 0xe0, 0xdf, 0xe0, 0x3e, 0xc2, 0xcf, 0x64, 0xab, 0x9f, 0x9f, 0x51, 0x5f, 0x5c, 0x27, 0xbe, 0x13, 0xd6, 0x17, 0x07, 0xa6, 0xe4, 0x3b, 0x63, 0x44, 0x85, 0x06, 0x13, 0xaa, 0x01, 0x3c, 0x58, 0x52, 0xa3, 0x98, 0x20, 0x65, 0x03, 0xd0, 0x40, 0xc8, 0xa0, 0xe9, 0xd2, 0xc0, 0x03, 0x5a, 0xab },
51 "256 3 8 AwEAAarbp0oh52KuF0SwXoSgMNRpcW/uPKCKQAu8NyYaY+e9G29rh7eqK1hqp7skbSvKKlItgAaFdDxZvPiD4AzBHQk=",
52 "rsa.",
53 "",
54 "",
55 512,
56 256,
57 0,
58 DNSSECKeeper::RSASHA256,
59 true
60 },
61 #ifdef HAVE_LIBCRYPTO_ECDSA
62 /* ECDSA-P256-SHA256 from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h */
63 { "Algorithm: 13\n"
64 "PrivateKey: iyLIPdk3DOIxVmmSYlmTstbtUPiVlEyDX46psyCwNVQ=\n",
65 "5345 13 1 954103ac7c43810ce9f414e80f30ab1cbe49b236",
66 "5345 13 2 bac2107036e735b50f85006ce409a19a3438cab272e70769ebda032239a3d0ca",
67 "5345 13 4 a0ac6790483872be72a258314200a88ab75cdd70f66a18a09f0f414c074df0989fdb1df0e67d82d4312cda67b93a76c1",
68 /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
69 { 0xa2, 0x95, 0x76, 0xb5, 0xf5, 0x7e, 0xbd, 0xdd, 0xf5, 0x62, 0xa2, 0xc3, 0xa4, 0x8d, 0xd4, 0x53, 0x5c, 0xba, 0x29, 0x71, 0x8c, 0xcc, 0x28, 0x7b, 0x58, 0xf3, 0x1e, 0x4e, 0x58, 0xe2, 0x36, 0x7e, 0xa0, 0x1a, 0xb6, 0xe6, 0x29, 0x71, 0x1b, 0xd3, 0x8c, 0x88, 0xc3, 0xee, 0x12, 0x0e, 0x69, 0x70, 0x55, 0x99, 0xec, 0xd5, 0xf6, 0x4f, 0x4b, 0xe2, 0x41, 0xd9, 0x10, 0x7e, 0x67, 0xe5, 0xad, 0x2f, },
70 "256 3 13 8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPiBw7gbvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==",
71 "ecdsa.",
72 "",
73 "",
74 256,
75 256,
76 0,
77 DNSSECKeeper::ECDSA256,
78 false
79 },
80 #endif /* HAVE_LIBCRYPTO_ECDSA */
81 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519)
82 /* ed25519 from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h,
83 also from rfc8080 section 6.1 */
84 { "Algorithm: 15\n"
85 "PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=\n",
86 "3612 15 1 501249721e1f09a79d30d5c6c4dca1dc1da4ed5d",
87 "3612 15 2 1b1c8766b2a96566ff196f77c0c4194af86aaa109c5346ff60231a27d2b07ac0",
88 "3612 15 4 d11831153af4985efbd0ae792c967eb4aff3c35488db95f7e2f85dcec74ae8f59f9a72641798c91c67c675db1d710c18",
89 /* from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sign.c */
90 { 0x0a, 0x9e, 0x51, 0x5f, 0x16, 0x89, 0x49, 0x27, 0x0e, 0x98, 0x34, 0xd3, 0x48, 0xef, 0x5a, 0x6e, 0x85, 0x2f, 0x7c, 0xd6, 0xd7, 0xc8, 0xd0, 0xf4, 0x2c, 0x68, 0x8c, 0x1f, 0xf7, 0xdf, 0xeb, 0x7c, 0x25, 0xd6, 0x1a, 0x76, 0x3e, 0xaf, 0x28, 0x1f, 0x1d, 0x08, 0x10, 0x20, 0x1c, 0x01, 0x77, 0x1b, 0x5a, 0x48, 0xd6, 0xe5, 0x1c, 0xf9, 0xe3, 0xe0, 0x70, 0x34, 0x5e, 0x02, 0x49, 0xfb, 0x9e, 0x05 },
91 "256 3 15 l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4=",
92 "ed25519.",
93 // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev 476d6ded) by printing signature_data
94 "00 0f 0f 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 0e 1d 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 ",
95 // vector verified from dnskey.py as above, and confirmed with https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
96 "oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3fx8A4M3e23mRZ9VrbpMngwcrqNAg==",
97 256,
98 256,
99 257,
100 DNSSECKeeper::ED25519,
101 true
102 },
103 #endif /* defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519) */
104 };
105
106 static void checkRR(const signerParams& signer)
107 {
108 DNSKEYRecordContent drc;
109 auto dcke = DNSCryptoKeyEngine::makeFromISCString(drc, signer.iscMap);
110 DNSSECPrivateKey dpk;
111 dpk.setKey(dcke);
112 dpk.d_flags = signer.rfcFlags;
113
114 sortedRecords_t rrs;
115 /* values taken from rfc8080 for ed25519 and ed448, rfc5933 for gost */
116 DNSName qname(dpk.d_algorithm == 12 ? "www.example.net." : "example.com.");
117
118 reportBasicTypes();
119
120 RRSIGRecordContent rrc;
121 uint32_t expire = 1440021600;
122 uint32_t inception = 1438207200;
123
124 if (dpk.d_algorithm == 12) {
125 rrc.d_signer = DNSName("example.net.");
126 inception = 946684800;
127 expire = 1893456000;
128 rrs.insert(DNSRecordContent::mastermake(QType::A, QClass::IN, "192.0.2.1"));
129 }
130 else {
131 rrc.d_signer = qname;
132 rrs.insert(DNSRecordContent::mastermake(QType::MX, QClass::IN, "10 mail.example.com."));
133 }
134
135 rrc.d_originalttl = 3600;
136 rrc.d_sigexpire = expire;
137 rrc.d_siginception = inception;
138 rrc.d_type = (*rrs.cbegin())->getType();
139 rrc.d_labels = qname.countLabels();
140 rrc.d_tag = dpk.getTag();
141 rrc.d_algorithm = dpk.d_algorithm;
142
143 string msg = getMessageForRRSET(qname, rrc, rrs, false);
144
145 BOOST_CHECK_EQUAL(makeHexDump(msg), signer.rfcMsgDump);
146
147 string signature = dcke->sign(msg);
148
149 BOOST_CHECK(dcke->verify(msg, signature));
150
151 if (signer.isDeterministic) {
152 string b64 = Base64Encode(signature);
153 BOOST_CHECK_EQUAL(b64, signer.rfcB64Signature);
154 }
155 else {
156 std::string raw;
157 B64Decode(signer.rfcB64Signature, raw);
158 BOOST_CHECK(dcke->verify(msg, raw));
159 }
160 }
161
162 BOOST_AUTO_TEST_CASE(test_generic_signers)
163 {
164 for (const auto& signer : signers) {
165 DNSKEYRecordContent drc;
166 auto dcke = DNSCryptoKeyEngine::makeFromISCString(drc, signer.iscMap);
167
168 BOOST_CHECK_EQUAL(dcke->getAlgorithm(), signer.algorithm);
169 BOOST_CHECK_EQUAL(dcke->getBits(), signer.bits);
170 BOOST_CHECK_EQUAL(dcke->checkKey(nullptr), true);
171
172 BOOST_CHECK_EQUAL(drc.d_algorithm, signer.algorithm);
173
174 DNSSECPrivateKey dpk;
175 dpk.setKey(dcke);
176 dpk.d_flags = signer.flags;
177 drc = dpk.getDNSKEY();
178
179 BOOST_CHECK_EQUAL(drc.d_algorithm, signer.algorithm);
180 BOOST_CHECK_EQUAL(drc.d_protocol, 3);
181 BOOST_CHECK_EQUAL(drc.getZoneRepresentation(), signer.zoneRepresentation);
182
183 DNSName name(signer.name);
184 auto ds1 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA1);
185 if (!signer.dsSHA1.empty()) {
186 BOOST_CHECK_EQUAL(ds1.getZoneRepresentation(), signer.dsSHA1);
187 }
188
189 auto ds2 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA256);
190 if (!signer.dsSHA256.empty()) {
191 BOOST_CHECK_EQUAL(ds2.getZoneRepresentation(), signer.dsSHA256);
192 }
193
194 auto ds4 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA384);
195 if (!signer.dsSHA384.empty()) {
196 BOOST_CHECK_EQUAL(ds4.getZoneRepresentation(), signer.dsSHA384);
197 }
198
199 auto signature = dcke->sign(message);
200 BOOST_CHECK(dcke->verify(message, signature));
201
202 if (signer.isDeterministic) {
203 BOOST_CHECK_EQUAL(signature, std::string(signer.signature.begin(), signer.signature.end()));
204 } else {
205 /* since the signing process is not deterministic, we can't directly compare our signature
206 with the one we have. Still the one we have should also validate correctly. */
207 BOOST_CHECK(dcke->verify(message, std::string(signer.signature.begin(), signer.signature.end())));
208 }
209
210 if (!signer.rfcMsgDump.empty() && !signer.rfcB64Signature.empty()) {
211 checkRR(signer);
212 }
213 }
214 }
215
216 #if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
217 BOOST_AUTO_TEST_CASE(test_ed448_signer) {
218 sortedRecords_t rrs;
219 DNSName qname("example.com.");
220 DNSKEYRecordContent drc;
221
222 // TODO: make this a collection of inputs and resulting sigs for various algos
223 shared_ptr<DNSCryptoKeyEngine> engine = DNSCryptoKeyEngine::makeFromISCString(drc,
224 "Private-key-format: v1.2\n"
225 "Algorithm: 16 (ED448)\n"
226 "PrivateKey: xZ+5Cgm463xugtkY5B0Jx6erFTXp13rYegst0qRtNsOYnaVpMx0Z/c5EiA9x8wWbDDct/U3FhYWA\n");
227
228 DNSSECPrivateKey dpk;
229 dpk.setKey(engine);
230
231 reportBasicTypes();
232
233 rrs.insert(DNSRecordContent::mastermake(QType::MX, 1, "10 mail.example.com."));
234
235 RRSIGRecordContent rrc;
236 rrc.d_originalttl = 3600;
237 rrc.d_sigexpire = 1440021600;
238 rrc.d_siginception = 1438207200;
239 rrc.d_signer = qname;
240 rrc.d_type = QType::MX;
241 rrc.d_labels = 2;
242 // TODO: derive the next two from the key
243 rrc.d_tag = 9713;
244 rrc.d_algorithm = 16;
245
246 string msg = getMessageForRRSET(qname, rrc, rrs, false);
247
248 // vector extracted from https://gitlab.labs.nic.cz/labs/ietf/blob/master/dnskey.py (rev 476d6ded) by printing signature_data
249 BOOST_CHECK_EQUAL(makeHexDump(msg), "00 0f 10 02 00 00 0e 10 55 d4 fc 60 55 b9 4c e0 25 f1 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 0f 00 01 00 00 0e 10 00 14 00 0a 04 6d 61 69 6c 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 ");
250
251 string signature = engine->sign(msg);
252 string b64 = Base64Encode(signature);
253
254 // vector verified from dnskey.py as above, and confirmed with https://www.rfc-editor.org/errata_search.php?rfc=8080&eid=4935
255 BOOST_CHECK_EQUAL(b64, "3cPAHkmlnxcDHMyg7vFC34l0blBhuG1qpwLmjInI8w1CMB29FkEAIJUA0amxWndkmnBZ6SKiwZSAxGILn/NBtOXft0+Gj7FSvOKxE/07+4RQvE581N3Aj/JtIyaiYVdnYtyMWbSNyGEY2213WKsJlwEA");
256 }
257 #endif /* defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448) */
258
259 BOOST_AUTO_TEST_SUITE_END()