blob: 22a25b3af2f0d627c6c484e07f8d6bbabe599969 [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 Yuf56c68f2014-04-24 21:50:13 -070041KeyChain::KeyChain()
42 : m_pib(0)
43 , m_tpm(0)
Yingdi Yu0f5fb692014-06-10 12:07:28 -070044 , m_lastTimestamp(time::toUnixTimestamp(time::system_clock::now()))
Yingdi Yuf56c68f2014-04-24 21:50:13 -070045{
46
47 ConfigFile config;
48 const ConfigFile::Parsed& parsed = config.getParsedConfiguration();
49
50 std::string pibName;
51 try
52 {
53 pibName = parsed.get<std::string>("pib");
54 }
55 catch (boost::property_tree::ptree_bad_path& error)
56 {
57 // pib is not specified, take the default
58 }
59 catch (boost::property_tree::ptree_bad_data& error)
60 {
61 throw ConfigFile::Error(error.what());
62 }
63
64 std::string tpmName;
65 try
66 {
67 tpmName = parsed.get<std::string>("tpm");
68 }
69 catch (boost::property_tree::ptree_bad_path& error)
70 {
71 // tpm is not specified, take the default
72 }
73 catch (boost::property_tree::ptree_bad_data& error)
74 {
75 throw ConfigFile::Error(error.what());
76 }
77
78
79 if (pibName.empty() || pibName == "sqlite3")
80 m_pib = new SecPublicInfoSqlite3;
81 else
82 throw Error("PIB type '" + pibName + "' is not supported");
83
84 if (tpmName.empty())
85#if defined(NDN_CXX_HAVE_OSX_SECURITY) and defined(NDN_CXX_WITH_OSX_KEYCHAIN)
86 m_tpm = new SecTpmOsx();
87#else
88 m_tpm = new SecTpmFile();
89#endif // defined(NDN_CXX_HAVE_OSX_SECURITY) and defined(NDN_CXX_WITH_OSX_KEYCHAIN)
90 else if (tpmName == "osx-keychain")
91#if defined(NDN_CXX_HAVE_OSX_SECURITY)
92 m_tpm = new SecTpmOsx();
93#else
94 throw Error("TPM type '" + tpmName + "' is not supported on this platform");
95#endif // NDN_CXX_HAVE_OSX_SECURITY
96 else if (tpmName == "file")
97 m_tpm = new SecTpmFile();
98 else
99 throw Error("TPM type '" + tpmName + "' is not supported");
100}
101
102KeyChain::KeyChain(const std::string& pibName,
103 const std::string& tpmName)
104 : m_pib(0)
105 , m_tpm(0)
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700106 , m_lastTimestamp(time::toUnixTimestamp(time::system_clock::now()))
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700107{
108 if (pibName == "sqlite3")
109 m_pib = new SecPublicInfoSqlite3;
110 else
111 throw Error("PIB type '" + pibName + "' is not supported");
112
113 if (tpmName == "file")
114 m_tpm = new SecTpmFile;
115#if defined(NDN_CXX_HAVE_OSX_SECURITY)
116 else if (tpmName == "osx-keychain")
117 m_tpm = new SecTpmOsx();
118#endif //NDN_CXX_HAVE_OSX_SECURITY
119 else
120 throw Error("TPM type '" + tpmName + "' is not supported");
121}
122
123shared_ptr<IdentityCertificate>
124KeyChain::prepareUnsignedIdentityCertificate(const Name& keyName,
125 const Name& signingIdentity,
126 const time::system_clock::TimePoint& notBefore,
127 const time::system_clock::TimePoint& notAfter,
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700128 const std::vector<CertificateSubjectDescription>& subjectDescription,
129 const Name& certPrefix)
130{
131 shared_ptr<PublicKey> publicKey;
132 try
133 {
134 publicKey = m_pib->getPublicKey(keyName);
135 }
136 catch (SecPublicInfo::Error& e)
137 {
138 return shared_ptr<IdentityCertificate>();
139 }
140
141 return prepareUnsignedIdentityCertificate(keyName, *publicKey, signingIdentity,
142 notBefore, notAfter,
143 subjectDescription, certPrefix);
144}
145
146shared_ptr<IdentityCertificate>
147KeyChain::prepareUnsignedIdentityCertificate(const Name& keyName,
148 const PublicKey& publicKey,
149 const Name& signingIdentity,
150 const time::system_clock::TimePoint& notBefore,
151 const time::system_clock::TimePoint& notAfter,
152 const std::vector<CertificateSubjectDescription>& subjectDescription,
153 const Name& certPrefix)
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700154{
155 if (keyName.size() < 1)
156 return shared_ptr<IdentityCertificate>();
157
Alexander Afanasyev9c578182014-05-14 17:28:28 -0700158 std::string keyIdPrefix = keyName.get(-1).toUri().substr(0, 4);
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700159 if (keyIdPrefix != "ksk-" && keyIdPrefix != "dsk-")
160 return shared_ptr<IdentityCertificate>();
161
162 shared_ptr<IdentityCertificate> certificate = make_shared<IdentityCertificate>();
163 Name certName;
164
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700165 if (certPrefix == KeyChain::DEFAULT_PREFIX)
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700166 {
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700167 // No certificate prefix hint, infer the prefix
168 if (signingIdentity.isPrefixOf(keyName))
169 certName.append(signingIdentity)
170 .append("KEY")
171 .append(keyName.getSubName(signingIdentity.size()))
172 .append("ID-CERT")
173 .appendVersion();
174 else
175 certName.append(keyName.getPrefix(-1))
176 .append("KEY")
177 .append(keyName.get(-1))
178 .append("ID-CERT")
179 .appendVersion();
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700180 }
181 else
182 {
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700183 // cert prefix hint is supplied, determine the cert name.
184 if (certPrefix.isPrefixOf(keyName) && certPrefix != keyName)
185 certName.append(certPrefix)
186 .append("KEY")
187 .append(keyName.getSubName(certPrefix.size()))
188 .append("ID-CERT")
189 .appendVersion();
190 else
191 return shared_ptr<IdentityCertificate>();
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700192 }
193
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700194
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700195 certificate->setName(certName);
196 certificate->setNotBefore(notBefore);
197 certificate->setNotAfter(notAfter);
Yingdi Yu0eb5d722014-06-10 15:06:25 -0700198 certificate->setPublicKeyInfo(publicKey);
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700199
200 if (subjectDescription.empty())
201 {
202 CertificateSubjectDescription subjectName("2.5.4.41", keyName.getPrefix(-1).toUri());
203 certificate->addSubjectDescription(subjectName);
204 }
205 else
206 {
207 std::vector<CertificateSubjectDescription>::const_iterator sdIt =
208 subjectDescription.begin();
209 std::vector<CertificateSubjectDescription>::const_iterator sdEnd =
210 subjectDescription.end();
211 for(; sdIt != sdEnd; sdIt++)
212 certificate->addSubjectDescription(*sdIt);
213 }
214
215 certificate->encode();
216
217 return certificate;
218}
219
220Signature
221KeyChain::sign(const uint8_t* buffer, size_t bufferLength, const Name& certificateName)
222{
223 if (!m_pib->doesCertificateExist(certificateName))
224 throw SecPublicInfo::Error("Requested certificate ["
225 + certificateName.toUri() + "] doesn't exist");
226
227 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(certificateName);
228
229 SignatureSha256WithRsa signature;
230 // implicit conversion should take care
231 signature.setKeyLocator(certificateName.getPrefix(-1));
232
233 // For temporary usage, we support RSA + SHA256 only, but will support more.
234 signature.setValue(m_tpm->signInTpm(buffer, bufferLength, keyName, DIGEST_ALGORITHM_SHA256));
235 return signature;
236}
237
238shared_ptr<IdentityCertificate>
239KeyChain::selfSign(const Name& keyName)
240{
241 shared_ptr<PublicKey> pubKey;
242 try
243 {
244 pubKey = m_pib->getPublicKey(keyName); // may throw an exception.
245 }
246 catch (SecPublicInfo::Error& e)
247 {
248 return shared_ptr<IdentityCertificate>();
249 }
250
251 shared_ptr<IdentityCertificate> certificate = make_shared<IdentityCertificate>();
252
253 Name certificateName = keyName.getPrefix(-1);
254 certificateName.append("KEY").append(keyName.get(-1)).append("ID-CERT").appendVersion();
255
256 certificate->setName(certificateName);
257 certificate->setNotBefore(time::system_clock::now());
258 certificate->setNotAfter(time::system_clock::now() + time::days(7300)/* ~20 years*/);
259 certificate->setPublicKeyInfo(*pubKey);
260 certificate->addSubjectDescription(CertificateSubjectDescription("2.5.4.41", keyName.toUri()));
261 certificate->encode();
262
263 selfSign(*certificate);
264 return certificate;
265}
266
267void
268KeyChain::selfSign(IdentityCertificate& cert)
269{
270 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(cert.getName());
271 if (!m_tpm->doesKeyExistInTpm(keyName, KEY_CLASS_PRIVATE))
272 throw SecTpm::Error("private key does not exist!");
273
274 SignatureSha256WithRsa signature;
275 signature.setKeyLocator(cert.getName().getPrefix(-1)); // implicit conversion should take care
276
277 // For temporary usage, we support RSA + SHA256 only, but will support more.
278 signPacketWrapper(cert, signature, keyName, DIGEST_ALGORITHM_SHA256);
279}
280
281shared_ptr<SecuredBag>
282KeyChain::exportIdentity(const Name& identity, const std::string& passwordStr)
283{
284 if (!m_pib->doesIdentityExist(identity))
285 throw SecPublicInfo::Error("Identity does not exist!");
286
287 Name keyName = m_pib->getDefaultKeyNameForIdentity(identity);
288
289 ConstBufferPtr pkcs5;
290 try
291 {
292 pkcs5 = m_tpm->exportPrivateKeyPkcs5FromTpm(keyName, passwordStr);
293 }
294 catch (SecTpm::Error& e)
295 {
296 throw SecPublicInfo::Error("Fail to export PKCS5 of private key");
297 }
298
299 shared_ptr<IdentityCertificate> cert;
300 try
301 {
302 cert = m_pib->getCertificate(m_pib->getDefaultCertificateNameForKey(keyName));
303 }
304 catch (SecPublicInfo::Error& e)
305 {
306 cert = selfSign(keyName);
307 m_pib->addCertificateAsIdentityDefault(*cert);
308 }
309
Alexander Afanasyevb67090a2014-04-29 22:31:01 -0700310 // make_shared on OSX 10.9 has some strange problem here
311 shared_ptr<SecuredBag> secureBag(new SecuredBag(*cert, pkcs5));
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700312
313 return secureBag;
314}
315
316void
317KeyChain::importIdentity(const SecuredBag& securedBag, const std::string& passwordStr)
318{
319 Name certificateName = securedBag.getCertificate().getName();
320 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(certificateName);
321 Name identity = keyName.getPrefix(-1);
322
323 // Add identity
324 m_pib->addIdentity(identity);
325
326 // Add key
327 m_tpm->importPrivateKeyPkcs5IntoTpm(keyName,
328 securedBag.getKey()->buf(),
329 securedBag.getKey()->size(),
330 passwordStr);
331
332 shared_ptr<PublicKey> pubKey = m_tpm->getPublicKeyFromTpm(keyName.toUri());
333 // HACK! We should set key type according to the pkcs8 info.
334 m_pib->addPublicKey(keyName, KEY_TYPE_RSA, *pubKey);
335 m_pib->setDefaultKeyNameForIdentity(keyName);
336
337 // Add cert
338 m_pib->addCertificateAsIdentityDefault(securedBag.getCertificate());
339}
340
341void
342KeyChain::setDefaultCertificateInternal()
343{
344 m_pib->refreshDefaultCertificate();
345
346 if (!static_cast<bool>(m_pib->defaultCertificate()))
347 {
348 Name defaultIdentity;
349 try
350 {
351 defaultIdentity = m_pib->getDefaultIdentity();
352 }
353 catch (SecPublicInfo::Error& e)
354 {
355 uint32_t random = random::generateWord32();
356 defaultIdentity.append("tmp-identity")
357 .append(reinterpret_cast<uint8_t*>(&random), 4);
358 }
359 createIdentity(defaultIdentity);
360 m_pib->setDefaultIdentity(defaultIdentity);
361 m_pib->refreshDefaultCertificate();
362 }
363}
364
365}