Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36624012
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
182 KB
Subscribers
None
View Options
diff --git a/src/Makefile.am b/src/Makefile.am
index 3adbcad..89331b5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,80 +1,81 @@
# Makefile.am - for the KSBA ASN.1 and X.509 library
# Copyright (C) 2001 g10 Code GmbH
#
# This file is part of KSBA.
#
# KSBA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# KSBA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
## Process this file with automake to produce Makefile.in
asn1_modules = tmttv2.asn cms.asn
EXTRA_DIST = ksba-config.in mkerrors $(asn1_modules)
BUILT_SOURCES = asn1-parse.c errors.c asn1-tables.c
bin_SCRIPTS = ksba-config
include_HEADERS = ksba.h
lib_LTLIBRARIES = libksba.la
noinst_PROGRAMS = asn1-gentables ber-dump
libksba_la_LDFLAGS = -version-info \
@LIBKSBA_LT_CURRENT@:@LIBKSBA_LT_REVISION@:@LIBKSBA_LT_AGE@
libksba_la_INCLUDES = -I$(top_srcdir)/lib
libksba_la_SOURCES = \
ksba.h \
reader.c reader.h \
writer.c writer.h \
asn1-parse.y asn1-parse.h \
asn1-func.c asn1-func2.c asn1-func.h \
ber-help.c ber-help.h \
ber-decoder.c ber-decoder.h \
+ der-encoder.c der-encoder.h \
cert.c cert.h \
cms.c cms.h \
cms-parser.c \
keyinfo.c keyinfo.h \
oid.c dn.c time.c convert.h \
util.c util.h shared.h errors.c \
asn1-tables.c
asn1_gentables_SOURCES = \
asn1-gentables.c \
asn1-parse.y asn1-parse.h \
asn1-func.c asn1-func.h \
oid.c \
util.c util.h
-
+
ber_dump_SOURCES = \
ber-dump.c \
ksba.h \
ber-decoder.c ber-decoder.h \
ber-help.c ber-help.h \
reader.c reader.h \
writer.c writer.h \
asn1-parse.y asn1-parse.h \
asn1-func.c asn1-func.h \
oid.c \
util.c util.h
-
+
asn1-parse.c : asn1-func.h
errors.c : ksba.h mkerrors
$(srcdir)/mkerrors < $(srcdir)/ksba.h > errors.c
asn1-tables.c : asn1-gentables $(asn1_modules)
./asn1-gentables $(asn1_modules) > asn1-tables.c
diff --git a/src/asn1-func.c b/src/asn1-func.c
index 4eba2d1..af9c919 100755
--- a/src/asn1-func.c
+++ b/src/asn1-func.c
@@ -1,1926 +1,1156 @@
/* asn1-func.c - Manage ASN.1 definitions
* Copyright (C) 2000, 2001 Fabio Fiorina
* Copyright (C) 2001 Free Software Foundation, Inc.
*
* This file is part of GNUTLS.
*
* GNUTLS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GNUTLS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "ksba.h"
#include "asn1-func.h"
#include "util.h"
-static int expand_identifier (AsnNode * node, AsnNode root);
-static int type_choice_config (AsnNode node);
-
-static char * /* FIXME: This is error prone */
-_asn1_ltostr (long v, char *str)
-{
- long d, r;
- char temp[20];
- int count, k, start;
-
- if (v < 0)
- {
- str[0] = '-';
- start = 1;
- v = -v;
- }
- else
- start = 0;
-
- count = 0;
- do
- {
- d = v / 10;
- r = v - d * 10;
- temp[start + count] = '0' + (char) r;
- count++;
- v = d;
- }
- while (v);
-
- for (k = 0; k < count; k++)
- str[k + start] = temp[start + count - k - 1];
- str[count + start] = 0;
- return str;
-}
-
-
-
static AsnNode
add_node (node_type_t type)
{
AsnNode punt;
punt = xmalloc (sizeof *punt);
punt->left = NULL;
punt->name = NULL;
punt->type = type;
punt->valuetype = VALTYPE_NULL;
punt->value.v_cstr = NULL;
punt->off = -1;
punt->nhdr = 0;
punt->len = 0;
punt->down = NULL;
punt->right = NULL;
punt->link_next = NULL;
return punt;
}
AsnNode
_ksba_asn_new_node (node_type_t type)
{
return add_node (type);
}
/* Change the value field of the node to the content of buffer value
of size LEN. With VALUE of NULL or LEN of 0 the value field is
deleted */
void
_ksba_asn_set_value (AsnNode node,
enum asn_value_type vtype, const void *value, size_t len)
{
return_if_fail (node);
if (node->valuetype)
{
if (node->valuetype == VALTYPE_CSTR)
xfree (node->value.v_cstr);
else if (node->valuetype == VALTYPE_MEM)
xfree (node->value.v_mem.buf);
node->valuetype = 0;
}
switch (vtype)
{
case VALTYPE_NULL:
break;
case VALTYPE_BOOL:
return_if_fail (len);
node->value.v_bool = !!(const unsigned *)value;
break;
case VALTYPE_CSTR:
node->value.v_cstr = xstrdup (value);
break;
case VALTYPE_MEM:
node->value.v_mem.len = len;
if (len)
{
node->value.v_mem.buf = xmalloc (len);
memcpy (node->value.v_mem.buf, value, len);
}
else
node->value.v_mem.buf = NULL;
break;
case VALTYPE_LONG:
return_if_fail (sizeof (long) == len);
node->value.v_long = *(long *)value;
break;
case VALTYPE_ULONG:
return_if_fail (sizeof (unsigned long) == len);
node->value.v_ulong = *(unsigned long *)value;
break;
default:
return_if_fail (0);
}
node->valuetype = vtype;
}
static void
copy_value (AsnNode d, const AsnNode s)
{
char helpbuf[1];
const void *buf = NULL;
size_t len = 0;
return_if_fail (d != s);
switch (s->valuetype)
{
case VALTYPE_NULL:
break;
case VALTYPE_BOOL:
len = 1;
helpbuf[1] = s->value.v_bool;
buf = helpbuf;
break;
case VALTYPE_CSTR:
buf = s->value.v_cstr;
break;
case VALTYPE_MEM:
len = s->value.v_mem.len;
buf = len? s->value.v_mem.buf : NULL;
break;
case VALTYPE_LONG:
len = sizeof (long);
buf = &s->value.v_long;
break;
case VALTYPE_ULONG:
len = sizeof (unsigned long);
buf = &s->value.v_ulong;
break;
default:
return_if_fail (0);
}
_ksba_asn_set_value (d, s->valuetype, buf, len);
d->off = s->off;
d->nhdr = s->nhdr;
d->len = s->len;
}
static AsnNode
copy_node (const AsnNode s)
{
AsnNode d = add_node (s->type);
if (s->name)
d->name = xstrdup (s->name);
d->flags = s->flags;
copy_value (d, s);
return d;
}
/* Change the name field of the node to NAME.
NAME may be NULL */
void
_ksba_asn_set_name (AsnNode node, const char *name)
{
return_if_fail (node);
if (node->name)
{
xfree (node->name);
node->name = NULL;
}
if (name && *name)
node->name = xstrdup (name);
}
static AsnNode
set_right (AsnNode node, AsnNode right)
{
if (node == NULL)
return node;
node->right = right;
if (right)
right->left = node;
return node;
}
static AsnNode
set_down (AsnNode node, AsnNode down)
{
if (node == NULL)
return node;
node->down = down;
if (down)
down->left = node;
return node;
}
void
_ksba_asn_remove_node (AsnNode node)
{
if (node == NULL)
return;
xfree (node->name);
if (node->valuetype == VALTYPE_CSTR)
xfree (node->value.v_cstr);
else if (node->valuetype == VALTYPE_MEM)
xfree (node->value.v_mem.buf);
xfree (node);
}
/* find the node with the given name. A name part of "?LAST" matches
the last element of a set of */
AsnNode
_ksba_asn_find_node (AsnNode root, const char *name)
{
AsnNode p;
const char *s;
char buf[129];
int i;
if (!name || !name[0])
return NULL;
/* find the first part */
s = name;
for (i=0; *s && *s != '.' && i < DIM(buf)-1; s++)
buf[i++] = *s;
buf[i] = 0;
return_null_if_fail (i < DIM(buf)-1);
for (p = root; p && (!p->name || strcmp (p->name, buf)); p = p->right)
;
/* find other parts */
while (p && *s)
{
assert (*s == '.');
s++; /* skip the dot */
if (!p->down)
return NULL; /* not found */
p = p->down;
for (i=0; *s && *s != '.' && i < DIM(buf)-1; s++)
buf[i++] = *s;
buf[i] = 0;
return_null_if_fail (i < DIM(buf)-1);
if (!*buf)
{
/* a double dot can be used to get over an unnamed sequence
in a set - Actually a hack to workaround a bug. We should
rethink the entire node naming issue */
}
else if (!strcmp (buf, "?LAST"))
{
if (!p)
return NULL;
while (p->right)
p = p->right;
}
else
{
for (; p && (!p->name || strcmp (p->name, buf)); p = p->right)
;
}
}
return p;
}
AsnNode
_asn1_find_left (AsnNode node)
{
if ((node == NULL) || (node->left == NULL) || (node->left->down == node))
return NULL;
return node->left;
}
static AsnNode
find_up (AsnNode node)
{
AsnNode p;
if (node == NULL)
return NULL;
p = node;
while ((p->left != NULL) && (p->left->right == p))
p = p->left;
return p->left;
}
static void
print_value (AsnNode node, FILE *fp)
{
if (!node->valuetype)
return;
fprintf (fp, " vt=%d val=", node->valuetype);
switch (node->valuetype)
{
case VALTYPE_BOOL:
fputs (node->value.v_bool? "True":"False", fp);
break;
case VALTYPE_CSTR:
fputs (node->value.v_cstr, fp);
break;
case VALTYPE_MEM:
{
size_t n;
unsigned char *p;
for (p=node->value.v_mem.buf, n=node->value.v_mem.len; n; n--, p++)
fprintf (fp, "%02X", *p);
}
break;
case VALTYPE_LONG:
fprintf (fp, "%ld", node->value.v_long);
break;
case VALTYPE_ULONG:
fprintf (fp, "%lu", node->value.v_ulong);
break;
default:
return_if_fail (0);
}
}
void
_ksba_asn_node_dump (AsnNode p, FILE *fp)
{
const char *typestr;
switch (p->type)
{
case TYPE_NULL: typestr = "NULL"; break;
case TYPE_CONSTANT: typestr = "CONST"; break;
case TYPE_IDENTIFIER: typestr = "IDENTIFIER"; break;
case TYPE_INTEGER: typestr = "INTEGER"; break;
case TYPE_ENUMERATED: typestr = "ENUMERATED"; break;
case TYPE_UTC_TIME: typestr = "UTCTIME"; break;
case TYPE_GENERALIZED_TIME: typestr = "GENERALIZEDTIME"; break;
case TYPE_BOOLEAN: typestr = "BOOLEAN"; break;
case TYPE_SEQUENCE: typestr = "SEQUENCE"; break;
case TYPE_BIT_STRING: typestr = "BIT_STR"; break;
case TYPE_OCTET_STRING: typestr = "OCT_STR"; break;
case TYPE_TAG: typestr = "TAG"; break;
case TYPE_DEFAULT: typestr = "DEFAULT"; break;
case TYPE_SIZE: typestr = "SIZE"; break;
case TYPE_SEQUENCE_OF: typestr = "SEQ_OF"; break;
case TYPE_OBJECT_ID: typestr = "OBJ_ID"; break;
case TYPE_ANY: typestr = "ANY"; break;
case TYPE_SET: typestr = "SET"; break;
case TYPE_SET_OF: typestr = "SET_OF"; break;
case TYPE_CHOICE: typestr = "CHOICE"; break;
case TYPE_DEFINITIONS: typestr = "DEFINITIONS"; break;
case TYPE_UTF8_STRING: typestr = "UTF8_STRING"; break;
case TYPE_NUMERIC_STRING: typestr = "NUMERIC_STRING"; break;
case TYPE_PRINTABLE_STRING: typestr = "PRINTABLE_STRING"; break;
case TYPE_TELETEX_STRING: typestr = "TELETEX_STRING"; break;
case TYPE_IA5_STRING: typestr = "IA5_STRING"; break;
default: typestr = "ERROR\n"; break;
}
fprintf (fp, "%s", typestr);
if (p->name)
fprintf (fp, " `%s'", p->name);
print_value (p, fp);
fputs (" ", fp);
switch (p->flags.class)
{
case CLASS_UNIVERSAL: fputs ("U", fp); break;
case CLASS_PRIVATE: fputs ("P", fp); break;
case CLASS_APPLICATION: fputs ("A", fp); break;
case CLASS_CONTEXT: fputs ("C", fp); break;
}
if (p->flags.explicit)
fputs (",explicit", fp);
if (p->flags.implicit)
fputs (",implicit", fp);
if (p->flags.is_implicit)
fputs (",is_implicit", fp);
if (p->flags.has_tag)
fputs (",tag", fp);
if (p->flags.has_default)
fputs (",default", fp);
if (p->flags.is_true)
fputs (",true", fp);
if (p->flags.is_false)
fputs (",false", fp);
if (p->flags.has_list)
fputs (",list", fp);
if (p->flags.has_min_max)
fputs (",min_max", fp);
if (p->flags.is_optional)
fputs (",optional", fp);
if (p->flags.one_param)
fputs (",1_param", fp);
if (p->flags.has_size)
fputs (",size", fp);
if (p->flags.has_defined_by)
fputs (",def_by", fp);
if (p->flags.has_imports)
fputs (",imports", fp);
if (p->flags.assignment)
fputs (",assign",fp);
if (p->flags.in_set)
fputs (",in_set",fp);
if (p->flags.in_choice)
fputs (",in_choice",fp);
if (p->flags.in_array)
fputs (",in_array",fp);
if (p->flags.not_used)
fputs (",not_used",fp);
if (p->flags.skip_this)
fputs (",[skip]",fp);
if (p->off != -1 )
fprintf (fp, " %d.%d.%d", p->off, p->nhdr, p->len );
}
void
_ksba_asn_node_dump_all (AsnNode root, FILE *fp)
{
AsnNode p = root;
int indent = 0;
while (p)
{
fprintf (fp, "%*s", indent, "");
_ksba_asn_node_dump (p, fp);
putc ('\n', fp);
if (p->down)
{
p = p->down;
indent += 2;
}
else if (p == root)
{
p = NULL;
break;
}
else if (p->right)
p = p->right;
else
{
while (1)
{
p = find_up (p);
if (p == root)
{
p = NULL;
break;
}
indent -= 2;
if (p->right)
{
p = p->right;
break;
}
}
}
}
}
/**
* ksba_asn_tree_dump:
* @tree: A Parse Tree
* @name: Name of the element or NULL
* @fp: dump to this stream
*
* If the first character of the name is a '<' the expanded version of
* the tree will be printed.
*
* This function is a debugging aid.
**/
void
ksba_asn_tree_dump (KsbaAsnTree tree, const char *name, FILE *fp)
{
AsnNode p, root;
int k, expand=0, indent = 0;
if (!tree || !tree->parse_tree)
return;
if ( name && *name== '<')
{
expand = 1;
name++;
if (!*name)
name = NULL;
}
root = name? _ksba_asn_find_node (tree->parse_tree, name) : tree->parse_tree;
if (!root)
return;
if (expand)
root = _ksba_asn_expand_tree (root, NULL);
p = root;
while (p)
{
for (k = 0; k < indent; k++)
fprintf (fp, " ");
_ksba_asn_node_dump (p, fp);
putc ('\n', fp);
if (p->down)
{
p = p->down;
indent += 2;
}
else if (p == root)
{
p = NULL;
break;
}
else if (p->right)
p = p->right;
else
{
while (1)
{
p = find_up (p);
if (p == root)
{
p = NULL;
break;
}
indent -= 2;
if (p->right)
{
p = p->right;
break;
}
}
}
}
/* FIXME: release the tree if expanded */
}
int
ksba_asn_delete_structure (AsnNode root)
{
AsnNode p, p2, p3;
if (root == NULL)
return KSBA_Element_Not_Found;
p = root;
while (p)
{
if (p->down)
{
p = p->down;
}
else
{ /* no down */
p2 = p->right;
if (p != root)
{
p3 = find_up (p);
set_down (p3, p2);
_ksba_asn_remove_node (p);
p = p3;
}
else
{ /* p==root */
p3 = _asn1_find_left (p);
if (!p3)
{
p3 = find_up (p);
if (p3)
set_down (p3, p2);
else
{
if (p->right)
p->right->left = NULL;
}
}
else
set_right (p3, p2);
_ksba_asn_remove_node (p);
p = NULL;
}
}
}
return 0;
}
-AsnNode
-_asn1_copy_structure3 (AsnNode source_node)
-{
- AsnNode dest_node, p_s, p_d, p_d_prev;
- int len;
- enum { DOWN, UP, RIGHT } move;
-
- if (source_node == NULL)
- return NULL;
-
- dest_node = add_node (source_node->type);
-
- p_s = source_node;
- p_d = dest_node;
-
- move = DOWN;
-
- do
- {
- if (move != UP)
- {
- if (p_s->name)
- _ksba_asn_set_name (p_d, p_s->name);
- if (p_s->valuetype)
- {
- switch (p_s->type)
- {
- case TYPE_OCTET_STRING:
- case TYPE_BIT_STRING:
- case TYPE_INTEGER:
- case TYPE_DEFAULT:
- len = 0 ;/* FIXME_ksba_asn_get_length_der (p_s->value, &len2);*/
- /* _ksba_asn_set_value (p_d, VALTYPE_MEM, p_s->value., len + len2);*/
- break;
- default:
- _ksba_asn_set_value (p_d, VALTYPE_CSTR, p_s->value.v_cstr, 0);
- }
- }
- move = DOWN;
- }
- else
- move = RIGHT;
-
- if (move == DOWN)
- {
- if (p_s->down)
- {
- p_s = p_s->down;
- p_d_prev = p_d;
- p_d = add_node (p_s->type);
- set_down (p_d_prev, p_d);
- }
- else
- move = RIGHT;
- }
-
- if (p_s == source_node)
- break;
-
- if (move == RIGHT)
- {
- if (p_s->right)
- {
- p_s = p_s->right;
- p_d_prev = p_d;
- p_d = add_node (p_s->type);
- set_right (p_d_prev, p_d);
- }
- else
- move = UP;
- }
- if (move == UP)
- {
- p_s = find_up (p_s);
- p_d = find_up (p_d);
- }
- }
- while (p_s != source_node);
-
- return dest_node;
-}
-
-
-AsnNode
-_asn1_copy_structure2 (AsnNode root, char *source_name)
-{
- AsnNode source_node;
-
- source_node = _ksba_asn_find_node (root, source_name);
- return _asn1_copy_structure3 (source_node);
-}
-
-
-int
-ksba_asn1_create_structure (AsnNode root, char *source_name,
- AsnNode * pointer, char *dest_name)
-{
- AsnNode dest_node;
- int res;
- char *end, n[129];
-
- *pointer = NULL;
-
- dest_node = _asn1_copy_structure2 (root, source_name);
-
- if (dest_node == NULL)
- return KSBA_Element_Not_Found;
-
- _ksba_asn_set_name (dest_node, dest_name);
-
- end = strchr (source_name, '.');
- if (end)
- {
- memcpy (n, source_name, end - source_name);
- n[end - source_name] = 0;
- }
- else
- {
- strcpy (n, source_name);
- }
-
- res = expand_identifier (&dest_node, root);
- type_choice_config (dest_node);
-
- *pointer = dest_node;
-
- return res;
-}
-
-
-int
-_asn1_append_sequence_set (AsnNode node)
-{
- AsnNode p, p2;
- char *temp;
- long n;
-
- if (!node || !(node->down))
- return KSBA_General_Error;
-
- p = node->down;
- while (p->type == TYPE_TAG || p->type == TYPE_SIZE)
- p = p->right;
- p2 = _asn1_copy_structure3 (p);
- while (p->right)
- p = p->right;
- set_right (p, p2);
- temp = xmalloc (10);
- if (p->name == NULL)
- strcpy (temp, "?1");
- else
- {
- n = strtol (p->name + 1, NULL, 0);
- n++;
- temp[0] = '?';
- _asn1_ltostr (n, temp + 1);
- }
- _ksba_asn_set_name (p2, temp);
- xfree (temp);
-
- return 0;
-}
-
-
-int
-ksba_asn1_write_value (AsnNode node_root, char *name, unsigned char *value,
- int len)
-{
-
- return -1;
-#if 0
- AsnNode node, p, p2;
- unsigned char *temp, *value_temp, *default_temp, val[4];
- int len2, k, k2, negative;
- unsigned char *root, *n_end;
-
- node = _ksba_asn_find_node (node_root, name);
- if (node == NULL)
- return KSBA_Element_Not_Found;
-
- if (node->flags.is_optional && !value && !len)
- {
- ksba_asn_delete_structure (node);
- return 0;
- }
-
- switch (node->type)
- {
- case TYPE_BOOLEAN:
- if (!strcmp (value, "TRUE") || !strcmp (value, "FALSE"))
- {
- if (node->flags.has_default)
- {
- p = node->down;
- while (p->type == TYPE_DEFAULT) /* XXX ???? */
- p = p->right;
- if (*value == 'T'? p->flags.is_true: p->flags.is_false)
- _ksba_asn_set_value (node, VALTYPE_NULL, NULL, 0);
- else
- _ksba_asn_set_value (node, VALTYPE_BOOL,
- *value == 'T'? "1":"" , 1);
- }
- else
- _ksba_asn_set_value (node, VALTYPE_BOOL? value,
- *value == 'T'? "1":"" , 1);
- }
- else
- return KSBA_BER_Error;
- break;
- case TYPE_INTEGER:
- case TYPE_ENUMERATED:
- if (!len)
- {
- if (isdigit (value[0]))
- {
- value_temp = xmalloc (4);
- convert_integer (value, value_temp, 4, &len);
- }
- else
- { /* is an identifier like v1 */
- if (!(node->flags.has_list))
- return KSBA_BER_Error;
- p = node->down;
- while (p)
- {
- if (p->type == TYPE_CONSTANT)
- {
- if (p->name && !strcmp (p->name, value))
- {
- value_temp = xmalloc (4);
- convert_integer (p->value, value_temp, 4,
- &len);
- break;
- }
- }
- p = p->right;
- }
- if (!p)
- return KSBA_BER_Error;
- }
- }
- else
- {
- value_temp = xmalloc (len);
- memcpy (value_temp, value, len);
- }
-
-
- if (value_temp[0] & 0x80)
- negative = 1;
- else
- negative = 0;
-
- if (negative && node->type == TYPE_ENUMERATED)
- {
- xfree (value_temp);
- return KSBA_BER_Error;
- }
-
- for (k = 0; k < len - 1; k++)
- if (negative && (value_temp[k] != 0xFF))
- break;
- else if (!negative && value_temp[k])
- break;
-
- if ((negative && !(value_temp[k] & 0x80)) ||
- (!negative && (value_temp[k] & 0x80)))
- k--;
-
- _asn1_length_der (len - k, NULL, &len2);
- temp = xmalloc (len - k + len2);
- _asn1_octet_der (value_temp + k, len - k, temp, &len2);
- _ksba_asn_set_value (node, VALTYPE_MEM, temp, len2);
-
- xfree (temp);
-
- if (node->flags.has_default)
- {
- p = node->down;
- while (p->type != TYPE_DEFAULT)
- p = p->right;
- if (p->valuetype == VALTYPE_CSTR && isdigit (*p->value.v_cstr))
- {
- default_temp = xmalloc (4);
- convert_integer (p->value.v_cstr, default_temp, 4, &len2);
- }
- else
- { /* is an identifier like v1 */
- if (!node->flags.has_list)
- return KSBA_BER_Error;
- p2 = node->down;
- while (p2)
- {
- if (p2->type == TYPE_CONSTANT)
- {
- if ((p2->name)
- && p->valuetype == VALTYPE_CSTR
- && !strcmp (p2->name, p->value.v_cstr))
- {
- default_temp = xmalloc (4);
- convert_integer (p2->value.v_cstr, default_temp, 4,
- &len2);
- break;
- }
- }
- p2 = p2->right;
- }
- if (p2 == NULL)
- return KSBA_BER_Error;
- }
-
- if ((len - k) == len2)
- {
- for (k2 = 0; k2 < len2; k2++)
- if (value_temp[k + k2] != default_temp[k2])
- {
- break;
- }
- if (k2 == len2)
- _ksba_asn_set_value (node, VALTYPE_NULL, NULL, 0);
- }
- xfree (default_temp);
- }
- xfree (value_temp);
- break;
- case TYPE_OBJECT_ID:
- for (k = 0; k < strlen (value); k++)
- if ((!isdigit (value[k])) && (value[k] != ' ') && (value[k] != '+'))
- return KSBA_BER_Error;
- _ksba_asn_set_value (node, VALTYPE_CSTR, value, 0);
- break;
- case TYPE_UTC_TIME:
- if (strlen (value) < 11)
- return KSBA_BER_Error;
- for (k = 0; k < 10; k++)
- if (!isdigit (value[k]))
- return KSBA_BER_Error;
- switch (strlen (value))
- {
- case 11:
- if (value[10] != 'Z')
- return KSBA_BER_Error;
- break;
- case 13:
- if ((!isdigit (value[10])) || (!isdigit (value[11])) ||
- (value[12] != 'Z'))
- return KSBA_BER_Error;
- break;
- case 15:
- if ((value[10] != '+') && (value[10] != '-'))
- return KSBA_BER_Error;
- for (k = 11; k < 15; k++)
- if (!isdigit (value[k]))
- return KSBA_BER_Error;
- break;
- case 17:
- if ((!isdigit (value[10])) || (!isdigit (value[11])))
- return KSBA_BER_Error;
- if ((value[12] != '+') && (value[12] != '-'))
- return KSBA_BER_Error;
- for (k = 13; k < 17; k++)
- if (!isdigit (value[k]))
- return KSBA_BER_Error;
- break;
- default:
- return KSBA_Value_Not_Found;
- }
- _ksba_asn_set_value (node, VALTYPE_CSTR, value, 0);
- break;
- case TYPE_GENERALIZED_TIME:
- if (value)
- _ksba_asn_set_value (node, VALTYPE_CSTR, value, 0);
- break;
- case TYPE_OCTET_STRING:
- _asn1_length_der (len, NULL, &len2);
- temp = xmalloc (len + len2);
- _asn1_octet_der (value, len, temp, &len2);
- _ksba_asn_set_value (node, VALTYPE_MEM, temp, len2);
- xfree (temp);
- break;
- case TYPE_BIT_STRING:
- _asn1_length_der ((len >> 3) + 2, NULL, &len2);
- temp = xmalloc ((len >> 3) + 2 + len2);
- _asn1_bit_der (value, len, temp, &len2);
- _ksba_asn_set_value (node, VALTYPE_MEM, temp, len2);
- xfree (temp);
- break;
- case TYPE_CHOICE:
- p = node->down;
- while (p)
- {
- if (!strcmp (p->name, value))
- {
- p2 = node->down;
- while (p2)
- {
- if (p2 != p)
- {
- ksba_asn_delete_structure (p2);
- p2 = node->down;
- }
- else
- p2 = p2->right;
- }
- break;
- }
- p = p->right;
- }
- if (!p)
- return KSBA_Element_Not_Found;
- break;
- case TYPE_ANY:
- _asn1_length_der (len, NULL, &len2);
- temp = xmalloc (len + len2);
- _asn1_octet_der (value, len, temp, &len2);
- _ksba_asn_set_value (node, VALTYPE_MEM, temp, len2);
- xfree (temp);
- break;
- case TYPE_SEQUENCE_OF:
- case TYPE_SET_OF:
- if (strcmp (value, "NEW"))
- return KSBA_BER_Error;
- _asn1_append_sequence_set (node);
- break;
- default:
- return KSBA_Element_Not_Found;
- break;
- }
-
- return 0;
-#endif
-}
-
-#define PUT_VALUE( ptr, ptr_size, data, data_size) \
- if (ptr_size < data_size) { \
- return KSBA_Out_Of_Core; \
- } else { \
- memcpy( ptr, data, data_size); \
- *len = data_size; \
- }
-
-#define PUT_STR_VALUE( ptr, ptr_size, data) \
- if (ptr_size <= strlen(data)) { \
- return KSBA_Out_Of_Core; \
- } else { \
- strcpy( ptr, data); \
- *len = strlen(ptr)+1; \
- }
-
-#define ADD_STR_VALUE( ptr, ptr_size, data) \
- if (ptr_size <= strlen(data)+strlen(ptr)) { \
- return KSBA_Out_Of_Core; \
- } else { \
- strcat( ptr, data); \
- *len = strlen(ptr)+1; \
- }
-
-
-int
-ksba_asn_read_value (AsnNode root, const char *name,
- unsigned char *value, int *len)
-{
- return -1;
-#if 0
-
- AsnNode node, p;
- int len2, len3;
- int value_size = *len;
-
- node = _ksba_asn_find_node (root, name);
- if (node == NULL)
- return KSBA_Element_Not_Found;
-
- if (node->type != TYPE_NULL
- && node->type != TYPE_CHOICE
- && !node->flags.has_default
- && !node->flags.assignment
- && !node->valuetype)
- return KSBA_Value_Not_Found;
-
- switch (node->type)
- {
- case TYPE_NULL:
- PUT_STR_VALUE (value, value_size, "NULL");
- break;
- case TYPE_BOOLEAN:
- if (!node->valuetype && node->flags.has_default)
- {
- p = node->down;
- while (p->type != TYPE_DEFAULT)
- p = p->right;
- assert (p); /* there should be a node of type default below it */
- if (p->flags.is_true)
- {
- PUT_STR_VALUE (value, value_size, "TRUE");
- }
- else
- {
- PUT_STR_VALUE (value, value_size, "FALSE");
- }
- }
- else if (node->valuetype == VALTYPE_CSTR
- && *node->value.v_cstr == 'T')
- {
- PUT_STR_VALUE (value, value_size, "TRUE");
- }
- else
- {
- PUT_STR_VALUE (value, value_size, "FALSE");
- }
- break;
- case TYPE_INTEGER:
- case TYPE_ENUMERATED:
- if (!node->valuetype && node->flags.has_default)
- {
- p = node->down;
- while (p->type != TYPE_DEFAULT)
- p = p->right;
- assert (p); /* there should be a node of type default below it */
- if (convert_integer (p->value, value, value_size, len) != 0)
- return KSBA_Out_Of_Core;
- }
- else if (_asn1_get_octet_der (node->value, &len2, value, value_size, len)
- != 0)
- return KSBA_Out_Of_Core;
- break;
- case TYPE_OBJECT_ID:
- if (node->flags.assignment)
- {
- strcpy (value, "");
- p = node->down;
- while (p)
- {
- if (p->type == TYPE_CONSTANT)
- {
- ADD_STR_VALUE (value, value_size, p->value);
- ADD_STR_VALUE (value, value_size, " ");
- }
- p = p->right;
- }
- }
- else
- {
- PUT_STR_VALUE (value, value_size, node->value);
- }
- break;
- case TYPE_UTC_TIME:
- case TYPE_GENERALIZED_TIME:
- PUT_STR_VALUE (value, value_size, node->value);
- break;
- case TYPE_OCTET_STRING:
- if (_asn1_get_octet_der (node->value, &len2, value, value_size, len) !=
- 0)
- return KSBA_Out_Of_Core;
- break;
- case TYPE_BIT_STRING:
- if (_asn1_get_bit_der (node->value, &len2, value, value_size, len) !=
- 0)
- return KSBA_Out_Of_Core;
- break;
- case TYPE_CHOICE:
- PUT_STR_VALUE (value, value_size, node->down->name);
- break;
- case TYPE_ANY:
- len2 = _ksba_asn_get_length_der (node->value, &len3);
- PUT_VALUE (value, value_size, node->value + len3, len2);
- break;
- default:
- return KSBA_Element_Not_Found;
- break;
- }
- return 0;
-#endif
-}
-
-
-
/* check that all identifiers referenced in the tree are available */
int
_ksba_asn_check_identifier (AsnNode node)
{
AsnNode p, p2;
char name2[129];
if (!node)
return KSBA_Element_Not_Found;
for (p = node; p; p = _ksba_asn_walk_tree (node, p))
{
if (p->type == TYPE_IDENTIFIER && p->valuetype == VALTYPE_CSTR)
{
strcpy (name2, node->name); /* FIXME: check overflow */
strcat (name2, ".");
strcat (name2, p->value.v_cstr);
p2 = _ksba_asn_find_node (node, name2);
if (!p2)
{
fprintf (stderr,"reference to `%s' not found\n", name2);
return KSBA_Identifier_Not_Found;
}
/* fprintf (stdout,"found reference for `%s' (", name2); */
/* print_node (p2, stdout); */
/* fputs (")\n", stdout); */
}
else if (p->type == TYPE_OBJECT_ID && p->flags.assignment)
{ /* an object ID in an assignment */
p2 = p->down;
if (p2 && (p2->type == TYPE_CONSTANT))
{
if (p2->valuetype == VALTYPE_CSTR && !isdigit (p2->value.v_cstr[0]))
{ /* the first constand below is a reference */
strcpy (name2, node->name); /* FIXME: check overflow */
strcat (name2, ".");
strcat (name2, p2->value.v_cstr);
p2 = _ksba_asn_find_node (node, name2);
if (!p2)
{
fprintf (stderr,"object id reference `%s' not found\n",
name2);
return KSBA_Identifier_Not_Found;
}
else if ( p2->type != TYPE_OBJECT_ID
|| !p2->flags.assignment )
{
fprintf (stderr,"`%s' is not an object id\n", name2);
return KSBA_Identifier_Not_Found;
}
/* fprintf (stdout,"found objid reference for `%s' (", name2); */
/* print_node (p2, stdout); */
/* fputs (")\n", stdout); */
}
}
}
}
return 0;
}
/* Get the next node until root is reached in which case NULL is
returned */
AsnNode
_ksba_asn_walk_tree (AsnNode root, AsnNode node)
{
if (!node)
;
else if (node->down)
node = node->down;
else
{
if (node == root)
node = NULL;
else if (node->right)
node = node->right;
else
{
for (;;)
{
node = find_up (node);
if (node == root)
{
node = NULL;
break;
}
if (node->right)
{
node = node->right;
break;
}
}
}
}
return node;
}
AsnNode
_ksba_asn_walk_tree_up_right (AsnNode root, AsnNode node)
{
if (node)
{
if (node == root)
node = NULL;
else
{
for (;;)
{
node = find_up (node);
if (node == root)
{
node = NULL;
break;
}
if (node->right)
{
node = node->right;
break;
}
}
}
}
return node;
}
/* walk over the tree and change the value type of all integer types
from string to long. */
int
_ksba_asn_change_integer_value (AsnNode node)
{
AsnNode p;
if (node == NULL)
return KSBA_Element_Not_Found;
for (p = node; p; p = _ksba_asn_walk_tree (node, p))
{
if (p->type == TYPE_INTEGER && p->flags.assignment)
{
if (p->valuetype == VALTYPE_CSTR)
{
long val = strtol (p->value.v_cstr, NULL, 10);
_ksba_asn_set_value (p, VALTYPE_LONG, &val, sizeof(val));
}
}
}
return 0;
}
-int
-_ksba_asn_delete_not_used (AsnNode node)
-{
- AsnNode p, p2;
-
- if (node == NULL)
- return KSBA_Element_Not_Found;
-
- for (p = node; p; p = _ksba_asn_walk_tree (node, p) )
- {
- if (p->flags.not_used)
- {
- p2 = NULL;
- if (p != node)
- {
- p2 = _asn1_find_left (p);
- if (!p2)
- p2 = find_up (p);
- }
- ksba_asn_delete_structure (p);
- p = p2;
- }
- }
-
- return 0;
-}
-
-
-
-static int
-expand_identifier (AsnNode * node, AsnNode root)
-{
- if (node == NULL)
- return KSBA_Element_Not_Found;
-#warning fix this
- return 0;
-#if 0
- AsnNode p, p2, p3;
- char name2[129];
- for (p = node; p; p = _ksba_asn_walk_tree (node, p))
- {
- if (p->type == TYPE_IDENTIFIER && p->valuetype == VALTYPE_CSTR)
- {
- strcpy (name2, root->name);
- strcat (name2, ".");
- strcat (name2, p->value.v_cstr);
- p2 = _asn1_copy_structure2 (root, name2);
- assert (p2);
- if (p2 == NULL)
- return KSBA_Identifier_Not_Found;
- _ksba_asn_set_name (p2, p->name);
- p2->right = p->right;
- p2->left = p->left;
- if (p->right)
- p->right->left = p2;
- p3 = p->down;
- if (p3)
- {
- while (p3->right)
- p3 = p3->right;
- set_right (p3, p2->down);
- set_down (p2, p->down);
- }
-
- p3 = _asn1_find_left (p);
- if (p3)
- set_right (p3, p2);
- else
- {
- p3 = find_up (p);
- if (p3)
- set_down (p3, p2);
- else
- {
- p2->left = NULL;
- }
- }
-
- if (p->flags.has_size)
- p2->flags.has_size = 1;
- if (p->flags.has_tag)
- p2->flags.has_tag = 1;
- if (p->flags.is_optional)
- p2->flags.is_optional = 1;
- if (p->flags.has_default)
- p2->flags.has_default = 1;
- if (p->flags.in_set)
- p2->flags.in_set = 1;;
- if (p->flags.in_choice)
- p2->flags.in_choice = 1;;
- if (p->flags.in_array)
- p2->flags.in_array = 1;;
- if (p->flags.not_used)
- p2->flags.not_used = 1;
-
- if (p == *node)
- *node = p2;
- _ksba_asn_remove_node (p);
- p = p2;
- }
- }
-#endif
-}
-
-
-
-static int
-type_choice_config (AsnNode node)
-{
- AsnNode p, p2, p3, p4;
-
- if (!node)
- return KSBA_Element_Not_Found;
-
- restart:
- for (p = node; p; p = _ksba_asn_walk_tree (node, p))
- {
- if (p->type == TYPE_CHOICE && p->flags.has_tag)
- {
- for (p2 = p->down; p2; p2 = p2->right)
- {
- if (p2->type != TYPE_TAG)
- {
- p2->flags.has_tag = 1;
- for (p3 = _asn1_find_left (p2);
- p3; p3 = _asn1_find_left (p3))
- {
- if (p3->type == TYPE_TAG)
- {
- p4 = add_node (TYPE_TAG);
- p4->flags = p3->flags;
- copy_value (p4, p3);
- set_right (p4, p2->down);
- set_down (p2, p4);
- }
- }
- }
- }
-
- p->flags.has_tag = 0;
- p2 = p->down;
- while (p2)
- {
- p3 = p2->right;
- if (p2->type == TYPE_TAG)
- ksba_asn_delete_structure (p2);
- p2 = p3;
- }
- goto restart;
- }
- }
-
- return 0;
-}
-
/* Expand all object ID constants */
int
_ksba_asn_expand_object_id (AsnNode node)
{
AsnNode p, p2, p3, p4, p5;
char name_root[129], name2[129*2+1];
/* FIXME: Make a cleaner implementation */
if (!node)
return KSBA_Element_Not_Found;
if (!node->name)
return KSBA_Invalid_Value;
if (strlen(node->name) >= DIM(name_root)-1)
return KSBA_General_Error;
strcpy (name_root, node->name);
restart:
for (p = node; p; p = _ksba_asn_walk_tree (node, p))
{
if (p->type == TYPE_OBJECT_ID && p->flags.assignment)
{
p2 = p->down;
if (p2 && p2->type == TYPE_CONSTANT)
{
if (p2->valuetype == VALTYPE_CSTR
&& !isdigit (p2->value.v_cstr[0]))
{
if (strlen(p2->value.v_cstr)+1+strlen(name2) >= DIM(name2)-1)
return KSBA_General_Error;
strcpy (name2, name_root);
strcat (name2, ".");
strcat (name2, p2->value.v_cstr);
p3 = _ksba_asn_find_node (node, name2);
if (!p3 || p3->type != TYPE_OBJECT_ID ||
!p3->flags.assignment)
return KSBA_Element_Not_Found;
set_down (p, p2->right);
_ksba_asn_remove_node (p2);
p2 = p;
p4 = p3->down;
while (p4)
{
if (p4->type == TYPE_CONSTANT)
{
p5 = add_node (TYPE_CONSTANT);
_ksba_asn_set_name (p5, p4->name);
_ksba_asn_set_value (p5, VALTYPE_CSTR,
p4->value.v_cstr, 0);
if (p2 == p)
{
set_right (p5, p->down);
set_down (p, p5);
}
else
{
set_right (p5, p2->right);
set_right (p2, p5);
}
p2 = p5;
}
p4 = p4->right;
}
goto restart; /* the most simple way to get it right ;-) */
}
}
}
}
return 0;
}
/* Walk the parse tree and set the default tag where appropriate. The
node must be of type DEFINITIONS */
void
_ksba_asn_set_default_tag (AsnNode node)
{
AsnNode p;
return_if_fail (node && node->type == TYPE_DEFINITIONS);
for (p = node; p; p = _ksba_asn_walk_tree (node, p))
{
if ( p->type == TYPE_TAG
&& !p->flags.explicit && !p->flags.implicit)
{
if (node->flags.explicit)
p->flags.explicit = 1;
else
p->flags.implicit = 1;
}
}
/* now mark the nodes which are implicit */
for (p = node; p; p = _ksba_asn_walk_tree (node, p))
{
if ( p->type == TYPE_TAG && p->flags.implicit && p->down)
{
if (p->down->type == TYPE_CHOICE)
; /* a CHOICE is per se implicit */
else if (p->down->type != TYPE_TAG)
p->down->flags.is_implicit = 1;
}
}
}
/* Walk the tree and set the is_set and not_used flags for all nodes below
a node of type SET. */
void
_ksba_asn_type_set_config (AsnNode node)
{
AsnNode p, p2;
return_if_fail (node && node->type == TYPE_DEFINITIONS);
for (p = node; p; p = _ksba_asn_walk_tree (node, p))
{
if (p->type == TYPE_SET)
{
for (p2 = p->down; p2; p2 = p2->right)
{
if (p2->type != TYPE_TAG)
{
p2->flags.in_set = 1;
p2->flags.not_used = 1;
}
}
}
else if (p->type == TYPE_CHOICE)
{
for (p2 = p->down; p2; p2 = p2->right)
{
p2->flags.in_choice = 1;
}
}
else if (p->type == TYPE_SEQUENCE_OF || p->type == TYPE_SET_OF)
{
for (p2 = p->down; p2; p2 = p2->right)
p2->flags.in_array = 1;
}
}
}
/* Create a copy the tree at SRC_ROOT. s is a helper which should be
set to SRC_ROOT by the caller */
static AsnNode
copy_tree (AsnNode src_root, AsnNode s)
{
AsnNode first=NULL, dprev=NULL, d, down, tmp;
for (; s; s=s->right )
{
down = s->down;
d = copy_node (s);
if (!first)
first = d;
else
{
dprev->right = d;
d->left = dprev;
}
dprev = d;
if (down)
{
tmp = copy_tree (src_root, down);
if (d->down && tmp)
{ /* Need to merge it with the existing down */
AsnNode x;
for (x=d->down; x->right; x = x->right)
;
x->right = tmp;
tmp->left = x;
}
else
{
d->down = tmp;
if (d->down)
d->down->left = d;
}
}
}
return first;
}
static AsnNode
resolve_identifier (AsnNode root, AsnNode node, int nestlevel)
{
char *buf;
AsnNode n;
if (nestlevel > 20)
return NULL;
return_null_if_fail (root);
return_null_if_fail (node->valuetype == VALTYPE_CSTR);
buf = alloca (strlen(root->name)+strlen(node->value.v_cstr)+2);
return_null_if_fail (buf);
strcpy (stpcpy (stpcpy (buf, root->name), "."), node->value.v_cstr);
n = _ksba_asn_find_node (root, buf);
/* we do just a simple indirection */
if (n && n->type == TYPE_IDENTIFIER)
n = resolve_identifier (root, n, nestlevel+1);
return n;
}
static AsnNode
do_expand_tree (AsnNode src_root, AsnNode s, int depth)
{
AsnNode first=NULL, dprev=NULL, d, down, tmp;
/* On the very first level we do not follow the right pointer so that
we can break out a valid subtree. */
for (; s; s=depth?s->right:NULL )
{
down = s->down;
if (s->type == TYPE_IDENTIFIER)
{
AsnNode s2, *dp;
d = resolve_identifier (src_root, s, 0);
if (!d)
{
fprintf (stderr, "RESOLVING IDENTIFIER FAILED\n");
continue;
}
down = d->down;
d = copy_node (d);
if (s->flags.is_optional)
d->flags.is_optional = 1;
if (s->flags.in_choice)
d->flags.in_choice = 1;
if (s->flags.in_array)
d->flags.in_array = 1;
if (s->flags.is_implicit)
d->flags.is_implicit = 1;
/* we don't want the resolved name - change it back */
_ksba_asn_set_name (d, s->name);
/* copy the default and tag attributes */
tmp = NULL;
dp = &tmp;
for (s2=s->down; s2; s2=s2->right)
{
AsnNode x;
x = copy_node (s2);
x->left = *dp? *dp : d;
*dp = x;
dp = &(*dp)->right;
if (x->type == TYPE_TAG)
d->flags.has_tag =1;
else if (x->type == TYPE_DEFAULT)
d->flags.has_default =1;
}
d->down = tmp;
}
else
d = copy_node (s);
if (!first)
first = d;
else
{
dprev->right = d;
d->left = dprev;
}
dprev = d;
if (down)
{
if (depth >= 1000)
{
fprintf (stderr, "ASN.1 TREE TOO TALL!\n");
tmp = NULL;
}
else
tmp = do_expand_tree (src_root, down, depth+1);
if (d->down && tmp)
{ /* Need to merge it with the existing down */
AsnNode x;
for (x=d->down; x->right; x = x->right)
;
x->right = tmp;
tmp->left = x;
}
else
{
d->down = tmp;
if (d->down)
d->down->left = d;
}
}
}
return first;
}
/* Expand the syntax tree so that all references are resolved and we
are able to store values right in the tree (except for set/sequence
of). This expanded tree is also an requirement for doing the DER
decoding as the resolving of identifiers leads to a lot of
problems. We use more memory of course, but this is negligible
because the entire code wioll be simpler and faster */
AsnNode
_ksba_asn_expand_tree (AsnNode parse_tree, const char *name)
{
AsnNode root;
root = name? _ksba_asn_find_node (parse_tree, name) : parse_tree;
return do_expand_tree (parse_tree, root, 0);
}
/* Insert a copy of the entire tree at NODE as the sibling of itself
and return the copy */
AsnNode
_ksba_asn_insert_copy (AsnNode node)
{
AsnNode n;
n = copy_tree (node, node);
if (!n)
return NULL; /* out of core */
return_null_if_fail (n->right == node->right);
node->right = n;
n->left = node;
return n;
}
/* Locate a type value sequence like
SEQUENCE {
type OBJECT IDENTIFIER
value ANY
}
below root and return the 'value' node. OIDBUF should contain the
DER encoding of an OID value. idx is the number of OIDs to skip;
this can be used to enumerate structures with the same OID */
AsnNode
_ksba_asn_find_type_value (const unsigned char *image, AsnNode root, int idx,
const void *oidbuf, size_t oidlen)
{
AsnNode n, noid;
if (!image || !root)
return NULL;
for (n = root; n; n = _ksba_asn_walk_tree (root, n) )
{
if ( n->type == TYPE_SEQUENCE
&& n->down && n->down->type == TYPE_OBJECT_ID)
{
noid = n->down;
if (noid->off != -1 && noid->len == oidlen
&& !memcmp (image + noid->off + noid->nhdr, oidbuf, oidlen)
&& noid->right)
{
if ( !idx-- )
return noid->right;
}
}
}
return NULL;
}
diff --git a/src/ber-decoder.c b/src/ber-decoder.c
index 4774a7d..22b8f42 100644
--- a/src/ber-decoder.c
+++ b/src/ber-decoder.c
@@ -1,1182 +1,1183 @@
/* ber-decoder.c - Basic Encoding Rules Decoder
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "ksba.h"
#include "asn1-func.h"
#include "ber-decoder.h"
#include "ber-help.h"
struct decoder_state_item_s {
AsnNode node;
int went_up;
int in_seq_of;
int in_any; /* actually in a constructed any */
int again;
int next_tag;
int length; /* length of the value */
int ndef_length; /* the length is of indefinite length */
int nread; /* number of value bytes processed */
};
typedef struct decoder_state_item_s DECODER_STATE_ITEM;
struct decoder_state_s {
DECODER_STATE_ITEM cur; /* current state */
int stacksize;
int idx;
DECODER_STATE_ITEM stack[1];
};
typedef struct decoder_state_s *DECODER_STATE;
struct ber_decoder_s {
AsnNode module; /* the ASN.1 structure */
KsbaReader reader;
const char *last_errdesc; /* string with the error description */
int non_der; /* set if the encoding is not DER conform */
AsnNode root; /* of the expanded parse tree */
DECODER_STATE ds;
int bypass;
int honor_module_end;
int debug;
int use_image;
struct {
unsigned char *buf;
size_t used;
size_t length;
} image;
struct {
int primitive; /* current value is a primitive one */
int length; /* length of the primitive one */
int nhdr; /* length of the header */
int tag;
int is_endtag;
AsnNode node; /* NULL or matching node */
} val;
};
static DECODER_STATE
new_decoder_state (void)
{
DECODER_STATE ds;
ds = xmalloc (sizeof (*ds) + 99*sizeof(DECODER_STATE_ITEM));
ds->stacksize = 100;
ds->idx = 0;
ds->cur.node = NULL;
ds->cur.in_seq_of = 0;
ds->cur.again = 0;
ds->cur.next_tag = 0;
ds->cur.went_up = 0;
ds->cur.length = 0;
ds->cur.ndef_length = 1;
ds->cur.nread = 0;
return ds;
}
static void
release_decoder_state (DECODER_STATE ds)
{
xfree (ds);
}
static void
dump_decoder_state (DECODER_STATE ds)
{
int i;
for (i=0; i < ds->idx; i++)
{
fprintf (stdout," ds stack[%d] (", i);
if (ds->stack[i].node)
_ksba_asn_node_dump (ds->stack[i].node, stdout);
else
printf ("Null");
fprintf (stdout,") %s%d (%d)%s\n",
ds->stack[i].ndef_length? "ndef ":"",
ds->stack[i].length,
ds->stack[i].nread,
ds->stack[i].in_seq_of? " in_seq_of":"");
}
}
/* Push ITEM onto the stack */
static void
push_decoder_state (DECODER_STATE ds)
{
if (ds->idx >= ds->stacksize)
{
fprintf (stderr, "ERROR: decoder stack overflow!\n");
abort ();
}
ds->stack[ds->idx++] = ds->cur;
}
static void
pop_decoder_state (DECODER_STATE ds)
{
if (!ds->idx)
{
fprintf (stderr, "ERROR: decoder stack underflow!\n");
abort ();
}
ds->cur = ds->stack[--ds->idx];
}
static int
set_error (BerDecoder d, AsnNode node, const char *text)
{
fprintf (stderr,"ber-decoder: node `%s': %s\n",
node? node->name:"?", text);
d->last_errdesc = text;
return KSBA_BER_Error;
}
static int
eof_or_error (BerDecoder d, int premature)
{
if (ksba_reader_error (d->reader))
{
set_error (d, NULL, "read error");
return KSBA_Read_Error;
}
if (premature)
return set_error (d, NULL, "premature EOF");
return -1;
}
static int
is_primitive_type (node_type_t type)
{
switch (type)
{
case TYPE_BOOLEAN:
case TYPE_INTEGER:
case TYPE_BIT_STRING:
case TYPE_OCTET_STRING:
case TYPE_NULL:
case TYPE_OBJECT_ID:
case TYPE_OBJECT_DESCRIPTOR:
case TYPE_REAL:
case TYPE_ENUMERATED:
case TYPE_UTF8_STRING:
case TYPE_REALTIVE_OID:
case TYPE_NUMERIC_STRING:
case TYPE_PRINTABLE_STRING:
case TYPE_TELETEX_STRING:
case TYPE_VIDEOTEX_STRING:
case TYPE_IA5_STRING:
case TYPE_UTC_TIME:
case TYPE_GENERALIZED_TIME:
case TYPE_GRAPHIC_STRING:
case TYPE_VISIBLE_STRING:
case TYPE_GENERAL_STRING:
case TYPE_UNIVERSAL_STRING:
case TYPE_CHARACTER_STRING:
case TYPE_BMP_STRING:
return 1;
default:
return 0;
}
}
static const char *
universal_tag_name (unsigned long no)
{
static const char *names[31] = {
"[End Tag]",
"BOOLEAN",
"INTEGER",
"BIT STRING",
"OCTECT STRING",
"NULL",
"OBJECT IDENTIFIER",
"ObjectDescriptor",
"EXTERNAL",
"REAL",
"ENUMERATED",
"EMBEDDED PDV",
"UTF8String",
"RELATIVE-OID",
"[UNIVERSAL 14]",
"[UNIVERSAL 15]",
"SEQUENCE",
"SET",
"NumericString",
"PrintableString",
"TeletexString",
"VideotexString",
"IA5String",
"UTCTime",
"GeneralizedTime",
"GraphicString",
"VisibleString",
"GeneralString",
"UniversalString",
"CHARACTER STRING",
"BMPString"
};
return no < DIM(names)? names[no]:NULL;
}
static void
dump_tlv (const struct tag_info *ti, FILE *fp)
{
const char *tagname = NULL;
if (ti->class == CLASS_UNIVERSAL)
tagname = universal_tag_name (ti->tag);
if (tagname)
fputs (tagname, fp);
else
fprintf (fp, "[%s %lu]",
ti->class == CLASS_UNIVERSAL? "UNIVERSAL" :
ti->class == CLASS_APPLICATION? "APPLICATION" :
ti->class == CLASS_CONTEXT? "CONTEXT-SPECIFIC" : "PRIVATE",
ti->tag);
fprintf (fp, " %c hdr=%u len=", ti->is_constructed? 'c':'p', ti->nhdr);
if (ti->ndef)
fputs ("ndef", fp);
else
fprintf (fp, "%lu", ti->length);
}
static void
clear_help_flags (AsnNode node)
{
AsnNode p;
for (p=node; p; p = _ksba_asn_walk_tree (node, p))
{
if (p->type == TYPE_TAG)
{
p->flags.tag_seen = 0;
}
p->flags.skip_this = 0;
}
}
static void
prepare_copied_tree (AsnNode node)
{
AsnNode p;
clear_help_flags (node);
for (p=node; p; p = _ksba_asn_walk_tree (node, p))
p->off = -1;
}
static void
fixup_type_any (AsnNode node)
{
AsnNode p;
for (p=node; p; p = _ksba_asn_walk_tree (node, p))
{
if (p->type == TYPE_ANY && p->off != -1)
p->type = p->actual_type;
}
}
BerDecoder
_ksba_ber_decoder_new (void)
{
BerDecoder d;
d = xtrycalloc (1, sizeof *d);
if (!d)
return NULL;
return d;
}
void
_ksba_ber_decoder_release (BerDecoder d)
{
xfree (d);
}
/**
* _ksba_ber_decoder_set_module:
* @d: Decoder object
* @module: ASN.1 Parse tree
*
* Initialize the decoder with the ASN.1 module. Note, that this is a
* shallow copy of the module. FIXME: What about ref-counting of
* AsnNodes?
*
* Return value: 0 on success or an error code
**/
KsbaError
_ksba_ber_decoder_set_module (BerDecoder d, KsbaAsnTree module)
{
if (!d || !module)
return KSBA_Invalid_Value;
if (d->module)
return KSBA_Conflict; /* module already set */
d->module = module->parse_tree;
return 0;
}
KsbaError
_ksba_ber_decoder_set_reader (BerDecoder d, KsbaReader r)
{
if (!d || !r)
return KSBA_Invalid_Value;
if (d->reader)
return KSBA_Conflict; /* reader already set */
d->reader = r;
return 0;
}
/**********************************************
*********** decoding machinery *************
**********************************************/
static int
read_byte (KsbaReader reader)
{
unsigned char buf;
size_t nread;
int rc;
do
rc = ksba_reader_read (reader, &buf, 1, &nread);
while (!rc && !nread);
return rc? -1: buf;
}
/* read COUNT bytes into buffer. buffer may be NULL to skip over
COUNT bytes. Return 0 on success */
static int
read_buffer (KsbaReader reader, char *buffer, size_t count)
{
size_t nread;
if (buffer)
{
while (count)
{
if (ksba_reader_read (reader, buffer, count, &nread))
return -1;
buffer += nread;
count -= nread;
}
}
else
{
char dummy[256];
size_t n;
while (count)
{
n = count > DIM(dummy) ? DIM(dummy): count;
if (ksba_reader_read (reader, dummy, n, &nread))
return -1;
count -= nread;
}
}
return 0;
}
/* Return 0 for no match, 1 for a match and 2 for an ANY match of an
constructed type */
static int
cmp_tag (AsnNode node, const struct tag_info *ti)
{
if (node->flags.class != ti->class)
return 0;
if (node->type == TYPE_TAG)
{
return_val_if_fail (node->valuetype == VALTYPE_ULONG, 0);
return node->value.v_ulong == ti->tag;
}
if (node->type == ti->tag)
return 1;
if (ti->class == CLASS_UNIVERSAL)
{
if (node->type == TYPE_SEQUENCE_OF && ti->tag == TYPE_SEQUENCE)
return 1;
if (node->type == TYPE_SET_OF && ti->tag == TYPE_SET)
return 1;
if (node->type == TYPE_ANY)
return is_primitive_type (ti->tag)? 1:2;
}
return 0;
}
/* Find the node in the tree ROOT corresponding to TI and return that
node. Returns NULL if the node was not found */
static AsnNode
find_anchor_node (AsnNode root, const struct tag_info *ti)
{
AsnNode node = root;
while (node)
{
if (cmp_tag (node, ti))
{
return node; /* found */
}
if (node->down)
node = node->down;
else if (node == root)
return NULL; /* not found */
else if (node->right)
node = node->right;
else
{ /* go up and right */
do
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left;
if (!node || node == root)
return NULL; /* back at the root -> not found */
}
while (!node->right);
node = node->right;
}
}
return NULL;
}
static int
match_der (AsnNode root, const struct tag_info *ti,
DECODER_STATE ds, AsnNode *retnode, int debug)
{
int rc;
AsnNode node;
*retnode = NULL;
node = ds->cur.node;
if (!node)
{
if (debug)
puts (" looking for anchor");
node = find_anchor_node (root, ti);
if (!node)
fputs (" anchor node not found\n", stdout);
}
else if (ds->cur.again)
{
if (debug)
puts (" doing last again");
ds->cur.again = 0;
}
else if (is_primitive_type (node->type) || node->type == TYPE_ANY
|| node->type == TYPE_SIZE || node->type == TYPE_DEFAULT )
{
if (debug)
puts (" primitive type - get next");
if (node->right)
node = node->right;
else if (!node->flags.in_choice)
node = NULL;
else /* in choice */
{
if (debug)
puts (" going up after choice - get next");
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (node)
node = node->right;
}
}
else if (node->type == TYPE_SEQUENCE_OF || node->type == TYPE_SET_OF)
{
if (debug)
{
printf (" prepare for seq/set_of (%d %d) ",
ds->cur.length, ds->cur.nread);
printf (" cur: ("); _ksba_asn_node_dump (node, stdout);
printf (")\n");
if (ds->cur.node->flags.in_array)
puts (" This is in an arrat!");
if (ds->cur.went_up)
puts (" And we going up!");
}
if ((ds->cur.went_up && !ds->cur.node->flags.in_array) ||
(ds->idx && ds->cur.nread >= ds->stack[ds->idx-1].length))
{
if (debug)
printf (" advancing\n");
if (node->right)
node = node->right;
else
{
for (;;)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (!node)
break;
if (node->right)
{
node = node->right;
break;
}
}
}
}
else if (ds->cur.node->flags.in_array
&& ds->cur.went_up)
{
if (debug)
puts (" Reiterating");
node = _ksba_asn_insert_copy (node);
if (node)
prepare_copied_tree (node);
}
else
node = node->down;
}
else /* constructed */
{
if (debug)
{
printf (" prepare for constructed (%d %d) ",
ds->cur.length, ds->cur.nread);
printf (" cur: ("); _ksba_asn_node_dump (node, stdout);
printf (")\n");
if (ds->cur.node->flags.in_array)
puts (" This is in an array!");
if (ds->cur.went_up)
puts (" And we going up!");
}
ds->cur.in_seq_of = 0;
if (ds->cur.node->flags.in_array
&& ds->cur.went_up)
{
if (debug)
puts (" Reiterating this");
node = _ksba_asn_insert_copy (node);
if (node)
prepare_copied_tree (node);
}
else if (ds->cur.went_up || ds->cur.next_tag || ds->cur.node->flags.skip_this)
{
if (node->right)
node = node->right;
else
{
for (;;)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (!node)
break;
if (node->right)
{
node = node->right;
break;
}
}
}
}
else
node = node->down;
}
if (!node)
return -1;
ds->cur.node = node;
ds->cur.went_up = 0;
ds->cur.next_tag = 0;
if (debug)
{
printf (" Expect ("); _ksba_asn_node_dump (node, stdout); printf (")\n");
}
if (node->flags.skip_this)
return 1;
if (node->type == TYPE_SIZE)
{
if (debug)
printf (" skipping size tag\n");
return 1;
}
if (node->type == TYPE_DEFAULT)
{
if (debug)
printf (" skipping default tag\n");
return 1;
}
if (node->flags.is_implicit)
{
if (debug)
printf (" dummy accept for implicit tag\n");
return 1; /* again */
}
if ( (rc=cmp_tag (node, ti)))
{
*retnode = node;
return rc==2? 4:3;
}
if (node->type == TYPE_CHOICE)
{
if (debug)
printf (" testing choice...\n");
for (node = node->down; node; node = node->right)
{
if (debug)
{
printf (" %s (", node->flags.skip_this? "skip":" cmp");
_ksba_asn_node_dump (node, stdout);
printf (")\n");
}
if (!node->flags.skip_this && cmp_tag (node, ti) == 1)
{
if (debug)
{
printf (" choice match <"); dump_tlv (ti, stdout);
printf (">\n");
}
/* mark the remaining as done */
for (node=node->right; node; node = node->right)
node->flags.skip_this = 1;
return 1;
}
node->flags.skip_this = 1;
}
node = ds->cur.node; /* reset */
}
if (node->flags.in_choice)
{
if (debug)
printf (" skipping non matching choice\n");
return 1;
}
if (node->flags.is_optional)
{
if (debug)
printf (" skipping optional element\n");
if (node->type == TYPE_TAG)
ds->cur.next_tag = 1;
return 1;
}
if (node->flags.has_default)
{
if (debug)
printf (" use default value\n");
if (node->type == TYPE_TAG)
ds->cur.next_tag = 1;
*retnode = node;
return 2;
}
return -1;
}
static KsbaError
decoder_init (BerDecoder d, const char *start_name)
{
d->ds = new_decoder_state ();
d->root = _ksba_asn_expand_tree (d->module, start_name);
clear_help_flags (d->root);
d->bypass = 0;
if (d->debug)
printf ("DECODER_INIT for `%s'\n", start_name? start_name: "[root]");
return 0;
}
static void
decoder_deinit (BerDecoder d)
{
release_decoder_state (d->ds);
d->ds = NULL;
d->val.node = NULL;
if (d->debug)
printf ("DECODER_DEINIT\n");
}
static KsbaError
decoder_next (BerDecoder d)
{
struct tag_info ti;
AsnNode node;
KsbaError err;
DECODER_STATE ds = d->ds;
int debug = d->debug;
err = _ksba_ber_read_tl (d->reader, &ti);
if (err)
{
return err;
}
if (debug)
{
printf ("ReadTLV <"); dump_tlv (&ti, stdout); printf (">\n");
}
if (d->use_image)
{
if (!d->image.buf)
{
/* we need some extra bytes to store the stuff we read ahead
at the end of the module which is later pushed back */
d->image.length = ti.length + 100;
d->image.used = 0;
d->image.buf = xtrymalloc (d->image.length);
if (!d->image.buf)
return KSBA_Out_Of_Core;
}
if (ti.nhdr + d->image.used >= d->image.length)
return set_error (d, NULL, "image buffer too short to store the tag");
memcpy (d->image.buf + d->image.used, ti.buf, ti.nhdr);
d->image.used += ti.nhdr;
}
if (!d->bypass)
{
int again, endtag;
do
{
again = endtag = 0;
switch ( ds->cur.in_any? 4
: (ti.class == CLASS_UNIVERSAL && !ti.tag)? (endtag=1,5)
: match_der (d->root, &ti, ds, &node, debug))
{
case -1:
if (debug)
{
printf (" FAIL <"); dump_tlv (&ti, stdout); printf (">\n");
}
if (d->honor_module_end)
{
/* We must push back the stuff we already read */
ksba_reader_unread (d->reader, ti.buf, ti.nhdr);
return -1;
}
else
d->bypass = 1;
break;
case 0:
if (debug)
puts (" End of description");
d->bypass = 1;
break;
case 1: /* again */
if (debug)
printf (" Again\n");
again = 1;
break;
case 2: /* use default value + again*/
if (debug)
printf (" Using default\n");
again = 1;
break;
case 4: /* match of ANY on a constructed type */
if (debug)
printf (" ANY");
ds->cur.in_any = 1;
case 3: /* match */
case 5: /* end tag */
if (debug)
{
printf (" Match <"); dump_tlv (&ti, stdout); printf (">\n");
if (ti.tag == TYPE_OCTET_STRING && ti.length == 64)
printf (" DEBUG POINT\n");
}
/* increment by the header length */
ds->cur.nread += ti.nhdr;
if (!ti.is_constructed)
ds->cur.nread += ti.length;
ds->cur.went_up = 0;
do
{
if (debug)
printf (" (length %d nread %d) %s\n",
ds->idx? ds->stack[ds->idx-1].length:-1,
ds->cur.nread,
ti.is_constructed? "con":"pri");
if ( ds->idx
&& !ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
> ds->stack[ds->idx-1].length))
{
fprintf (stderr, " ERROR: object length field %d octects"
" too large\n",
ds->cur.nread > ds->cur.length);
ds->cur.nread = ds->cur.length;
}
if ( ds->idx
&& (endtag
|| (!ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
>= ds->stack[ds->idx-1].length))))
{
int n = ds->cur.nread;
pop_decoder_state (ds);
ds->cur.nread += n;
ds->cur.went_up++;
}
endtag = 0;
}
while ( ds->idx
&& !ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
>= ds->stack[ds->idx-1].length));
if (ti.is_constructed)
{
/* prepare for the next level */
ds->cur.length = ti.length;
ds->cur.ndef_length = ti.ndef;
push_decoder_state (ds);
ds->cur.length = 0;
ds->cur.ndef_length = 0;
ds->cur.nread = 0;
}
if (debug)
printf (" (length %d nread %d) end\n",
ds->idx? ds->stack[ds->idx-1].length:-1,
ds->cur.nread);
break;
default:
never_reached ();
abort ();
break;
}
}
while (again);
}
d->val.primitive = !ti.is_constructed;
d->val.length = ti.length;
d->val.nhdr = ti.nhdr;
d->val.tag = ti.tag; /* kludge to fix TYPE_ANY probs */
d->val.is_endtag = (ti.class == CLASS_UNIVERSAL && !ti.tag);
d->val.node = d->bypass? NULL : node;
if (debug)
dump_decoder_state (ds);
return 0;
}
static KsbaError
decoder_skip (BerDecoder d)
{
if (d->val.primitive)
{
if (read_buffer (d->reader, NULL, d->val.length))
return eof_or_error (d, 1);
}
return 0;
}
/* Calculate the distance between the 2 nodes */
static int
distance (AsnNode root, AsnNode node)
{
int n=0;
while (node && node != root)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left;
n++;
}
return n;
}
/**
* _ksba_ber_decoder_dump:
* @d: Decoder object
*
* Dump a textual representation of the encoding to the given stream.
*
* Return value:
**/
KsbaError
_ksba_ber_decoder_dump (BerDecoder d, FILE *fp)
{
KsbaError err;
int depth = 0;
AsnNode node;
unsigned char *buf = NULL;
size_t buflen = 0;;
if (!d)
return KSBA_Invalid_Value;
d->debug = !!getenv("DEBUG_BER_DECODER");
d->use_image = 0;
d->image.buf = NULL;
err = decoder_init (d, NULL);
if (err)
return err;
while (!(err = decoder_next (d)))
{
node = d->val.node;
if (node)
depth = distance (d->root, node);
fprintf (fp, "%4lu %4u:%*s",
ksba_reader_tell (d->reader) - d->val.nhdr,
d->val.length,
depth*2, "");
if (node)
_ksba_asn_node_dump (node, fp);
else
fputs ("[No matching node]", fp);
if (node && d->val.primitive)
{
int i, n, c;
char *p;
if (!buf || buflen < d->val.length)
{
xfree (buf);
buflen = d->val.length + 100;
buf = xtrymalloc (buflen);
if (!buf)
err = KSBA_Out_Of_Core;
}
for (n=0; !err && n < d->val.length; n++)
{
if ( (c=read_byte (d->reader)) == -1)
err = eof_or_error (d, 1);
buf[n] = c;
}
if (err)
break;
fputs (" (", fp);
p = NULL;
switch (node->type)
{
case TYPE_OBJECT_ID:
p = ksba_oid_to_str (buf, n);
break;
default:
for (i=0; i < n && i < 20; i++)
fprintf (fp,"%02x", buf[i]);
if (i < n)
fputs ("..more..", fp);
break;
}
if (p)
{
fputs (p, fp);
xfree (p);
}
fputs (")\n", fp);
}
else
{
err = decoder_skip (d);
putc ('\n', fp);
}
if (err)
break;
}
if (err == -1)
err = 0;
decoder_deinit (d);
xfree (buf);
return err;
}
KsbaError
_ksba_ber_decoder_decode (BerDecoder d, const char *start_name,
AsnNode *r_root,
unsigned char **r_image, size_t *r_imagelen)
{
KsbaError err;
AsnNode node;
unsigned char *buf = NULL;
size_t buflen = 0;
unsigned long startoff;
if (!d)
return KSBA_Invalid_Value;
if (r_root)
*r_root = NULL;
d->debug = !!getenv("DEBUG_BER_DECODER");
d->honor_module_end = 1;
- d->use_image = 1;
+ d->use_image = 1; /* FIXME: remove the old cruft as we are only
+ using the image method. */
d->image.buf = NULL;
startoff = ksba_reader_tell (d->reader);
err = decoder_init (d, start_name);
if (err)
return err;
while (!(err = decoder_next (d)))
{
int n, c;
node = d->val.node;
if (node && d->use_image)
{
if (!d->val.is_endtag)
{ /* We don't have nodes for the end tag - so don't store it */
node->off = (ksba_reader_tell (d->reader)
- d->val.nhdr - startoff);
node->nhdr = d->val.nhdr;
node->len = d->val.length;
if (node->type == TYPE_ANY)
node->actual_type = d->val.tag;
}
if (d->image.used + d->val.length > d->image.length)
err = set_error(d, NULL, "TLV length too large");
else if (d->val.primitive)
{
if( read_buffer (d->reader,
d->image.buf + d->image.used, d->val.length))
err = eof_or_error (d, 1);
else
d->image.used += d->val.length;
}
}
else if (node && d->val.primitive)
{
if (!buf || buflen < d->val.length)
{
xfree (buf);
buflen = d->val.length + 100;
buf = xtrymalloc (buflen);
if (!buf)
err = KSBA_Out_Of_Core;
}
for (n=0; !err && n < d->val.length; n++)
{
if ( (c=read_byte (d->reader)) == -1)
err = eof_or_error (d, 1);
buf[n] = c;
}
if (err)
break;
switch (node->type)
{
default:
_ksba_asn_set_value (node, VALTYPE_MEM, buf, n);
break;
}
}
else
{
err = decoder_skip (d);
}
if (err)
break;
}
if (err == -1)
err = 0;
if (r_root && !err)
{
if (!d->image.buf)
{ /* Not even the first node available - return eof */
/* fixme: release d->root */
d->root = NULL;
err = -1;
}
fixup_type_any (d->root);
*r_root = d->root;
d->root = NULL;
*r_image = d->image.buf;
d->image.buf = NULL;
*r_imagelen = d->image.used;
if (d->debug)
{
fputs ("Value Tree:\n", stdout);
_ksba_asn_node_dump_all (*r_root, stdout);
}
}
decoder_deinit (d);
xfree (buf);
return err;
}
diff --git a/src/ber-help.c b/src/ber-help.c
index 5575202..effa8ec 100644
--- a/src/ber-help.c
+++ b/src/ber-help.c
@@ -1,162 +1,218 @@
/* ber-help.c - BER herlper functions
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "asn1-func.h" /* need some constants */
#include "ber-help.h"
static int
read_byte (KsbaReader reader)
{
unsigned char buf;
size_t nread;
int rc;
do
rc = ksba_reader_read (reader, &buf, 1, &nread);
while (!rc && !nread);
return rc? -1: buf;
}
static int
eof_or_error (KsbaReader reader, struct tag_info *ti, int premature)
{
if (ksba_reader_error (reader))
{
ti->err_string = "read error";
return KSBA_Read_Error;
}
if (premature)
{
ti->err_string = "premature EOF";
return KSBA_BER_Error;
}
return -1;
}
/*
Read the tag and the length part from the TLV triplet.
*/
KsbaError
_ksba_ber_read_tl (KsbaReader reader, struct tag_info *ti)
{
int c;
unsigned long tag;
ti->length = 0;
ti->ndef = 0;
ti->nhdr = 0;
ti->err_string = NULL;
ti->non_der = 0;
/* Get the tag */
c = read_byte (reader);
if (c==-1)
return eof_or_error (reader, ti, 0);
ti->buf[ti->nhdr++] = c;
ti->class = (c & 0xc0) >> 6;
ti->is_constructed = !!(c & 0x20);
tag = c & 0x1f;
if (tag == 0x1f)
{
tag = 0;
do
{
/* fixme: check for overflow of out datatype */
tag <<= 7;
c = read_byte (reader);
if (c == -1)
return eof_or_error (reader, ti, 1);
if (ti->nhdr >= DIM (ti->buf))
{
ti->err_string = "tag+length header too large";
return KSBA_BER_Error;
}
ti->buf[ti->nhdr++] = c;
tag |= c & 0x7f;
}
while (c & 0x80);
}
ti->tag = tag;
/* Get the length */
c = read_byte (reader);
if (c == -1)
return eof_or_error (reader, ti, 1);
if (ti->nhdr >= DIM (ti->buf))
{
ti->err_string = "tag+length header too large";
return KSBA_BER_Error;
}
ti->buf[ti->nhdr++] = c;
if ( !(c & 0x80) )
ti->length = c;
else if (c == 0x80)
{
ti->ndef = 1;
ti->non_der = 1;
}
else if (c == 0xff)
{
ti->err_string = "forbidden length value";
return KSBA_BER_Error;
}
else
{
unsigned long len = 0;
int count = c & 0x7f;
/* fixme: check for overflow of our length type */
for (; count; count--)
{
len <<= 8;
c = read_byte (reader);
if (c == -1)
return eof_or_error (reader, ti, 1);
if (ti->nhdr >= DIM (ti->buf))
{
ti->err_string = "tag+length header too large";
return KSBA_BER_Error;
}
ti->buf[ti->nhdr++] = c;
len |= c & 0xff;
}
ti->length = len;
}
/* Without this kludge some example certs can't be parsed */
if (ti->class == CLASS_UNIVERSAL && !ti->tag)
ti->length = 0;
return 0;
}
+
+/* Write TAG of CLASS to WRITER. constructed is a flag telling
+ whether the value is a constructed one. length gives the length of
+ the value, if it is 0 undefinite length is assumed. length is
+ ignored for the NULL tag. */
+KsbaError
+_ksba_ber_write_tl (KsbaWriter writer,
+ unsigned long tag,
+ enum tag_class class,
+ int constructed,
+ unsigned long length)
+{
+ unsigned char buf[50];
+ int buflen = 0;
+
+ if (tag < 0x1f)
+ {
+ *buf = (class << 6) | tag;
+ if (constructed)
+ *buf |= 0x20;
+ buflen++;
+ }
+ else
+ {
+ return KSBA_Not_Implemented;
+ }
+
+ if (!tag && !class)
+ buf[buflen++] = 0; /* NULL tag */
+ else if (!length)
+ buf[buflen++] = 0x80; /* indefinite length */
+ else if (length < 128)
+ buf[buflen++] = length;
+ else
+ {
+ int i;
+
+ /* fixme: if we know the sizeof an ulong we could support larger
+ objetcs - however this is pretty ridiculous */
+ i = (length <= 0xff ? 1:
+ length <= 0xffff ? 2:
+ length <= 0xffffff ? 3: 4);
+
+ buf[buflen++] = (0x80 | i);
+ if (i > 3)
+ buf[buflen++] = length >> 24;
+ if (i > 2)
+ buf[buflen++] = length >> 16;
+ if (i > 1)
+ buf[buflen++] = length >> 8;
+ buf[buflen++] = length;
+ }
+
+ return ksba_writer_write (writer, buf, buflen);
+}
+
diff --git a/src/ber-help.h b/src/ber-help.h
index 39f2fc2..986a4f5 100644
--- a/src/ber-help.h
+++ b/src/ber-help.h
@@ -1,42 +1,47 @@
/* ber-help.h - Basic Encoding Rules helpers
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifndef BER_HELP_H
#define BER_HELP_H 1
struct tag_info {
enum tag_class class;
int is_constructed;
unsigned long tag;
unsigned long length; /* length part of the TLV */
int ndef; /* It is an indefinite length */
size_t nhdr; /* number of bytes in the TL */
unsigned char buf[10]; /* buffer for the TL */
const char *err_string;
int non_der;
};
KsbaError _ksba_ber_read_tl (KsbaReader reader, struct tag_info *ti);
+KsbaError _ksba_ber_write_tl (KsbaWriter writer,
+ unsigned long tag,
+ enum tag_class class,
+ int constructed,
+ unsigned long length);
#endif /*BER_HELP_H*/
diff --git a/src/cms-parser.c b/src/cms-parser.c
index 3e11818..118dbb8 100644
--- a/src/cms-parser.c
+++ b/src/cms-parser.c
@@ -1,505 +1,510 @@
/* cms-parse.c - parse cryptographic message syntax
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
/*
We handle CMS by using a handcrafted parser for the outer
structures and the generic parser of the parts we can handle in
memory. Extending the generic parser to allow hooks for indefinite
length objects and to auto select the object depending on the
content type OID is too complicated.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "cms.h"
#include "asn1-func.h" /* need some constants */
#include "ber-decoder.h"
#include "ber-help.h"
#include "keyinfo.h"
static int
read_byte (KsbaReader reader)
{
unsigned char buf;
size_t nread;
int rc;
do
rc = ksba_reader_read (reader, &buf, 1, &nread);
while (!rc && !nread);
return rc? -1: buf;
}
/* read COUNT bytes into buffer. Return 0 on success */
static int
read_buffer (KsbaReader reader, char *buffer, size_t count)
{
size_t nread;
while (count)
{
if (ksba_reader_read (reader, buffer, count, &nread))
return -1;
buffer += nread;
count -= nread;
}
return 0;
}
/* Parse this structure and return the oid of the content. The read
position is then located at the value of content. This fucntion is
the core for parsing ContentInfo and EncapsulatedContentInfo.
ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT ANY DEFINED BY contentType
}
ContentType ::= OBJECT IDENTIFIER
Returns: 0 on success or an error code. Other values are returned
by the parameters.
*/
static KsbaError
parse_content_info (KsbaReader reader,
unsigned long *r_len, int *r_ndef,
char **r_oid, int *has_content)
{
struct tag_info ti;
KsbaError err;
int content_ndef;
unsigned long content_len;
unsigned char oidbuf[100]; /* pretty large for an OID */
char *oid = NULL;
int c, i;
/* read the sequence triplet */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return KSBA_Invalid_CMS_Object;
content_len = ti.length;
content_ndef = ti.ndef;
if (!content_ndef && content_len < 3)
return KSBA_Object_Too_Short; /* to encode an OID */
/* read the OID */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID
&& !ti.is_constructed && ti.length) )
return KSBA_Invalid_CMS_Object;
if (!content_ndef)
{
if (content_len < ti.nhdr)
return KSBA_BER_Error; /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (content_len < ti.length)
return KSBA_BER_Error; /* triplet larger that sequence */
content_len -= ti.length;
}
for (i=0; i < ti.length; i++)
{
if (i == DIM(oidbuf))
return KSBA_Object_Too_Large;
if ( (c=read_byte (reader)) == -1)
return KSBA_Read_Error;
oidbuf[i] = c;
}
oid = ksba_oid_to_str (oidbuf, i);
if (!oid)
return KSBA_Out_Of_Core;
-
- /* now read the explicit tag 0 */
- err = _ksba_ber_read_tl (reader, &ti);
- if (err)
- {
- xfree (oid);
- return err;
- }
- if ( ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed )
- {
- *has_content = 1;
- }
- else if ( ti.class == CLASS_UNIVERSAL && ti.tag == 0 && !ti.is_constructed )
- {
- *has_content = 0; /* this is optional - allow NUL tag */
- }
- else /* neither [0] nor NULL */
- {
- xfree (oid);
- return KSBA_Invalid_CMS_Object;
- }
- if (!content_ndef)
- {
- if (content_len < ti.nhdr)
- return KSBA_BER_Error; /* triplet header larger that sequence */
- content_len -= ti.nhdr;
- if (!ti.ndef && content_len < ti.length)
- return KSBA_BER_Error; /* triplet larger that sequence */
+ if (!content_ndef && !content_len)
+ { /* no data */
+ *has_content = 0;
}
+ else
+ { /* now read the explicit tag 0 which is optional */
+ err = _ksba_ber_read_tl (reader, &ti);
+ if (err)
+ {
+ xfree (oid);
+ return err;
+ }
+ if ( ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed )
+ {
+ *has_content = 1;
+ }
+ else if ( ti.class == CLASS_UNIVERSAL && ti.tag == 0 && !ti.is_constructed )
+ {
+ *has_content = 0; /* this is optional - allow NUL tag */
+ }
+ else /* neither [0] nor NULL */
+ {
+ xfree (oid);
+ return KSBA_Invalid_CMS_Object;
+ }
+ if (!content_ndef)
+ {
+ if (content_len < ti.nhdr)
+ return KSBA_BER_Error; /* triplet header larger that sequence */
+ content_len -= ti.nhdr;
+ if (!ti.ndef && content_len < ti.length)
+ return KSBA_BER_Error; /* triplet larger that sequence */
+ }
+ }
*r_len = content_len;
*r_ndef = content_ndef;
*r_oid = oid;
return 0;
}
/* Parse this structure and return the oid of the content. The read
position is then located at the value of content.
ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT ANY DEFINED BY contentType
}
ContentType ::= OBJECT IDENTIFIER
Returns: 0 on success or an error code. On success the OID and the
length values are stored in the cms structure.
*/
KsbaError
_ksba_cms_parse_content_info (KsbaCMS cms)
{
KsbaError err;
int has_content;
int content_ndef;
unsigned long content_len;
char *oid;
err = parse_content_info (cms->reader, &content_len, &content_ndef,
&oid, &has_content);
if (err)
{ /* return a more meaningful error message. This way the caller
can pass arbitrary data to the function and get back an error
that this is not CMS instead of BER Error */
if (err == KSBA_BER_Error || err == KSBA_Invalid_CMS_Object
|| err == KSBA_Object_Too_Short)
err = KSBA_No_CMS_Object;
return err;
}
if (!has_content)
return KSBA_No_CMS_Object; /* It is not optional here */
cms->content.length = content_len;
cms->content.ndef = content_ndef;
xfree (cms->content.oid);
cms->content.oid = oid;
return 0;
}
/* Parse a structure:
SignedData ::= SEQUENCE {
version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }),
digestAlgorithms SET OF AlgorithmIdentifier,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerInfos SignerInfos }
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}
*/
KsbaError
_ksba_cms_parse_signed_data_part_1 (KsbaCMS cms)
{
struct tag_info ti;
KsbaError err;
int signed_data_ndef;
unsigned long signed_data_len;
int algo_set_ndef;
unsigned long algo_set_len;
int encap_cont_ndef;
unsigned long encap_cont_len;
int has_content;
char *oid;
int c;
char *p, *buffer;
unsigned long off, len;
/* read the sequence triplet */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return KSBA_Invalid_CMS_Object;
signed_data_len = ti.length;
signed_data_ndef = ti.ndef;
if (!signed_data_ndef && signed_data_len < 3)
return KSBA_Object_Too_Short; /*to encode the version*/
/* read the version integer */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER
&& !ti.is_constructed && ti.length) )
return KSBA_Invalid_CMS_Object;
if (!signed_data_ndef)
{
if (signed_data_len < ti.nhdr)
return KSBA_BER_Error; /* triplet header larger that sequence */
signed_data_len -= ti.nhdr;
if (signed_data_len < ti.length)
return KSBA_BER_Error; /* triplet larger that sequence */
signed_data_len -= ti.length;
}
if (ti.length != 1)
return KSBA_Unsupported_CMS_Version;
if ( (c=read_byte (cms->reader)) == -1)
return KSBA_Read_Error;
if ( !(c == 0 || c == 1 || c == 2 || c == 3 || c == 4) )
return KSBA_Unsupported_CMS_Version;
cms->cms_version = c;
/* read the SET OF algorithmIdentifiers */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET
&& ti.is_constructed) )
return KSBA_Invalid_CMS_Object; /* not the expected SET tag */
if (!signed_data_ndef)
{
if (signed_data_len < ti.nhdr)
return KSBA_BER_Error; /* triplet header larger that sequence */
signed_data_len -= ti.nhdr;
if (!ti.ndef && signed_data_len < ti.length)
return KSBA_BER_Error; /* triplet larger that sequence */
signed_data_len -= ti.length;
}
algo_set_len = ti.length;
algo_set_ndef = ti.ndef;
/* fixme: we are not able to read ndef length algorithm indentifiers. */
if (algo_set_ndef)
return KSBA_Unsupported_Encoding;
/* read the entire sequence into a buffer */
buffer = xtrymalloc (algo_set_len);
if (!buffer)
return KSBA_Out_Of_Core;
if (read_buffer (cms->reader, buffer, algo_set_len))
{
xfree (buffer);
return KSBA_Read_Error;
}
p = buffer;
while (algo_set_len)
{
size_t nread;
struct oidlist_s *ol;
err = _ksba_parse_algorithm_identifier (p, algo_set_len, &nread, &oid);
if (err)
{
xfree (buffer);
return err;
}
assert (nread <= algo_set_len);
algo_set_len -= nread;
p += nread;
/* store the oid */
ol = xtrymalloc (sizeof *ol);
if (!ol)
{
xfree (oid);
return KSBA_Out_Of_Core;
}
ol->oid = oid;
ol->next = cms->digest_algos;
cms->digest_algos = ol;
}
xfree (buffer); buffer = NULL;
/* Now for the encapsulatedContentInfo */
off = ksba_reader_tell (cms->reader);
err = parse_content_info (cms->reader,
&encap_cont_len, &encap_cont_ndef,
&oid, &has_content);
if (err)
return err;
cms->encap_cont_type = oid;
cms->detached_signature = !has_content;
if (!signed_data_ndef)
{
len = ksba_reader_tell (cms->reader) - off;
if (signed_data_len < len)
return KSBA_BER_Error; /* parsed content info larger that sequence */
signed_data_len -= len;
if (!encap_cont_ndef && signed_data_len < encap_cont_len)
return KSBA_BER_Error; /* triplet larger that sequence */
}
/* Fixme: Do we ween to skip the OCTECT STRING tag here or should we
just use whatever comes? */
/* FIXME: need to store the content length info */
/* We have to stop here so that the caller can set up the hashing etc. */
return 0;
}
/* Continue parsing of the structure we started to parse with the
part_1 function. We expect to be right at the certificates tag. */
KsbaError
_ksba_cms_parse_signed_data_part_2 (KsbaCMS cms)
{
struct tag_info ti;
KsbaError err;
/* read the next triplet which is either a [0], a [1] or a SET OF
(signerInfo) */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)
{ /* implicit SET OF certificateSet with elements of CHOICE, but
we assume the first choice which is a Certificate; all other
choices are obsolete. We are now parsing a set of
certificates which we do by utilizing the ksba_cert code. */
KsbaCert cert;
if (ti.ndef)
return KSBA_Unsupported_Encoding;
for (;;)
{
struct certlist_s *cl;
/* first see whether this is really a sequence */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed))
break; /* not a sequence, so we are ready with the set */
/* We must unread so that the standard parser sees the sequence */
err = ksba_reader_unread (cms->reader, ti.buf, ti.nhdr);
if (err)
return err;
/* Use the standard certificate parser */
cert = ksba_cert_new ();
if (!cert)
return KSBA_Out_Of_Core;
err = ksba_cert_read_der (cert, cms->reader);
if (err)
{
ksba_cert_release (cert);
return err;
}
- cl = xtrymalloc (sizeof *cl);
+ cl = xtrycalloc (1, sizeof *cl);
if (!cl)
{
ksba_cert_release (cert);
return KSBA_Out_Of_Core;
}
cl->cert = cert;
cl->next = cms->cert_list;
cms->cert_list = cl;
}
}
if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed)
{ /* implicit SET OF certificateList. We should delegate the
parsing to a - not yet existing - ksba_crl module. CRLs are
quite importatnt for other applications too so we should
provide a nice interface */
fprintf (stderr,"ERROR: Can't handle CRLs yet\n");
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
}
/* expect a SET OF signerInfo */
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SET && ti.is_constructed))
return KSBA_Invalid_CMS_Object;
{
KsbaAsnTree cms_tree;
BerDecoder decoder;
err = ksba_asn_create_tree ("cms", &cms_tree);
if (err)
return err;
decoder = _ksba_ber_decoder_new ();
if (!decoder)
{
ksba_asn_tree_release (cms_tree);
return KSBA_Out_Of_Core;
}
err = _ksba_ber_decoder_set_reader (decoder, cms->reader);
if (err)
{
ksba_asn_tree_release (cms_tree);
_ksba_ber_decoder_release (decoder);
return err;
}
err = _ksba_ber_decoder_set_module (decoder, cms_tree);
if (err)
{
ksba_asn_tree_release (cms_tree);
_ksba_ber_decoder_release (decoder);
return err;
}
err = _ksba_ber_decoder_decode (decoder,
"CryptographicMessageSyntax.SignerInfos",
&cms->signer_info.root,
&cms->signer_info.image,
&cms->signer_info.imagelen);
_ksba_ber_decoder_release (decoder);
ksba_asn_tree_release (cms_tree);
if (err)
return err;
}
return 0;
}
diff --git a/src/cms.c b/src/cms.c
index fce7279..e4f116a 100644
--- a/src/cms.c
+++ b/src/cms.c
@@ -1,693 +1,1044 @@
/* cms.c - cryptographic message syntax main functions
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "cms.h"
#include "convert.h"
#include "keyinfo.h"
-
-static KsbaError ct_signed_data (KsbaCMS cms);
-static KsbaError ct_enveloped_data (KsbaCMS cms);
-static KsbaError ct_digested_data (KsbaCMS cms);
-static KsbaError ct_encrypted_data (KsbaCMS cms);
+#include "der-encoder.h"
+#include "ber-help.h"
+#include "cert.h" /* need to access cert->root and cert->image */
+
+static KsbaError ct_parse_signed_data (KsbaCMS cms);
+static KsbaError ct_parse_enveloped_data (KsbaCMS cms);
+static KsbaError ct_parse_digested_data (KsbaCMS cms);
+static KsbaError ct_parse_encrypted_data (KsbaCMS cms);
+static KsbaError ct_build_signed_data (KsbaCMS cms);
+static KsbaError ct_build_enveloped_data (KsbaCMS cms);
+static KsbaError ct_build_digested_data (KsbaCMS cms);
+static KsbaError ct_build_encrypted_data (KsbaCMS cms);
static struct {
const char *oid;
KsbaContentType ct;
- KsbaError (*handler)(KsbaCMS);
+ KsbaError (*parse_handler)(KsbaCMS);
+ KsbaError (*build_handler)(KsbaCMS);
} content_handlers[] = {
- { "1.2.840.113549.1.7.1", /* data*/ },
- { "1.2.840.113549.1.7.2", KSBA_CT_SIGNED_DATA, ct_signed_data },
- { "1.2.840.113549.1.7.3", KSBA_CT_ENVELOPED_DATA, ct_enveloped_data },
- { "1.2.840.113549.1.7.5", KSBA_CT_DIGESTED_DATA, ct_digested_data },
- { "1.2.840.113549.1.7.6", KSBA_CT_ENCRYPTED_DATA, ct_encrypted_data },
- { "1.2.840.113549.1.9.16.1.2", /*authData*/ },
+ { "1.2.840.113549.1.7.1", KSBA_CT_DATA },
+ { "1.2.840.113549.1.7.2", KSBA_CT_SIGNED_DATA,
+ ct_parse_signed_data , ct_build_signed_data },
+ { "1.2.840.113549.1.7.3", KSBA_CT_ENVELOPED_DATA,
+ ct_parse_enveloped_data, ct_build_enveloped_data },
+ { "1.2.840.113549.1.7.5", KSBA_CT_DIGESTED_DATA,
+ ct_parse_digested_data , ct_build_digested_data },
+ { "1.2.840.113549.1.7.6", KSBA_CT_ENCRYPTED_DATA,
+ ct_parse_encrypted_data, ct_build_encrypted_data },
+ { "1.2.840.113549.1.9.16.1.2", KSBA_CT_AUTH_DATA },
{ NULL }
};
/* 1.2.840.113549.1.9.4 */
static char oid_messageDigest[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x04";
/**
* ksba_cms_new:
*
* Create a new and empty CMS object
*
* Return value: A CMS object or NULL in case of memory problems.
**/
KsbaCMS
ksba_cms_new (void)
{
KsbaCMS cms;
cms = xtrycalloc (1, sizeof *cms);
if (!cms)
return NULL;
return cms;
}
/**
* ksba_cms_release:
* @cms: A CMS object
*
* Release a CMS object.
**/
void
ksba_cms_release (KsbaCMS cms)
{
if (!cms)
return;
xfree (cms->content.oid);
while (cms->digest_algos)
{
struct oidlist_s *ol = cms->digest_algos->next;
xfree (cms->digest_algos->oid);
xfree (cms->digest_algos);
cms->digest_algos = ol;
}
while (cms->cert_list)
{
struct certlist_s *cl = cms->cert_list->next;
ksba_cert_release (cms->cert_list->cert);
xfree (cms->cert_list);
cms->cert_list = cl;
}
xfree (cms->encap_cont_type);
xfree (cms->data.digest);
_ksba_asn_release_nodes (cms->signer_info.root);
xfree (cms->signer_info.image);
xfree (cms->signer_info.cache.digest_algo);
xfree (cms);
}
KsbaError
ksba_cms_set_reader_writer (KsbaCMS cms, KsbaReader r, KsbaWriter w)
{
- if (!cms || !r || !w)
+ if (!cms || !(r || w))
return KSBA_Invalid_Value;
- if (cms->reader || cms->writer )
+ if ((r && cms->reader) || (w && cms->writer) )
return KSBA_Conflict; /* already set */
cms->reader = r;
cms->writer = w;
return 0;
}
KsbaError
ksba_cms_parse (KsbaCMS cms, KsbaStopReason *r_stopreason)
{
KsbaError err;
int i;
if (!cms || !r_stopreason)
return KSBA_Invalid_Value;
*r_stopreason = KSBA_SR_RUNNING;
if (!cms->stop_reason)
{ /* Initial state: start parsing */
err = _ksba_cms_parse_content_info (cms);
if (err)
return err;
for (i=0; content_handlers[i].oid; i++)
{
if (!strcmp (content_handlers[i].oid, cms->content.oid))
break;
}
if (!content_handlers[i].oid)
return KSBA_Unknown_CMS_Object;
- if (!content_handlers[i].handler)
+ if (!content_handlers[i].parse_handler)
return KSBA_Unsupported_CMS_Object;
cms->content.ct = content_handlers[i].ct;
- cms->content.handler = content_handlers[i].handler;
+ cms->content.handler = content_handlers[i].parse_handler;
+ cms->stop_reason = KSBA_SR_GOT_CONTENT;
+ }
+ else if (cms->content.handler)
+ {
+ err = cms->content.handler (cms);
+ if (err)
+ return err;
+ }
+ else
+ return KSBA_Unsupported_CMS_Object;
+
+ *r_stopreason = cms->stop_reason;
+ return 0;
+}
+
+KsbaError
+ksba_cms_build (KsbaCMS cms, KsbaStopReason *r_stopreason)
+{
+ KsbaError err;
+
+ if (!cms || !r_stopreason)
+ return KSBA_Invalid_Value;
+
+ *r_stopreason = KSBA_SR_RUNNING;
+ if (!cms->stop_reason)
+ { /* Initial state: check that the content handler is known */
+ if (!cms->writer)
+ return KSBA_Missing_Action;
+ if (!cms->content.handler)
+ return KSBA_Missing_Action;
+ if (!cms->encap_cont_type)
+ return KSBA_Missing_Action;
cms->stop_reason = KSBA_SR_GOT_CONTENT;
}
else if (cms->content.handler)
{
err = cms->content.handler (cms);
if (err)
return err;
}
else
return KSBA_Unsupported_CMS_Object;
*r_stopreason = cms->stop_reason;
return 0;
}
/* Return the content type. A WHAT of 0 returns the real content type
whereas a 1 returns the inner content type.
*/
KsbaContentType
ksba_cms_get_content_type (KsbaCMS cms, int what)
{
int i;
if (!cms)
return 0;
if (!what)
return cms->content.ct;
if (what == 1 && cms->encap_cont_type)
{
for (i=0; content_handlers[i].oid; i++)
{
if (!strcmp (content_handlers[i].oid, cms->encap_cont_type))
return content_handlers[i].ct;
}
}
return 0;
}
/* Return the object ID of the current cms. This is a constant string
valid as long as the context is valid and no new parse is
started. */
const char *
ksba_cms_get_content_oid (KsbaCMS cms, int what)
{
if (!cms)
return NULL;
if (!what)
return cms->content.oid;
if (what == 1)
return cms->encap_cont_type;
return NULL;
}
/**
* ksba_cert_get_digest_algo_list:
* @cert: Initialized certificate object
* @idx: enumerator
*
* Figure out the the digest algorithm used for the signature and
* return its OID. Note that the algos returned are just hints on
* what to hash.
*
* Return value: NULL for no more algorithms or a string valid as long
* as the the cms object is valid.
**/
const char *
ksba_cms_get_digest_algo_list (KsbaCMS cms, int idx)
{
struct oidlist_s *ol;
if (!cms)
return NULL;
for (ol=cms->digest_algos; ol && idx; ol = ol->next, idx-- )
;
if (!ol)
return NULL;
return ol->oid;
}
KsbaError
ksba_cms_get_issuer_serial (KsbaCMS cms, int idx,
char **r_issuer, unsigned char **r_serial)
{
KsbaError err;
AsnNode n;
if (!cms)
return KSBA_Invalid_Value;
if (!cms->signer_info.root)
return KSBA_No_Data;
if (r_issuer)
{
n = _ksba_asn_find_node (cms->signer_info.root,
"SignerInfos..sid.issuerAndSerialNumber.issuer");
if (!n || !n->down)
return KSBA_No_Value;
n = n->down; /* dereference the choice node */
if (n->off == -1)
{
fputs ("get_issuer problem at node:\n", stderr);
_ksba_asn_node_dump_all (n, stderr);
return KSBA_General_Error;
}
err = _ksba_dn_to_str (cms->signer_info.image, n, r_issuer);
if (err)
return err;
}
if (r_serial)
{
unsigned char *p;
/* fixme: we do not release the r_issuer stuff on error */
n = _ksba_asn_find_node (cms->signer_info.root,
"SignerInfos..sid.issuerAndSerialNumber.serialNumber");
if (!n)
return KSBA_No_Value;
if (n->off == -1)
{
fputs ("get_serial problem at node:\n", stderr);
_ksba_asn_node_dump_all (n, stderr);
return KSBA_General_Error;
}
p = xtrymalloc (n->len + 4);
if (!p)
return KSBA_Out_Of_Core;
p[0] = n->len >> 24;
p[1] = n->len >> 16;
p[2] = n->len >> 8;
p[3] = n->len;
memcpy (p+4, cms->signer_info.image + n->off + n->nhdr, n->len);
*r_serial = p;
}
return 0;
}
/**
* ksba_cms_get_digest_algo:
* @cms: CMS object
* @idx: index of signer
*
* Figure out the the digest algorithm used by the signer @idx return
* its OID This is the algorithm acually used to calculate the
* signature.
*
* Return value: NULL for no such signer or a constn string valid as
* long as the CMS object lives.
**/
const char *
ksba_cms_get_digest_algo (KsbaCMS cms, int idx)
{
AsnNode n;
char *algo;
if (!cms)
return NULL;
if (!cms->signer_info.root)
return NULL;
if (idx)
return NULL; /* fixme: we can only handle one signer for now */
if (cms->signer_info.cache.digest_algo)
return cms->signer_info.cache.digest_algo;
n = _ksba_asn_find_node (cms->signer_info.root,
"SignerInfos..digestAlgorithm.algorithm");
algo = _ksba_oid_node_to_str (cms->signer_info.image, n);
if (algo)
{
cms->signer_info.cache.digest_algo = algo;
}
return algo;
}
/**
* ksba_cms_get_cert:
* @cms: CMS object
* @idx: enumerator
*
* Get the certificate out of a CMS. The caller should use this in a
* loop to get all certificates. NOte: This function can be used only
* once because an already retrieved cert is deleted from the CMS
* object for efficiency. FIXME: we should use reference counting instead.
*
* Return value: A Certificate object or NULL for end of list or error
**/
KsbaCert
ksba_cms_get_cert (KsbaCMS cms, int idx)
{
struct certlist_s *cl;
KsbaCert cert;
if (!cms || idx < 0)
return NULL;
for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--)
;
if (!cl)
return NULL;
cert = cl->cert;
cl->cert = NULL;
return cert;
}
/*
Return the extension attribute messageDigest
*/
KsbaError
ksba_cms_get_message_digest (KsbaCMS cms, int idx,
char **r_digest, size_t *r_digest_len)
{
AsnNode nsiginfo, n;
if (!cms || !r_digest || !r_digest_len)
return KSBA_Invalid_Value;
if (!cms->signer_info.root)
return KSBA_No_Data;
if (idx)
return KSBA_Not_Implemented;
*r_digest = NULL;
*r_digest_len = 0;
nsiginfo = _ksba_asn_find_node (cms->signer_info.root,
"SignerInfos..signedAttrs");
if (!nsiginfo)
return 0; /* this is okay, because the element is optional */
n = _ksba_asn_find_type_value (cms->signer_info.image, nsiginfo, 0,
oid_messageDigest, DIM(oid_messageDigest));
if (!n)
return KSBA_Value_Not_Found; /* message digest is required */
/* check that there is only one */
if (_ksba_asn_find_type_value (cms->signer_info.image, nsiginfo, 1,
oid_messageDigest, DIM(oid_messageDigest)))
return KSBA_Duplicate_Value;
/* the value is is a SET OF OCTECT STRING but the set must have
excactly one OCTECT STRING. (rfc2630 11.2) */
if ( !(n->type == TYPE_SET_OF && n->down
&& n->down->type == TYPE_OCTET_STRING && !n->down->right))
return KSBA_Invalid_CMS_Object;
n = n->down;
if (n->off == -1)
return KSBA_Bug;
*r_digest_len = n->len;
*r_digest = xtrymalloc (n->len);
if (!*r_digest)
return KSBA_Out_Of_Core;
memcpy (*r_digest, cms->signer_info.image + n->off + n->nhdr, n->len);
return 0;
}
/**
* ksba_cms_get_sig_val:
* @cms: CMS object
* @idx: index of signer
*
* Return the actual signature of signer @idx in a format suitable to
* be used as input to Libgcrypt's verification function. The caller
* must free the returned string.
*
* Return value: NULL or a string with a S-Exp.
**/
char *
ksba_cms_get_sig_val (KsbaCMS cms, int idx)
{
AsnNode n, n2;
KsbaError err;
char *string;
if (!cms)
return NULL;
if (!cms->signer_info.root)
return NULL;
if (idx)
return NULL; /* only one signer for now */
n = _ksba_asn_find_node (cms->signer_info.root,
"SignerInfos..signatureAlgorithm");
if (!n)
return NULL;
if (n->off == -1)
{
fputs ("ksba_cms_get_sig_val problem at node:\n", stderr);
_ksba_asn_node_dump_all (n, stderr);
return NULL;
}
n2 = n->right; /* point to the actual value */
err = _ksba_sigval_to_sexp (cms->signer_info.image + n->off,
n->nhdr + n->len
+ ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)),
&string);
if (err)
return NULL;
return string;
}
/* Provide a hash function so that we are able to hash the data */
void
ksba_cms_set_hash_function (KsbaCMS cms,
void (*hash_fnc)(void *, const void *, size_t),
void *hash_fnc_arg)
{
if (cms)
{
cms->hash_fnc = hash_fnc;
cms->hash_fnc_arg = hash_fnc_arg;
}
}
/* hash the signed attributes of the given signer */
KsbaError
ksba_cms_hash_signed_attrs (KsbaCMS cms, int idx)
{
AsnNode n;
if (!cms)
return KSBA_Invalid_Value;
if (!cms->hash_fnc)
return KSBA_Missing_Action;
if (idx)
return -1;
n = _ksba_asn_find_node (cms->signer_info.root,
"SignerInfos..signedAttrs");
if (!n || n->off == -1)
return KSBA_No_Value;
/* We don't hash the implicit tag [0] but a SET tag */
cms->hash_fnc (cms->hash_fnc_arg, "\x31", 1);
cms->hash_fnc (cms->hash_fnc_arg,
cms->signer_info.image + n->off + 1, n->nhdr + n->len - 1);
return 0;
}
/*
Code to create CMS structures
*/
-/* Add another issuer/serial to the sid list */
+/**
+ * ksba_cms_set_content_type:
+ * @cms: A CMS object
+ * @what: 0 for content type, 1 for inner content type
+ * @type: Tyep constant
+ *
+ * Set the content type used for build operations. This should be the
+ * first operation before starting to create a CMS message.
+ *
+ * Return value: 0 on success or an error code
+ **/
+KsbaError
+ksba_cms_set_content_type (KsbaCMS cms, int what, KsbaContentType type)
+{
+ int i;
+ char *oid;
+
+ if (!cms || what < 0 || what > 1 )
+ return KSBA_Invalid_Value;
+
+ for (i=0; content_handlers[i].oid; i++)
+ {
+ if (content_handlers[i].ct == type)
+ break;
+ }
+ if (!content_handlers[i].oid)
+ return KSBA_Unknown_CMS_Object;
+ if (!content_handlers[i].build_handler)
+ return KSBA_Unsupported_CMS_Object;
+ oid = xtrystrdup (content_handlers[i].oid);
+ if (!oid)
+ return KSBA_Out_Of_Core;
+
+ if (!what)
+ {
+ cms->content.oid = oid;
+ cms->content.ct = content_handlers[i].ct;
+ cms->content.handler = content_handlers[i].build_handler;
+ }
+ else
+ {
+ cms->encap_cont_type = oid;
+ }
+
+ return 0;
+}
+
+
+/**
+ * ksba_cms_add_digest_algo:
+ * @cms: A CMS object
+ * @oid: A stringified object OID describing the hash algorithm
+ *
+ * Set the algorithm to be used for crerating the hash. Note, that we
+ * currently can't do a per-signer hash.
+ *
+ * Return value: o on success or an error code
+ **/
+KsbaError
+ksba_cms_add_digest_algo (KsbaCMS cms, const char *oid)
+{
+ struct oidlist_s *ol;
+
+ if (!cms || !oid)
+ return KSBA_Invalid_Value;
+
+ ol = xtrymalloc (sizeof *ol);
+ if (!ol)
+ return KSBA_Out_Of_Core;
+
+ ol->oid = xtrystrdup (oid);
+ if (!ol->oid)
+ {
+ xfree (ol);
+ return KSBA_Out_Of_Core;
+ }
+ ol->next = cms->digest_algos;
+ cms->digest_algos = ol;
+ return 0;
+}
+
+
+/**
+ * ksba_cms_add_signer:
+ * @cms: A CMS object
+ * @cert: A certificate used to describe the signer.
+ *
+ * This functions starts assembly of a new signed data content or adds
+ * another signer to the list of signers.
+ *
+ * Note: after successful completion of this function ownership of
+ * @cert is transferred to @cms. The caller should not continue to
+ * use cert. Fixme: We should use reference counting instead.
+ *
+ * Return value: 0 on success or an error code.
+ **/
+KsbaError
+ksba_cms_add_signer (KsbaCMS cms, KsbaCert cert)
+{
+ struct certlist_s *cl;
+
+ if (!cms)
+ return KSBA_Invalid_Value;
+
+ cl = xtrycalloc (1,sizeof *cl);
+ if (!cl)
+ return KSBA_Out_Of_Core;
+
+ cl->cert = cert;
+ cl->next = cms->cert_list;
+ cms->cert_list = cl;
+ return 0;
+}
+
+
+/* Add the issuer/serial from the cert to the sid list */
static KsbaError
-add_issuer_serial (KsbaCMS cms,
- const char *issuer, const unsigned char *serial)
+add_issuer_serial (KsbaCMS cms, KsbaCert cert)
{
KsbaError err;
- AsnNode n;
+ AsnNode dst, src;
if (!cms)
return KSBA_Invalid_Value;
if (!cms->signer_info.root)
return KSBA_Conflict;
-#if 0
- if (r_issuer)
- {
- n = _ksba_asn_find_node (cms->signer_info.root,
- "SignerInfos..sid.issuerAndSerialNumber.issuer");
- if (!n || !n->down)
- return KSBA_No_Value;
- n = n->down; /* dereference the choice node */
-
- if (n->off == -1)
- {
- fputs ("get_issuer problem at node:\n", stderr);
- _ksba_asn_node_dump_all (n, stderr);
- return KSBA_General_Error;
- }
- err = _ksba_dn_to_str (cms->signer_info.image, n, r_issuer);
- if (err)
- return err;
- }
- if (r_serial)
- {
- unsigned char *p;
-
- /* fixme: we do not release the r_issuer stuff on error */
- n = _ksba_asn_find_node (cms->signer_info.root,
+ src = _ksba_asn_find_node (cert->root,
+ "Certificate.tbsCertificate.serialNumber");
+ dst = _ksba_asn_find_node (cms->signer_info.root,
"SignerInfos..sid.issuerAndSerialNumber.serialNumber");
- if (!n)
- return KSBA_No_Value;
-
- if (n->off == -1)
- {
- fputs ("get_serial problem at node:\n", stderr);
- _ksba_asn_node_dump_all (n, stderr);
- return KSBA_General_Error;
- }
+ err = _ksba_der_copy_tree (dst, src, cert->image);
+ if (err)
+ return err;
- p = xtrymalloc (n->len + 4);
- if (!p)
- return KSBA_Out_Of_Core;
+ src = _ksba_asn_find_node (cert->root,
+ "Certificate.tbsCertificate.issuer");
+ dst = _ksba_asn_find_node (cms->signer_info.root,
+ "SignerInfos..sid.issuerAndSerialNumber.issuer");
+ err = _ksba_der_copy_tree (dst, src, cert->image);
+ if (err)
+ return err;
- p[0] = n->len >> 24;
- p[1] = n->len >> 16;
- p[2] = n->len >> 8;
- p[3] = n->len;
- memcpy (p+4, cms->signer_info.image + n->off + n->nhdr, n->len);
- *r_serial = p;
- }
-#endif
+
+ return 0;
+}
+
+
+/**
+ * ksba_cms_set_message_digest:
+ * @cms: A CMS object
+ * @idx: The index of the signer
+ * @digest: a message digest
+ * @digest_len: the length of the message digest
+ *
+ * Set a message digest into the signedAttributes of the signer with
+ * the index IDX. The index of a signer is determined by the sequence
+ * of ksba_cms_add_signer() calls; the first signer has the index 0.
+ * This function is to be used when the hash value of the data has
+ * been calculated and before the create function requests the sign
+ * operation.
+ *
+ * Return value: 0 on success or an error code
+ **/
+KsbaError
+ksba_cms_set_message_digest (KsbaCMS cms, int idx,
+ const char *digest, size_t digest_len)
+{
+ struct certlist_s *cl;
+
+ if (!cms || !digest)
+ return KSBA_Invalid_Value;
+ if (!digest_len || digest_len > DIM(cl->msg_digest))
+ return KSBA_Invalid_Value;
+ if (idx < 0)
+ return KSBA_Invalid_Index;
+
+ for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--)
+ ;
+ if (!cl)
+ return KSBA_Invalid_Index; /* no certificate to store it */
+ cl->msg_digest_len = digest_len;
+ memcpy (cl->msg_digest, digest, digest_len);
return 0;
}
/*
- Content handler
+ Content handler for parsing messages
*/
+
static KsbaError
-ct_signed_data (KsbaCMS cms)
+ct_parse_signed_data (KsbaCMS cms)
{
enum {
sSTART,
sGOT_HASH,
sIN_DATA,
sERROR
} state = sERROR;
KsbaStopReason stop_reason = cms->stop_reason;
KsbaError err = 0;
cms->stop_reason = KSBA_SR_RUNNING;
/* Calculate state from last reason and do some checks */
if (stop_reason == KSBA_SR_GOT_CONTENT)
{
state = sSTART;
}
else if (stop_reason == KSBA_SR_NEED_HASH)
{
state = sGOT_HASH;
}
else if (stop_reason == KSBA_SR_BEGIN_DATA)
{
if (!cms->hash_fnc)
err = KSBA_Missing_Action;
else
state = sIN_DATA;
}
else if (stop_reason == KSBA_SR_END_DATA)
{
state = sGOT_HASH;
}
else if (stop_reason == KSBA_SR_RUNNING)
err = KSBA_Invalid_State;
else if (stop_reason)
err = KSBA_Bug;
if (err)
return err;
/* Do the action */
if (state == sSTART)
err = _ksba_cms_parse_signed_data_part_1 (cms);
else if (state == sGOT_HASH)
err = _ksba_cms_parse_signed_data_part_2 (cms);
else if (state == sIN_DATA)
; /* start a parser part which does the hash job */
+ else
+ err = KSBA_Invalid_State;
if (err)
return err;
/* Calculate new stop reason */
if (state == sSTART)
{
if (cms->detached_signature && !cms->data.digest)
{ /* We use this stop reason to inform the caller about a
detached signatures. Actually there is no need for him
to hash the data now, he can do this also later. */
stop_reason = KSBA_SR_NEED_HASH;
}
else
{ /* The user must now provide a hash function so that we can
hash the data in the next round */
stop_reason = KSBA_SR_BEGIN_DATA;
}
}
else if (state == sIN_DATA)
stop_reason = KSBA_SR_END_DATA;
else if (state ==sGOT_HASH)
stop_reason = KSBA_SR_READY;
cms->stop_reason = stop_reason;
return 0;
}
static KsbaError
-ct_enveloped_data (KsbaCMS cms)
+ct_parse_enveloped_data (KsbaCMS cms)
+{
+ return KSBA_Not_Implemented;
+}
+
+
+static KsbaError
+ct_parse_digested_data (KsbaCMS cms)
+{
+ return KSBA_Not_Implemented;
+}
+
+
+static KsbaError
+ct_parse_encrypted_data (KsbaCMS cms)
+{
+ return KSBA_Not_Implemented;
+}
+
+
+
+/*
+ Content handlers for building messages
+*/
+
+
+/* write everything up to the encapsulated data content type */
+static KsbaError
+build_signed_data_header (KsbaCMS cms)
+{
+ KsbaError err;
+ char *buf;
+ const char *s;
+ size_t len;
+ int i;
+
+ /* Write the outer contentInfo */
+ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
+ if (err)
+ return err;
+ err = ksba_oid_from_str (cms->content.oid, &buf, &len);
+ if (err)
+ return err;
+ err = _ksba_ber_write_tl (cms->writer,
+ TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len);
+ if (!err)
+ err = ksba_writer_write (cms->writer, buf, len);
+ xfree (buf);
+ if (err)
+ return err;
+
+ err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0);
+ if (err)
+ return err;
+
+
+ /* figure out the CMSVersion to be used */
+ if (1 /* fixme: have_attribute_certificates
+ || encapsulated_content != data
+ || any_signer_info_is_version_3*/ )
+ s = "\x03";
+ else
+ s = "\x01";
+ err = _ksba_ber_write_tl (cms->writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1);
+ if (err)
+ return err;
+ err = ksba_writer_write (cms->writer, s, 1);
+ if (err)
+ return err;
+
+ /* SET OF DigestAlgorithmIdentifier */
+ for (i=0; (s = ksba_cms_get_digest_algo_list (cms, i)); i++)
+ {
+ err = _ksba_der_write_algorithm_identifier (cms->writer, s);
+ if (err)
+ return err;
+ }
+
+ /* Write the (inner) encapsulatedContentInfo */
+ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
+ if (err)
+ return err;
+ err = ksba_oid_from_str (cms->encap_cont_type, &buf, &len);
+ if (err)
+ return err;
+ err = _ksba_ber_write_tl (cms->writer,
+ TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len);
+ if (!err)
+ err = ksba_writer_write (cms->writer, buf, len);
+ xfree (buf);
+ if (err)
+ return err;
+ err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0);
+ if (err)
+ return err;
+
+ return err;
+}
+
+/* Write the END of data NULL tag and everything we can write before
+ the user can calculate the signature */
+static KsbaError
+build_signed_data_attributes (KsbaCMS cms)
+{
+ KsbaError err;
+
+
+ /* The NULL tag to end the data */
+ err = _ksba_ber_write_tl (cms->writer, TYPE_NULL, CLASS_UNIVERSAL, 0, 0);
+ if (err)
+ return err;
+
+ /* FIXME: Write optional certificates */
+
+ /* FIXME: Write the optional CRLs */
+
+ /* Now we have to prepare the signer info */
+
+
+
+
+ return 0;
+}
+
+
+static KsbaError
+ct_build_signed_data (KsbaCMS cms)
+{
+ enum {
+ sSTART,
+ sDATAREADY,
+ sERROR
+ } state = sERROR;
+ KsbaStopReason stop_reason = cms->stop_reason;
+ KsbaError err = 0;
+
+ cms->stop_reason = KSBA_SR_RUNNING;
+
+ /* Calculate state from last reason and do some checks */
+ if (stop_reason == KSBA_SR_GOT_CONTENT)
+ {
+ state = sSTART;
+ }
+ else if (stop_reason == KSBA_SR_BEGIN_DATA)
+ {
+ /* fixme: check that the message digest has been set */
+ state = sDATAREADY;
+ }
+ else if (stop_reason == KSBA_SR_RUNNING)
+ err = KSBA_Invalid_State;
+ else if (stop_reason)
+ err = KSBA_Bug;
+
+ if (err)
+ return err;
+
+ /* Do the action */
+ if (state == sSTART)
+ err = build_signed_data_header (cms);
+ else if (state == sDATAREADY)
+ err = build_signed_data_attributes (cms);
+ else
+ err = KSBA_Invalid_State;
+
+ if (err)
+ return err;
+
+ /* Calculate new stop reason */
+ if (state == sSTART)
+ {
+ /* user should write the data */
+ stop_reason = KSBA_SR_BEGIN_DATA;
+ }
+ else if (state == sDATAREADY)
+ stop_reason = KSBA_SR_NEED_SIG;
+
+ cms->stop_reason = stop_reason;
+ return 0;
+}
+
+
+static KsbaError
+ct_build_enveloped_data (KsbaCMS cms)
{
return KSBA_Not_Implemented;
}
static KsbaError
-ct_digested_data (KsbaCMS cms)
+ct_build_digested_data (KsbaCMS cms)
{
return KSBA_Not_Implemented;
}
static KsbaError
-ct_encrypted_data (KsbaCMS cms)
+ct_build_encrypted_data (KsbaCMS cms)
{
return KSBA_Not_Implemented;
}
diff --git a/src/cms.h b/src/cms.h
index 272a2ce..2138649 100644
--- a/src/cms.h
+++ b/src/cms.h
@@ -1,99 +1,101 @@
/* cms.h - Internal definitions for the CMS functions
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifndef CMS_H
#define CMS_H 1
#include "ksba.h"
#ifndef HAVE_TYPEDEFD_ASNNODE
typedef struct asn_node_struct *AsnNode; /* FIXME: should not go here */
#define HAVE_TYPEDEFD_ASNNODE
#endif
struct oidlist_s {
struct oidlist_s *next;
char *oid;
};
struct certlist_s {
struct certlist_s *next;
KsbaCert cert;
+ int msg_digest_len; /* used length of .. */
+ char msg_digest[32]; /* enough space to store a SHA-256 hash */
};
struct ksba_cms_s {
KsbaError last_error;
KsbaReader reader;
KsbaWriter writer;
void (*hash_fnc)(void *, const void *, size_t);
void *hash_fnc_arg;
KsbaStopReason stop_reason;
struct {
char *oid;
unsigned long length;
int ndef;
KsbaContentType ct;
KsbaError (*handler)(KsbaCMS);
} content;
struct {
unsigned char *digest;
int digest_len;
} data;
int cms_version;
struct oidlist_s *digest_algos;
struct certlist_s *cert_list;
char *encap_cont_type; /* EncapsulatedContentInfo.contentType as string */
int detached_signature; /* no actual data */
struct {
AsnNode root; /* root of the tree with the values */
unsigned char *image;
size_t imagelen;
struct {
char *digest_algo;
} cache;
} signer_info;
};
/*-- cms.c --*/
/*-- cms-parser.c --*/
KsbaError _ksba_cms_parse_content_info (KsbaCMS cms);
KsbaError _ksba_cms_parse_signed_data_part_1 (KsbaCMS cms);
KsbaError _ksba_cms_parse_signed_data_part_2 (KsbaCMS cms);
#endif /*CMS_H*/
diff --git a/src/ber-decoder.c b/src/der-encoder.c
similarity index 75%
copy from src/ber-decoder.c
copy to src/der-encoder.c
index 4774a7d..2377f4c 100644
--- a/src/ber-decoder.c
+++ b/src/der-encoder.c
@@ -1,1182 +1,1070 @@
-/* ber-decoder.c - Basic Encoding Rules Decoder
+/* der-decoder.c - Distinguished Encoding Rules Encoder
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "ksba.h"
#include "asn1-func.h"
-#include "ber-decoder.h"
#include "ber-help.h"
+#include "der-encoder.h"
-struct decoder_state_item_s {
- AsnNode node;
- int went_up;
- int in_seq_of;
- int in_any; /* actually in a constructed any */
- int again;
- int next_tag;
- int length; /* length of the value */
- int ndef_length; /* the length is of indefinite length */
- int nread; /* number of value bytes processed */
-};
-typedef struct decoder_state_item_s DECODER_STATE_ITEM;
-
-struct decoder_state_s {
- DECODER_STATE_ITEM cur; /* current state */
- int stacksize;
- int idx;
- DECODER_STATE_ITEM stack[1];
-};
-typedef struct decoder_state_s *DECODER_STATE;
-
-
-struct ber_decoder_s {
+struct der_encoder_s {
AsnNode module; /* the ASN.1 structure */
- KsbaReader reader;
+ KsbaWriter writer;
const char *last_errdesc; /* string with the error description */
- int non_der; /* set if the encoding is not DER conform */
AsnNode root; /* of the expanded parse tree */
- DECODER_STATE ds;
- int bypass;
- int honor_module_end;
int debug;
- int use_image;
- struct {
- unsigned char *buf;
- size_t used;
- size_t length;
- } image;
- struct {
- int primitive; /* current value is a primitive one */
- int length; /* length of the primitive one */
- int nhdr; /* length of the header */
- int tag;
- int is_endtag;
- AsnNode node; /* NULL or matching node */
- } val;
};
-
-
-
-static DECODER_STATE
-new_decoder_state (void)
-{
- DECODER_STATE ds;
-
- ds = xmalloc (sizeof (*ds) + 99*sizeof(DECODER_STATE_ITEM));
- ds->stacksize = 100;
- ds->idx = 0;
- ds->cur.node = NULL;
- ds->cur.in_seq_of = 0;
- ds->cur.again = 0;
- ds->cur.next_tag = 0;
- ds->cur.went_up = 0;
- ds->cur.length = 0;
- ds->cur.ndef_length = 1;
- ds->cur.nread = 0;
- return ds;
-}
-
-static void
-release_decoder_state (DECODER_STATE ds)
-{
- xfree (ds);
-}
-
-static void
-dump_decoder_state (DECODER_STATE ds)
-{
- int i;
-
- for (i=0; i < ds->idx; i++)
- {
- fprintf (stdout," ds stack[%d] (", i);
- if (ds->stack[i].node)
- _ksba_asn_node_dump (ds->stack[i].node, stdout);
- else
- printf ("Null");
- fprintf (stdout,") %s%d (%d)%s\n",
- ds->stack[i].ndef_length? "ndef ":"",
- ds->stack[i].length,
- ds->stack[i].nread,
- ds->stack[i].in_seq_of? " in_seq_of":"");
- }
-}
-
-/* Push ITEM onto the stack */
-static void
-push_decoder_state (DECODER_STATE ds)
-{
- if (ds->idx >= ds->stacksize)
- {
- fprintf (stderr, "ERROR: decoder stack overflow!\n");
- abort ();
- }
- ds->stack[ds->idx++] = ds->cur;
-}
-
-static void
-pop_decoder_state (DECODER_STATE ds)
-{
- if (!ds->idx)
- {
- fprintf (stderr, "ERROR: decoder stack underflow!\n");
- abort ();
- }
- ds->cur = ds->stack[--ds->idx];
-}
-
-
-
+#if 0
static int
-set_error (BerDecoder d, AsnNode node, const char *text)
+set_error (DerEncoder d, AsnNode node, const char *text)
{
- fprintf (stderr,"ber-decoder: node `%s': %s\n",
+ fprintf (stderr,"der-encoder: node `%s': %s\n",
node? node->name:"?", text);
d->last_errdesc = text;
- return KSBA_BER_Error;
-}
-
-
-static int
-eof_or_error (BerDecoder d, int premature)
-{
- if (ksba_reader_error (d->reader))
- {
- set_error (d, NULL, "read error");
- return KSBA_Read_Error;
- }
- if (premature)
- return set_error (d, NULL, "premature EOF");
- return -1;
-}
-
-static int
-is_primitive_type (node_type_t type)
-{
- switch (type)
- {
- case TYPE_BOOLEAN:
- case TYPE_INTEGER:
- case TYPE_BIT_STRING:
- case TYPE_OCTET_STRING:
- case TYPE_NULL:
- case TYPE_OBJECT_ID:
- case TYPE_OBJECT_DESCRIPTOR:
- case TYPE_REAL:
- case TYPE_ENUMERATED:
- case TYPE_UTF8_STRING:
- case TYPE_REALTIVE_OID:
- case TYPE_NUMERIC_STRING:
- case TYPE_PRINTABLE_STRING:
- case TYPE_TELETEX_STRING:
- case TYPE_VIDEOTEX_STRING:
- case TYPE_IA5_STRING:
- case TYPE_UTC_TIME:
- case TYPE_GENERALIZED_TIME:
- case TYPE_GRAPHIC_STRING:
- case TYPE_VISIBLE_STRING:
- case TYPE_GENERAL_STRING:
- case TYPE_UNIVERSAL_STRING:
- case TYPE_CHARACTER_STRING:
- case TYPE_BMP_STRING:
- return 1;
- default:
- return 0;
- }
-}
-
-static const char *
-universal_tag_name (unsigned long no)
-{
- static const char *names[31] = {
- "[End Tag]",
- "BOOLEAN",
- "INTEGER",
- "BIT STRING",
- "OCTECT STRING",
- "NULL",
- "OBJECT IDENTIFIER",
- "ObjectDescriptor",
- "EXTERNAL",
- "REAL",
- "ENUMERATED",
- "EMBEDDED PDV",
- "UTF8String",
- "RELATIVE-OID",
- "[UNIVERSAL 14]",
- "[UNIVERSAL 15]",
- "SEQUENCE",
- "SET",
- "NumericString",
- "PrintableString",
- "TeletexString",
- "VideotexString",
- "IA5String",
- "UTCTime",
- "GeneralizedTime",
- "GraphicString",
- "VisibleString",
- "GeneralString",
- "UniversalString",
- "CHARACTER STRING",
- "BMPString"
- };
-
- return no < DIM(names)? names[no]:NULL;
+ return KSBA_Encoding_Error;
}
+#endif
-
-static void
-dump_tlv (const struct tag_info *ti, FILE *fp)
-{
- const char *tagname = NULL;
-
- if (ti->class == CLASS_UNIVERSAL)
- tagname = universal_tag_name (ti->tag);
-
- if (tagname)
- fputs (tagname, fp);
- else
- fprintf (fp, "[%s %lu]",
- ti->class == CLASS_UNIVERSAL? "UNIVERSAL" :
- ti->class == CLASS_APPLICATION? "APPLICATION" :
- ti->class == CLASS_CONTEXT? "CONTEXT-SPECIFIC" : "PRIVATE",
- ti->tag);
- fprintf (fp, " %c hdr=%u len=", ti->is_constructed? 'c':'p', ti->nhdr);
- if (ti->ndef)
- fputs ("ndef", fp);
- else
- fprintf (fp, "%lu", ti->length);
-}
-
-
-static void
-clear_help_flags (AsnNode node)
+/* To be useful for the DER encoder we store all data direct as the
+ binary image, so we use the VALTYPE_MEM */
+static KsbaError
+store_value (AsnNode node, const void *buffer, size_t length)
{
- AsnNode p;
-
- for (p=node; p; p = _ksba_asn_walk_tree (node, p))
- {
- if (p->type == TYPE_TAG)
- {
- p->flags.tag_seen = 0;
- }
- p->flags.skip_this = 0;
- }
-
+ _ksba_asn_set_value (node, VALTYPE_MEM, buffer, length);
+ return 0;
}
static void
-prepare_copied_tree (AsnNode node)
+clear_value (AsnNode node)
{
- AsnNode p;
-
- clear_help_flags (node);
- for (p=node; p; p = _ksba_asn_walk_tree (node, p))
- p->off = -1;
-
+ _ksba_asn_set_value (node, VALTYPE_NULL, NULL, 0);
}
-static void
-fixup_type_any (AsnNode node)
-{
- AsnNode p;
-
- for (p=node; p; p = _ksba_asn_walk_tree (node, p))
- {
- if (p->type == TYPE_ANY && p->off != -1)
- p->type = p->actual_type;
- }
-}
-BerDecoder
-_ksba_ber_decoder_new (void)
+DerEncoder
+_ksba_der_encoder_new (void)
{
- BerDecoder d;
+ DerEncoder d;
d = xtrycalloc (1, sizeof *d);
if (!d)
return NULL;
return d;
}
void
-_ksba_ber_decoder_release (BerDecoder d)
+_ksba_der_encoder_release (DerEncoder d)
{
xfree (d);
}
+
/**
- * _ksba_ber_decoder_set_module:
+ * _ksba_der_encoder_set_module:
* @d: Decoder object
* @module: ASN.1 Parse tree
*
* Initialize the decoder with the ASN.1 module. Note, that this is a
* shallow copy of the module. FIXME: What about ref-counting of
* AsnNodes?
*
* Return value: 0 on success or an error code
**/
KsbaError
-_ksba_ber_decoder_set_module (BerDecoder d, KsbaAsnTree module)
+_ksba_der_encoder_set_module (DerEncoder d, KsbaAsnTree module)
{
if (!d || !module)
return KSBA_Invalid_Value;
if (d->module)
return KSBA_Conflict; /* module already set */
d->module = module->parse_tree;
return 0;
}
KsbaError
-_ksba_ber_decoder_set_reader (BerDecoder d, KsbaReader r)
+_ksba_der_encoder_set_writer (DerEncoder d, KsbaWriter w)
{
- if (!d || !r)
+ if (!d || !w)
return KSBA_Invalid_Value;
- if (d->reader)
+ if (d->writer)
return KSBA_Conflict; /* reader already set */
- d->reader = r;
+ d->writer = w;
return 0;
}
/**********************************************
- *********** decoding machinery *************
+ *********** encoding machinery *************
**********************************************/
-
+#if 0
static int
-read_byte (KsbaReader reader)
+write_byte (KsbaWriter writer, int c)
{
unsigned char buf;
- size_t nread;
- int rc;
- do
- rc = ksba_reader_read (reader, &buf, 1, &nread);
- while (!rc && !nread);
- return rc? -1: buf;
+ buf = c;
+ return ksba_writer_write (writer, &buf, 1);
}
/* read COUNT bytes into buffer. buffer may be NULL to skip over
COUNT bytes. Return 0 on success */
static int
read_buffer (KsbaReader reader, char *buffer, size_t count)
{
size_t nread;
if (buffer)
{
while (count)
{
if (ksba_reader_read (reader, buffer, count, &nread))
return -1;
buffer += nread;
count -= nread;
}
}
else
{
char dummy[256];
size_t n;
while (count)
{
n = count > DIM(dummy) ? DIM(dummy): count;
if (ksba_reader_read (reader, dummy, n, &nread))
return -1;
count -= nread;
}
}
return 0;
}
/* Return 0 for no match, 1 for a match and 2 for an ANY match of an
constructed type */
static int
cmp_tag (AsnNode node, const struct tag_info *ti)
{
if (node->flags.class != ti->class)
return 0;
if (node->type == TYPE_TAG)
{
return_val_if_fail (node->valuetype == VALTYPE_ULONG, 0);
return node->value.v_ulong == ti->tag;
}
if (node->type == ti->tag)
return 1;
if (ti->class == CLASS_UNIVERSAL)
{
if (node->type == TYPE_SEQUENCE_OF && ti->tag == TYPE_SEQUENCE)
return 1;
if (node->type == TYPE_SET_OF && ti->tag == TYPE_SET)
return 1;
if (node->type == TYPE_ANY)
return is_primitive_type (ti->tag)? 1:2;
}
return 0;
}
/* Find the node in the tree ROOT corresponding to TI and return that
node. Returns NULL if the node was not found */
static AsnNode
find_anchor_node (AsnNode root, const struct tag_info *ti)
{
AsnNode node = root;
while (node)
{
if (cmp_tag (node, ti))
{
return node; /* found */
}
if (node->down)
node = node->down;
else if (node == root)
return NULL; /* not found */
else if (node->right)
node = node->right;
else
{ /* go up and right */
do
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left;
if (!node || node == root)
return NULL; /* back at the root -> not found */
}
while (!node->right);
node = node->right;
}
}
return NULL;
}
static int
match_der (AsnNode root, const struct tag_info *ti,
DECODER_STATE ds, AsnNode *retnode, int debug)
{
int rc;
AsnNode node;
*retnode = NULL;
node = ds->cur.node;
if (!node)
{
if (debug)
puts (" looking for anchor");
node = find_anchor_node (root, ti);
if (!node)
fputs (" anchor node not found\n", stdout);
}
else if (ds->cur.again)
{
if (debug)
puts (" doing last again");
ds->cur.again = 0;
}
else if (is_primitive_type (node->type) || node->type == TYPE_ANY
|| node->type == TYPE_SIZE || node->type == TYPE_DEFAULT )
{
if (debug)
puts (" primitive type - get next");
if (node->right)
node = node->right;
else if (!node->flags.in_choice)
node = NULL;
else /* in choice */
{
if (debug)
puts (" going up after choice - get next");
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (node)
node = node->right;
}
}
else if (node->type == TYPE_SEQUENCE_OF || node->type == TYPE_SET_OF)
{
if (debug)
{
printf (" prepare for seq/set_of (%d %d) ",
ds->cur.length, ds->cur.nread);
printf (" cur: ("); _ksba_asn_node_dump (node, stdout);
printf (")\n");
if (ds->cur.node->flags.in_array)
puts (" This is in an arrat!");
if (ds->cur.went_up)
puts (" And we going up!");
}
if ((ds->cur.went_up && !ds->cur.node->flags.in_array) ||
(ds->idx && ds->cur.nread >= ds->stack[ds->idx-1].length))
{
if (debug)
printf (" advancing\n");
if (node->right)
node = node->right;
else
{
for (;;)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (!node)
break;
if (node->right)
{
node = node->right;
break;
}
}
}
}
else if (ds->cur.node->flags.in_array
&& ds->cur.went_up)
{
if (debug)
puts (" Reiterating");
node = _ksba_asn_insert_copy (node);
if (node)
prepare_copied_tree (node);
}
else
node = node->down;
}
else /* constructed */
{
if (debug)
{
printf (" prepare for constructed (%d %d) ",
ds->cur.length, ds->cur.nread);
printf (" cur: ("); _ksba_asn_node_dump (node, stdout);
printf (")\n");
if (ds->cur.node->flags.in_array)
puts (" This is in an array!");
if (ds->cur.went_up)
puts (" And we going up!");
}
ds->cur.in_seq_of = 0;
if (ds->cur.node->flags.in_array
&& ds->cur.went_up)
{
if (debug)
puts (" Reiterating this");
node = _ksba_asn_insert_copy (node);
if (node)
prepare_copied_tree (node);
}
else if (ds->cur.went_up || ds->cur.next_tag || ds->cur.node->flags.skip_this)
{
if (node->right)
node = node->right;
else
{
for (;;)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (!node)
break;
if (node->right)
{
node = node->right;
break;
}
}
}
}
else
node = node->down;
}
if (!node)
return -1;
ds->cur.node = node;
ds->cur.went_up = 0;
ds->cur.next_tag = 0;
if (debug)
{
printf (" Expect ("); _ksba_asn_node_dump (node, stdout); printf (")\n");
}
if (node->flags.skip_this)
return 1;
if (node->type == TYPE_SIZE)
{
if (debug)
printf (" skipping size tag\n");
return 1;
}
if (node->type == TYPE_DEFAULT)
{
if (debug)
printf (" skipping default tag\n");
return 1;
}
if (node->flags.is_implicit)
{
if (debug)
printf (" dummy accept for implicit tag\n");
return 1; /* again */
}
if ( (rc=cmp_tag (node, ti)))
{
*retnode = node;
return rc==2? 4:3;
}
if (node->type == TYPE_CHOICE)
{
if (debug)
printf (" testing choice...\n");
for (node = node->down; node; node = node->right)
{
if (debug)
{
printf (" %s (", node->flags.skip_this? "skip":" cmp");
_ksba_asn_node_dump (node, stdout);
printf (")\n");
}
if (!node->flags.skip_this && cmp_tag (node, ti) == 1)
{
if (debug)
{
printf (" choice match <"); dump_tlv (ti, stdout);
printf (">\n");
}
/* mark the remaining as done */
for (node=node->right; node; node = node->right)
node->flags.skip_this = 1;
return 1;
}
node->flags.skip_this = 1;
}
node = ds->cur.node; /* reset */
}
if (node->flags.in_choice)
{
if (debug)
printf (" skipping non matching choice\n");
return 1;
}
if (node->flags.is_optional)
{
if (debug)
printf (" skipping optional element\n");
if (node->type == TYPE_TAG)
ds->cur.next_tag = 1;
return 1;
}
if (node->flags.has_default)
{
if (debug)
printf (" use default value\n");
if (node->type == TYPE_TAG)
ds->cur.next_tag = 1;
*retnode = node;
return 2;
}
return -1;
}
static KsbaError
-decoder_init (BerDecoder d, const char *start_name)
+decoder_init (DerEncoder d, const char *start_name)
{
d->ds = new_decoder_state ();
d->root = _ksba_asn_expand_tree (d->module, start_name);
clear_help_flags (d->root);
d->bypass = 0;
if (d->debug)
printf ("DECODER_INIT for `%s'\n", start_name? start_name: "[root]");
return 0;
}
static void
-decoder_deinit (BerDecoder d)
+decoder_deinit (DerEncoder d)
{
release_decoder_state (d->ds);
d->ds = NULL;
d->val.node = NULL;
if (d->debug)
printf ("DECODER_DEINIT\n");
}
static KsbaError
-decoder_next (BerDecoder d)
+decoder_next (DerEncoder d)
{
struct tag_info ti;
AsnNode node;
KsbaError err;
DECODER_STATE ds = d->ds;
int debug = d->debug;
err = _ksba_ber_read_tl (d->reader, &ti);
if (err)
{
return err;
}
if (debug)
{
printf ("ReadTLV <"); dump_tlv (&ti, stdout); printf (">\n");
}
if (d->use_image)
{
if (!d->image.buf)
{
/* we need some extra bytes to store the stuff we read ahead
at the end of the module which is later pushed back */
d->image.length = ti.length + 100;
d->image.used = 0;
d->image.buf = xtrymalloc (d->image.length);
if (!d->image.buf)
return KSBA_Out_Of_Core;
}
if (ti.nhdr + d->image.used >= d->image.length)
return set_error (d, NULL, "image buffer too short to store the tag");
memcpy (d->image.buf + d->image.used, ti.buf, ti.nhdr);
d->image.used += ti.nhdr;
}
if (!d->bypass)
{
int again, endtag;
do
{
again = endtag = 0;
switch ( ds->cur.in_any? 4
: (ti.class == CLASS_UNIVERSAL && !ti.tag)? (endtag=1,5)
: match_der (d->root, &ti, ds, &node, debug))
{
case -1:
if (debug)
{
printf (" FAIL <"); dump_tlv (&ti, stdout); printf (">\n");
}
if (d->honor_module_end)
{
/* We must push back the stuff we already read */
ksba_reader_unread (d->reader, ti.buf, ti.nhdr);
return -1;
}
else
d->bypass = 1;
break;
case 0:
if (debug)
puts (" End of description");
d->bypass = 1;
break;
case 1: /* again */
if (debug)
printf (" Again\n");
again = 1;
break;
case 2: /* use default value + again*/
if (debug)
printf (" Using default\n");
again = 1;
break;
case 4: /* match of ANY on a constructed type */
if (debug)
printf (" ANY");
ds->cur.in_any = 1;
case 3: /* match */
case 5: /* end tag */
if (debug)
{
printf (" Match <"); dump_tlv (&ti, stdout); printf (">\n");
if (ti.tag == TYPE_OCTET_STRING && ti.length == 64)
printf (" DEBUG POINT\n");
}
/* increment by the header length */
ds->cur.nread += ti.nhdr;
if (!ti.is_constructed)
ds->cur.nread += ti.length;
ds->cur.went_up = 0;
do
{
if (debug)
printf (" (length %d nread %d) %s\n",
ds->idx? ds->stack[ds->idx-1].length:-1,
ds->cur.nread,
ti.is_constructed? "con":"pri");
if ( ds->idx
&& !ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
> ds->stack[ds->idx-1].length))
{
fprintf (stderr, " ERROR: object length field %d octects"
" too large\n",
ds->cur.nread > ds->cur.length);
ds->cur.nread = ds->cur.length;
}
if ( ds->idx
&& (endtag
|| (!ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
>= ds->stack[ds->idx-1].length))))
{
int n = ds->cur.nread;
pop_decoder_state (ds);
ds->cur.nread += n;
ds->cur.went_up++;
}
endtag = 0;
}
while ( ds->idx
&& !ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
>= ds->stack[ds->idx-1].length));
if (ti.is_constructed)
{
/* prepare for the next level */
ds->cur.length = ti.length;
ds->cur.ndef_length = ti.ndef;
push_decoder_state (ds);
ds->cur.length = 0;
ds->cur.ndef_length = 0;
ds->cur.nread = 0;
}
if (debug)
printf (" (length %d nread %d) end\n",
ds->idx? ds->stack[ds->idx-1].length:-1,
ds->cur.nread);
break;
default:
never_reached ();
abort ();
break;
}
}
while (again);
}
d->val.primitive = !ti.is_constructed;
d->val.length = ti.length;
d->val.nhdr = ti.nhdr;
d->val.tag = ti.tag; /* kludge to fix TYPE_ANY probs */
d->val.is_endtag = (ti.class == CLASS_UNIVERSAL && !ti.tag);
d->val.node = d->bypass? NULL : node;
if (debug)
dump_decoder_state (ds);
return 0;
}
static KsbaError
-decoder_skip (BerDecoder d)
+decoder_skip (DerEncoder d)
{
if (d->val.primitive)
{
if (read_buffer (d->reader, NULL, d->val.length))
return eof_or_error (d, 1);
}
return 0;
}
/* Calculate the distance between the 2 nodes */
static int
distance (AsnNode root, AsnNode node)
{
int n=0;
while (node && node != root)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left;
n++;
}
return n;
}
/**
- * _ksba_ber_decoder_dump:
+ * _ksba_der_encoder_dump:
* @d: Decoder object
*
* Dump a textual representation of the encoding to the given stream.
*
* Return value:
**/
KsbaError
-_ksba_ber_decoder_dump (BerDecoder d, FILE *fp)
+_ksba_der_encoder_dump (DerEncoder d, FILE *fp)
{
KsbaError err;
int depth = 0;
AsnNode node;
unsigned char *buf = NULL;
size_t buflen = 0;;
if (!d)
return KSBA_Invalid_Value;
- d->debug = !!getenv("DEBUG_BER_DECODER");
+ d->debug = !!getenv("DEBUG_DER_ENCODER");
d->use_image = 0;
d->image.buf = NULL;
err = decoder_init (d, NULL);
if (err)
return err;
while (!(err = decoder_next (d)))
{
node = d->val.node;
if (node)
depth = distance (d->root, node);
fprintf (fp, "%4lu %4u:%*s",
ksba_reader_tell (d->reader) - d->val.nhdr,
d->val.length,
depth*2, "");
if (node)
_ksba_asn_node_dump (node, fp);
else
fputs ("[No matching node]", fp);
if (node && d->val.primitive)
{
int i, n, c;
char *p;
if (!buf || buflen < d->val.length)
{
xfree (buf);
buflen = d->val.length + 100;
buf = xtrymalloc (buflen);
if (!buf)
err = KSBA_Out_Of_Core;
}
for (n=0; !err && n < d->val.length; n++)
{
if ( (c=read_byte (d->reader)) == -1)
err = eof_or_error (d, 1);
buf[n] = c;
}
if (err)
break;
fputs (" (", fp);
p = NULL;
switch (node->type)
{
case TYPE_OBJECT_ID:
p = ksba_oid_to_str (buf, n);
break;
default:
for (i=0; i < n && i < 20; i++)
fprintf (fp,"%02x", buf[i]);
if (i < n)
fputs ("..more..", fp);
break;
}
if (p)
{
fputs (p, fp);
xfree (p);
}
fputs (")\n", fp);
}
else
{
err = decoder_skip (d);
putc ('\n', fp);
}
if (err)
break;
}
if (err == -1)
err = 0;
decoder_deinit (d);
xfree (buf);
return err;
}
KsbaError
-_ksba_ber_decoder_decode (BerDecoder d, const char *start_name,
+_ksba_der_encoder_encode (DerEncoder d, const char *start_name,
AsnNode *r_root,
unsigned char **r_image, size_t *r_imagelen)
{
KsbaError err;
AsnNode node;
unsigned char *buf = NULL;
size_t buflen = 0;
unsigned long startoff;
if (!d)
return KSBA_Invalid_Value;
if (r_root)
*r_root = NULL;
- d->debug = !!getenv("DEBUG_BER_DECODER");
+ d->debug = !!getenv("DEBUG_DER_ENCODER");
d->honor_module_end = 1;
d->use_image = 1;
d->image.buf = NULL;
startoff = ksba_reader_tell (d->reader);
err = decoder_init (d, start_name);
if (err)
return err;
while (!(err = decoder_next (d)))
{
int n, c;
node = d->val.node;
if (node && d->use_image)
{
if (!d->val.is_endtag)
{ /* We don't have nodes for the end tag - so don't store it */
node->off = (ksba_reader_tell (d->reader)
- d->val.nhdr - startoff);
node->nhdr = d->val.nhdr;
node->len = d->val.length;
if (node->type == TYPE_ANY)
node->actual_type = d->val.tag;
}
if (d->image.used + d->val.length > d->image.length)
err = set_error(d, NULL, "TLV length too large");
else if (d->val.primitive)
{
if( read_buffer (d->reader,
d->image.buf + d->image.used, d->val.length))
err = eof_or_error (d, 1);
else
d->image.used += d->val.length;
}
}
else if (node && d->val.primitive)
{
if (!buf || buflen < d->val.length)
{
xfree (buf);
buflen = d->val.length + 100;
buf = xtrymalloc (buflen);
if (!buf)
err = KSBA_Out_Of_Core;
}
for (n=0; !err && n < d->val.length; n++)
{
if ( (c=read_byte (d->reader)) == -1)
err = eof_or_error (d, 1);
buf[n] = c;
}
if (err)
break;
switch (node->type)
{
default:
_ksba_asn_set_value (node, VALTYPE_MEM, buf, n);
break;
}
}
else
{
err = decoder_skip (d);
}
if (err)
break;
}
if (err == -1)
err = 0;
if (r_root && !err)
{
if (!d->image.buf)
{ /* Not even the first node available - return eof */
/* fixme: release d->root */
d->root = NULL;
err = -1;
}
fixup_type_any (d->root);
*r_root = d->root;
d->root = NULL;
*r_image = d->image.buf;
d->image.buf = NULL;
*r_imagelen = d->image.used;
if (d->debug)
{
fputs ("Value Tree:\n", stdout);
_ksba_asn_node_dump_all (*r_root, stdout);
}
}
decoder_deinit (d);
xfree (buf);
return err;
}
+#endif
+
+
+/*
+ Helpers to construct and write out objects
+*/
+
+
+/* Create and write a
+
+ AlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER,
+ parameters ANY DEFINED BY algorithm OPTIONAL
+ }
+
+ where parameters is NULL */
+KsbaError
+_ksba_der_write_algorithm_identifier (KsbaWriter w, const char *oid)
+{
+ KsbaError err;
+ char *buf;
+ size_t len;
+
+ err = ksba_oid_from_str (oid, &buf, &len);
+ if (err)
+ return err;
+
+ /* write the sequence which is 4 octects longer than the OID */
+ err = _ksba_ber_write_tl (w, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, len+4);
+ if (err)
+ goto leave;
+
+ /* the OBJECT ID header and the value */
+ err = _ksba_ber_write_tl (w, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len);
+ if (!err)
+ err = ksba_writer_write (w, buf, len);
+ if (err)
+ goto leave;
+
+ /* The NULL tag as parameter */
+ err = _ksba_ber_write_tl (w, TYPE_NULL, CLASS_UNIVERSAL, 0, 0);
+
+ leave:
+ xfree (buf);
+ return err;
+}
+
+
+
+
+
+/*************************************************
+ *** Copy data from a tree image to the tree ***
+ *************************************************/
+
+/* Copy all values from the tree SRC (with values store in SRCIMAGE)
+ to the tree DST */
+KsbaError
+_ksba_der_copy_tree (AsnNode dst_root,
+ AsnNode src_root, const unsigned char *src_image)
+{
+ AsnNode s, d;
+
+ for (s = src_root, d = dst_root;
+ s && d && s->type == d->type;
+ s = _ksba_asn_walk_tree (src_root, s),
+ d = _ksba_asn_walk_tree (dst_root, d) )
+ {
+ if (s->type == TYPE_CHOICE)
+ ; /* just skip it */
+ else if (s->type == TYPE_TAG)
+ ; /* does not make sense */
+ else if (s->off == -1)
+ clear_value (d);
+ else
+ store_value (d, src_image + s->off + s->nhdr, s->len);
+ }
+
+ if (s || d)
+ {
+ fputs ("ksba_der_copy_tree: trees don't match\nSOURCE TREE:\n", stderr);
+ _ksba_asn_node_dump_all (src_root, stderr);
+ fputs ("DESTINATION TREE:\n", stderr);
+ _ksba_asn_node_dump_all (dst_root, stderr);
+ return KSBA_Encoding_Error;
+ }
+ return 0;
+}
+
+
+
+/*********************************************
+ ********** Store data in a tree *************
+ *********************************************/
+
+
+KsbaError
+_ksba_der_store_time (AsnNode node, time_t atime)
+{
+
+ if (node->type == TYPE_CHOICE)
+ {
+ /* find a suitable choice to store the value */
+ }
+
+ if (node->type == TYPE_GENERALIZED_TIME
+ || node->type == TYPE_UTC_TIME)
+ {
+ char buf[50], *p;
+ struct tm *tp;
+
+ tp = gmtime (&atime);
+ sprintf (buf, "%04d%02d%02d%02d%02d%02dZ",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ p = node->type == TYPE_UTC_TIME? (buf+2):buf;
+ return store_value (node, p, strlen (p));
+ }
+ else
+ return KSBA_Invalid_Value;
+}
+
+/* Store the utf-8 STRING in NODE. */
+KsbaError
+_ksba_der_store_string (AsnNode node, const char *string)
+{
+ if (node->type == TYPE_CHOICE)
+ {
+ /* find a suitable choice to store the value */
+ }
+
+
+ if (node->type == TYPE_PRINTABLE_STRING)
+ {
+ return store_value (node, string, strlen (string));
+ }
+ else
+ return KSBA_Invalid_Value;
+}
diff --git a/src/der-encoder.h b/src/der-encoder.h
new file mode 100644
index 0000000..f52a472
--- /dev/null
+++ b/src/der-encoder.h
@@ -0,0 +1,54 @@
+/* der-encoder.h - Definitions for the Distinguished Encoding Rules Encoder
+ * Copyright (C) 2001 g10 Code GmbH
+ *
+ * This file is part of KSBA.
+ *
+ * KSBA is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * KSBA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef DER_ENCODER_H
+#define DER_ENCODER_H 1
+
+#include "asn1-func.h"
+
+struct der_encoder_s;
+typedef struct der_encoder_s *DerEncoder;
+
+DerEncoder _ksba_der_encoder_new (void);
+void _ksba_der_encoder_release (DerEncoder d);
+
+KsbaError _ksba_der_encoder_set_module (DerEncoder d, KsbaAsnTree module);
+KsbaError _ksba_der_encoder_set_writer (DerEncoder d, KsbaWriter w);
+
+KsbaError _ksba_der_encoder_encode (DerEncoder d, const char *start_name);
+
+
+KsbaError _ksba_der_write_algorithm_identifier (KsbaWriter w, const char *oid);
+
+
+
+KsbaError _ksba_der_copy_tree (AsnNode dst,
+ AsnNode src, const unsigned char *srcimage);
+
+
+
+KsbaError _ksba_der_store_time (AsnNode node, time_t atime);
+KsbaError _ksba_der_store_string (AsnNode node, const char *string);
+
+
+
+#endif /*DER_ENCODER_H*/
+
+
diff --git a/src/ksba.h b/src/ksba.h
index c4950d4..ea477b8 100644
--- a/src/ksba.h
+++ b/src/ksba.h
@@ -1,238 +1,252 @@
/* ksba.h - X509 library for the Aegypten project
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifndef KSBA_H
#define KSBA_H 1
#include <time.h>
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
typedef enum {
KSBA_EOF = -1,
KSBA_No_Error = 0,
KSBA_General_Error = 1,
KSBA_Out_Of_Core = 2,
KSBA_Invalid_Value = 3,
KSBA_Not_Implemented = 4,
KSBA_Conflict = 5,
KSBA_Read_Error = 6,
KSBA_Write_Error = 7,
KSBA_Invalid_Attr = 8,
KSBA_No_Data = 9,
KSBA_No_Value = 10,
KSBA_Bug = 11,
KSBA_BER_Error = 12,
KSBA_Element_Not_Found = 13,
KSBA_Identifier_Not_Found = 14,
KSBA_Value_Not_Found = 15, /* Note, that this is not the same as No Value */
KSBA_Syntax_Error = 16,
KSBA_Invalid_Tag = 17,
KSBA_Invalid_Length = 18,
KSBA_Invalid_Keyinfo = 19,
KSBA_Unexpected_Tag = 20,
KSBA_Not_DER_Encoded = 21,
KSBA_Unknown_Algorithm = 22,
KSBA_Unsupported_Algorithm = 23,
KSBA_Object_Too_Large = 24,
KSBA_Object_Too_Short = 25,
KSBA_No_CMS_Object = 26,
KSBA_Unknown_CMS_Object = 27,
KSBA_Unsupported_CMS_Object = 28,
KSBA_Invalid_CMS_Object = 29,
KSBA_Unsupported_CMS_Version = 30,
KSBA_Unsupported_Encoding = 31,
KSBA_Missing_Value = 32,
KSBA_Invalid_State = 33,
KSBA_Duplicate_Value = 34,
KSBA_Missing_Action = 35,
KSBA_File_Error = 36,
KSBA_Module_Not_Found = 37,
KSBA_Encoding_Error = 38,
+ KSBA_Invalid_Index = 39,
} KsbaError;
typedef enum {
KSBA_CT_NONE = 0,
- KSBA_CT_SIGNED_DATA = 1,
- KSBA_CT_ENVELOPED_DATA = 2,
- KSBA_CT_DIGESTED_DATA = 3,
- KSBA_CT_ENCRYPTED_DATA = 4
+ KSBA_CT_DATA = 1,
+ KSBA_CT_SIGNED_DATA = 2,
+ KSBA_CT_ENVELOPED_DATA = 3,
+ KSBA_CT_DIGESTED_DATA = 4,
+ KSBA_CT_ENCRYPTED_DATA = 5,
+ KSBA_CT_AUTH_DATA = 6
} KsbaContentType;
typedef enum {
KSBA_SR_NONE = 0, /* never seen by libgcrypt user */
KSBA_SR_RUNNING = 1, /* never seen by libgcrypt user */
KSBA_SR_GOT_CONTENT = 2,
KSBA_SR_NEED_HASH = 3,
KSBA_SR_BEGIN_DATA = 4,
KSBA_SR_END_DATA = 5,
KSBA_SR_READY = 6,
-
+ KSBA_SR_NEED_SIG = 7
} KsbaStopReason;
/* X.509 certificates are represented by this object.
ksba_cert_new() creates such an object */
struct ksba_cert_s;
typedef struct ksba_cert_s *KsbaCert;
/* CMS objects are controlled by this object.
ksba_cms_new() creates it */
struct ksba_cms_s;
typedef struct ksba_cms_s *KsbaCMS;
/* This is a reader object vor various purposes
see ksba_reader_new et al. */
struct ksba_reader_s;
typedef struct ksba_reader_s *KsbaReader;
/* This is a writer object vor various purposes
see ksba_writer_new et al. */
struct ksba_writer_s;
typedef struct ksba_writer_s *KsbaWriter;
/* This is an object to store an ASN.1 parse tree as
create by ksba_asn_parse_file() */
struct ksba_asn_tree_s;
typedef struct ksba_asn_tree_s *KsbaAsnTree;
/*-- cert.c --*/
KsbaCert ksba_cert_new (void);
void ksba_cert_release (KsbaCert cert);
KsbaError ksba_cert_read_der (KsbaCert cert, KsbaReader reader);
KsbaError ksba_cert_init_from_mem (KsbaCert cert,
const void *buffer, size_t length);
const unsigned char *ksba_cert_get_image (KsbaCert cert, size_t *r_length);
KsbaError ksba_cert_hash (KsbaCert cert, int what,
void (*hasher)(void *,
const void *,
size_t length),
void *hasher_arg);
const char *ksba_cert_get_digest_algo (KsbaCert cert);
unsigned char *ksba_cert_get_serial (KsbaCert cert);
char *ksba_cert_get_issuer (KsbaCert cert);
time_t ksba_cert_get_validity (KsbaCert cert, int what);
char *ksba_cert_get_subject (KsbaCert cert);
char *ksba_cert_get_public_key (KsbaCert cert);
char *ksba_cert_get_sig_val (KsbaCert cert);
/*-- cms.c --*/
KsbaCMS ksba_cms_new (void);
void ksba_cms_release (KsbaCMS cms);
KsbaError ksba_cms_set_reader_writer (KsbaCMS cms, KsbaReader r, KsbaWriter w);
KsbaError ksba_cms_parse (KsbaCMS cms, KsbaStopReason *r_stopreason);
KsbaContentType ksba_cms_get_content_type (KsbaCMS cms, int what);
const char *ksba_cms_get_content_oid (KsbaCMS cms, int what);
const char *ksba_cms_get_digest_algo_list (KsbaCMS cms, int idx);
KsbaError ksba_cms_get_issuer_serial (KsbaCMS cms, int idx,
char **r_issuer,
unsigned char **r_serial);
const char *ksba_cms_get_digest_algo (KsbaCMS cms, int idx);
KsbaCert ksba_cms_get_cert (KsbaCMS cms, int idx);
KsbaError ksba_cms_get_message_digest (KsbaCMS cms, int idx,
char **r_digest, size_t *r_digest_len);
char *ksba_cms_get_sig_val (KsbaCMS cms, int idx);
void
ksba_cms_set_hash_function (KsbaCMS cms,
void (*hash_fnc)(void *, const void *, size_t),
void *hash_fnc_arg);
KsbaError ksba_cms_hash_signed_attrs (KsbaCMS cms, int idx);
+KsbaError ksba_cms_set_content_type (KsbaCMS cms, int what,
+ KsbaContentType type);
+KsbaError ksba_cms_add_digest_algo (KsbaCMS cms, const char *oid);
+KsbaError ksba_cms_add_signer (KsbaCMS cms, KsbaCert cert);
+KsbaError ksba_cms_set_message_digest (KsbaCMS cms, int idx,
+ const char *digest,
+ size_t digest_len);
+
+
+
+
/*-- reader.c --*/
KsbaReader ksba_reader_new (void);
void ksba_reader_release (KsbaReader r);
int ksba_reader_error (KsbaReader r);
KsbaError ksba_reader_set_mem (KsbaReader r,
const void *buffer, size_t length);
KsbaError ksba_reader_set_fd (KsbaReader r, int fd);
KsbaError ksba_reader_set_file (KsbaReader r, FILE *fp);
KsbaError ksba_reader_set_cb (KsbaReader r,
int (*cb)(void*,char *,size_t,size_t*),
void *cb_value );
KsbaError ksba_reader_read (KsbaReader r,
char *buffer, size_t length, size_t *nread);
KsbaError ksba_reader_unread (KsbaReader r, const void *buffer, size_t count);
unsigned long ksba_reader_tell (KsbaReader r);
/*-- writer.c --*/
KsbaWriter ksba_writer_new (void);
void ksba_writer_release (KsbaWriter r);
int ksba_writer_error (KsbaWriter w);
unsigned long ksba_writer_tell (KsbaWriter w);
KsbaError ksba_writer_set_fd (KsbaWriter w, int fd);
KsbaError ksba_writer_set_file (KsbaWriter w, FILE *fp);
KsbaError ksba_writer_set_cb (KsbaWriter w,
int (*cb)(void*,const void *,size_t),
void *cb_value);
-
+KsbaError ksba_writer_write (KsbaWriter w, const void *buffer, size_t length);
/*-- asn1-parse.y --*/
int ksba_asn_parse_file (const char *filename, KsbaAsnTree *result, int debug);
void ksba_asn_tree_release (KsbaAsnTree tree);
/*-- asn1-func.c --*/
void ksba_asn_tree_dump (KsbaAsnTree tree, const char *name, FILE *fp);
KsbaError ksba_asn_create_tree (const char *mod_name, KsbaAsnTree *result);
/*-- oid.c --*/
char *ksba_oid_to_str (const char *buffer, size_t length);
int ksba_oid_from_str (const char *string, char **rbuf, size_t *rlength);
/*-- util.c --*/
void ksba_set_malloc_hooks ( void *(*new_alloc_func)(size_t n),
void *(*new_realloc_func)(void *p, size_t n),
void (*new_free_func)(void*) );
void *ksba_malloc (size_t n );
void *ksba_calloc (size_t n, size_t m );
void *ksba_realloc (void *p, size_t n);
char *ksba_strdup (const char *p);
void ksba_free ( void *a );
/*-- errors.c (generated from this file) --*/
const char *ksba_strerror (KsbaError err);
#ifdef __cplusplus
}
#endif
#endif /*KSBA_H*/
diff --git a/src/oid.c b/src/oid.c
index d4ca2ad..9ceb610 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -1,143 +1,143 @@
/* oid.c - Object identifier helper functions
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "asn1-func.h"
#include "convert.h"
/**
* ksba_oid_to_str:
* @buffer: A BER encoded OID
* @length: The length of this OID
*
* Take a buffer with an object identifier in BER encoding and return
* a string representing this OID. We do not use the ASN.1 syntax
* here but delimit the arcs with dots, so it is easier to parse in
* most cases. This dotted-decimal notation is also known as LDAPOID
* string and described in RFC-2251.
*
* The function returns an empty string for an empty buffer and does
* no interpretation of the OID. The caller must free the returned
* string using ksba_free() or the function he has registered as a
* replacement.
*
*
* Return value: A allocated string or NULL in case of memory problem.
**/
char *
ksba_oid_to_str (const char *buffer, size_t length)
{
const unsigned char *buf = buffer;
char *string, *p;
int n = 0;
unsigned long val;
/* To calculate the length of the string we can safely assume an
upper limit of 3 decimal characters per byte. Two extra bytes
account for the special first octect */
string = p = xtrymalloc (length*(1+3)+2+1);
if (!string)
return NULL;
if (!length)
{
*p = 0;
return string;
}
/* fixme: open code the sprintf so that we can open with arbitrary
long integers - at least we should check for overflow of ulong */
if (buf[0] < 40)
p += sprintf (p, "0.%d", buf[n]);
else if (buf[0] < 80)
p += sprintf (p, "1.%d", buf[n]-40);
else {
val = buf[n] & 0x7f;
while ( (buf[n]&0x80) && ++n < length )
{
val <<= 7;
val |= buf[n] & 0x7f;
}
val -= 80;
sprintf (p, "2.%lu", val);
p += strlen (p);
}
for (n++; n < length; n++)
{
val = buf[n] & 0x7f;
while ( (buf[n]&0x80) && ++n < length )
{
val <<= 7;
val |= buf[n] & 0x7f;
}
sprintf (p, ".%lu", val);
p += strlen (p);
}
*p = 0;
return string;
}
/* Take the OID at the given and return it in string format */
char *
_ksba_oid_node_to_str (const unsigned char *image, AsnNode node)
{
if (!node || node->type != TYPE_OBJECT_ID || node->off == -1)
return NULL;
return ksba_oid_to_str (image+node->off + node->nhdr, node->len);
}
/**
* ksba_oid_from_str:
- * @string: A string with the OID in doitted decimal form
+ * @string: A string with the OID in dotted decimal form
* @rbuf: Returns the DER encoded OID
* @rlength: and its length
*
* Convertes the OID given in dotted decimal form to an DER encoding
* and returns it in allocated buffer rbuf and its length in rlength.
* rbuf is set to NULL in case of an error and -1 is returned.
* Scanning stops at the first white space.
* The caller must free the returned buffer using ksba_free() or the
* function he has registered as a replacement.
*
* Return value: Number of bytes scanned from string, or -1 in case of
* an error.
**/
int
ksba_oid_from_str (const char *string, char **rbuf, size_t *rlength)
{
*rbuf = NULL;
*rlength = 0;
return -1; /* error */
}
diff --git a/src/time.c b/src/time.c
index 47a5af5..a3e2ce0 100644
--- a/src/time.c
+++ b/src/time.c
@@ -1,114 +1,114 @@
/* time.c - UTCTime and GeneralizedTime helper
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include "util.h"
#include "convert.h"
#define digitp(p) (*(p) >= 0 && *(p) <= '9')
/* the atoi macros assume that the buffer has only valid digits */
#define atoi_1(p) (*(p) - '0' )
#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
/* Converts an UTCTime or GeneralizedTime to epoc. Returns (time_t)-1
on error. The function figures automagically the right format.
fixme: Currently we only zupport Zulu time and no timezone */
time_t
_ksba_asntime_to_epoch (const char *buffer, size_t length)
{
const char *s;
size_t n;
struct tm buf;
int year;
for (s=buffer, n=0; n < length && digitp (s); n++, s++)
;
if ((n != 12 && n != 14) || *s != 'Z')
return (time_t)(-1);
s = buffer;
if (n==12)
{
year = atoi_2 (s);
s += 2;
year += year < 50? 2000:1900;
}
else
{
year = atoi_4 (s);
s += 4;
}
if (year < 1900)
return (time_t)(-1);
buf.tm_year = year - 1900;
buf.tm_mon = atoi_2 (s) - 1;
s += 2;
buf.tm_mday = atoi_2 (s);
s += 2;
buf.tm_hour = atoi_2 (s);
s += 2;
buf.tm_min = atoi_2 (s);
s += 2;
buf.tm_sec = atoi_2 (s);
s += 2;
buf.tm_isdst = 0;
-#warning We should reset TZ if we cannot use timegm()
#ifdef HAVE_TIMEGM
return timegm (&buf);
#else
{
+#warning We should reset TZ if we cannot use timegm()
time_t tim;
putenv ("TZ=UTC");
tim = mktime (&buf);
return tim;
}
#endif
}
/* convert an epoch time T into Generalized Time and return that in
rbuf and rlength. Caller must free the returned buffer */
int
_ksba_asntime_from_epoch (time_t t, char **rbuf, size_t *rlength)
{
*rbuf = NULL;
*rlength = 0;
return -1; /* error */
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 7:06 PM (16 h, 24 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
91/22/f61cadd1d9b91c8dd17463fa24ad
Attached To
rK libksba
Event Timeline
Log In to Comment