/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "secder.h" #include "secerr.h" #if 0 /* * 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) } }; #endif static int header_length(DERTemplate *dtemplate, PRUint32 contents_len) { PRUint32 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) { PORT_Assert(dtemplate->sub != NULL); 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 PRUint32 contents_length(DERTemplate *dtemplate, void *src) { PRUint32 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) { PRUint32 sub_len; void **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 = dtemplate->sub; PORT_Assert(tmpt != NULL); for (; *indp != NULL; indp++) { void *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 = (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; PRUint32 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; PRUint32 contents_len; unsigned long encode_kind, under_kind; PRBool explicit, 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; 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 = dtemplate->sub; PORT_Assert(tmpt != NULL); for (; *indp != NULL; indp++) { void *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(PLArenaPool *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; }