diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-03-30 08:31:24 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-03-30 08:31:24 +0000 |
commit | 1c088a8b6237ec67f63c23f97a0f2dc4e99af869 (patch) | |
tree | 7fff7c8329d19bbaa910312e746a116f28fbfaf5 /src/libstrongswan/crypto/pkcs7.c | |
parent | bcc8f7ca7fd8e8ff6e8a4d579251458313133598 (diff) | |
download | vyos-strongswan-1c088a8b6237ec67f63c23f97a0f2dc4e99af869.tar.gz vyos-strongswan-1c088a8b6237ec67f63c23f97a0f2dc4e99af869.zip |
[svn-upgrade] Integrating new upstream version, strongswan (4.1.11)
Diffstat (limited to 'src/libstrongswan/crypto/pkcs7.c')
-rw-r--r-- | src/libstrongswan/crypto/pkcs7.c | 449 |
1 files changed, 408 insertions, 41 deletions
diff --git a/src/libstrongswan/crypto/pkcs7.c b/src/libstrongswan/crypto/pkcs7.c index 70510471a..48d3e2d78 100644 --- a/src/libstrongswan/crypto/pkcs7.c +++ b/src/libstrongswan/crypto/pkcs7.c @@ -7,7 +7,8 @@ /* * Copyright (C) 2005 Jan Hutter, Martin Willi - * Copyright (C) 2002-2005 Andreas Steffen + * Copyright (C) 2002-2008 Andreas Steffen + * * Hochschule fuer Technik Rapperswil, Switzerland * * This program is free software; you can redistribute it and/or modify it @@ -20,7 +21,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: pkcs7.c 3302 2007-10-12 21:57:20Z andreas $ + * RCSID $Id: pkcs7.c 3438 2008-02-02 00:29:03Z andreas $ */ #include <stdlib.h> @@ -32,8 +33,11 @@ #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> +#include <utils/randomizer.h> #include <utils/linked_list.h> #include "pkcs7.h" @@ -77,7 +81,7 @@ struct private_pkcs7_t { /** * ASN.1 encoded attributes */ - chunk_t attributes; + pkcs9_t *attributes; /** * Linked list of X.509 certificates @@ -244,25 +248,7 @@ 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_signedData. + * Implements pkcs7_t.is_data. */ static bool is_data(private_pkcs7_t *this) { @@ -278,7 +264,7 @@ static bool is_signedData(private_pkcs7_t *this) } /** - * Implements pkcs7_t.is_signedData. + * Implements pkcs7_t.is_envelopedData. */ static bool is_envelopedData(private_pkcs7_t *this) { @@ -316,6 +302,11 @@ static bool parse_data(private_pkcs7_t *this) { return FALSE; } + if (data.len == 0) + { + this->data = chunk_empty; + return TRUE; + } if (parse_asn1_simple_object(&data, ASN1_OCTET_STRING, this->level, "data")) { this->data = chunk_clone(data); @@ -328,7 +319,7 @@ static bool parse_data(private_pkcs7_t *this) } /** - * Parse PKCS#7 signedData content + * Implements pkcs7_t.parse_signedData. */ static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert) { @@ -363,11 +354,27 @@ static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert) digest_alg = parse_algorithmIdentifier(object, level, NULL); break; case PKCS7_SIGNED_CONTENT_INFO: - this->data = chunk_clone(object); + { + chunk_t pureData; + pkcs7_t *data = pkcs7_create_from_chunk(object, level+1); + + if (data == NULL) + { + return FALSE; + } + if (!data->parse_data(data)) + { + data->destroy(data); + return FALSE; + } + pureData = data->get_data(data); + this->data = (pureData.len)? chunk_clone(pureData) : chunk_empty; + data->destroy(data); + } 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) { @@ -389,8 +396,9 @@ 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); + *object.ptr = ASN1_CONTEXT_C_0; break; case PKCS7_DIGEST_ALGORITHM: digest_alg = parse_algorithmIdentifier(object, level, NULL); @@ -407,8 +415,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) { @@ -420,7 +428,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; @@ -431,7 +439,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; @@ -440,6 +448,39 @@ static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert) { DBG2("digest signature is valid"); } + if (this->data.ptr != NULL) + { + chunk_t messageDigest = this->attributes->get_messageDigest(this->attributes); + + if (messageDigest.ptr == NULL) + { + DBG1("messageDigest attribute not found"); + return FALSE; + } + else + { + hasher_t *hasher = hasher_create(algorithm); + chunk_t hash; + bool valid; + + hasher->allocate_hash(hasher, this->data, &hash); + hasher->destroy(hasher); + DBG3("hash: %B", &hash); + + valid = chunk_equals(messageDigest, hash); + free(messageDigest.ptr); + free(hash.ptr); + if (valid) + { + DBG2("messageDigest is valid"); + } + else + { + DBG1("invalid messageDigest"); + return FALSE; + } + } + } } return TRUE; } @@ -574,8 +615,9 @@ static bool parse_envelopedData(private_pkcs7_t *this, chunk_t serialNumber, } /* decrypt the content */ + crypter->set_key(crypter, symmetric_key); crypter->decrypt(crypter, encrypted_content, iv, &this->data); - DBG4("decrypted content with padding: %B", &this->data); + DBG3("decrypted content with padding: %B", &this->data); /* remove the padding */ { @@ -611,7 +653,7 @@ failed: } /** - * Implements pkcs7_t.get_data + * Implements pkcs7_t.get_data. */ static chunk_t get_data(private_pkcs7_t *this) { @@ -619,7 +661,49 @@ static chunk_t get_data(private_pkcs7_t *this) } /** - * Implements pkcs_t.create_crluri_iterator + * Implements pkcs7_t.get_contentInfo. + */ +static chunk_t get_contentInfo(private_pkcs7_t *this) +{ + chunk_t content_type; + + /* select DER-encoded OID for pkcs7_contentInfo type */ + switch(this->type) + { + case OID_PKCS7_DATA: + content_type = ASN1_pkcs7_data_oid; + break; + case OID_PKCS7_SIGNED_DATA: + content_type = ASN1_pkcs7_signed_data_oid; + break; + case OID_PKCS7_ENVELOPED_DATA: + content_type = ASN1_pkcs7_enveloped_data_oid; + break; + case OID_PKCS7_SIGNED_ENVELOPED_DATA: + content_type = ASN1_pkcs7_signed_enveloped_data_oid; + break; + case OID_PKCS7_DIGESTED_DATA: + content_type = ASN1_pkcs7_digested_data_oid; + break; + case OID_PKCS7_ENCRYPTED_DATA: + content_type = ASN1_pkcs7_encrypted_data_oid; + break; + case OID_UNKNOWN: + default: + DBG1("invalid pkcs7 contentInfo type"); + return chunk_empty; + } + + return (this->content.ptr == NULL) + ? asn1_simple_object(ASN1_SEQUENCE, content_type) + : asn1_wrap(ASN1_SEQUENCE, "cm", + content_type, + asn1_simple_object(ASN1_CONTEXT_C_0, this->content) + ); +} + +/** + * Implements pkcs7_t.create_certificate_iterator */ static iterator_t *create_certificate_iterator(const private_pkcs7_t *this) { @@ -627,11 +711,243 @@ 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", + issuer->get_encoding(issuer), + asn1_simple_object(ASN1_INTEGER, cert->get_serialNumber(cert))); +} + +/** + * Implements pkcs7_t.build_envelopedData. + */ +bool build_envelopedData(private_pkcs7_t *this, x509_t *cert, + encryption_algorithm_t alg) +{ + chunk_t iv, symmetricKey, in, out, alg_oid; + crypter_t *crypter; + + /* select OID of symmetric encryption algorithm */ + switch (alg) + { + case ENCR_DES: + alg_oid = ASN1_des_cbc_oid; + break; + case ENCR_3DES: + 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", + encryption_algorithm_names, alg); + return FALSE; + } + + /* generate a true random symmetric encryption key + * and a pseudo-random iv + */ + { + randomizer_t *randomizer = randomizer_create(); + + randomizer->allocate_random_bytes(randomizer, + crypter->get_key_size(crypter), &symmetricKey); + DBG4(" symmetric encryption key: %B", &symmetricKey); + + randomizer->allocate_pseudo_random_bytes(randomizer, + crypter->get_block_size(crypter), &iv); + DBG4(" initialization vector: %B", &iv); + + randomizer->destroy(randomizer); + } + + /* pad the data so that the total length becomes + * a multiple of the block size + */ + { + size_t block_size = crypter->get_block_size(crypter); + size_t padding = block_size - this->data.len % block_size; + + in.len = this->data.len + padding; + in.ptr = malloc(in.len); + + DBG2(" padding %d bytes of data to multiple block size of %d bytes", + (int)this->data.len, (int)in.len); + + /* copy data */ + memcpy(in.ptr, this->data.ptr, this->data.len); + /* append padding */ + memset(in.ptr + this->data.len, padding, padding); + } + DBG3(" padded unencrypted data: %B", &in); + + /* symmetric encryption of data object */ + crypter->set_key(crypter, symmetricKey); + crypter->encrypt(crypter, in, iv, &out); + crypter->destroy(crypter); + chunk_free_randomized(&in); + DBG3(" encrypted data: %B", &out); + + /* build pkcs7 enveloped data object */ + { + chunk_t contentEncryptionAlgorithm = asn1_wrap(ASN1_SEQUENCE, "cm", + alg_oid, + asn1_wrap(ASN1_OCTET_STRING, "m", iv)); + + chunk_t encryptedContentInfo = asn1_wrap(ASN1_SEQUENCE, "cmm", + ASN1_pkcs7_data_oid, + contentEncryptionAlgorithm, + asn1_wrap(ASN1_CONTEXT_S_0, "m", out)); + + chunk_t wrappedKey, encryptedKey, recipientInfo; + + rsa_public_key_t *public_key = cert->get_public_key(cert); + + public_key->pkcs1_encrypt(public_key, symmetricKey, &wrappedKey); + chunk_free_randomized(&symmetricKey); + + encryptedKey = asn1_wrap(ASN1_OCTET_STRING, "m", wrappedKey); + + recipientInfo = asn1_wrap(ASN1_SEQUENCE, "cmcm", + ASN1_INTEGER_0, + pkcs7_build_issuerAndSerialNumber(cert), + asn1_algorithmIdentifier(OID_RSA_ENCRYPTION), + encryptedKey); + + this->content = asn1_wrap(ASN1_SEQUENCE, "cmm", + ASN1_INTEGER_0, + asn1_wrap(ASN1_SET, "m", recipientInfo), + encryptedContentInfo); + this->type = OID_PKCS7_ENVELOPED_DATA; + } + return TRUE; +} + +/** + * Implements pkcs7_t.build_signedData. + */ +bool build_signedData(private_pkcs7_t *this, rsa_private_key_t *private_key, + hash_algorithm_t alg) +{ + 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) + { + if (this->data.ptr != NULL) + { + /* take the current time as signingTime */ + time_t now = time(NULL); + chunk_t signingTime = timetoasn1(&now, ASN1_UTCTIME); + + chunk_t messageDigest, attributes; + hasher_t *hasher = hasher_create(alg); + + hasher->allocate_hash(hasher, this->data, &messageDigest); + hasher->destroy(hasher); + this->attributes->set_attribute(this->attributes, + OID_PKCS9_CONTENT_TYPE, ASN1_pkcs7_data_oid); + this->attributes->set_messageDigest(this->attributes, + messageDigest); + this->attributes->set_attribute(this->attributes, + OID_PKCS9_SIGNING_TIME, signingTime); + attributes = this->attributes->get_encoding(this->attributes); + + free(messageDigest.ptr); + free(signingTime.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; +} + +/** * Implements pkcs7_t.destroy */ 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); } @@ -665,19 +981,19 @@ static bool parse_contentInfo(chunk_t blob, u_int level0, private_pkcs7_t *cInfo return FALSE; } } - else if (objectID == PKCS7_INFO_CONTENT) + else if (objectID == PKCS7_INFO_CONTENT && object.len > 0) { - cInfo->content = object; + cInfo->content = chunk_clone(object); } objectID++; } return TRUE; } -/* - * Described in header. +/** + * Generic private constructor */ -pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level) +static private_pkcs7_t *pkcs7_create_empty(void) { private_pkcs7_t *this = malloc_thing(private_pkcs7_t); @@ -685,9 +1001,9 @@ pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level) this->type = OID_UNKNOWN; this->content = chunk_empty; this->parsed = FALSE; - this->level = level + 2; + this->level = 0; this->data = chunk_empty; - this->attributes = chunk_empty; + this->attributes = NULL; this->certs = linked_list_create(); /*public functions */ @@ -698,9 +1014,25 @@ pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level) this->public.parse_signedData = (bool (*) (pkcs7_t*,x509_t*))parse_signedData; this->public.parse_envelopedData = (bool (*) (pkcs7_t*,chunk_t,rsa_private_key_t*))parse_envelopedData; 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; + return this; +} + +/* + * Described in header. + */ +pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level) +{ + private_pkcs7_t *this = pkcs7_create_empty(); + + this->level = level + 2; if (!parse_contentInfo(chunk, level, this)) { destroy(this); @@ -708,3 +1040,38 @@ pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level) } return &this->public; } + +/* + * Described in header. + */ +pkcs7_t *pkcs7_create_from_data(chunk_t data) +{ + private_pkcs7_t *this = pkcs7_create_empty(); + + this->data = chunk_clone(data); + 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; +} |