diff options
Diffstat (limited to 'src/libstrongswan/crypto')
22 files changed, 2402 insertions, 829 deletions
diff --git a/src/libstrongswan/crypto/ac.c b/src/libstrongswan/crypto/ac.c index 47605e9e1..1367494f8 100644 --- a/src/libstrongswan/crypto/ac.c +++ b/src/libstrongswan/crypto/ac.c @@ -19,17 +19,28 @@ * 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: ac.c 3300 2007-10-12 21:53:18Z andreas $ */ +#include <string.h> +#include <stdio.h> + #include <library.h> #include <debug.h> #include <asn1/asn1.h> +#include <asn1/pem.h> +#include <crypto/x509.h> +#include <crypto/ietf_attr_list.h> #include <utils/identification.h> #include <utils/linked_list.h> +#include <utils/lexparser.h> #include "ac.h" +#define ACERT_WARNING_INTERVAL 1 /* day */ + typedef struct private_x509ac_t private_x509ac_t; /** @@ -138,92 +149,6 @@ struct private_x509ac_t { }; /** - * definition of ietfAttribute kinds - */ -typedef enum { - IETF_ATTRIBUTE_OCTETS = 0, - IETF_ATTRIBUTE_OID = 1, - IETF_ATTRIBUTE_STRING = 2 -} ietfAttribute_t; - -/** - * access structure for an ietfAttribute - */ -typedef struct ietfAttr_t ietfAttr_t; - -struct ietfAttr_t { - /** - * IETF attribute kind - */ - ietfAttribute_t kind; - - /** - * IETF attribute valuse - */ - chunk_t value; - - /** - * Destroys the ietfAttr_t object. - * - * @param this ietfAttr_t to destroy - */ - void (*destroy) (ietfAttr_t *this); -}; - -/** - * Destroys an ietfAttr_t object - */ -static void ietfAttr_destroy(ietfAttr_t *this) -{ - free(this->value.ptr); - free(this); -} - -/** - * Creates an ietfAttr_t object. - */ -ietfAttr_t *ietfAttr_create(ietfAttribute_t kind, chunk_t value) -{ - ietfAttr_t *this = malloc_thing(ietfAttr_t); - - /* initialize */ - this->kind = kind; - this->value = chunk_clone(value); - - /* function */ - this->destroy = ietfAttr_destroy; - - return this; -} - -/** - * ASN.1 definition of ietfAttrSyntax - */ -static const asn1Object_t ietfAttrSyntaxObjects[] = -{ - { 0, "ietfAttrSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "policyAuthority", ASN1_CONTEXT_C_0, ASN1_OPT | - ASN1_BODY }, /* 1 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ - { 1, "values", ASN1_SEQUENCE, ASN1_LOOP }, /* 3 */ - { 2, "octets", ASN1_OCTET_STRING, ASN1_OPT | - ASN1_BODY }, /* 4 */ - { 2, "end choice", ASN1_EOC, ASN1_END }, /* 5 */ - { 2, "oid", ASN1_OID, ASN1_OPT | - ASN1_BODY }, /* 6 */ - { 2, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ - { 2, "string", ASN1_UTF8STRING, ASN1_OPT | - ASN1_BODY }, /* 8 */ - { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ - { 1, "end loop", ASN1_EOC, ASN1_END } /* 10 */ -}; - -#define IETF_ATTR_OCTETS 4 -#define IETF_ATTR_OID 6 -#define IETF_ATTR_STRING 8 -#define IETF_ATTR_ROOF 11 - -/** * ASN.1 definition of roleSyntax */ static const asn1Object_t roleSyntaxObjects[] = @@ -357,6 +282,23 @@ static err_t is_valid(const private_x509ac_t *this, time_t *until) } /** + * Implements x509ac_t.is_newer + */ +static bool is_newer(const private_x509ac_t *this, const private_x509ac_t *other) +{ + return this->notBefore > other->notBefore; +} + +/** + * Implements x509ac_t.equals_holder. + */ +static bool equals_holder(const private_x509ac_t *this, const private_x509ac_t *other) +{ + return this->holderIssuer->equals(this->holderIssuer, other->holderIssuer) + && chunk_equals(this->holderSerial, other->holderSerial); +} + +/** * parses a directoryName */ static bool parse_directoryName(chunk_t blob, int level, bool implicit, identification_t **name) @@ -364,7 +306,7 @@ static bool parse_directoryName(chunk_t blob, int level, bool implicit, identifi bool has_directoryName; linked_list_t *list = linked_list_create(); - parse_generalNames(blob, level, implicit, list); + x509_parse_generalNames(blob, level, implicit, list); has_directoryName = list->get_count(list) > 0; if (has_directoryName) @@ -398,43 +340,6 @@ static bool parse_directoryName(chunk_t blob, int level, bool implicit, identifi } /** - * parses ietfAttrSyntax - */ -static void parse_ietfAttrSyntax(chunk_t blob, int level0, linked_list_t *list) -{ - asn1_ctx_t ctx; - chunk_t object; - u_int level; - int objectID = 0; - - asn1_init(&ctx, blob, level0, FALSE, FALSE); - - while (objectID < IETF_ATTR_ROOF) - { - if (!extract_object(ietfAttrSyntaxObjects, &objectID, &object, &level, &ctx)) - { - return; - } - - switch (objectID) - { - case IETF_ATTR_OCTETS: - case IETF_ATTR_OID: - case IETF_ATTR_STRING: - { - ietfAttribute_t kind = (objectID - IETF_ATTR_OCTETS) / 2; - ietfAttr_t *attr = ietfAttr_create(kind, object); - list->insert_last(list, (void *)attr); - } - break; - default: - break; - } - objectID++; - } -} - -/** * parses roleSyntax */ static void parse_roleSyntax(chunk_t blob, int level0) @@ -470,9 +375,9 @@ static bool parse_certificate(chunk_t blob, private_x509ac_t *this) bool critical; chunk_t object; u_int level; - u_int type = OID_UNKNOWN; - u_int extn_oid = OID_UNKNOWN; int objectID = 0; + int type = OID_UNKNOWN; + int extn_oid = OID_UNKNOWN; asn1_init(&ctx, blob, 0, FALSE, FALSE); while (objectID < AC_OBJ_ROOF) @@ -549,10 +454,10 @@ static bool parse_certificate(chunk_t blob, private_x509ac_t *this) DBG2(" need to parse accessIdentity"); break; case OID_CHARGING_IDENTITY: - parse_ietfAttrSyntax(object, level, this->charging); + ietfAttr_list_create_from_chunk(object, this->charging, level); break; case OID_GROUP: - parse_ietfAttrSyntax(object, level, this->groups); + ietfAttr_list_create_from_chunk(object, this->groups, level); break; case OID_ROLE: parse_roleSyntax(object, level); @@ -577,7 +482,7 @@ static bool parse_certificate(chunk_t blob, private_x509ac_t *this) DBG2(" need to parse crlDistributionPoints"); break; case OID_AUTHORITY_KEY_ID: - parse_authorityKeyIdentifier(object, level, + x509_parse_authorityKeyIdentifier(object, level, &this->authKeyID, &this->authKeySerialNumber); break; case OID_TARGET_INFORMATION: @@ -603,7 +508,72 @@ static bool parse_certificate(chunk_t blob, private_x509ac_t *this) objectID++; } this->installed = time(NULL); - return FALSE; + return TRUE; +} + +/** + * Implementation of x509ac_t.list. + */ +static void list(const private_x509ac_t *this, FILE *out, bool utc) +{ + time_t now = time(NULL); + + fprintf(out, "%#T\n", &this->installed, utc); + + if (this->entityName) + { + fprintf(out, " holder: '%D'\n", this->entityName); + } + if (this->holderIssuer) + { + fprintf(out, " hissuer: '%D'\n", this->holderIssuer); + } + if (this->holderSerial.ptr) + { + fprintf(out, " hserial: %#B\n", &this->holderSerial); + } + + /* list all group attributes on a single line */ + fprintf(out, " groups: "); + ietfAttr_list_list(this->groups, out); + fprintf(out, "\n"); + + fprintf(out, " issuer: '%D'\n", this->issuerName); + fprintf(out, " serial: %#B\n", &this->serialNumber); + + fprintf(out, " validity: not before %#T, ", &this->notBefore, utc); + if (now < this->notBefore) + { + fprintf(out, "not valid yet (valid in %V)\n", &now, &this->notBefore); + } + else + { + fprintf(out, "ok\n"); + } + + fprintf(out, " not after %#T, ", &this->notAfter, utc); + if (now > this->notAfter) + { + fprintf(out, "expired (%V ago)\n", &now, &this->notAfter); + } + else + { + fprintf(out, "ok"); + if (now > this->notAfter - ACERT_WARNING_INTERVAL * 60 * 60 * 24) + { + fprintf(out, " (expires in %V)", &now, &this->notAfter); + } + fprintf(out, " \n"); + } + + if (this->authKeyID.ptr) + { + fprintf(out, " authkey: %#B\n", &this->authKeyID); + } + if (this->authKeySerialNumber.ptr) + { + fprintf(out, " aserial: %#B\n", &this->authKeySerialNumber); + } } /** @@ -614,10 +584,8 @@ static void destroy(private_x509ac_t *this) DESTROY_IF(this->holderIssuer); DESTROY_IF(this->entityName); DESTROY_IF(this->issuerName); - this->charging->destroy_offset(this->charging, - offsetof(ietfAttr_t, destroy)); - this->groups->destroy_offset(this->groups, - offsetof(ietfAttr_t, destroy)); + ietfAttr_list_destroy(this->charging); + ietfAttr_list_destroy(this->groups); free(this->certificate.ptr); free(this); } @@ -638,6 +606,9 @@ x509ac_t *x509ac_create_from_chunk(chunk_t chunk) /* public functions */ this->public.is_valid = (err_t (*) (const x509ac_t*,time_t*))is_valid; + this->public.is_newer = (bool (*) (const x509ac_t*,const x509ac_t*))is_newer; + this->public.equals_holder = (bool (*) (const x509ac_t*,const x509ac_t*))equals_holder; + this->public.list = (void (*) (const x509ac_t*,FILE*,bool))list; this->public.destroy = (void (*) (x509ac_t*))destroy; if (!parse_certificate(chunk, this)) diff --git a/src/libstrongswan/crypto/ac.h b/src/libstrongswan/crypto/ac.h index b7fd26c94..8a4ccbd4c 100644 --- a/src/libstrongswan/crypto/ac.h +++ b/src/libstrongswan/crypto/ac.h @@ -21,11 +21,15 @@ * 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: ac.h 3300 2007-10-12 21:53:18Z andreas $ */ #ifndef AC_H_ #define AC_H_ +#include <library.h> + typedef struct x509ac_t x509ac_t; /** @@ -48,6 +52,32 @@ struct x509ac_t { */ err_t (*is_valid) (const x509ac_t *this, time_t *until); + /** @brief Checks if this attr cert is newer than the other attr cert + * + * @param this calling object + * @param other other attr cert object + * @return TRUE if this was issued more recently than other + */ + bool (*is_newer) (const x509ac_t *this, const x509ac_t *other); + + /** + * @brief Checks if two attribute certificates belong to the same holder + * + * @param this calling attribute certificate + * @param that other attribute certificate + * @return TRUE if same holder + */ + bool (*equals_holder) (const x509ac_t *this, const x509ac_t *other); + + /** + * @brief Log the attribute certificate info to out. + * + * @param this calling object + * @param out stream to write to + * @param utc TRUE for UTC times, FALSE for local time + */ + void (*list)(const x509ac_t *this, FILE *out, bool utc); + /** * @brief Destroys the attribute certificate. * @@ -68,14 +98,13 @@ x509ac_t *x509ac_create_from_chunk(chunk_t chunk); /** * @brief Read a x509 attribute certificate from a DER encoded file. - * + * * @param filename file containing DER encoded data - * @return created x509ac_t certificate, or NULL if invalid. - * + * @return created x509ac_t certificate, or NULL if invalid. + * * @ingroup crypto */ x509ac_t *x509ac_create_from_file(const char *filename); - #endif /* AC_H_ */ diff --git a/src/libstrongswan/crypto/crl.c b/src/libstrongswan/crypto/crl.c index b4ae37b2e..d52078ea9 100755 --- a/src/libstrongswan/crypto/crl.c +++ b/src/libstrongswan/crypto/crl.c @@ -18,6 +18,8 @@ * 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: crl.c 3300 2007-10-12 21:53:18Z andreas $ */ #include <sys/stat.h> @@ -290,7 +292,8 @@ bool parse_x509crl(chunk_t blob, u_int level0, private_crl_t *crl) } else if (extn_oid == OID_AUTHORITY_KEY_ID) { - parse_authorityKeyIdentifier(object, level, &crl->authKeyID, &crl->authKeySerialNumber); + x509_parse_authorityKeyIdentifier(object, level, + &crl->authKeyID, &crl->authKeySerialNumber); } else if (extn_oid == OID_CRL_NUMBER) { @@ -304,6 +307,11 @@ bool parse_x509crl(chunk_t blob, u_int level0, private_crl_t *crl) break; case CRL_OBJ_ALGORITHM: crl->algorithm = parse_algorithmIdentifier(object, level, NULL); + if (crl->algorithm != crl->sigAlg) + { + DBG1(" signature algorithms do not agree"); + return FALSE; + } break; case CRL_OBJ_SIGNATURE: crl->signature = object; @@ -374,7 +382,14 @@ static bool is_newer(const private_crl_t *this, const private_crl_t *other) */ static bool verify(const private_crl_t *this, const rsa_public_key_t *signer) { - return signer->verify_emsa_pkcs1_signature(signer, this->tbsCertList, this->signature) == SUCCESS; + hash_algorithm_t algorithm = hasher_algorithm_from_oid(this->algorithm); + + if (algorithm == HASH_UNKNOWN) + { + DBG1(" unknown signature algorithm"); + return FALSE; + } + return signer->verify_emsa_pkcs1_signature(signer, algorithm, this->tbsCertList, this->signature) == SUCCESS; } /** diff --git a/src/libstrongswan/crypto/crl.h b/src/libstrongswan/crypto/crl.h index a367c3aff..bcf031dd4 100755 --- a/src/libstrongswan/crypto/crl.h +++ b/src/libstrongswan/crypto/crl.h @@ -18,6 +18,8 @@ * 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: crl.h 3300 2007-10-12 21:53:18Z andreas $ */ #ifndef CRL_H_ diff --git a/src/libstrongswan/crypto/diffie_hellman.c b/src/libstrongswan/crypto/diffie_hellman.c index e4062066c..605892e87 100644 --- a/src/libstrongswan/crypto/diffie_hellman.c +++ b/src/libstrongswan/crypto/diffie_hellman.c @@ -8,7 +8,7 @@ /* * Copyright (C) 1998-2002 D. Hugh Redelmeier. * Copyright (C) 1999, 2000, 2001 Henry Spencer. - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -24,11 +24,11 @@ */ #include <gmp.h> -#include <stdio.h> #include "diffie_hellman.h" #include <utils/randomizer.h> +#include <debug.h> ENUM_BEGIN(diffie_hellman_group_names, MODP_NONE, MODP_1024_BIT, "MODP_NONE", @@ -302,12 +302,12 @@ static u_int8_t group18_modulus[] = { 0x60,0xC9,0x80,0xDD,0x98,0xED,0xD3,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }; -typedef struct modulus_info_entry_t modulus_info_entry_t; +typedef struct modulus_entry_t modulus_entry_t; /** * Entry of the modulus list. */ -struct modulus_info_entry_t { +struct modulus_entry_t { /** * Group number as it is defined in file transform_substructure.h. */ @@ -321,7 +321,7 @@ struct modulus_info_entry_t { /* * Length of modulus in bytes. */ - size_t modulus_length; + size_t modulus_len; /* * Generator value. @@ -329,19 +329,18 @@ struct modulus_info_entry_t { u_int16_t generator; }; - /** * All supported modulus values. */ -static modulus_info_entry_t modulus_info_entries[] = { - {MODP_768_BIT,group1_modulus,sizeof(group1_modulus),2}, - {MODP_1024_BIT,group2_modulus,sizeof(group2_modulus),2}, - {MODP_1536_BIT,group5_modulus,sizeof(group5_modulus),2}, - {MODP_2048_BIT,group14_modulus,sizeof(group14_modulus),2}, - {MODP_3072_BIT,group15_modulus,sizeof(group15_modulus),2}, - {MODP_4096_BIT,group16_modulus,sizeof(group16_modulus),2}, - {MODP_6144_BIT,group17_modulus,sizeof(group17_modulus),2}, - {MODP_8192_BIT,group18_modulus,sizeof(group18_modulus),2}, +static modulus_entry_t modulus_entries[] = { + {MODP_768_BIT, group1_modulus, sizeof(group1_modulus), 2}, + {MODP_1024_BIT, group2_modulus, sizeof(group2_modulus), 2}, + {MODP_1536_BIT, group5_modulus, sizeof(group5_modulus), 2}, + {MODP_2048_BIT, group14_modulus, sizeof(group14_modulus), 2}, + {MODP_3072_BIT, group15_modulus, sizeof(group15_modulus), 2}, + {MODP_4096_BIT, group16_modulus, sizeof(group16_modulus), 2}, + {MODP_6144_BIT, group17_modulus, sizeof(group17_modulus), 2}, + {MODP_8192_BIT, group18_modulus, sizeof(group18_modulus), 2}, }; typedef struct private_diffie_hellman_t private_diffie_hellman_t; @@ -359,170 +358,133 @@ struct private_diffie_hellman_t { /** * Diffie Hellman group number. */ - u_int16_t dh_group_number; - - /** - * Modulus. - */ - mpz_t modulus; - - /** - * Modulus length. - */ - size_t modulus_length; + u_int16_t group; /* * Generator value. */ - u_int16_t generator; + mpz_t g; /** - * My private value . + * My private value. */ - mpz_t my_private_value; + mpz_t xa; /** * My public value. */ - mpz_t my_public_value; + mpz_t ya; /** * Other public value. */ - mpz_t other_public_value; + mpz_t yb; /** * Shared secret. */ - mpz_t shared_secret; + mpz_t zz; /** - * True if shared secret is computed and stored in my_public_value. - */ - bool shared_secret_is_computed; - - /** - * Sets the modulus for a specific diffie hellman group. - * - * @param this calling object - * @return - * SUCCESS if modulus could be found - * NOT_FOUND if modulus not supported + * Modulus. */ - status_t (*set_modulus) (private_diffie_hellman_t *this); + mpz_t p; /** - * Makes sure my public value is computed. - * - * @param this calling object + * Modulus length. */ - void (*compute_public_value) (private_diffie_hellman_t *this); + size_t p_len; /** - * Computes shared secret (other public value must be available). - * - * @param this calling object + * True if shared secret is computed and stored in my_public_value. */ - void (*compute_shared_secret) (private_diffie_hellman_t *this); + bool computed; }; /** - * Implementation of private_diffie_hellman_t.set_modulus. + * Implementation of diffie_hellman_t.set_other_public_value. */ -static status_t set_modulus(private_diffie_hellman_t *this) +static void set_other_public_value(private_diffie_hellman_t *this, chunk_t value) { - int i; - status_t status = NOT_FOUND; + mpz_t p_min_1; + + mpz_init(p_min_1); + mpz_sub_ui(p_min_1, this->p, 1); + + mpz_import(this->yb, value.len, 1, 1, 1, 0, value.ptr); - for (i = 0; i < (sizeof(modulus_info_entries) / sizeof(modulus_info_entry_t)); i++) + /* check public value: + * 1. 0 or 1 is invalid as 0^a = 0 and 1^a = 1 + * 2. a public value larger or equal the modulus is invalid */ + if (mpz_cmp_ui(this->yb, 1) > 0 || + mpz_cmp(this->yb, p_min_1) < 0) { - if (modulus_info_entries[i].group == this->dh_group_number) +#ifdef EXTENDED_DH_TEST + /* 3. test if y ^ q mod p = 1, where q = (p - 1)/2. */ + mpz_t q, one; + + mpz_init(q); + mpz_init(one); + mpz_fdiv_q_2exp(q, p_min_1, 1); + mpz_powm(one, this->yb, q, this->p); + mpz_clear(q); + if (mpz_cmp_ui(one, 1) == 0) { - chunk_t modulus_chunk; - modulus_chunk.ptr = modulus_info_entries[i].modulus; - modulus_chunk.len = modulus_info_entries[i].modulus_length; - mpz_import(this->modulus, modulus_chunk.len, 1, 1, 1, 0, modulus_chunk.ptr); - this->modulus_length = modulus_chunk.len; - this->generator = modulus_info_entries[i].generator; - status = SUCCESS; - break; + mpz_powm(this->zz, this->yb, this->xa, this->p); + this->computed = TRUE; + } + else + { + DBG1("public DH value verification failed: y ^ q mod p != 1"); } + mpz_clear(one); +#else + mpz_powm(this->zz, this->yb, this->xa, this->p); + this->computed = TRUE; +#endif } - return status; -} - -/** - * Implementation of diffie_hellman_t.set_other_public_value. - */ -static void set_other_public_value(private_diffie_hellman_t *this,chunk_t public_value) -{ - mpz_import(this->other_public_value, public_value.len, 1, 1, 1, 0, public_value.ptr); - this->compute_shared_secret(this); + else + { + DBG1("public DH value verification failed: y < 2 || y > p - 1 "); + } + mpz_clear(p_min_1); } /** * Implementation of diffie_hellman_t.get_other_public_value. */ -static status_t get_other_public_value(private_diffie_hellman_t *this,chunk_t *public_value) +static status_t get_other_public_value(private_diffie_hellman_t *this, + chunk_t *value) { - if (!this->shared_secret_is_computed) + if (!this->computed) { return FAILED; } - public_value->len = this->modulus_length; - public_value->ptr = mpz_export(NULL, NULL, 1, public_value->len, 1, 0, this->other_public_value); + value->len = this->p_len; + value->ptr = mpz_export(NULL, NULL, 1, value->len, 1, 0, this->yb); return SUCCESS; } /** - * Implementation of private_diffie_hellman_t.compute_shared_secret. - */ -static void compute_shared_secret (private_diffie_hellman_t *this) -{ - /* initialize my public value */ - mpz_init(this->shared_secret); - /* calculate my public value */ - mpz_powm(this->shared_secret,this->other_public_value,this->my_private_value,this->modulus); - - this->shared_secret_is_computed = TRUE; -} - -/** - * Implementation of private_diffie_hellman_t.compute_public_value. - */ -static void compute_public_value (private_diffie_hellman_t *this) -{ - mpz_t generator; - /* initialize generator and set it*/ - mpz_init_set_ui (generator,this->generator); - /* initialize my public value */ - mpz_init(this->my_public_value); - /* calculate my public value */ - mpz_powm(this->my_public_value,generator,this->my_private_value,this->modulus); - /* generator not used anymore */ - mpz_clear(generator); -} - -/** * Implementation of diffie_hellman_t.get_my_public_value. */ -static void get_my_public_value(private_diffie_hellman_t *this,chunk_t *public_value) +static void get_my_public_value(private_diffie_hellman_t *this,chunk_t *value) { - public_value->len = this->modulus_length; - public_value->ptr = mpz_export(NULL, NULL, 1, public_value->len, 1, 0, this->my_public_value); + value->len = this->p_len; + value->ptr = mpz_export(NULL, NULL, 1, value->len, 1, 0, this->ya); } /** * Implementation of diffie_hellman_t.get_shared_secret. */ -static status_t get_shared_secret(private_diffie_hellman_t *this,chunk_t *secret) +static status_t get_shared_secret(private_diffie_hellman_t *this, chunk_t *secret) { - if (!this->shared_secret_is_computed) + if (!this->computed) { return FAILED; } - secret->len = this->modulus_length; - secret->ptr = mpz_export(NULL, NULL, 1, secret->len, 1, 0, this->shared_secret); + secret->len = this->p_len; + secret->ptr = mpz_export(NULL, NULL, 1, secret->len, 1, 0, this->zz); return SUCCESS; } @@ -531,35 +493,57 @@ static status_t get_shared_secret(private_diffie_hellman_t *this,chunk_t *secret */ static diffie_hellman_group_t get_dh_group(private_diffie_hellman_t *this) { - return this->dh_group_number; + return this->group; } /** - * Implementation of diffie_hellman_t.destroy. + * Lookup the modulus in modulo table */ -static void destroy(private_diffie_hellman_t *this) +static status_t set_modulus(private_diffie_hellman_t *this) { - mpz_clear(this->modulus); - mpz_clear(this->my_private_value); - mpz_clear(this->my_public_value); - mpz_clear(this->other_public_value); - - if (this->shared_secret_is_computed) + int i; + status_t status = NOT_FOUND; + + for (i = 0; i < (sizeof(modulus_entries) / sizeof(modulus_entry_t)); i++) { - /* other public value gets initialized together with shared secret */ - mpz_clear(this->shared_secret); + if (modulus_entries[i].group == this->group) + { + chunk_t chunk; + chunk.ptr = modulus_entries[i].modulus; + chunk.len = modulus_entries[i].modulus_len; + mpz_import(this->p, chunk.len, 1, 1, 1, 0, chunk.ptr); + this->p_len = chunk.len; + mpz_set_ui(this->g, modulus_entries[i].generator); + status = SUCCESS; + break; + } } + return status; +} + +/** + * Implementation of diffie_hellman_t.destroy. + */ +static void destroy(private_diffie_hellman_t *this) +{ + mpz_clear(this->p); + mpz_clear(this->xa); + mpz_clear(this->ya); + mpz_clear(this->yb); + mpz_clear(this->zz); + mpz_clear(this->g); free(this); } /* * Described in header. */ -diffie_hellman_t *diffie_hellman_create(diffie_hellman_group_t dh_group_number) +diffie_hellman_t *diffie_hellman_create(diffie_hellman_group_t group) { private_diffie_hellman_t *this = malloc_thing(private_diffie_hellman_t); randomizer_t *randomizer; - chunk_t random_bytes; + chunk_t random; + status_t status; /* public functions */ this->public.get_shared_secret = (status_t (*)(diffie_hellman_t *, chunk_t *)) get_shared_secret; @@ -569,44 +553,37 @@ diffie_hellman_t *diffie_hellman_create(diffie_hellman_group_t dh_group_number) this->public.get_dh_group = (diffie_hellman_group_t (*)(diffie_hellman_t *)) get_dh_group; this->public.destroy = (void (*)(diffie_hellman_t *)) destroy; - /* private functions */ - this->set_modulus = set_modulus; - this->compute_public_value = compute_public_value; - this->compute_shared_secret = compute_shared_secret; - /* private variables */ - this->dh_group_number = dh_group_number; - mpz_init(this->modulus); - mpz_init(this->other_public_value); - mpz_init(this->my_private_value); + this->group = group; + mpz_init(this->p); + mpz_init(this->yb); + mpz_init(this->ya); + mpz_init(this->xa); + mpz_init(this->zz); + mpz_init(this->g); + + this->computed = FALSE; - /* set this->modulus */ - if (this->set_modulus(this) != SUCCESS) + /* find a modulus according to group */ + if (set_modulus(this) != SUCCESS) { - free(this); + destroy(this); return NULL; } randomizer = randomizer_create(); - if (randomizer == NULL) - { - free(this); - return NULL; - } - if (randomizer->allocate_pseudo_random_bytes(randomizer, this->modulus_length, &random_bytes) != SUCCESS) + status = randomizer->allocate_pseudo_random_bytes( + randomizer, this->p_len, &random); + randomizer->destroy(randomizer); + if (status != SUCCESS) { - randomizer->destroy(randomizer); - free(this); + destroy(this); return NULL; } + mpz_import(this->xa, random.len, 1, 1, 1, 0, random.ptr); + chunk_free(&random); - mpz_import(this->my_private_value, random_bytes.len, 1, 1, 1, 0, random_bytes.ptr); - chunk_free(&random_bytes); + mpz_powm(this->ya, this->g, this->xa, this->p); - randomizer->destroy(randomizer); - - this->compute_public_value(this); - - this->shared_secret_is_computed = FALSE; - - return &(this->public); + return &this->public; } + diff --git a/src/libstrongswan/crypto/diffie_hellman.h b/src/libstrongswan/crypto/diffie_hellman.h index 29a2ab45b..8cd06d60e 100644 --- a/src/libstrongswan/crypto/diffie_hellman.h +++ b/src/libstrongswan/crypto/diffie_hellman.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -36,7 +36,7 @@ typedef struct diffie_hellman_t diffie_hellman_t; * * See IKEv2 RFC 3.3.2 and RFC 3526. * - * @ingroup transforms + * @ingroup crypto */ enum diffie_hellman_group_t { MODP_NONE = 0, @@ -56,76 +56,74 @@ enum diffie_hellman_group_t { extern enum_name_t *diffie_hellman_group_names; /** - * @brief Implementation of the widely used Diffie-Hellman algorithm. + * @brief Implementation of the Diffie-Hellman algorithm, as in RFC2631. * * @b Constructors: * - diffie_hellman_create() * - * @ingroup transforms + * @ingroup crypto */ struct diffie_hellman_t { /** * @brief Returns the shared secret of this diffie hellman exchange. * - * @warning Space for returned secret is allocated and must be + * Space for returned secret is allocated and must be * freed by the caller. * - * @param this calling diffie_hellman_t object - * @param[out] secret shared secret will be written into this chunk + * @param this calling object + * @param secret shared secret will be written into this chunk * @return - * - SUCCESS - * - FAILED if not both DH values are set + * - SUCCESS + * - FAILED if not both DH values are set */ status_t (*get_shared_secret) (diffie_hellman_t *this, chunk_t *secret); /** * @brief Sets the public value of partner. * - * chunk gets cloned and can be destroyed afterwards. + * Chunk gets cloned and can be destroyed afterwards. * - * @param this calling diffie_hellman_t object - * @param public_value public value of partner + * @param this calling object + * @param value public value of partner */ - void (*set_other_public_value) (diffie_hellman_t *this, chunk_t public_value); + void (*set_other_public_value) (diffie_hellman_t *this, chunk_t value); /** * @brief Gets the public value of partner. * - * @warning Space for returned chunk is allocated and must be - * freed by the caller. + * Space for returned chunk is allocated and must be freed by the caller. * - * @param this calling diffie_hellman_t object - * @param[out] public_value public value of partner is stored at this location + * @param this calling object + * @param value public value of partner is stored at this location * @return - * - SUCCESS - * - FAILED if other public value not set + * - SUCCESS + * - FAILED if other public value not set */ - status_t (*get_other_public_value) (diffie_hellman_t *this, chunk_t *public_value); + status_t (*get_other_public_value) (diffie_hellman_t *this, chunk_t *value); /** - * @brief Gets the public value of caller + * @brief Gets the own public value to transmit. * - * @warning Space for returned chunk is allocated and must be - * freed by the caller. + * Space for returned chunk is allocated and must be freed by the caller. * - * @param this calling diffie_hellman_t object - * @param[out] public_value public value of caller is stored at this location + * @param this calling object + * @param value public value of caller is stored at this location */ - void (*get_my_public_value) (diffie_hellman_t *this, chunk_t *public_value); + void (*get_my_public_value) (diffie_hellman_t *this, chunk_t *value); /** * @brief Get the DH group used. * - * @param this calling diffie_hellman_t object - * @return DH group set in construction + * @param this calling object + * @return DH group set in construction */ diffie_hellman_group_t (*get_dh_group) (diffie_hellman_t *this); /** * @brief Destroys an diffie_hellman_t object. * - * @param this diffie_hellman_t object to destroy + * @param this diffie_hellman_t object to destroy */ void (*destroy) (diffie_hellman_t *this); }; @@ -133,15 +131,14 @@ struct diffie_hellman_t { /** * @brief Creates a new diffie_hellman_t object. * - * The first diffie hellman public value gets automatically created. - * - * @param dh_group_number Diffie Hellman group number to use + * @param group Diffie Hellman group number to use * @return - * - diffie_hellman_t object - * - NULL if dh group not supported + * - diffie_hellman_t object + * - NULL if dh group not supported * - * @ingroup transforms + * @ingroup crypto */ -diffie_hellman_t *diffie_hellman_create(diffie_hellman_group_t dh_group_number); +diffie_hellman_t *diffie_hellman_create(diffie_hellman_group_t group); #endif /*DIFFIE_HELLMAN_H_*/ + diff --git a/src/libstrongswan/crypto/hashers/hasher.c b/src/libstrongswan/crypto/hashers/hasher.c index 7fa6346d6..14bfb022f 100644 --- a/src/libstrongswan/crypto/hashers/hasher.c +++ b/src/libstrongswan/crypto/hashers/hasher.c @@ -19,17 +19,21 @@ * 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: hasher.c 3304 2007-10-12 23:18:42Z andreas $ */ #include "hasher.h" +#include <asn1/oid.h> #include <crypto/hashers/sha1_hasher.h> #include <crypto/hashers/sha2_hasher.h> #include <crypto/hashers/md5_hasher.h> -ENUM(hash_algorithm_names, HASH_MD2, HASH_SHA512, +ENUM(hash_algorithm_names, HASH_UNKNOWN, HASH_SHA512, + "HASH_UNKNOWN", "HASH_MD2", "HASH_MD5", "HASH_SHA1", @@ -63,3 +67,76 @@ hasher_t *hasher_create(hash_algorithm_t hash_algorithm) return NULL; } } + +/* + * Described in header. + */ +hash_algorithm_t hasher_algorithm_from_oid(int oid) +{ + hash_algorithm_t algorithm; + + switch (oid) + { + case OID_MD2: + case OID_MD2_WITH_RSA: + algorithm = HASH_MD2; + break; + case OID_MD5: + case OID_MD5_WITH_RSA: + algorithm = HASH_MD5; + break; + case OID_SHA1: + case OID_SHA1_WITH_RSA: + algorithm = HASH_SHA1; + break; + case OID_SHA256: + case OID_SHA256_WITH_RSA: + algorithm = HASH_SHA256; + break; + case OID_SHA384: + case OID_SHA384_WITH_RSA: + algorithm = HASH_SHA384; + break; + case OID_SHA512: + case OID_SHA512_WITH_RSA: + algorithm = HASH_SHA512; + break; + default: + algorithm = HASH_UNKNOWN; + } + return algorithm; +} + +/* + * Described in header. + */ +int hasher_signature_algorithm_to_oid(hash_algorithm_t alg) +{ + int oid; + + switch (alg) + { + case HASH_MD2: + oid = OID_MD2_WITH_RSA; + break; + case HASH_MD5: + oid = OID_MD5_WITH_RSA; + break; + case HASH_SHA1: + oid = OID_SHA1_WITH_RSA; + break; + case HASH_SHA256: + oid = OID_SHA256_WITH_RSA; + break; + case HASH_SHA384: + oid = OID_SHA384_WITH_RSA; + break; + case HASH_SHA512: + oid = OID_SHA512_WITH_RSA; + break; + default: + oid = OID_UNKNOWN; + } + return oid; +} + diff --git a/src/libstrongswan/crypto/hashers/hasher.h b/src/libstrongswan/crypto/hashers/hasher.h index 6c17f892d..48b904576 100644 --- a/src/libstrongswan/crypto/hashers/hasher.h +++ b/src/libstrongswan/crypto/hashers/hasher.h @@ -19,6 +19,8 @@ * 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: hasher.h 3307 2007-10-17 02:56:24Z andreas $ */ #ifndef HASHER_H_ @@ -42,17 +44,18 @@ typedef struct hasher_t hasher_t; * @ingroup hashers */ enum hash_algorithm_t { - HASH_MD2 = 0, + HASH_UNKNOWN = 0, + HASH_MD2 = 1, /** Implemented in class md5_hasher_t */ - HASH_MD5 = 1, + HASH_MD5 = 2, /** Implemented in class sha1_hasher_t */ - HASH_SHA1 = 2, + HASH_SHA1 = 3, /** Implemented in class sha2_hasher_t */ - HASH_SHA256 = 3, + HASH_SHA256 = 4, /** Implemented in class sha2_hasher_t */ - HASH_SHA384 = 4, + HASH_SHA384 = 5, /** Implemented in class sha2_hasher_t */ - HASH_SHA512 = 5, + HASH_SHA512 = 6, }; #define HASH_SIZE_MD2 16 @@ -68,7 +71,6 @@ enum hash_algorithm_t { */ extern enum_name_t *hash_algorithm_names; - /** * @brief Generic interface for all hash functions. * @@ -82,7 +84,7 @@ struct hasher_t { * @brief Hash data and write it in the buffer. * * If the parameter hash is NULL, no result is written back - * an more data can be appended to already hashed data. + * and more data can be appended to already hashed data. * If not, the result is written back and the hasher is reset. * * The hash output parameter must hold at least @@ -98,7 +100,7 @@ struct hasher_t { * @brief Hash data and allocate space for the hash. * * If the parameter hash is NULL, no result is written back - * an more data can be appended to already hashed data. + * and more data can be appended to already hashed data. * If not, the result is written back and the hasher is reset. * * @param this calling object @@ -156,4 +158,28 @@ struct hasher_t { */ hasher_t *hasher_create(hash_algorithm_t hash_algorithm); +/** + * @brief Conversion of ASN.1 OID to hash algorithm. + * + * @param oid ASN.1 OID + * @return + * - hash algorithm + * - HASH_UNKNOWN if OID unsuported + * + * @ingroup hashers + */ +hash_algorithm_t hasher_algorithm_from_oid(int oid); + +/** + * @brief Conversion of hash signature algorithm ASN.1 OID. + * + * @param alg hash algorithm + * @return + * - ASN.1 OID if known hash algorithm + * - OID_UNKNOW + * + * @ingroup hashers + */ +int hasher_signature_algorithm_to_oid(hash_algorithm_t alg); + #endif /* HASHER_H_ */ diff --git a/src/libstrongswan/crypto/hmac.h b/src/libstrongswan/crypto/hmac.h index d320bc5aa..06b75aaf9 100644 --- a/src/libstrongswan/crypto/hmac.h +++ b/src/libstrongswan/crypto/hmac.h @@ -42,7 +42,7 @@ typedef struct hmac_t hmac_t; * @b Constructors: * - hmac_create() * - * @ingroup transforms + * @ingroup crypto */ struct hmac_t { /** diff --git a/src/libstrongswan/crypto/ietf_attr_list.c b/src/libstrongswan/crypto/ietf_attr_list.c new file mode 100644 index 000000000..1ecadf679 --- /dev/null +++ b/src/libstrongswan/crypto/ietf_attr_list.c @@ -0,0 +1,405 @@ +/** + * @file ietf_attr.c + * + * @brief Implementation of ietfAttr_t. + * + */ + +/* + * Copyright (C) 2007 Andreas Steffen, 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 <string.h> +#include <stdio.h> + +#include <debug.h> +#include <asn1/asn1.h> +#include <utils/lexparser.h> + +#include "ietf_attr_list.h" + +/** + * Private definition of ietfAttribute kinds + */ +typedef enum { + IETF_ATTRIBUTE_OCTETS = 0, + IETF_ATTRIBUTE_OID = 1, + IETF_ATTRIBUTE_STRING = 2 +} ietfAttribute_t; + +typedef struct ietfAttr_t ietfAttr_t; + +/** + * Private definition of an ietfAttribute + */ +struct ietfAttr_t { + /** + * IETF attribute kind + */ + ietfAttribute_t kind; + + /** + * IETF attribute valuse + */ + chunk_t value; + + /** + * Compares two ietfAttributes + * + * return -1 if this is earlier in the alphabet than other + * return 0 if this equals other + * return +1 if this is later in the alphabet than other + * + * @param this calling object + * @param other other object + */ + int (*compare) (const ietfAttr_t *this ,const ietfAttr_t *other); + + /** + * Destroys the ietfAttr_t object. + * + * @param this ietfAttr_t to destroy + */ + void (*destroy) (ietfAttr_t *this); +}; + +/** + * Implements ietfAttr_t.compare. + */ +static int ietfAttr_compare(const ietfAttr_t *this ,const ietfAttr_t *other) +{ + int cmp_len, len, cmp_value; + + /* OID attributes are appended after STRING and OCTETS attributes */ + if (this->kind != IETF_ATTRIBUTE_OID && other->kind == IETF_ATTRIBUTE_OID) + { + return -1; + } + if (this->kind == IETF_ATTRIBUTE_OID && other->kind != IETF_ATTRIBUTE_OID) + { + return 1; + } + + cmp_len = this->value.len - other->value.len; + len = (cmp_len < 0)? this->value.len : other->value.len; + cmp_value = memcmp(this->value.ptr, other->value.ptr, len); + + return (cmp_value == 0)? cmp_len : cmp_value; +} + +/** + * Implements ietfAttr_t.destroy. + */ +static void ietfAttr_destroy(ietfAttr_t *this) +{ + free(this->value.ptr); + free(this); +} + +/** + * Creates an ietfAttr_t object. + */ +static ietfAttr_t *ietfAttr_create(ietfAttribute_t kind, chunk_t value) +{ + ietfAttr_t *this = malloc_thing(ietfAttr_t); + + /* initialize */ + this->kind = kind; + this->value = chunk_clone(value); + + /* function */ + this->compare = ietfAttr_compare; + this->destroy = ietfAttr_destroy; + + return this; +} + +/** + * Adds an ietfAttr_t object to a sorted linked list + */ +static void ietfAttr_add(linked_list_t *list, ietfAttr_t *attr) +{ + iterator_t *iterator = list->create_iterator(list, TRUE); + ietfAttr_t *current_attr; + bool found = FALSE; + + while (iterator->iterate(iterator, (void **)¤t_attr)) + { + int cmp = attr->compare(attr, current_attr); + + if (cmp > 0) + { + continue; + } + if (cmp == 0) + { + attr->destroy(attr); + } + else + { + iterator->insert_before(iterator, attr); + } + found = TRUE; + break; + } + iterator->destroy(iterator); + if (!found) + { + list->insert_last(list, attr); + } +} + +/* + * Described in header. + */ +bool ietfAttr_list_equals(linked_list_t *list_a, linked_list_t *list_b) +{ + bool result = TRUE; + + /* lists must have the same number of attributes */ + if (list_a->get_count(list_a) != list_b->get_count(list_b)) + { + return FALSE; + } + /* empty lists - no attributes */ + if (list_a->get_count(list_a) == 0) + { + return TRUE; + } + + /* compare two alphabetically-sorted lists */ + { + iterator_t *iterator_a = list_a->create_iterator(list_a, TRUE); + iterator_t *iterator_b = list_b->create_iterator(list_b, TRUE); + ietfAttr_t *attr_a, *attr_b; + + while (iterator_a->iterate(iterator_a, (void **)&attr_a) && + iterator_b->iterate(iterator_b, (void **)&attr_b)) + { + if (attr_a->compare(attr_a, attr_b) != 0) + { + /* we have a mismatch */ + result = FALSE; + break; + } + } + iterator_a->destroy(iterator_a); + iterator_b->destroy(iterator_b); + } + return result; +} + +/* + * Described in header. + */ +void ietfAttr_list_list(linked_list_t *list, FILE *out) +{ + iterator_t *iterator = list->create_iterator(list, TRUE); + ietfAttr_t *attr; + bool first = TRUE; + + while (iterator->iterate(iterator, (void **)&attr)) + { + if (first) + { + first = FALSE; + } + else + { + fprintf(out, ", "); + } + + switch (attr->kind) + { + case IETF_ATTRIBUTE_OCTETS: + case IETF_ATTRIBUTE_STRING: + fprintf(out, "%.*s", (int)attr->value.len, attr->value.ptr); + break; + case IETF_ATTRIBUTE_OID: + { + int oid = known_oid(attr->value); + + if (oid == OID_UNKNOWN) + { + fprintf(out, "0x#B", &attr->value); + } + else + { + fprintf(out, "%s", oid_names[oid]); + } + } + break; + default: + break; + } + } + iterator->destroy(iterator); +} + +/* + * Described in header. + */ +void ietfAttr_list_create_from_string(char *msg, linked_list_t *list) +{ + chunk_t line = { msg, strlen(msg) }; + + while (eat_whitespace(&line)) + { + chunk_t group; + + /* extract the next comma-separated group attribute */ + if (!extract_token(&group, ',', &line)) + { + group = line; + line.len = 0; + } + + /* remove any trailing spaces */ + while (group.len > 0 && *(group.ptr + group.len - 1) == ' ') + { + group.len--; + } + + /* add the group attribute to the list */ + if (group.len > 0) + { + ietfAttr_t *attr = ietfAttr_create(IETF_ATTRIBUTE_STRING, group); + + ietfAttr_add(list, attr); + } + } +} + +/** + * ASN.1 definition of ietfAttrSyntax + */ +static const asn1Object_t ietfAttrSyntaxObjects[] = +{ + { 0, "ietfAttrSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "policyAuthority", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_BODY }, /* 1 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ + { 1, "values", ASN1_SEQUENCE, ASN1_LOOP }, /* 3 */ + { 2, "octets", ASN1_OCTET_STRING, ASN1_OPT | + ASN1_BODY }, /* 4 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 5 */ + { 2, "oid", ASN1_OID, ASN1_OPT | + ASN1_BODY }, /* 6 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ + { 2, "string", ASN1_UTF8STRING, ASN1_OPT | + ASN1_BODY }, /* 8 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ + { 1, "end loop", ASN1_EOC, ASN1_END } /* 10 */ +}; + +#define IETF_ATTR_OCTETS 4 +#define IETF_ATTR_OID 6 +#define IETF_ATTR_STRING 8 +#define IETF_ATTR_ROOF 11 + +/* + * Described in header. + */ +void ietfAttr_list_create_from_chunk(chunk_t chunk, linked_list_t *list, int level0) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, chunk, level0, FALSE, FALSE); + + while (objectID < IETF_ATTR_ROOF) + { + if (!extract_object(ietfAttrSyntaxObjects, &objectID, &object, &level, &ctx)) + { + return; + } + + switch (objectID) + { + case IETF_ATTR_OCTETS: + case IETF_ATTR_OID: + case IETF_ATTR_STRING: + { + ietfAttribute_t kind = (objectID - IETF_ATTR_OCTETS) / 2; + ietfAttr_t *attr = ietfAttr_create(kind, object); + ietfAttr_add(list, attr); + } + break; + default: + break; + } + objectID++; + } +} + +/* + * Described in header. + */ +chunk_t ietfAttr_list_encode(linked_list_t *list) +{ + chunk_t ietfAttributes; + size_t size = 0; + u_char *pos; + iterator_t *iterator = list->create_iterator(list, TRUE); + ietfAttr_t *attr; + + /* precalculate the total size of all values */ + while (iterator->iterate(iterator, (void **)&attr)) + { + size_t len = attr->value.len; + + size += 1 + (len > 0) + (len >= 128) + (len >= 256) + (len >= 65536) + len; + } + iterator->destroy(iterator); + + pos = build_asn1_object(&ietfAttributes, ASN1_SEQUENCE, size); + + iterator = list->create_iterator(list, TRUE); + while (iterator->iterate(iterator, (void **)&attr)) + { + chunk_t ietfAttribute; + asn1_t type = ASN1_NULL; + + switch (attr->kind) + { + case IETF_ATTRIBUTE_OCTETS: + type = ASN1_OCTET_STRING; + break; + case IETF_ATTRIBUTE_STRING: + type = ASN1_UTF8STRING; + break; + case IETF_ATTRIBUTE_OID: + type = ASN1_OID; + break; + } + ietfAttribute = asn1_simple_object(type, attr->value); + + /* copy ietfAttribute into ietfAttributes chunk */ + memcpy(pos, ietfAttribute.ptr, ietfAttribute.len); + pos += ietfAttribute.len; + free(ietfAttribute.ptr); + } + iterator->destroy(iterator); + + return asn1_wrap(ASN1_SEQUENCE, "m", ietfAttributes); +} + +/* + * Described in header. + */ +void ietfAttr_list_destroy(linked_list_t *list) +{ + list->destroy_offset(list, offsetof(ietfAttr_t, destroy)); +} diff --git a/src/libstrongswan/crypto/ietf_attr_list.h b/src/libstrongswan/crypto/ietf_attr_list.h new file mode 100644 index 000000000..75407bbf6 --- /dev/null +++ b/src/libstrongswan/crypto/ietf_attr_list.h @@ -0,0 +1,89 @@ +/** + * @file ietf_attr_list.h + * + * @brief Handling of ietfAttr_t linked lists + * + */ + +/* + * Copyright (C) 2007 Andreas Steffen + * + * 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. + */ + +#ifndef IETF_ATTR_LIST_H_ +#define IETF_ATTR_LIST_H_ + +#include <library.h> +#include <utils/linked_list.h> + + +/** + * @brief Compare two linked lists of ietfAttr_t objects for equality + * + * @param list_a first alphabetically-sorted list + * @param list_b second alphabetically-sorted list + * @return TRUE if equal + * + * @ingroup crypto + */ +bool ietfAttr_list_equals(linked_list_t *list_a, linked_list_t *list_b); + +/** + * @brief Lists a linked list of ietfAttr_t objects + * + * @param list alphabetically-sorted linked list of attributes + @param out output file + * + * @ingroup crypto + */ +void ietfAttr_list_list(linked_list_t *list, FILE *out); + +/** + * @brief Create a linked list of ietfAttr_t objects from a string + * + * @param msg string with comma-separated group names + * @param list alphabetically-sorted linked list of attributes + * + * @ingroup crypto + */ +void ietfAttr_list_create_from_string(char *msg, linked_list_t *list); + +/** + * @brief Create a linked list of ietfAttr_t objects from an ASN.1-coded chunk + * + * @param chunk chunk containing ASN.1-coded attributes + * @param list alphabetically-sorted linked list of attributes + * @param level0 parsing level + */ +void ietfAttr_list_create_from_chunk(chunk_t chunk, linked_list_t *list, int level0); + +/** + * @brief Encode a linked list of ietfAttr_t objects into an ASN.1-coded chunk + * + * @param list alphabetically-sorted linked list of attributes + * @return chunk containing ASN.1-coded attributes + */ +chunk_t ietfAttr_list_encode(linked_list_t *list); + +/** + * @brief Destroys a linked list of ietfAttr_t objects + * + * @param list list to be destroyed + * + * @ingroup crypto + */ +void ietfAttr_list_destroy(linked_list_t *list); + +#endif /* IETF_ATTR_LIST_H_ */ + diff --git a/src/libstrongswan/crypto/ocsp.c b/src/libstrongswan/crypto/ocsp.c index 0d8093e4a..e4d907188 100644 --- a/src/libstrongswan/crypto/ocsp.c +++ b/src/libstrongswan/crypto/ocsp.c @@ -466,11 +466,11 @@ static chunk_t ocsp_build_request(private_ocsp_t *this) static bool ocsp_parse_basic_response(chunk_t blob, int level0, response_t *res) { u_int level, version; - u_int extn_oid = OID_UNKNOWN; asn1_ctx_t ctx; bool critical; chunk_t object; int objectID = 0; + int extn_oid = OID_UNKNOWN; asn1_init(&ctx, blob, level0, FALSE, FALSE); @@ -546,9 +546,8 @@ static response_status ocsp_parse_response(response_t *res) chunk_t object; u_int level; int objectID = 0; - + int ocspResponseType = OID_UNKNOWN; response_status rStatus = STATUS_INTERNALERROR; - u_int ocspResponseType = OID_UNKNOWN; asn1_init(&ctx, res->chunk, 0, FALSE, FALSE); @@ -615,6 +614,13 @@ static bool ocsp_valid_response(response_t *res, x509_t *ocsp_cert) rsa_public_key_t *public_key; time_t until = UNDEFINED_TIME; err_t ugh; + hash_algorithm_t algorithm = hasher_algorithm_from_oid(res->algorithm); + + if (algorithm == HASH_UNKNOWN) + { + DBG1("unknown signature algorithm"); + return FALSE; + } DBG2("verifying ocsp response signature:"); DBG2("signer: '%D'", ocsp_cert->get_subject(ocsp_cert)); @@ -627,8 +633,8 @@ static bool ocsp_valid_response(response_t *res, x509_t *ocsp_cert) return FALSE; } public_key = ocsp_cert->get_public_key(ocsp_cert); - - return public_key->verify_emsa_pkcs1_signature(public_key, res->tbs, res->signature) == SUCCESS; + + return public_key->verify_emsa_pkcs1_signature(public_key, algorithm, res->tbs, res->signature) == SUCCESS; } /** diff --git a/src/libstrongswan/crypto/pkcs7.c b/src/libstrongswan/crypto/pkcs7.c new file mode 100644 index 000000000..70510471a --- /dev/null +++ b/src/libstrongswan/crypto/pkcs7.c @@ -0,0 +1,710 @@ +/** + * @file pkcs7.c + * + * @brief Implementation of pkcs7_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Copyright (C) 2002-2005 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 3302 2007-10-12 21:57:20Z andreas $ + */ + +#include <stdlib.h> +#include <string.h> + +#include <library.h> +#include "debug.h" + +#include <asn1/asn1.h> +#include <asn1/oid.h> +#include <crypto/x509.h> +#include <crypto/hashers/hasher.h> +#include <crypto/crypters/crypter.h> +#include <utils/linked_list.h> + +#include "pkcs7.h" + +typedef struct private_pkcs7_t private_pkcs7_t; + +/** + * Private data of a pkcs7_t object. + */ +struct private_pkcs7_t { + /** + * Public interface for this certificate. + */ + pkcs7_t public; + + /** + * contentInfo type + */ + int type; + + /** + * ASN.1 encoded content + */ + chunk_t content; + + /** + * Has the content already been parsed? + */ + bool parsed; + + /** + * ASN.1 parsing start level + */ + u_int level; + + /** + * retrieved data + */ + chunk_t data; + + /** + * ASN.1 encoded attributes + */ + chunk_t attributes; + + /** + * Linked list of X.509 certificates + */ + linked_list_t *certs; +}; + +/** + * ASN.1 definition of the PKCS#7 ContentInfo type + */ +static const asn1Object_t contentInfoObjects[] = { + { 0, "contentInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "contentType", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "content", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_BODY }, /* 2 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 3 */ +}; + +#define PKCS7_INFO_TYPE 1 +#define PKCS7_INFO_CONTENT 2 +#define PKCS7_INFO_ROOF 4 + +/** + * ASN.1 definition of the PKCS#7 signedData type + */ +static const asn1Object_t signedDataObjects[] = { + { 0, "signedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "digestAlgorithms", ASN1_SET, ASN1_LOOP }, /* 2 */ + { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 3 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 4 */ + { 1, "contentInfo", ASN1_EOC, ASN1_RAW }, /* 5 */ + { 1, "certificates", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_LOOP }, /* 6 */ + { 2, "certificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 7 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 8 */ + { 1, "crls", ASN1_CONTEXT_C_1, ASN1_OPT | + ASN1_LOOP }, /* 9 */ + { 2, "crl", ASN1_SEQUENCE, ASN1_OBJ }, /* 10 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 11 */ + { 1, "signerInfos", ASN1_SET, ASN1_LOOP }, /* 12 */ + { 2, "signerInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 13 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 14 */ + { 3, "issuerAndSerialNumber", ASN1_SEQUENCE, ASN1_BODY }, /* 15 */ + { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 16 */ + { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 17 */ + { 3, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 18 */ + { 3, "authenticatedAttributes", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_OBJ }, /* 19 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 20 */ + { 3, "digestEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 21 */ + { 3, "encryptedDigest", ASN1_OCTET_STRING, ASN1_BODY }, /* 22 */ + { 3, "unauthenticatedAttributes", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 23 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 24 */ + { 1, "end loop", ASN1_EOC, ASN1_END } /* 25 */ +}; + +#define PKCS7_DIGEST_ALG 3 +#define PKCS7_SIGNED_CONTENT_INFO 5 +#define PKCS7_SIGNED_CERT 7 +#define PKCS7_SIGNER_INFO 13 +#define PKCS7_SIGNED_ISSUER 16 +#define PKCS7_SIGNED_SERIAL_NUMBER 17 +#define PKCS7_DIGEST_ALGORITHM 18 +#define PKCS7_AUTH_ATTRIBUTES 19 +#define PKCS7_DIGEST_ENC_ALGORITHM 21 +#define PKCS7_ENCRYPTED_DIGEST 22 +#define PKCS7_SIGNED_ROOF 26 + +/** + * ASN.1 definition of the PKCS#7 envelopedData type + */ +static const asn1Object_t envelopedDataObjects[] = { + { 0, "envelopedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "recipientInfos", ASN1_SET, ASN1_LOOP }, /* 2 */ + { 2, "recipientInfo", ASN1_SEQUENCE, ASN1_BODY }, /* 3 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 4 */ + { 3, "issuerAndSerialNumber", ASN1_SEQUENCE, ASN1_BODY }, /* 5 */ + { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 6 */ + { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 7 */ + { 3, "encryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 8 */ + { 3, "encryptedKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 9 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 10 */ + { 1, "encryptedContentInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ + { 2, "contentType", ASN1_OID, ASN1_BODY }, /* 12 */ + { 2, "contentEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 13 */ + { 2, "encryptedContent", ASN1_CONTEXT_S_0, ASN1_BODY } /* 14 */ +}; + +#define PKCS7_ENVELOPED_VERSION 1 +#define PKCS7_RECIPIENT_INFO_VERSION 4 +#define PKCS7_ISSUER 6 +#define PKCS7_SERIAL_NUMBER 7 +#define PKCS7_ENCRYPTION_ALG 8 +#define PKCS7_ENCRYPTED_KEY 9 +#define PKCS7_CONTENT_TYPE 12 +#define PKCS7_CONTENT_ENC_ALGORITHM 13 +#define PKCS7_ENCRYPTED_CONTENT 14 +#define PKCS7_ENVELOPED_ROOF 15 + +/** + * PKCS7 contentInfo OIDs + */ +static u_char ASN1_pkcs7_data_oid_str[] = { + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 +}; + +static u_char ASN1_pkcs7_signed_data_oid_str[] = { + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 +}; + +static u_char ASN1_pkcs7_enveloped_data_oid_str[] = { + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x03 +}; + +static u_char ASN1_pkcs7_signed_enveloped_data_oid_str[] = { + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x04 +}; + +static u_char ASN1_pkcs7_digested_data_oid_str[] = { + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x05 +}; + +static char ASN1_pkcs7_encrypted_data_oid_str[] = { + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 +}; + +static 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); +static const chunk_t ASN1_pkcs7_enveloped_data_oid = + chunk_from_buf(ASN1_pkcs7_enveloped_data_oid_str); +static const chunk_t ASN1_pkcs7_signed_enveloped_data_oid = + chunk_from_buf(ASN1_pkcs7_signed_enveloped_data_oid_str); +static const chunk_t ASN1_pkcs7_digested_data_oid = + chunk_from_buf(ASN1_pkcs7_digested_data_oid_str); +static const chunk_t ASN1_pkcs7_encrypted_data_oid = + chunk_from_buf(ASN1_pkcs7_encrypted_data_oid_str); + +/** + * 3DES and DES encryption OIDs + */ +static u_char ASN1_3des_ede_cbc_oid_str[] = { + 0x06, 0x08, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x07 +}; + +static u_char ASN1_des_cbc_oid_str[] = { + 0x06, 0x05, + 0x2B, 0x0E, 0x03, 0x02, 0x07 +}; + +static const chunk_t ASN1_3des_ede_cbc_oid = + chunk_from_buf(ASN1_3des_ede_cbc_oid_str); +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. + */ +static bool is_data(private_pkcs7_t *this) +{ + return this->type == OID_PKCS7_DATA; +} + +/** + * Implements pkcs7_t.is_signedData. + */ +static bool is_signedData(private_pkcs7_t *this) +{ + return this->type == OID_PKCS7_SIGNED_DATA; +} + +/** + * Implements pkcs7_t.is_signedData. + */ +static bool is_envelopedData(private_pkcs7_t *this) +{ + return this->type == OID_PKCS7_ENVELOPED_DATA; +} + +/** + * Check whether to abort the requested parsing + */ +static bool abort_parsing(private_pkcs7_t *this, int type) +{ + if (this->type != type) + { + DBG1("pkcs7 content to be parsed is not of type '%s'", + oid_names[type]); + return TRUE; + } + if (this->parsed) + { + DBG1("pkcs7 content has already been parsed"); + return TRUE; + } + this->parsed = TRUE; + return FALSE; +} + +/** + * Implements pkcs7_t.parse_data. + */ +static bool parse_data(private_pkcs7_t *this) +{ + chunk_t data = this->content; + + if (abort_parsing(this, OID_PKCS7_DATA)) + { + return FALSE; + } + if (parse_asn1_simple_object(&data, ASN1_OCTET_STRING, this->level, "data")) + { + this->data = chunk_clone(data); + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * Parse PKCS#7 signedData content + */ +static bool parse_signedData(private_pkcs7_t *this, x509_t *cacert) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + int digest_alg = OID_UNKNOWN; + int enc_alg = OID_UNKNOWN; + int signerInfos = 0; + + chunk_t encrypted_digest = chunk_empty; + + if (abort_parsing(this, OID_PKCS7_SIGNED_DATA)) + { + return FALSE; + } + + asn1_init(&ctx, this->content, this->level, FALSE, FALSE); + + while (objectID < PKCS7_SIGNED_ROOF) + { + if (!extract_object(signedDataObjects, &objectID, &object, &level, &ctx)) + { + return FALSE; + } + + switch (objectID) + { + case PKCS7_DIGEST_ALG: + digest_alg = parse_algorithmIdentifier(object, level, NULL); + break; + case PKCS7_SIGNED_CONTENT_INFO: + this->data = chunk_clone(object); + break; + case PKCS7_SIGNED_CERT: + { + x509_t *cert = x509_create_from_chunk(object, level+1); + + if (cert) + { + this->certs->insert_last(this->certs, (void*)cert); + } + } + break; + case PKCS7_SIGNER_INFO: + signerInfos++; + DBG2(" signer #%d", signerInfos); + break; + case PKCS7_SIGNED_ISSUER: + { + identification_t *issuer; + + issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); + DBG2(" '%D'", issuer); + issuer->destroy(issuer); + } + break; + case PKCS7_AUTH_ATTRIBUTES: + this->attributes = object; + *this->attributes.ptr = ASN1_SET; + break; + case PKCS7_DIGEST_ALGORITHM: + digest_alg = parse_algorithmIdentifier(object, level, NULL); + break; + case PKCS7_DIGEST_ENC_ALGORITHM: + enc_alg = parse_algorithmIdentifier(object, level, NULL); + break; + case PKCS7_ENCRYPTED_DIGEST: + encrypted_digest = object; + } + objectID++; + } + + /* 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); + + if (signerInfos == 0) + { + DBG1("no signerInfo object found"); + return FALSE; + } + else if (signerInfos > 1) + { + DBG1("more than one signerInfo object found"); + return FALSE; + } + if (this->attributes.ptr == NULL) + { + DBG1("no authenticatedAttributes object found"); + return FALSE; + } + if (enc_alg != OID_RSA_ENCRYPTION) + { + DBG1("only RSA digest encryption supported"); + return FALSE; + } + if (signer->verify_emsa_pkcs1_signature(signer, algorithm, + this->attributes, encrypted_digest) != SUCCESS) + { + DBG1("invalid digest signature"); + return FALSE; + } + else + { + DBG2("digest signature is valid"); + } + } + return TRUE; +} + +/** + * Parse PKCS#7 envelopedData content + */ +static bool parse_envelopedData(private_pkcs7_t *this, chunk_t serialNumber, + rsa_private_key_t *key) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + chunk_t iv = chunk_empty; + chunk_t symmetric_key = chunk_empty; + chunk_t encrypted_content = chunk_empty; + + crypter_t *crypter = NULL; + + if (abort_parsing(this, OID_PKCS7_ENVELOPED_DATA)) + { + return FALSE; + } + + asn1_init(&ctx, this->content, this->level, FALSE, FALSE); + + while (objectID < PKCS7_ENVELOPED_ROOF) + { + if (!extract_object(envelopedDataObjects, &objectID, &object, &level, &ctx)) + { + goto failed; + } + + switch (objectID) + { + case PKCS7_ENVELOPED_VERSION: + if (*object.ptr != 0) + { + DBG1("envelopedData version is not 0"); + goto failed; + } + break; + case PKCS7_RECIPIENT_INFO_VERSION: + if (*object.ptr != 0) + { + DBG1("recipient info version is not 0"); + goto failed; + } + break; + case PKCS7_ISSUER: + { + identification_t *issuer; + + issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); + DBG2(" '%D'", issuer); + issuer->destroy(issuer); + } + break; + case PKCS7_SERIAL_NUMBER: + if (!chunk_equals(serialNumber, object)) + { + DBG1("serial numbers do not match"); + goto failed; + } + break; + case PKCS7_ENCRYPTION_ALG: + { + int alg = parse_algorithmIdentifier(object, level, NULL); + + if (alg != OID_RSA_ENCRYPTION) + { + DBG1("only rsa encryption supported"); + goto failed; + } + } + break; + case PKCS7_ENCRYPTED_KEY: + if (key->pkcs1_decrypt(key, object, &symmetric_key) != SUCCESS) + { + DBG1("symmetric key could not be decrypted with rsa"); + goto failed; + } + DBG4("symmetric key : %B", &symmetric_key); + break; + case PKCS7_CONTENT_TYPE: + if (known_oid(object) != OID_PKCS7_DATA) + { + DBG1("encrypted content not of type pkcs7 data"); + goto failed; + } + break; + case PKCS7_CONTENT_ENC_ALGORITHM: + { + int alg = parse_algorithmIdentifier(object, level, &iv); + + switch (alg) + { + case OID_DES_CBC: + crypter = crypter_create(ENCR_DES, 0); + break; + case OID_3DES_EDE_CBC: + crypter = crypter_create(ENCR_3DES, 0); + break; + default: + DBG1("Only DES and 3DES supported for symmetric encryption"); + goto failed; + } + if (symmetric_key.len != crypter->get_key_size(crypter)) + { + DBG1("symmetric key has wrong length"); + goto failed; + } + if (!parse_asn1_simple_object(&iv, ASN1_OCTET_STRING, level+1, "IV")) + { + DBG1("IV could not be parsed"); + goto failed; + } + if (iv.len != crypter->get_block_size(crypter)) + { + DBG1("IV has wrong length"); + goto failed; + } + } + break; + case PKCS7_ENCRYPTED_CONTENT: + encrypted_content = object; + break; + } + objectID++; + } + + /* decrypt the content */ + crypter->decrypt(crypter, encrypted_content, iv, &this->data); + DBG4("decrypted content with padding: %B", &this->data); + + /* remove the padding */ + { + u_char *pos = this->data.ptr + this->data.len - 1; + u_char pattern = *pos; + size_t padding = pattern; + + if (padding > this->data.len) + { + DBG1("padding greater than data length"); + goto failed; + } + this->data.len -= padding; + + while (padding-- > 0) + { + if (*pos-- != pattern) + { + DBG1("wrong padding pattern"); + goto failed; + } + } + } + crypter->destroy(crypter); + free(symmetric_key.ptr); + return TRUE; + +failed: + DESTROY_IF(crypter); + free(symmetric_key.ptr); + chunk_free(&this->data); + return FALSE; +} + +/** + * Implements pkcs7_t.get_data + */ +static chunk_t get_data(private_pkcs7_t *this) +{ + return this->data; +} + +/** + * Implements pkcs_t.create_crluri_iterator + */ +static iterator_t *create_certificate_iterator(const private_pkcs7_t *this) +{ + return this->certs->create_iterator(this->certs, TRUE); +} + +/** + * Implements pkcs7_t.destroy + */ +static void destroy(private_pkcs7_t *this) +{ + this->certs->destroy_offset(this->certs, offsetof(x509_t, destroy)); + free(this->data.ptr); + free(this); +} + +/** + * Parse PKCS#7 contentInfo object + */ +static bool parse_contentInfo(chunk_t blob, u_int level0, private_pkcs7_t *cInfo) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < PKCS7_INFO_ROOF) + { + if (!extract_object(contentInfoObjects, &objectID, &object, &level, &ctx)) + { + return FALSE; + } + + if (objectID == PKCS7_INFO_TYPE) + { + cInfo->type = known_oid(object); + if (cInfo->type < OID_PKCS7_DATA + || cInfo->type > OID_PKCS7_ENCRYPTED_DATA) + { + DBG1("unknown pkcs7 content type"); + return FALSE; + } + } + else if (objectID == PKCS7_INFO_CONTENT) + { + cInfo->content = object; + } + objectID++; + } + return TRUE; +} + +/* + * Described in header. + */ +pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level) +{ + private_pkcs7_t *this = malloc_thing(private_pkcs7_t); + + /* initialize */ + this->type = OID_UNKNOWN; + this->content = chunk_empty; + this->parsed = FALSE; + this->level = level + 2; + this->data = chunk_empty; + this->attributes = chunk_empty; + this->certs = linked_list_create(); + + /*public functions */ + this->public.is_data = (bool (*) (pkcs7_t*))is_data; + this->public.is_signedData = (bool (*) (pkcs7_t*))is_signedData; + this->public.is_envelopedData = (bool (*) (pkcs7_t*))is_envelopedData; + this->public.parse_data = (bool (*) (pkcs7_t*))parse_data; + 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.create_certificate_iterator = (iterator_t* (*) (pkcs7_t*))create_certificate_iterator; + this->public.destroy = (void (*) (pkcs7_t*))destroy; + + if (!parse_contentInfo(chunk, level, this)) + { + destroy(this); + return NULL; + } + return &this->public; +} diff --git a/src/libstrongswan/crypto/pkcs7.h b/src/libstrongswan/crypto/pkcs7.h new file mode 100644 index 000000000..c8434225a --- /dev/null +++ b/src/libstrongswan/crypto/pkcs7.h @@ -0,0 +1,132 @@ +/** + * @file pkcs7.h + * + * @brief Interface of pkcs7_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Copyright (C) 2002-2007 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 3302 2007-10-12 21:57:20Z andreas $ + */ + +#ifndef _PKCS7_H +#define _PKCS7_H + +typedef struct pkcs7_t pkcs7_t; + +#include <library.h> +#include <crypto/x509.h> +#include <crypto/rsa/rsa_private_key.h> +#include <utils/iterator.h> + +/** + * @brief PKCS#7 contentInfo object. + * + * @b Constructors: + * -pkcs7_create_from_chunk() + * + * @ingroup crypto + */ +struct pkcs7_t { + /** + * @brief Check if the PKCS#7 contentType is data + * + * @param this calling object + * @return TRUE if the contentType is data + */ + bool (*is_data) (pkcs7_t *this); + + /** + * @brief Check if the PKCS#7 contentType is signedData + * + * @param this calling object + * @return TRUE if the contentType is signedData + */ + bool (*is_signedData) (pkcs7_t *this); + + /** + * @brief Check if the PKCS#7 contentType is envelopedData + * + * @param this calling object + * @return TRUE if the contentType is envelopedData + */ + bool (*is_envelopedData) (pkcs7_t *this); + + /** + * @brief Parse a PKCS#7 data content. + * + * @param this calling object + * @return TRUE if parsing was successful + */ + bool (*parse_data) (pkcs7_t *this); + + /** + * @brief Parse a PKCS#7 signedData content. + * + * @param this calling object + * @param cacert cacert used to verify the signature + * @return TRUE if parsing was successful + */ + bool (*parse_signedData) (pkcs7_t *this, x509_t *cacert); + + /** + * @brief Parse a PKCS#7 envelopedData content. + * + * @param this calling object + * @param serialNumber serialNumber of the request + * @param key RSA private key used to decrypt the symmetric key + * @return TRUE if parsing was successful + */ + bool (*parse_envelopedData) (pkcs7_t *this, chunk_t serialNumber, rsa_private_key_t *key); + + /** + * @brief Returns the parsed data object + * + * @param this calling object + * @return chunk containing the data object + */ + chunk_t (*get_data) (pkcs7_t *this); + + /** + * @brief Create an iterator for the certificates. + * + * @param this calling object + * @return iterator for the certificates + */ + iterator_t *(*create_certificate_iterator) (pkcs7_t *this); + + /** + * @brief Destroys the contentInfo object. + * + * @param this PKCS#7 contentInfo object to destroy + */ + void (*destroy) (pkcs7_t *this); +}; + +/** + * @brief Read a PKCS#7 contentInfo object from a DER encoded chunk. + * + * @param chunk chunk containing DER encoded data + * @param level ASN.1 parsing start level + * @return created pkcs7_contentInfo object, or NULL if invalid. + * + * @ingroup crypto + */ +pkcs7_t *pkcs7_create_from_chunk(chunk_t chunk, u_int level); + +#endif /* _PKCS7_H */ diff --git a/src/libstrongswan/crypto/rsa/rsa_private_key.c b/src/libstrongswan/crypto/rsa/rsa_private_key.c index 5b1647965..ec2f2fc74 100644 --- a/src/libstrongswan/crypto/rsa/rsa_private_key.c +++ b/src/libstrongswan/crypto/rsa/rsa_private_key.c @@ -19,6 +19,8 @@ * 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: rsa_private_key.c 3306 2007-10-17 02:55:53Z andreas $ */ #include <gmp.h> @@ -29,33 +31,21 @@ #include "rsa_public_key.h" #include "rsa_private_key.h" +#include <debug.h> #include <asn1/asn1.h> #include <asn1/pem.h> #include <utils/randomizer.h> /** - * OIDs for hash algorithms are defined in rsa_public_key.c. - */ -extern u_int8_t md2_oid[18]; -extern u_int8_t md5_oid[18]; -extern u_int8_t sha1_oid[15]; -extern u_int8_t sha256_oid[19]; -extern u_int8_t sha384_oid[19]; -extern u_int8_t sha512_oid[19]; - - -/** * defined in rsa_public_key.c */ extern chunk_t rsa_public_key_info_to_asn1(const mpz_t n, const mpz_t e); - /** * Public exponent to use for key generation. */ #define PUBLIC_EXPONENT 0x10001 - typedef struct private_rsa_private_key_t private_rsa_private_key_t; /** @@ -153,23 +143,23 @@ struct private_rsa_private_key_t { /* ASN.1 definition of a PKCS#1 RSA private key */ static const asn1Object_t privkey_objects[] = { - { 0, "RSAPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ - { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 2 */ - { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 3 */ - { 1, "privateExponent", ASN1_INTEGER, ASN1_BODY }, /* 4 */ - { 1, "prime1", ASN1_INTEGER, ASN1_BODY }, /* 5 */ - { 1, "prime2", ASN1_INTEGER, ASN1_BODY }, /* 6 */ - { 1, "exponent1", ASN1_INTEGER, ASN1_BODY }, /* 7 */ - { 1, "exponent2", ASN1_INTEGER, ASN1_BODY }, /* 8 */ - { 1, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 9 */ - { 1, "otherPrimeInfos", ASN1_SEQUENCE, ASN1_OPT | - ASN1_LOOP }, /* 10 */ - { 2, "otherPrimeInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 11 */ - { 3, "prime", ASN1_INTEGER, ASN1_BODY }, /* 12 */ - { 3, "exponent", ASN1_INTEGER, ASN1_BODY }, /* 13 */ - { 3, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 14 */ - { 1, "end opt or loop", ASN1_EOC, ASN1_END } /* 15 */ + { 0, "RSAPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 3 */ + { 1, "privateExponent", ASN1_INTEGER, ASN1_BODY }, /* 4 */ + { 1, "prime1", ASN1_INTEGER, ASN1_BODY }, /* 5 */ + { 1, "prime2", ASN1_INTEGER, ASN1_BODY }, /* 6 */ + { 1, "exponent1", ASN1_INTEGER, ASN1_BODY }, /* 7 */ + { 1, "exponent2", ASN1_INTEGER, ASN1_BODY }, /* 8 */ + { 1, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 9 */ + { 1, "otherPrimeInfos", ASN1_SEQUENCE, ASN1_OPT | + ASN1_LOOP }, /* 10 */ + { 2, "otherPrimeInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 11 */ + { 3, "prime", ASN1_INTEGER, ASN1_BODY }, /* 12 */ + { 3, "exponent", ASN1_INTEGER, ASN1_BODY }, /* 13 */ + { 3, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 14 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END } /* 15 */ }; #define PRIV_KEY_VERSION 1 @@ -186,6 +176,26 @@ static const asn1Object_t privkey_objects[] = { static private_rsa_private_key_t *rsa_private_key_create_empty(void); /** + * Auxiliary function overwriting private key material with + * pseudo-random bytes before releasing it + */ +static void mpz_clear_randomized(mpz_t z) +{ + size_t len = mpz_size(z) * GMP_LIMB_BITS / BITS_PER_BYTE; + u_int8_t *random_bytes = alloca(len); + + randomizer_t *randomizer = randomizer_create(); + + randomizer->get_pseudo_random_bytes(randomizer, len, random_bytes); + + /* overwrite mpz_t with pseudo-random bytes before clearing it */ + mpz_import(z, len, 1, 1, 1, 0, random_bytes); + mpz_clear(z); + + randomizer->destroy(randomizer); +} + +/** * Implementation of private_rsa_private_key_t.compute_prime. */ static status_t compute_prime(private_rsa_private_key_t *this, size_t prime_size, mpz_t *prime) @@ -216,7 +226,8 @@ static status_t compute_prime(private_rsa_private_key_t *this, size_t prime_size /* get next prime */ mpz_nextprime (*prime, *prime); - free(random_bytes.ptr); + /* free the random_bytes after overwriting them with a pseudo-random sequence */ + chunk_free_randomized(&random_bytes); } /* check if it isnt too large */ while (((mpz_sizeinbase(*prime, 2) + 7) / 8) > prime_size); @@ -251,59 +262,96 @@ static chunk_t rsadp(private_rsa_private_key_t *this, chunk_t data) decrypted.len = this->k; decrypted.ptr = mpz_export(NULL, NULL, 1, decrypted.len, 1, 0, t1); - mpz_clear(t1); - mpz_clear(t2); + mpz_clear_randomized(t1); + mpz_clear_randomized(t2); return decrypted; } /** - * Implementation of rsa_private_key.build_emsa_signature. + * Implementation of rsa_private_key_t.eme_pkcs1_decrypt. */ -static status_t build_emsa_pkcs1_signature(private_rsa_private_key_t *this, hash_algorithm_t hash_algorithm, chunk_t data, chunk_t *signature) +static status_t pkcs1_decrypt(private_rsa_private_key_t *this, + chunk_t in, chunk_t *out) +{ + status_t status = FAILED; + chunk_t em, em_ori; + + /* decrypt the input data */ + em = em_ori = this->rsadp(this, in); + + /* PKCS#1 v1.5 EME encryption formatting + * EM = 00 || 02 || PS || 00 || M + * PS = pseudo-random nonzero octets + */ + + /* check for magic bytes */ + if (*(em.ptr) != 0x00 || *(em.ptr+1) != 0x02) + { + DBG1("incorrect padding - probably wrong RSA key"); + goto end; + } + em.ptr += 2; + em.len -= 2; + + /* the plaintext data starts after first 0x00 byte */ + while (em.len-- > 0 && *em.ptr++ != 0x00); + + if (em.len == 0) + { + DBG1("no plaintext data found"); + goto end; + } + + *out = chunk_clone(em); + status = SUCCESS; + +end: + free(em_ori.ptr); + return status; +} + +/** + * Implementation of rsa_private_key_t.build_emsa_pkcs1_signature. + */ +static status_t build_emsa_pkcs1_signature(private_rsa_private_key_t *this, + hash_algorithm_t hash_algorithm, + chunk_t data, chunk_t *signature) { hasher_t *hasher; - chunk_t hash; - chunk_t em; - chunk_t oid; + chunk_t em, digestInfo, hash_id, hash; /* get oid string prepended to hash */ switch (hash_algorithm) { case HASH_MD2: { - oid.ptr = md2_oid; - oid.len = sizeof(md2_oid); + hash_id =ASN1_md2_id; break; } case HASH_MD5: { - oid.ptr = md5_oid; - oid.len = sizeof(md5_oid); + hash_id = ASN1_md5_id; break; } case HASH_SHA1: { - oid.ptr = sha1_oid; - oid.len = sizeof(sha1_oid); + hash_id = ASN1_sha1_id; break; } case HASH_SHA256: { - oid.ptr = sha256_oid; - oid.len = sizeof(sha256_oid); + hash_id = ASN1_sha256_id; break; } case HASH_SHA384: { - oid.ptr = sha384_oid; - oid.len = sizeof(sha384_oid); + hash_id = ASN1_sha384_id; break; } case HASH_SHA512: { - oid.ptr = sha512_oid; - oid.len = sizeof(sha512_oid); + hash_id = ASN1_sha512_id; break; } default: @@ -323,10 +371,17 @@ static status_t build_emsa_pkcs1_signature(private_rsa_private_key_t *this, hash hasher->allocate_hash(hasher, data, &hash); hasher->destroy(hasher); + /* build DER-encoded digestInfo */ + digestInfo = asn1_wrap(ASN1_SEQUENCE, "cm", + hash_id, + asn1_simple_object(ASN1_OCTET_STRING, hash) + ); + chunk_free(&hash); + /* build chunk to rsa-decrypt: * EM = 0x00 || 0x01 || PS || 0x00 || T. * PS = 0xFF padding, with length to fill em - * T = oid || hash + * T = encoded_hash */ em.len = this->k; em.ptr = malloc(em.len); @@ -336,78 +391,44 @@ static status_t build_emsa_pkcs1_signature(private_rsa_private_key_t *this, hash /* set magic bytes */ *(em.ptr) = 0x00; *(em.ptr+1) = 0x01; - *(em.ptr + em.len - hash.len - oid.len - 1) = 0x00; - /* set hash */ - memcpy(em.ptr + em.len - hash.len, hash.ptr, hash.len); - /* set oid */ - memcpy(em.ptr + em.len - hash.len - oid.len, oid.ptr, oid.len); - + *(em.ptr + em.len - digestInfo.len - 1) = 0x00; + /* set DER-encoded hash */ + memcpy(em.ptr + em.len - digestInfo.len, digestInfo.ptr, digestInfo.len); + /* build signature */ *signature = this->rsasp1(this, em); - free(hash.ptr); + free(digestInfo.ptr); free(em.ptr); return SUCCESS; } /** - * Implementation of rsa_private_key.get_key. + * Implementation of rsa_private_key_t.pkcs1_write. */ -static status_t get_key(private_rsa_private_key_t *this, chunk_t *key) -{ - chunk_t n, e, p, q, d, exp1, exp2, coeff; - - n.len = this->k; - n.ptr = mpz_export(NULL, NULL, 1, n.len, 1, 0, this->n); - e.len = this->k; - e.ptr = mpz_export(NULL, NULL, 1, e.len, 1, 0, this->e); - p.len = this->k; - p.ptr = mpz_export(NULL, NULL, 1, p.len, 1, 0, this->p); - q.len = this->k; - q.ptr = mpz_export(NULL, NULL, 1, q.len, 1, 0, this->q); - d.len = this->k; - d.ptr = mpz_export(NULL, NULL, 1, d.len, 1, 0, this->d); - exp1.len = this->k; - exp1.ptr = mpz_export(NULL, NULL, 1, exp1.len, 1, 0, this->exp1); - exp2.len = this->k; - exp2.ptr = mpz_export(NULL, NULL, 1, exp2.len, 1, 0, this->exp2); - coeff.len = this->k; - coeff.ptr = mpz_export(NULL, NULL, 1, coeff.len, 1, 0, this->coeff); - - key->len = this->k * 8; - key->ptr = malloc(key->len); - memcpy(key->ptr + this->k * 0, n.ptr , n.len); - memcpy(key->ptr + this->k * 1, e.ptr, e.len); - memcpy(key->ptr + this->k * 2, p.ptr, p.len); - memcpy(key->ptr + this->k * 3, q.ptr, q.len); - memcpy(key->ptr + this->k * 4, d.ptr, d.len); - memcpy(key->ptr + this->k * 5, exp1.ptr, exp1.len); - memcpy(key->ptr + this->k * 6, exp2.ptr, exp2.len); - memcpy(key->ptr + this->k * 7, coeff.ptr, coeff.len); - - free(n.ptr); - free(e.ptr); - free(p.ptr); - free(q.ptr); - free(d.ptr); - free(exp1.ptr); - free(exp2.ptr); - free(coeff.ptr); - - return SUCCESS; -} - -/** - * Implementation of rsa_private_key.save_key. - */ -static status_t save_key(private_rsa_private_key_t *this, char *file) +static bool pkcs1_write(private_rsa_private_key_t *this, const char *filename, bool force) { - return NOT_SUPPORTED; + bool status; + + chunk_t pkcs1 = asn1_wrap(ASN1_SEQUENCE, "cmmmmmmmm", + ASN1_INTEGER_0, + asn1_integer_from_mpz(this->n), + asn1_integer_from_mpz(this->e), + asn1_integer_from_mpz(this->d), + asn1_integer_from_mpz(this->p), + asn1_integer_from_mpz(this->q), + asn1_integer_from_mpz(this->exp1), + asn1_integer_from_mpz(this->exp2), + asn1_integer_from_mpz(this->coeff)); + + status = chunk_write(pkcs1, filename, "pkcs1", 0066, force); + chunk_free_randomized(&pkcs1); + return status; } /** - * Implementation of rsa_private_key.get_public_key. + * Implementation of rsa_private_key_t.get_public_key. */ rsa_public_key_t *get_public_key(private_rsa_private_key_t *this) { @@ -510,47 +531,26 @@ static status_t check(private_rsa_private_key_t *this) status = FAILED; } - mpz_clear(t); - mpz_clear(u); - mpz_clear(q1); + mpz_clear_randomized(t); + mpz_clear_randomized(u); + mpz_clear_randomized(q1); return status; } /** - * Implementation of rsa_private_key.clone. - */ -static rsa_private_key_t* _clone(private_rsa_private_key_t *this) -{ - private_rsa_private_key_t *clone = rsa_private_key_create_empty(); - - mpz_init_set(clone->n, this->n); - mpz_init_set(clone->e, this->e); - mpz_init_set(clone->p, this->p); - mpz_init_set(clone->q, this->q); - mpz_init_set(clone->d, this->d); - mpz_init_set(clone->exp1, this->exp1); - mpz_init_set(clone->exp2, this->exp2); - mpz_init_set(clone->coeff, this->coeff); - clone->keyid = chunk_clone(this->keyid); - clone->k = this->k; - - return &clone->public; -} - -/** * Implementation of rsa_private_key.destroy. */ static void destroy(private_rsa_private_key_t *this) { - mpz_clear(this->n); - mpz_clear(this->e); - mpz_clear(this->p); - mpz_clear(this->q); - mpz_clear(this->d); - mpz_clear(this->exp1); - mpz_clear(this->exp2); - mpz_clear(this->coeff); - free(this->keyid.ptr); + mpz_clear_randomized(this->n); + mpz_clear_randomized(this->e); + mpz_clear_randomized(this->p); + mpz_clear_randomized(this->q); + mpz_clear_randomized(this->d); + mpz_clear_randomized(this->exp1); + mpz_clear_randomized(this->exp2); + mpz_clear_randomized(this->coeff); + chunk_free_randomized(&this->keyid); free(this); } @@ -562,12 +562,11 @@ static private_rsa_private_key_t *rsa_private_key_create_empty(void) private_rsa_private_key_t *this = malloc_thing(private_rsa_private_key_t); /* public functions */ + this->public.pkcs1_decrypt = (status_t (*) (rsa_private_key_t*,chunk_t,chunk_t*))pkcs1_decrypt; this->public.build_emsa_pkcs1_signature = (status_t (*) (rsa_private_key_t*,hash_algorithm_t,chunk_t,chunk_t*))build_emsa_pkcs1_signature; - this->public.get_key = (status_t (*) (rsa_private_key_t*,chunk_t*))get_key; - this->public.save_key = (status_t (*) (rsa_private_key_t*,char*))save_key; - this->public.get_public_key = (rsa_public_key_t *(*) (rsa_private_key_t*))get_public_key; + this->public.pkcs1_write = (bool (*) (rsa_private_key_t*,const char*,bool))pkcs1_write; + this->public.get_public_key = (rsa_public_key_t* (*) (rsa_private_key_t*))get_public_key; this->public.belongs_to = (bool (*) (rsa_private_key_t*,rsa_public_key_t*))belongs_to; - this->public.clone = (rsa_private_key_t*(*)(rsa_private_key_t*))_clone; this->public.destroy = (void (*) (rsa_private_key_t*))destroy; /* private functions */ @@ -575,6 +574,8 @@ static private_rsa_private_key_t *rsa_private_key_create_empty(void) this->rsasp1 = rsadp; /* same algorithm */ this->compute_prime = compute_prime; + this->keyid = chunk_empty; + return this; } @@ -613,9 +614,7 @@ rsa_private_key_t *rsa_private_key_create(size_t key_size) /* Swapping Primes so p is larger then q */ if (mpz_cmp(p, q) < 0) { - mpz_set(t, p); - mpz_set(p, q); - mpz_set(q, t); + mpz_swap(p, q); } mpz_mul(n, p, q); /* n = p*q */ @@ -645,9 +644,9 @@ rsa_private_key_t *rsa_private_key_create(size_t key_size) mpz_add(coeff, coeff, p); } - mpz_clear(q1); - mpz_clear(m); - mpz_clear(t); + mpz_clear_randomized(q1); + mpz_clear_randomized(m); + mpz_clear_randomized(t); /* apply values */ *(this->p) = *p; @@ -733,7 +732,7 @@ rsa_private_key_t *rsa_private_key_create_from_chunk(chunk_t blob) objectID++; } - this->k = (mpz_sizeinbase(this->n, 2) + 7) / 8; + this->k = (mpz_sizeinbase(this->n, 2) + 7) / BITS_PER_BYTE; /* form the keyid as a SHA-1 hash of a publicKeyInfo object */ { @@ -769,6 +768,6 @@ rsa_private_key_t *rsa_private_key_create_from_file(char *filename, chunk_t *pas return NULL; key = rsa_private_key_create_from_chunk(chunk); - free(chunk.ptr); + chunk_free_randomized(&chunk); return key; } diff --git a/src/libstrongswan/crypto/rsa/rsa_private_key.h b/src/libstrongswan/crypto/rsa/rsa_private_key.h index 9ec07704e..e5cf49810 100644 --- a/src/libstrongswan/crypto/rsa/rsa_private_key.h +++ b/src/libstrongswan/crypto/rsa/rsa_private_key.h @@ -19,6 +19,8 @@ * 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: rsa_private_key.h 3296 2007-10-12 15:23:29Z andreas $ */ #ifndef RSA_PRIVATE_KEY_H_ @@ -42,13 +44,24 @@ typedef struct rsa_private_key_t rsa_private_key_t; * * @see rsa_public_key_t * - * @todo Implement get_key(), save_key(), get_public_key() - * * @ingroup rsa */ struct rsa_private_key_t { /** + * @brief Decrypt a data block based on EME-PKCS1 encoding. + * + * + * @param this calling object + * @param data encrypted input data + * @param out decrypted output data + * @return + * - SUCCESS + * - FAILED if padding is not correct + */ + status_t (*pkcs1_decrypt) (rsa_private_key_t *this, chunk_t in, chunk_t *out); + + /** * @brief Build a signature over a chunk using EMSA-PKCS1 encoding. * * This signature creates a hash using the specified hash algorithm, concatenates @@ -67,45 +80,17 @@ struct rsa_private_key_t { status_t (*build_emsa_pkcs1_signature) (rsa_private_key_t *this, hash_algorithm_t hash_algorithm, chunk_t data, chunk_t *signature); /** - * @brief Gets the key. - * - * UNIMPLEMENTED! - * + * @brief Writes an RSA private key to a file in PKCS#1 format. + * * @param this calling object - * @param key key (in a propriarity format) - * @return - * - SUCCESS - * - INVALID_STATE, if key not set + * @param filename file to which the key should be written. + * @param force if TRUE overwrite existing file + * @return TRUE if successful - FALSE otherwise */ - status_t (*get_key) (rsa_private_key_t *this, chunk_t *key); + bool (*pkcs1_write) (rsa_private_key_t *this, const char *filename, bool force); /** - * @brief Saves a key to a file. - * - * Not implemented! - * - * @param this calling object - * @param file file to which the key should be written. - * @return NOT_SUPPORTED - */ - status_t (*save_key) (rsa_private_key_t *this, char *file); - - /** - * @brief Generate a new key. - * - * Generates a new private_key with specified key size - * - * @param this calling object - * @param key_size size of the key in bits - * @return - * - SUCCESS - * - INVALID_ARG if key_size invalid - */ - status_t (*generate_key) (rsa_private_key_t *this, size_t key_size); - - /** - * @brief Create a rsa_public_key_t with the public - * parts of the key. + * @brief Create a rsa_public_key_t with the public part of the key. * * @param this calling object * @return public_key @@ -125,14 +110,6 @@ struct rsa_private_key_t { bool (*belongs_to) (rsa_private_key_t *this, rsa_public_key_t *public); /** - * @brief Clone the private key. - * - * @param this private key to clone - * @return clone of this - */ - rsa_private_key_t *(*clone) (rsa_private_key_t *this); - - /** * @brief Destroys the private key. * * @param this private key to destroy diff --git a/src/libstrongswan/crypto/rsa/rsa_public_key.c b/src/libstrongswan/crypto/rsa/rsa_public_key.c index 38899670f..6f2158d2b 100644 --- a/src/libstrongswan/crypto/rsa/rsa_public_key.c +++ b/src/libstrongswan/crypto/rsa/rsa_public_key.c @@ -19,6 +19,8 @@ * 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: rsa_public_key.c 3303 2007-10-12 22:49:39Z andreas $ */ #include <gmp.h> @@ -29,74 +31,13 @@ #include "rsa_public_key.h" +#include <debug.h> #include <crypto/hashers/hasher.h> #include <asn1/asn1.h> #include <asn1/pem.h> -/* - * For simplicity, we use these predefined values for hash algorithm OIDs - * These also contain the length of the appended hash - * These values are also used in rsa_private_key.c. - */ - -const u_int8_t md2_oid[] = { - 0x30,0x20, - 0x30,0x0c, - 0x06,0x08, - 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x02, - 0x05,0x00, - 0x04,0x10 -}; - -const u_int8_t md5_oid[] = { - 0x30,0x20, - 0x30,0x0c, - 0x06,0x08, - 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05, - 0x05,0x00, - 0x04,0x10 -}; - -const u_int8_t sha1_oid[] = { - 0x30,0x21, - 0x30,0x09, - 0x06,0x05, - 0x2b,0x0e,0x03,0x02,0x1a, - 0x05,0x00, - 0x04,0x14 -}; - -const u_int8_t sha256_oid[] = { - 0x30,0x31, - 0x30,0x0d, - 0x06,0x09, - 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01, - 0x05,0x00, - 0x04,0x20 -}; - -const u_int8_t sha384_oid[] = { - 0x30,0x41, - 0x30,0x0d, - 0x06,0x09, - 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02, - 0x05,0x00, - 0x04,0x30 -}; - -const u_int8_t sha512_oid[] = { - 0x30,0x51, - 0x30,0x0d, - 0x06,0x09, - 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03, - 0x05,0x00, - 0x04,0x40 -}; - -#define LARGEST_HASH_OID_SIZE sizeof(sha512_oid) - -/* ASN.1 definition public key */ -static const asn1Object_t pubkey_objects[] = { +/* ASN.1 definition of RSApublicKey */ +static const asn1Object_t pubkeyObjects[] = { { 0, "RSAPublicKey", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 1 */ { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 2 */ @@ -107,6 +48,18 @@ static const asn1Object_t pubkey_objects[] = { #define PUB_KEY_EXPONENT 2 #define PUB_KEY_ROOF 3 +/* ASN.1 definition of digestInfo */ +static const asn1Object_t digestInfoObjects[] = { + { 0, "digestInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ + { 1, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 1, "digest", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ +}; + +#define DIGEST_INFO 0 +#define DIGEST_INFO_ALGORITHM 1 +#define DIGEST_INFO_DIGEST 2 +#define DIGEST_INFO_ROOF 3 + typedef struct private_rsa_public_key_t private_rsa_public_key_t; /** @@ -186,12 +139,11 @@ static chunk_t rsaep(const private_rsa_public_key_t *this, chunk_t data) /** * Implementation of rsa_public_key.verify_emsa_pkcs1_signature. */ -static status_t verify_emsa_pkcs1_signature(const private_rsa_public_key_t *this, chunk_t data, chunk_t signature) +static status_t verify_emsa_pkcs1_signature(const private_rsa_public_key_t *this, + hash_algorithm_t algorithm, + chunk_t data, chunk_t signature) { - hasher_t *hasher = NULL; - chunk_t hash; - chunk_t em; - u_int8_t *pos; + chunk_t em_ori, em; status_t res = FAILED; /* remove any preceding 0-bytes from signature */ @@ -207,7 +159,7 @@ static status_t verify_emsa_pkcs1_signature(const private_rsa_public_key_t *this } /* unpack signature */ - em = this->rsavp1(this, signature); + em_ori = em = this->rsavp1(this, signature); /* result should look like this: * EM = 0x00 || 0x01 || PS || 0x00 || T. @@ -216,141 +168,160 @@ static status_t verify_emsa_pkcs1_signature(const private_rsa_public_key_t *this */ /* check magic bytes */ - if ((*(em.ptr) != 0x00) || (*(em.ptr+1) != 0x01)) + if (*(em.ptr) != 0x00 || *(em.ptr+1) != 0x01) { + DBG2("incorrect padding - probably wrong RSA key"); goto end; } + em.ptr += 2; + em.len -= 2; /* find magic 0x00 */ - pos = em.ptr + 2; - while (pos <= em.ptr + em.len) + while (em.len > 0) { - if (*pos == 0x00) + if (*em.ptr == 0x00) { /* found magic byte, stop */ - pos++; + em.ptr++; + em.len--; break; } - else if (*pos != 0xFF) + else if (*em.ptr != 0xFF) { /* bad padding, decryption failed ?!*/ goto end; } - pos++; + em.ptr++; + em.len--; } - if (pos + LARGEST_HASH_OID_SIZE > em.ptr + em.len) - { - /* not enought room for oid compare */ - goto end; - } - - if (memeq(md2_oid, pos, sizeof(md2_oid))) - { - hasher = hasher_create(HASH_MD2); - pos += sizeof(md2_oid); - } - else if (memeq(md5_oid, pos, sizeof(md5_oid))) + if (em.len == 0) { - hasher = hasher_create(HASH_MD5); - pos += sizeof(md5_oid); - } - else if (memeq(sha1_oid, pos, sizeof(sha1_oid))) - { - hasher = hasher_create(HASH_SHA1); - pos += sizeof(sha1_oid); - } - else if (memeq(sha256_oid, pos, sizeof(sha256_oid))) - { - hasher = hasher_create(HASH_SHA256); - pos += sizeof(sha256_oid); - } - else if (memeq(sha384_oid, pos, sizeof(sha384_oid))) - { - hasher = hasher_create(HASH_SHA384); - pos += sizeof(sha384_oid); - } - else if (memeq(sha512_oid, pos, sizeof(sha512_oid))) - { - hasher = hasher_create(HASH_SHA512); - pos += sizeof(sha512_oid); - } - - if (hasher == NULL) - { - /* unsupported hash algorithm */ - res = NOT_SUPPORTED;; + /* no digestInfo found */ goto end; } - - if (pos + hasher->get_hash_size(hasher) != em.ptr + em.len) + + /* parse ASN.1-based digestInfo */ { - /* bad length */ - hasher->destroy(hasher); - goto end; + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + hash_algorithm_t hash_algorithm = HASH_UNKNOWN; + + asn1_init(&ctx, em, 0, FALSE, FALSE); + + while (objectID < DIGEST_INFO_ROOF) + { + if (!extract_object(digestInfoObjects, &objectID, &object, &level, &ctx)) + { + goto end; + } + switch (objectID) + { + case DIGEST_INFO: + if (em.len > object.len) + { + DBG1("digestInfo field in signature is followed by %u surplus bytes", + em.len - object.len); + goto end; + } + break; + case DIGEST_INFO_ALGORITHM: + { + int hash_oid = parse_algorithmIdentifier(object, level+1, NULL); + + hash_algorithm = hasher_algorithm_from_oid(hash_oid); + if (hash_algorithm == HASH_UNKNOWN + || (algorithm != HASH_UNKNOWN && hash_algorithm != algorithm)) + { + DBG1("wrong hash algorithm used in signature"); + goto end; + } + } + break; + case DIGEST_INFO_DIGEST: + { + chunk_t hash; + hasher_t *hasher = hasher_create(hash_algorithm); + + if (object.len != hasher->get_hash_size(hasher)) + { + DBG1("hash size in signature is %u bytes instead of %u bytes", + object.len, hasher->get_hash_size(hasher)); + hasher->destroy(hasher); + goto end; + } + + /* build our own hash */ + hasher->allocate_hash(hasher, data, &hash); + hasher->destroy(hasher); + + /* compare the hashes */ + res = memeq(object.ptr, hash.ptr, hash.len) ? SUCCESS : FAILED; + free(hash.ptr); + } + break; + default: + break; + } + objectID++; + } } - - /* build our own hash */ - hasher->allocate_hash(hasher, data, &hash); - hasher->destroy(hasher); - - /* compare the hashes */ - res = memeq(hash.ptr, pos, hash.len) ? SUCCESS : FAILED; - free(hash.ptr); end: - free(em.ptr); + free(em_ori.ptr); return res; } - + + /** - * Implementation of rsa_public_key.get_key. + * Implementation of rsa_public_key_t.get_modulus. */ -static status_t get_key(const private_rsa_public_key_t *this, chunk_t *key) -{ - chunk_t n, e; - - n.len = this->k; - n.ptr = mpz_export(NULL, NULL, 1, n.len, 1, 0, this->n); - e.len = this->k; - e.ptr = mpz_export(NULL, NULL, 1, e.len, 1, 0, this->e); - - key->len = this->k * 2; - key->ptr = malloc(key->len); - memcpy(key->ptr, n.ptr, n.len); - memcpy(key->ptr + n.len, e.ptr, e.len); - free(n.ptr); - free(e.ptr); - - return SUCCESS; +static mpz_t *get_modulus(const private_rsa_public_key_t *this) +{ + return (mpz_t*)&this->n; } /** - * Implementation of rsa_public_key.save_key. + * Implementation of rsa_public_key_t.get_keysize. */ -static status_t save_key(const private_rsa_public_key_t *this, char *file) +static size_t get_keysize(const private_rsa_public_key_t *this) { - return NOT_SUPPORTED; + return this->k; } /** - * Implementation of rsa_public_key.get_modulus. + * Build a DER-encoded publicKeyInfo object from an RSA public key. + * Also used in rsa_private_key.c. */ -static mpz_t *get_modulus(const private_rsa_public_key_t *this) +chunk_t rsa_public_key_info_to_asn1(const mpz_t n, const mpz_t e) { - return (mpz_t*)&this->n; + chunk_t rawKey = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_integer_from_mpz(n), + asn1_integer_from_mpz(e)); + chunk_t publicKey; + + u_char *pos = build_asn1_object(&publicKey, ASN1_BIT_STRING, 1 + rawKey.len); + + *pos++ = 0x00; + memcpy(pos, rawKey.ptr, rawKey.len); + free(rawKey.ptr); + + return asn1_wrap(ASN1_SEQUENCE, "cm", ASN1_rsaEncryption_id, + publicKey); } /** - * Implementation of rsa_public_key.get_keysize. + * Implementation of rsa_public_key_t.get_publicKeyInfo. */ -static size_t get_keysize(const private_rsa_public_key_t *this) +static chunk_t get_publicKeyInfo(const private_rsa_public_key_t *this) { - return this->k; + return rsa_public_key_info_to_asn1(this->n, this->e); } /** - * Implementation of rsa_public_key.get_keyid. + * Implementation of rsa_public_key_t.get_keyid. */ static chunk_t get_keyid(const private_rsa_public_key_t *this) { @@ -358,7 +329,7 @@ static chunk_t get_keyid(const private_rsa_public_key_t *this) } /** - * Implementation of rsa_public_key.clone. + * Implementation of rsa_public_key_t.clone. */ static rsa_public_key_t* _clone(const private_rsa_public_key_t *this) { @@ -373,7 +344,7 @@ static rsa_public_key_t* _clone(const private_rsa_public_key_t *this) } /** - * Implementation of rsa_public_key.destroy. + * Implementation of rsa_public_key_t.destroy. */ static void destroy(private_rsa_public_key_t *this) { @@ -391,11 +362,10 @@ private_rsa_public_key_t *rsa_public_key_create_empty(void) private_rsa_public_key_t *this = malloc_thing(private_rsa_public_key_t); /* public functions */ - this->public.verify_emsa_pkcs1_signature = (status_t (*) (const rsa_public_key_t*,chunk_t,chunk_t))verify_emsa_pkcs1_signature; - this->public.get_key = (status_t (*) (const rsa_public_key_t*,chunk_t*))get_key; - this->public.save_key = (status_t (*) (const rsa_public_key_t*,char*))save_key; + this->public.verify_emsa_pkcs1_signature = (status_t (*) (const rsa_public_key_t*,hash_algorithm_t,chunk_t,chunk_t))verify_emsa_pkcs1_signature; this->public.get_modulus = (mpz_t *(*) (const rsa_public_key_t*))get_modulus; this->public.get_keysize = (size_t (*) (const rsa_public_key_t*))get_keysize; + this->public.get_publicKeyInfo = (chunk_t (*) (const rsa_public_key_t*))get_publicKeyInfo; this->public.get_keyid = (chunk_t (*) (const rsa_public_key_t*))get_keyid; this->public.clone = (rsa_public_key_t* (*) (const rsa_public_key_t*))_clone; this->public.destroy = (void (*) (rsa_public_key_t*))destroy; @@ -407,27 +377,6 @@ private_rsa_public_key_t *rsa_public_key_create_empty(void) return this; } -/** - * Build a DER-encoded publicKeyInfo object from an RSA public key. - * Also used in rsa_private_key.c. - */ -chunk_t rsa_public_key_info_to_asn1(const mpz_t n, const mpz_t e) -{ - chunk_t rawKey = asn1_wrap(ASN1_SEQUENCE, "mm", - asn1_integer_from_mpz(n), - asn1_integer_from_mpz(e)); - chunk_t publicKey; - - u_char *pos = build_asn1_object(&publicKey, ASN1_BIT_STRING, 1 + rawKey.len); - - *pos++ = 0x00; - memcpy(pos, rawKey.ptr, rawKey.len); - free(rawKey.ptr); - - return asn1_wrap(ASN1_SEQUENCE, "cm", ASN1_rsaEncryption_id, - publicKey); -} - /* * See header */ @@ -447,7 +396,7 @@ rsa_public_key_t *rsa_public_key_create_from_chunk(chunk_t blob) while (objectID < PUB_KEY_ROOF) { - if (!extract_object(pubkey_objects, &objectID, &object, &level, &ctx)) + if (!extract_object(pubkeyObjects, &objectID, &object, &level, &ctx)) { destroy(this); return FALSE; @@ -489,8 +438,9 @@ rsa_public_key_t *rsa_public_key_create_from_file(char *filename) rsa_public_key_t *pubkey = NULL; if (!pem_asn1_load_file(filename, NULL, "public key", &chunk, &pgp)) + { return NULL; - + } pubkey = rsa_public_key_create_from_chunk(chunk); free(chunk.ptr); return pubkey; diff --git a/src/libstrongswan/crypto/rsa/rsa_public_key.h b/src/libstrongswan/crypto/rsa/rsa_public_key.h index 1ee54dcc3..0a40c2204 100644 --- a/src/libstrongswan/crypto/rsa/rsa_public_key.h +++ b/src/libstrongswan/crypto/rsa/rsa_public_key.h @@ -19,6 +19,8 @@ * 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: rsa_public_key.h 3303 2007-10-12 22:49:39Z andreas $ */ #ifndef RSA_PUBLIC_KEY_H_ @@ -29,6 +31,7 @@ typedef struct rsa_public_key_t rsa_public_key_t; #include <gmp.h> #include <library.h> +#include <crypto/hashers/hasher.h> /** * @brief RSA public key with associated functions. @@ -58,6 +61,7 @@ struct rsa_public_key_t { * * @param this rsa_public_key to use * @param data data to sign + # @param algorithm hash algorithm the signature is based on * @param signature signature to verify * @return * - SUCCESS, if signature ok @@ -66,34 +70,9 @@ struct rsa_public_key_t { * - INVALID_ARG, if signature is not a signature * - FAILED if signature invalid or unable to verify */ - status_t (*verify_emsa_pkcs1_signature) (const rsa_public_key_t *this, chunk_t data, chunk_t signature); - - /** - * @brief Gets the key. - * - * Currently uses a proprietary format which is only inteded - * for testing. This should be replaced with a proper - * ASN1 encoded key format, when charon gets the ASN1 - * capabilities. - * - * @param this calling object - * @param key key (in a propriarity format) - * @return - * - SUCCESS - * - INVALID_STATE, if key not set - */ - status_t (*get_key) (const rsa_public_key_t *this, chunk_t *key); - - /** - * @brief Saves a key to a file. - * - * Not implemented! - * - * @param this calling object - * @param file file to which the key should be written. - * @return NOT_SUPPORTED - */ - status_t (*save_key) (const rsa_public_key_t *this, char *file); + status_t (*verify_emsa_pkcs1_signature) (const rsa_public_key_t *this, + hash_algorithm_t algorithm, + chunk_t data, chunk_t signature); /** * @brief Get the modulus of the key. @@ -112,6 +91,14 @@ struct rsa_public_key_t { size_t (*get_keysize) (const rsa_public_key_t *this); /** + * @brief Get the DER encoded publicKeyInfo object. + * + * @param this calling object + * @return DER encoded publicKeyInfo object + */ + chunk_t (*get_publicKeyInfo) (const rsa_public_key_t *this); + + /** * @brief Get the keyid formed as the SHA-1 hash of a publicKeyInfo object. * * @param this calling object diff --git a/src/libstrongswan/crypto/signers/hmac_signer.c b/src/libstrongswan/crypto/signers/hmac_signer.c index 76e1ce50e..ad5b882a6 100644 --- a/src/libstrongswan/crypto/signers/hmac_signer.c +++ b/src/libstrongswan/crypto/signers/hmac_signer.c @@ -52,14 +52,19 @@ struct private_hmac_signer_t { /** * Implementation of signer_t.get_signature. */ -static void get_signature (private_hmac_signer_t *this, chunk_t data, u_int8_t *buffer) +static void get_signature(private_hmac_signer_t *this, chunk_t data, u_int8_t *buffer) { - u_int8_t full_mac[this->hmac_prf->get_block_size(this->hmac_prf)]; - - this->hmac_prf->get_bytes(this->hmac_prf, data, full_mac); - - /* copy MAC depending on truncation */ - memcpy(buffer, full_mac, this->block_size); + if (buffer == NULL) + { /* append mode */ + this->hmac_prf->get_bytes(this->hmac_prf, data, NULL); + } + else + { + u_int8_t full_mac[this->hmac_prf->get_block_size(this->hmac_prf)]; + + this->hmac_prf->get_bytes(this->hmac_prf, data, full_mac); + memcpy(buffer, full_mac, this->block_size); + } } /** @@ -67,18 +72,24 @@ static void get_signature (private_hmac_signer_t *this, chunk_t data, u_int8_t * */ static void allocate_signature (private_hmac_signer_t *this, chunk_t data, chunk_t *chunk) { - chunk_t signature; - u_int8_t full_mac[this->hmac_prf->get_block_size(this->hmac_prf)]; - - this->hmac_prf->get_bytes(this->hmac_prf,data,full_mac); + if (chunk == NULL) + { /* append mode */ + this->hmac_prf->get_bytes(this->hmac_prf, data, NULL); + } + else + { + chunk_t signature; + u_int8_t full_mac[this->hmac_prf->get_block_size(this->hmac_prf)]; + + this->hmac_prf->get_bytes(this->hmac_prf, data, full_mac); - signature.ptr = malloc(this->block_size); - signature.len = this->block_size; - - /* copy signature */ - memcpy(signature.ptr, full_mac, this->block_size); + signature.ptr = malloc(this->block_size); + signature.len = this->block_size; + + memcpy(signature.ptr, full_mac, this->block_size); - *chunk = signature; + *chunk = signature; + } } /** diff --git a/src/libstrongswan/crypto/signers/signer.h b/src/libstrongswan/crypto/signers/signer.h index 0f3709712..4218e4146 100644 --- a/src/libstrongswan/crypto/signers/signer.h +++ b/src/libstrongswan/crypto/signers/signer.h @@ -74,6 +74,9 @@ extern enum_name_t *integrity_algorithm_names; struct signer_t { /** * @brief Generate a signature. + * + * If buffer is NULL, data is processed and prepended to a next call until + * buffer is a valid pointer. * * @param this calling object * @param data a chunk containing the data to sign @@ -83,6 +86,9 @@ struct signer_t { /** * @brief Generate a signature and allocate space for it. + * + * If chunk is NULL, data is processed and prepended to a next call until + * chunk is a valid chunk pointer. * * @param this calling object * @param data a chunk containing the data to sign diff --git a/src/libstrongswan/crypto/x509.c b/src/libstrongswan/crypto/x509.c index 5bf3f26d7..d9093fc62 100755 --- a/src/libstrongswan/crypto/x509.c +++ b/src/libstrongswan/crypto/x509.c @@ -23,6 +23,8 @@ * 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: x509.c 3301 2007-10-12 21:56:30Z andreas $ */ #include <gmp.h> @@ -114,7 +116,7 @@ struct private_x509_t { /** * Signature algorithm */ - int sigAlg; + int signatureAlgorithm; /** * ID representing the certificate issuer @@ -197,11 +199,6 @@ struct private_x509_t { bool isOcspSigner; /** - * Signature algorithm (must be identical to sigAlg) - */ - int algorithm; - - /** * Signature */ chunk_t signature; @@ -445,16 +442,15 @@ static bool parse_basicConstraints(chunk_t blob, int level0) return isCA; } -/* +/** * extracts an otherName */ -static bool -parse_otherName(chunk_t blob, int level0) +static bool parse_otherName(chunk_t blob, int level0) { asn1_ctx_t ctx; chunk_t object; - int objectID = 0; u_int level; + int objectID = 0; int oid = OID_UNKNOWN; asn1_init(&ctx, blob, level0, FALSE, FALSE); @@ -484,7 +480,7 @@ parse_otherName(chunk_t blob, int level0) return TRUE; } -/* +/** * extracts a generalName */ static identification_t *parse_generalName(chunk_t blob, int level0) @@ -544,10 +540,10 @@ static identification_t *parse_generalName(chunk_t blob, int level0) } -/** - * extracts one or several GNs and puts them into a chained list +/* + * Defined in header. */ -void parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list) +void x509_parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list) { asn1_ctx_t ctx; chunk_t object; @@ -589,10 +585,10 @@ static chunk_t parse_keyIdentifier(chunk_t blob, int level0, bool implicit) return object; } -/** - * extracts an authoritykeyIdentifier +/* + * Defined in header. */ -void parse_authorityKeyIdentifier(chunk_t blob, int level0 , chunk_t *authKeyID, chunk_t *authKeySerialNumber) +void x509_parse_authorityKeyIdentifier(chunk_t blob, int level0 , chunk_t *authKeyID, chunk_t *authKeySerialNumber) { asn1_ctx_t ctx; chunk_t object; @@ -639,8 +635,7 @@ static void parse_authorityInfoAccess(chunk_t blob, int level0, linked_list_t *l chunk_t object; u_int level; int objectID = 0; - - u_int accessMethod = OID_UNKNOWN; + int accessMethod = OID_UNKNOWN; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < AUTH_INFO_ACCESS_ROOF) @@ -659,15 +654,26 @@ static void parse_authorityInfoAccess(chunk_t blob, int level0, linked_list_t *l switch (accessMethod) { case OID_OCSP: - if (*object.ptr == ASN1_CONTEXT_S_6) + case OID_CA_ISSUERS: { identification_t *accessLocation; - if (asn1_length(&object) == ASN1_INVALID_LENGTH) + accessLocation = parse_generalName(object, level+1); + if (accessLocation == NULL) + { + /* parsing went wrong - abort */ return; - DBG2(" '%.*s'",(int)object.len, object.ptr); - accessLocation = identification_create_from_encoding(ID_DER_ASN1_GN_URI, object); - list->insert_last(list, (void *)accessLocation); + } + DBG2(" '%D'", accessLocation); + if (accessMethod == OID_OCSP) + { + list->insert_last(list, (void *)accessLocation); + } + else + { + /* caIsssuer accessLocation is not used yet */ + accessLocation->destroy(accessLocation); + } } break; default: @@ -731,7 +737,7 @@ static void parse_crlDistributionPoints(chunk_t blob, int level0, linked_list_t if (objectID == CRL_DIST_POINTS_FULLNAME) { /* append extracted generalNames to existing chained list */ - parse_generalNames(object, level+1, TRUE, list); + x509_parse_generalNames(object, level+1, TRUE, list); } objectID++; @@ -748,8 +754,8 @@ static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *this) bool critical; chunk_t object; u_int level; - u_int extn_oid = OID_UNKNOWN; int objectID = 0; + int extn_oid = OID_UNKNOWN; asn1_init(&ctx, blob, level0, FALSE, FALSE); while (objectID < X509_OBJ_ROOF) @@ -778,7 +784,7 @@ static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *this) this->serialNumber = object; break; case X509_OBJ_SIG_ALG: - this->sigAlg = parse_algorithmIdentifier(object, level, NULL); + this->signatureAlgorithm = parse_algorithmIdentifier(object, level, NULL); break; case X509_OBJ_ISSUER: this->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); @@ -797,7 +803,7 @@ static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *this) case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM: if (parse_algorithmIdentifier(object, level, NULL) != OID_RSA_ENCRYPTION) { - DBG2(" unsupported public key algorithm"); + DBG1(" unsupported public key algorithm"); return FALSE; } break; @@ -809,7 +815,7 @@ static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *this) } else { - DBG2(" invalid RSA public key format"); + DBG1(" invalid RSA public key format"); return FALSE; } break; @@ -831,7 +837,7 @@ static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *this) this->subjectKeyID = chunk_clone(parse_keyIdentifier(object, level, FALSE)); break; case OID_SUBJECT_ALT_NAME: - parse_generalNames(object, level, FALSE, this->subjectAltNames); + x509_parse_generalNames(object, level, FALSE, this->subjectAltNames); break; case OID_BASIC_CONSTRAINTS: this->isCA = parse_basicConstraints(object, level); @@ -840,7 +846,8 @@ static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *this) parse_crlDistributionPoints(object, level, this->crlDistributionPoints); break; case OID_AUTHORITY_KEY_ID: - parse_authorityKeyIdentifier(object, level , &this->authKeyID, &this->authKeySerialNumber); + x509_parse_authorityKeyIdentifier(object, level, + &this->authKeyID, &this->authKeySerialNumber); break; case OID_AUTHORITY_INFO_ACCESS: parse_authorityInfoAccess(object, level, this->ocspAccessLocations); @@ -861,7 +868,15 @@ static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *this) break; } case X509_OBJ_ALGORITHM: - this->algorithm = parse_algorithmIdentifier(object, level, NULL); + { + int alg = parse_algorithmIdentifier(object, level, NULL); + + if (alg != this->signatureAlgorithm) + { + DBG1(" signature algorithms do not agree"); + return FALSE; + } + } break; case X509_OBJ_SIGNATURE: this->signature = object; @@ -1119,7 +1134,14 @@ static iterator_t *create_ocspuri_iterator(const private_x509_t *this) */ static bool verify(const private_x509_t *this, const rsa_public_key_t *signer) { - return signer->verify_emsa_pkcs1_signature(signer, this->tbsCertificate, this->signature) == SUCCESS; + hash_algorithm_t algorithm = hasher_algorithm_from_oid(this->signatureAlgorithm); + + if (algorithm == HASH_UNKNOWN) + { + DBG1(" unknown signature algorithm"); + return FALSE; + } + return signer->verify_emsa_pkcs1_signature(signer, algorithm, this->tbsCertificate, this->signature) == SUCCESS; } /** @@ -1221,6 +1243,101 @@ static void list(private_x509_t *this, FILE *out, bool utc) } } +/* + * Defined in header. + */ +chunk_t x509_build_generalNames(linked_list_t *list) +{ + linked_list_t *generalNames = linked_list_create(); + iterator_t *iterator = list->create_iterator(list, TRUE); + identification_t *name; + size_t len = 0; + + while (iterator->iterate(iterator, (void**)&name)) + { + asn1_t asn1_type = ASN1_EOC; + chunk_t *generalName = malloc_thing(chunk_t); + + switch (name->get_type(name)) + { + case ID_RFC822_ADDR: + asn1_type = ASN1_CONTEXT_S_1; + break; + case ID_FQDN: + asn1_type = ASN1_CONTEXT_S_2; + break; + case ID_DER_ASN1_DN: + asn1_type = ASN1_CONTEXT_C_4; + break; + case ID_DER_ASN1_GN_URI: + asn1_type = ASN1_CONTEXT_S_6; + break; + case ID_IPV4_ADDR: + asn1_type = ASN1_CONTEXT_S_7; + break; + default: + continue; + } + + *generalName = asn1_simple_object(asn1_type, name->get_encoding(name)); + len += generalName->len; + generalNames->insert_last(generalNames, generalName); + } + iterator->destroy(iterator); + + if (len > 0) + { + iterator_t *iterator = generalNames->create_iterator(generalNames, TRUE); + chunk_t names, *generalName; + u_char *pos = build_asn1_object(&names, ASN1_SEQUENCE, len); + + while (iterator->iterate(iterator, (void**)&generalName)) + { + memcpy(pos, generalName->ptr, generalName->len); + pos += generalName->len; + free(generalName->ptr); + free(generalName); + } + iterator->destroy(iterator); + generalNames->destroy(generalNames); + + return asn1_wrap(ASN1_OCTET_STRING, "m", names); + } + else + { + return chunk_empty; + } +} + +/* + * Defined in header. + */ +chunk_t x509_build_subjectAltNames(linked_list_t *list) +{ + chunk_t generalNames = x509_build_generalNames(list); + + if (generalNames.len) + { + return asn1_wrap(ASN1_SEQUENCE, "cm", + ASN1_subjectAltName_oid, + asn1_wrap(ASN1_OCTET_STRING, "m", generalNames) + ); + } + else + { + return chunk_empty; + } +} + +/** + * Implementation of x509_t.build_encoding. + */ +static void build_encoding(private_x509_t *this, hash_algorithm_t alg, + rsa_private_key_t *private_key) +{ + +} + /** * Implements x509_t.destroy */ @@ -1240,10 +1357,10 @@ static void destroy(private_x509_t *this) free(this); } -/* - * Described in header. +/** + * Internal generic constructor */ -x509_t *x509_create_from_chunk(chunk_t chunk, u_int level) +static private_x509_t *x509_create_empty(void) { private_x509_t *this = malloc_thing(private_x509_t); @@ -1290,9 +1407,34 @@ x509_t *x509_create_from_chunk(chunk_t chunk, u_int level) this->public.create_crluri_iterator = (iterator_t* (*) (const x509_t*))create_crluri_iterator; this->public.create_ocspuri_iterator = (iterator_t* (*) (const x509_t*))create_ocspuri_iterator; this->public.verify = (bool (*) (const x509_t*,const rsa_public_key_t*))verify; - this->public.list = (void(*)(x509_t*, FILE *out, bool utc))list; + this->public.list = (void (*) (x509_t*, FILE *out, bool utc))list; + this->public.build_encoding = (void (*) (x509_t*,hash_algorithm_t,rsa_private_key_t*))build_encoding; this->public.destroy = (void (*) (x509_t*))destroy; + return this; +} + +/* + * Described in header. + */ +x509_t *x509_create_(chunk_t serialNumber, identification_t *issuer, identification_t *subject) +{ + private_x509_t *this = x509_create_empty(); + + this->serialNumber = serialNumber; + this->issuer = issuer->clone(issuer); + this->subject = subject->clone(subject); + + return &this->public; +} + +/* + * Described in header. + */ +x509_t *x509_create_from_chunk(chunk_t chunk, u_int level) +{ + private_x509_t *this = x509_create_empty(); + if (!parse_certificate(chunk, level, this)) { destroy(this); @@ -1314,8 +1456,15 @@ x509_t *x509_create_from_chunk(chunk_t chunk, u_int level) this->isSelfSigned = FALSE; if (this->subject->equals(this->subject, this->issuer)) { + hash_algorithm_t algorithm = hasher_algorithm_from_oid(this->signatureAlgorithm); + + if (algorithm == HASH_UNKNOWN) + { + destroy(this); + return NULL; + } this->isSelfSigned = this->public_key->verify_emsa_pkcs1_signature(this->public_key, - this->tbsCertificate, this->signature) == SUCCESS; + algorithm, this->tbsCertificate, this->signature) == SUCCESS; } if (this->isSelfSigned) { diff --git a/src/libstrongswan/crypto/x509.h b/src/libstrongswan/crypto/x509.h index c6fe148d4..1ab267dac 100755 --- a/src/libstrongswan/crypto/x509.h +++ b/src/libstrongswan/crypto/x509.h @@ -23,6 +23,8 @@ * 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: x509.h 3301 2007-10-12 21:56:30Z andreas $ */ #ifndef X509_H_ @@ -31,7 +33,8 @@ typedef struct x509_t x509_t; #include <library.h> -#include <crypto/rsa/rsa_public_key.h> +#include <crypto/rsa/rsa_private_key.h> +#include <crypto/hashers/hasher.h> #include <crypto/certinfo.h> #include <crypto/ca.h> #include <utils/identification.h> @@ -49,6 +52,7 @@ typedef struct x509_t x509_t; * @brief X.509 certificate. * * @b Constructors: + * - x509_create() * - x509_create_from_chunk() * - x509_create_from_file() * @@ -288,9 +292,26 @@ struct x509_t { * @param out stream to write to * @param utc TRUE for UTC times, FALSE for local time */ - void (*list)(x509_t *this, FILE *out, bool utc); + void (*list) (x509_t *this, FILE *out, bool utc); /** + * @brief Adds a list of subjectAltNames + * + * @param this calling object + * @param subjectAltNames list of subjectAltNames to be added + */ + void (*add_subjectAltNames) (x509_t *this, linked_list_t *subjectAltNames); + + /** + * @brief Builds a DER-encoded signed X.509 certificate + * + * @param this calling object + * @param alg hash algorithm used to compute the certificate digest + * @param private_key RSA private key used to sign the certificate digest + */ + void (*build_encoding) (x509_t *this, hash_algorithm_t alg, rsa_private_key_t *private_key); + + /** * @brief Destroys the certificate. * * @param this certificate to destroy @@ -299,17 +320,34 @@ struct x509_t { }; /** - * @brief Read a x509 certificate from a DER encoded blob. - * + * @brief Create a X.509 certificate from its components + * + * @param serialNumber chunk containing the serialNumber + * @param issuer issuer distinguished name + * @param notBefore start date of validity + * @param notAfter end date of validity + * @param subject subject distinguished name + * + * @return created x509_t certificate, or NULL if invalid. + * + * @ingroup crypto + */ +x509_t *x509_create(chunk_t serialNumber, identification_t *issuer, + time_t notBefore, time_t notAfter, + identification_t *subject); + +/** + * @brief Read a X.509 certificate from a DER encoded blob. + * * @param chunk chunk containing DER encoded data - * @return created x509_t certificate, or NULL if invlid. + * @return created x509_t certificate, or NULL if invalid. * * @ingroup crypto */ x509_t *x509_create_from_chunk(chunk_t chunk, u_int level); /** - * @brief Read a x509 certificate from a DER encoded file. + * @brief Read a X.509 certificate from a DER encoded file. * * @param filename file containing DER encoded data * @param label label describing kind of certificate @@ -329,7 +367,7 @@ x509_t *x509_create_from_file(const char *filename, const char *label); * * @ingroup crypto */ -void parse_authorityKeyIdentifier(chunk_t blob, int level0, chunk_t *authKeyID, chunk_t *authKeySerialNumber); +void x509_parse_authorityKeyIdentifier(chunk_t blob, int level0, chunk_t *authKeyID, chunk_t *authKeySerialNumber); /** * @brief Parses DER encoded generalNames @@ -337,10 +375,30 @@ void parse_authorityKeyIdentifier(chunk_t blob, int level0, chunk_t *authKeyID, * @param blob blob containing DER encoded data * @param level0 indicates the current parsing level * @param implicit implicit coding is used - * @param list linked list of decoded generalNames + * @param list list of decoded generalNames + * + * @ingroup crypto + */ +void x509_parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list); + +/** + * @brief Builds a DER encoded list of generalNames + * + * @param list list of generalNames to be encoded + * @return DER encoded list of generalNames + * + * @ingroup crypto + */ +chunk_t x509_build_generalNames(linked_list_t *list); + +/** + * @brief Builds a DER encoded list of subjectAltNames + * + * @param list list of subjectAltNames to be encoded + * @return DER encoded list of subjectAltNames * * @ingroup crypto */ -void parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list); +chunk_t x509_build_subjectAltNames(linked_list_t *list); #endif /* X509_H_ */ |