Merge pull request #2622 from SparkiDev/ber_to_der_rework

Rework BER to DER to not be recursive
This commit is contained in:
toddouska
2019-11-27 14:05:36 -08:00
committed by GitHub
3 changed files with 357 additions and 183 deletions

View File

@ -174,10 +174,16 @@ WOLFSSL_LOCAL int GetLength_ex(const byte* input, word32* inOutIdx, int* len,
return BUFFER_E;
}
if (bytes > sizeof(length)) {
return ASN_PARSE_E;
}
while (bytes--) {
b = input[idx++];
length = (length << 8) | b;
}
if (length < 0) {
return ASN_PARSE_E;
}
}
else
length = b;
@ -968,6 +974,150 @@ static word32 SetBitString(word32 len, byte unusedBits, byte* output)
#endif /* !NO_RSA || HAVE_ECC || HAVE_ED25519 */
#ifdef ASN_BER_TO_DER
/* Pull informtation from the ASN.1 BER encoded item header */
static int GetBerHeader(const byte* data, word32* idx, word32 maxIdx,
byte* pTag, word32* pLen, int* indef)
{
int len = 0;
byte tag;
word32 i = *idx;
*indef = 0;
/* Check there is enough data for a minimal header */
if (i + 2 > maxIdx) {
return ASN_PARSE_E;
}
/* Retrieve tag */
tag = data[i++];
/* Indefinite length handled specially */
if (data[i] == 0x80) {
/* Check valid tag for indefinite */
if (((tag & 0xc0) == 0) && ((tag & ASN_CONSTRUCTED) == 0x00)) {
return ASN_PARSE_E;
}
i++;
*indef = 1;
}
else if (GetLength(data, &i, &len, maxIdx) < 0) {
return ASN_PARSE_E;
}
/* Return tag, length and index after BER item header */
*pTag = tag;
*pLen = len;
*idx = i;
return 0;
}
#ifndef INDEF_ITEMS_MAX
#define INDEF_ITEMS_MAX 20
#endif
/* Indef length item data */
typedef struct Indef {
word32 start;
int depth;
int headerLen;
word32 len;
} Indef;
/* Indef length items */
typedef struct IndefItems
{
Indef len[INDEF_ITEMS_MAX];
int cnt;
int idx;
int depth;
} IndefItems;
/* Get header length of current item */
static int IndefItems_HeaderLen(IndefItems* items)
{
return items->len[items->idx].headerLen;
}
/* Get data length of current item */
static word32 IndefItems_Len(IndefItems* items)
{
return items->len[items->idx].len;
}
/* Add a indefinite length item */
static int IndefItems_AddItem(IndefItems* items, word32 start)
{
int ret = 0;
int i;
if (items->cnt == INDEF_ITEMS_MAX) {
ret = MEMORY_E;
}
else {
i = items->cnt++;
items->len[i].start = start;
items->len[i].depth = items->depth++;
items->len[i].headerLen = 1;
items->len[i].len = 0;
items->idx = i;
}
return ret;
}
/* Increase data length of current item */
static void IndefItems_AddData(IndefItems* items, word32 length)
{
items->len[items->idx].len += length;
}
/* Update header length of current item to reflect data length */
static void IndefItems_UpdateHeaderLen(IndefItems* items)
{
items->len[items->idx].headerLen +=
SetLength(items->len[items->idx].len, NULL);
}
/* Go to indefinite parent of current item */
static void IndefItems_Up(IndefItems* items)
{
int i;
int depth = items->len[items->idx].depth - 1;
for (i = items->cnt - 1; i >= 0; i--) {
if (items->len[i].depth == depth) {
break;
}
}
items->idx = i;
items->depth = depth + 1;
}
/* Calcuate final length by adding length of indefinite child items */
static void IndefItems_CalcLength(IndefItems* items)
{
int i;
int idx = items->idx;
for (i = idx + 1; i < items->cnt; i++) {
if (items->len[i].depth == items->depth) {
items->len[idx].len += items->len[i].headerLen;
items->len[idx].len += items->len[i].len;
}
}
items->len[idx].headerLen += SetLength(items->len[idx].len, NULL);
}
/* Add more data to indefinite length item */
static void IndefItems_MoreData(IndefItems* items, word32 length)
{
if (items->cnt > 0 && items->idx >= 0) {
items->len[items->idx].len += length;
}
}
/* Convert a BER encoding with indefinite length items to DER.
*
* ber BER encoded data.
@ -982,202 +1132,217 @@ static word32 SetBitString(word32 len, byte unusedBits, byte* output)
*/
int wc_BerToDer(const byte* ber, word32 berSz, byte* der, word32* derSz)
{
int ret;
word32 i, j, k;
int len, l;
int ret = 0;
word32 i, j;
#ifdef WOLFSSL_SMALL_STACK
IndefItems* indefItems = NULL;
#else
IndefItems indefItems[1];
#endif
byte tag, basic;
word32 length;
int indef;
int depth = 0;
byte type;
word32 cnt, sz;
word32 outSz = 0;
byte lenBytes[4];
if (ber == NULL || derSz == NULL)
return BAD_FUNC_ARG;
sz = 0;
outSz = *derSz;
for (i = 0, j = 0; i < berSz; ) {
word32 localIdx;
byte tag;
/* Check that there is data for an ASN item to parse. */
if (i + 2 > berSz)
return ASN_PARSE_E;
/* End Of Content (EOC) mark end of indefinite length items.
* EOCs are not encoded in DER.
* Keep track of no. indefinite length items that have not been
* terminated in depth.
*/
if (ber[i] == 0 && ber[i+1] == 0) {
if (depth == 0)
break;
if (--depth == 0)
break;
i += 2;
continue;
#ifdef WOLFSSL_SMALL_STACK
indefItems = XMALLOC(sizeof(IndefItems), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (indefItems == NULL) {
ret = MEMORY_E;
goto end;
}
#endif
/* Indefinite length is encoded as: 0x80 */
type = ber[i];
indef = ber[i+1] == ASN_INDEF_LENGTH;
XMEMSET(indefItems, 0, sizeof(*indefItems));
localIdx = i;
if (GetASNTag(ber, &localIdx, &tag, berSz) != 0)
return ASN_PARSE_E;
/* Calculate indefinite item lengths */
for (i = 0; i < berSz; ) {
word32 start = i;
if (indef && (type & 0xC0) == 0 &&
tag != (ASN_SEQUENCE | ASN_CONSTRUCTED) &&
tag != (ASN_SET | ASN_CONSTRUCTED)) {
/* Indefinite length OCTET STRING or other simple type.
* Put all the data into one entry.
*/
/* Type no longer constructed. */
type &= ~ASN_CONSTRUCTED;
if (der != NULL) {
/* Ensure space for type. */
if (j + 1 >= outSz)
return BUFFER_E;
der[j] = type;
/* Get next BER item */
ret = GetBerHeader(ber, &i, berSz, &tag, &length, &indef);
if (ret != 0) {
goto end;
}
i++; j++;
/* Skip indefinite length. */
i++;
/* There must be further ASN1 items to combine. */
if (i + 2 > berSz)
return ASN_PARSE_E;
/* Calculate length of combined data. */
len = 0;
k = i;
while (ber[k] != 0x00) {
/* Each ASN item must be the same type as the constructed. */
if (ber[k] != type)
return ASN_PARSE_E;
k++;
ret = GetLength(ber, &k, &l, berSz);
if (ret < 0)
return ASN_PARSE_E;
k += l;
len += l;
/* Must at least have terminating EOC. */
if (k + 2 > berSz)
return ASN_PARSE_E;
}
/* Ensure a valid EOC ASN item. */
if (ber[k+1] != 0x00)
return ASN_PARSE_E;
if (der == NULL) {
/* Add length of ASN item length encoding and data. */
j += SetLength(len, lenBytes);
j += len;
}
else {
/* Check space for encoded length. */
if (SetLength(len, lenBytes) > outSz - j)
return BUFFER_E;
/* Encode new length. */
j += SetLength(len, der + j);
/* Encode data in single item. */
k = i;
while (ber[k] != 0x00) {
/* Skip ASN type. */
k++;
/* Find length of data in ASN item. */
ret = GetLength(ber, &k, &l, berSz);
if (ret < 0)
return ASN_PARSE_E;
/* Ensure space for data and copy in. */
if (j + l > outSz)
return BUFFER_E;
XMEMCPY(der + j, ber + k, l);
k += l; j += l;
}
}
/* Continue conversion after EOC. */
i = k + 2;
continue;
}
if (der != NULL) {
/* Ensure space for type and at least one byte of length. */
if (j + 1 >= outSz)
return BUFFER_E;
/* Put in type. */
der[j] = ber[i];
}
i++; j++;
if (indef) {
/* Skip indefinite length. */
i++;
/* Calculate the size of the data inside constructed. */
ret = wc_BerToDer(ber + i, berSz - i, NULL, &sz);
if (ret != LENGTH_ONLY_E)
return ret;
/* Indefinite item - add to list */
ret = IndefItems_AddItem(indefItems, i);
if (ret != 0) {
goto end;
}
if (der != NULL) {
/* Ensure space for encoded length. */
if (SetLength(sz, lenBytes) > outSz - j)
return BUFFER_E;
/* Encode real length. */
j += SetLength(sz, der + j);
if ((tag & 0xC0) == 0 &&
tag != (ASN_SEQUENCE | ASN_CONSTRUCTED) &&
tag != (ASN_SET | ASN_CONSTRUCTED)) {
/* Constructed basic type - get repeating tag */
basic = tag & (~ASN_CONSTRUCTED);
/* Add up lengths of each item below */
for (; i < berSz; ) {
/* Get next BER_item */
ret = GetBerHeader(ber, &i, berSz, &tag, &length, &indef);
if (ret != 0) {
goto end;
}
/* End of content closes item */
if (tag == ASN_EOC) {
/* Must be zero length */
if (length != 0) {
ret = ASN_PARSE_E;
goto end;
}
break;
}
/* Must not be indefinite and tag must match parent */
if (indef || tag != basic) {
ret = ASN_PARSE_E;
goto end;
}
/* Add to length */
IndefItems_AddData(indefItems, length);
/* Skip data */
i += length;
}
/* Ensure we got an EOC and not end of data */
if (tag != ASN_EOC) {
ret = ASN_PARSE_E;
goto end;
}
/* Set the header length to include the length field */
IndefItems_UpdateHeaderLen(indefItems);
/* Go to indefinte parent item */
IndefItems_Up(indefItems);
}
}
else if (tag == ASN_EOC) {
/* End-of-content must be 0 length */
if (length != 0) {
ret = ASN_PARSE_E;
goto end;
}
/* Check there is an item to close - missing EOC */
if (indefItems->depth == 0) {
ret = ASN_PARSE_E;
goto end;
}
/* Finish calculation of data length for indefinite item */
IndefItems_CalcLength(indefItems);
/* Go to indefinte parent item */
IndefItems_Up(indefItems);
}
else {
/* Add size of encoded length. */
j += SetLength(sz, lenBytes);
/* Known length item to add in - make sure enough data for it */
if (i + length > berSz) {
ret = ASN_PARSE_E;
goto end;
}
/* Another EOC to find. */
depth++;
/* Include all data - can't have indefinite inside definite */
i += length;
/* Add entire item to current indefinite item */
IndefItems_MoreData(indefItems, i - start);
}
}
/* Check we had a EOC for each indefinite item */
if (indefItems->depth != 0) {
ret = ASN_PARSE_E;
goto end;
}
/* Write out DER */
j = 0;
/* Reset index */
indefItems->idx = 0;
for (i = 0; i < berSz; ) {
word32 start = i;
/* Get item - checked above */
(void)GetBerHeader(ber, &i, berSz, &tag, &length, &indef);
if (indef) {
if (der != NULL) {
/* Check enough space for header */
if (j + IndefItems_HeaderLen(indefItems) > *derSz) {
ret = BUFFER_E;
goto end;
}
if ((tag & 0xC0) == 0 &&
tag != (ASN_SEQUENCE | ASN_CONSTRUCTED) &&
tag != (ASN_SET | ASN_CONSTRUCTED)) {
/* Remove constructed tag for basic types */
tag &= ~ASN_CONSTRUCTED;
}
/* Add tag and length */
der[j] = tag;
(void)SetLength(IndefItems_Len(indefItems), der + j + 1);
}
/* Add header length of indefinite item */
j += IndefItems_HeaderLen(indefItems);
if ((tag & 0xC0) == 0 &&
tag != (ASN_SEQUENCE | ASN_CONSTRUCTED) &&
tag != (ASN_SET | ASN_CONSTRUCTED)) {
/* For basic type - get each child item and add data */
for (; i < berSz; ) {
(void)GetBerHeader(ber, &i, berSz, &tag, &length, &indef);
if (tag == ASN_EOC) {
break;
}
if (der != NULL) {
if (j + length > *derSz) {
ret = BUFFER_E;
goto end;
}
XMEMCPY(der + j, ber + i, length);
}
j += length;
i += length;
}
}
/* Move to next indef item in list */
indefItems->idx++;
}
else if (tag == ASN_EOC) {
/* End-Of-Content is not written out in DER */
}
else {
/* Get the size of the encode length and length value. */
cnt = i;
ret = GetLength(ber, &cnt, &len, berSz);
if (ret < 0)
return ASN_PARSE_E;
cnt -= i;
/* Check there is enough data to copy out. */
if (i + cnt + len > berSz)
return ASN_PARSE_E;
/* Write out definite length item as is. */
i += length;
if (der != NULL) {
/* Ensure space in DER buffer. */
if (j + cnt + len > outSz)
return BUFFER_E;
/* Copy length and data into DER buffer. */
XMEMCPY(der + j, ber + i, cnt + len);
/* Ensure space for item */
if (j + i - start > *derSz) {
ret = BUFFER_E;
goto end;
}
/* Continue conversion after this ASN item. */
i += cnt + len;
j += cnt + len;
/* Copy item as is */
XMEMCPY(der + j, ber + start, i - start);
}
j += i - start;
}
}
if (depth >= 1)
return ASN_PARSE_E;
/* Return length if no buffer to write to. */
if (der == NULL) {
/* Return the length of the DER encoded ASN.1 */
*derSz = j;
return LENGTH_ONLY_E;
if (der == NULL) {
ret = LENGTH_ONLY_E;
}
return 0;
end:
#ifdef WOLFSSL_SMALL_STACK
if (indefItems != NULL) {
XFREE(indefItems, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
#endif
return ret;
}
#endif

View File

@ -24791,7 +24791,7 @@ int berder_test(void)
0x00, 0x00,
};
static const byte good4_out[] = {
0x30, 0x0d,
0x30, 0x12,
0x02, 0x01, 0x01,
0x30, 0x08,
0x04, 0x03, 0x01, 0x02, 0x03,
@ -24799,12 +24799,14 @@ int berder_test(void)
0x31, 0x03,
0x06, 0x01, 0x01
};
static const byte good5_in[] = { 0x30, 0x03, 0x02, 0x01, 0x01 };
berDerTestData testData[] = {
{ good1_in, sizeof(good1_in), good1_out, sizeof(good1_out) },
{ good2_in, sizeof(good2_in), good2_out, sizeof(good2_out) },
{ good3_in, sizeof(good3_in), good3_out, sizeof(good3_out) },
{ good4_in, sizeof(good4_in), good4_out, sizeof(good4_out) },
{ good5_in, sizeof(good5_in), good5_in , sizeof(good5_in ) },
};
for (i = 0; i < (int)(sizeof(testData) / sizeof(*testData)); i++) {
@ -24829,32 +24831,38 @@ int berder_test(void)
if (ret != ASN_PARSE_E)
return -9741;
}
for (l = 0; l < testData[i].outSz-1; l++) {
ret = wc_BerToDer(testData[i].in, testData[i].inSz, out, &l);
if (ret != BUFFER_E)
return -9742;
}
}
ret = wc_BerToDer(NULL, 4, NULL, NULL);
if (ret != BAD_FUNC_ARG)
return -9742;
return -9743;
ret = wc_BerToDer(out, 4, NULL, NULL);
if (ret != BAD_FUNC_ARG)
return -9743;
return -9744;
ret = wc_BerToDer(NULL, 4, NULL, &len);
if (ret != BAD_FUNC_ARG)
return -9744;
return -9745;
ret = wc_BerToDer(NULL, 4, out, NULL);
if (ret != BAD_FUNC_ARG)
return -9745;
return -9746;
ret = wc_BerToDer(out, 4, out, NULL);
if (ret != BAD_FUNC_ARG)
return -9746;
return -9747;
ret = wc_BerToDer(NULL, 4, out, &len);
if (ret != BAD_FUNC_ARG)
return -9747;
return -9748;
for (l = 1; l < sizeof(good4_out); l++) {
len = l;
ret = wc_BerToDer(good4_in, sizeof(good4_in), out, &len);
if (ret != BUFFER_E)
return -9748;
return -9749;
}
return 0;

View File

@ -78,6 +78,7 @@ enum {
/* ASN Tags */
enum ASN_Tags {
ASN_EOC = 0x00,
ASN_BOOLEAN = 0x01,
ASN_INTEGER = 0x02,
ASN_BIT_STRING = 0x03,