1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
6 #include <boost/test/unit_test.hpp>
7 #include <boost/assign/list_of.hpp>
9 #include <boost/tuple/tuple.hpp>
12 #include "dnsseckeeper.hh"
13 #include "dnssecinfra.hh"
15 BOOST_AUTO_TEST_SUITE(test_signers
)
17 static const std::string message
= "Very good, young padawan.";
19 static const struct signerParams
25 std::vector
<uint8_t> signature
;
26 std::string zoneRepresentation
;
28 std::string rfcMsgDump
;
29 std::string rfcB64Signature
;
36 /* RSA from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h */
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=",
58 DNSSECKeeper::RSASHA256
,
61 #ifdef HAVE_LIBCRYPTO_ECDSA
62 /* ECDSA-P256-SHA256 from https://github.com/CZ-NIC/knot/blob/master/src/dnssec/tests/sample_keys.h */
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==",
77 DNSSECKeeper::ECDSA256
,
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 */
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=",
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==",
100 DNSSECKeeper::ED25519
,
103 #endif /* defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519) */
106 static void checkRR(const signerParams
& signer
)
108 DNSKEYRecordContent drc
;
109 auto dcke
= DNSCryptoKeyEngine::makeFromISCString(drc
, signer
.iscMap
);
110 DNSSECPrivateKey dpk
;
112 dpk
.d_flags
= signer
.rfcFlags
;
115 /* values taken from rfc8080 for ed25519 and ed448, rfc5933 for gost */
116 DNSName
qname(dpk
.d_algorithm
== 12 ? "www.example.net." : "example.com.");
120 RRSIGRecordContent rrc
;
121 uint32_t expire
= 1440021600;
122 uint32_t inception
= 1438207200;
124 if (dpk
.d_algorithm
== 12) {
125 rrc
.d_signer
= DNSName("example.net.");
126 inception
= 946684800;
128 rrs
.insert(DNSRecordContent::mastermake(QType::A
, QClass::IN
, "192.0.2.1"));
131 rrc
.d_signer
= qname
;
132 rrs
.insert(DNSRecordContent::mastermake(QType::MX
, QClass::IN
, "10 mail.example.com."));
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
;
143 string msg
= getMessageForRRSET(qname
, rrc
, rrs
, false);
145 BOOST_CHECK_EQUAL(makeHexDump(msg
), signer
.rfcMsgDump
);
147 string signature
= dcke
->sign(msg
);
149 BOOST_CHECK(dcke
->verify(msg
, signature
));
151 if (signer
.isDeterministic
) {
152 string b64
= Base64Encode(signature
);
153 BOOST_CHECK_EQUAL(b64
, signer
.rfcB64Signature
);
157 B64Decode(signer
.rfcB64Signature
, raw
);
158 BOOST_CHECK(dcke
->verify(msg
, raw
));
162 BOOST_AUTO_TEST_CASE(test_generic_signers
)
164 for (const auto& signer
: signers
) {
165 DNSKEYRecordContent drc
;
166 auto dcke
= DNSCryptoKeyEngine::makeFromISCString(drc
, signer
.iscMap
);
168 BOOST_CHECK_EQUAL(dcke
->getAlgorithm(), signer
.algorithm
);
169 BOOST_CHECK_EQUAL(dcke
->getBits(), signer
.bits
);
170 BOOST_CHECK_EQUAL(dcke
->checkKey(nullptr), true);
172 BOOST_CHECK_EQUAL(drc
.d_algorithm
, signer
.algorithm
);
174 DNSSECPrivateKey dpk
;
176 dpk
.d_flags
= signer
.flags
;
177 drc
= dpk
.getDNSKEY();
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
);
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
);
189 auto ds2
= makeDSFromDNSKey(name
, drc
, DNSSECKeeper::DIGEST_SHA256
);
190 if (!signer
.dsSHA256
.empty()) {
191 BOOST_CHECK_EQUAL(ds2
.getZoneRepresentation(), signer
.dsSHA256
);
194 auto ds4
= makeDSFromDNSKey(name
, drc
, DNSSECKeeper::DIGEST_SHA384
);
195 if (!signer
.dsSHA384
.empty()) {
196 BOOST_CHECK_EQUAL(ds4
.getZoneRepresentation(), signer
.dsSHA384
);
199 auto signature
= dcke
->sign(message
);
200 BOOST_CHECK(dcke
->verify(message
, signature
));
202 if (signer
.isDeterministic
) {
203 BOOST_CHECK_EQUAL(signature
, std::string(signer
.signature
.begin(), signer
.signature
.end()));
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())));
210 if (!signer
.rfcMsgDump
.empty() && !signer
.rfcB64Signature
.empty()) {
216 #if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
217 BOOST_AUTO_TEST_CASE(test_ed448_signer
) {
219 DNSName
qname("example.com.");
220 DNSKEYRecordContent drc
;
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");
228 DNSSECPrivateKey dpk
;
233 rrs
.insert(DNSRecordContent::mastermake(QType::MX
, 1, "10 mail.example.com."));
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
;
242 // TODO: derive the next two from the key
244 rrc
.d_algorithm
= 16;
246 string msg
= getMessageForRRSET(qname
, rrc
, rrs
, false);
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 ");
251 string signature
= engine
->sign(msg
);
252 string b64
= Base64Encode(signature
);
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");
257 #endif /* defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448) */
259 BOOST_AUTO_TEST_SUITE_END()