Quellcode-Bibliothek secasn1d.c
Sprache: C
/* 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/. */
/*
* Support for DEcoding ASN . 1 data based on BER / DER ( Basic / Distinguished
* Encoding Rules ) .
*/
/* #define DEBUG_ASN1D_STATES 1 */
#ifdef DEBUG_ASN1D_STATES
#include <stdio.h>
#define PR_Assert sec_asn1d_Assert
#endif
#include <limits.h>
#include "secasn1.h"
#include "secerr.h"
typedef enum {
beforeIdentifier,
duringIdentifier,
afterIdentifier,
beforeLength,
duringLength,
afterLength,
beforeBitString,
duringBitString,
duringConstructedString,
duringGroup,
duringLeaf,
duringSaveEncoding,
duringSequence,
afterConstructedString,
afterGroup,
afterExplicit,
afterImplicit,
afterInline,
afterPointer,
afterSaveEncoding,
beforeEndOfContents,
duringEndOfContents,
afterEndOfContents,
beforeChoice,
duringChoice,
afterChoice,
notInUse
} sec_asn1d_parse_place;
#ifdef DEBUG_ASN1D_STATES
static const char *
const place_names[] = {
"beforeIdentifier" ,
"duringIdentifier" ,
"afterIdentifier" ,
"beforeLength" ,
"duringLength" ,
"afterLength" ,
"beforeBitString" ,
"duringBitString" ,
"duringConstructedString" ,
"duringGroup" ,
"duringLeaf" ,
"duringSaveEncoding" ,
"duringSequence" ,
"afterConstructedString" ,
"afterGroup" ,
"afterExplicit" ,
"afterImplicit" ,
"afterInline" ,
"afterPointer" ,
"afterSaveEncoding" ,
"beforeEndOfContents" ,
"duringEndOfContents" ,
"afterEndOfContents" ,
"beforeChoice" ,
"duringChoice" ,
"afterChoice" ,
"notInUse"
};
static const char *
const class_names[] = {
"UNIVERSAL" ,
"APPLICATION" ,
"CONTEXT_SPECIFIC" ,
"PRIVATE"
};
static const char *
const method_names[] = {
"PRIMITIVE" ,
"CONSTRUCTED" };
static const char *
const type_names[] = {
"END_OF_CONTENTS" ,
"BOOLEAN" ,
"INTEGER" ,
"BIT_STRING" ,
"OCTET_STRING" ,
"NULL" ,
"OBJECT_ID" ,
"OBJECT_DESCRIPTOR" ,
"(type 08)" ,
"REAL" ,
"ENUMERATED" ,
"EMBEDDED" ,
"UTF8_STRING" ,
"(type 0d)" ,
"(type 0e)" ,
"(type 0f)" ,
"SEQUENCE" ,
"SET" ,
"NUMERIC_STRING" ,
"PRINTABLE_STRING" ,
"T61_STRING" ,
"VIDEOTEXT_STRING" ,
"IA5_STRING" ,
"UTC_TIME" ,
"GENERALIZED_TIME" ,
"GRAPHIC_STRING" ,
"VISIBLE_STRING" ,
"GENERAL_STRING" ,
"UNIVERSAL_STRING" ,
"(type 1d)" ,
"BMP_STRING" ,
"HIGH_TAG_VALUE"
};
static const char *
const flag_names[] = {
/* flags, right to left */
"OPTIONAL" ,
"EXPLICIT" ,
"ANY" ,
"INLINE" ,
"POINTER" ,
"GROUP" ,
"DYNAMIC" ,
"SKIP" ,
"INNER" ,
"SAVE" ,
"" ,
/* decoder ignores "MAY_STREAM", */
"SKIP_REST" ,
"CHOICE" ,
"NO_STREAM" ,
"DEBUG_BREAK" ,
"unknown 08" ,
"unknown 10" ,
"unknown 20" ,
"unknown 40" ,
"unknown 80"
};
static int /* bool */
formatKind(
unsigned long kind,
char *buf,
int space_in_buffer)
{
int i;
unsigned long k = kind & SEC_ASN1_TAGNUM_MASK;
unsigned long notag = kind & (SEC_ASN1_CHOICE | SEC_ASN1_POINTER |
SEC_ASN1_INLINE | SEC_ASN1_ANY | SEC_ASN1_SAVE);
buf[
0 ] =
0 ;
if ((kind & SEC_ASN1_CLASS_MASK) != SEC_ASN1_UNIVERSAL) {
space_in_buffer -= snprintf(buf, space_in_buffer,
" %s" , class_names[(kind & SEC_ASN
1_CLASS_MASK) >> 6 ]);
buf += strlen(buf);
}
if (kind & SEC_ASN1_METHOD_MASK) {
space_in_buffer -= snprintf(buf, space_in_buffer, " %s" , method_names[1 ]);
buf += strlen(buf);
}
if ((kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) {
if (k || !notag) {
space_in_buffer -= snprintf(buf, space_in_buffer, " %s" , type_names[k]);
if ((k == SEC_ASN1_SET || k == SEC_ASN1_SEQUENCE) &&
(kind & SEC_ASN1_GROUP)) {
buf += strlen(buf);
space_in_buffer -= snprintf(buf, space_in_buffer, "_OF" );
}
}
} else {
space_in_buffer -= snprintf(buf, space_in_buffer, " [%lu]" , k);
}
buf += strlen(buf);
for (k = kind >> 8 , i = 0 ; k; k >>= 1 , ++i) {
if (k & 1 ) {
space_in_buffer -= snprintf(buf, space_in_buffer, " %s" , flag_names[i]);
buf += strlen(buf);
}
}
return notag != 0 ;
}
#endif /* DEBUG_ASN1D_STATES */
typedef enum {
allDone,
decodeError,
keepGoing,
needBytes
} sec_asn1d_parse_status;
struct subitem {
const void *data;
unsigned long len; /* only used for substrings */
struct subitem *next;
};
typedef struct sec_asn1d_state_struct {
SEC_ASN1DecoderContext *top;
const SEC_ASN1Template *theTemplate;
void *dest;
void *our_mark; /* free on completion */
struct sec_asn1d_state_struct *parent; /* aka prev */
struct sec_asn1d_state_struct *child; /* aka next */
sec_asn1d_parse_place place;
/*
* XXX explain the next fields as clearly as possible . . .
*/
unsigned char found_tag_modifiers;
unsigned char expect_tag_modifiers;
unsigned long check_tag_mask;
unsigned long found_tag_number;
unsigned long expect_tag_number;
unsigned long underlying_kind;
unsigned long contents_length;
unsigned long pending;
unsigned long consumed;
int depth;
/*
* Bit strings have their length adjusted - - the first octet of the
* contents contains a value between 0 and 7 which says how many bits
* at the end of the octets are not actually part of the bit string ;
* when parsing bit strings we put that value here because we need it
* later , for adjustment of the length ( when the whole string is done ) .
*/
unsigned int bit_string_unused_bits;
/*
* The following are used for indefinite - length constructed strings .
*/
struct subitem *subitems_head;
struct subitem *subitems_tail;
PRPackedBool
allocate, /* when true, need to allocate the destination */
endofcontents, /* this state ended up parsing its parent's end-of-contents octets */
explicit , /* we are handling an explicit header */
indefinite, /* the current item has indefinite-length encoding */
missing, /* an optional field that was not present */
optional, /* the template says this field may be omitted */
substring; /* this is a substring of a constructed string */
} sec_asn1d_state;
#define IS_HIGH_TAG_NUMBER(n) ((n) == SEC_ASN1_HIGH_TAG_NUMBER)
#define LAST_TAG_NUMBER_BYTE(b) (((b)&0 x80) == 0 )
#define TAG_NUMBER_BITS 7
#define TAG_NUMBER_MASK 0 x7f
#define LENGTH_IS_SHORT_FORM(b) (((b)&0 x80) == 0 )
#define LONG_FORM_LENGTH(b) ((b)&0 x7f)
#define HIGH_BITS(field, cnt) ((field) >> ((sizeof (field) * 8 ) - (cnt)))
/*
* An " outsider " will have an opaque pointer to this , created by calling
* SEC_ASN1DecoderStart ( ) . It will be passed back in to all subsequent
* calls to SEC_ASN1DecoderUpdate ( ) , and when done it is passed to
* SEC_ASN1DecoderFinish ( ) .
*/
struct sec_DecoderContext_struct {
PLArenaPool *our_pool; /* for our internal allocs */
PLArenaPool *their_pool; /* for destination structure allocs */
#ifdef SEC_ASN1D_FREE_ON_ERROR /* \
* XXX see comment below ( by same \
* ifdef ) that explains why this \
* does not work ( need more smarts \
* in order to free back to mark ) \
*/
/*
* XXX how to make their_mark work in the case where they do NOT
* give us a pool pointer ?
*/
void *their_mark; /* free on error */
#endif
sec_asn1d_state *current;
sec_asn1d_parse_status status;
/* The maximum size the caller is willing to allow a single element
* to be before returning an error .
*
* In the case of an indefinite length element , this is the sum total
* of all child elements .
*
* In the case of a definite length element , this represents the maximum
* size of the top - level element .
*/
unsigned long max_element_size;
SEC_ASN1NotifyProc notify_proc; /* call before/after handling field */
void *notify_arg; /* argument to notify_proc */
PRBool during_notify; /* true during call to notify_proc */
SEC_ASN1WriteProc filter_proc; /* pass field bytes to this */
void *filter_arg; /* argument to that function */
PRBool filter_only; /* do not allocate/store fields */
};
/*
* XXX this is a fairly generic function that may belong elsewhere
*/
static void *
sec_asn1d_alloc(PLArenaPool *poolp, unsigned long len)
{
void *thing;
if (poolp != NULL) {
/*
* Allocate from the pool .
*/
thing = PORT_ArenaAlloc(poolp, len);
} else {
/*
* Allocate generically .
*/
thing = PORT_Alloc(len);
}
return thing;
}
/*
* XXX this is a fairly generic function that may belong elsewhere
*/
static void *
sec_asn1d_zalloc(PLArenaPool *poolp, unsigned long len)
{
void *thing;
thing = sec_asn1d_alloc(poolp, len);
if (thing != NULL)
PORT_Memset(thing, 0 , len);
return thing;
}
static sec_asn1d_state *
sec_asn1d_push_state(SEC_ASN1DecoderContext *cx,
const SEC_ASN1Template *theTemplate,
void *dest, PRBool new_depth)
{
sec_asn1d_state *state, *new_state;
state = cx->current;
PORT_Assert(state == NULL || state->child == NULL);
if (state != NULL) {
PORT_Assert(state->our_mark == NULL);
state->our_mark = PORT_ArenaMark(cx->our_pool);
}
if (theTemplate == NULL) {
PORT_SetError(SEC_ERROR_BAD_TEMPLATE);
goto loser;
}
new_state = (sec_asn1d_state *)sec_asn1d_zalloc(cx->our_pool,
sizeof (*new_state));
if (new_state == NULL) {
goto loser;
}
new_state->top = cx;
new_state->parent = state;
new_state->theTemplate = theTemplate;
new_state->place = notInUse;
if (dest != NULL)
new_state->dest = (char *)dest + theTemplate->offset;
if (state != NULL) {
new_state->depth = state->depth;
if (new_depth) {
if (++new_state->depth > SEC_ASN1D_MAX_DEPTH) {
PORT_SetError(SEC_ERROR_BAD_DER);
goto loser;
}
}
state->child = new_state;
}
cx->current = new_state;
return new_state;
loser:
cx->status = decodeError;
if (state != NULL) {
PORT_ArenaRelease(cx->our_pool, state->our_mark);
state->our_mark = NULL;
}
return NULL;
}
static void
sec_asn1d_scrub_state(sec_asn1d_state *state)
{
/*
* Some default " scrubbing " .
* XXX right set of initializations ?
*/
state->place = beforeIdentifier;
state->endofcontents = PR_FALSE;
state->indefinite = PR_FALSE;
state->missing = PR_FALSE;
PORT_Assert(state->consumed == 0 );
}
static void
sec_asn1d_notify_before(SEC_ASN1DecoderContext *cx, void *dest, int depth)
{
if (cx->notify_proc == NULL)
return ;
cx->during_notify = PR_TRUE;
(*cx->notify_proc)(cx->notify_arg, PR_TRUE, dest, depth);
cx->during_notify = PR_FALSE;
}
static void
sec_asn1d_notify_after(SEC_ASN1DecoderContext *cx, void *dest, int depth)
{
if (cx->notify_proc == NULL)
return ;
cx->during_notify = PR_TRUE;
(*cx->notify_proc)(cx->notify_arg, PR_FALSE, dest, depth);
cx->during_notify = PR_FALSE;
}
static sec_asn1d_state *
sec_asn1d_init_state_based_on_template(sec_asn1d_state *state)
{
PRBool explicit , optional, universal;
unsigned char expect_tag_modifiers;
unsigned long encode_kind, under_kind;
unsigned long check_tag_mask, expect_tag_number;
/* XXX Check that both of these tests are really needed/appropriate. */
if (state == NULL || state->top->status == decodeError)
return state;
encode_kind = state->theTemplate->kind;
if (encode_kind & SEC_ASN1_SAVE) {
/*
* This is a " magic " field that saves away all bytes , allowing
* the immediately following field to still be decoded from this
* same spot - - sort of a fork .
*/
/* check that there are no extraneous bits */
PORT_Assert(encode_kind == SEC_ASN1_SAVE);
if (state->top->filter_only) {
/*
* If we are not storing , then we do not do the SAVE field
* at all . Just move ahead to the " real " field instead ,
* doing the appropriate notify calls before and after .
*/
sec_asn1d_notify_after(state->top, state->dest, state->depth);
/*
* Since we are not storing , allow for our current dest value
* to be NULL . ( This might not actually occur , but right now I
* cannot convince myself one way or the other . ) If it is NULL ,
* assume that our parent dest can help us out .
*/
if (state->dest == NULL)
state->dest = state->parent->dest;
else
state->dest = (char *)state->dest - state->theTemplate->offset;
state->theTemplate++;
if (state->dest != NULL)
state->dest = (char *)state->dest + state->theTemplate->offset;
sec_asn1d_notify_before(state->top, state->dest, state->depth);
encode_kind = state->theTemplate->kind;
PORT_Assert((encode_kind & SEC_ASN1_SAVE) == 0 );
} else {
sec_asn1d_scrub_state(state);
state->place = duringSaveEncoding;
state = sec_asn1d_push_state(state->top, SEC_AnyTemplate,
state->dest, PR_FALSE);
if (state != NULL)
state = sec_asn1d_init_state_based_on_template(state);
return state;
}
}
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)); /* bad templates */
encode_kind &= ~SEC_ASN1_DYNAMIC;
encode_kind &= ~SEC_ASN1_MAY_STREAM;
if (encode_kind & SEC_ASN1_CHOICE) {
#if 0 /* XXX remove? */
sec_asn1d_state *child = sec_asn1d_push_state(state->top, state->theTemplate, state->dest, PR_FALSE);
if ((sec_asn1d_state *)NULL == child) {
return (sec_asn1d_state *)NULL;
}
child->allocate = state->allocate;
child->place = beforeChoice;
return child;
#else
state->place = beforeChoice;
return state;
#endif
}
if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || (!universal && !explicit )) {
const SEC_ASN1Template *subt;
void *dest;
PRBool child_allocate;
PORT_Assert((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0 );
sec_asn1d_scrub_state(state);
child_allocate = PR_FALSE;
if (encode_kind & SEC_ASN1_POINTER) {
/*
* A POINTER means we need to allocate the destination for
* this field . But , since it may also be an optional field ,
* we defer the allocation until later ; we just record that
* it needs to be done .
*
* There are two possible scenarios here - - one is just a
* plain POINTER ( kind of like INLINE , except with allocation )
* and the other is an implicitly - tagged POINTER . We don ' t
* need to do anything special here for the two cases , but
* since the template definition can be tricky , we do check
* that there are no extraneous bits set in encode_kind .
*
* XXX The same conditions which assert should set an error .
*/
if (universal) {
/*
* " universal " means this entry is a standalone POINTER ;
* there should be no other bits set in encode_kind .
*/
PORT_Assert(encode_kind == SEC_ASN1_POINTER);
} else {
/*
* If we get here we have an implicitly - tagged field
* that needs to be put into a POINTER . The subtemplate
* will determine how to decode the field , but encode_kind
* describes the ( implicit ) tag we are looking for .
* The non - tag bits of encode_kind will be ignored by
* the code below ; none of them should be set , however ,
* except for the POINTER bit itself - - so check that .
*/
PORT_Assert((encode_kind & ~SEC_ASN1_TAG_MASK) == SEC_ASN1_POINTER);
}
if (!state->top->filter_only)
child_allocate = PR_TRUE;
dest = NULL;
state->place = afterPointer;
} else {
dest = state->dest;
if (encode_kind & SEC_ASN1_INLINE) {
/* check that there are no extraneous bits */
PORT_Assert(encode_kind == SEC_ASN1_INLINE);
state->place = afterInline;
} else {
state->place = afterImplicit;
}
}
state->optional = optional;
subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->dest, PR_FALSE);
state = sec_asn1d_push_state(state->top, subt, dest, PR_FALSE);
if (state == NULL)
return NULL;
state->allocate = child_allocate;
if (universal) {
state = sec_asn1d_init_state_based_on_template(state);
if (state != NULL) {
/*
* If this field is optional , we need to record that on
* the pushed child so it won ' t fail if the field isn ' t
* found . I can ' t think of a way that this new state
* could already have optional set ( which we would wipe
* out below if our local optional is not set ) - - but
* just to be sure , assert that it isn ' t set .
*/
PORT_Assert(!state->optional);
state->optional = optional;
}
return state;
}
under_kind = state->theTemplate->kind;
under_kind &= ~SEC_ASN1_MAY_STREAM;
} else if (explicit ) {
/*
* For explicit , we only need to match the encoding tag next ,
* then we will push another state to handle the entire inner
* part . In this case , there is no underlying kind which plays
* any part in the determination of the outer , explicit tag .
* So we just set under_kind to 0 , which is not a valid tag ,
* and the rest of the tag matching stuff should be okay .
*/
under_kind = 0 ;
} else {
/*
* Nothing special ; the underlying kind and the given encoding
* information are the same .
*/
under_kind = encode_kind;
}
/* XXX is this the right set of bits to test here? */
PORT_Assert((under_kind & (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_MAY_STREAM | SEC_ASN1_INLINE | SEC_ASN1_POINTER)) == 0 );
if (encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) {
PORT_Assert(encode_kind == under_kind);
if (encode_kind & SEC_ASN1_SKIP) {
PORT_Assert(!optional);
PORT_Assert(encode_kind == SEC_ASN1_SKIP);
state->dest = NULL;
}
check_tag_mask = 0 ;
expect_tag_modifiers = 0 ;
expect_tag_number = 0 ;
} else {
check_tag_mask = SEC_ASN1_TAG_MASK;
expect_tag_modifiers = (unsigned char )encode_kind & SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK;
/*
* XXX This assumes only single - octet identifiers . To handle
* the HIGH TAG form we would need to do some more work , especially
* in how to specify them in the template , because right now we
* do not provide a way to specify more * tag * bits in encode_kind .
*/
expect_tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK;
switch (under_kind & SEC_ASN1_TAGNUM_MASK) {
case SEC_ASN1_SET:
/*
* XXX A plain old SET ( as opposed to a SET OF ) is not implemented .
* If it ever is , remove this assert . . .
*/
PORT_Assert((under_kind & SEC_ASN1_GROUP) != 0 );
/* fallthru */
case SEC_ASN1_SEQUENCE:
expect_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:
check_tag_mask &= ~SEC_ASN1_CONSTRUCTED;
break ;
}
}
state->check_tag_mask = check_tag_mask;
state->expect_tag_modifiers = expect_tag_modifiers;
state->expect_tag_number = expect_tag_number;
state->underlying_kind = under_kind;
state->explicit = explicit ;
state->optional = optional;
sec_asn1d_scrub_state(state);
return state;
}
static sec_asn1d_state *
sec_asn1d_get_enclosing_construct(sec_asn1d_state *state)
{
for (state = state->parent; state; state = state->parent) {
sec_asn1d_parse_place place = state->place;
if (place != afterImplicit &&
place != afterPointer &&
place != afterInline &&
place != afterSaveEncoding &&
place != duringSaveEncoding &&
place != duringChoice) {
/* we've walked up the stack to a state that represents
* * the enclosing construct .
*/
break ;
}
}
return state;
}
static PRBool
sec_asn1d_parent_allows_EOC(sec_asn1d_state *state)
{
/* get state of enclosing construct. */
state = sec_asn1d_get_enclosing_construct(state);
if (state) {
sec_asn1d_parse_place place = state->place;
/* Is it one of the types that permits an unexpected EOC? */
int eoc_permitted =
(place == duringGroup ||
place == duringConstructedString ||
state->child->optional);
return (state->indefinite && eoc_permitted) ? PR_TRUE : PR_FALSE;
}
return PR_FALSE;
}
static unsigned long
sec_asn1d_parse_identifier(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
unsigned char byte;
unsigned char tag_number;
PORT_Assert(state->place == beforeIdentifier);
if (len == 0 ) {
state->top->status = needBytes;
return 0 ;
}
byte = (unsigned char )*buf;
#ifdef DEBUG_ASN1D_STATES
{
int bufsize = 256 ;
char kindBuf[bufsize];
formatKind(byte, kindBuf, bufsize);
printf("Found tag %02x %s\n" , byte, kindBuf);
}
#endif
tag_number = byte & SEC_ASN1_TAGNUM_MASK;
if (IS_HIGH_TAG_NUMBER(tag_number)) {
state->place = duringIdentifier;
state->found_tag_number = 0 ;
/*
* Actually , we have no idea how many bytes are pending , but we
* do know that it is at least 1 . That is all we know ; we have
* to look at each byte to know if there is another , etc .
*/
state->pending = 1 ;
} else {
if (byte == 0 && sec_asn1d_parent_allows_EOC(state)) {
/*
* Our parent has indefinite - length encoding , and the
* entire tag found is 0 , so it seems that we have hit the
* end - of - contents octets . To handle this , we just change
* our state to that which expects to get the bytes of the
* end - of - contents octets and let that code re - read this byte
* so that our categorization of field types is correct .
* After that , our parent will then deal with everything else .
*/
state->place = duringEndOfContents;
state->pending = 2 ;
state->found_tag_number = 0 ;
state->found_tag_modifiers = 0 ;
/*
* We might be an optional field that is , as we now find out ,
* missing . Give our parent a clue that this happened .
*/
if (state->optional)
state->missing = PR_TRUE;
return 0 ;
}
state->place = afterIdentifier;
state->found_tag_number = tag_number;
}
state->found_tag_modifiers = byte & ~SEC_ASN1_TAGNUM_MASK;
return 1 ;
}
static unsigned long
sec_asn1d_parse_more_identifier(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
unsigned char byte;
int count;
PORT_Assert(state->pending == 1 );
PORT_Assert(state->place == duringIdentifier);
if (len == 0 ) {
state->top->status = needBytes;
return 0 ;
}
count = 0 ;
while (len && state->pending) {
if (HIGH_BITS(state->found_tag_number, TAG_NUMBER_BITS) != 0 ) {
/*
* The given high tag number overflows our container ;
* just give up . This is not likely to * ever * happen .
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0 ;
}
state->found_tag_number <<= TAG_NUMBER_BITS;
byte = (unsigned char )buf[count++];
state->found_tag_number |= (byte & TAG_NUMBER_MASK);
len--;
if (LAST_TAG_NUMBER_BYTE(byte))
state->pending = 0 ;
}
if (state->pending == 0 )
state->place = afterIdentifier;
return count;
}
static void
sec_asn1d_confirm_identifier(sec_asn1d_state *state)
{
PRBool match;
PORT_Assert(state->place == afterIdentifier);
match = (PRBool)(((state->found_tag_modifiers & state->check_tag_mask) == state->expect_tag_modifiers) && ((state->found_tag_number & state->check_tag_mask) == state->expect_tag_number));
if (match) {
state->place = beforeLength;
} else {
if (state->optional) {
state->missing = PR_TRUE;
state->place = afterEndOfContents;
} else {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
}
}
}
static unsigned long
sec_asn1d_parse_length(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
unsigned char byte;
PORT_Assert(state->place == beforeLength);
if (len == 0 ) {
state->top->status = needBytes;
return 0 ;
}
/*
* The default / likely outcome . It may get adjusted below .
*/
state->place = afterLength;
byte = (unsigned char )*buf;
if (LENGTH_IS_SHORT_FORM(byte)) {
state->contents_length = byte;
} else {
state->contents_length = 0 ;
state->pending = LONG_FORM_LENGTH(byte);
if (state->pending == 0 ) {
state->indefinite = PR_TRUE;
} else {
state->place = duringLength;
}
}
/* If we're parsing an ANY, SKIP, or SAVE template, and
* * the object being saved is definite length encoded and constructed ,
* * there ' s no point in decoding that construct ' s members .
* * So , just forget it ' s constructed and treat it as primitive .
* * ( SAVE appears as an ANY at this point )
*/
if (!state->indefinite &&
(state->underlying_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP))) {
state->found_tag_modifiers &= ~SEC_ASN1_CONSTRUCTED;
}
return 1 ;
}
static unsigned long
sec_asn1d_parse_more_length(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
int count;
PORT_Assert(state->pending > 0 );
PORT_Assert(state->place == duringLength);
if (len == 0 ) {
state->top->status = needBytes;
return 0 ;
}
count = 0 ;
while (len && state->pending) {
if (HIGH_BITS(state->contents_length, 9 ) != 0 ) {
/*
* The given full content length overflows our container ;
* just give up .
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0 ;
}
state->contents_length <<= 8 ;
state->contents_length |= (unsigned char )buf[count++];
len--;
state->pending--;
}
if (state->pending == 0 )
state->place = afterLength;
return count;
}
/*
* Helper function for sec_asn1d_prepare_for_contents .
* Checks that a value representing a number of bytes consumed can be
* subtracted from a remaining length . If so , returns PR_TRUE .
* Otherwise , sets the error SEC_ERROR_BAD_DER , indicates that there was a
* decoding error in the given SEC_ASN1DecoderContext , and returns PR_FALSE .
*/
static PRBool
sec_asn1d_check_and_subtract_length(unsigned long *remaining,
unsigned long consumed,
SEC_ASN1DecoderContext *cx)
{
PORT_Assert(remaining);
PORT_Assert(cx);
if (!remaining) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
cx->status = decodeError;
return PR_FALSE;
}
if (*remaining < consumed) {
PORT_SetError(SEC_ERROR_BAD_DER);
cx->status = decodeError;
return PR_FALSE;
}
*remaining -= consumed;
return PR_TRUE;
}
static void
sec_asn1d_prepare_for_contents(sec_asn1d_state *state)
{
SECItem *item;
PLArenaPool *poolp;
unsigned long alloc_len;
sec_asn1d_state *parent;
#ifdef DEBUG_ASN1D_STATES
{
printf("Found Length %lu %s\n" , state->contents_length,
state->indefinite ? "indefinite" : "" );
}
#endif
/**
* The maximum length for a child element should be constrained to the
* length remaining in the first definite length element in the ancestor
* stack . If there is no definite length element in the ancestor stack ,
* there ' s nothing to constrain the length of the child , so there ' s no
* further processing necessary .
*
* It ' s necessary to walk the ancestor stack , because it ' s possible to have
* definite length children that are part of an indefinite length element ,
* which is itself part of an indefinite length element , and which is
* ultimately part of a definite length element . A simple example of this
* would be the handling of constructed OCTET STRINGs in BER encoding .
*
* This algorithm finds the first definite length element in the ancestor
* stack , if any , and if so , ensures that the length of the child element
* is consistent with the number of bytes remaining in the constraining
* ancestor element ( that is , after accounting for any other sibling
* elements that may have been read ) .
*
* It ' s slightly complicated by the need to account both for integer
* underflow and overflow , as well as ensure that for indefinite length
* encodings , there ' s also enough space for the End - of - Contents ( EOC )
* octets ( Tag = 0 x00 , Length = 0 x00 , or two bytes ) .
*/
/* Determine the maximum length available for this element by finding the
* first definite length ancestor, if any. */
parent = sec_asn1d_get_enclosing_construct(state);
while (parent && parent->indefinite) {
parent = sec_asn1d_get_enclosing_construct(parent);
}
/* If parent is null, state is either the outermost state / at the top of
* the stack , or the outermost state uses indefinite length encoding . In
* these cases , there ' s nothing external to constrain this element , so
* there's nothing to check. */
if (parent) {
unsigned long remaining = parent->pending;
parent = state;
do {
if (!sec_asn1d_check_and_subtract_length(
&remaining, parent->consumed, state->top) ||
/* If parent->indefinite is true, parent->contents_length is
* zero and this is a no-op. */
!sec_asn1d_check_and_subtract_length(
&remaining, parent->contents_length, state->top) ||
/* If parent->indefinite is true, then ensure there is enough
* space for an EOC tag of 2 bytes. */
(parent->indefinite && !sec_asn1d_check_and_subtract_length(&remaining, 2 , state->top))) {
/* This element is larger than its enclosing element, which is
* invalid. */
return ;
}
} while ((parent = sec_asn1d_get_enclosing_construct(parent)) &&
parent->indefinite);
}
/*
* XXX I cannot decide if this allocation should exclude the case
* where state - > endofcontents is true - - figure it out !
*/
if (state->allocate) {
void *dest;
PORT_Assert(state->dest == NULL);
/*
* We are handling a POINTER or a member of a GROUP , and need to
* allocate for the data structure .
*/
dest = sec_asn1d_zalloc(state->top->their_pool,
state->theTemplate->size);
if (dest == NULL) {
state->top->status = decodeError;
return ;
}
state->dest = (char *)dest + state->theTemplate->offset;
/*
* For a member of a GROUP , our parent will later put the
* pointer wherever it belongs . But for a POINTER , we need
* to record the destination now , in case notify or filter
* procs need access to it - - they cannot find it otherwise ,
* until it is too late ( for one - pass processing ) .
*/
if (state->parent->place == afterPointer) {
void **placep;
placep = state->parent->dest;
*placep = dest;
}
}
/*
* Remember , length may be indefinite here ! In that case ,
* both contents_length and pending will be zero .
*/
state->pending = state->contents_length;
/*
* An EXPLICIT is nothing but an outer header , which we have
* already parsed and accepted . Now we need to do the inner
* header and its contents .
*/
if (state->explicit ) {
state->place = afterExplicit;
state = sec_asn1d_push_state(state->top,
SEC_ASN1GetSubtemplate(state->theTemplate,
state->dest,
PR_FALSE),
state->dest, PR_TRUE);
if (state != NULL) {
(void )sec_asn1d_init_state_based_on_template(state);
}
return ;
}
/*
* For GROUP ( SET OF , SEQUENCE OF ) , even if we know the length here
* we cannot tell how many items we will end up with . . . so push a
* state that can keep track of " children " ( the individual members
* of the group ; we will allocate as we go and put them all together
* at the end .
*/
if (state->underlying_kind & SEC_ASN1_GROUP) {
/* XXX If this assertion holds (should be able to confirm it via
* inspection , too ) then move this code into the switch statement
* below under cases SET_OF and SEQUENCE_OF ; it will be cleaner .
*/
PORT_Assert(state->underlying_kind == SEC_ASN1_SET_OF || state->underlying_kind == SEC_ASN1_SEQUENCE_OF || state->underlying_kind == (SEC_ASN1_SET_OF | SEC_ASN1_DYNAMIC) || state->underlying_kind == (SEC_ASN1_SEQUENCE_OF | SEC_ASN1_DYNAMIC));
if (state->contents_length != 0 || state->indefinite) {
const SEC_ASN1Template *subt;
state->place = duringGroup;
subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->dest,
PR_FALSE);
state = sec_asn1d_push_state(state->top, subt, NULL, PR_TRUE);
if (state != NULL) {
if (!state->top->filter_only)
state->allocate = PR_TRUE; /* XXX propogate this? */
/*
* Do the " before " field notification for next in group .
*/
sec_asn1d_notify_before(state->top, state->dest, state->depth);
(void )sec_asn1d_init_state_based_on_template(state);
}
} else {
/*
* A group of zero ; we are done .
* Set state to afterGroup and let that code plant the NULL .
*/
state->place = afterGroup;
}
return ;
}
switch (state->underlying_kind) {
case SEC_ASN1_SEQUENCE:
/*
* We need to push a child to handle the individual fields .
*/
state->place = duringSequence;
state = sec_asn1d_push_state(state->top, state->theTemplate + 1 ,
state->dest, PR_TRUE);
if (state != NULL) {
/*
* Do the " before " field notification .
*/
sec_asn1d_notify_before(state->top, state->dest, state->depth);
(void )sec_asn1d_init_state_based_on_template(state);
}
break ;
case SEC_ASN1_SET: /* XXX SET is not really implemented */
/*
* XXX A plain SET requires special handling ; scanning of a
* template to see where a field should go ( because by definition ,
* they are not in any particular order , and you have to look at
* each tag to disambiguate what the field is ) . We may never
* implement this because in practice , it seems to be unused .
*/
PORT_Assert(0 );
PORT_SetError(SEC_ERROR_BAD_DER); /* XXX */
state->top->status = decodeError;
break ;
case SEC_ASN1_NULL:
/*
* The NULL type , by definition , is " nothing " , content length of zero .
* An indefinite - length encoding is not alloweed .
*/
if (state->contents_length || state->indefinite) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
break ;
}
if (state->dest != NULL) {
item = (SECItem *)(state->dest);
item->data = NULL;
item->len = 0 ;
}
state->place = afterEndOfContents;
break ;
case SEC_ASN1_BMP_STRING:
/* Error if length is not divisable by 2 */
if (state->contents_length % 2 ) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
break ;
}
/* otherwise, handle as other string types */
goto regular_string_type;
case SEC_ASN1_UNIVERSAL_STRING:
/* Error if length is not divisable by 4 */
if (state->contents_length % 4 ) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
break ;
}
/* otherwise, handle as other string types */
goto regular_string_type;
case SEC_ASN1_SKIP:
case SEC_ASN1_ANY:
case SEC_ASN1_ANY_CONTENTS:
/*
* These are not ( necessarily ) strings , but they need nearly
* identical handling ( especially when we need to deal with
* constructed sub - pieces ) , so we pretend they are .
*/
/* fallthru */
regular_string_type:
case SEC_ASN1_BIT_STRING:
case SEC_ASN1_IA5_STRING:
case SEC_ASN1_OCTET_STRING:
case SEC_ASN1_PRINTABLE_STRING:
case SEC_ASN1_T61_STRING:
case SEC_ASN1_UTC_TIME:
case SEC_ASN1_UTF8_STRING:
case SEC_ASN1_VISIBLE_STRING:
/*
* We are allocating for a primitive or a constructed string .
* If it is a constructed string , it may also be indefinite - length .
* If it is primitive , the length can ( legally ) be zero .
* Our first order of business is to allocate the memory for
* the string , if we can ( if we know the length ) .
*/
item = (SECItem *)(state->dest);
/*
* If the item is a definite - length constructed string , then
* the contents_length is actually larger than what we need
* ( because it also counts each intermediate header which we
* will be throwing away as we go ) , but it is a perfectly good
* upper bound that we just allocate anyway , and then concat
* as we go ; we end up wasting a few extra bytes but save a
* whole other copy .
*/
alloc_len = state->contents_length;
poolp = NULL; /* quiet compiler warnings about unused... */
if (item == NULL || state->top->filter_only) {
if (item != NULL) {
item->data = NULL;
item->len = 0 ;
}
alloc_len = 0 ;
} else if (state->substring) {
/*
* If we are a substring of a constructed string , then we may
* not have to allocate anything ( because our parent , the
* actual constructed string , did it for us ) . If we are a
* substring and we * do * have to allocate , that means our
* parent is an indefinite - length , so we allocate from our pool ;
* later our parent will copy our string into the aggregated
* whole and free our pool allocation .
*/
if (item->data == NULL) {
PORT_Assert(item->len == 0 );
poolp = state->top->our_pool;
} else {
alloc_len = 0 ;
}
} else {
item->len = 0 ;
item->data = NULL;
poolp = state->top->their_pool;
}
if (alloc_len || ((!state->indefinite) && (state->subitems_head != NULL))) {
struct subitem *subitem;
int len;
PORT_Assert(item);
if (!item) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return ;
}
PORT_Assert(item->len == 0 && item->data == NULL);
/*
* Check for and handle an ANY which has stashed aside the
* header ( identifier and length ) bytes for us to include
* in the saved contents .
*/
if (state->subitems_head != NULL) {
PORT_Assert(state->underlying_kind == SEC_ASN1_ANY);
for (subitem = state->subitems_head;
subitem != NULL; subitem = subitem->next)
alloc_len += subitem->len;
}
if (state->top->max_element_size > 0 &&
alloc_len > state->top->max_element_size) {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
state->top->status = decodeError;
return ;
}
item->data = (unsigned char *)sec_asn1d_zalloc(poolp, alloc_len);
if (item->data == NULL) {
state->top->status = decodeError;
break ;
}
len = 0 ;
for (subitem = state->subitems_head;
subitem != NULL; subitem = subitem->next) {
PORT_Memcpy(item->data + len, subitem->data, subitem->len);
len += subitem->len;
}
item->len = len;
/*
* Because we use arenas and have a mark set , we later free
* everything we have allocated , so this does * not * present
* a memory leak ( it is just temporarily left dangling ) .
*/
state->subitems_head = state->subitems_tail = NULL;
}
if (state->contents_length == 0 && (!state->indefinite)) {
/*
* A zero - length simple or constructed string ; we are done .
*/
state->place = afterEndOfContents;
} else if (state->found_tag_modifiers & SEC_ASN1_CONSTRUCTED) {
const SEC_ASN1Template *sub;
switch (state->underlying_kind) {
case SEC_ASN1_ANY:
case SEC_ASN1_ANY_CONTENTS:
sub = SEC_AnyTemplate;
break ;
case SEC_ASN1_BIT_STRING:
sub = SEC_BitStringTemplate;
break ;
case SEC_ASN1_BMP_STRING:
sub = SEC_BMPStringTemplate;
break ;
case SEC_ASN1_GENERALIZED_TIME:
sub = SEC_GeneralizedTimeTemplate;
break ;
case SEC_ASN1_IA5_STRING:
sub = SEC_IA5StringTemplate;
break ;
case SEC_ASN1_OCTET_STRING:
sub = SEC_OctetStringTemplate;
break ;
case SEC_ASN1_PRINTABLE_STRING:
sub = SEC_PrintableStringTemplate;
break ;
case SEC_ASN1_T61_STRING:
sub = SEC_T61StringTemplate;
break ;
case SEC_ASN1_UNIVERSAL_STRING:
sub = SEC_UniversalStringTemplate;
break ;
case SEC_ASN1_UTC_TIME:
sub = SEC_UTCTimeTemplate;
break ;
case SEC_ASN1_UTF8_STRING:
sub = SEC_UTF8StringTemplate;
break ;
case SEC_ASN1_VISIBLE_STRING:
sub = SEC_VisibleStringTemplate;
break ;
case SEC_ASN1_SKIP:
sub = SEC_SkipTemplate;
break ;
default : /* redundant given outer switch cases, but */
PORT_Assert(0 ); /* the compiler does not seem to know that, */
sub = NULL; /* so just do enough to quiet it. */
break ;
}
state->place = duringConstructedString;
state = sec_asn1d_push_state(state->top, sub, item, PR_TRUE);
if (state != NULL) {
state->substring = PR_TRUE; /* XXX propogate? */
(void )sec_asn1d_init_state_based_on_template(state);
}
} else if (state->indefinite) {
/*
* An indefinite - length string * must * be constructed !
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
} else {
/*
* A non - zero - length simple string .
*/
if (state->underlying_kind == SEC_ASN1_BIT_STRING)
state->place = beforeBitString;
else
state->place = duringLeaf;
}
break ;
default :
/*
* We are allocating for a simple leaf item .
*/
if (state->contents_length) {
if (state->dest != NULL) {
item = (SECItem *)(state->dest);
item->len = 0 ;
if (state->top->max_element_size > 0 &&
state->contents_length > state->top->max_element_size) {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
state->top->status = decodeError;
return ;
}
if (state->top->filter_only) {
item->data = NULL;
} else {
item->data = (unsigned char *)
sec_asn1d_zalloc(state->top->their_pool,
state->contents_length);
if (item->data == NULL) {
state->top->status = decodeError;
return ;
}
}
}
state->place = duringLeaf;
} else {
/*
* An indefinite - length or zero - length item is not allowed .
* ( All legal cases of such were handled above . )
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
}
}
}
static void
sec_asn1d_free_child(sec_asn1d_state *state, PRBool error)
{
if (state->child != NULL) {
PORT_Assert(error || state->child->consumed == 0 );
PORT_Assert(state->our_mark != NULL);
PORT_ArenaZRelease(state->top->our_pool, state->our_mark);
if (error && state->top->their_pool == NULL) {
/*
* XXX We need to free anything allocated .
* At this point , we failed in the middle of decoding . But we
* can ' t free the data we previously allocated with PR_Malloc
* unless we keep track of every pointer . So instead we have a
* memory leak when decoding fails half - way , unless an arena is
* used . See bug 95311 .
*/
}
state->child = NULL;
state->our_mark = NULL;
} else {
/*
* It is important that we do not leave a mark unreleased / unmarked .
* But I do not think we should ever have one set in this case , only
* if we had a child ( handled above ) . So check for that . If this
* assertion should ever get hit , then we probably need to add code
* here to release back to our_mark ( and then set our_mark to NULL ) .
*/
PORT_Assert(state->our_mark == NULL);
}
state->place = beforeEndOfContents;
}
/* We have just saved an entire encoded ASN.1 object (type) for a SAVE
* * template , and now in the next template , we are going to decode that
* * saved data by calling SEC_ASN1DecoderUpdate recursively .
* * If that recursive call fails with needBytes , it is a fatal error ,
* * because the encoded object should have been complete .
* * If that recursive call fails with decodeError , it will have already
* * cleaned up the state stack , so we must bail out quickly .
* *
* * These checks of the status returned by the recursive call are now
* * done in the caller of this function , immediately after it returns .
*/
static void
sec_asn1d_reuse_encoding(sec_asn1d_state *state)
{
sec_asn1d_state *child;
unsigned long consumed;
SECItem *item;
void *dest;
child = state->child;
PORT_Assert(child != NULL);
consumed = child->consumed;
child->consumed = 0 ;
item = (SECItem *)(state->dest);
PORT_Assert(item != NULL);
PORT_Assert(item->len == consumed);
/*
* Free any grandchild .
*/
sec_asn1d_free_child(child, PR_FALSE);
/*
* Notify after the SAVE field .
*/
sec_asn1d_notify_after(state->top, state->dest, state->depth);
/*
* Adjust to get new dest and move forward .
*/
dest = (char *)state->dest - state->theTemplate->offset;
state->theTemplate++;
child->dest = (char *)dest + state->theTemplate->offset;
child->theTemplate = state->theTemplate;
/*
* Notify before the " real " field .
*/
PORT_Assert(state->depth == child->depth);
sec_asn1d_notify_before(state->top, child->dest, child->depth);
/*
* This will tell DecoderUpdate to return when it is done .
*/
state->place = afterSaveEncoding;
/*
* We already have a child ; " push " it by making it current .
*/
state->top->current = child;
/*
* And initialize it so it is ready to parse .
*/
(void )sec_asn1d_init_state_based_on_template(child);
/*
* Now parse that out of our data .
*/
if (SEC_ASN1DecoderUpdate(state->top,
(char *)item->data, item->len) != SECSuccess)
return ;
if (state->top->status == needBytes) {
return ;
}
PORT_Assert(state->top->current == state);
PORT_Assert(state->child == child);
/*
* That should have consumed what we consumed before .
*/
PORT_Assert(consumed == child->consumed);
child->consumed = 0 ;
/*
* Done .
*/
state->consumed += consumed;
child->place = notInUse;
state->place = afterEndOfContents;
}
static unsigned long
sec_asn1d_parse_leaf(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
SECItem *item;
unsigned long bufLen;
if (len == 0 ) {
state->top->status = needBytes;
return 0 ;
}
if (state->pending < len)
len = state->pending;
bufLen = len;
item = (SECItem *)(state->dest);
if (item != NULL && item->data != NULL) {
unsigned long offset;
/* Strip leading zeroes when target is unsigned integer */
if (state->underlying_kind == SEC_ASN1_INTEGER && /* INTEGER */
item->len == 0 && /* MSB */
item->type == siUnsignedInteger) /* unsigned */
{
while (len > 1 && buf[0 ] == 0 ) { /* leading 0 */
buf++;
len--;
}
}
offset = item->len;
if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
// The previous bit string must have no unused bits.
if (item->len & 0 x7) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0 ;
}
// If this is a bit string, the length is bits, not bytes.
offset = item->len >> 3 ;
}
if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
unsigned long len_in_bits;
// Protect against overflow during the bytes-to-bits conversion.
if (len >= (ULONG_MAX >> 3 ) + 1 ) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0 ;
}
len_in_bits = (len << 3 ) - state->bit_string_unused_bits;
// Protect against overflow when computing the total length in bits.
if (UINT_MAX - item->len < len_in_bits) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0 ;
}
item->len += len_in_bits;
} else {
if (UINT_MAX - item->len < len) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0 ;
}
item->len += len;
}
PORT_Memcpy(item->data + offset, buf, len);
}
state->pending -= bufLen;
if (state->pending == 0 )
state->place = beforeEndOfContents;
return bufLen;
}
static unsigned long
sec_asn1d_parse_bit_string(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
unsigned char byte;
/*PORT_Assert (state->pending > 0); */
PORT_Assert(state->place == beforeBitString);
if (state->pending == 0 ) {
if (state->dest != NULL) {
SECItem *item = (SECItem *)(state->dest);
item->data = NULL;
item->len = 0 ;
state->place = beforeEndOfContents;
return 0 ;
}
}
if (len == 0 ) {
state->top->status = needBytes;
return 0 ;
}
byte = (unsigned char )*buf;
if (byte > 7 ) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0 ;
}
state->bit_string_unused_bits = byte;
state->place = duringBitString;
state->pending -= 1 ;
return 1 ;
}
static unsigned long
sec_asn1d_parse_more_bit_string(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
PORT_Assert(state->place == duringBitString);
if (state->pending == 0 ) {
/* An empty bit string with some unused bits is invalid. */
if (state->bit_string_unused_bits) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
} else {
/* An empty bit string with no unused bits is OK. */
state->place = beforeEndOfContents;
}
return 0 ;
}
len = sec_asn1d_parse_leaf(state, buf, len);
return len;
}
/*
* XXX All callers should be looking at return value to detect
* out - of - memory errors ( and stop ! ) .
*/
static struct subitem *
sec_asn1d_add_to_subitems(sec_asn1d_state *state,
const void *data, unsigned long len,
PRBool copy_data)
{
struct subitem *thing;
thing = (struct subitem *)sec_asn1d_zalloc(state->top->our_pool,
sizeof (struct subitem));
if (thing == NULL) {
state->top->status = decodeError;
return NULL;
}
if (copy_data) {
void *copy;
copy = sec_asn1d_alloc(state->top->our_pool, len);
if (copy == NULL) {
state->top->status = decodeError;
if (!state->top->our_pool)
PORT_Free(thing);
return NULL;
}
PORT_Memcpy(copy, data, len);
thing->data = copy;
} else {
thing->data = data;
}
thing->len = len;
thing->next = NULL;
if (state->subitems_head == NULL) {
PORT_Assert(state->subitems_tail == NULL);
state->subitems_head = state->subitems_tail = thing;
} else {
state->subitems_tail->next = thing;
state->subitems_tail = thing;
}
return thing;
}
static void
sec_asn1d_record_any_header(sec_asn1d_state *state,
const char *buf,
unsigned long len)
{
SECItem *item;
item = (SECItem *)(state->dest);
if (item != NULL && item->data != NULL) {
PORT_Assert(state->substring);
PORT_Memcpy(item->data + item->len, buf, len);
item->len += len;
} else {
sec_asn1d_add_to_subitems(state, buf, len, PR_TRUE);
}
}
/*
* We are moving along through the substrings of a constructed string ,
* and have just finished parsing one - - we need to save our child data
* ( if the child was not already writing directly into the destination )
* and then move forward by one .
*
* We also have to detect when we are done :
* - a definite - length encoding stops when our pending value hits 0
* - an indefinite - length encoding stops when our child is empty
* ( which means it was the end - of - contents octets )
*/
static void
sec_asn1d_next_substring(sec_asn1d_state *state)
{
sec_asn1d_state *child;
SECItem *item;
unsigned long child_consumed;
PRBool done;
PORT_Assert(state->place == duringConstructedString);
PORT_Assert(state->child != NULL);
child = state->child;
child_consumed = child->consumed;
child->consumed = 0 ;
state->consumed += child_consumed;
done = PR_FALSE;
if (state->pending) {
PORT_Assert(!state->indefinite);
if (child_consumed > state->pending) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return ;
}
state->pending -= child_consumed;
if (state->pending == 0 )
done = PR_TRUE;
} else {
PRBool preallocatedString;
sec_asn1d_state *temp_state;
PORT_Assert(state->indefinite);
item = (SECItem *)(child->dest);
/**
* At this point , there ' s three states at play :
* child : The element that was just parsed
* state : The currently processed element
* ' parent ' ( aka state - > parent ) : The enclosing construct
* of state , or NULL if this is the top - most element .
*
* This state handles both substrings of a constructed string AND
* child elements of items whose template type was that of
* SEC_ASN1_ANY , SEC_ASN1_SAVE , SEC_ASN1_ANY_CONTENTS , SEC_ASN1_SKIP
* template , as described in sec_asn1d_prepare_for_contents . For
* brevity , these will be referred to as ' string ' and ' any ' types .
*
* This leads to the following possibilities :
* 1 : This element is an indefinite length string , part of a
* definite length string .
* 2 : This element is an indefinite length string , part of an
* indefinite length string .
* 3 : This element is an indefinite length any , part of a
* definite length any .
* 4 : This element is an indefinite length any , part of an
* indefinite length any .
* 5 : This element is an indefinite length any and does not
* meet any of the above criteria . Note that this would include
* an indefinite length string type matching an indefinite
* length any template .
*
* In Cases # 1 and # 3 , the definite length ' parent ' element will
* have allocated state - > dest based on the parent elements definite
* size . During the processing of ' child ' , sec_asn1d_parse_leaf will
* have copied the ( string , any ) data directly into the offset of
* dest , as appropriate , so there ' s no need for this class to still
* store the child - it ' s already been processed .
*
* In Cases # 2 and # 4 , dest will be set to the parent element ' s dest ,
* but dest - > data will not have been allocated yet , due to the
* indefinite length encoding . In this situation , it ' s necessary to
* hold onto child ( and all other children ) until the EOC , at which
* point , it becomes possible to compute ' state ' s overall length . Once
* ' state ' has a computed length , this can then be fed to ' parent ' ( via
* this state ) , and then ' parent ' can similarly compute the length of
* all of its children up to the EOC , which will ultimately transit to
* sec_asn1d_concat_substrings , determine the overall size needed ,
* allocate , and copy the contents ( of all of parent ' s children , which
* would include ' state ' , just as ' state ' will have copied all of its
* children via sec_asn1d_concat_substrings )
*
* The final case , Case # 5 , will manifest in that item - > data and
* item - > len will be NULL / 0 , respectively , since this element was
* indefinite - length encoded . In that case , both the tag and length will
* already exist in state ' s subitems , via sec_asn1d_record_any_header ,
* and so the contents ( aka ' child ' ) should be added to that list of
* items to concatenate in sec_asn1d_concat_substrings once the EOC
* is encountered .
*
* To distinguish # 2 / # 4 from # 1 / # 3 , it ' s sufficient to walk the ancestor
* tree . If the current type is a string type , then the enclosing
* construct will be that same type ( # 1 / # 2 ) . If the current type is an
* any type , then the enclosing construct is either an any type ( # 3 / # 4 )
* or some other type ( # 5 ) . Since this is BER , this nesting relationship
* between ' state ' and ' parent ' may go through several levels of
* constructed encoding , so continue walking the ancestor chain until a
* clear determination can be made .
*
* The variable preallocatedString is used to indicate Case # 1 / # 3 ,
* indicating an in - place copy has already occurred , and Cases # 2 , # 4 ,
* and # 5 all have the same behaviour of adding a new substring .
*/
preallocatedString = PR_FALSE;
temp_state = state;
while (temp_state && item == temp_state->dest && temp_state->indefinite) {
sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(temp_state);
if (!parent || parent->underlying_kind != temp_state->underlying_kind) {
/* Case #5 - Either this is a top-level construct or it is part
* of some other element ( e . g . a SEQUENCE ) , in which case , a
* new item should be allocated. */
break ;
}
if (!parent->indefinite) {
/* Cases #1 / #3 - A definite length ancestor exists, for which
* this is a substring that has already copied into dest. */
preallocatedString = PR_TRUE;
break ;
}
if (!parent->substring) {
/* Cases #2 / #4 - If the parent is not a substring, but is
* indefinite , then there ' s nothing further up that may have
* preallocated dest , thus child will not have already
* been copied in place , therefore it ' s necessary to save child
* as a subitem. */
break ;
}
temp_state = parent;
}
if (item != NULL && item->data != NULL && !preallocatedString) {
/*
* Save the string away for later concatenation .
*/
PORT_Assert(item->data != NULL);
sec_asn1d_add_to_subitems(state, item->data, item->len, PR_FALSE);
/*
* Clear the child item for the next round .
*/
item->data = NULL;
item->len = 0 ;
}
/*
* If our child was just our end - of - contents octets , we are done .
*/
if (child->endofcontents)
done = PR_TRUE;
}
/*
* Stop or do the next one .
*/
if (done) {
child->place = notInUse;
state->place = afterConstructedString;
} else {
sec_asn1d_scrub_state(child);
state->top->current = child;
}
}
/*
* We are doing a SET OF or SEQUENCE OF , and have just finished an item .
*/
static void
sec_asn1d_next_in_group(sec_asn1d_state *state)
{
sec_asn1d_state *child;
unsigned long child_consumed;
PORT_Assert(state->place == duringGroup);
PORT_Assert(state->child != NULL);
child = state->child;
child_consumed = child->consumed;
child->consumed = 0 ;
state->consumed += child_consumed;
/*
* If our child was just our end - of - contents octets , we are done .
*/
if (child->endofcontents) {
/* XXX I removed the PORT_Assert (child->dest == NULL) because there
* was a bug in that a template that was a sequence of which also had
* a child of a sequence of , in an indefinite group was not working
* properly . This fix seems to work , ( added the if statement below ) ,
* and nothing appears broken , but I am putting this note here just
* in case. */
/*
* XXX No matter how many times I read that comment ,
* I cannot figure out what case he was fixing . I believe what he
* did was deliberate , so I am loathe to touch it . I need to
* understand how it could ever be that child - > dest ! = NULL but
* child - > endofcontents is true , and why it is important to check
* that state - > subitems_head is NULL . This really needs to be
* figured out , as I am not sure if the following code should be
* compensating for " offset " , as is done a little farther below
* in the more normal case .
*/
/*
* XXX We used to assert our overall state was that we were decoding
* an indefinite - length object here ( state - > indefinite = = TRUE and no
* pending bytes in the decoder ) , but those assertions aren ' t correct
* as it ' s legitimate to wrap indefinite sequences inside definite ones
* and this code handles that case . Additionally , when compiled in
* release mode these assertions aren ' t checked anyway , yet function
* safely .
*/
if (child->dest && !state->subitems_head) {
sec_asn1d_add_to_subitems(state, child->dest, 0 , PR_FALSE);
child->dest = NULL;
}
child->place = notInUse;
state->place = afterGroup;
return ;
}
/*
* Do the " after " field notification for next in group .
*/
sec_asn1d_notify_after(state->top, child->dest, child->depth);
/*
* Save it away ( unless we are not storing ) .
*/
if (child->dest != NULL) {
void *dest;
dest = child->dest;
dest = (char *)dest - child->theTemplate->offset;
sec_asn1d_add_to_subitems(state, dest, 0 , PR_FALSE);
child->dest = NULL;
}
/*
* Account for those bytes ; see if we are done .
*/
if (state->pending) {
PORT_Assert(!state->indefinite);
if (child_consumed > state->pending) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return ;
}
state->pending -= child_consumed;
if (state->pending == 0 ) {
child->place = notInUse;
state->place = afterGroup;
return ;
}
}
/*
* Do the " before " field notification for next item in group .
*/
sec_asn1d_notify_before(state->top, child->dest, child->depth);
/*
* Now we do the next one .
*/
sec_asn1d_scrub_state(child);
/* Initialize child state from the template */
sec_asn1d_init_state_based_on_template(child);
state->top->current = child;
}
/*
* We are moving along through a sequence ; move forward by one ,
* ( detecting end - of - sequence when it happens ) .
* XXX The handling of " missing " is ugly . Fix it .
*/
static void
sec_asn1d_next_in_sequence(sec_asn1d_state *state)
{
sec_asn1d_state *child;
unsigned long child_consumed;
PRBool child_missing;
PORT_Assert(state->place == duringSequence);
PORT_Assert(state->child != NULL);
child = state->child;
/*
* Do the " after " field notification .
*/
sec_asn1d_notify_after(state->top, child->dest, child->depth);
child_missing = (PRBool)child->missing;
child_consumed = child->consumed;
child->consumed = 0 ;
/*
* Take care of accounting .
*/
if (child_missing) {
PORT_Assert(child->optional);
} else {
state->consumed += child_consumed;
/*
* Free any grandchild .
*/
sec_asn1d_free_child(child, PR_FALSE);
if (state->pending) {
PORT_Assert(!state->indefinite);
if (child_consumed > state->pending) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return ;
}
state->pending -= child_consumed;
if (state->pending == 0 ) {
child->theTemplate++;
while (child->theTemplate->kind != 0 ) {
if ((child->theTemplate->kind & SEC_ASN1_OPTIONAL) == 0 ) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return ;
}
child->theTemplate++;
}
child->place = notInUse;
state->place = afterEndOfContents;
return ;
}
}
}
/*
* Move forward .
*/
child->theTemplate++;
if (child->theTemplate->kind == 0 ) {
/*
* We are done with this sequence .
*/
child->place = notInUse;
if (state->pending) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
} else if (child_missing) {
/*
* We got to the end , but have a child that started parsing
* and ended up " missing " . The only legitimate reason for
* this is that we had one or more optional fields at the
* end of our sequence , and we were encoded indefinite - length ,
* so when we went looking for those optional fields we
* found our end - of - contents octets instead .
* ( Yes , this is ugly ; dunno a better way to handle it . )
* So , first confirm the situation , and then mark that we
* are done .
*/
if (state->indefinite && child->endofcontents) {
PORT_Assert(child_consumed == 2 );
if (child_consumed != 2 ) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
} else {
state->consumed += child_consumed;
state->place = afterEndOfContents;
}
} else {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
}
} else {
/*
* We have to finish out , maybe reading end - of - contents octets ;
* let the normal logic do the right thing .
*/
state->place = beforeEndOfContents;
}
} else {
unsigned char child_found_tag_modifiers = 0 ;
unsigned long child_found_tag_number = 0 ;
/*
* Reset state and push .
*/
if (state->dest != NULL)
child->dest = (char *)state->dest + child->theTemplate->offset;
/*
* Do the " before " field notification .
*/
sec_asn1d_notify_before(state->top, child->dest, child->depth);
if (child_missing) { /* if previous child was missing, copy the tag data we already have */
child_found_tag_modifiers = child->found_tag_modifiers;
child_found_tag_number = child->found_tag_number;
}
state->top->current = child;
child = sec_asn1d_init_state_based_on_template(child);
if (child_missing && child) {
child->place = afterIdentifier;
child->found_tag_modifiers = child_found_tag_modifiers;
child->found_tag_number = child_found_tag_number;
child->consumed = child_consumed;
if (child->underlying_kind == SEC_ASN1_ANY && !child->top->filter_only) {
/*
* If the new field is an ANY , and we are storing , then
* we need to save the tag out . We would have done this
* already in the normal case , but since we were looking
* for an optional field , and we did not find it , we only
* now realize we need to save the tag .
*/
unsigned char identifier;
/*
* Check that we did not end up with a high tag ; for that
* we need to re - encode the tag into multiple bytes in order
* to store it back to look like what we parsed originally .
* In practice this does not happen , but for completeness
* sake it should probably be made to work at some point .
*/
if (child_found_tag_modifiers >= SEC_ASN1_HIGH_TAG_NUMBER) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
state->top->status = decodeError;
} else {
identifier = (unsigned char )(child_found_tag_modifiers | child_found_tag_number);
sec_asn1d_record_any_header(child, (char *)&identifier, 1 );
}
}
}
}
}
static void
sec_asn1d_concat_substrings(sec_asn1d_state *state)
{
PORT_Assert(state->place == afterConstructedString);
if (state->subitems_head != NULL) {
struct subitem *substring;
unsigned long alloc_len, item_len;
unsigned char *where;
SECItem *item;
PRBool is_bit_string;
item_len = 0 ;
is_bit_string = (state->underlying_kind == SEC_ASN1_BIT_STRING)
? PR_TRUE
: PR_FALSE;
substring = state->subitems_head;
while (substring != NULL) {
/*
* All bit - string substrings except the last one should be
* a clean multiple of 8 bits .
*/
if (is_bit_string && (substring->next != NULL) && (substring->len & 0 x7)) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return ;
}
item_len += substring->len;
substring = substring->next;
}
if (is_bit_string) {
alloc_len = ((item_len + 7 ) >> 3 );
} else {
/*
* Add 2 for the end - of - contents octets of an indefinite - length
* ANY that is * not * also an INNER . Because we zero - allocate
* below , all we need to do is increase the length here .
*/
if (state->underlying_kind == SEC_ASN1_ANY && state->indefinite)
item_len += 2 ;
alloc_len = item_len;
}
if (state->top->max_element_size > 0 &&
alloc_len > state->top->max_element_size) {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
state->top->status = decodeError;
return ;
}
item = (SECItem *)(state->dest);
PORT_Assert(item != NULL);
PORT_Assert(item->data == NULL);
item->data = (unsigned char *)sec_asn1d_zalloc(state->top->their_pool,
alloc_len);
if (item->data == NULL) {
state->top->status = decodeError;
return ;
}
item->len = item_len;
where = item->data;
substring = state->subitems_head;
while (substring != NULL) {
if (is_bit_string)
item_len = (substring->len + 7 ) >> 3 ;
else
item_len = substring->len;
PORT_Memcpy(where, substring->data, item_len);
where += item_len;
substring = substring->next;
}
/*
* Because we use arenas and have a mark set , we later free
* everything we have allocated , so this does * not * present
* a memory leak ( it is just temporarily left dangling ) .
*/
state->subitems_head = state->subitems_tail = NULL;
}
state->place = afterEndOfContents;
}
static void
sec_asn1d_concat_group(sec_asn1d_state *state)
{
const void ***placep;
PORT_Assert(state->place == afterGroup);
placep = (const void ***)state->dest;
PORT_Assert(state->subitems_head == NULL || placep != NULL);
if (placep != NULL) {
struct subitem *item;
const void **group;
int count;
count = 0 ;
item = state->subitems_head;
while (item != NULL) {
PORT_Assert(item->next != NULL || item == state->subitems_tail);
count++;
item = item->next;
}
group = (const void **)sec_asn1d_zalloc(state->top->their_pool,
(count + 1 ) * (sizeof (void *)));
if (group == NULL) {
state->top->status = decodeError;
return ;
}
*placep = group;
item = state->subitems_head;
while (item != NULL) {
*group++ = item->data;
item = item->next;
}
*group = NULL;
/*
* Because we use arenas and have a mark set , we later free
* everything we have allocated , so this does * not * present
* a memory leak ( it is just temporarily left dangling ) .
*/
state->subitems_head = state->subitems_tail = NULL;
}
state->place = afterEndOfContents;
}
/*
* For those states that push a child to handle a subtemplate ,
* " absorb " that child ( transfer necessary information ) .
*/
static void
sec_asn1d_absorb_child(sec_asn1d_state *state)
{
/*
* There is absolutely supposed to be a child there .
*/
PORT_Assert(state->child != NULL);
/*
* Inherit the missing status of our child , and do the ugly
* backing - up if necessary .
*/
state->missing = state->child->missing;
if (state->missing) {
state->found_tag_number = state->child->found_tag_number;
state->found_tag_modifiers = state->child->found_tag_modifiers;
state->endofcontents = state->child->endofcontents;
}
/*
* Add in number of bytes consumed by child .
* ( Only EXPLICIT should have already consumed bytes itself . )
*/
PORT_Assert(state->place == afterExplicit || state->consumed == 0 );
state->consumed += state->child->consumed;
/*
* Subtract from bytes pending ; this only applies to a definite - length
* EXPLICIT field .
*/
if (state->pending) {
PORT_Assert(!state->indefinite);
PORT_Assert(state->place == afterExplicit);
/*
* If we had a definite - length explicit , then what the child
* consumed should be what was left pending .
*/
if (state->pending != state->child->consumed) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return ;
}
state->pending = 0 ;
}
/*
* Indicate that we are done with child .
*/
state->child->consumed = 0 ;
/*
* And move on to final state .
* ( Technically everybody could move to afterEndOfContents except
* for an indefinite - length EXPLICIT ; for simplicity though we assert
* that but let the end - of - contents code do the real determination . )
*/
PORT_Assert(state->place == afterExplicit || (!state->indefinite));
state->place = beforeEndOfContents;
}
static void
sec_asn1d_prepare_for_end_of_contents(sec_asn1d_state *state)
{
PORT_Assert(state->place == beforeEndOfContents);
if (state->indefinite) {
state->place = duringEndOfContents;
state->pending = 2 ;
} else {
state->place = afterEndOfContents;
}
}
static unsigned long
sec_asn1d_parse_end_of_contents(sec_asn1d_state *state,
const char *buf, unsigned long len)
{
unsigned int i;
PORT_Assert(state->pending <= 2 );
PORT_Assert(state->place == duringEndOfContents);
if (len == 0 ) {
state->top->status = needBytes;
return 0 ;
}
if (state->pending < len)
len = state->pending;
for (i = 0 ; i < len; i++) {
if (buf[i] != 0 ) {
/*
* We expect to find only zeros ; if not , just give up .
*/
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return 0 ;
}
}
state->pending -= len;
if (state->pending == 0 ) {
state->place = afterEndOfContents;
/* These end-of-contents octets either terminate a SEQUENCE, a GROUP,
* or a constructed string . The SEQUENCE case is unique in that the
* state parses its own end - of - contents octets and therefore should not
* have its ` endofcontents ` flag set . We identify the SEQUENCE case by
* checking whether the child state ' s template is pointing at a
* template terminator ( see ` sec_asn1d_next_in_sequence ` ) .
*/
if (state->child && state->child->theTemplate->kind == 0 ) {
state->endofcontents = PR_FALSE;
} else {
state->endofcontents = PR_TRUE;
}
}
return len;
}
static void
sec_asn1d_pop_state(sec_asn1d_state *state)
{
#if 0 /* XXX I think this should always be handled explicitly by parent? */
/*
* Account for our child .
*/
if (state->child != NULL) {
state->consumed += state->child->consumed;
if (state->pending) {
PORT_Assert (!state->indefinite);
if (state->child->consumed > state->pending) {
PORT_SetError (SEC_ERROR_BAD_DER);
state->top->status = decodeError;
} else {
state->pending -= state->child->consumed;
}
}
state->child->consumed = 0 ;
}
#endif /* XXX */
/*
* Free our child .
*/
sec_asn1d_free_child(state, PR_FALSE);
/*
* Just make my parent be the current state . It will then clean
* up after me and free me ( or reuse me ) .
*/
state->top->current = state->parent;
}
static sec_asn1d_state *
sec_asn1d_before_choice(sec_asn1d_state *state)
{
sec_asn1d_state *child;
if (state->allocate) {
void *dest;
dest = sec_asn1d_zalloc(state->top->their_pool, state->theTemplate->size);
if ((void *)NULL == dest) {
state->top->status = decodeError;
return (sec_asn1d_state *)NULL;
}
state->dest = (char *)dest + state->theTemplate->offset;
}
char *dest = state->dest ? (char *)state->dest - state->theTemplate->offset : NULL;
child = sec_asn1d_push_state(state->top, state->theTemplate + 1 ,
dest,
PR_FALSE);
if ((sec_asn1d_state *)NULL == child) {
return (sec_asn1d_state *)NULL;
}
sec_asn1d_scrub_state(child);
child = sec_asn1d_init_state_based_on_template(child);
if ((sec_asn1d_state *)NULL == child) {
return (sec_asn1d_state *)NULL;
}
child->optional = PR_TRUE;
state->place = duringChoice;
return child;
}
static sec_asn1d_state *
sec_asn1d_during_choice(sec_asn1d_state *state)
{
sec_asn1d_state *child = state->child;
PORT_Assert((sec_asn1d_state *)NULL != child);
if (child->missing) {
unsigned char child_found_tag_modifiers = 0 ;
unsigned long child_found_tag_number = 0 ;
void *dest;
state->consumed += child->consumed;
if (child->endofcontents) {
/* This choice is probably the first item in a GROUP
* * ( e . g . SET_OF ) that was indefinite - length encoded .
* * We ' re actually at the end of that GROUP .
* * We look up the stack to be sure that we find
* * a state with indefinite length encoding before we
* * find a state ( like a SEQUENCE ) that is definite .
*/
child->place = notInUse;
state->place = afterChoice;
state->endofcontents = PR_TRUE; /* propagate this up */
if (sec_asn1d_parent_allows_EOC(state))
return state;
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return NULL;
}
dest = child->dest ? (char *)child->dest - child->theTemplate->offset : NULL;
child->theTemplate++;
if (0 == child->theTemplate->kind) {
/* Ran out of choices */
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return (sec_asn1d_state *)NULL;
}
child->dest = dest ? (char *)dest + child->theTemplate->offset : NULL;
/* cargo'd from next_in_sequence innards */
if (state->pending) {
PORT_Assert(!state->indefinite);
if (child->consumed > state->pending) {
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return NULL;
}
state->pending -= child->consumed;
if (0 == state->pending) {
/* XXX uh.. not sure if I should have stopped this
* from happening before. */
PORT_Assert(0 );
PORT_SetError(SEC_ERROR_BAD_DER);
state->top->status = decodeError;
return (sec_asn1d_state *)NULL;
}
}
child->consumed = 0 ;
sec_asn1d_scrub_state(child);
/* move it on top again */
state->top->current = child;
child_found_tag_modifiers = child->found_tag_modifiers;
child_found_tag_number = child->found_tag_number;
child = sec_asn1d_init_state_based_on_template(child);
if ((sec_asn1d_state *)NULL == child) {
return (sec_asn1d_state *)NULL;
}
/* copy our findings to the new top */
child->found_tag_modifiers = child_found_tag_modifiers;
child->found_tag_number = child_found_tag_number;
child->optional = PR_TRUE;
child->place = afterIdentifier;
return child;
}
if ((void *)NULL != state->dest) {
/* Store the enum */
int *which = (int *)state->dest;
*which = (int )child->theTemplate->size;
}
child->place = notInUse;
state->place = afterChoice;
return state;
}
static void
sec_asn1d_after_choice(sec_asn1d_state *state)
{
state->consumed += state->child->consumed;
state->child->consumed = 0 ;
state->place = afterEndOfContents;
sec_asn1d_pop_state(state);
}
unsigned long
sec_asn1d_uinteger(SECItem *src)
{
unsigned long value;
int len;
if (src->len > 5 || (src->len > 4 && src->data[0 ] == 0 ))
return 0 ;
value = 0 ;
len = src->len;
while (len) {
value <<= 8 ;
value |= src->data[--len];
}
return value;
}
SECStatus
SEC_ASN1DecodeInteger(SECItem *src, unsigned long *value)
{
unsigned long v;
unsigned int i;
if (src == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (src->len > sizeof (unsigned long )) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (src->data == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (src->data[0 ] & 0 x80)
v = -1 ; /* signed and negative - start with all 1's */
else
v = 0 ;
for (i = 0 ; i < src->len; i++) {
/* shift in next byte */
v <<= 8 ;
v |= src->data[i];
}
*value = v;
return SECSuccess;
}
#ifdef DEBUG_ASN1D_STATES
static void
dump_states(SEC_ASN1DecoderContext *cx)
{
sec_asn1d_state *state;
int bufsize = 256 ;
char kindBuf[bufsize];
for (state = cx->current; state->parent; state = state->parent) {
;
}
for (; state; state = state->child) {
int i;
for (i = 0 ; i < state->depth; i++) {
printf(" " );
}
i = formatKind(state->theTemplate->kind, kindBuf, bufsize);
printf("%s: tmpl kind %s" ,
(state == cx->current) ? "STATE" : "State" ,
kindBuf);
printf(" %s" , (state->place <= notInUse) ? place_names[state->place] : "(undefined)" );
if (!i)
printf(", expect 0x%02lx" ,
state->expect_tag_number | state->expect_tag_modifiers);
printf("%s%s%s %lu\n" ,
state->indefinite ? ", indef" : "" ,
state->missing ? ", miss" : "" ,
state->endofcontents ? ", EOC" : "" ,
state->pending);
}
return ;
}
#endif /* DEBUG_ASN1D_STATES */
SECStatus
SEC_ASN1DecoderUpdate(SEC_ASN1DecoderContext *cx,
const char *buf, unsigned long len)
{
sec_asn1d_state *state = NULL;
unsigned long consumed;
SEC_ASN1EncodingPart what;
if (!cx) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (cx->status == needBytes)
cx->status = keepGoing;
while (cx->status == keepGoing) {
state = cx->current;
what = SEC_ASN1_Contents;
consumed = 0 ;
#ifdef DEBUG_ASN1D_STATES
printf("\nPLACE = %s, next byte = 0x%02x, %p[%lu]\n" ,
(state->place <= notInUse) ? place_names[state->place] : "(undefined)" ,
len ? (unsigned int )((unsigned char *)buf)[consumed] : 0 ,
buf, consumed);
dump_states(cx);
#endif /* DEBUG_ASN1D_STATES */
switch (state->place) {
case beforeIdentifier:
consumed = sec_asn1d_parse_identifier(state, buf, len);
what = SEC_ASN1_Identifier;
break ;
case duringIdentifier:
consumed = sec_asn1d_parse_more_identifier(state, buf, len);
what = SEC_ASN1_Identifier;
break ;
case afterIdentifier:
sec_asn1d_confirm_identifier(state);
break ;
case beforeLength:
consumed = sec_asn1d_parse_length(state, buf, len);
what = SEC_ASN1_Length;
break ;
case duringLength:
consumed = sec_asn1d_parse_more_length(state, buf, len);
what = SEC_ASN1_Length;
break ;
case afterLength:
sec_asn1d_prepare_for_contents(state);
break ;
case beforeBitString:
consumed = sec_asn1d_parse_bit_string(state, buf, len);
break ;
case duringBitString:
consumed = sec_asn1d_parse_more_bit_string(state, buf, len);
break ;
case duringConstructedString:
sec_asn1d_next_substring(state);
break ;
case duringGroup:
sec_asn1d_next_in_group(state);
break ;
case duringLeaf:
consumed = sec_asn1d_parse_leaf(state, buf, len);
break ;
case duringSaveEncoding:
sec_asn1d_reuse_encoding(state);
if (cx->status == decodeError) {
/* recursive call has already popped all states from stack.
* * Bail out quickly .
*/
return SECFailure;
}
if (cx->status == needBytes) {
/* recursive call wanted more data. Fatal. Clean up below. */
PORT_SetError(SEC_ERROR_BAD_DER);
cx->status = decodeError;
}
break ;
case duringSequence:
sec_asn1d_next_in_sequence(state);
break ;
case afterConstructedString:
sec_asn1d_concat_substrings(state);
break ;
case afterExplicit:
case afterImplicit:
case afterInline:
case afterPointer:
sec_asn1d_absorb_child(state);
break ;
case afterGroup:
sec_asn1d_concat_group(state);
break ;
case afterSaveEncoding:
/* SEC_ASN1DecoderUpdate has called itself recursively to
* * decode SAVEd encoded data , and now is done decoding that .
* * Return to the calling copy of SEC_ASN1DecoderUpdate .
*/
return SECSuccess;
case beforeEndOfContents:
sec_asn1d_prepare_for_end_of_contents(state);
break ;
case duringEndOfContents:
consumed = sec_asn1d_parse_end_of_contents(state, buf, len);
what = SEC_ASN1_EndOfContents;
break ;
case afterEndOfContents:
sec_asn1d_pop_state(state);
break ;
case beforeChoice:
state = sec_asn1d_before_choice(state);
break ;
case duringChoice:
state = sec_asn1d_during_choice(state);
break ;
case afterChoice:
sec_asn1d_after_choice(state);
break ;
case notInUse:
default :
/* This is not an error, but rather a plain old BUG! */
PORT_Assert(0 );
PORT_SetError(SEC_ERROR_BAD_DER);
cx->status = decodeError;
break ;
}
if (cx->status == decodeError)
break ;
/* We should not consume more than we have. */
PORT_Assert(consumed <= len);
if (consumed > len) {
PORT_SetError(SEC_ERROR_BAD_DER);
cx->status = decodeError;
break ;
}
/* It might have changed, so we have to update our local copy. */
state = cx->current;
/* If it is NULL, we have popped all the way to the top. */
if (state == NULL) {
PORT_Assert(consumed == 0 );
#if 0 /* XXX I want this here, but it seems that we have situations (like \
* downloading a pkcs7 cert chain from some issuers ) that give us a \
* length which is greater than the entire encoding . So , we cannot \
* have this be an error . \
*/
if (len > 0 ) {
PORT_SetError (SEC_ERROR_BAD_DER);
cx->status = decodeError;
} else
#endif
cx->status = allDone;
break ;
} else if (state->theTemplate->kind == SEC_ASN1_SKIP_REST) {
cx->status = allDone;
break ;
}
if (consumed == 0 )
continue ;
/*
* The following check is specifically looking for an ANY
* that is * not * also an INNER , because we need to save aside
* all bytes in that case - - the contents parts will get
* handled like all other contents , and the end - of - contents
* bytes are added by the concat code , but the outer header
* bytes need to get saved too , so we do them explicitly here .
*/
if (state->underlying_kind == SEC_ASN1_ANY && !cx->filter_only && (what == SEC_ASN1_Identifier || what == SEC_ASN1_Length)) {
sec_asn1d_record_any_header(state, buf, consumed);
}
/*
* We had some number of good , accepted bytes . If the caller
* has registered to see them , pass them along .
*/
if (state->top->filter_proc != NULL) {
int depth;
depth = state->depth;
if (what == SEC_ASN1_EndOfContents && !state->indefinite) {
depth--;
PORT_Assert(depth == sec_asn1d_get_enclosing_construct(state)->depth);
}
(*state->top->filter_proc)(state->top->filter_arg,
buf, consumed, depth, what);
}
state->consumed += consumed;
buf += consumed;
len -= consumed;
}
if (cx->status == decodeError) {
while (state != NULL) {
sec_asn1d_free_child(state, PR_TRUE);
state = state->parent;
}
#ifdef SEC_ASN1D_FREE_ON_ERROR /* \
* XXX This does not work because we can \
* end up leaving behind dangling pointers \
* to stuff that was allocated . In order \
* to make this really work ( which would \
* be a good thing , I think ) , we need to \
* keep track of every place / pointer that \
* was allocated and make sure to NULL it \
* out before we then free back to the mark . \
*/
if (cx->their_pool != NULL) {
PORT_Assert(cx->their_mark != NULL);
PORT_ArenaRelease(cx->their_pool, cx->their_mark);
cx->their_mark = NULL;
}
#endif
return SECFailure;
}
#if 0 /* XXX This is what I want, but cannot have because it seems we \
* have situations ( like when downloading a pkcs7 cert chain from \
* some issuers ) that give us a total length which is greater than \
* the entire encoding . So , we have to allow allDone to have a \
* remaining length greater than zero . I wanted to catch internal \
* bugs with this , noticing when we do not have the right length . \
* Oh well . \
*/
PORT_Assert (len == 0
&& (cx->status == needBytes || cx->status == allDone));
#else
PORT_Assert((len == 0 && cx->status == needBytes) || cx->status == allDone);
#endif
return SECSuccess;
}
SECStatus
SEC_ASN1DecoderFinish(SEC_ASN1DecoderContext *cx)
{
SECStatus rv;
if (!cx || cx->status == needBytes) {
if (0 == PORT_GetError()) {
/* don't clobber a real reason for the failure like bad password
* or invalid algorithm */
PORT_SetError(SEC_ERROR_BAD_DER);
}
rv = SECFailure;
} else {
rv = SECSuccess;
}
/*
* XXX anything else that needs to be finished ?
*/
if (cx) {
PORT_FreeArena(cx->our_pool, PR_TRUE);
}
return rv;
}
SEC_ASN1DecoderContext *
SEC_ASN1DecoderStart(PLArenaPool *their_pool, void *dest,
const SEC_ASN1Template *theTemplate)
{
PLArenaPool *our_pool;
SEC_ASN1DecoderContext *cx;
our_pool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
if (our_pool == NULL)
return NULL;
cx = (SEC_ASN1DecoderContext *)PORT_ArenaZAlloc(our_pool, sizeof (*cx));
if (cx == NULL) {
PORT_FreeArena(our_pool, PR_FALSE);
return NULL;
}
cx->our_pool = our_pool;
if (their_pool != NULL) {
cx->their_pool = their_pool;
#ifdef SEC_ASN1D_FREE_ON_ERROR
cx->their_mark = PORT_ArenaMark(their_pool);
#endif
}
cx->status = needBytes;
if (sec_asn1d_push_state(cx, theTemplate, dest, PR_FALSE) == NULL || sec_asn1d_init_state_based_on_template(cx->current) == NULL) {
/*
* Trouble initializing ( probably due to failed allocations )
* requires that we just give up .
*/
PORT_FreeArena(our_pool, PR_FALSE);
return NULL;
}
return cx;
}
void
SEC_ASN1DecoderSetFilterProc(SEC_ASN1DecoderContext *cx,
SEC_ASN1WriteProc fn, void *arg,
PRBool only)
{
/* check that we are "between" fields here */
PORT_Assert(cx->during_notify);
cx->filter_proc = fn;
cx->filter_arg = arg;
cx->filter_only = only;
}
void
SEC_ASN1DecoderClearFilterProc(SEC_ASN1DecoderContext *cx)
{
/* check that we are "between" fields here */
PORT_Assert(cx->during_notify);
cx->filter_proc = NULL;
cx->filter_arg = NULL;
cx->filter_only = PR_FALSE;
}
void
SEC_ASN1DecoderSetNotifyProc(SEC_ASN1DecoderContext *cx,
SEC_ASN1NotifyProc fn, void *arg)
{
cx->notify_proc = fn;
cx->notify_arg = arg;
}
void
SEC_ASN1DecoderClearNotifyProc(SEC_ASN1DecoderContext *cx)
{
cx->notify_proc = NULL;
cx->notify_arg = NULL; /* not necessary; just being clean */
}
void
SEC_ASN1DecoderSetMaximumElementSize(SEC_ASN1DecoderContext *cx,
unsigned long max_size)
{
cx->max_element_size = max_size;
}
void
SEC_ASN1DecoderAbort(SEC_ASN1DecoderContext *cx, int error)
{
PORT_Assert(cx);
PORT_SetError(error);
cx->status = decodeError;
}
SECStatus
SEC_ASN1Decode(PLArenaPool *poolp, void *dest,
const SEC_ASN1Template *theTemplate,
const char *buf, long len)
{
SEC_ASN1DecoderContext *dcx;
SECStatus urv, frv;
dcx = SEC_ASN1DecoderStart(poolp, dest, theTemplate);
if (dcx == NULL)
return SECFailure;
/* In one-shot mode, there's no possibility of streaming data beyond the
* length of len */
SEC_ASN1DecoderSetMaximumElementSize(dcx, len);
urv = SEC_ASN1DecoderUpdate(dcx, buf, len);
frv = SEC_ASN1DecoderFinish(dcx);
if (urv != SECSuccess)
return urv;
return frv;
}
SECStatus
SEC_ASN1DecodeItem(PLArenaPool *poolp, void *dest,
const SEC_ASN1Template *theTemplate,
const SECItem *src)
{
return SEC_ASN1Decode(poolp, dest, theTemplate,
(const char *)src->data, src->len);
}
#ifdef DEBUG_ASN1D_STATES
void
sec_asn1d_Assert(const char *s, const char *file, PRIntn ln)
{
printf("Assertion failed, \" %s\", file %s, line %d\n" , s, file, ln);
fflush(stdout);
}
#endif
/*
* Generic templates for individual / simple items and pointers to
* and sets of same .
*
* If you need to add a new one , please note the following :
* - For each new basic type you should add * four * templates :
* one plain , one PointerTo , one SequenceOf and one SetOf .
* - If the new type can be constructed ( meaning , it is a
* * string * type according to BER / DER rules ) , then you should
* or - in SEC_ASN1_MAY_STREAM to the type in the basic template .
* See the definition of the OctetString template for an example .
* - It may not be obvious , but these are in * alphabetical *
* order based on the SEC_ASN1_XXX name ; so put new ones in
* the appropriate place .
*/
const SEC_ASN1Template SEC_SequenceOfAnyTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_AnyTemplate }
};
#if 0
const SEC_ASN1Template SEC_PointerToBitStringTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_BitStringTemplate }
};
const SEC_ASN1Template SEC_SequenceOfBitStringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_BitStringTemplate }
};
const SEC_ASN1Template SEC_SetOfBitStringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_BitStringTemplate }
};
const SEC_ASN1Template SEC_PointerToBMPStringTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_BMPStringTemplate }
};
const SEC_ASN1Template SEC_SequenceOfBMPStringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_BMPStringTemplate }
};
const SEC_ASN1Template SEC_SetOfBMPStringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_BMPStringTemplate }
};
const SEC_ASN1Template SEC_PointerToBooleanTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_BooleanTemplate }
};
const SEC_ASN1Template SEC_SequenceOfBooleanTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_BooleanTemplate }
};
const SEC_ASN1Template SEC_SetOfBooleanTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_BooleanTemplate }
};
#endif
const SEC_ASN1Template SEC_EnumeratedTemplate[] = {
{ SEC_ASN1_ENUMERATED, 0 , NULL, sizeof (SECItem) }
};
const SEC_ASN1Template SEC_PointerToEnumeratedTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_EnumeratedTemplate }
};
#if 0
const SEC_ASN1Template SEC_SequenceOfEnumeratedTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_EnumeratedTemplate }
};
#endif
const SEC_ASN1Template SEC_SetOfEnumeratedTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_EnumeratedTemplate }
};
const SEC_ASN1Template SEC_PointerToGeneralizedTimeTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_GeneralizedTimeTemplate }
};
#if 0
const SEC_ASN1Template SEC_SequenceOfGeneralizedTimeTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_GeneralizedTimeTemplate }
};
const SEC_ASN1Template SEC_SetOfGeneralizedTimeTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_GeneralizedTimeTemplate }
};
const SEC_ASN1Template SEC_PointerToIA5StringTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_IA5StringTemplate }
};
const SEC_ASN1Template SEC_SequenceOfIA5StringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_IA5StringTemplate }
};
const SEC_ASN1Template SEC_SetOfIA5StringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_IA5StringTemplate }
};
const SEC_ASN1Template SEC_PointerToIntegerTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_IntegerTemplate }
};
const SEC_ASN1Template SEC_SequenceOfIntegerTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_IntegerTemplate }
};
const SEC_ASN1Template SEC_SetOfIntegerTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_IntegerTemplate }
};
const SEC_ASN1Template SEC_PointerToNullTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_NullTemplate }
};
const SEC_ASN1Template SEC_SequenceOfNullTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_NullTemplate }
};
const SEC_ASN1Template SEC_SetOfNullTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_NullTemplate }
};
const SEC_ASN1Template SEC_PointerToObjectIDTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_ObjectIDTemplate }
};
#endif
const SEC_ASN1Template SEC_SequenceOfObjectIDTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_ObjectIDTemplate }
};
#if 0
const SEC_ASN1Template SEC_SetOfObjectIDTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_ObjectIDTemplate }
};
const SEC_ASN1Template SEC_SequenceOfOctetStringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_OctetStringTemplate }
};
const SEC_ASN1Template SEC_SetOfOctetStringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_OctetStringTemplate }
};
#endif
const SEC_ASN1Template SEC_PrintableStringTemplate[] = {
{ SEC_ASN1_PRINTABLE_STRING | SEC_ASN1_MAY_STREAM, 0 , NULL, sizeof (SECItem) }
};
#if 0
const SEC_ASN1Template SEC_PointerToPrintableStringTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_PrintableStringTemplate }
};
const SEC_ASN1Template SEC_SequenceOfPrintableStringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_PrintableStringTemplate }
};
const SEC_ASN1Template SEC_SetOfPrintableStringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_PrintableStringTemplate }
};
#endif
const SEC_ASN1Template SEC_T61StringTemplate[] = {
{ SEC_ASN1_T61_STRING | SEC_ASN1_MAY_STREAM, 0 , NULL, sizeof (SECItem) }
};
#if 0
const SEC_ASN1Template SEC_PointerToT61StringTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_T61StringTemplate }
};
const SEC_ASN1Template SEC_SequenceOfT61StringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_T61StringTemplate }
};
const SEC_ASN1Template SEC_SetOfT61StringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_T61StringTemplate }
};
#endif
const SEC_ASN1Template SEC_UniversalStringTemplate[] = {
{ SEC_ASN1_UNIVERSAL_STRING | SEC_ASN1_MAY_STREAM, 0 , NULL, sizeof (SECItem) }
};
#if 0
const SEC_ASN1Template SEC_PointerToUniversalStringTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_UniversalStringTemplate }
};
const SEC_ASN1Template SEC_SequenceOfUniversalStringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_UniversalStringTemplate }
};
const SEC_ASN1Template SEC_SetOfUniversalStringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_UniversalStringTemplate }
};
const SEC_ASN1Template SEC_PointerToUTCTimeTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_UTCTimeTemplate }
};
const SEC_ASN1Template SEC_SequenceOfUTCTimeTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_UTCTimeTemplate }
};
const SEC_ASN1Template SEC_SetOfUTCTimeTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_UTCTimeTemplate }
};
const SEC_ASN1Template SEC_PointerToUTF8StringTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_UTF8StringTemplate }
};
const SEC_ASN1Template SEC_SequenceOfUTF8StringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_UTF8StringTemplate }
};
const SEC_ASN1Template SEC_SetOfUTF8StringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_UTF8StringTemplate }
};
#endif
const SEC_ASN1Template SEC_VisibleStringTemplate[] = {
{ SEC_ASN1_VISIBLE_STRING | SEC_ASN1_MAY_STREAM, 0 , NULL, sizeof (SECItem) }
};
#if 0
const SEC_ASN1Template SEC_PointerToVisibleStringTemplate[] = {
{ SEC_ASN1_POINTER, 0 , SEC_VisibleStringTemplate }
};
const SEC_ASN1Template SEC_SequenceOfVisibleStringTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0 , SEC_VisibleStringTemplate }
};
const SEC_ASN1Template SEC_SetOfVisibleStringTemplate[] = {
{ SEC_ASN1_SET_OF, 0 , SEC_VisibleStringTemplate }
};
#endif
/*
* Template for skipping a subitem .
*
* Note that it only makes sense to use this for decoding ( when you want
* to decode something where you are only interested in one or two of
* the fields ) ; you cannot encode a SKIP !
*/
const SEC_ASN1Template SEC_SkipTemplate[] = {
{ SEC_ASN1_SKIP }
};
/* These functions simply return the address of the above-declared templates.
* * This is necessary for Windows DLLs . Sigh .
*/
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_EnumeratedTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PointerToEnumeratedTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SequenceOfAnyTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SequenceOfObjectIDTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SkipTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_UniversalStringTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PrintableStringTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_T61StringTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PointerToGeneralizedTimeTemplate)
Messung V0.5 in Prozent C=94 H=95 G=94
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.138Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-01)
¤
*Eine klare Vorstellung vom Zielzustand
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691