diff options
-rw-r--r-- | security/nss/lib/util/derdec.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/security/nss/lib/util/derdec.c b/security/nss/lib/util/derdec.c index 4faa680cf..2398c2404 100644 --- a/security/nss/lib/util/derdec.c +++ b/security/nss/lib/util/derdec.c @@ -213,6 +213,340 @@ der_capture(unsigned char *buf, unsigned char *end, return SECSuccess; } +static unsigned char * +der_decode(PRArenaPool *arena, void *dest, DERTemplate *dtemplate, + unsigned char *buf, int header_len, PRUint32 contents_len) +{ + unsigned char *orig_buf, *end; + unsigned long encode_kind, under_kind; + PRBool explicit, optional, universal, check_tag; + SECItem *item; + SECStatus rv; + PRBool indefinite_length, explicit_indefinite_length; + + encode_kind = dtemplate->kind; + explicit = (encode_kind & DER_EXPLICIT) ? PR_TRUE : PR_FALSE; + optional = (encode_kind & DER_OPTIONAL) ? PR_TRUE : PR_FALSE; + universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL) + ? PR_TRUE : PR_FALSE; + + PORT_Assert (!(explicit && universal)); /* bad templates */ + + if (header_len == 0) { + if (optional || (encode_kind & DER_ANY)) + return buf; + PORT_SetError(SEC_ERROR_BAD_DER); + return NULL; + } + + if (encode_kind & DER_POINTER) { + void *place, **placep; + int offset; + + if (dtemplate->sub != NULL) { + dtemplate = dtemplate->sub; + under_kind = dtemplate->kind; + if (universal) { + encode_kind = under_kind; + } + place = PORT_ArenaZAlloc(arena, dtemplate->arg); + offset = dtemplate->offset; + } else { + if (universal) { + under_kind = encode_kind & ~DER_POINTER; + } else { + under_kind = dtemplate->arg; + } + place = PORT_ArenaZAlloc(arena, sizeof(SECItem)); + offset = 0; + } + if (place == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; /* Out of memory */ + } + placep = (void **)dest; + *placep = place; + dest = (void *)((char *)place + offset); + } else if (encode_kind & DER_INLINE) { + PORT_Assert (dtemplate->sub != NULL); + dtemplate = dtemplate->sub; + under_kind = dtemplate->kind; + if (universal) { + encode_kind = under_kind; + } + dest = (void *)((char *)dest + dtemplate->offset); + } else if (universal) { + under_kind = encode_kind; + } else { + under_kind = dtemplate->arg; + } + + orig_buf = buf; + end = buf + header_len + contents_len; + + explicit_indefinite_length = PR_FALSE; + + if (explicit) { + /* + * This tag is expected to match exactly. + * (The template has all of the bits specified.) + */ + if (*buf != (encode_kind & DER_TAG_MASK)) { + if (optional) + return buf; + PORT_SetError(SEC_ERROR_BAD_DER); + return NULL; + } + if ((header_len == 2) && (*(buf + 1) == 0x80)) + explicit_indefinite_length = PR_TRUE; + buf += header_len; + rv = der_capture (buf, end, &header_len, &contents_len); + if (rv != SECSuccess) + return NULL; + if (header_len == 0) { /* XXX is this right? */ + PORT_SetError(SEC_ERROR_BAD_DER); + return NULL; + } + optional = PR_FALSE; /* can no longer be optional */ + encode_kind = under_kind; + } + + check_tag = PR_TRUE; + if (encode_kind & (DER_DERPTR | DER_ANY | DER_FORCE | DER_SKIP)) { + PORT_Assert ((encode_kind & DER_ANY) || !optional); + encode_kind = encode_kind & (~DER_FORCE); + under_kind = under_kind & (~DER_FORCE); + check_tag = PR_FALSE; + } + + if (check_tag) { + PRBool wrong; + unsigned char expect_tag, expect_num; + + /* + * This tag is expected to match, but the simple types + * may or may not have the constructed bit set, so we + * have to have all this extra logic. + */ + wrong = PR_TRUE; + expect_tag = (unsigned char)encode_kind & DER_TAG_MASK; + expect_num = expect_tag & DER_TAGNUM_MASK; + if (expect_num == DER_SET || expect_num == DER_SEQUENCE) { + if (*buf == (expect_tag | DER_CONSTRUCTED)) + wrong = PR_FALSE; + } else { + if (*buf == expect_tag) + wrong = PR_FALSE; + else if (*buf == (expect_tag | DER_CONSTRUCTED)) + wrong = PR_FALSE; + } + if (wrong) { + if (optional) + return buf; + PORT_SetError(SEC_ERROR_BAD_DER); + return NULL; + } + } + + if (under_kind & DER_DERPTR) { + item = (SECItem *)dest; + if (under_kind & DER_OUTER) { + item->data = buf; + item->len = header_len + contents_len; + } else { + item->data = buf + header_len; + item->len = contents_len; + } + return orig_buf; + } + + if (encode_kind & DER_ANY) { + contents_len += header_len; + header_len = 0; + } + + if ((header_len == 2) && (*(buf + 1) == 0x80)) + indefinite_length = PR_TRUE; + else + indefinite_length = PR_FALSE; + + buf += header_len; + + if (contents_len == 0) + return buf; + + under_kind &= ~DER_OPTIONAL; + + if (under_kind & DER_INDEFINITE) { + int count, thing_size; + unsigned char *sub_buf; + DERTemplate *tmpt; + void *things, **indp, ***placep; + + under_kind &= ~DER_INDEFINITE; + + /* + * Count items. + */ + count = 0; + sub_buf = buf; + while (sub_buf < end) { + if (indefinite_length && sub_buf[0] == 0 && sub_buf[1] == 0) { + break; + } + rv = der_capture (sub_buf, end, &header_len, &contents_len); + if (rv != SECSuccess) + return NULL; + count++; + sub_buf += header_len + contents_len; + } + + /* + * Allocate an array of pointers to items; extra one is for a NULL. + */ + indp = (void**)PORT_ArenaZAlloc(arena, (count + 1) * sizeof(void *)); + if (indp == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* + * Prepare. + */ + if (under_kind == DER_SET || under_kind == DER_SEQUENCE) { + tmpt = dtemplate->sub; + PORT_Assert (tmpt != NULL); + thing_size = tmpt->arg; + PORT_Assert (thing_size != 0); + } else { + tmpt = NULL; + thing_size = sizeof(SECItem); + } + + /* + * Allocate the items themselves. + */ + things = PORT_ArenaZAlloc(arena, count * thing_size); + if (things == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + placep = (void ***)dest; + *placep = indp; + + while (count) { + /* ignore return value because we already did whole thing above */ + (void) der_capture (buf, end, &header_len, &contents_len); + if (tmpt != NULL) { + void *sub_thing; + + sub_thing = (void *)((char *)things + tmpt->offset); + buf = der_decode (arena, sub_thing, tmpt, + buf, header_len, contents_len); + if (buf == NULL) + return NULL; + } else { + item = (SECItem *)things; + if (under_kind == DER_ANY) { + contents_len += header_len; + header_len = 0; + } + buf += header_len; + if (under_kind == DER_BIT_STRING) { + item->data = buf + 1; + item->len = ((contents_len - 1) << 3) - *buf; + } else { + item->data = buf; + item->len = contents_len; + } + buf += contents_len; + } + *indp++ = things; + things = (void *)((char *)things + thing_size); + count--; + } + + *indp = NULL; + + goto der_decode_done; + } + + switch (under_kind) { + case DER_SEQUENCE: + case DER_SET: + { + DERTemplate *tmpt; + void *sub_dest; + + for (tmpt = dtemplate + 1; tmpt->kind; tmpt++) { + sub_dest = (void *)((char *)dest + tmpt->offset); + rv = der_capture (buf, end, &header_len, &contents_len); + if (rv != SECSuccess) + return NULL; + buf = der_decode (arena, sub_dest, tmpt, + buf, header_len, contents_len); + if (buf == NULL) + return NULL; + } + } + break; + + case DER_BIT_STRING: + item = (SECItem *)dest; + item->data = buf + 1; + item->len = ((contents_len - 1) << 3) - *buf; + buf += contents_len; + break; + + case DER_SKIP: + buf += contents_len; + break; + + default: + item = (SECItem *)dest; + item->data = buf; + item->len = contents_len; + buf += contents_len; + break; + } + +der_decode_done: + + if (indefinite_length && buf[0] == 0 && buf[1] == 0) { + buf += 2; + } + + if (explicit_indefinite_length && buf[0] == 0 && buf[1] == 0) { + buf += 2; + } + + return buf; +} + +SECStatus +DER_Decode(PRArenaPool *arena, void *dest, DERTemplate *dtemplate, SECItem *src) +{ + unsigned char *buf; + PRUint32 buf_len, contents_len; + int header_len; + SECStatus rv; + + buf = src->data; + buf_len = src->len; + + rv = der_capture (buf, buf + buf_len, &header_len, &contents_len); + if (rv != SECSuccess) + return rv; + + dest = (void *)((char *)dest + dtemplate->offset); + buf = der_decode (arena, dest, dtemplate, buf, header_len, contents_len); + if (buf == NULL) + return SECFailure; + + return SECSuccess; +} + SECStatus DER_Lengths(SECItem *item, int *header_len_p, PRUint32 *contents_len_p) { |