blob: d03f5727d5322680ae140940a1e63abd8e3e60b5 [file] [log] [blame]
Yingdi Yufe4733a2015-10-22 14:24:12 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento82d6a4c2017-12-23 19:47:20 -05002/*
Davide Pesavento0f830802018-01-16 23:58:58 -05003 * Copyright (c) 2013-2018 Regents of the University of California.
Yingdi Yufe4733a2015-10-22 14:24:12 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
22#include "security/v2/key-chain.hpp"
23#include "security/signing-helpers.hpp"
Alexander Afanasyev574aa862017-01-10 19:53:28 -080024#include "security/verification-helpers.hpp"
Yingdi Yufe4733a2015-10-22 14:24:12 -070025
26#include "boost-test.hpp"
27#include "unit-tests/test-home-env-saver.hpp"
Alexander Afanasyevf8379172017-01-11 16:56:04 -080028#include "identity-management-fixture.hpp"
Yingdi Yufe4733a2015-10-22 14:24:12 -070029
30namespace ndn {
31namespace security {
32namespace v2 {
33namespace tests {
34
35using namespace ndn::tests;
36
37BOOST_AUTO_TEST_SUITE(Security)
38BOOST_AUTO_TEST_SUITE(V2)
39BOOST_FIXTURE_TEST_SUITE(TestKeyChain, TestHomeEnvSaver)
40
41template<class Path>
42class TestHomeAndPibFixture : public TestHomeFixture<Path>
43{
44public:
45 TestHomeAndPibFixture()
46 {
47 unsetenv("NDN_CLIENT_PIB");
48 unsetenv("NDN_CLIENT_TPM");
49 }
Alexander Afanasyev80782e02017-01-04 13:16:54 -080050
51 ~TestHomeAndPibFixture()
52 {
53 const_cast<std::string&>(KeyChain::getDefaultPibLocator()).clear();
54 const_cast<std::string&>(KeyChain::getDefaultTpmLocator()).clear();
55 }
Yingdi Yufe4733a2015-10-22 14:24:12 -070056};
57
58struct PibPathConfigFileHome
59{
60 const std::string PATH = "build/config-file-home/";
61};
62
63BOOST_FIXTURE_TEST_CASE(ConstructorNormalConfig, TestHomeAndPibFixture<PibPathConfigFileHome>)
64{
65 createClientConf({"pib=pib-memory:", "tpm=tpm-memory:"});
66
67 BOOST_REQUIRE_NO_THROW(KeyChain());
68
69 KeyChain keyChain;
70 BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
71 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
72 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
73}
74
75struct PibPathConfigFileEmptyHome
76{
77 const std::string PATH = "build/config-file-empty-home/";
78};
79
80BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig, TestHomeAndPibFixture<PibPathConfigFileEmptyHome>)
81{
82 createClientConf({"pib=pib-memory:"});
83
Alexander Afanasyev0cf887d2017-03-26 16:58:59 -050084#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
Yingdi Yufe4733a2015-10-22 14:24:12 -070085 std::string oldHOME;
86 if (std::getenv("OLD_HOME"))
87 oldHOME = std::getenv("OLD_HOME");
88
89 std::string HOME;
90 if (std::getenv("HOME"))
91 HOME = std::getenv("HOME");
92
93 if (!oldHOME.empty())
94 setenv("HOME", oldHOME.c_str(), 1);
95 else
96 unsetenv("HOME");
97#endif
98
99 BOOST_REQUIRE_NO_THROW(KeyChain());
100 KeyChain keyChain;
101 BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
102
Alexander Afanasyev0cf887d2017-03-26 16:58:59 -0500103#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
Yingdi Yufe4733a2015-10-22 14:24:12 -0700104 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-osxkeychain:");
105 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-osxkeychain:");
106#else
107 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-file:");
108 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
109#endif
110
Alexander Afanasyev0cf887d2017-03-26 16:58:59 -0500111#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
Yingdi Yufe4733a2015-10-22 14:24:12 -0700112 if (!HOME.empty())
113 setenv("HOME", HOME.c_str(), 1);
114 else
115 unsetenv("HOME");
116
117 if (!oldHOME.empty())
118 setenv("OLD_HOME", oldHOME.c_str(), 1);
119 else
120 unsetenv("OLD_HOME");
121#endif
122}
123
124struct PibPathConfigFileEmpty2Home
125{
126 const std::string PATH = "build/config-file-empty2-home/";
127};
128
129BOOST_FIXTURE_TEST_CASE(ConstructorEmpty2Config, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>)
130{
131 createClientConf({"tpm=tpm-memory:"});
132
133 BOOST_REQUIRE_NO_THROW(KeyChain());
134
135 KeyChain keyChain;
136 BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
137 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
138 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
139}
140
141struct PibPathConfigFileMalformedHome
142{
143 const std::string PATH = "build/config-file-malformed-home/";
144};
145
146BOOST_FIXTURE_TEST_CASE(ConstructorMalConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>)
147{
148 createClientConf({"pib=lord", "tpm=ring"});
149
150 BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
151}
152
153struct PibPathConfigFileMalformed2Home
154{
155 const std::string PATH = "build/config-file-malformed2-home/";
156};
157
158BOOST_FIXTURE_TEST_CASE(ConstructorMal2Config, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>)
159{
160 createClientConf({"pib=pib-sqlite3:%PATH%", "tpm=just-wrong"});
161
162 BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
163}
164
165BOOST_AUTO_TEST_CASE(KeyChainWithCustomTpmAndPib)
166{
167 BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory", "tpm-memory")));
168 BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:", "tpm-memory:")));
169 BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:/something", "tpm-memory:/something")));
170
171 KeyChain keyChain("pib-memory", "tpm-memory");
172 BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
173 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
174 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
175}
176
Yingdi Yufe4733a2015-10-22 14:24:12 -0700177BOOST_FIXTURE_TEST_CASE(Management, IdentityManagementFixture)
178{
179 Name identityName("/test/id");
180 Name identity2Name("/test/id2");
181
182 BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
183 BOOST_REQUIRE_THROW(m_keyChain.getPib().getDefaultIdentity(), Pib::Error);
184
185 // Create identity
186 Identity id = m_keyChain.createIdentity(identityName);
187 BOOST_CHECK(id);
188 BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) != m_keyChain.getPib().getIdentities().end());
189 // The first added identity becomes the default identity
190 BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getDefaultIdentity());
191 // The default key of the added identity must exist
192 Key key;
193 BOOST_REQUIRE_NO_THROW(key = id.getDefaultKey());
194 // The default certificate of the default key must exist
195 BOOST_REQUIRE_NO_THROW(key.getDefaultCertificate());
196
197 // Delete key
198 Name key1Name = key.getName();
199 BOOST_CHECK_NO_THROW(id.getKey(key1Name));
200 BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
201 m_keyChain.deleteKey(id, key);
202 // The key instance should not be valid any more
203 BOOST_CHECK(!key);
204 BOOST_CHECK_THROW(id.getKey(key1Name), Pib::Error);
205 BOOST_CHECK_EQUAL(id.getKeys().size(), 0);
206
207 // Create another key
208 m_keyChain.createKey(id);
209 // The added key becomes the default key.
210 BOOST_REQUIRE_NO_THROW(id.getDefaultKey());
211 Key key2 = id.getDefaultKey();
212 BOOST_REQUIRE(key2);
213 BOOST_CHECK_NE(key2.getName(), key1Name);
214 BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
215 BOOST_REQUIRE_NO_THROW(key2.getDefaultCertificate());
216
217 // Create the third key
218 Key key3 = m_keyChain.createKey(id);
219 BOOST_CHECK(key3.getName() != key2.getName());
220 // The added key will not be the default key, because the default key already exists
221 BOOST_CHECK(id.getDefaultKey().getName() == key2.getName());
222 BOOST_CHECK_EQUAL(id.getKeys().size(), 2);
223 BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
224
225 // Delete cert
226 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
227 Certificate key3Cert1 = *key3.getCertificates().begin();
228 Name key3CertName = key3Cert1.getName();
229 m_keyChain.deleteCertificate(key3, key3CertName);
230 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 0);
231 BOOST_REQUIRE_THROW(key3.getDefaultCertificate(), Pib::Error);
232
233 // Add cert
234 m_keyChain.addCertificate(key3, key3Cert1);
235 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
236 BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
Alexander Afanasyeva10b2ff2017-01-30 12:44:15 -0800237 m_keyChain.addCertificate(key3, key3Cert1); // overwriting the cert should work
Yingdi Yufe4733a2015-10-22 14:24:12 -0700238 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
239 // Add another cert
240 Certificate key3Cert2 = key3Cert1;
241 Name key3Cert2Name = key3.getName();
242 key3Cert2Name.append("Self");
243 key3Cert2Name.appendVersion();
244 key3Cert2.setName(key3Cert2Name);
245 m_keyChain.addCertificate(key3, key3Cert2);
246 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 2);
247
248 // Default certificate setting
249 BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3CertName);
250 m_keyChain.setDefaultCertificate(key3, key3Cert2);
251 BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3Cert2Name);
252
253 // Default key setting
254 BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
255 m_keyChain.setDefaultKey(id, key3);
256 BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key3.getName());
257
258 // Default identity setting
259 Identity id2 = m_keyChain.createIdentity(identity2Name);
260 BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id.getName());
261 m_keyChain.setDefaultIdentity(id2);
262 BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id2.getName());
263
264 // Delete identity
265 m_keyChain.deleteIdentity(id);
266 // The identity instance should not be valid any more
267 BOOST_CHECK(!id);
268 BOOST_REQUIRE_THROW(m_keyChain.getPib().getIdentity(identityName), Pib::Error);
269 BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) == m_keyChain.getPib().getIdentities().end());
270}
271
272BOOST_FIXTURE_TEST_CASE(GeneralSigningInterface, IdentityManagementFixture)
273{
274 Identity id = addIdentity("/id");
275 Key key = id.getDefaultKey();
276 Certificate cert = key.getDefaultCertificate();
277
278 std::list<SigningInfo> signingInfos = {
279 SigningInfo(),
280
281 SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName()),
282 signingByIdentity(id.getName()),
283
Alexander Afanasyevd6d78aa2017-01-02 18:14:23 -0800284 SigningInfo(id),
285 signingByIdentity(id),
286
Yingdi Yufe4733a2015-10-22 14:24:12 -0700287 SigningInfo(SigningInfo::SIGNER_TYPE_KEY, key.getName()),
288 signingByKey(key.getName()),
289
Alexander Afanasyevd6d78aa2017-01-02 18:14:23 -0800290 SigningInfo(key),
291 signingByKey(key),
292
Yingdi Yufe4733a2015-10-22 14:24:12 -0700293 SigningInfo(SigningInfo::SIGNER_TYPE_CERT, cert.getName()),
294 signingByCertificate(cert.getName()),
Alexander Afanasyevd6d78aa2017-01-02 18:14:23 -0800295 signingByCertificate(cert),
Yingdi Yufe4733a2015-10-22 14:24:12 -0700296
297 SigningInfo(SigningInfo::SIGNER_TYPE_SHA256),
298 signingWithSha256()
299 };
300
301 for (const auto& signingInfo : signingInfos) {
302 BOOST_TEST_MESSAGE("SigningInfo: " << signingInfo);
303 Data data("/data");
304 Interest interest("/interest");
305
306 if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_NULL) {
307 m_keyChain.sign(data);
308 m_keyChain.sign(interest);
309 }
310 else {
311 m_keyChain.sign(data, signingInfo);
312 m_keyChain.sign(interest, signingInfo);
313 }
314
315 Signature interestSignature(interest.getName()[-2].blockFromValue(), interest.getName()[-1].blockFromValue());
316
317 if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_SHA256) {
318 BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::DigestSha256);
319 BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::DigestSha256);
320
Alexander Afanasyev574aa862017-01-10 19:53:28 -0800321 BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
322 BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
Yingdi Yufe4733a2015-10-22 14:24:12 -0700323 }
324 else {
325 BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureSha256WithEcdsa);
326 BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureSha256WithEcdsa);
327
328 BOOST_CHECK_EQUAL(data.getSignature().getKeyLocator().getName(), cert.getName().getPrefix(-2));
329 BOOST_CHECK_EQUAL(interestSignature.getKeyLocator().getName(), cert.getName().getPrefix(-2));
330
Alexander Afanasyev574aa862017-01-10 19:53:28 -0800331 BOOST_CHECK(verifySignature(data, key));
332 BOOST_CHECK(verifySignature(interest, key));
Yingdi Yufe4733a2015-10-22 14:24:12 -0700333 }
334 }
335}
336
337BOOST_FIXTURE_TEST_CASE(PublicKeySigningDefaults, IdentityManagementFixture)
338{
339 Data data("/test/data");
340
341 // Identity will be created with generated key and self-signed cert with default parameters
342 BOOST_CHECK_THROW(m_keyChain.sign(data, signingByIdentity("/non-existing/identity")), KeyChain::InvalidSigningInfoError);
343
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700344 // Create identity with EC key and the corresponding self-signed certificate
345 Identity id = addIdentity("/ndn/test/ec", EcKeyParams());
Yingdi Yufe4733a2015-10-22 14:24:12 -0700346 BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
347 BOOST_CHECK_EQUAL(data.getSignature().getType(),
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700348 KeyChain::getSignatureType(EcKeyParams().getKeyType(), DigestAlgorithm::SHA256));
Yingdi Yufe4733a2015-10-22 14:24:12 -0700349 BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
350
351 // Create identity with RSA key and the corresponding self-signed certificate
352 id = addIdentity("/ndn/test/rsa", RsaKeyParams());
353 BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
354 BOOST_CHECK_EQUAL(data.getSignature().getType(),
355 KeyChain::getSignatureType(RsaKeyParams().getKeyType(), DigestAlgorithm::SHA256));
356 BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
357}
358
359BOOST_FIXTURE_TEST_CASE(ExportImport, IdentityManagementFixture)
360{
361 Identity id = addIdentity("/TestKeyChain/ExportIdentity/");
362 Certificate cert = id.getDefaultKey().getDefaultCertificate();
363
364 shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4);
365 Block block = exported->wireEncode();
366
367 m_keyChain.deleteIdentity(id);
Davide Pesavento82d6a4c2017-12-23 19:47:20 -0500368 BOOST_CHECK_THROW(m_keyChain.exportSafeBag(cert, "1234", 4), KeyChain::Error);
Yingdi Yufe4733a2015-10-22 14:24:12 -0700369
370 BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
371 BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
372
373 SafeBag imported;
374 imported.wireDecode(block);
375 m_keyChain.importSafeBag(imported, "1234", 4);
Davide Pesavento82d6a4c2017-12-23 19:47:20 -0500376 BOOST_CHECK_THROW(m_keyChain.importSafeBag(imported, "1234", 4), KeyChain::Error);
Yingdi Yufe4733a2015-10-22 14:24:12 -0700377
378 BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), true);
379 BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1);
380 BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getIdentity(cert.getIdentity()));
381 Identity newId = m_keyChain.getPib().getIdentity(cert.getIdentity());
382 BOOST_CHECK_EQUAL(newId.getKeys().size(), 1);
383 BOOST_REQUIRE_NO_THROW(newId.getKey(cert.getKeyName()));
384 Key newKey = newId.getKey(cert.getKeyName());
385 BOOST_CHECK_EQUAL(newKey.getCertificates().size(), 1);
386 BOOST_REQUIRE_NO_THROW(newKey.getCertificate(cert.getName()));
387
388 m_keyChain.deleteIdentity(newId);
389 BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
390 BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
391}
392
Alexander Afanasyevf8379172017-01-11 16:56:04 -0800393BOOST_FIXTURE_TEST_CASE(SelfSignedCertValidity, IdentityManagementFixture)
394{
395 Certificate cert = addIdentity("/Security/V2/TestKeyChain/SelfSignedCertValidity")
396 .getDefaultKey()
397 .getDefaultCertificate();
398 BOOST_CHECK(cert.isValid());
Davide Pesavento0f830802018-01-16 23:58:58 -0500399 BOOST_CHECK(cert.isValid(time::system_clock::now() + 10 * 365_days));
400 BOOST_CHECK_GT(cert.getValidityPeriod().getPeriod().second, time::system_clock::now() + 10 * 365_days);
Alexander Afanasyevf8379172017-01-11 16:56:04 -0800401}
402
Yingdi Yufe4733a2015-10-22 14:24:12 -0700403BOOST_AUTO_TEST_SUITE_END() // TestKeyChain
Alexander Afanasyevf8379172017-01-11 16:56:04 -0800404BOOST_AUTO_TEST_SUITE_END() // V2
Yingdi Yufe4733a2015-10-22 14:24:12 -0700405BOOST_AUTO_TEST_SUITE_END() // Security
406
407} // namespace tests
408} // namespace v2
409} // namespace security
410} // namespace ndn