extended and debugged PKCS#7 signedData support
authorAndreas Steffen <andreas.steffen@strongswan.org>
Fri, 1 Feb 2008 14:19:26 +0000 (14:19 -0000)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Fri, 1 Feb 2008 14:19:26 +0000 (14:19 -0000)
src/libstrongswan/Makefile.am
src/libstrongswan/crypto/pkcs7.c
src/libstrongswan/crypto/pkcs7.h
src/libstrongswan/crypto/pkcs9.c [new file with mode: 0644]
src/libstrongswan/crypto/pkcs9.h [new file with mode: 0644]

index e8859ad..fc642c6 100644 (file)
@@ -35,6 +35,7 @@ crypto/hmac.c crypto/hmac.h \
 crypto/ietf_attr_list.c crypto/ietf_attr_list.h \
 crypto/ocsp.c crypto/ocsp.h \
 crypto/pkcs7.c crypto/pkcs7.h \
+crypto/pkcs9.c crypto/pkcs9.h \
 crypto/prfs/fips_prf.c crypto/prfs/fips_prf.h \
 crypto/prfs/hmac_prf.c crypto/prfs/hmac_prf.h \
 crypto/prfs/prf.c crypto/prfs/prf.h \
index 820cbd5..e6e2679 100644 (file)
@@ -32,6 +32,7 @@
 #include <asn1/asn1.h>
 #include <asn1/oid.h>
 #include <crypto/x509.h>
+#include <crypto/pkcs9.h>
 #include <crypto/hashers/hasher.h>
 #include <crypto/crypters/crypter.h>
 #include <crypto/rsa/rsa_public_key.h>
@@ -79,7 +80,7 @@ struct private_pkcs7_t {
        /**
         * ASN.1 encoded attributes
         */
-       chunk_t attributes;
+       pkcs9_t *attributes;
 
        /**
         * Linked list of X.509 certificates
@@ -214,7 +215,7 @@ static char ASN1_pkcs7_encrypted_data_oid_str[] = {
                  0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06
 };
 
-static const chunk_t ASN1_pkcs7_data_oid = 
+const chunk_t ASN1_pkcs7_data_oid = 
                                                chunk_from_buf(ASN1_pkcs7_data_oid_str);
 static const chunk_t ASN1_pkcs7_signed_data_oid =
                                                chunk_from_buf(ASN1_pkcs7_signed_data_oid_str);
@@ -246,24 +247,6 @@ static const chunk_t ASN1_des_cbc_oid =
                                                chunk_from_buf(ASN1_des_cbc_oid_str);
 
 /**
- * PKCS#7 attribute type OIDs
- */
-static u_char ASN1_contentType_oid_str[] = {
-       0x06, 0x09,
-                 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x03
-};
-
-static u_char ASN1_messageDigest_oid_str[] = {
-       0x06, 0x09,
-                 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x04
-};
-
-static const chunk_t ASN1_contentType_oid =
-                                               chunk_from_buf(ASN1_contentType_oid_str);
-static const chunk_t ASN1_messageDigest_oid =
-                                               chunk_from_buf(ASN1_messageDigest_oid_str);
-
-/**
  * Implements pkcs7_t.is_data.
  */
 static bool is_data(private_pkcs7_t *this)
@@ -369,7 +352,7 @@ static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert)
                                break;
                        case PKCS7_SIGNED_CERT:
                                {
-                                       x509_t *cert = x509_create_from_chunk(object, level+1);
+                                       x509_t *cert = x509_create_from_chunk(chunk_clone(object), level+1);
 
                                        if (cert)
                                        {
@@ -391,8 +374,8 @@ static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert)
                                }
                                break;
                        case PKCS7_AUTH_ATTRIBUTES:
-                               this->attributes = object;
-                               *this->attributes.ptr = ASN1_SET;
+                               *object.ptr = ASN1_SET;
+                               this->attributes = pkcs9_create_from_chunk(object, level+1);
                                break;
                        case PKCS7_DIGEST_ALGORITHM:
                                digest_alg = parse_algorithmIdentifier(object, level, NULL);
