/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* The following is the mozilla license blurb, as the bodies some of
these functions were derived from the mozilla source. */
/*
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1994-2000
* the Initial Developer. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*/
/*
* Author: Chris Toshok (toshok@ximian.com)
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "e-asn1-object.h"
#include "secasn1.h"
struct _EASN1ObjectPrivate {
PRUint32 tag;
PRUint32 type;
gboolean valid_container;
GList *children;
gchar *display_name;
gchar *value;
gchar *data;
guint data_len;
};
#define PARENT_TYPE G_TYPE_OBJECT
static GObjectClass *parent_class;
static void
e_asn1_object_dispose (GObject *object)
{
EASN1Object *obj = E_ASN1_OBJECT (object);
if (obj->priv) {
if (obj->priv->display_name)
g_free (obj->priv->display_name);
if (obj->priv->value)
g_free (obj->priv->value);
g_list_foreach (obj->priv->children, (GFunc)g_object_unref, NULL);
g_list_free (obj->priv->children);
g_free (obj->priv);
obj->priv = NULL;
}
}
static void
e_asn1_object_class_init (EASN1ObjectClass *klass)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS(klass);
parent_class = g_type_class_ref (PARENT_TYPE);
object_class->dispose = e_asn1_object_dispose;
}
static void
e_asn1_object_init (EASN1Object *asn1)
{
asn1->priv = g_new0 (EASN1ObjectPrivate, 1);
asn1->priv->valid_container = TRUE;
}
GType
e_asn1_object_get_type (void)
{
static GType asn1_object_type = 0;
if (!asn1_object_type) {
static const GTypeInfo asn1_object_info = {
sizeof (EASN1ObjectClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) e_asn1_object_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (EASN1Object),
0, /* n_preallocs */
(GInstanceInitFunc) e_asn1_object_init,
};
asn1_object_type = g_type_register_static (PARENT_TYPE, "EASN1Object", &asn1_object_info, 0);
}
return asn1_object_type;
}
/* This function is used to interpret an integer that
was encoded in a DER buffer. This function is used
when converting a DER buffer into a nsIASN1Object
structure. This interprets the buffer in data
as defined by the DER (Distinguised Encoding Rules) of
ASN1.
*/
static gint
get_integer_256 (guchar *data, guint nb)
{
gint val;
switch (nb) {
case 1:
val = data[0];
break;
case 2:
val = (data[0] << 8) | data[1];
break;
case 3:
val = (data[0] << 16) | (data[1] << 8) | data[2];
break;
case 4:
val = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
break;
default:
return -1;
}
return val;
}
/* This function is used to retrieve the lenght of a DER encoded
item. It looks to see if this a multibyte length and then
interprets the buffer accordingly to get the actual length value.
This funciton is used mostly while parsing the DER headers.
A DER encoded item has the following structure:
<tag><length<data consisting of lenght bytes>
*/
static guint32
get_der_item_length (guchar *data, guchar *end,
unsigned long *bytesUsed, gboolean *indefinite)
{
guchar lbyte = *data++;
PRInt32 length = -1;
*indefinite = FALSE;
if (lbyte >= 0x80) {
/* Multibyte length */
unsigned nb = (unsigned) (lbyte & 0x7f);
if (nb > 4) {
return -1;
}
if (nb > 0) {
if ((data+nb) > end) {
return -1;
}
length = get_integer_256 (data, nb);
if (length < 0)
return -1;
} else {
*indefinite = TRUE;
length = 0;
}
*bytesUsed = nb+1;
} else {
length = lbyte;
*bytesUsed = 1;
}
return length;
}
static gboolean
build_from_der (EASN1Object *parent, gchar *data, gchar *end)
{
unsigned long bytesUsed;
gboolean indefinite;
PRInt32 len;
PRUint32 type;
guchar code, tagnum;
EASN1Object *asn1object = NULL;
if (data >= end)
return TRUE;
/*
A DER item has the form of |tag|len|data
tag is one byte and describes the type of elment
we are dealing with.
len is a DER encoded gint telling us how long the data is
data is a buffer that is len bytes long and has to be
interpreted according to its type.
*/
while (data < end) {
code = *data;
tagnum = code & SEC_ASN1_TAGNUM_MASK;
/*
* NOTE: This code does not (yet) handle the high-tag-number form!
*/
if (tagnum == SEC_ASN1_HIGH_TAG_NUMBER) {
return FALSE;
}
data++;
len = get_der_item_length ((guchar *)data, (guchar *)end, &bytesUsed, &indefinite);
data += bytesUsed;
if ((len < 0) || ((data+len) > end))
return FALSE;
if (code & SEC_ASN1_CONSTRUCTED) {
if (len > 0 || indefinite) {
switch (code & SEC_ASN1_CLASS_MASK) {
case SEC_ASN1_UNIVERSAL:
type = tagnum;
break;
case SEC_ASN1_APPLICATION:
type = E_ASN1_OBJECT_TYPE_APPLICATION;
break;
case SEC_ASN1_CONTEXT_SPECIFIC:
type = E_ASN1_OBJECT_TYPE_CONTEXT_SPECIFIC;
break;
case SEC_ASN1_PRIVATE:
type = E_ASN1_OBJECT_TYPE_PRIVATE;
break;
default:
g_warning ("bad DER");
return FALSE;
}
asn1object = e_asn1_object_new ();
asn1object->priv->tag = tagnum;
asn1object->priv->type = type;
if (!build_from_der (asn1object, data, (len == 0) ? end : data + len)) {
g_object_unref (asn1object);
return FALSE;
}
}
} else {
asn1object = e_asn1_object_new ();
asn1object->priv->type = tagnum;
asn1object->priv->tag = tagnum;
/*printableItem->SetData((gchar *)data, len);*/
}
data += len;
parent->priv->children = g_list_append (parent->priv->children, asn1object);
}
return TRUE;
}
EASN1Object*
e_asn1_object_new_from_der (gchar *data, guint32 len)
{
EASN1Object *obj = g_object_new (E_TYPE_ASN1_OBJECT, NULL);
if (!build_from_der (obj, data, data + len)) {
g_object_unref (obj);
return NULL;
}
return obj;
}
EASN1Object*
e_asn1_object_new (void)
{
return E_ASN1_OBJECT (g_object_new (E_TYPE_ASN1_OBJECT, NULL));
}
void
e_asn1_object_set_valid_container (EASN1Object *obj, gboolean flag)
{
obj->priv->valid_container = flag;
}
gboolean
e_asn1_object_is_valid_container (EASN1Object *obj)
{
return obj->priv->valid_container;
}
PRUint32
e_asn1_object_get_asn1_type (EASN1Object *obj)
{
return obj->priv->type;
}
PRUint32
e_asn1_object_get_asn1_tag (EASN1Object *obj)
{
return obj->priv->tag;
}
GList*
e_asn1_object_get_children (EASN1Object *obj)
{
GList *children = g_list_copy (obj->priv->children);
g_list_foreach (children, (GFunc)g_object_ref, NULL);
return children;
}
void
e_asn1_object_append_child (EASN1Object *parent, EASN1Object *child)
{
parent->priv->children = g_list_append (parent->priv->children, g_object_ref (child));
}
void
e_asn1_object_set_display_name (EASN1Object *obj, const gchar *name)
{
g_free (obj->priv->display_name);
obj->priv->display_name = g_strdup (name);
}
const gchar *
e_asn1_object_get_display_name (EASN1Object *obj)
{
return obj->priv->display_name;
}
void
e_asn1_object_set_display_value (EASN1Object *obj, const gchar *value)
{
g_free (obj->priv->value);
obj->priv->value = g_strdup (value);
}
const gchar *
e_asn1_object_get_display_value (EASN1Object *obj)
{
return obj->priv->value;
}
void
e_asn1_object_get_data (EASN1Object *obj, gchar **data, guint32 *len)
{
*data = obj->priv->data;
*len = obj->priv->data_len;
}