#include "secasn1.h"
typedef enum {
beforeHeader,
duringContents,
duringGroup,
duringSequence,
afterContents,
afterImplicit,
afterInline,
afterPointer,
afterChoice,
notInUse
} sec_asn1e_parse_place;
typedef enum {
allDone,
encodeError,
keepGoing,
needBytes
} sec_asn1e_parse_status;
typedef struct sec_asn1e_state_struct {
SEC_ASN1EncoderContext *top;
const SecAsn1Template *theTemplate;
void *src;
struct sec_asn1e_state_struct *parent;
struct sec_asn1e_state_struct *child;
sec_asn1e_parse_place place;
unsigned char tag_modifiers;
unsigned char tag_number;
unsigned long underlying_kind;
int depth;
PRBool explicit,
indefinite,
is_string,
may_stream,
optional,
ignore_stream
#ifdef __APPLE__
,
signedInt
#endif
;
} sec_asn1e_state;
struct sec_EncoderContext_struct {
PRArenaPool *our_pool;
sec_asn1e_state *current;
sec_asn1e_parse_status status;
PRBool streaming;
PRBool from_buf;
SEC_ASN1NotifyProc notify_proc;
void *notify_arg;
PRBool during_notify;
SEC_ASN1WriteProc output_proc;
void *output_arg;
};
static sec_asn1e_state *
sec_asn1e_push_state (SEC_ASN1EncoderContext *cx,
const SecAsn1Template *theTemplate,
const void *src, PRBool new_depth)
{
sec_asn1e_state *state, *new_state;
state = cx->current;
new_state = (sec_asn1e_state*)PORT_ArenaZAlloc (cx->our_pool,
sizeof(*new_state));
if (new_state == NULL) {
cx->status = encodeError;
return NULL;
}
new_state->top = cx;
new_state->parent = state;
new_state->theTemplate = theTemplate;
new_state->place = notInUse;
if (src != NULL)
new_state->src = (char *)src + theTemplate->offset;
if (state != NULL) {
new_state->depth = state->depth;
if (new_depth)
new_state->depth++;
state->child = new_state;
}
cx->current = new_state;
return new_state;
}
static void
sec_asn1e_scrub_state (sec_asn1e_state *state)
{
state->place = beforeHeader;
state->indefinite = PR_FALSE;
}
static void
sec_asn1e_notify_before (SEC_ASN1EncoderContext *cx, void *src, int depth)
{
if (cx->notify_proc == NULL)
return;
cx->during_notify = PR_TRUE;
(* cx->notify_proc) (cx->notify_arg, PR_TRUE, src, depth);
cx->during_notify = PR_FALSE;
}
static void
sec_asn1e_notify_after (SEC_ASN1EncoderContext *cx, void *src, int depth)
{
if (cx->notify_proc == NULL)
return;
cx->during_notify = PR_TRUE;
(* cx->notify_proc) (cx->notify_arg, PR_FALSE, src, depth);
cx->during_notify = PR_FALSE;
}
static sec_asn1e_state *
sec_asn1e_init_state_based_on_template (sec_asn1e_state *state)
{
PRBool explicit, is_string, may_stream, optional, universal, ignore_stream;
unsigned char tag_modifiers;
unsigned long encode_kind, under_kind;
unsigned long tag_number;
#ifdef __APPLE__
PRBool signedInt, dynamic;
#endif
encode_kind = state->theTemplate->kind;
universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
? PR_TRUE : PR_FALSE;
explicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_EXPLICIT;
optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_OPTIONAL;
PORT_Assert (!(explicit && universal));
may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_MAY_STREAM;
ignore_stream = (encode_kind & SEC_ASN1_NO_STREAM) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_NO_STREAM;
#ifdef __APPLE__
signedInt = (encode_kind & SEC_ASN1_SIGNED_INT) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_SIGNED_INT;
#endif
#ifdef __APPLE__
dynamic = (encode_kind & SEC_ASN1_DYNAMIC) ? PR_TRUE : PR_FALSE;
#endif
encode_kind &= ~SEC_ASN1_DYNAMIC;
if( encode_kind & SEC_ASN1_CHOICE ) {
under_kind = SEC_ASN1_CHOICE;
} else
if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || (!universal
&& !explicit)) {
const SecAsn1Template *subt;
void *src;
PORT_Assert ((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0);
sec_asn1e_scrub_state (state);
if (encode_kind & SEC_ASN1_POINTER) {
src = *(void **)state->src;
state->place = afterPointer;
if (src == NULL) {
if (optional)
return state;
}
} else {
src = state->src;
if (encode_kind & SEC_ASN1_INLINE) {
PORT_Assert (encode_kind == SEC_ASN1_INLINE && !optional);
state->place = afterInline;
} else {
state->tag_modifiers = (unsigned char)encode_kind & SEC_ASN1_TAG_MASK
& ~SEC_ASN1_TAGNUM_MASK;
state->tag_number = (unsigned char)encode_kind & SEC_ASN1_TAGNUM_MASK;
state->place = afterImplicit;
state->optional = optional;
}
}
subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->src, PR_TRUE,
NULL );
state = sec_asn1e_push_state (state->top, subt, src, PR_FALSE);
if (state == NULL)
return NULL;
if (universal) {
return sec_asn1e_init_state_based_on_template (state);
}
under_kind = state->theTemplate->kind;
if (under_kind & SEC_ASN1_MAY_STREAM) {
if (!ignore_stream)
may_stream = PR_TRUE;
under_kind &= ~SEC_ASN1_MAY_STREAM;
}
} else {
under_kind = encode_kind;
}
PORT_Assert ((under_kind & (SEC_ASN1_OPTIONAL
| SEC_ASN1_SKIP | SEC_ASN1_INNER
| SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM
| SEC_ASN1_INLINE | SEC_ASN1_POINTER)) == 0);
if (encode_kind & SEC_ASN1_ANY) {
PORT_Assert (encode_kind == under_kind);
tag_modifiers = 0;
tag_number = 0;
is_string = PR_TRUE;
} else {
tag_modifiers = (unsigned char)encode_kind & SEC_ASN1_TAG_MASK &
~SEC_ASN1_TAGNUM_MASK;
#ifdef __APPLE__
if(dynamic) {
tag_number = state->theTemplate->kind & SEC_ASN1_TAGNUM_MASK;
explicit = (state->theTemplate->kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
tag_modifiers |= (state->theTemplate->kind & SEC_ASN1_CONSTRUCTED);
}
else
#endif
tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK;
is_string = PR_FALSE;
switch (under_kind & SEC_ASN1_TAGNUM_MASK) {
case SEC_ASN1_SET:
PORT_Assert ((under_kind & SEC_ASN1_GROUP) != 0);
case SEC_ASN1_SEQUENCE:
tag_modifiers |= SEC_ASN1_CONSTRUCTED;
break;
case SEC_ASN1_BIT_STRING:
case SEC_ASN1_BMP_STRING:
case SEC_ASN1_GENERALIZED_TIME:
case SEC_ASN1_IA5_STRING:
case SEC_ASN1_OCTET_STRING:
case SEC_ASN1_PRINTABLE_STRING:
case SEC_ASN1_T61_STRING:
case SEC_ASN1_UNIVERSAL_STRING:
case SEC_ASN1_UTC_TIME:
case SEC_ASN1_UTF8_STRING:
case SEC_ASN1_VISIBLE_STRING:
is_string = PR_TRUE;
break;
}
}
state->tag_modifiers = tag_modifiers;
state->tag_number = (unsigned char)tag_number;
state->underlying_kind = under_kind;
state->explicit = explicit;
state->may_stream = may_stream;
state->is_string = is_string;
state->optional = optional;
state->ignore_stream = ignore_stream;
#ifdef __APPLE__
state->signedInt = signedInt;
#endif
sec_asn1e_scrub_state (state);
return state;
}
static void
sec_asn1e_write_part (sec_asn1e_state *state,
const char *buf, size_t len,
SEC_ASN1EncodingPart part)
{
SEC_ASN1EncoderContext *cx;
cx = state->top;
(* cx->output_proc) (cx->output_arg, buf, len, state->depth, part);
}
static void
sec_asn1e_write_identifier_bytes (sec_asn1e_state *state, unsigned char value)
{
char byte;
byte = (char) value;
sec_asn1e_write_part (state, &byte, 1, SEC_ASN1_Identifier);
}
int
SEC_ASN1EncodeLength(unsigned char *buf,unsigned long value) {
int lenlen;
lenlen = SEC_ASN1LengthLength (value);
if (lenlen == 1) {
buf[0] = value;
} else {
int i;
i = lenlen - 1;
buf[0] = 0x80 | i;
while (i) {
buf[i--] = value;
value >>= 8;
}
PORT_Assert (value == 0);
}
return lenlen;
}
static void
sec_asn1e_write_length_bytes (sec_asn1e_state *state, unsigned long value,
PRBool indefinite)
{
int lenlen;
unsigned char buf[sizeof(unsigned long) + 1];
if (indefinite) {
PORT_Assert (value == 0);
buf[0] = 0x80;
lenlen = 1;
} else {
lenlen = SEC_ASN1EncodeLength(buf,value);
}
sec_asn1e_write_part (state, (char *) buf, lenlen, SEC_ASN1_Length);
}
static void
sec_asn1e_write_contents_bytes (sec_asn1e_state *state,
const char *buf, unsigned long len)
{
sec_asn1e_write_part (state, buf, len, SEC_ASN1_Contents);
}
static void
sec_asn1e_write_end_of_contents_bytes (sec_asn1e_state *state)
{
const char eoc[2] = {0, 0};
sec_asn1e_write_part (state, eoc, 2, SEC_ASN1_EndOfContents);
}
static int
sec_asn1e_which_choice
(
void *src,
const SecAsn1Template *theTemplate
)
{
int rv;
unsigned int which = *(unsigned int *)src;
for( rv = 1, theTemplate++; theTemplate->kind != 0; rv++, theTemplate++ ) {
if( which == theTemplate->size ) {
return rv;
}
}
return 0;
}
static unsigned long
sec_asn1e_contents_length (const SecAsn1Template *theTemplate, void *src,
PRBool ignoresubstream, PRBool *noheaderp)
{
unsigned long encode_kind, underlying_kind;
PRBool explicit, optional, universal, may_stream;
unsigned long len;
#ifdef __APPLE__
PRBool signedInt;
#endif
encode_kind = theTemplate->kind;
universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
? PR_TRUE : PR_FALSE;
explicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_EXPLICIT;
optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_OPTIONAL;
PORT_Assert (!(explicit && universal));
may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_MAY_STREAM;
encode_kind &= ~SEC_ASN1_DYNAMIC;
encode_kind &= ~SEC_ASN1_NO_STREAM;
if( encode_kind & SEC_ASN1_CHOICE ) {
void *src2;
int indx = sec_asn1e_which_choice(src, theTemplate);
if( 0 == indx ) {
return 0;
}
src2 = (void *)((char *)src - theTemplate->offset + theTemplate[indx].offset);
return sec_asn1e_contents_length(&theTemplate[indx], src2,
PR_FALSE, noheaderp);
}
if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || !universal) {
theTemplate = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE,
NULL );
if (encode_kind & SEC_ASN1_POINTER) {
src = *(void **)src;
if (src == NULL) {
if (optional)
*noheaderp = PR_TRUE;
else
*noheaderp = PR_FALSE;
return 0;
}
} else if (encode_kind & SEC_ASN1_INLINE) {
PORT_Assert (encode_kind == SEC_ASN1_INLINE && !optional);
}
src = (char *)src + theTemplate->offset;
if (explicit) {
len = sec_asn1e_contents_length (theTemplate, src, PR_FALSE,
noheaderp);
if (len == 0 && optional) {
*noheaderp = PR_TRUE;
} else if (*noheaderp) {
*noheaderp = PR_FALSE;
} else {
len += 1 + SEC_ASN1LengthLength (len);
}
return len;
}
underlying_kind = theTemplate->kind;
underlying_kind &= ~SEC_ASN1_MAY_STREAM;
} else {
underlying_kind = encode_kind;
}
#ifdef __APPLE__
signedInt = (underlying_kind & SEC_ASN1_SIGNED_INT) ?
PR_TRUE : PR_FALSE;
#endif
if (underlying_kind & SEC_ASN1_SAVE) {
PORT_Assert (underlying_kind == SEC_ASN1_SAVE);
*noheaderp = PR_TRUE;
return 0;
}
PORT_Assert ((underlying_kind & (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL
| SEC_ASN1_INLINE | SEC_ASN1_POINTER
| SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM
| SEC_ASN1_SAVE | SEC_ASN1_SKIP)) == 0);
if( underlying_kind & SEC_ASN1_CHOICE ) {
void *src2;
int indx = sec_asn1e_which_choice(src, theTemplate);
if( 0 == indx ) {
return 0;
}
src2 = (void *)((char *)src - theTemplate->offset + theTemplate[indx].offset);
len = sec_asn1e_contents_length(&theTemplate[indx], src2, PR_FALSE,
noheaderp);
} else
switch (underlying_kind) {
case SEC_ASN1_SEQUENCE_OF:
case SEC_ASN1_SET_OF:
{
const SecAsn1Template *tmpt;
void *sub_src;
unsigned long sub_len;
void **group;
len = 0;
group = *(void ***)src;
if (group == NULL)
break;
tmpt = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE,
NULL );
for (; *group != NULL; group++) {
sub_src = (char *)(*group) + tmpt->offset;
sub_len = sec_asn1e_contents_length (tmpt, sub_src, PR_FALSE,
noheaderp);
len += sub_len;
if (!*noheaderp)
len += 1 + SEC_ASN1LengthLength (sub_len);
}
}
break;
case SEC_ASN1_SEQUENCE:
case SEC_ASN1_SET:
{
const SecAsn1Template *tmpt;
void *sub_src;
unsigned long sub_len;
len = 0;
for (tmpt = theTemplate + 1; tmpt->kind; tmpt++) {
sub_src = (char *)src + tmpt->offset;
sub_len = sec_asn1e_contents_length (tmpt, sub_src, PR_FALSE,
noheaderp);
len += sub_len;
if (!*noheaderp)
len += 1 + SEC_ASN1LengthLength (sub_len);
}
}
break;
case SEC_ASN1_BIT_STRING:
len = (((SecAsn1Item *)src)->Length + 7) >> 3;
if (len)
len++;
break;
case SEC_ASN1_INTEGER:
{
unsigned char *buf = ((SecAsn1Item *)src)->Data;
#ifndef __APPLE__
SecAsn1ItemType integerType = ((SecAsn1Item *)src)->type;
#endif
len = ((SecAsn1Item *)src)->Length;
while (len > 0) {
if (*buf != 0) {
#ifdef __APPLE__
if (*buf & 0x80 && !signedInt) {
#else
if (*buf & 0x80 && integerType == siUnsignedInteger) {
#endif len++;
}
break;
}
if (len == 1) {
break;
}
if (buf[1] & 0x80) {
break;
}
buf++;
len--;
}
}
break;
default:
len = ((SecAsn1Item *)src)->Length;
if (may_stream && len == 0 && !ignoresubstream)
len = 1;
break;
}
if ((len == 0 && optional) || underlying_kind == SEC_ASN1_ANY)
*noheaderp = PR_TRUE;
else
*noheaderp = PR_FALSE;
return len;
}
static void
sec_asn1e_write_header (sec_asn1e_state *state)
{
unsigned long contents_length;
unsigned char tag_number, tag_modifiers;
PRBool noheader;
PORT_Assert (state->place == beforeHeader);
tag_number = state->tag_number;
tag_modifiers = state->tag_modifiers;
if (state->underlying_kind == SEC_ASN1_ANY) {
state->place = duringContents;
return;
}
if( state->underlying_kind & SEC_ASN1_CHOICE ) {
int indx = sec_asn1e_which_choice(state->src, state->theTemplate);
if( 0 == indx ) {
state->top->status = encodeError;
return;
}
state->place = afterChoice;
state = sec_asn1e_push_state(state->top, &state->theTemplate[indx],
(char *)state->src - state->theTemplate->offset,
PR_TRUE);
if( (sec_asn1e_state *)NULL != state ) {
sec_asn1e_notify_before (state->top, state->src, state->depth);
state = sec_asn1e_init_state_based_on_template (state);
}
return;
}
contents_length = sec_asn1e_contents_length (state->theTemplate,
state->src,
state->ignore_stream,
&noheader);
if (noheader || (contents_length == 0 && state->optional)) {
state->place = afterContents;
if (state->top->streaming && state->may_stream && state->top->from_buf)
state->top->status = needBytes;
return;
}
if (state->top->streaming && state->may_stream
&& (state->top->from_buf || !state->is_string)) {
state->indefinite = PR_TRUE;
PORT_Assert ((tag_number == SEC_ASN1_SET)
|| (tag_number == SEC_ASN1_SEQUENCE)
|| ((tag_modifiers & SEC_ASN1_CLASS_MASK) != 0)
|| state->is_string);
tag_modifiers |= SEC_ASN1_CONSTRUCTED;
contents_length = 0;
}
sec_asn1e_write_identifier_bytes (state, (unsigned char)(tag_number | tag_modifiers));
sec_asn1e_write_length_bytes (state, contents_length, state->indefinite);
if (contents_length == 0 && !state->indefinite) {
state->place = afterContents;
return;
}
if (state->explicit) {
state->place = afterContents;
state = sec_asn1e_push_state (state->top,
SEC_ASN1GetSubtemplate(state->theTemplate,
state->src,
PR_TRUE,
NULL ),
state->src, PR_TRUE);
if (state != NULL)
state = sec_asn1e_init_state_based_on_template (state);
return;
}
switch (state->underlying_kind) {
case SEC_ASN1_SET_OF:
case SEC_ASN1_SEQUENCE_OF:
{
void **group;
const SecAsn1Template *subt;
group = *(void ***)state->src;
if (group == NULL || *group == NULL) {
state->place = afterContents;
return;
}
state->place = duringGroup;
subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->src,
PR_TRUE, NULL );
state = sec_asn1e_push_state (state->top, subt, *group, PR_TRUE);
if (state != NULL)
state = sec_asn1e_init_state_based_on_template (state);
}
break;
case SEC_ASN1_SEQUENCE:
case SEC_ASN1_SET:
state->place = duringSequence;
state = sec_asn1e_push_state (state->top, state->theTemplate + 1,
state->src, PR_TRUE);
if (state != NULL) {
sec_asn1e_notify_before (state->top, state->src, state->depth);
state = sec_asn1e_init_state_based_on_template (state);
}
break;
default:
state->place = duringContents;
break;
}
}
static void
sec_asn1e_write_contents (sec_asn1e_state *state,
const char *buf, unsigned long len)
{
PORT_Assert (state->place == duringContents);
if (state->top->from_buf) {
if (buf == NULL || len == 0) {
state->top->status = needBytes;
return;
}
PORT_Assert (state->is_string);
if (state->underlying_kind != SEC_ASN1_ANY) {
unsigned char identifier;
identifier = (unsigned char)state->underlying_kind & SEC_ASN1_TAG_MASK;
PORT_Assert ((identifier & SEC_ASN1_TAGNUM_MASK) == identifier);
sec_asn1e_write_identifier_bytes (state, identifier);
if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
char byte;
sec_asn1e_write_length_bytes (state, len + 1, PR_FALSE);
byte = 0;
sec_asn1e_write_contents_bytes (state, &byte, 1);
} else {
sec_asn1e_write_length_bytes (state, len, PR_FALSE);
}
}
sec_asn1e_write_contents_bytes (state, buf, len);
state->top->status = needBytes;
} else {
switch (state->underlying_kind) {
case SEC_ASN1_SET:
case SEC_ASN1_SEQUENCE:
PORT_Assert (0);
break;
case SEC_ASN1_BIT_STRING:
{
SecAsn1Item *item;
char rem;
item = (SecAsn1Item *)state->src;
len = (item->Length + 7) >> 3;
rem = (unsigned char)((len << 3) - item->Length);
sec_asn1e_write_contents_bytes (state, &rem, 1);
sec_asn1e_write_contents_bytes (state, (char *) item->Data,
len);
}
break;
case SEC_ASN1_BMP_STRING:
if ((((SecAsn1Item *)state->src)->Length) % 2) {
SEC_ASN1EncoderContext *cx;
cx = state->top;
cx->status = encodeError;
break;
}
goto process_string;
case SEC_ASN1_UNIVERSAL_STRING:
if ((((SecAsn1Item *)state->src)->Length) % 4) {
SEC_ASN1EncoderContext *cx;
cx = state->top;
cx->status = encodeError;
break;
}
goto process_string;
case SEC_ASN1_INTEGER:
{
size_t blen;
unsigned char *intbuf;
#ifdef __APPLE__
PRBool signedInt = state->signedInt;
#else
SECItemType integerType = ((SecAsn1Item *)state->src)->type;
#endif
blen = ((SecAsn1Item *)state->src)->Length;
intbuf = ((SecAsn1Item *)state->src)->Data;
while (blen > 0) {
#ifdef __APPLE__
if (*intbuf & 0x80 && !signedInt) {
#else
if (*intbuf & 0x80 && integerType == siUnsignedInteger) {
#endif
char zero = 0;
sec_asn1e_write_contents_bytes(state, &zero, 1);
sec_asn1e_write_contents_bytes(state,
(char *)intbuf, blen);
break;
}
if (*intbuf != 0 ||
blen == 1 ||
#ifdef __APPLE__
(intbuf[1] & 0x80 && signedInt) )
#else
(intbuf[1] & 0x80 && integerType != siUnsignedInteger) )
#endif
{
sec_asn1e_write_contents_bytes(state,
(char *)intbuf, blen);
break;
}
intbuf++;
blen--;
}
}
break;
process_string:
default:
{
SecAsn1Item *item;
item = (SecAsn1Item *)state->src;
sec_asn1e_write_contents_bytes (state, (char *) item->Data,
item->Length);
}
break;
}
state->place = afterContents;
}
}
static void
sec_asn1e_next_in_group (sec_asn1e_state *state)
{
sec_asn1e_state *child;
void **group;
void *member;
PORT_Assert (state->place == duringGroup);
PORT_Assert (state->child != NULL);
child = state->child;
group = *(void ***)state->src;
member = (char *)(state->child->src) - child->theTemplate->offset;
while (*group != member)
group++;
group++;
if (*group == NULL) {
child->place = notInUse;
state->place = afterContents;
return;
}
child->src = (char *)(*group) + child->theTemplate->offset;
sec_asn1e_scrub_state (child);
state->top->current = child;
}
static void
sec_asn1e_next_in_sequence (sec_asn1e_state *state)
{
sec_asn1e_state *child;
PORT_Assert (state->place == duringSequence);
PORT_Assert (state->child != NULL);
child = state->child;
sec_asn1e_notify_after (state->top, child->src, child->depth);
child->theTemplate++;
if (child->theTemplate->kind == 0) {
child->place = notInUse;
state->place = afterContents;
return;
}
child->src = (char *)state->src + child->theTemplate->offset;
sec_asn1e_notify_before (state->top, child->src, child->depth);
state->top->current = child;
(void) sec_asn1e_init_state_based_on_template (child);
}
static void
sec_asn1e_after_contents (sec_asn1e_state *state)
{
PORT_Assert (state->place == afterContents);
if (state->indefinite)
sec_asn1e_write_end_of_contents_bytes (state);
state->top->current = state->parent;
}
SECStatus
SEC_ASN1EncoderUpdate (SEC_ASN1EncoderContext *cx,
const char *buf, unsigned long len)
{
sec_asn1e_state *state;
if (cx->status == needBytes) {
PORT_Assert (buf != NULL && len != 0);
cx->status = keepGoing;
}
while (cx->status == keepGoing) {
state = cx->current;
switch (state->place) {
case beforeHeader:
sec_asn1e_write_header (state);
break;
case duringContents:
sec_asn1e_write_contents (state, buf, len);
break;
case duringGroup:
sec_asn1e_next_in_group (state);
break;
case duringSequence:
sec_asn1e_next_in_sequence (state);
break;
case afterContents:
sec_asn1e_after_contents (state);
break;
case afterImplicit:
case afterInline:
case afterPointer:
case afterChoice:
PORT_Assert (!state->indefinite);
state->place = afterContents;
break;
case notInUse:
default:
PORT_Assert (0);
cx->status = encodeError;
break;
}
if (cx->status == encodeError)
break;
state = cx->current;
if (state == NULL) {
cx->status = allDone;
break;
}
}
if (cx->status == encodeError) {
return SECFailure;
}
return SECSuccess;
}
void
SEC_ASN1EncoderFinish (SEC_ASN1EncoderContext *cx)
{
PORT_FreeArena (cx->our_pool, PR_FALSE);
}
SEC_ASN1EncoderContext *
SEC_ASN1EncoderStart (const void *src, const SecAsn1Template *theTemplate,
SEC_ASN1WriteProc output_proc, void *output_arg)
{
PRArenaPool *our_pool;
SEC_ASN1EncoderContext *cx;
our_pool = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE);
if (our_pool == NULL)
return NULL;
cx = (SEC_ASN1EncoderContext*)PORT_ArenaZAlloc (our_pool, sizeof(*cx));
if (cx == NULL) {
PORT_FreeArena (our_pool, PR_FALSE);
return NULL;
}
cx->our_pool = our_pool;
cx->output_proc = output_proc;
cx->output_arg = output_arg;
cx->status = keepGoing;
if (sec_asn1e_push_state(cx, theTemplate, src, PR_FALSE) == NULL
|| sec_asn1e_init_state_based_on_template (cx->current) == NULL) {
PORT_FreeArena (our_pool, PR_FALSE);
return NULL;
}
return cx;
}
void
SEC_ASN1EncoderSetNotifyProc (SEC_ASN1EncoderContext *cx,
SEC_ASN1NotifyProc fn, void *arg)
{
cx->notify_proc = fn;
cx->notify_arg = arg;
}
void
SEC_ASN1EncoderClearNotifyProc (SEC_ASN1EncoderContext *cx)
{
cx->notify_proc = NULL;
cx->notify_arg = NULL;
}
void
SEC_ASN1EncoderAbort(SEC_ASN1EncoderContext *cx, int error)
{
PORT_Assert(cx);
PORT_SetError(error);
cx->status = encodeError;
}
void
SEC_ASN1EncoderSetStreaming (SEC_ASN1EncoderContext *cx)
{
cx->streaming = PR_TRUE;
}
void
SEC_ASN1EncoderClearStreaming (SEC_ASN1EncoderContext *cx)
{
cx->streaming = PR_FALSE;
}
void
SEC_ASN1EncoderSetTakeFromBuf (SEC_ASN1EncoderContext *cx)
{
PORT_Assert (cx->streaming);
cx->from_buf = PR_TRUE;
}
void
SEC_ASN1EncoderClearTakeFromBuf (SEC_ASN1EncoderContext *cx)
{
PORT_Assert (cx->from_buf);
if (! cx->from_buf)
return;
cx->from_buf = PR_FALSE;
if (cx->status == needBytes) {
cx->status = keepGoing;
cx->current->place = afterContents;
}
}
SECStatus
SEC_ASN1Encode (const void *src, const SecAsn1Template *theTemplate,
SEC_ASN1WriteProc output_proc, void *output_arg)
{
SEC_ASN1EncoderContext *ecx;
SECStatus rv;
ecx = SEC_ASN1EncoderStart (src, theTemplate, output_proc, output_arg);
if (ecx == NULL)
return SECFailure;
rv = SEC_ASN1EncoderUpdate (ecx, NULL, 0);
SEC_ASN1EncoderFinish (ecx);
return rv;
}
void
sec_asn1e_encode_item_count (void *arg, const char *buf, size_t len,
int depth, SEC_ASN1EncodingPart data_kind)
{
size_t *count;
count = (unsigned long*)arg;
PORT_Assert (count != NULL);
*count += len;
}
void
sec_asn1e_encode_item_store (void *arg, const char *buf, size_t len,
int depth, SEC_ASN1EncodingPart data_kind)
{
SecAsn1Item *dest;
dest = (SecAsn1Item*)arg;
PORT_Assert (dest != NULL);
PORT_Memcpy (dest->Data + dest->Length, buf, len);
dest->Length += len;
}
SecAsn1Item *
sec_asn1e_allocate_item (PRArenaPool *poolp, SecAsn1Item *dest, unsigned long len)
{
if (poolp != NULL) {
void *release;
release = PORT_ArenaMark (poolp);
if (dest == NULL)
dest = (SecAsn1Item*)PORT_ArenaAlloc (poolp, sizeof(SecAsn1Item));
if (dest != NULL) {
dest->Data = (unsigned char*)PORT_ArenaAlloc (poolp, len);
if (dest->Data == NULL) {
dest = NULL;
}
}
if (dest == NULL) {
PORT_ArenaRelease (poolp, release);
} else {
PORT_ArenaUnmark (poolp, release);
}
} else {
SecAsn1Item *indest;
indest = dest;
if (dest == NULL)
dest = (SecAsn1Item*)PORT_Alloc (sizeof(SecAsn1Item));
if (dest != NULL) {
#ifndef __APPLE__
dest->type = siBuffer;
#endif
dest->Data = (unsigned char*)PORT_Alloc (len);
if (dest->Data == NULL) {
if (indest == NULL)
PORT_Free (dest);
dest = NULL;
}
}
}
return dest;
}
SecAsn1Item *
SEC_ASN1EncodeItem (PRArenaPool *poolp, SecAsn1Item *dest, const void *src,
const SecAsn1Template *theTemplate)
{
unsigned long encoding_length;
SECStatus rv;
PORT_Assert (dest == NULL || dest->Data == NULL);
encoding_length = 0;
rv = SEC_ASN1Encode (src, theTemplate,
sec_asn1e_encode_item_count, &encoding_length);
if (rv != SECSuccess)
return NULL;
dest = sec_asn1e_allocate_item (poolp, dest, encoding_length);
if (dest == NULL)
return NULL;
PORT_Assert (dest->Data != NULL);
if (dest->Data == NULL)
return NULL;
dest->Length = 0;
(void) SEC_ASN1Encode (src, theTemplate, sec_asn1e_encode_item_store, dest);
PORT_Assert (encoding_length == dest->Length);
return dest;
}
static SecAsn1Item *
sec_asn1e_integer(PRArenaPool *poolp, SecAsn1Item *dest, unsigned long value,
PRBool make_unsigned)
{
unsigned long copy;
unsigned char sign;
int len = 0;
copy = value;
do {
len++;
sign = (unsigned char)(copy & 0x80);
copy >>= 8;
} while (copy);
if (sign && make_unsigned)
len++;
dest = sec_asn1e_allocate_item (poolp, dest, len);
if (dest == NULL)
return NULL;
dest->Length = len;
while (len) {
dest->Data[--len] = (unsigned char)value;
value >>= 8;
}
PORT_Assert (value == 0);
return dest;
}
SecAsn1Item *
SEC_ASN1EncodeInteger(PRArenaPool *poolp, SecAsn1Item *dest, long value)
{
return sec_asn1e_integer (poolp, dest, (unsigned long) value, PR_FALSE);
}
extern SecAsn1Item *
SEC_ASN1EncodeUnsignedInteger(PRArenaPool *poolp,
SecAsn1Item *dest, unsigned long value)
{
return sec_asn1e_integer (poolp, dest, value, PR_TRUE);
}