@@ -409,8 +392,8 @@ static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert)
        /* check the signature only if a cacert is available */
        if (cacert != NULL)
        {
-               rsa_public_key_t *signer = cacert->get_public_key(cacert);
                hash_algorithm_t algorithm = hasher_algorithm_from_oid(digest_alg);
+               rsa_public_key_t *signer = cacert->get_public_key(cacert);
 
                if (signerInfos == 0)
                {
@@ -422,7 +405,7 @@ static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert)
                        DBG1("more than one signerInfo object found");
                        return FALSE;
                }
-               if (this->attributes.ptr == NULL)
+               if (this->attributes == NULL)
                {
                        DBG1("no authenticatedAttributes object found");
                        return FALSE;
@@ -433,7 +416,7 @@ static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert)
                        return FALSE;
                }
                if (signer->verify_emsa_pkcs1_signature(signer, algorithm,
-                       this->attributes, encrypted_digest) != SUCCESS)
+                               this->attributes->get_encoding(this->attributes), encrypted_digest) != SUCCESS)
                {
                        DBG1("invalid digest signature");
                        return FALSE;
@@ -657,7 +640,7 @@ static chunk_t get_contentInfo(private_pkcs7_t *this)
 
        return (this->content.ptr == NULL)
                        ? asn1_simple_object(ASN1_SEQUENCE, content_type)
-                       : asn1_wrap(ASN1_SEQUENCE, "cc",
+                       : asn1_wrap(ASN1_SEQUENCE, "cm",
                                        content_type,
                                        asn1_simple_object(ASN1_CONTEXT_C_0, this->content)
                          );
@@ -672,12 +655,34 @@ static iterator_t *create_certificate_iterator(const private_pkcs7_t *this)
 }
 
 /**
+ * Implements pkcs7_t.set_certificate
+ */
+static void set_certificate(private_pkcs7_t *this, x509_t *cert)
+{
+       if (cert)
+       {
+               /* TODO the certificate is currently not cloned */
+               this->certs->insert_last(this->certs, cert);
+       }
+}
+
+/**
+ * Implements pkcs7_t.set_attributes
+ */
+static void set_attributes(private_pkcs7_t *this, pkcs9_t *attributes)
+{
+       this->attributes = attributes;
+}
+
+/**
  * build a DER-encoded issuerAndSerialNumber object
  */
 chunk_t pkcs7_build_issuerAndSerialNumber(x509_t *cert)
 {
+       identification_t *issuer = cert->get_issuer(cert);
+
     return asn1_wrap(ASN1_SEQUENCE, "cm",
-                       cert->get_issuer(cert),
+                       issuer->get_encoding(issuer),
                        asn1_simple_object(ASN1_INTEGER, cert->get_serialNumber(cert)));
 }
 
@@ -687,7 +692,7 @@ chunk_t pkcs7_build_issuerAndSerialNumber(x509_t *cert)
 bool build_envelopedData(private_pkcs7_t *this, x509_t *cert,
                                                 encryption_algorithm_t alg)
 {
-       chunk_t iv, symmetricKey, out, alg_oid;
+       chunk_t iv, symmetricKey, in, out, alg_oid;
        crypter_t *crypter;
 
        /* select OID of symmetric encryption algorithm */
@@ -700,13 +705,15 @@ bool build_envelopedData(private_pkcs7_t *this, x509_t *cert,
                        alg_oid = ASN1_3des_ede_cbc_oid;
                        break;
                default:
+                       DBG1("  encryption algorithm %N not supported",
+                                 encryption_algorithm_names, alg);
                        return FALSE;
        }
 
        crypter = crypter_create(alg, 0);
        if (crypter == NULL)
        {
-               DBG1("could not create crypter for algorithm %N",
+               DBG1("  could not create crypter for algorithm %N",
                         encryption_algorithm_names, alg);
                return FALSE;
        }
@@ -719,11 +726,11 @@ bool build_envelopedData(private_pkcs7_t *this, x509_t *cert,
 
                randomizer->allocate_random_bytes(randomizer,
                         crypter->get_key_size(crypter), &symmetricKey);
-               DBG4("symmetric encryption key: %B", &symmetricKey);
+               DBG4("  symmetric encryption key: %B", &symmetricKey);
 
                randomizer->allocate_pseudo_random_bytes(randomizer,
                        crypter->get_block_size(crypter), &iv);
-               DBG4("initialization vector: %B", &iv);
+               DBG4("  initialization vector: %B", &iv);
 
                randomizer->destroy(randomizer);
        }
@@ -733,31 +740,27 @@ bool build_envelopedData(private_pkcs7_t *this, x509_t *cert,
         */
        {
                size_t block_size = crypter->get_block_size(crypter);
-               size_t padding = this->data.len % block_size;
+               size_t padding = block_size - this->data.len % block_size;
 
-               if (padding == 0)
-               {
-                       padding += block_size;
-               }
+               in.len = this->data.len + padding;
+               in.ptr = malloc(in.len);
 
-               out.len = this->data.len + padding;
-               out.ptr = malloc(out.len);
-
-               DBG2("padding %d bytes of data to multiple block size of %d bytes",
-                       (int)this->data.len, (int)out.len);
+               DBG2("  padding %d bytes of data to multiple block size of %d bytes",
+                       (int)this->data.len, (int)in.len);
 
                /* copy data */
-               memcpy(out.ptr, this->data.ptr, this->data.len);
+               memcpy(in.ptr, this->data.ptr, this->data.len);
                /* append padding */
-               memset(out.ptr + this->data.len, padding, padding);
+               memset(in.ptr + this->data.len, padding, padding);
        }
-       DBG3("padded unencrypted data: %B", &out);
+       DBG3("  padded unencrypted data: %B", &in);
 
        /* symmetric encryption of data object */
        crypter->set_key(crypter, symmetricKey);
-       crypter->encrypt(crypter, this->data, iv, &out);
+       crypter->encrypt(crypter, in, iv, &out);
        crypter->destroy(crypter);
-    DBG3("encrypted data: %B", &out);
+       chunk_free_randomized(&in);
+    DBG3("  encrypted data: %B", &out);
 
        /* build pkcs7 enveloped data object */ 
        {
@@ -797,10 +800,70 @@ bool build_envelopedData(private_pkcs7_t *this, x509_t *cert,
 /**
  * Implements pkcs7_t.build_signedData.
  */
-bool build_signedData(private_pkcs7_t *this, rsa_private_key_t *key,
+bool build_signedData(private_pkcs7_t *this, rsa_private_key_t *private_key,
                                          hash_algorithm_t alg)
 {
-       return FALSE;
+       int signature_oid = hasher_signature_algorithm_to_oid(alg);
+       chunk_t authenticatedAttributes = chunk_empty;
+       chunk_t encryptedDigest = chunk_empty;
+       chunk_t signerInfo;
+       x509_t *cert;
+
+       if (this->certs->get_first(this->certs, (void**)&cert) != SUCCESS)
+       {
+               DBG1("  no pkcs7 signer certificate found");
+               return FALSE;
+       }
+
+       if (this->attributes != NULL)
+       {
+               chunk_t attributes = this->attributes->get_encoding(this->attributes);
+
+               if (attributes.ptr)
+               {
+                       private_key->build_emsa_pkcs1_signature(private_key, alg,
+                                                       attributes, &encryptedDigest);
+                       authenticatedAttributes = chunk_clone(attributes);
+                       *authenticatedAttributes.ptr = ASN1_CONTEXT_C_0;
+               }
+       }
+       else if (this->data.ptr != NULL)
+       {
+               private_key->build_emsa_pkcs1_signature(private_key, alg,
+                                               this->data, &encryptedDigest);
+       }
+       if (encryptedDigest.ptr)
+       {
+               encryptedDigest = asn1_wrap(ASN1_OCTET_STRING, "m", encryptedDigest);
+       }
+
+       signerInfo = asn1_wrap(ASN1_SEQUENCE, "cmcmcm",
+                                       ASN1_INTEGER_1,
+                                       pkcs7_build_issuerAndSerialNumber(cert),
+                                       asn1_algorithmIdentifier(signature_oid),
+                                       authenticatedAttributes,
+                                       asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
+                                       encryptedDigest);
+
+       if (this->data.ptr != NULL)
+       {
+               this->content = asn1_simple_object(ASN1_OCTET_STRING, this->data);
+               chunk_free(&this->data);
+       }
+       this->type = OID_PKCS7_DATA;
+       this->data = get_contentInfo(this);
+       chunk_free(&this->content);
+
+       this->type = OID_PKCS7_SIGNED_DATA;
+
+       this->content = asn1_wrap(ASN1_SEQUENCE, "cmcmm",
+                       ASN1_INTEGER_1,
+                       asn1_simple_object(ASN1_SET, asn1_algorithmIdentifier(signature_oid)),
+                       this->data,
+                       asn1_simple_object(ASN1_CONTEXT_C_0, cert->get_certificate(cert)),
+                       asn1_wrap(ASN1_SET, "m", signerInfo));
+
+       return TRUE;
 }
 
 /**
@@ -808,7 +871,9 @@ bool build_signedData(private_pkcs7_t *this, rsa_private_key_t *key,
  */
 static void destroy(private_pkcs7_t *this)
 {
+       DESTROY_IF(this->attributes);
        this->certs->destroy_offset(this->certs, offsetof(x509_t, destroy));
+       free(this->content.ptr);
        free(this->data.ptr);
        free(this);
 }
@@ -844,7 +909,7 @@ static bool parse_contentInfo(chunk_t blob, u_int level0, private_pkcs7_t *cInfo
                }
                else if (objectID == PKCS7_INFO_CONTENT)
                {
-                       cInfo->content = object;
+                       cInfo->content = chunk_clone(object);
                }
                objectID++;
        }
@@ -864,7 +929,7 @@ static private_pkcs7_t *pkcs7_create_empty(void)
        this->parsed = FALSE;
        this->level = 0;
        this->data = chunk_empty;
-       this->attributes = chunk_empty;
+       this->attributes = NULL;
        this->certs = linked_list_create();
 
        /*public functions */
@@ -877,6 +942,8 @@ static private_pkcs7_t *pkcs7_create_empty(void)
        this->public.get_data = (chunk_t (*) (pkcs7_t*))get_data;
        this->public.get_contentInfo = (chunk_t (*) (pkcs7_t*))get_contentInfo;
        this->public.create_certificate_iterator = (iterator_t* (*) (pkcs7_t*))create_certificate_iterator;
+       this->public.set_certificate = (void (*) (pkcs7_t*,x509_t*))set_certificate;
+       this->public.set_attributes = (void (*) (pkcs7_t*,pkcs9_t*))set_attributes;
        this->public.build_envelopedData = (bool (*) (pkcs7_t*,x509_t*,encryption_algorithm_t))build_envelopedData;
        this->public.build_signedData = (bool (*) (pkcs7_t*,rsa_private_key_t*,hash_algorithm_t))build_signedData;
        this->public.destroy = (void (*) (pkcs7_t*))destroy;
@@ -903,14 +970,34 @@ pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level)
 /*
  * Described in header.
  */
-pkcs7_t *pkcs7_create_from_data(chunk_t data, chunk_t attributes, x509_t *cert)
+pkcs7_t *pkcs7_create_from_data(chunk_t data)
 {
        private_pkcs7_t *this = pkcs7_create_empty();
 
        this->data = chunk_clone(data);
-       this->attributes = attributes;
-       this->certs->insert_last(this->certs, cert);
        this->parsed = TRUE;
 
        return &this->public;
 }
+
+/*
+ * Described in header.
+ */
+pkcs7_t *pkcs7_create_from_file(const char *filename, const char *label)
+{
+       bool pgp = FALSE;
+       chunk_t chunk = chunk_empty;
+       char cert_label[BUF_LEN];
+       pkcs7_t *pkcs7;
+
+       snprintf(cert_label, BUF_LEN, "%s pkcs7", label);
+
+       if (!pem_asn1_load_file(filename, NULL, cert_label, &chunk, &pgp))
+       {
+               return NULL;
+       }
+
+       pkcs7 = pkcs7_create_from_chunk(chunk, 0);
+       free(chunk.ptr);
+       return pkcs7;
+}
index 8e5166d..05ddd3a 100644 (file)
@@ -31,16 +31,19 @@ typedef struct pkcs7_t pkcs7_t;
 
 #include <library.h>
 #include <crypto/x509.h>
+#include <crypto/pkcs9.h>
 #include <crypto/rsa/rsa_private_key.h>
 #include <crypto/crypters/crypter.h>
 #include <utils/iterator.h>
 
+extern const chunk_t ASN1_pkcs7_data_oid;
+
 /**
  * @brief PKCS#7 contentInfo object.
  * 
  * @b Constructors:
  *  -pkcs7_create_from_chunk()
- *  -pkcs7_create()
+ *  -pkcs7_create_from_data()
  *
  * @ingroup crypto
  */
@@ -115,12 +118,36 @@ struct pkcs7_t {
        /**
         * @brief Create an iterator for the certificates.
         * 
-        * @param this                          calling object
-        * @return                                      iterator for the certificates
+        * @param this                  calling object
+        * @return                              iterator for the certificates
         */
        iterator_t *(*create_certificate_iterator) (pkcs7_t *this);
 
        /**
+        * @brief Add a certificate.
+        * 
+        * @param this                  calling object
+        * @param cert                  certificate to be included
+        */
+       void (*set_certificate) (pkcs7_t *this, x509_t *cert);
+
+       /**
+        * @brief Add authenticated attributes.
+        * 
+        * @param this                  calling object
+        * @param attributes    attributes to be included
+        */
+       void (*set_attributes) (pkcs7_t *this, pkcs9_t *attributes);
+
+       /**
+        * @brief Build a data object
+        *
+        * @param this                  PKCS#7 data to be built
+        * @return                              TRUE if build was successful
+        */
+       bool (*build_data) (pkcs7_t *this);
+
+       /**
         * @brief Build an envelopedData object
         *
         * @param this                  PKCS#7 data object to envelop
@@ -163,12 +190,22 @@ pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level);
  * @brief Create a PKCS#7 contentInfo object
  * 
  * @param chunk                        chunk containing data
- * @param attributes   chunk containing attributes
- * @param cert                 certificate to be included in the pkcs7_contentInfo object
  * @return                             created pkcs7_contentInfo object.
  * 
  * @ingroup crypto
  */
-pkcs7_t *pkcs7_create_from_data(chunk_t data, chunk_t attributes, x509_t *cert);
+pkcs7_t *pkcs7_create_from_data(chunk_t data);
+
+/**
+ * @brief Read a X.509 certificate from a DER encoded file.
+ * 
+ * @param filename     file containing DER encoded data
+ * @param label                label describing kind of PKCS#7 file
+ * @return                     created pkcs7_t object, or NULL if invalid.
+ * 
+ * @ingroup crypto
+ */
+pkcs7_t *pkcs7_create_from_file(const char *filename, const char *label);
+
 
 #endif /* _PKCS7_H */
diff --git a/src/libstrongswan/crypto/pkcs9.c b/src/libstrongswan/crypto/pkcs9.c
new file mode 100644 (file)
index 0000000..d7aecf3
--- /dev/null
@@ -0,0 +1,424 @@
+/**
+ * @file pkcs9.c
+ *
+ * @brief Implementation of pkcs9_t.
+ *
+ */
+
+/*
+ * 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 <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.
+ *
+ * RCSID $Id: pkcs7.c 3423 2008-01-22 10:32:37Z andreas $
+ */
+
+#include <library.h>
+#include <debug.h>
+
+#include <asn1/oid.h>
+#include <asn1/asn1.h>
+#include <utils/linked_list.h>
+
+#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.
+        * 
+        * @param this                  attribute to destroy
+        */
+       void (*destroy) (attribute_t *this);
+
+};
+
+/* 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 */
+};
+
+#define ATTRIBUTE_OBJ_TYPE     2
+#define ATTRIBUTE_OBJ_VALUE    4
+#define ATTRIBUTE_OBJ_ROOF     7
+
+/**
+ * PKCS#9 attribute type OIDs
+ */
+static u_char ASN1_contentType_oid_str[] = {
+       0x06, 0x09,
+                 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x03
+};
+
+static u_char ASN1_messageDigest_oid_str[] = {
+       0x06, 0x09,
+                 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x04
+};
+
+static u_char ASN1_signingTime_oid_str[] = {
+       0x06, 0x09,
+                 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x05
+};
+
+static char ASN1_messageType_oid_str[] = {
+       0x06, 0x0A,
+                 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x02
+};
+
+static char ASN1_senderNonce_oid_str[] = {
+       0x06, 0x0A,
+                 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x05
+};
+
+static char ASN1_transId_oid_str[] = {
+       0x06, 0x0A,
+                 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x07
+};
+
+static const chunk_t ASN1_contentType_oid =
+                                               chunk_from_buf(ASN1_contentType_oid_str);
+static const chunk_t ASN1_messageDigest_oid =
+                                               chunk_from_buf(ASN1_messageDigest_oid_str);
+static const chunk_t ASN1_signingTime_oid =
+                                               chunk_from_buf(ASN1_signingTime_oid_str);
+static const chunk_t ASN1_messageType_oid =
+                                               chunk_from_buf(ASN1_messageType_oid_str);
+static const chunk_t ASN1_senderNonce_oid =
+                                               chunk_from_buf(ASN1_senderNonce_oid_str);
+static const chunk_t ASN1_transId_oid =
+                                               chunk_from_buf(ASN1_transId_oid_str);
+
+/**
+ * return the ASN.1 encoded OID of a PKCS#9 attribute
+ */
+static chunk_t asn1_attributeIdentifier(int oid)
+{
+       switch (oid)
+       {
+               case OID_PKCS9_CONTENT_TYPE:
+                       return ASN1_contentType_oid;
+               case OID_PKCS9_MESSAGE_DIGEST:
+                       return ASN1_messageDigest_oid;
+               case OID_PKCS9_SIGNING_TIME:
+                       return ASN1_signingTime_oid;
+               case OID_PKI_MESSAGE_TYPE:
+                       return ASN1_messageType_oid;
+               case OID_PKI_SENDER_NONCE:
+                       return ASN1_senderNonce_oid;
+               case OID_PKI_TRANS_ID:
+                       return ASN1_transId_oid;;
+               default:
+                       return chunk_empty;
+       }
+}
+
+/**
+ * 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 = malloc_thing(attribute_t);
+
+       this->oid = oid;
+       this->value = chunk_clone(value);
+       this->encoding = asn1_wrap(ASN1_SEQUENCE, "cm",
+                                               asn1_attributeIdentifier(oid),
+                                               asn1_simple_object(ASN1_SET, value));
+       this->destroy = (void (*) (attribute_t*))attribute_destroy;
+       return this;
+}
+
+/**
+ * Implements pkcs9_t.build_encoding
+ */
+static void build_encoding(private_pkcs9_t *this)
+{
+       iterator_t *iterator;
+       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 */
+       iterator = this->attributes->create_iterator(this->attributes, TRUE);
+
+       while (iterator->iterate(iterator, (void**)&attribute))
+       {
+               attributes_len += attribute->encoding.len;
+       }
+       iterator->destroy(iterator);
+
+       /* allocate memory for the attributes and build the encoding */
+       {
+               u_char *pos = build_asn1_object(&this->encoding, ASN1_SET, attributes_len);
+               
+               iterator = this->attributes->create_iterator(this->attributes, TRUE);
+
+               while (iterator->iterate(iterator, (void**)&attribute))
+               {
+                       memcpy(pos, attribute->encoding.ptr, attribute->encoding.len);
+            pos += attribute->encoding.len;
+               }
+               iterator->destroy(iterator);
+       }
+}
+
+/**
+ * Implements pkcs9_t.get_encoding
+ */
+static chunk_t get_encoding(private_pkcs9_t *this)
+{
+       if (this->encoding.ptr == NULL)
+       {
+               build_encoding(this);
+       }
+       return this->encoding;
+}
+
+/**
+ * Implements pkcs9_t.get_attribute
+ */
+static chunk_t get_attribute(private_pkcs9_t *this, int oid)
+{
+       return chunk_empty;
+}
+
+/**
+ * Implements pkcs9_t.set_attribute
+ */
+static void set_attribute(private_pkcs9_t *this, int oid, chunk_t value)
+{
+       attribute_t *attribute = attribute_create(oid, value);
+
+       this->attributes->insert_last(this->attributes, (void*)attribute);
+}
+
+/**
+ * Implements pkcs9_t.destroy
+ */
+static void destroy(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 = malloc_thing(private_pkcs9_t);
+       
+       /* initialize */
+       this->encoding = chunk_empty;
+       this->attributes = linked_list_create();
+
+       /*public functions */
+       this->public.build_encoding = (void (*) (pkcs9_t*))build_encoding;
+       this->public.get_encoding = (chunk_t (*) (pkcs9_t*))get_encoding;
+       this->public.get_attribute = (chunk_t (*) (pkcs9_t*,int))get_attribute;
+       this->public.set_attribute = (void (*) (pkcs9_t*,int,chunk_t))set_attribute;
+       this->public.destroy = (void (*) (pkcs9_t*))destroy;
+
+       return this;
+}
+
+/*
+ * Described in header.
+ */
+pkcs9_t *pkcs9_create(void)
+{
+       private_pkcs9_t *this = pkcs9_create_empty();
+
+       return &this->public;
+}
+
+/**
+ * Parse a PKCS#9 attribute list
+ */
+static bool parse_attributes(chunk_t chunk, int level0, private_pkcs9_t* this)
+{
+       asn1_ctx_t ctx;
+       chunk_t object;
+       u_int level;
+       int oid = OID_UNKNOWN;
+       int objectID = 0;
+
+       asn1_init(&ctx, chunk, level0, FALSE, FALSE);
+
+       while (objectID < ATTRIBUTE_OBJ_ROOF)
+       {
+               if (!extract_object(attributesObjects, &objectID, &object, &level, &ctx))
+               {
+            return FALSE;
+               }
+
+               switch (objectID)
+               {
+                       case ATTRIBUTE_OBJ_TYPE:
+                               oid = 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 (!parse_asn1_simple_object(&object, type, level+1, oid_names[oid].name))
+                                               {
+                                                       return FALSE;
+                                               }
+                                       }
+                               }
+               }
+               objectID++;
+       }
+       return TRUE;
+}
+
+
+ /*
+ * 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;
+}
diff --git a/src/libstrongswan/crypto/pkcs9.h b/src/libstrongswan/crypto/pkcs9.h
new file mode 100644 (file)
index 0000000..8dbdb7c
--- /dev/null
@@ -0,0 +1,105 @@
+/**
+ * @file pkcs7.h
+ * 
+ * @brief Interface of pkcs9_t.
+ * 
+ */
+
+/*
+  * 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 <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.
+ *
+ * RCSID $Id: pkcs7.h 3423 2008-01-22 10:32:37Z andreas $
+ */
+
+#ifndef _PKCS9_H
+#define _PKCS9_H
+
+typedef struct pkcs9_t pkcs9_t;
+
+#include <library.h>
+
+/**
+ * @brief PKCS#9 .
+ * 
+ * @b Constructors:
+ *  -pkcs9_create_from_chunk()
+ *  -pkcs9_create()
+ *
+ * @ingroup crypto
+ */
+struct pkcs9_t {
+       /**
+        * @brief generate ASN.1 encoding of attribute list
+        *
+        * @param this                  PKCS#9 attribute list to be encoded
+        */
+       void (*build_encoding) (pkcs9_t *this);
+
+       /**
+        * @brief gets ASN.1 encoding of PKCS#9 attribute list
+        *
+        * @param this                  calling object
+        * @return                              ASN.1 encoded PKCSI#9 list
+        */
+       chunk_t (*get_encoding) (pkcs9_t *this);
+
+       /**
+        * @brief gets a PKCS#9 attribute
+        *
+        * @param this                  calling object
+        * @param oid                   OID of the attribute
+        * @return                              ASN.1 encoded value of the attribute
+        */
+       chunk_t (*get_attribute) (pkcs9_t *this, int oid);
+
+       /**
+        * @brief adds a PKCS#9 attribute
+        *
+        * @param this                  calling object
+        * @param oid                   OID of the attribute
+        * @param value                 ASN.1 encoded value of the attribute 
+        */
+       void (*set_attribute) (pkcs9_t *this, int oid, chunk_t value);
+
+       /**
+        * @brief Destroys the PKCS#9 attribute list.
+        *
+        * @param this                  PKCS#9 attribute list to destroy
+        */
+       void (*destroy) (pkcs9_t *this);
+};
+
+/**
+ * @brief Read a PKCS#9 attribute list from a DER encoded chunk.
+ * 
+ * @param chunk                chunk containing DER encoded data
+ * @param level                ASN.1 parsing start level
+ * @return                     created pkcs9 attribute list, or NULL if invalid.
+ * 
+ * @ingroup crypto
+ */
+pkcs9_t *pkcs9_create_from_chunk(chunk_t chunk, u_int level);
+
+/**
+ * @brief Create an empty PKCS#9 attribute list
+ * 
+ * @param chunk                        chunk containing data
+ * @return                             created pkcs9 attribute list.
+ * 
+ * @ingroup crypto
+ */
+pkcs9_t *pkcs9_create(void);
+
+#endif /* _PKCS9_H */