andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "secder.h" andre@0: #include "secerr.h" andre@0: andre@0: #if 0 andre@0: /* andre@0: * Generic templates for individual/simple items. andre@0: */ andre@0: andre@0: DERTemplate SECAnyTemplate[] = { andre@0: { DER_ANY, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECBitStringTemplate[] = { andre@0: { DER_BIT_STRING, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECBooleanTemplate[] = { andre@0: { DER_BOOLEAN, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECIA5StringTemplate[] = { andre@0: { DER_IA5_STRING, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECIntegerTemplate[] = { andre@0: { DER_INTEGER, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECNullTemplate[] = { andre@0: { DER_NULL, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECObjectIDTemplate[] = { andre@0: { DER_OBJECT_ID, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECOctetStringTemplate[] = { andre@0: { DER_OCTET_STRING, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECPrintableStringTemplate[] = { andre@0: { DER_PRINTABLE_STRING, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECT61StringTemplate[] = { andre@0: { DER_T61_STRING, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: DERTemplate SECUTCTimeTemplate[] = { andre@0: { DER_UTC_TIME, andre@0: 0, NULL, sizeof(SECItem) } andre@0: }; andre@0: andre@0: #endif andre@0: andre@0: static int andre@0: header_length(DERTemplate *dtemplate, PRUint32 contents_len) andre@0: { andre@0: PRUint32 len; andre@0: unsigned long encode_kind, under_kind; andre@0: PRBool explicit, optional, universal; andre@0: andre@0: encode_kind = dtemplate->kind; andre@0: andre@0: explicit = (encode_kind & DER_EXPLICIT) ? PR_TRUE : PR_FALSE; andre@0: optional = (encode_kind & DER_OPTIONAL) ? PR_TRUE : PR_FALSE; andre@0: universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL) andre@0: ? PR_TRUE : PR_FALSE; andre@0: andre@0: PORT_Assert (!(explicit && universal)); /* bad templates */ andre@0: andre@0: if (encode_kind & DER_POINTER) { andre@0: if (dtemplate->sub != NULL) { andre@0: under_kind = dtemplate->sub->kind; andre@0: if (universal) { andre@0: encode_kind = under_kind; andre@0: } andre@0: } else if (universal) { andre@0: under_kind = encode_kind & ~DER_POINTER; andre@0: } else { andre@0: under_kind = dtemplate->arg; andre@0: } andre@0: } else if (encode_kind & DER_INLINE) { andre@0: PORT_Assert (dtemplate->sub != NULL); andre@0: under_kind = dtemplate->sub->kind; andre@0: if (universal) { andre@0: encode_kind = under_kind; andre@0: } andre@0: } else if (universal) { andre@0: under_kind = encode_kind; andre@0: } else { andre@0: under_kind = dtemplate->arg; andre@0: } andre@0: andre@0: /* This is only used in decoding; it plays no part in encoding. */ andre@0: if (under_kind & DER_DERPTR) andre@0: return 0; andre@0: andre@0: /* No header at all for an "empty" optional. */ andre@0: if ((contents_len == 0) && optional) andre@0: return 0; andre@0: andre@0: /* And no header for a full DER_ANY. */ andre@0: if (encode_kind & DER_ANY) andre@0: return 0; andre@0: andre@0: /* andre@0: * The common case: one octet for identifier and as many octets andre@0: * as necessary to hold the content length. andre@0: */ andre@0: len = 1 + DER_LengthLength(contents_len); andre@0: andre@0: /* Account for the explicit wrapper, if necessary. */ andre@0: if (explicit) { andre@0: #if 0 /* andre@0: * Well, I was trying to do something useful, but these andre@0: * assertions are too restrictive on valid templates. andre@0: * I wanted to make sure that the top-level "kind" of andre@0: * a template does not also specify DER_EXPLICIT, which andre@0: * should only modify a component field. Maybe later andre@0: * I can figure out a better way to detect such a problem, andre@0: * but for now I must remove these checks altogether. andre@0: */ andre@0: /* andre@0: * This modifier applies only to components of a set or sequence; andre@0: * it should never be used on a set/sequence itself -- confirm. andre@0: */ andre@0: PORT_Assert (under_kind != DER_SEQUENCE); andre@0: PORT_Assert (under_kind != DER_SET); andre@0: #endif andre@0: andre@0: len += 1 + DER_LengthLength(len + contents_len); andre@0: } andre@0: andre@0: return len; andre@0: } andre@0: andre@0: andre@0: static PRUint32 andre@0: contents_length(DERTemplate *dtemplate, void *src) andre@0: { andre@0: PRUint32 len; andre@0: unsigned long encode_kind, under_kind; andre@0: PRBool universal; andre@0: andre@0: andre@0: PORT_Assert (src != NULL); andre@0: andre@0: encode_kind = dtemplate->kind; andre@0: andre@0: universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL) andre@0: ? PR_TRUE : PR_FALSE; andre@0: encode_kind &= ~DER_OPTIONAL; andre@0: andre@0: if (encode_kind & DER_POINTER) { andre@0: src = *(void **)src; andre@0: if (src == NULL) { andre@0: return 0; andre@0: } andre@0: if (dtemplate->sub != NULL) { andre@0: dtemplate = dtemplate->sub; andre@0: under_kind = dtemplate->kind; andre@0: src = (void *)((char *)src + dtemplate->offset); andre@0: } else if (universal) { andre@0: under_kind = encode_kind & ~DER_POINTER; andre@0: } else { andre@0: under_kind = dtemplate->arg; andre@0: } andre@0: } else if (encode_kind & DER_INLINE) { andre@0: PORT_Assert (dtemplate->sub != NULL); andre@0: dtemplate = dtemplate->sub; andre@0: under_kind = dtemplate->kind; andre@0: src = (void *)((char *)src + dtemplate->offset); andre@0: } else if (universal) { andre@0: under_kind = encode_kind; andre@0: } else { andre@0: under_kind = dtemplate->arg; andre@0: } andre@0: andre@0: /* Having any of these bits is not expected here... */ andre@0: PORT_Assert ((under_kind & (DER_EXPLICIT | DER_INLINE | DER_OPTIONAL andre@0: | DER_POINTER | DER_SKIP)) == 0); andre@0: andre@0: /* This is only used in decoding; it plays no part in encoding. */ andre@0: if (under_kind & DER_DERPTR) andre@0: return 0; andre@0: andre@0: if (under_kind & DER_INDEFINITE) { andre@0: PRUint32 sub_len; andre@0: void **indp = *(void ***)src; andre@0: andre@0: if (indp == NULL) andre@0: return 0; andre@0: andre@0: len = 0; andre@0: under_kind &= ~DER_INDEFINITE; andre@0: andre@0: if (under_kind == DER_SET || under_kind == DER_SEQUENCE) { andre@0: DERTemplate *tmpt = dtemplate->sub; andre@0: PORT_Assert (tmpt != NULL); andre@0: andre@0: for (; *indp != NULL; indp++) { andre@0: void *sub_src = (void *)((char *)(*indp) + tmpt->offset); andre@0: sub_len = contents_length (tmpt, sub_src); andre@0: len += sub_len + header_length (tmpt, sub_len); andre@0: } andre@0: } else { andre@0: /* andre@0: * XXX Lisa is not sure this code (for handling, for example, andre@0: * DER_INDEFINITE | DER_OCTET_STRING) is right. andre@0: */ andre@0: for (; *indp != NULL; indp++) { andre@0: SECItem *item = (SECItem *)(*indp); andre@0: sub_len = item->len; andre@0: if (under_kind == DER_BIT_STRING) { andre@0: sub_len = (sub_len + 7) >> 3; andre@0: /* bit string contents involve an extra octet */ andre@0: if (sub_len) andre@0: sub_len++; andre@0: } andre@0: if (under_kind != DER_ANY) andre@0: len += 1 + DER_LengthLength (sub_len); andre@0: } andre@0: } andre@0: andre@0: return len; andre@0: } andre@0: andre@0: switch (under_kind) { andre@0: case DER_SEQUENCE: andre@0: case DER_SET: andre@0: { andre@0: DERTemplate *tmpt; andre@0: void *sub_src; andre@0: PRUint32 sub_len; andre@0: andre@0: len = 0; andre@0: for (tmpt = dtemplate + 1; tmpt->kind; tmpt++) { andre@0: sub_src = (void *)((char *)src + tmpt->offset); andre@0: sub_len = contents_length (tmpt, sub_src); andre@0: len += sub_len + header_length (tmpt, sub_len); andre@0: } andre@0: } andre@0: break; andre@0: andre@0: case DER_BIT_STRING: andre@0: len = (((SECItem *)src)->len + 7) >> 3; andre@0: /* bit string contents involve an extra octet */ andre@0: if (len) andre@0: len++; andre@0: break; andre@0: andre@0: default: andre@0: len = ((SECItem *)src)->len; andre@0: break; andre@0: } andre@0: andre@0: return len; andre@0: } andre@0: andre@0: andre@0: static unsigned char * andre@0: der_encode(unsigned char *buf, DERTemplate *dtemplate, void *src) andre@0: { andre@0: int header_len; andre@0: PRUint32 contents_len; andre@0: unsigned long encode_kind, under_kind; andre@0: PRBool explicit, optional, universal; andre@0: andre@0: andre@0: /* andre@0: * First figure out how long the encoding will be. Do this by andre@0: * traversing the template from top to bottom and accumulating andre@0: * the length of each leaf item. andre@0: */ andre@0: contents_len = contents_length (dtemplate, src); andre@0: header_len = header_length (dtemplate, contents_len); andre@0: andre@0: /* andre@0: * Enough smarts was involved already, so that if both the andre@0: * header and the contents have a length of zero, then we andre@0: * are not doing any encoding for this element. andre@0: */ andre@0: if (header_len == 0 && contents_len == 0) andre@0: return buf; andre@0: andre@0: encode_kind = dtemplate->kind; andre@0: andre@0: explicit = (encode_kind & DER_EXPLICIT) ? PR_TRUE : PR_FALSE; andre@0: optional = (encode_kind & DER_OPTIONAL) ? PR_TRUE : PR_FALSE; andre@0: encode_kind &= ~DER_OPTIONAL; andre@0: universal = ((encode_kind & DER_CLASS_MASK) == DER_UNIVERSAL) andre@0: ? PR_TRUE : PR_FALSE; andre@0: andre@0: if (encode_kind & DER_POINTER) { andre@0: if (contents_len) { andre@0: src = *(void **)src; andre@0: PORT_Assert (src != NULL); andre@0: } andre@0: if (dtemplate->sub != NULL) { andre@0: dtemplate = dtemplate->sub; andre@0: under_kind = dtemplate->kind; andre@0: if (universal) { andre@0: encode_kind = under_kind; andre@0: } andre@0: src = (void *)((char *)src + dtemplate->offset); andre@0: } else if (universal) { andre@0: under_kind = encode_kind & ~DER_POINTER; andre@0: } else { andre@0: under_kind = dtemplate->arg; andre@0: } andre@0: } else if (encode_kind & DER_INLINE) { andre@0: dtemplate = dtemplate->sub; andre@0: under_kind = dtemplate->kind; andre@0: if (universal) { andre@0: encode_kind = under_kind; andre@0: } andre@0: src = (void *)((char *)src + dtemplate->offset); andre@0: } else if (universal) { andre@0: under_kind = encode_kind; andre@0: } else { andre@0: under_kind = dtemplate->arg; andre@0: } andre@0: andre@0: if (explicit) { andre@0: buf = DER_StoreHeader (buf, encode_kind, andre@0: (1 + DER_LengthLength(contents_len) andre@0: + contents_len)); andre@0: encode_kind = under_kind; andre@0: } andre@0: andre@0: if ((encode_kind & DER_ANY) == 0) { /* DER_ANY already contains header */ andre@0: buf = DER_StoreHeader (buf, encode_kind, contents_len); andre@0: } andre@0: andre@0: /* If no real contents to encode, then we are done. */ andre@0: if (contents_len == 0) andre@0: return buf; andre@0: andre@0: if (under_kind & DER_INDEFINITE) { andre@0: void **indp; andre@0: andre@0: indp = *(void ***)src; andre@0: PORT_Assert (indp != NULL); andre@0: andre@0: under_kind &= ~DER_INDEFINITE; andre@0: if (under_kind == DER_SET || under_kind == DER_SEQUENCE) { andre@0: DERTemplate *tmpt = dtemplate->sub; andre@0: PORT_Assert (tmpt != NULL); andre@0: for (; *indp != NULL; indp++) { andre@0: void *sub_src = (void *)((char *)(*indp) + tmpt->offset); andre@0: buf = der_encode (buf, tmpt, sub_src); andre@0: } andre@0: } else { andre@0: for (; *indp != NULL; indp++) { andre@0: SECItem *item; andre@0: int sub_len; andre@0: andre@0: item = (SECItem *)(*indp); andre@0: sub_len = item->len; andre@0: if (under_kind == DER_BIT_STRING) { andre@0: if (sub_len) { andre@0: int rem; andre@0: andre@0: sub_len = (sub_len + 7) >> 3; andre@0: buf = DER_StoreHeader (buf, under_kind, sub_len + 1); andre@0: rem = (sub_len << 3) - item->len; andre@0: *buf++ = rem; /* remaining bits */ andre@0: } else { andre@0: buf = DER_StoreHeader (buf, under_kind, 0); andre@0: } andre@0: } else if (under_kind != DER_ANY) { andre@0: buf = DER_StoreHeader (buf, under_kind, sub_len); andre@0: } andre@0: PORT_Memcpy (buf, item->data, sub_len); andre@0: buf += sub_len; andre@0: } andre@0: } andre@0: return buf; andre@0: } andre@0: andre@0: switch (under_kind) { andre@0: case DER_SEQUENCE: andre@0: case DER_SET: andre@0: { andre@0: DERTemplate *tmpt; andre@0: void *sub_src; andre@0: andre@0: for (tmpt = dtemplate + 1; tmpt->kind; tmpt++) { andre@0: sub_src = (void *)((char *)src + tmpt->offset); andre@0: buf = der_encode (buf, tmpt, sub_src); andre@0: } andre@0: } andre@0: break; andre@0: andre@0: case DER_BIT_STRING: andre@0: { andre@0: SECItem *item; andre@0: int rem; andre@0: andre@0: /* andre@0: * The contents length includes our extra octet; subtract andre@0: * it off so we just have the real string length there. andre@0: */ andre@0: contents_len--; andre@0: item = (SECItem *)src; andre@0: PORT_Assert (contents_len == ((item->len + 7) >> 3)); andre@0: rem = (contents_len << 3) - item->len; andre@0: *buf++ = rem; /* remaining bits */ andre@0: PORT_Memcpy (buf, item->data, contents_len); andre@0: buf += contents_len; andre@0: } andre@0: break; andre@0: andre@0: default: andre@0: { andre@0: SECItem *item; andre@0: andre@0: item = (SECItem *)src; andre@0: PORT_Assert (contents_len == item->len); andre@0: PORT_Memcpy (buf, item->data, contents_len); andre@0: buf += contents_len; andre@0: } andre@0: break; andre@0: } andre@0: andre@0: return buf; andre@0: } andre@0: andre@0: andre@0: SECStatus andre@0: DER_Encode(PLArenaPool *arena, SECItem *dest, DERTemplate *dtemplate, void *src) andre@0: { andre@0: unsigned int contents_len, header_len; andre@0: andre@0: src = (void **)((char *)src + dtemplate->offset); andre@0: andre@0: /* andre@0: * First figure out how long the encoding will be. Do this by andre@0: * traversing the template from top to bottom and accumulating andre@0: * the length of each leaf item. andre@0: */ andre@0: contents_len = contents_length (dtemplate, src); andre@0: header_len = header_length (dtemplate, contents_len); andre@0: andre@0: dest->len = contents_len + header_len; andre@0: andre@0: /* Allocate storage to hold the encoding */ andre@0: dest->data = (unsigned char*) PORT_ArenaAlloc(arena, dest->len); andre@0: if (dest->data == NULL) { andre@0: PORT_SetError(SEC_ERROR_NO_MEMORY); andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* Now encode into the buffer */ andre@0: (void) der_encode (dest->data, dtemplate, src); andre@0: andre@0: return SECSuccess; andre@0: }