]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/test-signers.cc
Merge pull request #9070 from rgacogne/boost-173
[thirdparty/pdns.git] / pdns / test-signers.cc
CommitLineData
22de9dc8
PD
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>
22de9dc8
PD
10
11#include "base64.hh"
12#include "dnsseckeeper.hh"
13#include "dnssecinfra.hh"
14#include "misc.hh"
22de9dc8
PD
15BOOST_AUTO_TEST_SUITE(test_signers)
16
620d4801
RG
17static const std::string message = "Very good, young padawan.";
18
19static 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;
690b86b7 30 int bits;
620d4801
RG
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 },
620d4801
RG
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 */
439974d7 81#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519)
620d4801
RG
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 },
439974d7 103#endif /* defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519) */
620d4801 104};
22de9dc8 105
620d4801
RG
106static 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;
22de9dc8 113
c1e7b833 114 sortedRecords_t rrs;
620d4801
RG
115 /* values taken from rfc8080 for ed25519 and ed448, rfc5933 for gost */
116 DNSName qname(dpk.d_algorithm == 12 ? "www.example.net." : "example.com.");
22de9dc8 117
620d4801 118 reportBasicTypes();
22de9dc8 119
620d4801
RG
120 RRSIGRecordContent rrc;
121 uint32_t expire = 1440021600;
122 uint32_t inception = 1438207200;
22de9dc8 123
620d4801
RG
124 if (dpk.d_algorithm == 12) {
125 rrc.d_signer = DNSName("example.net.");
126 inception = 946684800;
127 expire = 1893456000;
c1e7b833 128 rrs.insert(DNSRecordContent::mastermake(QType::A, QClass::IN, "192.0.2.1"));
620d4801
RG
129 }
130 else {
22de9dc8 131 rrc.d_signer = qname;
c1e7b833 132 rrs.insert(DNSRecordContent::mastermake(QType::MX, QClass::IN, "10 mail.example.com."));
620d4801 133 }
22de9dc8 134
620d4801
RG
135 rrc.d_originalttl = 3600;
136 rrc.d_sigexpire = expire;
137 rrc.d_siginception = inception;
c1e7b833 138 rrc.d_type = (*rrs.cbegin())->getType();
620d4801
RG
139 rrc.d_labels = qname.countLabels();
140 rrc.d_tag = dpk.getTag();
141 rrc.d_algorithm = dpk.d_algorithm;
22de9dc8 142
620d4801
RG
143 string msg = getMessageForRRSET(qname, rrc, rrs, false);
144
145 BOOST_CHECK_EQUAL(makeHexDump(msg), signer.rfcMsgDump);
146
147 string signature = dcke->sign(msg);
22de9dc8 148
620d4801
RG
149 BOOST_CHECK(dcke->verify(msg, signature));
150
151 if (signer.isDeterministic) {
22de9dc8 152 string b64 = Base64Encode(signature);
620d4801
RG
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}
22de9dc8 161
620d4801
RG
162BOOST_AUTO_TEST_CASE(test_generic_signers)
163{
aa93edd5 164 for (const auto& signer : signers) {
620d4801
RG
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);
ac3f3893 170 BOOST_CHECK_EQUAL(dcke->checkKey(nullptr), true);
620d4801
RG
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);
690b86b7 184 auto ds1 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA1);
620d4801
RG
185 if (!signer.dsSHA1.empty()) {
186 BOOST_CHECK_EQUAL(ds1.getZoneRepresentation(), signer.dsSHA1);
187 }
188
690b86b7 189 auto ds2 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA256);
620d4801
RG
190 if (!signer.dsSHA256.empty()) {
191 BOOST_CHECK_EQUAL(ds2.getZoneRepresentation(), signer.dsSHA256);
192 }
193
690b86b7 194 auto ds4 = makeDSFromDNSKey(name, drc, DNSSECKeeper::DIGEST_SHA384);
620d4801
RG
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 }
22de9dc8 214}
22de9dc8 215
439974d7 216#if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
2b7da695 217BOOST_AUTO_TEST_CASE(test_ed448_signer) {
c1e7b833 218 sortedRecords_t rrs;
2b7da695
KM
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
c1e7b833 233 rrs.insert(DNSRecordContent::mastermake(QType::MX, 1, "10 mail.example.com."));
2b7da695
KM
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}
439974d7 257#endif /* defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448) */
2b7da695 258
22de9dc8 259BOOST_AUTO_TEST_SUITE_END()