/* * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2008 Andreas Steffen * Hochschule fuer Technik Rapperswil, Switzerland * * 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 . * * 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 #include #include #include #include #include #include "pkcs9.h" typedef struct private_pkcs9_t private_pkcs9_t; /** * Private data of a pkcs9_t attribute list. */ struct private_pkcs9_t { /** * Public interface */ pkcs9_t public; /** * DER encoding of PKCS#9 attributes */ chunk_t encoding; /** * Linked list of PKCS#9 attributes */ linked_list_t *attributes; }; typedef struct attribute_t attribute_t; /** * 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; /** * Destroys the attribute. */ void (*destroy) (attribute_t *this); }; /** * return the ASN.1 encoding of a PKCS#9 attribute */ static asn1_t asn1_attributeType(int oid) { asn1_t type; switch (oid) { case OID_PKCS9_CONTENT_TYPE: type = ASN1_OID; break; case OID_PKCS9_SIGNING_TIME: type = ASN1_UTCTIME; break; case OID_PKCS9_MESSAGE_DIGEST: type = ASN1_OCTET_STRING; break; case OID_PKI_MESSAGE_TYPE: type = ASN1_PRINTABLESTRING; break; case OID_PKI_STATUS: type = ASN1_PRINTABLESTRING; break; case OID_PKI_FAIL_INFO: type = ASN1_PRINTABLESTRING; break; case OID_PKI_SENDER_NONCE: type = ASN1_OCTET_STRING; break; case OID_PKI_RECIPIENT_NONCE: type = ASN1_OCTET_STRING; break; case OID_PKI_TRANS_ID: type = ASN1_PRINTABLESTRING; break; default: type = ASN1_EOC; } return type; } /** * Destroy an attribute_t object. */ static void attribute_destroy(attribute_t *this) { free(this->value.ptr); free(this->encoding.ptr); free(this); } /** * Create an attribute_t object. */ static attribute_t *attribute_create(int oid, chunk_t value) { attribute_t *this; INIT(this, .destroy = attribute_destroy, .oid = oid, .value = chunk_clone(value), .encoding = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(oid), asn1_simple_object(ASN1_SET, value)), ); return this; } METHOD(pkcs9_t, build_encoding, void, private_pkcs9_t *this) { enumerator_t *enumerator; attribute_t *attribute; u_int attributes_len = 0; if (this->encoding.ptr) { chunk_free(&this->encoding); } if (this->attributes->get_count(this->attributes) == 0) { return; } /* compute the total length of the encoded attributes */ enumerator = this->attributes->create_enumerator(this->attributes); while (enumerator->enumerate(enumerator, (void**)&attribute)) { attributes_len += attribute->encoding.len; } enumerator->destroy(enumerator); /* allocate memory for the attributes and build the encoding */ { u_char *pos = asn1_build_object(&this->encoding, ASN1_SET, attributes_len); enumerator = this->attributes->create_enumerator(this->attributes); while (enumerator->enumerate(enumerator, (void**)&attribute)) { memcpy(pos, attribute->encoding.ptr, attribute->encoding.len); pos += attribute->encoding.len; } enumerator->destroy(enumerator); } } METHOD(pkcs9_t, get_encoding, chunk_t, private_pkcs9_t *this) { if (this->encoding.ptr == NULL) { build_encoding(this); } return this->encoding; } METHOD(pkcs9_t, get_attribute, chunk_t, private_pkcs9_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, (void**)&attribute)) { if (attribute->oid == oid) { value = attribute->value; break; } } enumerator->destroy(enumerator); if (value.ptr && !asn1_parse_simple_object(&value, asn1_attributeType(oid), 0, oid_names[oid].name)) { return chunk_empty; } return value; } METHOD(pkcs9_t, set_attribute_raw, void, private_pkcs9_t *this, int oid, chunk_t value) { attribute_t *attribute = attribute_create(oid, value); this->attributes->insert_last(this->attributes, attribute); chunk_free(&value); } METHOD(pkcs9_t, set_attribute, void, private_pkcs9_t *this, int oid, chunk_t value) { chunk_t attr = asn1_simple_object(asn1_attributeType(oid), value); set_attribute_raw(this, oid, attr); } METHOD(pkcs9_t, destroy, void, private_pkcs9_t *this) { this->attributes->destroy_offset(this->attributes, offsetof(attribute_t, destroy)); free(this->encoding.ptr); free(this); } /** * Generic private constructor */ static private_pkcs9_t *pkcs9_create_empty(void) { private_pkcs9_t *this; INIT(this, .public = { .build_encoding = _build_encoding, .get_encoding = _get_encoding, .get_attribute = _get_attribute, .set_attribute = _set_attribute, .set_attribute_raw = _set_attribute_raw, .destroy = _destroy, }, .attributes = linked_list_create(), ); return this; } /* * Described in header. */ pkcs9_t *pkcs9_create(void) { private_pkcs9_t *this = pkcs9_create_empty(); return &this->public; } /** * ASN.1 definition of the X.501 atttribute 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_pkcs9_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) { break; } /* add the attribute to a linked list */ { attribute_t *attribute = attribute_create(oid, object); this->attributes->insert_last(this->attributes, (void*)attribute); } /* parse known attributes */ { asn1_t type = asn1_attributeType(oid); if (type != ASN1_EOC) { if (!asn1_parse_simple_object(&object, type, parser->get_level(parser)+1, oid_names[oid].name)) { goto end; } } } } } success = parser->success(parser); end: parser->destroy(parser); return success; } /* * Described in header. */ pkcs9_t *pkcs9_create_from_chunk(chunk_t chunk, u_int level) { private_pkcs9_t *this = pkcs9_create_empty(); this->encoding = chunk_clone(chunk); if (!parse_attributes(chunk, level, this)) { destroy(this); return NULL; } return &this->public; }