summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--security/nss/lib/util/derdec.c334
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)
{