blob: 4eaf75ed066e72265929a65b56cb99d98402f3c3 [file] [log] [blame]
Yingdi Yufe4733a2015-10-22 14:24:12 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2017 Regents of the University of California.
4 *
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 }
50};
51
52struct PibPathConfigFileHome
53{
54 const std::string PATH = "build/config-file-home/";
55};
56
57BOOST_FIXTURE_TEST_CASE(ConstructorNormalConfig, TestHomeAndPibFixture<PibPathConfigFileHome>)
58{
59 createClientConf({"pib=pib-memory:", "tpm=tpm-memory:"});
60
61 BOOST_REQUIRE_NO_THROW(KeyChain());
62
63 KeyChain keyChain;
64 BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
65 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
66 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
67}
68
69struct PibPathConfigFileEmptyHome
70{
71 const std::string PATH = "build/config-file-empty-home/";
72};
73
74BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig, TestHomeAndPibFixture<PibPathConfigFileEmptyHome>)
75{
76 createClientConf({"pib=pib-memory:"});
77
78#if defined(NDN_CXX_HAVE_OSX_SECURITY)
79 std::string oldHOME;
80 if (std::getenv("OLD_HOME"))
81 oldHOME = std::getenv("OLD_HOME");
82
83 std::string HOME;
84 if (std::getenv("HOME"))
85 HOME = std::getenv("HOME");
86
87 if (!oldHOME.empty())
88 setenv("HOME", oldHOME.c_str(), 1);
89 else
90 unsetenv("HOME");
91#endif
92
93 BOOST_REQUIRE_NO_THROW(KeyChain());
94 KeyChain keyChain;
95 BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
96
97#if defined(NDN_CXX_HAVE_OSX_SECURITY)
98 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-osxkeychain:");
99 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-osxkeychain:");
100#else
101 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-file:");
102 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
103#endif
104
105#if defined(NDN_CXX_HAVE_OSX_SECURITY)
106 if (!HOME.empty())
107 setenv("HOME", HOME.c_str(), 1);
108 else
109 unsetenv("HOME");
110
111 if (!oldHOME.empty())
112 setenv("OLD_HOME", oldHOME.c_str(), 1);
113 else
114 unsetenv("OLD_HOME");
115#endif
116}
117
118struct PibPathConfigFileEmpty2Home
119{
120 const std::string PATH = "build/config-file-empty2-home/";
121};
122
123BOOST_FIXTURE_TEST_CASE(ConstructorEmpty2Config, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>)
124{
125 createClientConf({"tpm=tpm-memory:"});
126
127 BOOST_REQUIRE_NO_THROW(KeyChain());
128
129 KeyChain keyChain;
130 BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
131 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
132 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
133}
134
135struct PibPathConfigFileMalformedHome
136{
137 const std::string PATH = "build/config-file-malformed-home/";
138};
139
140BOOST_FIXTURE_TEST_CASE(ConstructorMalConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>)
141{
142 createClientConf({"pib=lord", "tpm=ring"});
143
144 BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
145}
146
147struct PibPathConfigFileMalformed2Home
148{
149 const std::string PATH = "build/config-file-malformed2-home/";
150};
151
152BOOST_FIXTURE_TEST_CASE(ConstructorMal2Config, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>)
153{
154 createClientConf({"pib=pib-sqlite3:%PATH%", "tpm=just-wrong"});
155
156 BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
157}
158
159BOOST_AUTO_TEST_CASE(KeyChainWithCustomTpmAndPib)
160{
161 BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory", "tpm-memory")));
162 BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:", "tpm-memory:")));
163 BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:/something", "tpm-memory:/something")));
164
165 KeyChain keyChain("pib-memory", "tpm-memory");
166 BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
167 BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
168 BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
169}
170
Yingdi Yufe4733a2015-10-22 14:24:12 -0700171BOOST_FIXTURE_TEST_CASE(Management, IdentityManagementFixture)
172{
173 Name identityName("/test/id");
174 Name identity2Name("/test/id2");
175
176 BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
177 BOOST_REQUIRE_THROW(m_keyChain.getPib().getDefaultIdentity(), Pib::Error);
178
179 // Create identity
180 Identity id = m_keyChain.createIdentity(identityName);
181 BOOST_CHECK(id);
182 BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) != m_keyChain.getPib().getIdentities().end());
183 // The first added identity becomes the default identity
184 BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getDefaultIdentity());
185 // The default key of the added identity must exist
186 Key key;
187 BOOST_REQUIRE_NO_THROW(key = id.getDefaultKey());
188 // The default certificate of the default key must exist
189 BOOST_REQUIRE_NO_THROW(key.getDefaultCertificate());
190
191 // Delete key
192 Name key1Name = key.getName();
193 BOOST_CHECK_NO_THROW(id.getKey(key1Name));
194 BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
195 m_keyChain.deleteKey(id, key);
196 // The key instance should not be valid any more
197 BOOST_CHECK(!key);
198 BOOST_CHECK_THROW(id.getKey(key1Name), Pib::Error);
199 BOOST_CHECK_EQUAL(id.getKeys().size(), 0);
200
201 // Create another key
202 m_keyChain.createKey(id);
203 // The added key becomes the default key.
204 BOOST_REQUIRE_NO_THROW(id.getDefaultKey());
205 Key key2 = id.getDefaultKey();
206 BOOST_REQUIRE(key2);
207 BOOST_CHECK_NE(key2.getName(), key1Name);
208 BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
209 BOOST_REQUIRE_NO_THROW(key2.getDefaultCertificate());
210
211 // Create the third key
212 Key key3 = m_keyChain.createKey(id);
213 BOOST_CHECK(key3.getName() != key2.getName());
214 // The added key will not be the default key, because the default key already exists
215 BOOST_CHECK(id.getDefaultKey().getName() == key2.getName());
216 BOOST_CHECK_EQUAL(id.getKeys().size(), 2);
217 BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
218
219 // Delete cert
220 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
221 Certificate key3Cert1 = *key3.getCertificates().begin();
222 Name key3CertName = key3Cert1.getName();
223 m_keyChain.deleteCertificate(key3, key3CertName);
224 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 0);
225 BOOST_REQUIRE_THROW(key3.getDefaultCertificate(), Pib::Error);
226
227 // Add cert
228 m_keyChain.addCertificate(key3, key3Cert1);
229 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
230 BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
Alexander Afanasyeva10b2ff2017-01-30 12:44:15 -0800231 m_keyChain.addCertificate(key3, key3Cert1); // overwriting the cert should work
Yingdi Yufe4733a2015-10-22 14:24:12 -0700232 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
233 // Add another cert
234 Certificate key3Cert2 = key3Cert1;
235 Name key3Cert2Name = key3.getName();
236 key3Cert2Name.append("Self");
237 key3Cert2Name.appendVersion();
238 key3Cert2.setName(key3Cert2Name);
239 m_keyChain.addCertificate(key3, key3Cert2);
240 BOOST_CHECK_EQUAL(key3.getCertificates().size(), 2);
241
242 // Default certificate setting
243 BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3CertName);
244 m_keyChain.setDefaultCertificate(key3, key3Cert2);
245 BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3Cert2Name);
246
247 // Default key setting
248 BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
249 m_keyChain.setDefaultKey(id, key3);
250 BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key3.getName());
251
252 // Default identity setting
253 Identity id2 = m_keyChain.createIdentity(identity2Name);
254 BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id.getName());
255 m_keyChain.setDefaultIdentity(id2);
256 BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id2.getName());
257
258 // Delete identity
259 m_keyChain.deleteIdentity(id);
260 // The identity instance should not be valid any more
261 BOOST_CHECK(!id);
262 BOOST_REQUIRE_THROW(m_keyChain.getPib().getIdentity(identityName), Pib::Error);
263 BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) == m_keyChain.getPib().getIdentities().end());
264}
265
266BOOST_FIXTURE_TEST_CASE(GeneralSigningInterface, IdentityManagementFixture)
267{
268 Identity id = addIdentity("/id");
269 Key key = id.getDefaultKey();
270 Certificate cert = key.getDefaultCertificate();
271
272 std::list<SigningInfo> signingInfos = {
273 SigningInfo(),
274
275 SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName()),
276 signingByIdentity(id.getName()),
277
Alexander Afanasyevd6d78aa2017-01-02 18:14:23 -0800278 SigningInfo(id),
279 signingByIdentity(id),
280
Yingdi Yufe4733a2015-10-22 14:24:12 -0700281 SigningInfo(SigningInfo::SIGNER_TYPE_KEY, key.getName()),
282 signingByKey(key.getName()),
283
Alexander Afanasyevd6d78aa2017-01-02 18:14:23 -0800284 SigningInfo(key),
285 signingByKey(key),
286
Yingdi Yufe4733a2015-10-22 14:24:12 -0700287 SigningInfo(SigningInfo::SIGNER_TYPE_CERT, cert.getName()),
288 signingByCertificate(cert.getName()),
Alexander Afanasyevd6d78aa2017-01-02 18:14:23 -0800289 signingByCertificate(cert),
Yingdi Yufe4733a2015-10-22 14:24:12 -0700290
291 SigningInfo(SigningInfo::SIGNER_TYPE_SHA256),
292 signingWithSha256()
293 };
294
295 for (const auto& signingInfo : signingInfos) {
296 BOOST_TEST_MESSAGE("SigningInfo: " << signingInfo);
297 Data data("/data");
298 Interest interest("/interest");
299
300 if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_NULL) {
301 m_keyChain.sign(data);
302 m_keyChain.sign(interest);
303 }
304 else {
305 m_keyChain.sign(data, signingInfo);
306 m_keyChain.sign(interest, signingInfo);
307 }
308
309 Signature interestSignature(interest.getName()[-2].blockFromValue(), interest.getName()[-1].blockFromValue());
310
311 if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_SHA256) {
312 BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::DigestSha256);
313 BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::DigestSha256);
314
Alexander Afanasyev574aa862017-01-10 19:53:28 -0800315 BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
316 BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
Yingdi Yufe4733a2015-10-22 14:24:12 -0700317 }
318 else {
319 BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureSha256WithEcdsa);
320 BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureSha256WithEcdsa);
321
322 BOOST_CHECK_EQUAL(data.getSignature().getKeyLocator().getName(), cert.getName().getPrefix(-2));
323 BOOST_CHECK_EQUAL(interestSignature.getKeyLocator().getName(), cert.getName().getPrefix(-2));
324
Alexander Afanasyev574aa862017-01-10 19:53:28 -0800325 BOOST_CHECK(verifySignature(data, key));
326 BOOST_CHECK(verifySignature(interest, key));
Yingdi Yufe4733a2015-10-22 14:24:12 -0700327 }
328 }
329}
330
331BOOST_FIXTURE_TEST_CASE(PublicKeySigningDefaults, IdentityManagementFixture)
332{
333 Data data("/test/data");
334
335 // Identity will be created with generated key and self-signed cert with default parameters
336 BOOST_CHECK_THROW(m_keyChain.sign(data, signingByIdentity("/non-existing/identity")), KeyChain::InvalidSigningInfoError);
337
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700338 // Create identity with EC key and the corresponding self-signed certificate
339 Identity id = addIdentity("/ndn/test/ec", EcKeyParams());
Yingdi Yufe4733a2015-10-22 14:24:12 -0700340 BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
341 BOOST_CHECK_EQUAL(data.getSignature().getType(),
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700342 KeyChain::getSignatureType(EcKeyParams().getKeyType(), DigestAlgorithm::SHA256));
Yingdi Yufe4733a2015-10-22 14:24:12 -0700343 BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
344
345 // Create identity with RSA key and the corresponding self-signed certificate
346 id = addIdentity("/ndn/test/rsa", RsaKeyParams());
347 BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
348 BOOST_CHECK_EQUAL(data.getSignature().getType(),
349 KeyChain::getSignatureType(RsaKeyParams().getKeyType(), DigestAlgorithm::SHA256));
350 BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
351}
352
353BOOST_FIXTURE_TEST_CASE(ExportImport, IdentityManagementFixture)
354{
355 Identity id = addIdentity("/TestKeyChain/ExportIdentity/");
356 Certificate cert = id.getDefaultKey().getDefaultCertificate();
357
358 shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4);
359 Block block = exported->wireEncode();
360
361 m_keyChain.deleteIdentity(id);
362
363 BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
364 BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
365
366 SafeBag imported;
367 imported.wireDecode(block);
368 m_keyChain.importSafeBag(imported, "1234", 4);
369
370 BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), true);
371 BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1);
372 BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getIdentity(cert.getIdentity()));
373 Identity newId = m_keyChain.getPib().getIdentity(cert.getIdentity());
374 BOOST_CHECK_EQUAL(newId.getKeys().size(), 1);
375 BOOST_REQUIRE_NO_THROW(newId.getKey(cert.getKeyName()));
376 Key newKey = newId.getKey(cert.getKeyName());
377 BOOST_CHECK_EQUAL(newKey.getCertificates().size(), 1);
378 BOOST_REQUIRE_NO_THROW(newKey.getCertificate(cert.getName()));
379
380 m_keyChain.deleteIdentity(newId);
381 BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
382 BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
383}
384
Alexander Afanasyevf8379172017-01-11 16:56:04 -0800385BOOST_FIXTURE_TEST_CASE(SelfSignedCertValidity, IdentityManagementFixture)
386{
387 Certificate cert = addIdentity("/Security/V2/TestKeyChain/SelfSignedCertValidity")
388 .getDefaultKey()
389 .getDefaultCertificate();
390 BOOST_CHECK(cert.isValid());
391 BOOST_CHECK(cert.isValid(time::system_clock::now() + time::days(10 * 365)));
392 BOOST_CHECK_GT(cert.getValidityPeriod().getPeriod().second,
393 time::system_clock::now() + time::days(10 * 365));
394}
395
Yingdi Yufe4733a2015-10-22 14:24:12 -0700396BOOST_AUTO_TEST_SUITE_END() // TestKeyChain
Alexander Afanasyevf8379172017-01-11 16:56:04 -0800397BOOST_AUTO_TEST_SUITE_END() // V2
Yingdi Yufe4733a2015-10-22 14:24:12 -0700398BOOST_AUTO_TEST_SUITE_END() // Security
399
400} // namespace tests
401} // namespace v2
402} // namespace security
403} // namespace ndn