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