blob: bc96b694ad5ca9fd7a817c2b21035c726203e10f [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Jeff Thompsona5dc3512013-10-17 10:26:19 -07002/**
Alexander Afanasyevc169a812014-05-20 20:37:29 -04003 * Copyright (c) 2013-2014 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -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.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -070020 *
21 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
22 * @author Jeff Thompson <jefft0@remap.ucla.edu>
23 * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
Jeff Thompsona5dc3512013-10-17 10:26:19 -070024 */
25
Alexander Afanasyev09c613f2014-01-29 00:23:58 -080026#include "common.hpp"
Alexander Afanasyev8e96e582013-11-19 12:07:04 -080027
Alexander Afanasyeve2dcdfd2014-02-07 15:53:28 -080028#include "certificate.hpp"
Yingdi Yu4f324632014-01-15 18:10:03 -080029#include "../util/time.hpp"
Junxiao Shi482ccc52014-03-31 13:05:24 -070030#include "cryptopp.hpp"
Yingdi Yu4f324632014-01-15 18:10:03 -080031#include "../encoding/cryptopp/asn_ext.hpp"
Alexander Afanasyev258ec2b2014-05-14 16:15:37 -070032#include "../encoding/buffer-stream.hpp"
Yingdi Yu80979ba2014-11-25 14:38:36 -080033#include "../util/concepts.hpp"
Jeff Thompsona5dc3512013-10-17 10:26:19 -070034
Yingdi Yu3e8b52e2014-11-26 22:05:00 -080035#include <boost/algorithm/string/split.hpp>
36
Jeff Thompsona5dc3512013-10-17 10:26:19 -070037namespace ndn {
38
Yingdi Yu80979ba2014-11-25 14:38:36 -080039BOOST_CONCEPT_ASSERT((WireEncodable<Certificate>));
40BOOST_CONCEPT_ASSERT((WireDecodable<Certificate>));
41static_assert(std::is_base_of<tlv::Error, Certificate::Error>::value,
42 "Certificate::Error must inherit from tlv::Error");
43
Jeff Thompsona5dc3512013-10-17 10:26:19 -070044Certificate::Certificate()
Yingdi Yu4b8c6a22014-04-15 23:00:54 -070045 : m_notBefore(time::system_clock::TimePoint::max())
46 , m_notAfter(time::system_clock::TimePoint::min())
47{
48}
Jeff Thompsona5dc3512013-10-17 10:26:19 -070049
50Certificate::Certificate(const Data& data)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -070051 // Use the copy constructor. It clones the signature object.
52 : Data(data)
Jeff Thompsona5dc3512013-10-17 10:26:19 -070053{
Jeff Thompsona5dc3512013-10-17 10:26:19 -070054 decode();
55}
56
Yingdi Yu80979ba2014-11-25 14:38:36 -080057Certificate::Certificate(const Block& block)
58 : Data(block)
59{
60 decode();
61}
62
Jeff Thompsona5dc3512013-10-17 10:26:19 -070063Certificate::~Certificate()
64{
Yingdi Yu80979ba2014-11-25 14:38:36 -080065}
66
67void
68Certificate::wireDecode(const Block& wire)
69{
70 Data::wireDecode(wire);
71 decode();
Jeff Thompsona5dc3512013-10-17 10:26:19 -070072}
73
74bool
75Certificate::isTooEarly()
76{
Yingdi Yu4b8c6a22014-04-15 23:00:54 -070077 if (time::system_clock::now() < m_notBefore)
Jeff Thompsona5dc3512013-10-17 10:26:19 -070078 return true;
79 else
80 return false;
81}
82
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070083bool
Jeff Thompsona5dc3512013-10-17 10:26:19 -070084Certificate::isTooLate()
85{
Yingdi Yu4b8c6a22014-04-15 23:00:54 -070086 if (time::system_clock::now() > m_notAfter)
Jeff Thompsona5dc3512013-10-17 10:26:19 -070087 return true;
88 else
89 return false;
90}
91
Jeff Thompsona5dc3512013-10-17 10:26:19 -070092void
93Certificate::encode()
94{
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -080095 // Name
96 // <key_name>/ID-CERT/<id#>
97 // Content
98 // DER encoded idCert:
99 //
Yingdi Yu5ec0ee32014-06-24 16:26:09 -0700100 // idCert ::= SEQUENCE {
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800101 // validity Validity,
Yingdi Yu5ec0ee32014-06-24 16:26:09 -0700102 // subject Name,
103 // subjectPubKeyInfo SubjectPublicKeyInfo,
104 // extension Extensions OPTIONAL }
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800105 //
Yingdi Yu5ec0ee32014-06-24 16:26:09 -0700106 // Validity ::= SEQUENCE {
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800107 // notBefore Time,
108 // notAfter Time }
109 //
110 // Name ::= CHOICE {
111 // RDNSequence }
112 //
113 // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
114 //
115 // RelativeDistinguishedName ::=
116 // SET OF AttributeTypeAndValue
117 //
Yingdi Yu5ec0ee32014-06-24 16:26:09 -0700118 // SubjectPublicKeyInfo ::= SEQUENCE {
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800119 // algorithm AlgorithmIdentifier
120 // keybits BIT STRING }
121 //
122 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
123 //
124 // (see http://www.ietf.org/rfc/rfc3280.txt for more detail)
125 //
126 // KeyLocator
127 // issuer’s certificate name
128 // Signature
129
130 using namespace CryptoPP;
131
132 OBufferStream os;
133 CryptoPP::FileSink sink(os);
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700134
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800135 // idCert ::= SEQUENCE {
136 // validity Validity,
137 // subject Name,
138 // subjectPubKeyInfo SubjectPublicKeyInfo,
139 // extension Extensions OPTIONAL }
140 DERSequenceEncoder idCert(sink);
141 {
142 // Validity ::= SEQUENCE {
143 // notBefore Time,
144 // notAfter Time }
145 DERSequenceEncoder validity(idCert);
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700146 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700147 DEREncodeGeneralTime(validity, m_notBefore);
148 DEREncodeGeneralTime(validity, m_notAfter);
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700149 }
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800150 validity.MessageEnd();
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700151
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800152 // Name ::= CHOICE {
153 // RDNSequence }
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700154 //
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800155 // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
156 DERSequenceEncoder name(idCert);
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700157 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700158 for (SubjectDescriptionList::iterator it = m_subjectDescriptionList.begin();
159 it != m_subjectDescriptionList.end(); ++it)
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800160 {
161 it->encode(name);
162 }
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700163 }
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800164 name.MessageEnd();
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700165
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800166 // SubjectPublicKeyInfo
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700167 m_key.encode(idCert);
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700168
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800169 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
170 //
171 // Extension ::= SEQUENCE {
172 // extnID OBJECT IDENTIFIER,
173 // critical BOOLEAN DEFAULT FALSE,
174 // extnValue OCTET STRING }
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700175 if (!m_extensionList.empty())
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800176 {
177 DERSequenceEncoder extensions(idCert);
178 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700179 for (ExtensionList::iterator it = m_extensionList.begin();
180 it != m_extensionList.end(); ++it)
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800181 {
182 it->encode(extensions);
183 }
184 }
185 extensions.MessageEnd();
186 }
187 }
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700188
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800189 idCert.MessageEnd();
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700190
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800191 setContent(os.buf());
Junxiao Shia464b922014-11-12 21:13:06 -0700192 setContentType(tlv::ContentType_Key);
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700193}
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700194
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700195void
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700196Certificate::decode()
197{
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800198 using namespace CryptoPP;
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700199
Yingdi Yu80979ba2014-11-25 14:38:36 -0800200 try {
201 OBufferStream os;
202 StringSource source(getContent().value(), getContent().value_size(), true);
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700203
Yingdi Yu80979ba2014-11-25 14:38:36 -0800204 // idCert ::= SEQUENCE {
205 // validity Validity,
206 // subject Name,
207 // subjectPubKeyInfo SubjectPublicKeyInfo,
208 // extension Extensions OPTIONAL }
209 BERSequenceDecoder idCert(source);
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800210 {
Yingdi Yu80979ba2014-11-25 14:38:36 -0800211 // Validity ::= SEQUENCE {
212 // notBefore Time,
213 // notAfter Time }
214 BERSequenceDecoder validity(idCert);
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800215 {
Yingdi Yu80979ba2014-11-25 14:38:36 -0800216 BERDecodeTime(validity, m_notBefore);
217 BERDecodeTime(validity, m_notAfter);
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800218 }
Yingdi Yu80979ba2014-11-25 14:38:36 -0800219 validity.MessageEnd();
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700220
Yingdi Yu80979ba2014-11-25 14:38:36 -0800221 // Name ::= CHOICE {
222 // RDNSequence }
223 //
224 // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
225 m_subjectDescriptionList.clear();
226 BERSequenceDecoder name(idCert);
227 {
228 while (!name.EndReached())
229 {
230 m_subjectDescriptionList.push_back(CertificateSubjectDescription(name));
231 }
232 }
233 name.MessageEnd();
234
235 // SubjectPublicKeyInfo ::= SEQUENCE {
236 // algorithm AlgorithmIdentifier
237 // keybits BIT STRING }
238 m_key.decode(idCert);
239
240 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
241 //
242 // Extension ::= SEQUENCE {
243 // extnID OBJECT IDENTIFIER,
244 // critical BOOLEAN DEFAULT FALSE,
245 // extnValue OCTET STRING }
246 m_extensionList.clear();
247 if (!idCert.EndReached())
248 {
249 BERSequenceDecoder extensions(idCert);
250 {
251 while (!extensions.EndReached())
252 {
253 m_extensionList.push_back(CertificateExtension(extensions));
254 }
255 }
256 extensions.MessageEnd();
257 }
258 }
259
260 idCert.MessageEnd();
261 }
262 catch (CryptoPP::BERDecodeErr&) {
263 throw Error("Certificate Decoding Error");
264 }
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700265}
266
Yingdi Yu3e8b52e2014-11-26 22:05:00 -0800267/**
268 * @brief Output to stream with specified indent
269 *
270 * Based on http://stackoverflow.com/a/2212940/2150331
271 */
272class IndentedStream : public std::ostream
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700273{
Yingdi Yu3e8b52e2014-11-26 22:05:00 -0800274public:
275 IndentedStream(std::ostream& os, const std::string& indent = "")
276 : std::ostream(&m_buffer)
277 , m_buffer(os, indent)
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800278 {
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700279 }
280
Yingdi Yu3e8b52e2014-11-26 22:05:00 -0800281 ~IndentedStream()
282 {
283 flush();
284 }
285
286private:
287 // Write a stream buffer that prefixes each line with Plop
288 class StreamBuf : public std::stringbuf
289 {
290 public:
291 StreamBuf(std::ostream& os, const std::string& indent)
292 : m_output(os)
293 , m_indent(indent)
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800294 {
Alexander Afanasyev0ea6e082013-12-26 15:16:37 -0800295 }
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700296
Yingdi Yu3e8b52e2014-11-26 22:05:00 -0800297 virtual int
298 sync()
299 {
300 typedef boost::iterator_range<std::string::const_iterator> StringView;
301
302 const std::string& output = str();
303 std::vector<StringView> splitOutput;
304 boost::split(splitOutput, output, [] (const char& ch) { return ch == '\n'; });
305
306 if (!splitOutput.empty() && splitOutput.back().empty()) {
307 splitOutput.pop_back();
308 }
309 for (const StringView& line : splitOutput) {
310 m_output << m_indent << line << "\n";
311 }
312 return 0; // success
313 }
314 private:
315 std::ostream& m_output;
316 std::string m_indent;
317 };
318
319 StreamBuf m_buffer;
320};
321
322void
323Certificate::printCertificate(std::ostream& oss, const std::string& indent) const
324{
325 IndentedStream os(oss, indent);
326
327 os << "Certificate name:\n";
328 os << " " << getName() << "\n";
329 os << "Validity:\n";
330 {
331 os << " NotBefore: " << time::toIsoString(m_notBefore) << "\n";
332 os << " NotAfter: " << time::toIsoString(m_notAfter) << "\n";
333 }
334
335 os << "Subject Description:\n";
336 for (const auto& description : m_subjectDescriptionList)
337 os << " " << description.getOidString() << ": " << description.getValue() << "\n";
338
Yingdi Yu3b4c3142014-11-18 10:30:45 -0800339 os << "Public key bits: ";
340 switch (m_key.getKeyType()) {
341 case KEY_TYPE_RSA:
342 os << "(RSA)";
343 break;
344 case KEY_TYPE_ECDSA:
345 os << "(ECDSA)";
346 break;
347 default:
348 os << "(Unknown key type)";
349 break;
350 }
Yingdi Yu3e8b52e2014-11-26 22:05:00 -0800351 os << "\n";
Yingdi Yu3b4c3142014-11-18 10:30:45 -0800352
Yingdi Yu3e8b52e2014-11-26 22:05:00 -0800353 {
354 IndentedStream os2(os, " ");
355 CryptoPP::Base64Encoder encoder(new CryptoPP::FileSink(os2), true, 64);
356 m_key.encode(encoder);
357 }
358
359 os << "Signature Information:\n";
Yingdi Yu3b4c3142014-11-18 10:30:45 -0800360 {
361 os << " Signature Type: ";
362 switch (getSignature().getType()) {
363 case tlv::SignatureTypeValue::DigestSha256:
364 os << "DigestSha256";
365 break;
366 case tlv::SignatureTypeValue::SignatureSha256WithRsa:
367 os << "SignatureSha256WithRsa";
368 break;
369 case tlv::SignatureTypeValue::SignatureSha256WithEcdsa:
370 os << "SignatureSha256WithEcdsa";
371 break;
372 default:
373 os << "Unknown Signature Type";
374 }
Yingdi Yu3e8b52e2014-11-26 22:05:00 -0800375 os << "\n";
Yingdi Yu3b4c3142014-11-18 10:30:45 -0800376
377 if (getSignature().hasKeyLocator()) {
378 const KeyLocator& keyLocator = getSignature().getKeyLocator();
379 os << " Key Locator: ";
380 switch (keyLocator.getType()) {
381 case KeyLocator::KeyLocator_Name:
382 {
383 const Name& signerName = keyLocator.getName();
384 if (signerName.isPrefixOf(getName()))
385 os << "(Self-Signed) " << keyLocator.getName();
386 else
387 os << "(Name) " << keyLocator.getName();
388 break;
389 }
390 case KeyLocator::KeyLocator_KeyDigest:
391 os << "(KeyDigest)";
392 break;
393 case KeyLocator::KeyLocator_None:
394 os << "None";
395 break;
396 default:
397 os << "Unknown";
398 }
Yingdi Yu3e8b52e2014-11-26 22:05:00 -0800399 os << "\n";
Yingdi Yu3b4c3142014-11-18 10:30:45 -0800400 }
401 }
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700402}
Jeff Thompsona5dc3512013-10-17 10:26:19 -0700403
Yingdi Yu80979ba2014-11-25 14:38:36 -0800404std::ostream&
405operator<<(std::ostream& os, const Certificate& cert)
406{
407 cert.printCertificate(os);
408 return os;
409}
410
411
Yingdi Yufc40d872014-02-18 12:56:04 -0800412} // namespace ndn