blob: a04e58b1bdd1ee5eeb0e25028da8627251e9fe60 [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Yingdi Yuf56c68f2014-04-24 21:50:13 -07002/**
Alexander Afanasyevc169a812014-05-20 20:37:29 -04003 * Copyright (c) 2013-2014 Regents of the University of California.
Yingdi Yuf56c68f2014-04-24 21:50:13 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Yingdi Yuf56c68f2014-04-24 21:50:13 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * 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.
Yingdi Yuf56c68f2014-04-24 21:50:13 -070020 *
21 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
22 */
23
24#include "key-chain.hpp"
25
26#include "sec-public-info-sqlite3.hpp"
27#include "sec-tpm-file.hpp"
28
29#ifdef NDN_CXX_HAVE_OSX_SECURITY
30#include "sec-tpm-osx.hpp"
31#endif
32
33#include "../util/random.hpp"
34#include "../util/config-file.hpp"
35
36namespace ndn {
37
Yingdi Yu0eb5d722014-06-10 15:06:25 -070038// Use a GUID as a magic number of KeyChain::DEFAULT_PREFIX identifier
39const Name KeyChain::DEFAULT_PREFIX("/723821fd-f534-44b3-80d9-44bf5f58bbbb");
40
Yingdi Yu7036ce22014-06-19 18:53:37 -070041const RsaKeyParams KeyChain::DEFAULT_KEY_PARAMS;
42
Yingdi Yuf56c68f2014-04-24 21:50:13 -070043KeyChain::KeyChain()
44 : m_pib(0)
45 , m_tpm(0)
Yingdi Yu0f5fb692014-06-10 12:07:28 -070046 , m_lastTimestamp(time::toUnixTimestamp(time::system_clock::now()))
Yingdi Yuf56c68f2014-04-24 21:50:13 -070047{
48
49 ConfigFile config;
50 const ConfigFile::Parsed& parsed = config.getParsedConfiguration();
51
52 std::string pibName;
53 try
54 {
55 pibName = parsed.get<std::string>("pib");
56 }
57 catch (boost::property_tree::ptree_bad_path& error)
58 {
59 // pib is not specified, take the default
60 }
61 catch (boost::property_tree::ptree_bad_data& error)
62 {
63 throw ConfigFile::Error(error.what());
64 }
65
66 std::string tpmName;
67 try
68 {
69 tpmName = parsed.get<std::string>("tpm");
70 }
71 catch (boost::property_tree::ptree_bad_path& error)
72 {
73 // tpm is not specified, take the default
74 }
75 catch (boost::property_tree::ptree_bad_data& error)
76 {
77 throw ConfigFile::Error(error.what());
78 }
79
80
81 if (pibName.empty() || pibName == "sqlite3")
82 m_pib = new SecPublicInfoSqlite3;
83 else
84 throw Error("PIB type '" + pibName + "' is not supported");
85
86 if (tpmName.empty())
87#if defined(NDN_CXX_HAVE_OSX_SECURITY) and defined(NDN_CXX_WITH_OSX_KEYCHAIN)
88 m_tpm = new SecTpmOsx();
89#else
90 m_tpm = new SecTpmFile();
91#endif // defined(NDN_CXX_HAVE_OSX_SECURITY) and defined(NDN_CXX_WITH_OSX_KEYCHAIN)
92 else if (tpmName == "osx-keychain")
93#if defined(NDN_CXX_HAVE_OSX_SECURITY)
94 m_tpm = new SecTpmOsx();
95#else
96 throw Error("TPM type '" + tpmName + "' is not supported on this platform");
97#endif // NDN_CXX_HAVE_OSX_SECURITY
98 else if (tpmName == "file")
99 m_tpm = new SecTpmFile();
100 else
101 throw Error("TPM type '" + tpmName + "' is not supported");
102}
103
104KeyChain::KeyChain(const std::string& pibName,
105 const std::string& tpmName)
106 : m_pib(0)
107 , m_tpm(0)
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700108 , m_lastTimestamp(time::toUnixTimestamp(time::system_clock::now()))
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700109{
110 if (pibName == "sqlite3")
111 m_pib = new SecPublicInfoSqlite3;
112 else
113 throw Error("PIB type '" + pibName + "' is not supported");
114
115 if (tpmName == "file")
116 m_tpm = new SecTpmFile;
117#if defined(NDN_CXX_HAVE_OSX_SECURITY)
118 else if (tpmName == "osx-keychain")
119 m_tpm = new SecTpmOsx();
120#endif //NDN_CXX_HAVE_OSX_SECURITY
121 else
122 throw Error("TPM type '" + tpmName + "' is not supported");
123}
124
Yingdi Yu7036ce22014-06-19 18:53:37 -0700125Name
126KeyChain::createIdentity(const Name& identityName, const KeyParams& params)
127{
128 m_pib->addIdentity(identityName);
129
130 Name keyName;
131 try
132 {
133 keyName = m_pib->getDefaultKeyNameForIdentity(identityName);
134 }
135 catch (SecPublicInfo::Error& e)
136 {
137 keyName = generateKeyPair(identityName, true, params);
138 m_pib->setDefaultKeyNameForIdentity(keyName);
139 }
140
141 Name certName;
142 try
143 {
144 certName = m_pib->getDefaultCertificateNameForKey(keyName);
145 }
146 catch (SecPublicInfo::Error& e)
147 {
148 shared_ptr<IdentityCertificate> selfCert = selfSign(keyName);
149 m_pib->addCertificateAsIdentityDefault(*selfCert);
150 certName = selfCert->getName();
151 }
152
153 return certName;
154}
155
156Name
157KeyChain::generateRsaKeyPairAsDefault(const Name& identityName, bool isKsk, int keySize)
158{
159 RsaKeyParams params(keySize);
160
161 Name keyName = generateKeyPair(identityName, isKsk, params);
162
163 m_pib->setDefaultKeyNameForIdentity(keyName);
164
165 return keyName;
166}
167
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700168shared_ptr<IdentityCertificate>
169KeyChain::prepareUnsignedIdentityCertificate(const Name& keyName,
170 const Name& signingIdentity,
171 const time::system_clock::TimePoint& notBefore,
172 const time::system_clock::TimePoint& notAfter,
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700173 const std::vector<CertificateSubjectDescription>& subjectDescription,
174 const Name& certPrefix)
175{
176 shared_ptr<PublicKey> publicKey;
177 try
178 {
179 publicKey = m_pib->getPublicKey(keyName);
180 }
181 catch (SecPublicInfo::Error& e)
182 {
183 return shared_ptr<IdentityCertificate>();
184 }
185
186 return prepareUnsignedIdentityCertificate(keyName, *publicKey, signingIdentity,
187 notBefore, notAfter,
188 subjectDescription, certPrefix);
189}
190
191shared_ptr<IdentityCertificate>
192KeyChain::prepareUnsignedIdentityCertificate(const Name& keyName,
193 const PublicKey& publicKey,
194 const Name& signingIdentity,
195 const time::system_clock::TimePoint& notBefore,
196 const time::system_clock::TimePoint& notAfter,
197 const std::vector<CertificateSubjectDescription>& subjectDescription,
198 const Name& certPrefix)
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700199{
200 if (keyName.size() < 1)
201 return shared_ptr<IdentityCertificate>();
202
Alexander Afanasyev9c578182014-05-14 17:28:28 -0700203 std::string keyIdPrefix = keyName.get(-1).toUri().substr(0, 4);
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700204 if (keyIdPrefix != "ksk-" && keyIdPrefix != "dsk-")
205 return shared_ptr<IdentityCertificate>();
206
207 shared_ptr<IdentityCertificate> certificate = make_shared<IdentityCertificate>();
208 Name certName;
209
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700210 if (certPrefix == KeyChain::DEFAULT_PREFIX)
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700211 {
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700212 // No certificate prefix hint, infer the prefix
213 if (signingIdentity.isPrefixOf(keyName))
214 certName.append(signingIdentity)
215 .append("KEY")
216 .append(keyName.getSubName(signingIdentity.size()))
217 .append("ID-CERT")
218 .appendVersion();
219 else
220 certName.append(keyName.getPrefix(-1))
221 .append("KEY")
222 .append(keyName.get(-1))
223 .append("ID-CERT")
224 .appendVersion();
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700225 }
226 else
227 {
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700228 // cert prefix hint is supplied, determine the cert name.
229 if (certPrefix.isPrefixOf(keyName) && certPrefix != keyName)
230 certName.append(certPrefix)
231 .append("KEY")
232 .append(keyName.getSubName(certPrefix.size()))
233 .append("ID-CERT")
234 .appendVersion();
235 else
236 return shared_ptr<IdentityCertificate>();
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700237 }
238
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700239
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700240 certificate->setName(certName);
241 certificate->setNotBefore(notBefore);
242 certificate->setNotAfter(notAfter);
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700243 certificate->setPublicKeyInfo(publicKey);
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700244
245 if (subjectDescription.empty())
246 {
Yingdi Yu7036ce22014-06-19 18:53:37 -0700247 // OID 2.5.4.41 is the oid of subject name.
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700248 CertificateSubjectDescription subjectName("2.5.4.41", keyName.getPrefix(-1).toUri());
249 certificate->addSubjectDescription(subjectName);
250 }
251 else
252 {
253 std::vector<CertificateSubjectDescription>::const_iterator sdIt =
254 subjectDescription.begin();
255 std::vector<CertificateSubjectDescription>::const_iterator sdEnd =
256 subjectDescription.end();
257 for(; sdIt != sdEnd; sdIt++)
258 certificate->addSubjectDescription(*sdIt);
259 }
260
261 certificate->encode();
262
263 return certificate;
264}
265
266Signature
267KeyChain::sign(const uint8_t* buffer, size_t bufferLength, const Name& certificateName)
268{
269 if (!m_pib->doesCertificateExist(certificateName))
Yingdi Yu7036ce22014-06-19 18:53:37 -0700270 throw SecPublicInfo::Error("Requested certificate [" +
271 certificateName.toUri() + "] doesn't exist");
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700272
273 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(certificateName);
274
275 SignatureSha256WithRsa signature;
276 // implicit conversion should take care
277 signature.setKeyLocator(certificateName.getPrefix(-1));
278
279 // For temporary usage, we support RSA + SHA256 only, but will support more.
280 signature.setValue(m_tpm->signInTpm(buffer, bufferLength, keyName, DIGEST_ALGORITHM_SHA256));
281 return signature;
282}
283
284shared_ptr<IdentityCertificate>
285KeyChain::selfSign(const Name& keyName)
286{
287 shared_ptr<PublicKey> pubKey;
288 try
289 {
290 pubKey = m_pib->getPublicKey(keyName); // may throw an exception.
291 }
292 catch (SecPublicInfo::Error& e)
293 {
294 return shared_ptr<IdentityCertificate>();
295 }
296
297 shared_ptr<IdentityCertificate> certificate = make_shared<IdentityCertificate>();
298
299 Name certificateName = keyName.getPrefix(-1);
300 certificateName.append("KEY").append(keyName.get(-1)).append("ID-CERT").appendVersion();
301
302 certificate->setName(certificateName);
303 certificate->setNotBefore(time::system_clock::now());
304 certificate->setNotAfter(time::system_clock::now() + time::days(7300)/* ~20 years*/);
305 certificate->setPublicKeyInfo(*pubKey);
306 certificate->addSubjectDescription(CertificateSubjectDescription("2.5.4.41", keyName.toUri()));
307 certificate->encode();
308
309 selfSign(*certificate);
310 return certificate;
311}
312
313void
314KeyChain::selfSign(IdentityCertificate& cert)
315{
316 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(cert.getName());
317 if (!m_tpm->doesKeyExistInTpm(keyName, KEY_CLASS_PRIVATE))
Yingdi Yu7036ce22014-06-19 18:53:37 -0700318 throw SecTpm::Error("Private key does not exist");
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700319
320 SignatureSha256WithRsa signature;
321 signature.setKeyLocator(cert.getName().getPrefix(-1)); // implicit conversion should take care
322
323 // For temporary usage, we support RSA + SHA256 only, but will support more.
324 signPacketWrapper(cert, signature, keyName, DIGEST_ALGORITHM_SHA256);
325}
326
327shared_ptr<SecuredBag>
328KeyChain::exportIdentity(const Name& identity, const std::string& passwordStr)
329{
330 if (!m_pib->doesIdentityExist(identity))
Yingdi Yu7036ce22014-06-19 18:53:37 -0700331 throw SecPublicInfo::Error("Identity does not exist");
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700332
333 Name keyName = m_pib->getDefaultKeyNameForIdentity(identity);
334
335 ConstBufferPtr pkcs5;
336 try
337 {
338 pkcs5 = m_tpm->exportPrivateKeyPkcs5FromTpm(keyName, passwordStr);
339 }
340 catch (SecTpm::Error& e)
341 {
342 throw SecPublicInfo::Error("Fail to export PKCS5 of private key");
343 }
344
345 shared_ptr<IdentityCertificate> cert;
346 try
347 {
348 cert = m_pib->getCertificate(m_pib->getDefaultCertificateNameForKey(keyName));
349 }
350 catch (SecPublicInfo::Error& e)
351 {
352 cert = selfSign(keyName);
353 m_pib->addCertificateAsIdentityDefault(*cert);
354 }
355
Alexander Afanasyevb67090a2014-04-29 22:31:01 -0700356 // make_shared on OSX 10.9 has some strange problem here
357 shared_ptr<SecuredBag> secureBag(new SecuredBag(*cert, pkcs5));
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700358
359 return secureBag;
360}
361
362void
363KeyChain::importIdentity(const SecuredBag& securedBag, const std::string& passwordStr)
364{
365 Name certificateName = securedBag.getCertificate().getName();
366 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(certificateName);
367 Name identity = keyName.getPrefix(-1);
368
369 // Add identity
370 m_pib->addIdentity(identity);
371
372 // Add key
373 m_tpm->importPrivateKeyPkcs5IntoTpm(keyName,
374 securedBag.getKey()->buf(),
375 securedBag.getKey()->size(),
376 passwordStr);
377
378 shared_ptr<PublicKey> pubKey = m_tpm->getPublicKeyFromTpm(keyName.toUri());
379 // HACK! We should set key type according to the pkcs8 info.
380 m_pib->addPublicKey(keyName, KEY_TYPE_RSA, *pubKey);
381 m_pib->setDefaultKeyNameForIdentity(keyName);
382
383 // Add cert
384 m_pib->addCertificateAsIdentityDefault(securedBag.getCertificate());
385}
386
387void
388KeyChain::setDefaultCertificateInternal()
389{
390 m_pib->refreshDefaultCertificate();
391
392 if (!static_cast<bool>(m_pib->defaultCertificate()))
393 {
394 Name defaultIdentity;
395 try
396 {
397 defaultIdentity = m_pib->getDefaultIdentity();
398 }
399 catch (SecPublicInfo::Error& e)
400 {
401 uint32_t random = random::generateWord32();
402 defaultIdentity.append("tmp-identity")
403 .append(reinterpret_cast<uint8_t*>(&random), 4);
404 }
405 createIdentity(defaultIdentity);
406 m_pib->setDefaultIdentity(defaultIdentity);
407 m_pib->refreshDefaultCertificate();
408 }
409}
410
Yingdi Yu7036ce22014-06-19 18:53:37 -0700411Name
412KeyChain::generateKeyPair(const Name& identityName, bool isKsk, const KeyParams& params)
413{
414 Name keyName = m_pib->getNewKeyName(identityName, isKsk);
415
416 m_tpm->generateKeyPairInTpm(keyName.toUri(), params);
417
418 shared_ptr<PublicKey> pubKey = m_tpm->getPublicKeyFromTpm(keyName.toUri());
419 m_pib->addKey(keyName, *pubKey);
420
421 return keyName;
422}
423
424void
425KeyChain::signPacketWrapper(Data& data, const SignatureSha256WithRsa& signature,
426 const Name& keyName, DigestAlgorithm digestAlgorithm)
427{
428 data.setSignature(signature);
429
430 EncodingBuffer encoder;
431 data.wireEncode(encoder, true);
432
433 Block signatureValue = m_tpm->signInTpm(encoder.buf(), encoder.size(),
434 keyName, digestAlgorithm);
435 data.wireEncode(encoder, signatureValue);
436}
437
438void
439KeyChain::signPacketWrapper(Interest& interest, const SignatureSha256WithRsa& signature,
440 const Name& keyName, DigestAlgorithm digestAlgorithm)
441{
442 time::milliseconds timestamp = time::toUnixTimestamp(time::system_clock::now());
443 if (timestamp <= m_lastTimestamp)
444 {
445 timestamp = m_lastTimestamp + time::milliseconds(1);
446 }
447
448 Name signedName = interest.getName();
449 signedName
450 .append(name::Component::fromNumber(timestamp.count())) // timestamp
451 .append(name::Component::fromNumber(random::generateWord64())) // nonce
452 .append(signature.getInfo()); // signatureInfo
453
454 Block sigValue = m_tpm->signInTpm(signedName.wireEncode().value(),
455 signedName.wireEncode().value_size(),
456 keyName,
457 digestAlgorithm);
458 sigValue.encode();
459 signedName.append(sigValue); // signatureValue
460 interest.setName(signedName);
461}
462
463Signature
464KeyChain::signByIdentity(const uint8_t* buffer, size_t bufferLength, const Name& identityName)
465{
466 Name signingCertificateName;
467 try
468 {
469 signingCertificateName = m_pib->getDefaultCertificateNameForIdentity(identityName);
470 }
471 catch (SecPublicInfo::Error& e)
472 {
473 signingCertificateName = createIdentity(identityName);
474 // Ideally, no exception will be thrown out, unless something goes wrong in the TPM, which
475 // is a fatal error.
476 }
477
478 // We either get or create the signing certificate, sign data! (no exception unless fatal error
479 // in TPM)
480 return sign(buffer, bufferLength, signingCertificateName);
481}
482
483void
484KeyChain::signWithSha256(Data& data)
485{
486 DigestSha256 sig;
487 data.setSignature(sig);
488
489 Block sigValue(Tlv::SignatureValue,
490 crypto::sha256(data.wireEncode().value(),
491 data.wireEncode().value_size() -
492 data.getSignature().getValue().size()));
493 data.setSignatureValue(sigValue);
494}
495
496void
497KeyChain::deleteCertificate(const Name& certificateName)
498{
499 try
500 {
501 if (m_pib->getDefaultCertificateName() == certificateName)
502 return;
503 }
504 catch (SecPublicInfo::Error& e)
505 {
506 // Not a real error, just try to delete the certificate
507 }
508
509 m_pib->deleteCertificateInfo(certificateName);
510}
511
512void
513KeyChain::deleteKey(const Name& keyName)
514{
515 try
516 {
517 if (m_pib->getDefaultKeyNameForIdentity(m_pib->getDefaultIdentity()) == keyName)
518 return;
519 }
520 catch (SecPublicInfo::Error& e)
521 {
522 // Not a real error, just try to delete the key
523 }
524
525 m_pib->deletePublicKeyInfo(keyName);
526 m_tpm->deleteKeyPairInTpm(keyName);
527}
528
529void
530KeyChain::deleteIdentity(const Name& identity)
531{
532 try
533 {
534 if (m_pib->getDefaultIdentity() == identity)
535 return;
536 }
537 catch (SecPublicInfo::Error& e)
538 {
539 // Not a real error, just try to delete the identity
540 }
541
542 std::vector<Name> nameList;
543 m_pib->getAllKeyNamesOfIdentity(identity, nameList, true);
544 m_pib->getAllKeyNamesOfIdentity(identity, nameList, false);
545
546 m_pib->deleteIdentityInfo(identity);
547
548 std::vector<Name>::const_iterator it = nameList.begin();
549 for(; it != nameList.end(); it++)
550 m_tpm->deleteKeyPairInTpm(*it);
551}
552
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700553}