summaryrefslogtreecommitdiff
path: root/security/nss/lib/util/derenc.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/util/derenc.c')
-rw-r--r--security/nss/lib/util/derenc.c504
1 files changed, 504 insertions, 0 deletions
diff --git a/security/nss/lib/util/derenc.c b/security/nss/lib/util/derenc.c
new file mode 100644
index 000000000..191b00528
--- /dev/null
+++ b/security/nss/lib/util/derenc.c
@@ -0,0 +1,504 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#include "secder.h"
+#include "secerr.h"
+/*
+ * Generic templates for individual/simple items.
+ */
+
+DERTemplate SECAnyTemplate[] = {
+ { DER_ANY,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECBitStringTemplate[] = {
+ { DER_BIT_STRING,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECBooleanTemplate[] = {
+ { DER_BOOLEAN,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECIA5StringTemplate[] = {
+ { DER_IA5_STRING,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECIntegerTemplate[] = {
+ { DER_INTEGER,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECNullTemplate[] = {
+ { DER_NULL,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECObjectIDTemplate[] = {
+ { DER_OBJECT_ID,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECOctetStringTemplate[] = {
+ { DER_OCTET_STRING,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECPrintableStringTemplate[] = {
+ { DER_PRINTABLE_STRING,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECT61StringTemplate[] = {
+ { DER_T61_STRING,
+ 0, NULL, sizeof(SECItem) }
+};
+
+DERTemplate SECUTCTimeTemplate[] = {
+ { DER_UTC_TIME,
+ 0, NULL, sizeof(SECItem) }
+};
+
+
+static int
+header_length(DERTemplate *dtemplate, uint32 contents_len)
+{
+ uint32 len;
+ unsigned long encode_kind, under_kind;
+ PRBool explicit, optional, universal;
+
+ 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 (encode_kind & DER_POINTER) {
+ if (dtemplate->sub != NULL) {
+ under_kind = dtemplate->sub->kind;
+ if (universal) {
+ encode_kind = under_kind;
+ }
+ } else if (universal) {
+ under_kind = encode_kind & ~DER_POINTER;
+ } else {
+ under_kind = dtemplate->arg;
+ }
+ } else if (encode_kind & DER_INLINE) {
+ under_kind = dtemplate->sub->kind;
+ if (universal) {
+ encode_kind = under_kind;
+ }
+ } else if (universal) {
+ under_kind = encode_kind;
+ } else {
+ under_kind = dtemplate->arg;
+ }
+
+ /* This is only used in decoding; it plays no part in encoding. */
+ if (under_kind & DER_DERPTR)
+ return 0;
+
+ /* No header at all for an "empty" optional. */
+ if ((contents_len == 0) && optional)
+ return 0;
+
+ /* And no header for a full DER_ANY. */
+ if (encode_kind & DER_ANY)
+ return 0;
+
+ /*
+ * The common case: one octet for identifier and as many octets
+ * as necessary to hold the content length.
+ */
+ len = 1 + DER_LengthLength(contents_len);
+
+ /* Account for the explicit wrapper, if necessary. */
+ if (explicit) {
+#if 0 /*
+ * Well, I was trying to do something useful, but these
+ * assertions are too restrictive on valid templates.
+ * I wanted to make sure that the top-level "kind" of
+ * a template does not also specify DER_EXPLICIT, which
+ * should only modify a component field. Maybe later
+ * I can figure out a better way to detect such a problem,
+ * but for now I must remove these checks altogether.
+ */
+ /*
+ * This modifier applies only to components of a set or sequence;
+ * it should never be used on a set/sequence itself -- confirm.
+ */
+ PORT_Assert (under_kind != DER_SEQUENCE);
+ PORT_Assert (under_kind != DER_SET);
+#endif
+
+ len += 1 + DER_LengthLength(len + contents_len);
+ }
+
+ return len;
+}
+
+
+static uint32
+contents_length(DERTemplate *dtemplate, void *src)
+{
+ uint32 len;
+ unsigned long encode_kind, under_kind;
+ PRBool universal;
+
+
+ PORT_Assert (src != NULL);
+
+ encode_kind = dtemplate->kind;
+
+ universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL)
+ ? PR_TRUE : PR_FALSE;
+ encode_kind &= ~DER_OPTIONAL;
+
+ if (encode_kind & DER_POINTER) {
+ src = *(void **)src;
+ if (src == NULL) {
+ return 0;
+ }
+ if (dtemplate->sub != NULL) {
+ dtemplate = dtemplate->sub;
+ under_kind = dtemplate->kind;
+ src = (void *)((char *)src + dtemplate->offset);
+ } else if (universal) {
+ under_kind = encode_kind & ~DER_POINTER;
+ } else {
+ under_kind = dtemplate->arg;
+ }
+ } else if (encode_kind & DER_INLINE) {
+ PORT_Assert (dtemplate->sub != NULL);
+ dtemplate = dtemplate->sub;
+ under_kind = dtemplate->kind;
+ src = (void *)((char *)src + dtemplate->offset);
+ } else if (universal) {
+ under_kind = encode_kind;
+ } else {
+ under_kind = dtemplate->arg;
+ }
+
+ /* Having any of these bits is not expected here... */
+ PORT_Assert ((under_kind & (DER_EXPLICIT | DER_INLINE | DER_OPTIONAL
+ | DER_POINTER | DER_SKIP)) == 0);
+
+ /* This is only used in decoding; it plays no part in encoding. */
+ if (under_kind & DER_DERPTR)
+ return 0;
+
+ if (under_kind & DER_INDEFINITE) {
+ uint32 sub_len;
+ void **indp;
+
+ indp = *(void ***)src;
+ if (indp == NULL)
+ return 0;
+
+ len = 0;
+ under_kind &= ~DER_INDEFINITE;
+
+ if (under_kind == DER_SET || under_kind == DER_SEQUENCE) {
+ DERTemplate *tmpt;
+ void *sub_src;
+
+ tmpt = dtemplate->sub;
+
+ for (; *indp != NULL; indp++) {
+ sub_src = (void *)((char *)(*indp) + tmpt->offset);
+ sub_len = contents_length (tmpt, sub_src);
+ len += sub_len + header_length (tmpt, sub_len);
+ }
+ } else {
+ /*
+ * XXX Lisa is not sure this code (for handling, for example,
+ * DER_INDEFINITE | DER_OCTET_STRING) is right.
+ */
+ for (; *indp != NULL; indp++) {
+ SECItem *item;
+ item = (SECItem *)(*indp);
+ sub_len = item->len;
+ if (under_kind == DER_BIT_STRING) {
+ sub_len = (sub_len + 7) >> 3;
+ /* bit string contents involve an extra octet */
+ if (sub_len)
+ sub_len++;
+ }
+ if (under_kind != DER_ANY)
+ len += 1 + DER_LengthLength (sub_len);
+ }
+ }
+
+ return len;
+ }
+
+ switch (under_kind) {
+ case DER_SEQUENCE:
+ case DER_SET:
+ {
+ DERTemplate *tmpt;
+ void *sub_src;
+ uint32 sub_len;
+
+ len = 0;
+ for (tmpt = dtemplate + 1; tmpt->kind; tmpt++) {
+ sub_src = (void *)((char *)src + tmpt->offset);
+ sub_len = contents_length (tmpt, sub_src);
+ len += sub_len + header_length (tmpt, sub_len);
+ }
+ }
+ break;
+
+ case DER_BIT_STRING:
+ len = (((SECItem *)src)->len + 7) >> 3;
+ /* bit string contents involve an extra octet */
+ if (len)
+ len++;
+ break;
+
+ default:
+ len = ((SECItem *)src)->len;
+ break;
+ }
+
+ return len;
+}
+
+
+static unsigned char *
+der_encode(unsigned char *buf, DERTemplate *dtemplate, void *src)
+{
+ int header_len;
+ uint32 contents_len;
+ unsigned long encode_kind, under_kind;
+ PRBool explicit, optional, universal;
+
+
+ /*
+ * First figure out how long the encoding will be. Do this by
+ * traversing the template from top to bottom and accumulating
+ * the length of each leaf item.
+ */
+ contents_len = contents_length (dtemplate, src);
+ header_len = header_length (dtemplate, contents_len);
+
+ /*
+ * Enough smarts was involved already, so that if both the
+ * header and the contents have a length of zero, then we
+ * are not doing any encoding for this element.
+ */
+ if (header_len == 0 && contents_len == 0)
+ return buf;
+
+ encode_kind = dtemplate->kind;
+
+ explicit = (encode_kind & DER_EXPLICIT) ? PR_TRUE : PR_FALSE;
+ optional = (encode_kind & DER_OPTIONAL) ? PR_TRUE : PR_FALSE;
+ encode_kind &= ~DER_OPTIONAL;
+ universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL)
+ ? PR_TRUE : PR_FALSE;
+
+ if (encode_kind & DER_POINTER) {
+ if (contents_len) {
+ src = *(void **)src;
+ PORT_Assert (src != NULL);
+ }
+ if (dtemplate->sub != NULL) {
+ dtemplate = dtemplate->sub;
+ under_kind = dtemplate->kind;
+ if (universal) {
+ encode_kind = under_kind;
+ }
+ src = (void *)((char *)src + dtemplate->offset);
+ } else if (universal) {
+ under_kind = encode_kind & ~DER_POINTER;
+ } else {
+ under_kind = dtemplate->arg;
+ }
+ } else if (encode_kind & DER_INLINE) {
+ dtemplate = dtemplate->sub;
+ under_kind = dtemplate->kind;
+ if (universal) {
+ encode_kind = under_kind;
+ }
+ src = (void *)((char *)src + dtemplate->offset);
+ } else if (universal) {
+ under_kind = encode_kind;
+ } else {
+ under_kind = dtemplate->arg;
+ }
+
+ if (explicit) {
+ buf = DER_StoreHeader (buf, encode_kind,
+ (1 + DER_LengthLength(contents_len)
+ + contents_len));
+ encode_kind = under_kind;
+ }
+
+ if ((encode_kind & DER_ANY) == 0) { /* DER_ANY already contains header */
+ buf = DER_StoreHeader (buf, encode_kind, contents_len);
+ }
+
+ /* If no real contents to encode, then we are done. */
+ if (contents_len == 0)
+ return buf;
+
+ if (under_kind & DER_INDEFINITE) {
+ void **indp;
+
+ indp = *(void ***)src;
+ PORT_Assert (indp != NULL);
+
+ under_kind &= ~DER_INDEFINITE;
+ if (under_kind == DER_SET || under_kind == DER_SEQUENCE) {
+ DERTemplate *tmpt;
+ void *sub_src;
+
+ tmpt = dtemplate->sub;
+ for (; *indp != NULL; indp++) {
+ sub_src = (void *)((char *)(*indp) + tmpt->offset);
+ buf = der_encode (buf, tmpt, sub_src);
+ }
+ } else {
+ for (; *indp != NULL; indp++) {
+ SECItem *item;
+ int sub_len;
+
+ item = (SECItem *)(*indp);
+ sub_len = item->len;
+ if (under_kind == DER_BIT_STRING) {
+ if (sub_len) {
+ int rem;
+
+ sub_len = (sub_len + 7) >> 3;
+ buf = DER_StoreHeader (buf, under_kind, sub_len + 1);
+ rem = (sub_len << 3) - item->len;
+ *buf++ = rem; /* remaining bits */
+ } else {
+ buf = DER_StoreHeader (buf, under_kind, 0);
+ }
+ } else if (under_kind != DER_ANY) {
+ buf = DER_StoreHeader (buf, under_kind, sub_len);
+ }
+ PORT_Memcpy (buf, item->data, sub_len);
+ buf += sub_len;
+ }
+ }
+ return buf;
+ }
+
+ switch (under_kind) {
+ case DER_SEQUENCE:
+ case DER_SET:
+ {
+ DERTemplate *tmpt;
+ void *sub_src;
+
+ for (tmpt = dtemplate + 1; tmpt->kind; tmpt++) {
+ sub_src = (void *)((char *)src + tmpt->offset);
+ buf = der_encode (buf, tmpt, sub_src);
+ }
+ }
+ break;
+
+ case DER_BIT_STRING:
+ {
+ SECItem *item;
+ int rem;
+
+ /*
+ * The contents length includes our extra octet; subtract
+ * it off so we just have the real string length there.
+ */
+ contents_len--;
+ item = (SECItem *)src;
+ PORT_Assert (contents_len == ((item->len + 7) >> 3));
+ rem = (contents_len << 3) - item->len;
+ *buf++ = rem; /* remaining bits */
+ PORT_Memcpy (buf, item->data, contents_len);
+ buf += contents_len;
+ }
+ break;
+
+ default:
+ {
+ SECItem *item;
+
+ item = (SECItem *)src;
+ PORT_Assert (contents_len == item->len);
+ PORT_Memcpy (buf, item->data, contents_len);
+ buf += contents_len;
+ }
+ break;
+ }
+
+ return buf;
+}
+
+
+SECStatus
+DER_Encode(PRArenaPool *arena, SECItem *dest, DERTemplate *dtemplate, void *src)
+{
+ unsigned int contents_len, header_len;
+
+ src = (void **)((char *)src + dtemplate->offset);
+
+ /*
+ * First figure out how long the encoding will be. Do this by
+ * traversing the template from top to bottom and accumulating
+ * the length of each leaf item.
+ */
+ contents_len = contents_length (dtemplate, src);
+ header_len = header_length (dtemplate, contents_len);
+
+ dest->len = contents_len + header_len;
+
+ /* Allocate storage to hold the encoding */
+ dest->data = (unsigned char*) PORT_ArenaAlloc(arena, dest->len);
+ if (dest->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ /* Now encode into the buffer */
+ (void) der_encode (dest->data, dtemplate, src);
+
+ return SECSuccess;
+}