Index: nss/lib/util/quickder.c |
diff --git a/nss/lib/util/quickder.c b/nss/lib/util/quickder.c |
index 6f518ddfd172e5236052e6dec0c42e7fa4c0a11a..f9776bb9d581c8e816204651545b26fa2aff3ef8 100644 |
--- a/nss/lib/util/quickder.c |
+++ b/nss/lib/util/quickder.c |
@@ -16,55 +16,110 @@ |
*/ |
static unsigned char* definite_length_decoder(const unsigned char *buf, |
- const unsigned int length, |
- unsigned int *data_length, |
+ const unsigned int buf_length, |
+ unsigned int *out_data_length, |
PRBool includeTag) |
{ |
unsigned char tag; |
- unsigned int used_length= 0; |
- unsigned int data_len; |
+ unsigned int used_length = 0; |
+ unsigned int data_length = 0; |
+ unsigned char length_field_len = 0; |
+ unsigned char byte; |
+ unsigned int i; |
- if (used_length >= length) |
+ if (used_length >= buf_length) |
{ |
+ /* Tag field was not found! */ |
return NULL; |
} |
tag = buf[used_length++]; |
- /* blow out when we come to the end */ |
if (tag == 0) |
{ |
+ /* End-of-contents octects should not be present in DER because |
+ DER doesn't use the indefinite length form. */ |
return NULL; |
} |
- if (used_length >= length) |
+ if ((tag & 0x1F) == 0x1F) |
{ |
+ /* High tag number (a tag number > 30) is not supported */ |
return NULL; |
} |
- data_len = buf[used_length++]; |
- if (data_len&0x80) |
+ if (used_length >= buf_length) |
{ |
- int len_count = data_len & 0x7f; |
+ /* Length field was not found! */ |
+ return NULL; |
+ } |
+ byte = buf[used_length++]; |
- data_len = 0; |
+ if (!(byte & 0x80)) |
+ { |
+ /* Short form: The high bit is not set. */ |
+ data_length = byte; /* clarity; we're returning a 32-bit int. */ |
+ } |
+ else |
+ { |
+ /* Long form. Extract the field length */ |
+ length_field_len = byte & 0x7F; |
+ if (length_field_len == 0) |
+ { |
+ /* DER doesn't use the indefinite length form. */ |
+ return NULL; |
+ } |
+ |
+ if (length_field_len > sizeof(data_length)) |
+ { |
+ /* We don't support an extended length field longer than |
+ 4 bytes (2^32) */ |
+ return NULL; |
+ } |
- while (len_count-- > 0) |
+ if (length_field_len > (buf_length - used_length)) |
{ |
- if (used_length >= length) |
+ /* Extended length field was not found */ |
+ return NULL; |
+ } |
+ |
+ /* Iterate across the extended length field */ |
+ for (i = 0; i < length_field_len; i++) |
+ { |
+ byte = buf[used_length++]; |
+ data_length = (data_length << 8) | byte; |
+ |
+ if (i == 0) |
{ |
- return NULL; |
+ PRBool too_long = PR_FALSE; |
+ if (length_field_len == 1) |
+ { |
+ too_long = ((byte & 0x80) == 0); /* Short form suffices */ |
+ } |
+ else |
+ { |
+ too_long = (byte == 0); /* This zero byte can be omitted */ |
+ } |
+ if (too_long) |
+ { |
+ /* The length is longer than needed. */ |
+ return NULL; |
+ } |
} |
- data_len = (data_len << 8) | buf[used_length++]; |
} |
} |
- if (data_len > (length-used_length) ) |
+ if (data_length > (buf_length - used_length)) |
{ |
+ /* The decoded length exceeds the available buffer */ |
return NULL; |
} |
- if (includeTag) data_len += used_length; |
- *data_length = data_len; |
+ if (includeTag) |
+ { |
+ data_length += used_length; |
+ } |
+ |
+ *out_data_length = data_length; |
return ((unsigned char*)buf + (includeTag ? 0 : used_length)); |
} |