blob: 726d41da0d7342135e97784c1e8db028b57e9d3d [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
/**
* Copyright (C) 2013 Regents of the University of California.
* @author: Yingdi Yu <yingdi@cs.ucla.edu>
* @author: Jeff Thompson <jefft0@remap.ucla.edu>
* See COPYING for copyright and distribution information.
*/
#if 1 // TODO: Remove this when we don't throw "not implemented".
#include <stdexcept>
#endif
#include "der-exception.hpp"
#include "../../util/logging.hpp"
#include "der.hpp"
INIT_LOGGER("ndn.der.DER");
using namespace std;
using namespace ndn::ptr_lib;
namespace ndn {
namespace der {
/*
* DerNode
*/
DerNode::DerNode()
:parent_(0)
{}
DerNode::DerNode(DerType type)
:type_(type),
parent_(0)
{}
DerNode::DerNode(std::istream& start)
:parent_(0)
{
decode(start);
}
DerNode::~DerNode()
{}
void
DerNode::encodeHeader(int size)
{
header_.push_back((char)type_);
if(size >= 127)
{
int val = size;
char buf[sizeof(val) + 1];
char *p = &(buf[sizeof(buf)-1]);
int n = 0;
int mask = (1 << 8) - 1;
while(val != 0)
{
p[0] = (char)(val & mask);
p--;
n++;
val >>= 8;
}
p[0] = (char)((1<<7) | n);
n++;
header_.insert(header_.end(), p, p+n);
}
else if(size >= 0)
{
header_.push_back((char)size);
}
else
throw NegativeLengthException("Negative length");
}
int
DerNode::decodeHeader(istream& start)
{
uint8_t type = start.get();
// char type = start.get();
header_.push_back(type);
type_ = static_cast<DerType>((int)type);
uint8_t sizeLen = start.get();
// char sizeLen = start.get();
header_.push_back(sizeLen);
bool longFormat = sizeLen & (1 << 7);
if(!longFormat)
{
// _LOG_DEBUG("Short Format");
// _LOG_DEBUG("sizeLen: " << (int)sizeLen);
return (int)sizeLen;
}
else
{
// _LOG_DEBUG("Long Format");
uint8_t byte;
// char byte;
int lenCount = sizeLen & ((1<<7) - 1);
// _LOG_DEBUG("sizeLen: " << (int)sizeLen);
// _LOG_DEBUG("mask: " << (int)((1<<7) - 1));
// _LOG_DEBUG("lenCount: " << (int)lenCount);
int size = 0;
do
{
byte = start.get();
header_.push_back(byte);
size = size * 256 + (int)byte;
// _LOG_DEBUG("byte: " << (int)byte);
// _LOG_DEBUG("size: " << size);
lenCount--;
}
while(lenCount > 0);
return size;
}
}
void
DerNode::encode(ostream& start)
{
start.write((const char*)&header_[0], header_.size());
start.write((const char*)&payload_[0], payload_.size());
}
void
DerNode::decode(istream& start)
{
int payloadSize = decodeHeader(start);
// _LOG_DEBUG("payloadSize: " << payloadSize);
if(payloadSize > 0 )
{
char buf[payloadSize];
start.read(buf, payloadSize);
payload_.insert(payload_.end(), buf, buf + payloadSize);
}
}
shared_ptr<DerNode>
DerNode::parse(istream& start)
{
int type = ((uint8_t)start.peek());
// _LOG_DEBUG("Type: " << hex << setw(2) << setfill('0') << type);
switch(type) {
case DER_BOOLEAN:
return shared_ptr<DerBool>(new DerBool(start));
case DER_INTEGER:
return shared_ptr<DerInteger>(new DerInteger(start));
case DER_BIT_STRING:
return shared_ptr<DerBitString>(new DerBitString(start));
case DER_OCTET_STRING:
return shared_ptr<DerOctetString>(new DerOctetString(start));
case DER_NULL:
return shared_ptr<DerNull>(new DerNull(start));
case DER_OBJECT_IDENTIFIER:
return shared_ptr<DerOid>(new DerOid(start));
case DER_SEQUENCE:
return shared_ptr<DerSequence>(new DerSequence(start));
case DER_PRINTABLE_STRING:
return shared_ptr<DerPrintableString>(new DerPrintableString(start));
case DER_GENERALIZED_TIME:
return shared_ptr<DerGtime>(new DerGtime(start));
default:
throw DerDecodingException("Unimplemented DER types");
}
}
/*
* DerComplex
*/
DerComplex::DerComplex()
:DerNode(),
childChanged_(false),
size_(0)
{}
DerComplex::DerComplex(DerType type)
:DerNode(type),
childChanged_(false),
size_(0)
{}
DerComplex::DerComplex(istream& start)
:DerNode(),
childChanged_(false),
size_(0)
{
size_ = DerNode::decodeHeader(start);
// _LOG_DEBUG("Size: " << size_);
int accSize = 0;
while(accSize < size_)
{
// _LOG_DEBUG("accSize: " << accSize);
shared_ptr<DerNode> nodePtr = DerNode::parse(start);
accSize += nodePtr->getSize();
addChild(nodePtr, false);
}
}
DerComplex::~DerComplex()
{}
int
DerComplex::getSize()
{
if(childChanged_)
{
updateSize();
childChanged_ = false;
}
header_.clear();
DerNode::encodeHeader(size_);
return size_ + header_.size();
}
shared_ptr<vector<uint8_t> >
DerComplex::getRaw()
{
shared_ptr<vector<uint8_t> > blob(new vector<uint8_t>());
blob->insert(blob->end(), header_.begin(), header_.end());
DerNodePtrList::iterator it = nodeList_.begin();
for(; it != nodeList_.end(); it++)
{
shared_ptr<vector<uint8_t> > childBlob = (*it)->getRaw();
blob->insert(blob->end(), childBlob->begin(), childBlob->end());
}
return blob;
}
void
DerComplex::updateSize()
{
int newSize = 0;
DerNodePtrList::iterator it = nodeList_.begin();
for(; it != nodeList_.end(); it++)
{
newSize += (*it)->getSize();
}
size_ = newSize;
childChanged_ = false;
}
void
DerComplex::addChild(shared_ptr<DerNode> nodePtr, bool notifyParent)
{
nodePtr->setParent(this);
nodeList_.push_back(nodePtr);
if(!notifyParent)
return;
if(childChanged_)
return;
else
childChanged_ = true;
if(0 != parent_)
parent_->setChildChanged();
}
void
DerComplex::setChildChanged()
{
if(0 != parent_ && !childChanged_)
{
parent_->setChildChanged();
childChanged_ = true;
}
else
childChanged_ = true;
}
void
DerComplex::encode(ostream& start)
{
updateSize();
header_.clear();
DerNode::encodeHeader(size_);
start.write((const char*)&header_[0], header_.size());
DerNodePtrList::iterator it = nodeList_.begin();
for(; it != nodeList_.end(); it++)
(*it)->encode(start);
}
/*
* DerByteString
*/
DerByteString::DerByteString(const string& str, DerType type)
:DerNode(type)
{
payload_.insert(payload_.end(), str.begin(), str.end());
DerNode::encodeHeader(payload_.size());
}
DerByteString::DerByteString(const std::vector<uint8_t>& blob, DerType type)
:DerNode(type)
{
payload_.insert(payload_.end(), blob.begin(), blob.end());
DerNode::encodeHeader(payload_.size());
}
DerByteString::DerByteString(istream& start)
:DerNode(start)
{}
DerByteString::~DerByteString()
{}
/*
* DerBool
*/
DerBool::DerBool(bool value)
:DerNode(DER_BOOLEAN)
{
char payload = (value ? 0xFF : 0x00);
payload_.push_back(payload);
DerNode::encodeHeader(payload_.size());
}
DerBool::DerBool(istream& start)
:DerNode(start)
{}
DerBool::~DerBool()
{}
/*
* DerInteger
*/
DerInteger::DerInteger(const vector<uint8_t>& blob)
:DerNode(DER_INTEGER)
{
payload_.insert(payload_.end(), blob.begin(), blob.end());
DerNode::encodeHeader(payload_.size());
}
DerInteger::DerInteger(istream& start)
:DerNode(start)
{}
DerInteger::~DerInteger()
{}
/*
* DerBitString
*/
DerBitString::DerBitString(const vector<uint8_t>& blob, uint8_t paddingLen)
:DerNode(DER_BIT_STRING)
{
payload_.push_back((char)paddingLen);
payload_.insert(payload_.end(), blob.begin(), blob.end());
DerNode::encodeHeader(payload_.size());
}
DerBitString::DerBitString(istream& start)
:DerNode(start)
{}
DerBitString::~DerBitString()
{}
/*
* DerOctetString
*/
DerOctetString::DerOctetString(const string& str)
:DerByteString(str, DER_OCTET_STRING)
{}
DerOctetString::DerOctetString(const vector<uint8_t>& blob)
:DerByteString(blob, DER_OCTET_STRING)
{}
DerOctetString::DerOctetString(istream& start)
:DerByteString(start)
{}
DerOctetString::~DerOctetString()
{}
/*
* DerNull
*/
DerNull::DerNull()
:DerNode(DER_NULL)
{
DerNode::encodeHeader(0);
}
DerNull::DerNull(istream& start)
:DerNode(start)
{}
DerNull::~DerNull()
{}
/*
* DerOid
*/
DerOid::DerOid(const OID& oid)
:DerNode(DER_OBJECT_IDENTIFIER)
{
prepareEncoding(oid.getIntegerList());
}
DerOid::DerOid(const string& oidStr)
:DerNode(DER_OBJECT_IDENTIFIER)
{
vector<int> value;
string str = oidStr + ".";
size_t pos = 0;
size_t ppos = 0;
while(string::npos != pos){
ppos = pos;
pos = str.find_first_of('.', pos);
if(string::npos == pos)
break;
value.push_back(atoi(str.substr(ppos, pos - ppos).c_str()));
pos++;
}
prepareEncoding(value);
}
DerOid::DerOid(const vector<int>& value)
:DerNode(DER_OBJECT_IDENTIFIER)
{
prepareEncoding(value);
}
DerOid::DerOid(istream& start)
:DerNode(start)
{}
DerOid::~DerOid()
{}
void
DerOid::prepareEncoding(const vector<int>& value)
{
ostringstream os;
int firstNumber = 0;
if(value.size() >= 1){
if(0 <= value[0] && 2 >= value[0])
firstNumber = value[0] * 40;
else
throw DerEncodingException("first integer of oid is out of range");
}
else
throw DerEncodingException("no integer in oid");
if(value.size() >= 2){
if(0 <= value[1] && 39 >= value[1])
firstNumber += value[1];
else
throw DerEncodingException("second integer of oid is out of range");
}
encode128(firstNumber, os);
if(value.size() > 2){
int i = 2;
for(; i < value.size(); i++)
encode128(value[i], os);
}
string output = os.str();
DerNode::encodeHeader(output.size());
payload_.insert(payload_.end(), output.begin(), output.end());
}
void
DerOid::encode128(int value, ostringstream& os)
{
int mask = (1 << 7) - 1;
if(128 > value)
{
uint8_t singleByte = (uint8_t) mask & value;
os.write((char *)&singleByte, 1);
}
else{
uint8_t buf[(sizeof(value)*8 + 6)/7 + 1];
uint8_t *p = &(buf[sizeof(buf)-1]);
int n = 1;
p[0] = (uint8_t)(value & mask);
value >>= 7;
while(value != 0)
{
(--p)[0] = (uint8_t)((value & mask) | (1 << 7));
n++;
value >>= 7;
}
os.write((char *)p, n);
}
}
int
DerOid::decode128(int & offset)
{
uint8_t flagMask = 0x80;
int result = 0;
while(payload_[offset] & flagMask){
result = 128 * result + (uint8_t) payload_[offset] - 128;
offset++;
}
result = result * 128 + payload_[offset];
offset++;
return result;
}
/*
* DerSequence
*/
DerSequence::DerSequence()
:DerComplex(DER_SEQUENCE)
{}
DerSequence::DerSequence(istream& start)
:DerComplex(start)
{}
DerSequence::~DerSequence()
{}
/*
* DerPrintableString
*/
DerPrintableString::DerPrintableString(const string& str)
:DerByteString(str, DER_PRINTABLE_STRING)
{}
DerPrintableString::DerPrintableString(const vector<uint8_t>& blob)
:DerByteString(blob, DER_PRINTABLE_STRING)
{}
DerPrintableString::DerPrintableString(istream& start)
:DerByteString(start)
{}
DerPrintableString::~DerPrintableString()
{}
/*
* DerGtime
*/
DerGtime::DerGtime(const Time& time)
:DerNode(DER_GENERALIZED_TIME)
{
string pTimeStr = toIsoString(time);
int index = pTimeStr.find_first_of('T');
string derTime = pTimeStr.substr(0, index) + pTimeStr.substr(index+1, pTimeStr.size() - index -1) + "Z";
payload_.insert(payload_.end(), derTime.begin(), derTime.end());
DerNode::encodeHeader(payload_.size());
}
DerGtime::DerGtime(istream& start)
:DerNode(start)
{}
DerGtime::~DerGtime()
{}
string DerGtime::toIsoString(const Time& time)
{
#if 1
throw std::runtime_error("not implemented");
#endif
}
Time DerGtime::fromIsoString(const string& isoString)
{
#if 1
throw std::runtime_error("not implemented");
#endif
}
} // der
}