blob: 823c994a4d0b01862d4caf42c7ac6ac4847148cb [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2013-2016 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
* ndn-cxx library is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received copies of the GNU General Public License and GNU Lesser
* General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
* <http://www.gnu.org/licenses/>.
*
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
*
* @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
* @author Jeff Thompson <jefft0@remap.ucla.edu>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
*/
#include "certificate.hpp"
#include "../../util/time.hpp"
#include "cryptopp.hpp"
#include "../../encoding/cryptopp/asn_ext.hpp"
#include "../../encoding/buffer-stream.hpp"
#include "../../util/concepts.hpp"
#include "../../util/indented-stream.hpp"
#include <boost/algorithm/string/split.hpp>
namespace ndn {
namespace security {
namespace v1 {
BOOST_CONCEPT_ASSERT((WireEncodable<Certificate>));
BOOST_CONCEPT_ASSERT((WireDecodable<Certificate>));
static_assert(std::is_base_of<tlv::Error, Certificate::Error>::value,
"Certificate::Error must inherit from tlv::Error");
Certificate::Certificate()
: m_notBefore(time::system_clock::TimePoint::max())
, m_notAfter(time::system_clock::TimePoint::min())
{
}
Certificate::Certificate(const Data& data)
// Use the copy constructor. It clones the signature object.
: Data(data)
{
decode();
}
Certificate::Certificate(const Block& block)
: Data(block)
{
decode();
}
Certificate::~Certificate()
{
}
void
Certificate::wireDecode(const Block& wire)
{
Data::wireDecode(wire);
decode();
}
bool
Certificate::isTooEarly()
{
if (time::system_clock::now() < m_notBefore)
return true;
else
return false;
}
bool
Certificate::isTooLate()
{
if (time::system_clock::now() > m_notAfter)
return true;
else
return false;
}
void
Certificate::encode()
{
// Name
// <key_name>/ID-CERT/<id#>
// Content
// DER encoded idCert:
//
// idCert ::= SEQUENCE {
// validity Validity,
// subject Name,
// subjectPubKeyInfo SubjectPublicKeyInfo,
// extension Extensions OPTIONAL }
//
// Validity ::= SEQUENCE {
// notBefore Time,
// notAfter Time }
//
// Name ::= CHOICE {
// RDNSequence }
//
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
//
// RelativeDistinguishedName ::=
// SET OF AttributeTypeAndValue
//
// SubjectPublicKeyInfo ::= SEQUENCE {
// algorithm AlgorithmIdentifier
// keybits BIT STRING }
//
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
//
// (see http://www.ietf.org/rfc/rfc3280.txt for more detail)
//
// KeyLocator
// issuer’s certificate name
// Signature
using namespace CryptoPP;
OBufferStream os;
CryptoPP::FileSink sink(os);
// idCert ::= SEQUENCE {
// validity Validity,
// subject Name,
// subjectPubKeyInfo SubjectPublicKeyInfo,
// extension Extensions OPTIONAL }
DERSequenceEncoder idCert(sink);
{
// Validity ::= SEQUENCE {
// notBefore Time,
// notAfter Time }
DERSequenceEncoder validity(idCert);
{
DEREncodeGeneralTime(validity, m_notBefore);
DEREncodeGeneralTime(validity, m_notAfter);
}
validity.MessageEnd();
// Name ::= CHOICE {
// RDNSequence }
//
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
DERSequenceEncoder name(idCert);
{
for (SubjectDescriptionList::iterator it = m_subjectDescriptionList.begin();
it != m_subjectDescriptionList.end(); ++it)
{
it->encode(name);
}
}
name.MessageEnd();
// SubjectPublicKeyInfo
m_key.encode(idCert);
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
//
// Extension ::= SEQUENCE {
// extnID OBJECT IDENTIFIER,
// critical BOOLEAN DEFAULT FALSE,
// extnValue OCTET STRING }
if (!m_extensionList.empty())
{
DERSequenceEncoder extensions(idCert);
{
for (ExtensionList::iterator it = m_extensionList.begin();
it != m_extensionList.end(); ++it)
{
it->encode(extensions);
}
}
extensions.MessageEnd();
}
}
idCert.MessageEnd();
setContent(os.buf());
setContentType(tlv::ContentType_Key);
}
void
Certificate::decode()
{
using namespace CryptoPP;
try {
OBufferStream os;
StringSource source(getContent().value(), getContent().value_size(), true);
// idCert ::= SEQUENCE {
// validity Validity,
// subject Name,
// subjectPubKeyInfo SubjectPublicKeyInfo,
// extension Extensions OPTIONAL }
BERSequenceDecoder idCert(source);
{
// Validity ::= SEQUENCE {
// notBefore Time,
// notAfter Time }
BERSequenceDecoder validity(idCert);
{
BERDecodeTime(validity, m_notBefore);
BERDecodeTime(validity, m_notAfter);
}
validity.MessageEnd();
// Name ::= CHOICE {
// RDNSequence }
//
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
m_subjectDescriptionList.clear();
BERSequenceDecoder name(idCert);
{
while (!name.EndReached())
{
m_subjectDescriptionList.push_back(CertificateSubjectDescription(name));
}
}
name.MessageEnd();
// SubjectPublicKeyInfo ::= SEQUENCE {
// algorithm AlgorithmIdentifier
// keybits BIT STRING }
m_key.decode(idCert);
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
//
// Extension ::= SEQUENCE {
// extnID OBJECT IDENTIFIER,
// critical BOOLEAN DEFAULT FALSE,
// extnValue OCTET STRING }
m_extensionList.clear();
if (!idCert.EndReached())
{
BERSequenceDecoder extensions(idCert);
{
while (!extensions.EndReached())
{
m_extensionList.push_back(CertificateExtension(extensions));
}
}
extensions.MessageEnd();
}
}
idCert.MessageEnd();
}
catch (CryptoPP::BERDecodeErr&) {
BOOST_THROW_EXCEPTION(Error("Certificate Decoding Error"));
}
}
void
Certificate::printCertificate(std::ostream& oss, const std::string& indent) const
{
util::IndentedStream os(oss, indent);
os << "Certificate name:\n";
os << " " << getName() << "\n";
os << "Validity:\n";
{
os << " NotBefore: " << time::toIsoString(m_notBefore) << "\n";
os << " NotAfter: " << time::toIsoString(m_notAfter) << "\n";
}
os << "Subject Description:\n";
for (const auto& description : m_subjectDescriptionList)
os << " " << description.getOidString() << ": " << description.getValue() << "\n";
os << "Public key bits: ";
switch (m_key.getKeyType()) {
case KeyType::RSA:
os << "(RSA)";
break;
case KeyType::EC:
os << "(ECDSA)";
break;
default:
os << "(Unknown key type)";
break;
}
os << "\n";
{
util::IndentedStream os2(os, " ");
CryptoPP::Base64Encoder encoder(new CryptoPP::FileSink(os2), true, 64);
m_key.encode(encoder);
}
os << "Signature Information:\n";
{
os << " Signature Type: ";
switch (getSignature().getType()) {
case tlv::SignatureTypeValue::DigestSha256:
os << "DigestSha256";
break;
case tlv::SignatureTypeValue::SignatureSha256WithRsa:
os << "SignatureSha256WithRsa";
break;
case tlv::SignatureTypeValue::SignatureSha256WithEcdsa:
os << "SignatureSha256WithEcdsa";
break;
default:
os << "Unknown Signature Type";
}
os << "\n";
if (getSignature().hasKeyLocator()) {
const KeyLocator& keyLocator = getSignature().getKeyLocator();
os << " Key Locator: ";
switch (keyLocator.getType()) {
case KeyLocator::KeyLocator_Name:
{
const Name& signerName = keyLocator.getName();
if (signerName.isPrefixOf(getName()))
os << "(Self-Signed) " << keyLocator.getName();
else
os << "(Name) " << keyLocator.getName();
break;
}
case KeyLocator::KeyLocator_KeyDigest:
os << "(KeyDigest)";
break;
case KeyLocator::KeyLocator_None:
os << "None";
break;
default:
os << "Unknown";
}
os << "\n";
}
}
}
std::ostream&
operator<<(std::ostream& os, const Certificate& cert)
{
cert.printCertificate(os);
return os;
}
} // namespace v1
} // namespace security
} // namespace ndn