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