42 #include <encode/base64.h>
45 static void throw_err(
const std::string& msg)
47 throw std::runtime_error(msg);
50 using namespace scc::crypto;
56 return os.write(b.
str().c_str(), b.
str().size());
61 auto id = tag & DerBase::id_mask;
62 auto cls = tag & DerBase::class_mask;
66 return BasePtr(
new DerBase(tag));
72 case DerBase::type_sequence:
74 case DerBase::type_set:
75 return BasePtr(
new DerSet(tag));
76 case DerBase::type_integer:
78 case DerBase::type_bit_string:
80 case DerBase::type_octet_string:
82 case DerBase::type_utf8_string:
84 case DerBase::type_printable_string:
86 case DerBase::type_ia5_string:
88 case DerBase::type_bmp_string:
90 case DerBase::type_universal_string:
92 case DerBase::type_teletex_string:
94 case DerBase::type_null:
95 return BasePtr(
new DerNull(tag));
96 case DerBase::type_boolean:
98 case DerBase::type_utc_time:
100 case DerBase::type_generalized_time:
102 case DerBase::type_object_identifier:
106 return BasePtr(
new DerBase(tag));
112 if ((m_tag & DerBase::id_mask) == DerBase::id_mask)
145 if (v.size() <
pre_len()) throw_err(
"dump_pre vector too small");
150 if ((m_tag & DerBase::id_mask) == DerBase::id_mask)
153 std::stack<uint8_t> bs;
166 if (!bs.empty()) b |= 0x80;
180 std::stack<uint8_t> bs;
188 v[++idx] = 0x80 | bs.size();
202 if (v.size() < m_dat.size()) throw_err(
"dump_data vector too small");
204 for (
size_t i = 0; i < m_dat.size(); i++)
206 v[i] =
static_cast<char>(m_dat[i]);
217 return typeid(*this) ==
typeid(
DerSet);
222 if (!
is_contain()) throw_err(
"invalid cast attempt to container");
224 return *
dynamic_cast<std::vector<BasePtr>*
>(
this);
234 if (!
is_integer()) throw_err(
"invalid cast attempt to integer");
246 if (!
is_bit_string()) throw_err(
"invalid cast attempt to bit string");
293 if (!
is_string()) throw_err(
"invalid cast attempt to string");
300 if (!
is_string()) throw_err(
"invalid cast attempt to string");
307 if (!
is_string()) throw_err(
"invalid cast attempt to string");
314 if (!
is_string()) throw_err(
"invalid cast attempt to string");
321 if (!
is_string()) throw_err(
"invalid cast attempt to string");
328 if (!
is_string()) throw_err(
"invalid cast attempt to string");
335 return typeid(*this) ==
typeid(
DerNull);
345 if (!
is_boolean()) throw_err(
"invalid cast attempt to boolean");
347 return dynamic_cast<DerBoolean*
>(
this)->get();
352 if (!
is_boolean()) throw_err(
"invalid cast attempt to boolean");
354 return dynamic_cast<DerBoolean*
>(
this)->set(v);
369 if (!
is_time()) throw_err(
"invalid cast attempt to time");
381 if (!
is_object_id()) throw_err(
"invalid cast attempt to object id");
388 if (!
is_object_id()) throw_err(
"invalid cast attempt to object id");
403 return "type_boolean";
405 return "type_integer";
406 case type_bit_string:
407 return "type_bit_string";
408 case type_octet_string:
409 return "type_octet_string";
412 case type_object_identifier:
413 return "type_object_identifier";
414 case type_utf8_string:
415 return "type_utf8_string";
417 return "type_sequence";
420 case type_printable_string:
421 return "type_printable_string";
422 case type_ia5_string:
423 return "type_ia5_string";
425 return "type_utc_time";
426 case type_generalized_time:
427 return "type_generalized_time";
428 case type_bmp_string:
429 return "type_bmp_string";
439 return "class_universal";
440 case class_application:
441 return "class_application";
443 return "class_context";
445 return "class_private";
454 return "constructed";
468 DerContainerBase::~DerContainerBase()
475 for (
auto& i : *
this)
487 s <<
" items " << size();
492 static std::string bin_print(
const void* xloc,
size_t len,
size_t max=12)
494 char* loc = (
char*)xloc;
520 static std::string bn_print(
const Bignum& bn,
unsigned max=8)
522 std::vector<char> v(bn.
len_2sc(),
'\0');
524 bn.
get_2sc((
char*)v.data(), v.size());
526 return Hex::bin_to_hexstr(v.data(), v.size(),
":", max);
533 s <<
" str " << bin_print(m_dat.data(), m_dat.size());
535 s <<
" hex " << Hex::bin_to_hexstr(m_dat.data(), m_dat.size(),
":", 8);
540 std::string DerObjectIdentifier::oid_str()
const
544 if (!m_v.size())
return "(empty oid)";
551 std::ostream&
operator<<(std::ostream& s,
const std::vector<uint32_t>& oid)
553 std::stringstream outs;
555 for (
auto i = oid.begin(); i != oid.end(); ++i)
557 if (i != oid.begin()) s <<
".";
565 if (v.size() == 0) throw_err(
"oid parse error no data");
574 std::stack<uint8_t> bs;
579 if ((x & 0x80) == 0)
break;
585 nxt += bs.top()*mult;
598 for (
size_t i = 0; i < m_v.size(); i++)
601 if (i != m_v.size()-1) s <<
".";
610 for (
size_t i = 2; i < m_v.size(); i++)
625 v[0] = m_v[0]*40 + m_v[1];
628 for (
size_t i = 2; i < m_v.size(); i++)
631 std::stack<uint8_t> bs;
641 auto x = bs.top() | 0x80;
643 if (bs.empty()) x &= ~0x80;
651 if (v.size() < 2) throw_err(
"oid missing v1 and v2 values");
652 if (v[0] > 2) throw_err(
"oid v1 out of range");
653 if (v[1] > 39) throw_err(
"oid v2 out of range");
663 memset(&tm, 0,
sizeof(tm));
665 tm.tm_year = year - 1900;
666 tm.tm_mon = month - 1;
669 tm.tm_min = minute - tzmins;
680 memset(&tm, 0,
sizeof(tm));
682 tm.tm_year = year - 1900;
683 tm.tm_mon = month - 1;
699 localtime_r(&t, &tm);
700 s <<
" local " << std::put_time(&tm,
"%F %T %Z");
702 s <<
" utc " << std::put_time(&tm,
"%F %T UTC");
708 static bool parse_tz(std::string& v,
int& tzval,
int& tzsize)
710 auto p = v.find_last_of(
"+-Z");
712 if (p == std::string::npos)
return false;
718 if (tzsize > 1)
throw std::runtime_error(std::string(
"timezone parse error on ")+v);
725 if (v[p] !=
'+' && v[p] !=
'-')
throw std::runtime_error(std::string(
"timezone parse error on ")+v);
727 int sign = 1, hh, mm = 0;
728 if (v[p] ==
'-') sign = -1;
730 int n = sscanf(&v[p+1],
"%02d%02d", &hh, &mm);
732 if (n < 1)
throw std::runtime_error(std::string(
"timezone parse error on ")+v);
735 tzval = sign*(hh*60+mm);
741 std::string v(&vin[0], &vin[0]+vin.size());
745 if (!parse_tz(v, tzmins, tzsize)) throw_err(
"utc time no timezone found");
747 if (tzsize > 1 && tzsize < 5) throw_err(
"utc time requires Z or +/-HHMM format");
749 unsigned yy, mm, dd, h, m, s = 0;
751 if (v.size() != 10 && v.size() != 12) throw_err(
"utc time parse error YYMMDDhhmm[ss] wrong size");
753 int n = sscanf(&v[0],
"%02u%02u%02u%02u%02u%02u", &yy, &mm, &dd, &h, &m, &s);
754 if (n < 5) throw_err(
"utc time parse error YYMMDDhhmm[ss]");
756 set_time(yy < 70 ? yy + 2000 : yy + 1900, mm, dd, h, m, s, tzmins);
765 sprintf(&v[0],
"%02d%02d%02d%02d%02d",
766 tm.tm_year < 70 ? tm.tm_year : tm.tm_year-100,
775 sprintf(&v[idx],
"%02d", tm.tm_sec);
794 static double parse_frac(std::string& v)
796 auto p = v.find_last_of(
",.");
797 if (p == std::string::npos)
return 0.0;
802 int n = sscanf(&v[p],
"%lf", &ret);
804 if (n < 1)
return -1.0;
812 std::string v(&vin[0], &vin[0]+vin.size());
816 bool tzpost = parse_tz(v, tzmins, tzsize);
818 double frac = parse_frac(v);
819 if (frac < 0.0) throw_err(
"generalized time parse error fractional part");
821 if (v.size() < 10) throw_err(
"generalized time parse error YYYYMMDDhh wrong size");
823 unsigned yy, mm, dd, h, m=0, s=0;
825 int n = sscanf(&v[0],
"%4u%02u%02u%02u", &yy, &mm, &dd, &h);
826 if (n < 4) throw_err(
"generalized time parse error YYYYMMDDhh");
835 else if (v.size() == 12)
837 n = sscanf(&v[10],
"%02u", &m);
838 if (n < 1) throw_err(
"generalized time parse error mm");
845 else if (v.size() == 14)
847 n = sscanf(&v[10],
"%02u%02u", &m, &s);
848 if (n < 2) throw_err(
"generalized time parse error mmss");
854 throw_err(
"generalized time parse error YYYYMMDDhh wrong size");
859 set_time(yy, mm, dd, h, m, s, tzmins);
873 sprintf(&v[0],
"%4d%02d%02d%02d",
882 sprintf(&v[idx],
"%02d", tm.tm_min);
887 sprintf(&v[idx],
"%02d", tm.tm_sec);
910 m_val.resize(v.size());
912 for (
size_t i = 0; i < v.size(); i++)
922 s <<
" str " << bin_print(m_val.data(), m_val.size(), 80);
930 for (
size_t i = 0; i < m_val.size(); i++)
938 m_bool = v[0] == 0 ? false :
true;
943 v[0] = m_bool ? 1 : 0;
955 s <<
" width " << m_bn.
width();
957 if (m_bn <= 0xffffffff)
959 s <<
" dec " << m_bn;
962 s <<
" hex " << bn_print(m_bn);
987 if (pad > 7) throw_err(
"bit string parse error pad bits too high");
990 width((v.size()-1)*8 - pad);
994 for (
size_t i = 1; i < v.size(); i++)
996 m_data.at(i-1)= v[i];
1002 if (m_data.size() <
len()-1) throw_err(
"bitstring data_str invalid size");
1004 std::stringstream s;
1008 s <<
" hex " << Hex::bin_to_hexstr(m_data.data(), m_data.size(),
":", 12);
1013 uint32_t w =
width();
1014 for (uint32_t i = 0; i < w; i++)
1035 return m_data.size() + 1;
1042 for (
size_t i = 1; i < v.size(); i++)
1044 v[i] = m_data.at(i-1);
1050 if (!ctx->context_class() || !ctx->constructed()) throw_err(
"context_to_explicit() element must be constructed and context-class");
1061 std::vector<uint8_t> v;
1064 BasePtr base =
DerBase::create(DerBase::construct_mask|DerBase::class_context);
1074 if (
id >= DerBase::id_mask) throw_err(
"context_to_implicit() invalid id");
1075 if (ctx->id() >= DerBase::id_mask) throw_err(
"context_to_implicit() context multi-byte id not supported");
1076 if (!ctx->context_class()) throw_err(
"context_to_implicit() context element must be context-class");
1078 std::vector<uint8_t> v;
1081 v[0] = (v[0] & ~
DerBase::id_mask) |
id;
1083 v[0] &= ~class_mask;
1092 if (
id >= DerBase::id_mask) throw_err(
"implicit_to_context() invalid id");
1093 if (orig->id() >= DerBase::id_mask) throw_err(
"implicit_to_context() original multi-byte id not supported");
1094 if (!orig->uni_class()) throw_err(
"implicit_to_context() original element must be universal class");
1096 std::vector<uint8_t> v;
1099 v[0] = (v[0] & ~
DerBase::id_mask) |
id;
1100 v[0] |= class_context;
1109 if (off == v.size()) throw_err(
"create() no tag byte");
1113 base->m_eloff = off;
1116 if ((v[off] & DerBase::id_mask) == DerBase::id_mask)
1118 std::stack<uint8_t> idb;
1121 if (++off == v.size()) throw_err(
"create() no id byte");
1123 idb.push(v[off] & 0x7f);
1125 while ((v[off] & 0x80) == 0x80);
1130 while (!idb.empty())
1141 if (++off == v.size()) throw_err(
"create() no length byte");
1144 if (v[off] & DerBase::length_multi_mask)
1146 size_t lensz = v[off] & DerBase::length_bytes_mask;
1153 throw_err(
"create() indefinite-sized elements not supported by DER");
1156 for (
size_t i = 0; i < lensz; i++)
1158 if (++off == v.size()) throw_err(
"create() insufficient extended length bytes");
1160 base->m_elsz += ((size_t)v[off] << (lensz-i-1)*8);
1165 base->m_elsz = v[off];
1177 if (!base->is_contain())
1179 if (off+base->hdrsz()+base->elsz() > binv.size()) throw_err(base->name()+
" parse_element binary size mismatch");
1182 base->parse(std::vector<uint8_t>(&binv[off+base->hdrsz()], &binv[off+base->hdrsz()+base->elsz()]));
1189 off += base->hdrsz();
1190 size_t elsz = off+base->elsz();
1195 base->contain().emplace_back(nxt);
1196 off += nxt->hdrsz() + nxt->elsz();
1202 for (
auto& e : base->contain())
1204 allsz += e->hdrsz()+e->elsz();
1206 if (allsz != base->elsz()) throw_err(
"parse_element() container/element size mismatch");
1211 void DerDocument::parse_bin()
1220 m_bin.insert(m_bin.begin(), v.begin(), v.end());
1233 if (base ==
nullptr)
return;
1235 std::vector<char> v(base->pre_len());
1237 vout.insert(vout.end(), v.begin(), v.end());
1239 if (base->is_contain())
1241 for (
auto& x : base->contain())
1243 if (base ==
nullptr) throw_err(
"dump_element() container element with null object");
1250 v.resize(base->len());
1252 vout.insert(vout.end(), v.begin(), v.end());
1258 if (m_root ==
nullptr && other.m_root ==
nullptr)
1263 std::vector<char> d1, d2;
1268 bool ret = (d1 == d2);
1270 explicit_bzero(d1.data(), d1.size());
1271 explicit_bzero(d2.data(), d2.size());
1278 if (m_root ==
nullptr) throw_err(
"dump() called on empty document");
1281 vout.insert(vout.end(), m_bin.begin(), m_bin.end());
1284 void DerDocument::dump_bin()
1299 if (m_root ==
nullptr) throw_err(
"root() called on empty document");
1308 std::stringstream s;
1311 static void print_helper(PHelp& ph, BasePtr base,
int level)
1313 if (base ==
nullptr)
1319 for (
int i = 0; i < level; i++) ph.s << ph.indent;
1321 if (ph.debug) ph.s <<
"(" << base->eloff() <<
"," << base->elsz() <<
"," << base->hdrsz() <<
") ";
1325 if (base->is_contain())
1328 for (
auto& x : base->contain())
1331 print_helper(ph, x, level);
1342 print_helper(ph, base, 0);
1349 std::stringstream s;
1351 if (debug) s <<
"bin_sz(" << m_bin.size() <<
") " << std::endl;
1358 return os.write(b.
str().c_str(), b.
str().size());
1363 std::stringstream sst;
1364 sst.write(v.data(), v.size());
1366 explicit_bzero(sst.str().data(), sst.str().size());
1372 const auto np = std::string::npos;
1377 if (!std::getline(sst, l)) throw_err(
"PEM input end of stream before BEGIN");
1378 auto p = l.find(
"-----BEGIN ");
1383 auto ep = l.rfind(
"-----");
1384 if (ep == np) throw_err(
"PEM input BEGIN line does not end with -----");
1386 m_label = l.substr(11, l.size()-16);
1388 if (m_label.empty()) throw_err(
"PEM input zero length label");
1391 m_chars_per_line = 0;
1392 while (std::getline(sst, l))
1394 auto p = l.find(
"-----END ");
1397 ep = l.rfind(
"-----");
1398 if (ep == np) throw_err(
"PEM input END does not end with -----");
1400 auto elabel = l.substr(9, l.size()-14);
1402 if (m_label != elabel) throw_err(
"PEM input BEGIN and END labels do not match");
1404 if (!Base64::base64_decode(b64, m_bin)) throw_err(
"PEM input invalid base64 data");
1406 explicit_bzero(b64.data(), b64.size());
1411 b64.insert(b64.end(), l.begin(), l.end());
1412 if (m_chars_per_line < l.size())
1414 m_chars_per_line = l.size();
1419 throw_err(
"PEM input no ----END found");
1424 if (m_chars_per_line == 0) throw_err(
"PEM output cannot have 0 chars per line");
1425 if (m_label.empty()) throw_err(
"PEM output zero length label");
1427 DerDocument::dump_bin();
1430 Base64::base64_encode(m_bin, b64);
1433 l =
"-----BEGIN "+m_label+
"-----\n";
1434 v.insert(v.end(), l.begin(), l.end());
1436 size_t sz = b64.size(), towrite = sz;
1439 size_t lw = towrite > m_chars_per_line ? m_chars_per_line : towrite;
1440 v.insert(v.end(), &b64[sz-towrite], &b64[sz-towrite]+lw);
1442 v.insert(v.end(),
'\n');
1445 l =
"-----END "+m_label+
"-----\n";
1446 v.insert(v.end(), l.begin(), l.end());
1448 explicit_bzero(b64.data(), b64.size());
void set_2sc(const void *, int)
Set integer from twos complement input stream.
void get_2sc(void *, int) const
Octal (byte) output, in two's complement form.
int width() const
Number of significant bits.
int len_2sc() const
Get the length of octal output in twos complement form.
bool is_bit_set(uint32_t bit) const
Is the bit set? First bit is bit 0.
uint32_t pad_bits() const
Number of padding bits (0 bits at the end of the last byte).
uint32_t width() const
Bit width.
scc::crypto::Bignum & integer()
Return reference to a scc::crypto::Bignum.
bool is_utf8_string() const
Is this a utf8 (ascii) string?
BitString & bit_string()
Return reference to bit string.
bool is_teletex_string() const
Is this a teletex (ascii) string?
virtual void dump_pre(std::vector< char > &) const
Dump prefix data.
time_t time_epoch()
Epoch time.
virtual std::string data_str() const
Print vizualized data.
static BasePtr explicit_to_context(const BasePtr &BasePtr, uint32_t)
Convert explicit to context.
static BasePtr implicit_to_context(const BasePtr &BasePtr, uint32_t)
Convert implicit to context.
bool is_bit_string() const
Is this a DerBitString?
bool is_universal_string() const
Is this a univeral (ascii) string?
bool is_visible_string() const
Is this a visible (ascii) string?
DerBase(uint8_t tag=0)
Construct a base object.
oid_value object_id()
Return the object identifier value.
static BasePtr context_to_explicit(const BasePtr &)
Change a context element to explicit.
bool is_boolean() const
Is this a boolean type?
bool is_utc_time() const
Is this a utc time type?
bool is_contain() const
Is this a container type?
bool boolean()
Return boolean value.
bool is_octet_string() const
Is this an octet string (8 bit chars)?
static BasePtr create(int)
Create a base pointer, using only the tag byte.
size_t pre_len() const
The length of prefix bytes (tag/id, and length).
bool is_seq() const
Is this a sequence type?
bool is_string() const
Is this a generic ascii string?
bool is_time() const
Is this a time type?
bool uni_class() const
Universal class.
std::vector< uint8_t > & data()
Underlying data.
std::string string()
Return string.
void string_set(const std::vector< char > &)
Set string vector.
std::vector< BasePtr > & contain()
Return container, or throw an error if this is not a container.
bool constructed() const
Constructed flag (bit 6).
std::string class_str() const
String version of the classification.
bool is_ia5_string() const
Is this an ia5 (ascii) string?
virtual void dump_data(std::vector< char > &) const
Dump data.
uint8_t type_class() const
Classification of the type (bits 7-8)
static BasePtr context_to_implicit(const BasePtr &, uint32_t)
Change a context element to implicit.
bool is_bmp_string() const
Is this a bmp (ascii) string?
bool is_null() const
Is this a null type?
bool is_printable_string() const
Is this a printable string?
bool is_set() const
Is this a set type?
void string_get(std::vector< char > &)
Get string vector.
bool is_integer() const
Is this a DerInteger?
bool is_object_id() const
Is this an object identifier?
std::string construct_str() const
String version of the construct flag.
virtual std::string str(uint32_t=100) const
Print summary line to maximum width.
std::string id_str() const
String version of the id.
virtual size_t len() const
Length of the data.
bool is_generalized_time() const
Is this as generalized time type?
uint32_t id() const
Tag id of the type.
virtual void parse(const std::vector< uint8_t > &)
Parse raw data into the object.
virtual std::string data_str() const
Print vizualized data.
virtual size_t len() const
Length of the data.
virtual void dump_data(std::vector< char > &v) const
Dump data.
virtual void dump_data(std::vector< char > &v) const
Dump data.
virtual void parse(const std::vector< uint8_t > &)
Parse raw data into the object.
virtual size_t len() const
Length of the data.
virtual std::string data_str() const
Print vizualized data.
bool equal(const DerDocument &) const
Compare binary data.
std::string str(bool=false) const
Debug string dump.
virtual void dump(std::vector< char > &)
Dump and append to a buffer.
static std::string print_element(const BasePtr &, bool=false, const std::string &=" |")
Print an element and any sub-elements to a string.
static void dump_element(const BasePtr &, std::vector< uint8_t > &)
Dump an element to a data vector.
virtual void parse(const std::vector< char > &)
Parse a buffer from a binary DER-formatted vector.
DerBase & root()
Root DerBase.
static BasePtr parse_element(const std::vector< uint8_t > &, size_t=0)
Parse an element from a data vector.
virtual void dump_data(std::vector< char > &v) const
Dump data.
virtual void parse(const std::vector< uint8_t > &)
Parse raw data into the object.
virtual size_t len() const
Length of the data.
virtual std::string data_str() const
Print vizualized data.
virtual void dump_data(std::vector< char > &) const
Dump data.
virtual size_t len() const
Length of the data.
virtual void parse(const std::vector< uint8_t > &)
Parse raw data into the object.
virtual void dump_data(std::vector< char > &v) const
Dump data.
virtual void parse(const std::vector< uint8_t > &)
Parse raw data into the object.
void set(const oid_value &v)
Set oid values.
virtual std::string data_str() const
Print vizualized data.
virtual size_t len() const
Length of the data.
An ASN.1 SEQUENCE or SEQUENCE OF type.
An ASN.1 SET or SET OF type.
All strings derive from simple string base class.
virtual void dump_data(std::vector< char > &v) const
Dump data.
virtual void parse(const std::vector< uint8_t > &)
Parse raw data into the object.
virtual std::string data_str() const
Print vizualized data.
virtual std::string data_str() const
Print vizualized data.
void set_time(int year, int month, int day, int hour, int minute, int second)
Set time in the local time zone.
virtual void parse(const std::vector< uint8_t > &)
Parse raw data into the object.
virtual void dump_data(std::vector< char > &v) const
Dump data.
virtual size_t len() const
Length of the data.
virtual void parse(std::istream &)
Parse document from an input stream.
virtual void dump(std::vector< char > &)
PEM-formatted dump to a buffer.
void clear() noexcept
Zero the original vector and clear it.
Distinguished encoding rules (DER).
Binary to hex string converter.
std::ostream & operator<<(std::ostream &, const scc::net::InetAddr &)
Print the socket address details to an output stream.