#include "bee2/core/der.h"
#include "bee2/core/mem.h"
#include "bee2/core/oid.h"
#include "bee2/core/str.h"
#include "bee2/core/util.h"
static bool_t derTIsValid(u32 tag)
{
if (tag == 0)
return FALSE;
else if (tag < 256)
{
if ((tag & 31) == 31)
return FALSE;
}
else
{
u32 t;
u32 b;
if (tag & 128)
return FALSE;
for (b = tag & 127, t = b, tag >>= 8; tag > 255; tag >>= 8)
{
if ((tag & 128) == 0 || (t >> 25) != 0)
return FALSE;
b = tag & 127, t = t << 7, t |= b;
}
if (t < 31 || b == 0 || (tag & 31) != 31)
return FALSE;
}
return TRUE;
}
static bool_t derTIsPrimitive(u32 tag)
{
ASSERT(derTIsValid(tag));
for (; tag > 255; tag >>= 8);
return ((tag >> 5) & 1) == 0;
}
static bool_t derTIsConstructive(u32 tag)
{
return !derTIsPrimitive(tag);
}
static size_t derTEnc(octet der[], u32 tag)
{
size_t t_count = 0;
if (!derTIsValid(tag))
return SIZE_MAX;
{
u32 t = tag;
for (; t; ++t_count, t >>= 8);
if (t_count == 0)
t_count = 1;
}
if (der)
{
size_t pos = t_count;
ASSERT(memIsValid(der, t_count));
while (pos--)
{
der[pos] = (octet)tag;
tag >>= 8;
}
ASSERT(tag == 0);
}
return t_count;
}
static size_t derTDec(u32* tag, const octet der[], size_t count)
{
u32 t;
size_t t_count = 1;
if (count < 1)
return SIZE_MAX;
ASSERT(memIsValid(der, count));
count = MIN2(4, count);
if ((der[0] & 31) == 31)
{
if (count < 2 || (der[1] & 127) == 0)
return SIZE_MAX;
for (t = 0; t_count < count;)
{
t <<= 8, t |= der[t_count] & 127;
if ((der[t_count++] & 128) == 0)
break;
}
if (t_count == count || t < 31)
return SIZE_MAX;
}
else if (der[0] == 0)
return SIZE_MAX;
if (tag)
{
size_t pos;
ASSERT(memIsValid(tag, 4));
for (t = der[0], pos = 1; pos < t_count; ++pos)
t <<= 8, t |= der[pos];
*tag = t;
}
return t_count;
}
static size_t derLEnc(octet der[], size_t len)
{
size_t l_count = 1;
{
size_t l = len;
if (l >= 128)
for (; l; l >>= 8, ++l_count);
}
if (der)
{
ASSERT(memIsValid(der, l_count));
if (len < 128)
{
ASSERT(l_count == 1);
der[0] = (octet)len;
}
else
{
size_t r = l_count - 1;
ASSERT(r >= 1);
der[0] = (octet)(r | 128);
for (; r; der[r--] = (octet)len, len >>= 8);
ASSERT(len == 0);
}
}
return l_count;
}
static size_t derLDec(size_t* len, const octet der[], size_t count)
{
size_t l_count = 1;
size_t l;
if (count < l_count || der[0] == 128 || der[0] == 255)
return SIZE_MAX;
if (der[0] < 128)
l = der[0];
else
{
size_t r = der[0] - 128;
l_count += r;
if (count < l_count || r > O_PER_S ||
der[1] == 0 ||
r == 1 && der[1] < 128)
return SIZE_MAX;
for (l = 0, r = 1; r < l_count; ++r)
l <<= 8, l |= (size_t)der[r];
if (l == SIZE_MAX)
return SIZE_MAX;
}
if (len)
{
ASSERT(memIsValid(len, sizeof(size_t)));
*len = l;
}
return l_count;
}
size_t derTLDec(u32* tag, size_t* len, const octet der[], size_t count)
{
size_t t_count;
size_t l_count;
size_t l;
ASSERT(memIsValid(der, count));
ASSERT(tag == 0 || memIsDisjoint2(tag, 4, der, count));
t_count = derTDec(tag, der, count);
if (t_count == SIZE_MAX)
return SIZE_MAX;
ASSERT(count >= t_count);
l_count = derLDec(&l, der + t_count, count - t_count);
if (l_count == SIZE_MAX || t_count + l_count > count)
return SIZE_MAX;
if (len)
{
ASSERT(memIsDisjoint2(len, O_PER_S, der, count));
ASSERT(tag == 0 || memIsDisjoint2(len, O_PER_S, tag, 4));
*len = l;
}
return t_count + l_count;
}
size_t derTLEnc(octet der[], u32 tag, size_t len)
{
size_t t_count;
size_t l_count;
t_count = derTEnc(0, tag);
if (t_count == SIZE_MAX)
return SIZE_MAX;
l_count = derLEnc(0, len);
if (l_count == SIZE_MAX)
return SIZE_MAX;
if (der)
{
ASSERT(memIsValid(der, t_count + l_count));
if (derTEnc(der, tag) != t_count ||
derLEnc(der + t_count, len) != l_count)
return SIZE_MAX;
}
return t_count + l_count;
}
size_t derEnc(octet der[], u32 tag, const void* val, size_t len)
{
size_t t_count;
size_t l_count;
t_count = derTEnc(0, tag);
if (t_count == SIZE_MAX)
return SIZE_MAX;
l_count = derLEnc(0, len);
if (l_count == SIZE_MAX)
return SIZE_MAX;
if (der)
{
ASSERT(memIsValid(val, len));
ASSERT(memIsValid(der, t_count + l_count + len));
memMove(der + t_count + l_count, val, len);
if (derTEnc(der, tag) != t_count ||
derLEnc(der + t_count, len) != l_count)
return SIZE_MAX;
}
return t_count + l_count + len;
}
static const size_t _max_depth = 32;
bool_t derIsValid(const octet der[], size_t count)
{
size_t t_count;
size_t l_count;
size_t len;
t_count = derTDec(0, der, count);
if (t_count == SIZE_MAX)
return FALSE;
l_count = derLDec(&len, der + t_count, count - t_count);
if (l_count == SIZE_MAX)
return FALSE;
return count == t_count + l_count + len &&
memIsValid(der + t_count + l_count, len);
}
bool_t derIsValid2(const octet der[], size_t count, u32 tag)
{
size_t t_count;
size_t l_count;
u32 t;
size_t len;
t_count = derTDec(&t, der, count);
if (t_count == SIZE_MAX || t != tag)
return FALSE;
l_count = derLDec(&len, der + t_count, count - t_count);
if (l_count == SIZE_MAX)
return FALSE;
return count == t_count + l_count + len &&
memIsValid(der + t_count + l_count, len);
}
static size_t derParse(const octet der[], size_t count, size_t depth)
{
u32 tag;
size_t len;
size_t c;
if (depth > _max_depth)
return SIZE_MAX;
c = derTLDec(&tag, &len, der, count);
if (c == SIZE_MAX || c + len > count)
return SIZE_MAX;
if (derTIsPrimitive(tag))
{
if (tag == 0x03)
return derBITDec(0, &len, der, count);
if (tag == 0x05)
return derNULLDec(der, count);
if (tag == 0x06)
return derOIDDec(0, &len, der, count);
if (tag == 0x13)
return derPSTRDec(0, &len, der, count);
return c + len;
}
while (len)
{
size_t c1;
c1 = derParse(der + c, len, depth + 1);
if (c1 == SIZE_MAX)
return SIZE_MAX;
c += c1, len -= c1;
}
return c;
}
bool_t derIsValid3(const octet der[], size_t count)
{
return derParse(der, count, 0) == count;
}
bool_t derStartsWith(const octet der[], size_t count, u32 tag)
{
u32 t;
return derTDec(&t, der, count) != SIZE_MAX && t == tag;
}
size_t derDec(u32* tag, const octet** val, size_t* len, const octet der[],
size_t count)
{
size_t l;
size_t tl_count;
if (!len)
len = &l;
tl_count = derTLDec(tag, len, der, count);
if (tl_count == SIZE_MAX || tl_count + *len > count)
return SIZE_MAX;
if (val)
{
ASSERT(memIsDisjoint2(val, sizeof(octet*), der, count));
ASSERT(tag == 0 || memIsDisjoint2(val, sizeof(octet*), tag, 4));
*val = der + tl_count;
}
return tl_count + *len;
}
size_t derDec2(const octet** val, size_t* len, const octet der[],
size_t count, u32 tag)
{
u32 t;
count = derDec(&t, val, len, der, count);
if (count == SIZE_MAX || t != tag)
return SIZE_MAX;
return count;
}
size_t derDec3(const octet** val, const octet der[], size_t count,
u32 tag, size_t len)
{
u32 t;
size_t l;
count = derDec(&t, val, &l, der, count);
if (count == SIZE_MAX || t != tag || l != len)
return SIZE_MAX;
return count;
}
size_t derDec4(const octet der[], size_t count, u32 tag, const void* val,
size_t len)
{
u32 t;
const octet* v;
size_t l;
ASSERT(memIsValid(val, len));
count = derDec(&t, &v, &l, der, count);
if (count == SIZE_MAX || t != tag || l != len || !memEq(v, val, len))
return SIZE_MAX;
return count;
}
size_t derTSIZEEnc(octet der[], u32 tag, size_t val)
{
size_t len = 1;
size_t t_count;
size_t l_count;
{
register size_t v = val;
for (; v >= 256; v >>= 8, ++len);
len += v >> 7, CLEAN(v);
}
t_count = derTEnc(der, tag);
if (t_count == SIZE_MAX)
return SIZE_MAX;
l_count = derLEnc(der ? der + t_count : 0, len);
if (l_count == SIZE_MAX)
return SIZE_MAX;
if (der)
{
size_t pos = len;
der += t_count + l_count;
ASSERT(memIsValid(der, len));
for (; pos--; val >>= 8)
der[pos] = (octet)val;
}
return t_count + l_count + len;
}
size_t derTSIZEDec(size_t* val, const octet der[], size_t count, u32 tag)
{
u32 t;
size_t t_count;
size_t l_count;
size_t len;
ASSERT(memIsNullOrValid(val, O_PER_S));
ASSERT(val == 0 || memIsDisjoint2(val, O_PER_S, der, count));
t_count = derTDec(&t, der, count);
if (t_count == SIZE_MAX || t != tag)
return SIZE_MAX;
der += t_count, count -= t_count;
l_count = derLDec(&len, der, count);
if (l_count == SIZE_MAX || len > O_PER_S + 1)
return SIZE_MAX;
der += l_count;
{
register size_t v = 0;
size_t pos = 0;
if ((der[0] & 0x80) ||
der[0] == 0 && len > 1 && (der[1] & 0x80) == 0 ||
len == O_PER_S + 1 && der[0] != 0)
return SIZE_MAX;
for (; pos < len; ++pos)
v <<= 8, v |= der[pos];
if (val)
*val = v;
CLEAN(v);
}
return t_count + l_count + len;
}
size_t derTSIZEDec2(const octet der[], size_t count, u32 tag, size_t val)
{
register size_t v = val;
count = derTSIZEDec(&val, der, count, tag);
if (v != val)
count = SIZE_MAX;
CLEAN(v);
return count;
}
size_t derTUINTEnc(octet der[], u32 tag, const octet* val, size_t len)
{
size_t ex;
size_t tl_count;
ASSERT(len > 0);
ASSERT(memIsValid(val, len));
while (len > 1 && val[len - 1] == 0)
--len;
ex = (val[len - 1] & 128) ? 1 : 0;
tl_count = derTLEnc(der, tag, len + ex);
if (tl_count == SIZE_MAX)
return SIZE_MAX;
if (der)
{
ASSERT(memIsValid(der, tl_count + len + ex));
der += tl_count;
memCopy(der, val, len);
if (ex)
der[len] = 0;
memRev(der, len + ex);
}
return tl_count + len + ex;
}
size_t derTUINTDec(octet* val, size_t* len, const octet der[], size_t count,
u32 tag)
{
const octet* v;
size_t l;
size_t ex;
count = derDec2(&v, &l, der, count, tag);
if (count == SIZE_MAX)
return SIZE_MAX;
if (l < 1 || (v[0] & 128) ||
v[0] == 0 && l > 1 && !(v[1] & 128))
return SIZE_MAX;
ex = (v[0] == 0 && l > 1 && (v[1] & 128)) ? 1 : 0;
if (val)
{
ASSERT(memIsValid(val, l - ex));
ASSERT(len == 0 || memIsDisjoint2(len, O_PER_S, val, l - ex));
memMove(val, v + ex, l - ex);
memRev(val, l - ex);
}
if (len)
{
ASSERT(memIsValid(len, O_PER_S));
*len = l - ex;
}
return count;
}
size_t derTUINTDec2(octet* val, const octet der[], size_t count, u32 tag,
size_t len)
{
const octet* v;
size_t l;
size_t ex;
count = derDec2(&v, &l, der, count, tag);
if (count == SIZE_MAX)
return SIZE_MAX;
if (l < 1 || (v[0] & 128) ||
v[0] == 0 && l > 1 && !(v[1] & 128))
return SIZE_MAX;
ex = (v[0] == 0 && l > 1 && (v[1] & 128)) ? 1 : 0;
if (l - ex != len)
return SIZE_MAX;
if (val)
{
ASSERT(memIsValid(val, len));
memMove(val, v + ex, len);
memRev(val, len);
}
return count;
}
size_t derTBITEnc(octet der[], u32 tag, const octet* val, size_t len)
{
size_t t_count;
size_t l_count;
t_count = derTEnc(0, tag);
if (t_count == SIZE_MAX)
return SIZE_MAX;
l_count = derLEnc(0, (len + 15) / 8);
if (l_count == SIZE_MAX)
return SIZE_MAX;
if (der)
{
memMove(der + t_count + l_count + 1, val, (len + 7) / 8);
if (len % 8)
{
der[t_count + l_count + 1 + len / 8] >>= 8 - len % 8;
der[t_count + l_count + 1 + len / 8] <<= 8 - len % 8;
der[t_count + l_count] = 8 - len % 8;
}
else
der[t_count + l_count] = 0;
if (derTEnc(der, tag) != t_count ||
derLEnc(der + t_count, (len + 15) / 8) != l_count)
return SIZE_MAX;
}
return t_count + l_count + (len + 15) / 8;
}
size_t derTBITDec(octet* val, size_t* len, const octet der[], size_t count,
u32 tag)
{
const octet* v;
size_t l;
count = derDec2(&v, &l, der, count, tag);
if (count == SIZE_MAX)
return SIZE_MAX;
if (l < 1 || v[0] > 7 || v[0] != 0 && l == 1)
return SIZE_MAX;
if (val)
{
ASSERT(memIsValid(val, l - 1));
ASSERT(len == 0 || memIsDisjoint2(len, O_PER_S, val, l - 1));
memMove(val, v + 1, l - 1);
}
if (len)
{
ASSERT(memIsValid(len, O_PER_S));
*len = (l - 1) * 8 - v[0];
}
return count;
}
size_t derTBITDec2(octet* val, const octet der[], size_t count, u32 tag,
size_t len)
{
const octet* v;
size_t l;
count = derDec2(&v, &l, der, count, tag);
if (count == SIZE_MAX)
return SIZE_MAX;
if (l < 1 || v[0] > 7 || v[0] != 0 && l == 1 || (l - 1) * 8 != len + v[0])
return SIZE_MAX;
if (val)
memMove(val, v + 1, l - 1);
return count;
}
size_t derTOCTDec(octet* val, size_t* len, const octet der[], size_t count,
u32 tag)
{
const octet* v;
size_t l;
count = derDec2(&v, &l, der, count, tag);
if (count == SIZE_MAX)
return SIZE_MAX;
if (val)
{
ASSERT(memIsValid(val, l));
ASSERT(len == 0 || memIsDisjoint2(len, O_PER_S, val, l));
memMove(val, v, l);
}
if (len)
{
ASSERT(memIsValid(len, O_PER_S));
*len = l;
}
return count;
}
size_t derTOCTDec2(octet* val, const octet der[], size_t count, u32 tag,
size_t len)
{
const octet* v;
count = derDec3(&v, der, count, tag, len);
if (count == SIZE_MAX)
return SIZE_MAX;
if (val)
memMove(val, v, len);
return count;
}
static size_t derSIDEnc(octet* der, u32 val)
{
size_t count = 0;
u32 t = val;
if (val)
for (; t; t >>= 7, count++);
else
++count;
if (der)
{
size_t pos = count - 1;
ASSERT(memIsValid(der, count));
der[pos] = (t = val) & 127;
while (pos--)
t >>= 7, der[pos] = 128 | (t & 127);
}
return count;
}
static size_t derSIDDec(char* oid, u32 val)
{
size_t count = 0, pos;
u32 t = val;
do
t /= 10, count++;
while (t > 0);
if (oid)
{
ASSERT(memIsValid(oid, count));
pos = count - 1;
oid[pos] = (t = val) % 10 + '0';
while (pos--)
t /= 10, oid[pos] = t % 10 + '0';
}
return count;
}
static size_t derSIDDec2(u32 val, const char* oid)
{
size_t count = 0, pos;
u32 t = val;
do
t /= 10, count++;
while (t > 0);
ASSERT(strIsValid(oid));
pos = count - 1;
if (oid[pos] != '0' + (char)((t = val) % 10))
return SIZE_MAX;
while (pos--)
{
t /= 10;
if (oid[pos] != '0' + (char)(t % 10))
return SIZE_MAX;
}
return count;
}
size_t derOIDEnc(octet der[], const char* oid)
{
u32 d1;
u32 val = 0;
size_t pos = 0;
size_t count = 0;
if (!oidIsValid(oid))
return SIZE_MAX;
ASSERT(oid[0] == '0' || oid[0] == '1' || oid[0] == '2');
ASSERT(oid[1] == '.');
d1 = oid[0] - '0';
oid += 2;
while (1)
{
if (oid[pos] == '.' || oid[pos] == '\0')
{
if (d1 != 3)
val += 40 * d1, d1 = 3;
count += derSIDEnc(der ? der + count : der, val);
if (oid[pos] == '\0')
break;
oid += ++pos, pos = 0, val = 0;
continue;
}
val *= 10;
val += oid[pos] - '0';
++pos;
}
count = derEnc(der, 0x06, der, count);
CLEAN3(d1, val, pos);
return count;
}
size_t derOIDDec(char* oid, size_t* len, const octet der[], size_t count)
{
u32 d1 = 3;
u32 val = 0;
size_t l, pos, oid_len;
ASSERT(memIsValid(der, count));
count = derDec2(&der, &l, der, count, 0x06);
if (count == SIZE_MAX)
return SIZE_MAX;
for (pos = oid_len = 0; pos < l; ++pos)
{
if (val & 0xFE000000)
return SIZE_MAX;
if (val == 0 && der[pos] == 128)
return SIZE_MAX;
val <<= 7, val |= (size_t)der[pos] & 127;
if ((der[pos] & 128) == 0)
{
if (d1 == 3)
{
if (val < 40)
d1 = 0;
else if (val < 80)
d1 = 1, val -= 40;
else
d1 = 2, val -= 80;
oid_len += derSIDDec(oid ? oid + oid_len : oid, d1);
d1 = 0;
}
oid ? oid[oid_len++] = '.' : oid_len++;
oid_len += derSIDDec(oid ? oid + oid_len : oid, val);
val = 0;
}
}
CLEAN2(d1, val), CLEAN2(pos, l);
oid ? oid[oid_len] = '\0' : oid_len;
if (len)
{
ASSERT(memIsValid(len, O_PER_S));
ASSERT(oid == 0 || memIsDisjoint2(len, O_PER_S, oid, strLen(oid)));
*len = oid_len;
}
return count;
}
size_t derOIDDec2(const octet der[], size_t count, const char* oid)
{
u32 d1 = 3;
u32 val = 0;
size_t len, pos, oid_delta;
if (count == SIZE_MAX)
return SIZE_MAX;
ASSERT(memIsValid(der, count));
count = derDec2(&der, &len, der, count, 0x06);
if (count == SIZE_MAX)
return SIZE_MAX;
for (pos = 0; pos < len; ++pos)
{
if (val & 0xFE000000)
return SIZE_MAX;
if (val == 0 && der[pos] == 128)
return SIZE_MAX;
val <<= 7, val |= (size_t)der[pos] & 127;
if ((der[pos] & 128) == 0)
{
if (d1 == 3)
{
if (val < 40)
d1 = 0;
else if (val < 80)
d1 = 1, val -= 40;
else
d1 = 2, val -= 80;
oid_delta = derSIDDec2(d1, oid);
if (oid_delta == SIZE_MAX)
return SIZE_MAX;
oid += oid_delta, d1 = 0;
}
if (*oid != '.')
return SIZE_MAX;
++oid;
oid_delta = derSIDDec2(val, oid);
if (oid_delta == SIZE_MAX)
return SIZE_MAX;
oid += oid_delta, val = 0;
}
}
CLEAN2(d1, val), CLEAN2(pos, len);
if (*oid != '\0')
return SIZE_MAX;
return count;
}
size_t derTPSTREnc(octet der[], u32 tag, const char* val)
{
if (!strIsValid(val) || !strIsPrintable(val))
return SIZE_MAX;
return derEnc(der, tag, val, strLen(val));
}
size_t derTPSTRDec(char* val, size_t* len, const octet der[], size_t count,
u32 tag)
{
const octet* v;
size_t l;
size_t pos;
count = derDec2(&v, &l, der, count, tag);
if (count == SIZE_MAX)
return SIZE_MAX;
for (pos = 0; pos < l; ++pos)
{
register char ch = (char)v[pos];
if ((ch < '0' || ch > '9') &&
(ch < 'A' || ch > 'Z') &&
(ch < 'a' || ch > 'z') &&
!strContains(" '()+,-./:=?", ch))
{
CLEAN(ch);
return SIZE_MAX;
}
CLEAN(ch);
}
if (val)
{
ASSERT(memIsValid(val, l + 1));
ASSERT(len == 0 || memIsDisjoint2(len, O_PER_S, val, l + 1));
memMove(val, v, l);
val[l] = 0;
}
if (len)
{
ASSERT(memIsValid(len, O_PER_S));
*len = l;
}
return count;
}
size_t derTSEQEncStart(der_anchor_t* anchor, octet der[], size_t pos, u32 tag)
{
ASSERT(memIsValid(anchor, sizeof(der_anchor_t)));
if (!derTIsValid(tag) || !derTIsConstructive(tag))
return SIZE_MAX;
anchor->der = der;
anchor->pos = pos;
anchor->tag = tag;
anchor->len = 0;
return derEnc(der, tag, 0, 0);
}
size_t derTSEQEncStop(octet der[], size_t pos, const der_anchor_t* anchor)
{
size_t t_count;
size_t l_count;
size_t len;
size_t l_count1;
ASSERT(memIsValid(anchor, sizeof(der_anchor_t)));
ASSERT(anchor->der == 0 ||
der != 0 && anchor->der + pos == der + anchor->pos);
t_count = derTEnc(0, anchor->tag);
l_count = derLEnc(0, anchor->len);
if (anchor->pos + t_count + l_count > pos)
return SIZE_MAX;
len = pos - anchor->pos - t_count - l_count;
l_count1 = derLEnc(0, len);
pos = l_count1 - l_count;
if (anchor->der)
{
ASSERT(anchor->der + t_count == der - len - l_count);
memMove(der - len + pos, der - len, len);
if (derLEnc(der - len - l_count, len) != l_count1)
return SIZE_MAX;
}
return pos;
}
size_t derTSEQDecStart(der_anchor_t* anchor, const octet der[], size_t count,
u32 tag)
{
size_t t_count;
size_t l_count;
ASSERT(memIsValid(anchor, sizeof(der_anchor_t)));
if (!derTIsConstructive(tag))
return SIZE_MAX;
anchor->der = der;
t_count = derTDec(&anchor->tag, der, count);
if (t_count == SIZE_MAX || anchor->tag != tag)
return SIZE_MAX;
l_count = derLDec(&anchor->len, der + t_count, count - t_count);
if (l_count == SIZE_MAX)
return SIZE_MAX;
return t_count + l_count;
}
size_t derTSEQDecStop(const octet der[], const der_anchor_t* anchor)
{
const octet* val;
ASSERT(memIsValid(anchor, sizeof(der_anchor_t)));
val = anchor->der + derTEnc(0, anchor->tag) + derLEnc(0, anchor->len);
if (val > der)
return SIZE_MAX;
return (der == val + anchor->len) ? 0 : SIZE_MAX;
}