Source code

Revision control

Copy as Markdown

Other Tools

/* 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;
}