Test for distinguished encoding rules.
#include <gtest/gtest.h>
#include <string>
#include <chrono>
#include <ctime>
#include <system_error>
using std::cout;
using std::endl;
using std::string;
using std::runtime_error;
using scc::crypto::BasePtr;
using CharVec = std::vector<char>;
{
DerBase b;
cout << "** base dump ***" << endl;
cout << b << endl;
DerInteger i;
cout << "** integer dump ***" << endl;
cout << i << endl;
DerSequence seq;
cout << "** sequence dump ***" << endl;
cout << seq << endl;
DerSet set;
cout << "** set dump ***" << endl;
cout << set << endl;
}
TEST(DerTest, low_tag_len_3)
{
CharVec inv = {'\x8a','\x03'};
for (int i = 0; i < 3; i++) inv.push_back((char)i);
DerDocument d;
d.parse(inv);
cout << "** class, id 10, length 3 dump:" << endl;
cout << d << endl;
auto r = d.root();
ASSERT_TRUE(r.context_class());
ASSERT_EQ(r.id(), 10);
ASSERT_EQ(r.pre_len(), 2);
ASSERT_EQ(r.len(), 3);
for (int i = 0; i < 3; i++)
{
ASSERT_EQ((int)(r.data()[i]), i%256);
}
CharVec prev(r.pre_len());
r.dump_pre(prev);
for (size_t i = 0; i < prev.size(); i++)
{
ASSERT_EQ(inv[i], prev[i]);
}
CharVec alld;
d.dump(alld);
cout << "doc before: " << Hex::bin_to_hex(inv) << endl;
cout << "doc after: " << Hex::bin_to_hex(alld) << endl;
ASSERT_EQ(inv, alld);
}
TEST(DerTest, high_tag_length_257)
{
CharVec inv = {'\x9f','\x81','\x01','\x82','\x01','\x01'};
for (int i = 0; i < 257; i++) inv.push_back((char)(i%256));
DerDocument d;
d.parse(inv);
cout << "** class, id 129, length 3 dump:" << endl;
cout << d << endl;
auto r = d.root();
ASSERT_TRUE(r.context_class());
ASSERT_EQ(r.id(), 129);
ASSERT_EQ(r.len(), 257);
ASSERT_EQ(r.pre_len(), 6);
for (int i = 0; i < 257; i++)
{
ASSERT_EQ((int)(r.data()[i]), i%256);
}
CharVec prev(r.pre_len());
r.dump_pre(prev);
for (size_t i = 0; i < prev.size(); i++)
{
ASSERT_EQ(inv[i], prev[i]);
}
CharVec alld;
d.dump(alld);
cout << "doc before: " << Hex::bin_to_hex(inv) << endl;
cout << "doc after: " << Hex::bin_to_hex(alld) << endl;
ASSERT_EQ(inv, alld);
}
{
DerDocument d;
CharVec inv = CharVec({'\x8a'});
ASSERT_THROW(d.parse(inv), runtime_error);
inv = CharVec({'\x8a','\x03','\xa1','\xa2'});
ASSERT_THROW(d.parse(inv), runtime_error);
inv = CharVec({'\x8a','\x82','\x01','\x01','\xab'});
ASSERT_THROW(d.parse(inv), runtime_error);
inv = CharVec({'\x8a','\x80'});
ASSERT_THROW(d.parse(inv), runtime_error);
inv = CharVec({'\x8a','\x82','\x01'});
ASSERT_THROW(d.parse(inv), runtime_error);
}
TEST(DerTest, container_and_integers)
{
CharVec inv;
inv = CharVec({
'\x30', '\x1d',
'\x8a','\x03','X','Y','Z',
'\x02','\x01','\x00',
'\x02','\x01','\x7f',
'\x02','\x02','\x00','\x80',
'\x02','\x02','\x01','\x00',
'\x02','\x01','\x80',
'\x02','\x02','\xff','\x7f',
'\x02','\x01','\xff'
});
DerDocument d;
d.parse(inv);
cout << "** container dump:" << endl;
cout << d << endl;
ASSERT_TRUE(d.root().is_seq());
ASSERT_TRUE(d.root().is_contain());
ASSERT_FALSE(d.root().is_set());
auto& c = d.root().contain();
ASSERT_EQ(c.size(), 8);
ASSERT_FALSE(c[0]->is_integer());
ASSERT_FALSE(c[0]->is_contain());
ASSERT_TRUE(c[1]->is_integer());
ASSERT_TRUE(c[2]->is_integer());
ASSERT_TRUE(c[3]->is_integer());
ASSERT_TRUE(c[4]->is_integer());
ASSERT_TRUE(c[5]->is_integer());
ASSERT_TRUE(c[6]->is_integer());
ASSERT_TRUE(c[7]->is_integer());
ASSERT_EQ(c[1]->integer(), bn);
bn = 127;
ASSERT_EQ(c[2]->integer(), bn);
bn = 128;
ASSERT_EQ(c[3]->integer(), bn);
bn = 256;
ASSERT_EQ(c[4]->integer(), bn);
bn = 0;
bn -= 128;
ASSERT_EQ(c[5]->integer(), bn);
bn = 0;
bn -= 129;
ASSERT_EQ(c[6]->integer(), bn);
bn = 0;
bn -= 1;
ASSERT_EQ(c[7]->integer(), bn);
CharVec alld;
d.dump(alld);
cout << "doc before: " << Hex::bin_to_hex(inv) << endl;
cout << "doc after: " << Hex::bin_to_hex(alld) << endl;
ASSERT_EQ(inv, alld);
DerSequence* x = new DerSequence;
BasePtr valptr(x);
DerBase* b = new DerBase(DerBase::class_context|0x0a);
b->data() = std::vector<uint8_t>({'X','Y','Z'});
x->push_back(BasePtr(b));
DerInteger* i = new DerInteger;
x->push_back(BasePtr(i));
i = new DerInteger;
i->data() = 127;
x->push_back(BasePtr(i));
i = new DerInteger;
i->data() = 128;
x->push_back(BasePtr(i));
i = new DerInteger;
i->data() = 256;
x->push_back(BasePtr(i));
i = new DerInteger;
i->data() -= 128;
x->push_back(BasePtr(i));
i = new DerInteger;
i->data() -= 129;
x->push_back(BasePtr(i));
i = new DerInteger;
i->data() -= 1;
x->push_back(BasePtr(i));
cout << endl << "contructed doc:\n" << DerDocument::print_element(valptr) << endl;
CharVec valv;
DerDocument::dump_element(valptr, valv);
ASSERT_EQ(alld, valv);
inv = CharVec({'\x03','\x04','\x08','\x6e','\x5d','\xc0'});
ASSERT_THROW(d.parse(inv), runtime_error);
}
TEST(DerTest, bit_string)
{
auto inv = CharVec({'\x03','\x04','\x06','\x6e','\x5d','\xc0'});
auto outv = CharVec({'\x6e','\x5d','\xc0'});
DerDocument d;
d.parse(inv);
cout << "** bitstring dump:" << endl;
cout << d << endl;
auto& r = d.root();
ASSERT_TRUE(r.is_bit_string());
auto& bs = r.bit_string();
ASSERT_EQ(bs.width(), 18);
ASSERT_EQ(bs.pad_bits(), 6);
ASSERT_FALSE(bs.is_bit_set(0));
ASSERT_TRUE(bs.is_bit_set(1));
ASSERT_TRUE(bs.is_bit_set(2));
ASSERT_FALSE(bs.is_bit_set(3));
ASSERT_TRUE(bs.is_bit_set(4));
ASSERT_TRUE(bs.is_bit_set(5));
ASSERT_TRUE(bs.is_bit_set(6));
ASSERT_FALSE(bs.is_bit_set(7));
ASSERT_FALSE(bs.is_bit_set(8));
ASSERT_TRUE(bs.is_bit_set(9));
ASSERT_FALSE(bs.is_bit_set(10));
ASSERT_TRUE(bs.is_bit_set(11));
ASSERT_TRUE(bs.is_bit_set(12));
ASSERT_TRUE(bs.is_bit_set(13));
ASSERT_FALSE(bs.is_bit_set(14));
ASSERT_TRUE(bs.is_bit_set(15));
ASSERT_TRUE(bs.is_bit_set(16));
ASSERT_TRUE(bs.is_bit_set(17));
ASSERT_FALSE(bs.is_bit_set(18));
ASSERT_FALSE(bs.is_bit_set(19));
ASSERT_FALSE(bs.is_bit_set(20));
ASSERT_FALSE(bs.is_bit_set(21));
ASSERT_FALSE(bs.is_bit_set(22));
ASSERT_FALSE(bs.is_bit_set(23));
ASSERT_FALSE(bs.is_bit_set(24));
CharVec dumpvec;
bs.get(dumpvec);
cout << " dump: " << Hex::bin_to_hexstr(dumpvec.data(), dumpvec.size(), ":", 12) << endl;
cout << " outv: " << Hex::bin_to_hexstr(outv.data(), outv.size(), ":", 12) << endl;
ASSERT_EQ(dumpvec, outv);
auto newv = CharVec({'\x6e','\x5d','\xff'});
CharVec t;
b.get(t);
ASSERT_EQ(outv, t);
inv = CharVec({'\x03','\x04','\x08','\x6e','\x5d','\xc0'});
ASSERT_THROW(d.parse(inv), runtime_error);
}
{
CharVec inv;
inv = CharVec({
'\x31', '\x2c',
'\x04', '\x08', 'o','c','t','e','t','[','\x00',']',
'\x0c', '\x04', 'u','t','f','8',
'\x13', '\x10', 'p','r','i','n','t','a','b','l','e',' ','s','t','r','i','n','g',
'\x16', '\x03', 'i','a','5',
'\x1e', '\x03', 'b','m','p'
});
DerDocument d;
d.parse(inv);
cout << "** strings dump:" << endl;
cout << d << endl;
ASSERT_TRUE(d.root().is_set());
ASSERT_TRUE(d.root().is_contain());
ASSERT_FALSE(d.root().is_seq());
auto& c = d.root().contain();
ASSERT_EQ(c.size(), 5);
for (int i = 0; i < 5; i++)
{
ASSERT_TRUE(c[i]->is_string());
}
ASSERT_TRUE(c[0]->is_octet_string());
ASSERT_TRUE(c[1]->is_utf8_string());
ASSERT_TRUE(c[2]->is_printable_string());
ASSERT_TRUE(c[3]->is_ia5_string());
ASSERT_TRUE(c[4]->is_bmp_string());
ASSERT_EQ(c[2]->string(), string("printable string"));
CharVec alld;
d.dump(alld);
cout << "doc before: " << Hex::bin_to_hex(inv) << endl;
cout << "doc after: " << Hex::bin_to_hex(alld) << endl;
ASSERT_EQ(inv, alld);
DerSet* x = new DerSet;
BasePtr valptr(x);
auto s1 = new DerOctetString;
s1->set(CharVec({'o','c','t','e','t','[','\x00',']'}));
x->push_back(BasePtr(s1));
auto s2 = new DerUtf8String;
s2->set("utf8");
x->push_back(BasePtr(s2));
auto s3 = new DerPrintableString;
s3->set("printable string");
x->push_back(BasePtr(s3));
auto s4 = new DerIa5String;
s4->set("ia5");
x->push_back(BasePtr(s4));
auto s5 = new DerBmpString;
s5->set("bmp");
x->push_back(BasePtr(s5));
cout << endl << "contructed doc:\n" << DerDocument::print_element(valptr) << endl;
CharVec valv;
DerDocument::dump_element(valptr, valv);
cout << "val after: ";
for (auto c : valv)
{
if (isprint(c)) cout << c;
else cout << "<" << std::hex << (int)c << std::dec << ">";
}
cout << endl;
ASSERT_EQ(alld, valv);
}
TEST(DerTest, null_and_bool)
{
CharVec inv;
inv = CharVec({
'\x30', '\x08',
'\x05', '\x00',
'\x01', '\x01', '\x00',
'\x01', '\x01', '\x01'
});
DerDocument d;
d.parse(inv);
cout << "** null and bool dump:" << endl;
cout << d << endl;
ASSERT_TRUE(d.root().is_seq());
ASSERT_TRUE(d.root().is_contain());
ASSERT_FALSE(d.root().is_set());
auto& c = d.root().contain();
ASSERT_EQ(c.size(), 3);
ASSERT_TRUE(c[0]->is_null());
ASSERT_TRUE(c[1]->is_boolean());
ASSERT_TRUE(c[2]->is_boolean());
ASSERT_FALSE(c[1]->boolean());
ASSERT_TRUE(c[2]->boolean());
CharVec alld;
d.dump(alld);
cout << "doc before: " << Hex::bin_to_hex(inv) << endl;
cout << "doc after: " << Hex::bin_to_hex(alld) << endl;
ASSERT_EQ(inv, alld);
DerSequence* x = new DerSequence;
BasePtr valptr(x);
x->push_back(BasePtr(new DerNull));
DerBoolean* b = new DerBoolean;
b->set(false);
x->push_back(BasePtr(b));
b = new DerBoolean;
b->set(true);
x->push_back(BasePtr(b));
cout << endl << "contructed doc:\n" << DerDocument::print_element(valptr) << endl;
CharVec valv;
DerDocument::dump_element(valptr, valv);
cout << "val after: ";
for (auto c : valv)
{
if (isprint(c)) cout << c;
else cout << "<" << std::hex << (int)c << std::dec << ">";
}
cout << endl;
ASSERT_EQ(alld, valv);
}
struct Timetest : public testing::Test
{
Timetest() {}
virtual ~Timetest() {}
DerDocument doc;
void verify(int id, const string& v, const string& verify="")
{
CharVec inv;
inv.push_back(id);
inv.push_back(v.size());
inv.insert(inv.end(), v.begin(), v.end());
doc.parse(inv);
cout << "*** value raw: " << v << endl;
cout << doc << endl;
DerBase& r = doc.root();
ASSERT_TRUE(r.is_time());
ASSERT_EQ(r.id(), id);
ASSERT_TRUE(r.uni_class());
ASSERT_FALSE(r.constructed());
CharVec outv;
doc.dump(outv);
ASSERT_GT(outv.size(), 2);
ASSERT_EQ(outv[0], (char)id);
int sz = (int)outv[1];
ASSERT_EQ(outv.size(), sz+2);
string ts;
auto i = outv.begin();
++i;
++i;
ts.insert(ts.begin(), i, outv.end());
cout << "output ts: " << ts << endl << endl;
if (verify.size())
{
ASSERT_EQ(ts, verify);
}
else
{
ASSERT_EQ(ts, v);
}
}
time_t make_time(int year, int month, int day, int hour, int minute, int second)
{
std::tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_hour = hour;
tm.tm_min = minute;
tm.tm_sec = second;
tm.tm_isdst = -1;
return mktime(&tm);
}
};
TEST_F(Timetest, time)
{
ASSERT_THROW(verify(DerBase::type_utc_time, "030201120102"), runtime_error);
ASSERT_THROW(verify(DerBase::type_utc_time, "030201120102+04"), runtime_error);
ASSERT_THROW(verify(DerBase::type_utc_time, "03020112+04"), runtime_error);
verify(DerBase::type_utc_time, "030201120102Z");
verify(DerBase::type_utc_time, "0302011201Z");
verify(DerBase::type_utc_time, "030201120102+0000", "030201120102Z");
verify(DerBase::type_utc_time, "0302011201+0000", "0302011201Z");
verify(DerBase::type_utc_time, "030201120102-0430", "030201163102Z");
verify(DerBase::type_utc_time, "0302011201-0430", "0302011631Z");
verify(DerBase::type_utc_time, "030201120102+0430", "030201073102Z");
verify(DerBase::type_utc_time, "0302011201+0430", "0302010731Z");
ASSERT_THROW(verify(DerBase::type_generalized_time, "20030201Z"), runtime_error);
verify(DerBase::type_generalized_time, "20030201120102Z");
verify(DerBase::type_generalized_time, "200302011201Z");
verify(DerBase::type_generalized_time, "2003020112Z");
verify(DerBase::type_generalized_time, "20030201120102-0430", "20030201163102Z");
verify(DerBase::type_generalized_time, "200302011201-0430", "200302011631Z");
verify(DerBase::type_generalized_time, "2003020112-0430", "200302011630Z");
verify(DerBase::type_generalized_time, "20030201120102+0430", "20030201073102Z");
verify(DerBase::type_generalized_time, "200302011201+0430", "200302010731Z");
verify(DerBase::type_generalized_time, "2003020112+0430", "200302010730Z");
verify(DerBase::type_generalized_time, "20030201120102-04", "20030201160102Z");
verify(DerBase::type_generalized_time, "200302011201-04", "200302011601Z");
verify(DerBase::type_generalized_time, "2003020112-04", "2003020116Z");
verify(DerBase::type_generalized_time, "20030201120102+04", "20030201080102Z");
verify(DerBase::type_generalized_time, "200302011201+04", "200302010801Z");
verify(DerBase::type_generalized_time, "2003020112+04", "2003020108Z");
verify(DerBase::type_generalized_time, "20030201120102.5Z", "20030201120102Z");
verify(DerBase::type_generalized_time, "200302011201.5Z", "20030201120130Z");
verify(DerBase::type_generalized_time, "2003020112.5Z", "200302011230Z");
setenv("TZ", "/usr/share/zoneinfo/America/Los_Angeles", 1);
auto t = make_time(2020, 3, 8, 1, 59, 59);
verify(DerBase::type_generalized_time, "20200308015959", "20200308095959Z");
ASSERT_EQ(t, doc.root().time_epoch());
ASSERT_NE(getenv("TZ"), nullptr);
ASSERT_EQ(string(getenv("TZ")), "/usr/share/zoneinfo/America/Los_Angeles");
unsetenv("TZ");
tzset();
}
struct Oidtest : public testing::Test
{
Oidtest() {}
virtual ~Oidtest() {}
CharVec inv;
DerDocument doc;
void parse(CharVec v)
{
inv.clear();
inv.push_back(DerBase::type_object_identifier);
inv.push_back(v.size());
inv.insert(inv.end(), v.begin(), v.end());
doc.parse(inv);
cout << endl << "***\n" << doc << endl;
DerBase& r = doc.root();
ASSERT_TRUE(r.is_object_id());
ASSERT_TRUE(r.uni_class());
ASSERT_FALSE(r.constructed());
CharVec outv;
doc.dump(outv);
ASSERT_EQ(inv, outv);
}
void validate(std::vector<uint32_t> v)
{
ASSERT_EQ(v, doc.root().object_id());
DerObjectIdentifier* x = new DerObjectIdentifier;
x->set(v);
BasePtr valptr(x);
cout << endl << "contructed doc:\n" << DerDocument::print_element(valptr) << endl;
CharVec valv;
DerDocument::dump_element(valptr, valv);
ASSERT_EQ(inv, valv);
}
};
TEST_F(Oidtest, oid)
{
parse({'\x2a','\x00','\x01','\x02'});
validate({1,2,0,1,2});
parse({'\x2a','\x86','\x48','\x86','\xf7','\x0d','\x01'});
validate({1,2,840,113549,1});
using OidVec = std::vector<uint32_t>;
DerObjectIdentifier x;
ASSERT_THROW(x.set(OidVec({})), runtime_error);
ASSERT_THROW(x.set(OidVec({1})), runtime_error);
ASSERT_THROW(x.set(OidVec({0,50})), runtime_error);
ASSERT_THROW(x.set(OidVec({3,1})), runtime_error);
}
void set(const std::vector< uint8_t > &v, size_t w)
Set the bit string from a bit string input.
An ASN.1 SEQUENCE or SEQUENCE OF type.
An ASN.1 SET or SET OF type.
PEM formatted DER document.
Distinguished encoding rules (DER).
Common file system utilities.
Binary to hex string converter.
TEST(inet_example, client_server_stream_test)
[Inet client server]