287 lines
6.2 KiB
C
287 lines
6.2 KiB
C
/*
|
|
* Copyright (C) 2012 Tobias Brunner
|
|
* Copyright (C) 2008 Andreas Steffen
|
|
* HSR Hochschule fuer Technik Rapperswil
|
|
*
|
|
* This program 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. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program 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.
|
|
*/
|
|
|
|
#include <library.h>
|
|
#include <utils/debug.h>
|
|
|
|
#include <asn1/oid.h>
|
|
#include <asn1/asn1.h>
|
|
#include <asn1/asn1_parser.h>
|
|
#include <collections/array.h>
|
|
#include <collections/linked_list.h>
|
|
|
|
#include "pkcs7_attributes.h"
|
|
|
|
typedef struct private_pkcs7_attributes_t private_pkcs7_attributes_t;
|
|
typedef struct attribute_t attribute_t;
|
|
|
|
/**
|
|
* Private data of a pkcs7_attributes_t attribute list.
|
|
*/
|
|
struct private_pkcs7_attributes_t {
|
|
/**
|
|
* Public interface
|
|
*/
|
|
pkcs7_attributes_t public;
|
|
|
|
/**
|
|
* DER encoding of PKCS#9 attributes
|
|
*/
|
|
chunk_t encoding;
|
|
|
|
/**
|
|
* Linked list of PKCS#9 attributes
|
|
*/
|
|
linked_list_t *attributes;
|
|
};
|
|
|
|
/**
|
|
* Definition of an attribute_t object.
|
|
*/
|
|
struct attribute_t {
|
|
|
|
/**
|
|
* Object Identifier (OID)
|
|
*/
|
|
int oid;
|
|
|
|
/**
|
|
* Attribute value
|
|
*/
|
|
chunk_t value;
|
|
|
|
/**
|
|
* ASN.1 encoding
|
|
*/
|
|
chunk_t encoding;
|
|
};
|
|
|
|
/**
|
|
* Destroy an attribute_t object.
|
|
*/
|
|
static void attribute_destroy(attribute_t *this)
|
|
{
|
|
free(this->value.ptr);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* Create an attribute_t object.
|
|
*/
|
|
static attribute_t *attribute_create(int oid, chunk_t value)
|
|
{
|
|
attribute_t *this;
|
|
|
|
INIT(this,
|
|
.oid = oid,
|
|
.value = chunk_clone(value),
|
|
);
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Compare two encoded attributes
|
|
*/
|
|
static int cmp_attributes(const chunk_t *a, const chunk_t *b, void *unused)
|
|
{
|
|
return chunk_compare(*a, *b);
|
|
}
|
|
|
|
/**
|
|
* Build encoding of the attribute list
|
|
*/
|
|
static void build_encoding(private_pkcs7_attributes_t *this)
|
|
{
|
|
enumerator_t *enumerator;
|
|
attribute_t *attribute;
|
|
u_int len = 0, count, i = 0;
|
|
array_t *chunks;
|
|
chunk_t chunk;
|
|
u_char *pos;
|
|
|
|
count = this->attributes->get_count(this->attributes);
|
|
chunks = array_create(sizeof(chunk_t), count);
|
|
|
|
enumerator = this->attributes->create_enumerator(this->attributes);
|
|
while (enumerator->enumerate(enumerator, &attribute))
|
|
{
|
|
chunk = asn1_wrap(ASN1_SEQUENCE, "mm",
|
|
asn1_build_known_oid(attribute->oid),
|
|
asn1_wrap(ASN1_SET, "c", attribute->value));
|
|
array_insert(chunks, ARRAY_TAIL, &chunk);
|
|
len += chunk.len;
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
array_sort(chunks, (void*)cmp_attributes, NULL);
|
|
|
|
pos = asn1_build_object(&this->encoding, ASN1_SET, len);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
array_get(chunks, i, &chunk);
|
|
memcpy(pos, chunk.ptr, chunk.len);
|
|
pos += chunk.len;
|
|
free(chunk.ptr);
|
|
}
|
|
array_destroy(chunks);
|
|
}
|
|
|
|
METHOD(pkcs7_attributes_t, get_encoding, chunk_t,
|
|
private_pkcs7_attributes_t *this)
|
|
{
|
|
if (!this->encoding.len)
|
|
{
|
|
build_encoding(this);
|
|
}
|
|
return this->encoding;
|
|
}
|
|
|
|
METHOD(pkcs7_attributes_t, get_attribute, chunk_t,
|
|
private_pkcs7_attributes_t *this, int oid)
|
|
{
|
|
enumerator_t *enumerator;
|
|
chunk_t value = chunk_empty;
|
|
attribute_t *attribute;
|
|
|
|
enumerator = this->attributes->create_enumerator(this->attributes);
|
|
while (enumerator->enumerate(enumerator, &attribute))
|
|
{
|
|
if (attribute->oid == oid)
|
|
{
|
|
value = attribute->value;
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
if (value.len && asn1_unwrap(&value, &value) != ASN1_INVALID)
|
|
{
|
|
return value;
|
|
}
|
|
return chunk_empty;
|
|
}
|
|
|
|
METHOD(pkcs7_attributes_t, add_attribute, void,
|
|
private_pkcs7_attributes_t *this, int oid, chunk_t value)
|
|
{
|
|
this->attributes->insert_last(this->attributes,
|
|
attribute_create(oid, value));
|
|
chunk_free(&value);
|
|
|
|
/* rebuild encoding when adding attributes */
|
|
chunk_free(&this->encoding);
|
|
}
|
|
|
|
METHOD(pkcs7_attributes_t, destroy, void,
|
|
private_pkcs7_attributes_t *this)
|
|
{
|
|
this->attributes->destroy_function(this->attributes,
|
|
(void*)attribute_destroy);
|
|
free(this->encoding.ptr);
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* Described in header.
|
|
*/
|
|
pkcs7_attributes_t *pkcs7_attributes_create(void)
|
|
{
|
|
private_pkcs7_attributes_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.get_encoding = _get_encoding,
|
|
.get_attribute = _get_attribute,
|
|
.add_attribute = _add_attribute,
|
|
.destroy = _destroy,
|
|
},
|
|
.attributes = linked_list_create(),
|
|
);
|
|
|
|
return &this->public;
|
|
}
|
|
|
|
/**
|
|
* ASN.1 definition of the X.501 attribute type
|
|
*/
|
|
static const asn1Object_t attributesObjects[] = {
|
|
{ 0, "attributes", ASN1_SET, ASN1_LOOP }, /* 0 */
|
|
{ 1, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
|
|
{ 2, "type", ASN1_OID, ASN1_BODY }, /* 2 */
|
|
{ 2, "values", ASN1_SET, ASN1_LOOP }, /* 3 */
|
|
{ 3, "value", ASN1_EOC, ASN1_RAW }, /* 4 */
|
|
{ 2, "end loop", ASN1_EOC, ASN1_END }, /* 5 */
|
|
{ 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */
|
|
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
|
|
};
|
|
#define ATTRIBUTE_OBJ_TYPE 2
|
|
#define ATTRIBUTE_OBJ_VALUE 4
|
|
|
|
/**
|
|
* Parse a PKCS#9 attribute list
|
|
*/
|
|
static bool parse_attributes(chunk_t chunk, int level0,
|
|
private_pkcs7_attributes_t* this)
|
|
{
|
|
asn1_parser_t *parser;
|
|
chunk_t object;
|
|
int objectID;
|
|
int oid = OID_UNKNOWN;
|
|
bool success = FALSE;
|
|
|
|
parser = asn1_parser_create(attributesObjects, chunk);
|
|
parser->set_top_level(parser, level0);
|
|
|
|
while (parser->iterate(parser, &objectID, &object))
|
|
{
|
|
switch (objectID)
|
|
{
|
|
case ATTRIBUTE_OBJ_TYPE:
|
|
oid = asn1_known_oid(object);
|
|
break;
|
|
case ATTRIBUTE_OBJ_VALUE:
|
|
if (oid != OID_UNKNOWN)
|
|
{
|
|
this->attributes->insert_last(this->attributes,
|
|
attribute_create(oid, object));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
success = parser->success(parser);
|
|
|
|
parser->destroy(parser);
|
|
return success;
|
|
}
|
|
|
|
/*
|
|
* Described in header.
|
|
*/
|
|
pkcs7_attributes_t *pkcs7_attributes_create_from_chunk(chunk_t chunk,
|
|
u_int level)
|
|
{
|
|
private_pkcs7_attributes_t *this;
|
|
|
|
this = (private_pkcs7_attributes_t*)pkcs7_attributes_create();
|
|
this->encoding = chunk_clone(chunk);
|
|
if (!parse_attributes(chunk, level, this))
|
|
{
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
return &this->public;
|
|
}
|