diff options
Diffstat (limited to 'src')
23 files changed, 1027 insertions, 133 deletions
diff --git a/src/charon-tkm/src/tkm/tkm_keymat.c b/src/charon-tkm/src/tkm/tkm_keymat.c index 772fac8b0..80721fafe 100644 --- a/src/charon-tkm/src/tkm/tkm_keymat.c +++ b/src/charon-tkm/src/tkm/tkm_keymat.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyrigth (C) 2012 Reto Buerki * Copyright (C) 2012 Adrian-Ken Rueegsegger * Hochschule fuer Technik Rapperswil @@ -17,6 +18,7 @@ #include <daemon.h> #include <tkm/constants.h> #include <tkm/client.h> +#include <crypto/hashers/hash_algorithm_set.h> #include "tkm.h" #include "tkm_types.h" @@ -71,6 +73,10 @@ struct private_tkm_keymat_t { */ chunk_t other_init_msg; + /** + * Set of hash algorithms supported by peer for signature authentication + */ + hash_algorithm_set_t *hash_algorithms; }; /** @@ -417,6 +423,26 @@ METHOD(keymat_v2_t, get_psk_sig, bool, return FALSE; } +METHOD(keymat_v2_t, hash_algorithm_supported, bool, + private_tkm_keymat_t *this, hash_algorithm_t hash) +{ + if (!this->hash_algorithms) + { + return FALSE; + } + return this->hash_algorithms->contains(this->hash_algorithms, hash); +} + +METHOD(keymat_v2_t, add_hash_algorithm, void, + private_tkm_keymat_t *this, hash_algorithm_t hash) +{ + if (!this->hash_algorithms) + { + this->hash_algorithms = hash_algorithm_set_create(); + } + this->hash_algorithms->add(this->hash_algorithms, hash); +} + METHOD(keymat_t, destroy, void, private_tkm_keymat_t *this) { @@ -435,6 +461,7 @@ METHOD(keymat_t, destroy, void, tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_AE, this->ae_ctx_id); } + DESTROY_IF(this->hash_algorithms); DESTROY_IF(this->aead_in); DESTROY_IF(this->aead_out); chunk_free(&this->auth_payload); @@ -488,6 +515,8 @@ tkm_keymat_t *tkm_keymat_create(bool initiator) .get_skd = _get_skd, .get_auth_octets = _get_auth_octets, .get_psk_sig = _get_psk_sig, + .add_hash_algorithm = _add_hash_algorithm, + .hash_algorithm_supported = _hash_algorithm_supported, }, .get_isa_id = _get_isa_id, .set_auth_payload = _set_auth_payload, diff --git a/src/libcharon/encoding/payloads/notify_payload.c b/src/libcharon/encoding/payloads/notify_payload.c index 94723ddd7..f32a1273f 100644 --- a/src/libcharon/encoding/payloads/notify_payload.c +++ b/src/libcharon/encoding/payloads/notify_payload.c @@ -65,7 +65,7 @@ ENUM_NEXT(notify_type_names, ME_CONNECT_FAILED, ME_CONNECT_FAILED, CHILD_SA_NOT_ "ME_CONNECT_FAILED"); ENUM_NEXT(notify_type_names, MS_NOTIFY_STATUS, MS_NOTIFY_STATUS, ME_CONNECT_FAILED, "MS_NOTIFY_STATUS"); -ENUM_NEXT(notify_type_names, INITIAL_CONTACT, FRAGMENTATION_SUPPORTED, MS_NOTIFY_STATUS, +ENUM_NEXT(notify_type_names, INITIAL_CONTACT, SIGNATURE_HASH_ALGORITHMS, MS_NOTIFY_STATUS, "INITIAL_CONTACT", "SET_WINDOW_SIZE", "ADDITIONAL_TS_POSSIBLE", @@ -112,8 +112,9 @@ ENUM_NEXT(notify_type_names, INITIAL_CONTACT, FRAGMENTATION_SUPPORTED, MS_NOTIFY "ERX_SUPPORTED", "IFOM_CAPABILITY", "SENDER_REQUEST_ID", - "FRAGMENTATION_SUPPORTED"); -ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, FRAGMENTATION_SUPPORTED, + "FRAGMENTATION_SUPPORTED", + "SIGNATURE_HASH_ALGORITHMS"); +ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, SIGNATURE_HASH_ALGORITHMS, "INITIAL_CONTACT"); ENUM_NEXT(notify_type_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1, "DPD_R_U_THERE", @@ -174,7 +175,7 @@ ENUM_NEXT(notify_type_short_names, ME_CONNECT_FAILED, ME_CONNECT_FAILED, CHILD_S "ME_CONN_FAIL"); ENUM_NEXT(notify_type_short_names, MS_NOTIFY_STATUS, MS_NOTIFY_STATUS, ME_CONNECT_FAILED, "MS_STATUS"); -ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT, FRAGMENTATION_SUPPORTED, MS_NOTIFY_STATUS, +ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT, SIGNATURE_HASH_ALGORITHMS, MS_NOTIFY_STATUS, "INIT_CONTACT", "SET_WINSIZE", "ADD_TS_POSS", @@ -221,8 +222,9 @@ ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT, FRAGMENTATION_SUPPORTED, MS_ "ERX_SUP", "IFOM_CAP", "SENDER_REQ_ID", - "FRAG_SUP"); -ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, FRAGMENTATION_SUPPORTED, + "FRAG_SUP", + "HASH_ALG"); +ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, SIGNATURE_HASH_ALGORITHMS, "INITIAL_CONTACT"); ENUM_NEXT(notify_type_short_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1, "DPD", @@ -473,6 +475,14 @@ METHOD(payload_t, verify, status_t, } break; } + case SIGNATURE_HASH_ALGORITHMS: + { + if (this->notify_data.len % 2) + { + bad_length = TRUE; + } + break; + } case AUTH_LIFETIME: { if (this->notify_data.len != 4) diff --git a/src/libcharon/encoding/payloads/notify_payload.h b/src/libcharon/encoding/payloads/notify_payload.h index 25521c2bb..690757383 100644 --- a/src/libcharon/encoding/payloads/notify_payload.h +++ b/src/libcharon/encoding/payloads/notify_payload.h @@ -151,6 +151,8 @@ enum notify_type_t { SENDER_REQUEST_ID = 16429, /* IKEv2 fragmentation supported, RFC 7383 */ FRAGMENTATION_SUPPORTED = 16430, + /* Signature Hash Algorithms, RFC 7427 */ + SIGNATURE_HASH_ALGORITHMS = 16431, /* IKEv1 initial contact */ INITIAL_CONTACT_IKEV1 = 24578, /* IKEv1 DPD */ diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index 88abe4951..0483ba2f5 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -301,7 +301,8 @@ static void build_crl_policy(auth_cfg_t *cfg, bool local, int policy) static void parse_pubkey_constraints(char *auth, auth_cfg_t *cfg) { enumerator_t *enumerator; - bool rsa = FALSE, ecdsa = FALSE, rsa_len = FALSE, ecdsa_len = FALSE; + bool rsa = FALSE, ecdsa = FALSE, bliss = FALSE, + rsa_len = FALSE, ecdsa_len = FALSE, bliss_strength = FALSE; int strength; char *token; @@ -328,6 +329,9 @@ static void parse_pubkey_constraints(char *auth, auth_cfg_t *cfg) { "sha256", SIGN_ECDSA_256, KEY_ECDSA, }, { "sha384", SIGN_ECDSA_384, KEY_ECDSA, }, { "sha512", SIGN_ECDSA_521, KEY_ECDSA, }, + { "sha256", SIGN_BLISS_WITH_SHA256, KEY_BLISS, }, + { "sha384", SIGN_BLISS_WITH_SHA384, KEY_BLISS, }, + { "sha512", SIGN_BLISS_WITH_SHA512, KEY_BLISS, }, }; if (rsa_len || ecdsa_len) @@ -343,8 +347,12 @@ static void parse_pubkey_constraints(char *auth, auth_cfg_t *cfg) { cfg->add(cfg, AUTH_RULE_ECDSA_STRENGTH, (uintptr_t)strength); } + else if (bliss_strength) + { + cfg->add(cfg, AUTH_RULE_BLISS_STRENGTH, (uintptr_t)strength); + } } - rsa_len = ecdsa_len = FALSE; + rsa_len = ecdsa_len = bliss_strength = FALSE; if (strength) { continue; @@ -360,6 +368,11 @@ static void parse_pubkey_constraints(char *auth, auth_cfg_t *cfg) ecdsa = ecdsa_len = TRUE; continue; } + if (streq(token, "bliss")) + { + bliss = bliss_strength = TRUE; + continue; + } if (streq(token, "pubkey")) { continue; @@ -376,7 +389,8 @@ static void parse_pubkey_constraints(char *auth, auth_cfg_t *cfg) */ if ((rsa && schemes[i].key == KEY_RSA) || (ecdsa && schemes[i].key == KEY_ECDSA) || - (!rsa && !ecdsa)) + (bliss && schemes[i].key == KEY_BLISS) || + (!rsa && !ecdsa && !bliss)) { cfg->add(cfg, AUTH_RULE_SIGNATURE_SCHEME, (uintptr_t)schemes[i].scheme); @@ -590,7 +604,8 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, /* authentication metod (class, actually) */ if (strpfx(auth, "pubkey") || strpfx(auth, "rsa") || - strpfx(auth, "ecdsa")) + strpfx(auth, "ecdsa") || + strpfx(auth, "bliss")) { cfg->add(cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); build_crl_policy(cfg, local, msg->add_conn.crl_policy); diff --git a/src/libcharon/sa/authenticator.c b/src/libcharon/sa/authenticator.c index a3d67ed7a..6c3681a2d 100644 --- a/src/libcharon/sa/authenticator.c +++ b/src/libcharon/sa/authenticator.c @@ -31,15 +31,14 @@ ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS, "RSA signature", "pre-shared key", "DSS signature"); -ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_NULL, AUTH_DSS, +ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_DS, AUTH_DSS, "ECDSA-256 signature", "ECDSA-384 signature", "ECDSA-521 signature", "secure password method", - "NULL authentication"); -ENUM_NEXT(auth_method_names, AUTH_BLISS, AUTH_BLISS, AUTH_NULL, - "BLISS signature"); -ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_BLISS, + "NULL authentication", + "digital signature"); +ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_DS, "XAuthInitPSK", "XAuthRespPSK", "XAuthInitRSA", @@ -102,7 +101,7 @@ authenticator_t *authenticator_create_verifier( case AUTH_ECDSA_256: case AUTH_ECDSA_384: case AUTH_ECDSA_521: - case AUTH_BLISS: + case AUTH_DS: return (authenticator_t*)pubkey_authenticator_create_verifier(ike_sa, sent_nonce, received_init, reserved); case AUTH_PSK: diff --git a/src/libcharon/sa/authenticator.h b/src/libcharon/sa/authenticator.h index aefb94294..97c042e71 100644 --- a/src/libcharon/sa/authenticator.h +++ b/src/libcharon/sa/authenticator.h @@ -85,9 +85,9 @@ enum auth_method_t { AUTH_NULL = 13, /** - * BLISS Authentication Method + * Digital Signature as specified in RFC 7427 */ - AUTH_BLISS = 220, + AUTH_DS = 14, /** * IKEv1 initiator XAUTH with PSK, outside of IANA range diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index f65efd81f..9dbc805c9 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -131,6 +131,11 @@ enum ike_extension_t { * peer supports proprietary IKEv1 or standardized IKEv2 fragmentation */ EXT_IKE_FRAGMENTATION = (1<<11), + + /** + * Signature Authentication, RFC 7427 + */ + EXT_SIGNATURE_AUTH = (1<<12), }; /** diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 2188fb2e8..965f70aa5 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -20,6 +20,8 @@ #include <daemon.h> #include <encoding/payloads/auth_payload.h> #include <sa/ikev2/keymat_v2.h> +#include <asn1/asn1.h> +#include <asn1/oid.h> typedef struct private_pubkey_authenticator_t private_pubkey_authenticator_t; @@ -52,8 +54,136 @@ struct private_pubkey_authenticator_t { * Reserved bytes of ID payload */ char reserved[3]; + + /** + * Whether to store signature schemes on remote auth configs. + */ + bool store_signature_scheme; }; +/** + * Parse authentication data used for Signature Authentication as per RFC 7427 + */ +static bool parse_signature_auth_data(chunk_t *auth_data, key_type_t *key_type, + signature_scheme_t *scheme) +{ + u_int8_t len; + int oid; + + if (!auth_data->len) + { + return FALSE; + } + len = auth_data->ptr[0]; + *auth_data = chunk_skip(*auth_data, 1); + /* we currently don't support schemes that require parameters */ + oid = asn1_parse_algorithmIdentifier(*auth_data, 1, NULL); + *scheme = signature_scheme_from_oid(oid); + if (*scheme == SIGN_UNKNOWN) + { + return FALSE; + } + *key_type = key_type_from_signature_scheme(*scheme); + *auth_data = chunk_skip(*auth_data, len); + return TRUE; +} + +/** + * Build authentication data used for Signature Authentication as per RFC 7427 + */ +static bool build_signature_auth_data(chunk_t *auth_data, + signature_scheme_t scheme) +{ + chunk_t data; + u_int8_t len; + int oid; + + oid = signature_scheme_to_oid(scheme); + if (oid == OID_UNKNOWN) + { + return FALSE; + } + data = asn1_algorithmIdentifier(oid); + len = data.len; + *auth_data = chunk_cat("cmm", chunk_from_thing(len), data, *auth_data); + return TRUE; +} + +/** + * Select a signature scheme based on our configuration, the other peer's + * capabilities and the private key + */ +static signature_scheme_t select_signature_scheme(keymat_v2_t *keymat, + auth_cfg_t *auth, private_key_t *private) +{ + enumerator_t *enumerator; + signature_scheme_t selected = SIGN_UNKNOWN, scheme; + uintptr_t config; + auth_rule_t rule; + key_type_t key_type; + bool have_config = FALSE; + + key_type = private->get_type(private); + enumerator = auth->create_enumerator(auth); + while (enumerator->enumerate(enumerator, &rule, &config)) + { + if (rule != AUTH_RULE_SIGNATURE_SCHEME) + { + continue; + } + have_config = TRUE; + if (key_type == key_type_from_signature_scheme(config) && + keymat->hash_algorithm_supported(keymat, + hasher_from_signature_scheme(config))) + { + selected = config; + break; + } + } + enumerator->destroy(enumerator); + + if (selected == SIGN_UNKNOWN && !have_config) + { + /* if no specific configuration, find a scheme appropriate for the key + * and supported by the other peer */ + enumerator = signature_schemes_for_key(key_type, + private->get_keysize(private)); + while (enumerator->enumerate(enumerator, &scheme)) + { + if (keymat->hash_algorithm_supported(keymat, + hasher_from_signature_scheme(scheme))) + { + selected = scheme; + break; + } + } + enumerator->destroy(enumerator); + + /* for RSA we tried at least SHA-512, also try other schemes down to + * what we'd use with classic authentication */ + if (selected == SIGN_UNKNOWN && key_type == KEY_RSA) + { + signature_scheme_t schemes[] = { + SIGN_RSA_EMSA_PKCS1_SHA384, + SIGN_RSA_EMSA_PKCS1_SHA256, + SIGN_RSA_EMSA_PKCS1_SHA1, + }; + int i; + + for (i = 0; i < countof(schemes); i++) + { + if (keymat->hash_algorithm_supported(keymat, + hasher_from_signature_scheme(schemes[i]))) + { + selected = scheme; + break; + } + } + } + } + return selected; +} + METHOD(authenticator_t, build, status_t, private_pubkey_authenticator_t *this, message_t *message) { @@ -64,7 +194,7 @@ METHOD(authenticator_t, build, status_t, auth_cfg_t *auth; auth_payload_t *auth_payload; auth_method_t auth_method; - signature_scheme_t scheme; + signature_scheme_t scheme = SIGN_UNKNOWN; keymat_v2_t *keymat; id = this->ike_sa->get_my_id(this->ike_sa); @@ -75,62 +205,75 @@ METHOD(authenticator_t, build, status_t, DBG1(DBG_IKE, "no private key found for '%Y'", id); return NOT_FOUND; } + keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); - switch (private->get_type(private)) + if (this->ike_sa->supports_extension(this->ike_sa, EXT_SIGNATURE_AUTH)) { - case KEY_RSA: - /* we currently use always SHA1 for signatures, - * TODO: support other hashes depending on configuration/auth */ - scheme = SIGN_RSA_EMSA_PKCS1_SHA1; - auth_method = AUTH_RSA; - break; - case KEY_ECDSA: - /* we try to deduct the signature scheme from the keysize */ - switch (private->get_keysize(private)) - { - case 256: - scheme = SIGN_ECDSA_256; - auth_method = AUTH_ECDSA_256; - break; - case 384: - scheme = SIGN_ECDSA_384; - auth_method = AUTH_ECDSA_384; - break; - case 521: - scheme = SIGN_ECDSA_521; - auth_method = AUTH_ECDSA_521; - break; - default: - DBG1(DBG_IKE, "%d bit ECDSA private key size not supported", - private->get_keysize(private)); - return status; - } - break; - case KEY_BLISS: - /* we currently use SHA512 only */ - scheme = SIGN_BLISS_WITH_SHA512; - auth_method = AUTH_BLISS; - break; - default: - DBG1(DBG_IKE, "private key of type %N not supported", - key_type_names, private->get_type(private)); + scheme = select_signature_scheme(keymat, auth, private); + auth_method = AUTH_DS; + if (scheme == SIGN_UNKNOWN) + { + DBG1(DBG_IKE, "no common hash algorithm found to create signature " + "with %N key", key_type_names, private->get_type(private)); return status; + } } - keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); + else + { + switch (private->get_type(private)) + { + case KEY_RSA: + scheme = SIGN_RSA_EMSA_PKCS1_SHA1; + auth_method = AUTH_RSA; + break; + case KEY_ECDSA: + /* deduct the signature scheme from the keysize */ + switch (private->get_keysize(private)) + { + case 256: + scheme = SIGN_ECDSA_256; + auth_method = AUTH_ECDSA_256; + break; + case 384: + scheme = SIGN_ECDSA_384; + auth_method = AUTH_ECDSA_384; + break; + case 521: + scheme = SIGN_ECDSA_521; + auth_method = AUTH_ECDSA_521; + break; + default: + DBG1(DBG_IKE, "%d bit ECDSA private key size not " + "supported", private->get_keysize(private)); + return status; + } + break; + default: + DBG1(DBG_IKE, "private key of type %N not supported", + key_type_names, private->get_type(private)); + return status; + } + } + if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, this->nonce, id, this->reserved, &octets) && private->sign(private, scheme, octets, &auth_data)) { - auth_payload = auth_payload_create(); - auth_payload->set_auth_method(auth_payload, auth_method); - auth_payload->set_data(auth_payload, auth_data); - chunk_free(&auth_data); - message->add_payload(message, (payload_t*)auth_payload); - status = SUCCESS; + if (auth_method != AUTH_DS || + build_signature_auth_data(&auth_data, scheme)) + { + auth_payload = auth_payload_create(); + auth_payload->set_auth_method(auth_payload, auth_method); + auth_payload->set_data(auth_payload, auth_data); + chunk_free(&auth_data); + message->add_payload(message, (payload_t*)auth_payload); + status = SUCCESS; + } } DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id, - auth_method_names, auth_method, - (status == SUCCESS)? "successful":"failed"); + auth_method == AUTH_DS ? signature_scheme_names : auth_method_names, + auth_method == AUTH_DS ? scheme : auth_method, + status == SUCCESS ? "successful" : "failed"); chunk_free(&octets); private->destroy(private); @@ -158,11 +301,10 @@ METHOD(authenticator_t, process, status_t, return FAILED; } auth_method = auth_payload->get_auth_method(auth_payload); + auth_data = auth_payload->get_data(auth_payload); switch (auth_method) { case AUTH_RSA: - /* We currently accept SHA1 signatures only - * TODO: allow other hash algorithms and note it in "auth" */ key_type = KEY_RSA; scheme = SIGN_RSA_EMSA_PKCS1_SHA1; break; @@ -175,14 +317,15 @@ METHOD(authenticator_t, process, status_t, case AUTH_ECDSA_521: scheme = SIGN_ECDSA_521; break; - case AUTH_BLISS: - key_type = KEY_BLISS; - scheme = SIGN_BLISS_WITH_SHA512; - break; + case AUTH_DS: + if (parse_signature_auth_data(&auth_data, &key_type, &scheme)) + { + break; + } + /* fall-through */ default: return INVALID_ARG; } - auth_data = auth_payload->get_data(auth_payload); id = this->ike_sa->get_other_id(this->ike_sa); keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); if (!keymat->get_auth_octets(keymat, TRUE, this->ike_sa_init, @@ -197,11 +340,16 @@ METHOD(authenticator_t, process, status_t, { if (public->verify(public, scheme, octets, auth_data)) { - DBG1(DBG_IKE, "authentication of '%Y' with %N successful", - id, auth_method_names, auth_method); + DBG1(DBG_IKE, "authentication of '%Y' with %N successful", id, + auth_method == AUTH_DS ? signature_scheme_names : auth_method_names, + auth_method == AUTH_DS ? scheme : auth_method); status = SUCCESS; auth->merge(auth, current_auth, FALSE); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); + if (this->store_signature_scheme) + { + auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, (uintptr_t)scheme); + } break; } else @@ -274,6 +422,8 @@ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa, .ike_sa = ike_sa, .ike_sa_init = received_init, .nonce = sent_nonce, + .store_signature_scheme = lib->settings->get_bool(lib->settings, + "%s.signature_authentication_constraints", TRUE, lib->ns), ); memcpy(this->reserved, reserved, sizeof(this->reserved)); diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index 88ad14faf..f237f7059 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -17,6 +18,7 @@ #include <daemon.h> #include <crypto/prf_plus.h> +#include <crypto/hashers/hash_algorithm_set.h> typedef struct private_keymat_v2_t private_keymat_v2_t; @@ -69,6 +71,11 @@ struct private_keymat_v2_t { * Key to verify incoming authentication data (SKp) */ chunk_t skp_verify; + + /** + * Set of hash algorithms supported by peer for signature authentication + */ + hash_algorithm_set_t *hash_algorithms; }; METHOD(keymat_t, get_version, ike_version_t, @@ -676,6 +683,26 @@ METHOD(keymat_v2_t, get_psk_sig, bool, return TRUE; } +METHOD(keymat_v2_t, hash_algorithm_supported, bool, + private_keymat_v2_t *this, hash_algorithm_t hash) +{ + if (!this->hash_algorithms) + { + return FALSE; + } + return this->hash_algorithms->contains(this->hash_algorithms, hash); +} + +METHOD(keymat_v2_t, add_hash_algorithm, void, + private_keymat_v2_t *this, hash_algorithm_t hash) +{ + if (!this->hash_algorithms) + { + this->hash_algorithms = hash_algorithm_set_create(); + } + this->hash_algorithms->add(this->hash_algorithms, hash); +} + METHOD(keymat_t, destroy, void, private_keymat_v2_t *this) { @@ -685,6 +712,7 @@ METHOD(keymat_t, destroy, void, chunk_clear(&this->skd); chunk_clear(&this->skp_verify); chunk_clear(&this->skp_build); + DESTROY_IF(this->hash_algorithms); free(this); } @@ -709,6 +737,9 @@ keymat_v2_t *keymat_v2_create(bool initiator) .get_skd = _get_skd, .get_auth_octets = _get_auth_octets, .get_psk_sig = _get_psk_sig, + .add_hash_algorithm = _add_hash_algorithm, + .hash_algorithm_supported = _hash_algorithm_supported, + }, .initiator = initiator, .prf_alg = PRF_UNDEFINED, diff --git a/src/libcharon/sa/ikev2/keymat_v2.h b/src/libcharon/sa/ikev2/keymat_v2.h index 04432f05b..927b62b03 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.h +++ b/src/libcharon/sa/ikev2/keymat_v2.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Tobias Brunner + * Copyright (C) 2011-2015 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -124,6 +124,22 @@ struct keymat_v2_t { bool (*get_psk_sig)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init, chunk_t nonce, chunk_t secret, identification_t *id, char reserved[3], chunk_t *sig); + + /** + * Add a hash algorithm supported by the peer for signature authentication. + * + * @param hash hash algorithm + */ + void (*add_hash_algorithm)(keymat_v2_t *this, hash_algorithm_t hash); + + /** + * Check if a given hash algorithm is supported by the peer for signature + * authentication. + * + * @param hash hash algorithm + * @return TRUE if supported, FALSE otherwise + */ + bool (*hash_algorithm_supported)(keymat_v2_t *this, hash_algorithm_t hash); }; /** diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index b3e92d8ca..ab3d57af6 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -20,8 +20,11 @@ #include <string.h> #include <daemon.h> +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> #include <sa/ikev2/keymat_v2.h> #include <crypto/diffie_hellman.h> +#include <crypto/hashers/hash_algorithm_set.h> #include <encoding/payloads/sa_payload.h> #include <encoding/payloads/ke_payload.h> #include <encoding/payloads/nonce_payload.h> @@ -100,9 +103,111 @@ struct private_ike_init_t { * retries done so far after failure (cookie or bad dh group) */ u_int retry; + + /** + * Whether to use Signature Authentication as per RFC 7427 + */ + bool signature_authentication; }; /** + * Notify the peer about the hash algorithms we support or expect, + * as per RFC 7427 + */ +static void send_supported_hash_algorithms(private_ike_init_t *this, + message_t *message) +{ + hash_algorithm_set_t *algos; + enumerator_t *enumerator, *rounds; + bio_writer_t *writer; + hash_algorithm_t hash; + peer_cfg_t *peer; + auth_cfg_t *auth; + auth_rule_t rule; + uintptr_t config; + char *plugin_name; + + algos = hash_algorithm_set_create(); + peer = this->ike_sa->get_peer_cfg(this->ike_sa); + if (peer) + { + rounds = peer->create_auth_cfg_enumerator(peer, FALSE); + while (rounds->enumerate(rounds, &auth)) + { + enumerator = auth->create_enumerator(auth); + while (enumerator->enumerate(enumerator, &rule, &config)) + { + if (rule == AUTH_RULE_SIGNATURE_SCHEME) + { + hash = hasher_from_signature_scheme(config); + if (hasher_algorithm_for_ikev2(hash)) + { + algos->add(algos, hash); + } + } + } + enumerator->destroy(enumerator); + } + rounds->destroy(rounds); + } + + if (!algos->count(algos)) + { + enumerator = lib->crypto->create_hasher_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &hash, &plugin_name)) + { + if (hasher_algorithm_for_ikev2(hash)) + { + algos->add(algos, hash); + } + } + enumerator->destroy(enumerator); + } + + if (algos->count(algos)) + { + writer = bio_writer_create(0); + enumerator = algos->create_enumerator(algos); + while (enumerator->enumerate(enumerator, &hash)) + { + writer->write_uint16(writer, hash); + } + enumerator->destroy(enumerator); + message->add_notify(message, FALSE, SIGNATURE_HASH_ALGORITHMS, + writer->get_buf(writer)); + writer->destroy(writer); + } + algos->destroy(algos); +} + +/** + * Store algorithms supported by other peer + */ +static void handle_supported_hash_algorithms(private_ike_init_t *this, + notify_payload_t *notify) +{ + bio_reader_t *reader; + u_int16_t algo; + bool added = FALSE; + + reader = bio_reader_create(notify->get_notification_data(notify)); + while (reader->remaining(reader) >= 2 && reader->read_uint16(reader, &algo)) + { + if (hasher_algorithm_for_ikev2(algo)) + { + this->keymat->add_hash_algorithm(this->keymat, algo); + added = TRUE; + } + } + reader->destroy(reader); + + if (added) + { + this->ike_sa->enable_extension(this->ike_sa, EXT_SIGNATURE_AUTH); + } +} + +/** * build the payloads for the message */ static void build_payloads(private_ike_init_t *this, message_t *message) @@ -174,6 +279,16 @@ static void build_payloads(private_ike_init_t *this, message_t *message) chunk_empty); } } + /* submit supported hash algorithms for signature authentication */ + if (!this->old_sa && this->signature_authentication) + { + if (this->initiator || + this->ike_sa->supports_extension(this->ike_sa, + EXT_SIGNATURE_AUTH)) + { + send_supported_hash_algorithms(this, message); + } + } } /** @@ -228,11 +343,23 @@ static void process_payloads(private_ike_init_t *this, message_t *message) { notify_payload_t *notify = (notify_payload_t*)payload; - if (notify->get_notify_type(notify) == FRAGMENTATION_SUPPORTED) + switch (notify->get_notify_type(notify)) { - this->ike_sa->enable_extension(this->ike_sa, - EXT_IKE_FRAGMENTATION); + case FRAGMENTATION_SUPPORTED: + this->ike_sa->enable_extension(this->ike_sa, + EXT_IKE_FRAGMENTATION); + break; + case SIGNATURE_HASH_ALGORITHMS: + if (this->signature_authentication) + { + handle_supported_hash_algorithms(this, notify); + } + break; + default: + /* other notifies are handled elsewhere */ + break; } + } default: break; @@ -637,6 +764,8 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) .dh_group = MODP_NONE, .keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa), .old_sa = old_sa, + .signature_authentication = lib->settings->get_bool(lib->settings, + "%s.signature_authentication", TRUE, lib->ns), ); if (initiator) diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 9b775f9b3..9b6f3f03c 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -8,6 +8,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \ collections/array.c \ collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \ +crypto/hashers/hash_algorithm_set.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \ crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 91d92c3f3..47477de12 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -6,6 +6,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \ collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \ collections/array.c \ collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \ +crypto/hashers/hash_algorithm_set.c \ crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \ crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \ crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \ @@ -61,7 +62,8 @@ library.h \ asn1/asn1.h asn1/asn1_parser.h asn1/oid.h bio/bio_reader.h bio/bio_writer.h \ collections/blocking_queue.h collections/enumerator.h collections/hashtable.h \ collections/linked_list.h collections/array.h collections/dictionary.h \ -crypto/crypters/crypter.h crypto/hashers/hasher.h crypto/mac.h \ +crypto/crypters/crypter.h crypto/hashers/hasher.h \ +crypto/hashers/hash_algorithm_set.h crypto/mac.h \ crypto/proposal/proposal_keywords.h crypto/proposal/proposal_keywords_static.h \ crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/rngs/rng.h crypto/nonce_gen.h \ crypto/prf_plus.h crypto/signers/signer.h crypto/signers/mac_signer.h \ diff --git a/src/libstrongswan/credentials/auth_cfg.c b/src/libstrongswan/credentials/auth_cfg.c index db08c6b96..0ca45a15b 100644 --- a/src/libstrongswan/credentials/auth_cfg.c +++ b/src/libstrongswan/credentials/auth_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2012 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2007-2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -49,6 +49,7 @@ ENUM(auth_rule_names, AUTH_RULE_IDENTITY, AUTH_HELPER_AC_CERT, "RULE_GROUP", "RULE_RSA_STRENGTH", "RULE_ECDSA_STRENGTH", + "RULE_BLISS_STRENGTH", "RULE_SIGNATURE_SCHEME", "RULE_CERT_POLICY", "HELPER_IM_CERT", @@ -71,6 +72,7 @@ static inline bool is_multi_value_rule(auth_rule_t type) case AUTH_RULE_EAP_VENDOR: case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_IDENTITY: case AUTH_RULE_IDENTITY_LOOSE: case AUTH_RULE_EAP_IDENTITY: @@ -207,6 +209,7 @@ static void init_entry(entry_t *this, auth_rule_t type, va_list args) case AUTH_RULE_OCSP_VALIDATION: case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: /* integer type */ this->value = (void*)(uintptr_t)va_arg(args, u_int); @@ -255,6 +258,7 @@ static bool entry_equals(entry_t *e1, entry_t *e2) case AUTH_RULE_OCSP_VALIDATION: case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: { return e1->value == e2->value; @@ -345,6 +349,7 @@ static void destroy_entry_value(entry_t *entry) case AUTH_RULE_OCSP_VALIDATION: case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: case AUTH_RULE_MAX: break; @@ -376,6 +381,7 @@ static void replace(private_auth_cfg_t *this, entry_enumerator_t *enumerator, case AUTH_RULE_OCSP_VALIDATION: case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: /* integer type */ entry->value = (void*)(uintptr_t)va_arg(args, u_int); @@ -450,6 +456,7 @@ METHOD(auth_cfg_t, get, void*, case AUTH_RULE_EAP_VENDOR: case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: return (void*)0; case AUTH_RULE_SIGNATURE_SCHEME: return (void*)HASH_UNKNOWN; @@ -513,6 +520,7 @@ METHOD(auth_cfg_t, complies, bool, signature_scheme_t scheme = SIGN_UNKNOWN; u_int strength = 0; auth_rule_t t1, t2; + char *key_type; void *value; e1 = constraints->create_enumerator(constraints); @@ -703,6 +711,7 @@ METHOD(auth_cfg_t, complies, bool, } case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: { strength = (uintptr_t)value; break; @@ -797,30 +806,39 @@ METHOD(auth_cfg_t, complies, bool, e2 = create_enumerator(this); while (e2->enumerate(e2, &t2, &strength)) { - if (t2 == AUTH_RULE_RSA_STRENGTH || - t2 == AUTH_RULE_ECDSA_STRENGTH) + switch (t2) { - success = FALSE; - e1 = constraints->create_enumerator(constraints); - while (e1->enumerate(e1, &t1, &value)) + default: + continue; + case AUTH_RULE_RSA_STRENGTH: + key_type = "RSA"; + break; + case AUTH_RULE_ECDSA_STRENGTH: + key_type = "ECDSA"; + break; + case AUTH_RULE_BLISS_STRENGTH: + key_type = "BLISS"; + break; + } + success = FALSE; + e1 = constraints->create_enumerator(constraints); + while (e1->enumerate(e1, &t1, &value)) + { + if (t1 == t2 && (uintptr_t)value <= strength) { - if (t1 == t2 && (uintptr_t)value <= strength) - { - success = TRUE; - break; - } + success = TRUE; + break; } - e1->destroy(e1); - if (!success) + } + e1->destroy(e1); + if (!success) + { + if (log_error) { - if (log_error) - { - DBG1(DBG_CFG, "%s-%d signatures not acceptable", - t2 == AUTH_RULE_RSA_STRENGTH ? "RSA" : "ECDSA", - strength); - } - break; + DBG1(DBG_CFG, "%s-%d signatures not acceptable", + key_type, strength); } + break; } } e2->destroy(e2); @@ -891,6 +909,7 @@ static void merge(private_auth_cfg_t *this, private_auth_cfg_t *other, bool copy case AUTH_RULE_EAP_VENDOR: case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: { add(this, type, (uintptr_t)value); @@ -1060,6 +1079,7 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_RULE_OCSP_VALIDATION: case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: clone->add(clone, type, (uintptr_t)value); break; diff --git a/src/libstrongswan/credentials/auth_cfg.h b/src/libstrongswan/credentials/auth_cfg.h index 95b36d706..53f1b3805 100644 --- a/src/libstrongswan/credentials/auth_cfg.h +++ b/src/libstrongswan/credentials/auth_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2012 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2007-2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -102,6 +102,8 @@ enum auth_rule_t { AUTH_RULE_RSA_STRENGTH, /** required ECDSA public key strength, u_int in bits */ AUTH_RULE_ECDSA_STRENGTH, + /** required BLISS public key strength, u_int in bits */ + AUTH_RULE_BLISS_STRENGTH, /** required signature scheme, signature_scheme_t */ AUTH_RULE_SIGNATURE_SCHEME, /** certificatePolicy constraint, numerical OID as char* */ diff --git a/src/libstrongswan/credentials/credential_manager.c b/src/libstrongswan/credentials/credential_manager.c index b0c8e48ba..371e6404d 100644 --- a/src/libstrongswan/credentials/credential_manager.c +++ b/src/libstrongswan/credentials/credential_manager.c @@ -698,6 +698,9 @@ static void get_key_strength(certificate_t *cert, auth_cfg_t *auth) case KEY_ECDSA: auth->add(auth, AUTH_RULE_ECDSA_STRENGTH, strength); break; + case KEY_BLISS: + auth->add(auth, AUTH_RULE_BLISS_STRENGTH, strength); + break; default: break; } diff --git a/src/libstrongswan/credentials/keys/public_key.c b/src/libstrongswan/credentials/keys/public_key.c index c342ac128..bd5915e60 100644 --- a/src/libstrongswan/credentials/keys/public_key.c +++ b/src/libstrongswan/credentials/keys/public_key.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2007 Martin Willi * Copyright (C) 2014 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil @@ -142,8 +143,151 @@ signature_scheme_t signature_scheme_from_oid(int oid) return SIGN_BLISS_WITH_SHA256; case OID_BLISS_WITH_SHA384: return SIGN_BLISS_WITH_SHA384; - default: - return SIGN_UNKNOWN; } + return SIGN_UNKNOWN; } +/* + * Defined in header. + */ +int signature_scheme_to_oid(signature_scheme_t scheme) +{ + switch (scheme) + { + case SIGN_UNKNOWN: + case SIGN_RSA_EMSA_PKCS1_NULL: + case SIGN_ECDSA_WITH_NULL: + case SIGN_ECDSA_256: + case SIGN_ECDSA_384: + case SIGN_ECDSA_521: + break; + case SIGN_RSA_EMSA_PKCS1_MD5: + return OID_MD5_WITH_RSA; + case SIGN_RSA_EMSA_PKCS1_SHA1: + return OID_SHA1_WITH_RSA; + case SIGN_RSA_EMSA_PKCS1_SHA224: + return OID_SHA224_WITH_RSA; + case SIGN_RSA_EMSA_PKCS1_SHA256: + return OID_SHA256_WITH_RSA; + case SIGN_RSA_EMSA_PKCS1_SHA384: + return OID_SHA384_WITH_RSA; + case SIGN_RSA_EMSA_PKCS1_SHA512: + return OID_SHA512_WITH_RSA; + case SIGN_ECDSA_WITH_SHA1_DER: + return OID_ECDSA_WITH_SHA1; + case SIGN_ECDSA_WITH_SHA256_DER: + return OID_ECDSA_WITH_SHA256; + case SIGN_ECDSA_WITH_SHA384_DER: + return OID_ECDSA_WITH_SHA384; + case SIGN_ECDSA_WITH_SHA512_DER: + return OID_ECDSA_WITH_SHA512; + case SIGN_BLISS_WITH_SHA256: + return OID_BLISS_WITH_SHA256; + case SIGN_BLISS_WITH_SHA384: + return OID_BLISS_WITH_SHA384; + case SIGN_BLISS_WITH_SHA512: + return OID_BLISS_WITH_SHA512; + } + return OID_UNKNOWN; +} + +/** + * Map for signature schemes to the key type and maximum key size allowed. + * We only cover schemes with hash algorithms supported by IKEv2 signature + * authentication. + */ +static struct { + signature_scheme_t scheme; + key_type_t type; + int max_keysize; +} scheme_map[] = { + { SIGN_RSA_EMSA_PKCS1_SHA256, KEY_RSA, 3072 }, + { SIGN_RSA_EMSA_PKCS1_SHA384, KEY_RSA, 7680 }, + { SIGN_RSA_EMSA_PKCS1_SHA512, KEY_RSA, 0 }, + { SIGN_ECDSA_WITH_SHA256_DER, KEY_ECDSA, 256 }, + { SIGN_ECDSA_WITH_SHA384_DER, KEY_ECDSA, 384 }, + { SIGN_ECDSA_WITH_SHA512_DER, KEY_ECDSA, 0 }, + { SIGN_BLISS_WITH_SHA256, KEY_BLISS, 128 }, + { SIGN_BLISS_WITH_SHA384, KEY_BLISS, 192 }, + { SIGN_BLISS_WITH_SHA512, KEY_BLISS, 0 }, +}; + +/** + * Private data for signature scheme enumerator + */ +typedef struct { + enumerator_t public; + int index; + key_type_t type; + int size; +} private_enumerator_t; + +METHOD(enumerator_t, signature_schemes_enumerate, bool, + private_enumerator_t *this, signature_scheme_t *scheme) +{ + while (++this->index < countof(scheme_map)) + { + if (this->type == scheme_map[this->index].type && + (this->size <= scheme_map[this->index].max_keysize || + !scheme_map[this->index].max_keysize)) + { + *scheme = scheme_map[this->index].scheme; + return TRUE; + } + } + return FALSE; +} + +/* + * Defined in header. + */ +enumerator_t *signature_schemes_for_key(key_type_t type, int size) +{ + private_enumerator_t *this; + + INIT(this, + .public = { + .enumerate = (void*)_signature_schemes_enumerate, + .destroy = (void*)free, + }, + .index = -1, + .type = type, + .size = size, + ); + + return &this->public; +} + +/* + * Defined in header. + */ +key_type_t key_type_from_signature_scheme(signature_scheme_t scheme) +{ + switch (scheme) + { + case SIGN_UNKNOWN: + break; + case SIGN_RSA_EMSA_PKCS1_NULL: + case SIGN_RSA_EMSA_PKCS1_MD5: + case SIGN_RSA_EMSA_PKCS1_SHA1: + case SIGN_RSA_EMSA_PKCS1_SHA224: + case SIGN_RSA_EMSA_PKCS1_SHA256: + case SIGN_RSA_EMSA_PKCS1_SHA384: + case SIGN_RSA_EMSA_PKCS1_SHA512: + return KEY_RSA; + case SIGN_ECDSA_WITH_SHA1_DER: + case SIGN_ECDSA_WITH_SHA256_DER: + case SIGN_ECDSA_WITH_SHA384_DER: + case SIGN_ECDSA_WITH_SHA512_DER: + case SIGN_ECDSA_WITH_NULL: + case SIGN_ECDSA_256: + case SIGN_ECDSA_384: + case SIGN_ECDSA_521: + return KEY_ECDSA; + case SIGN_BLISS_WITH_SHA256: + case SIGN_BLISS_WITH_SHA384: + case SIGN_BLISS_WITH_SHA512: + return KEY_BLISS; + } + return KEY_ANY; +} diff --git a/src/libstrongswan/credentials/keys/public_key.h b/src/libstrongswan/credentials/keys/public_key.h index 9cdcc8e6c..66e98b294 100644 --- a/src/libstrongswan/credentials/keys/public_key.h +++ b/src/libstrongswan/credentials/keys/public_key.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2007 Martin Willi * Copyright (C) 2014 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil @@ -243,8 +244,35 @@ bool public_key_has_fingerprint(public_key_t *public, chunk_t fingerprint); * Conversion of ASN.1 signature or hash OID to signature scheme. * * @param oid ASN.1 OID - * @return signature_scheme, SIGN_UNKNOWN if OID is unsupported + * @return signature scheme, SIGN_UNKNOWN if OID is unsupported */ signature_scheme_t signature_scheme_from_oid(int oid); +/** + * Conversion of signature scheme to ASN.1 signature OID. + * + * @param scheme signature scheme + * @return ASN.1 OID, OID_UNKNOWN if not supported + */ +int signature_scheme_to_oid(signature_scheme_t scheme); + +/** + * Enumerate signature schemes that are appropriate for a key of the given type + * and size|strength. + * + * @param type type of the key + * @param size size or strength of the key + * @return enumerator over signature_scheme_t (increasing strength) + */ +enumerator_t *signature_schemes_for_key(key_type_t type, int size); + +/** + * Determine the type of key associated with a given signature scheme. + * + * @param scheme signature scheme + * @return key type (could be KEY_ANY) + */ +key_type_t key_type_from_signature_scheme(signature_scheme_t scheme); + + #endif /** PUBLIC_KEY_H_ @}*/ diff --git a/src/libstrongswan/crypto/hashers/hash_algorithm_set.c b/src/libstrongswan/crypto/hashers/hash_algorithm_set.c new file mode 100644 index 000000000..93b67cb13 --- /dev/null +++ b/src/libstrongswan/crypto/hashers/hash_algorithm_set.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * 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 "hash_algorithm_set.h" + +#include <collections/array.h> + +typedef struct private_hash_algorithm_set_t private_hash_algorithm_set_t; + +struct private_hash_algorithm_set_t { + + /** + * Public interface + */ + hash_algorithm_set_t public; + + /** + * Algorithms contained in the set + */ + array_t *algorithms; +}; + +/** + * Sort hash algorithms + */ +static int hash_sort(const void *a, const void *b, void *user) +{ + const hash_algorithm_t *ha = a, *hb = b; + return *ha - *hb; +} + +/** + * Find a hash algorithm + */ +static int hash_find(const void *a, const void *b) +{ + return hash_sort(a, b, NULL); +} + +METHOD(hash_algorithm_set_t, contains, bool, + private_hash_algorithm_set_t *this, hash_algorithm_t hash) +{ + return array_bsearch(this->algorithms, &hash, hash_find, NULL) != -1; +} + +METHOD(hash_algorithm_set_t, add, void, + private_hash_algorithm_set_t *this, hash_algorithm_t hash) +{ + if (!contains(this, hash)) + { + array_insert(this->algorithms, ARRAY_TAIL, &hash); + array_sort(this->algorithms, hash_sort, NULL); + } +} + +METHOD(hash_algorithm_set_t, count, int, + private_hash_algorithm_set_t *this) +{ + return array_count(this->algorithms); +} + +static bool hash_filter(void *data, void **in, hash_algorithm_t *out) +{ + *out = **(hash_algorithm_t**)in; + return TRUE; +} + +METHOD(hash_algorithm_set_t, create_enumerator, enumerator_t*, + private_hash_algorithm_set_t *this) +{ + return enumerator_create_filter(array_create_enumerator(this->algorithms), + (void*)hash_filter, NULL, NULL); +} + +METHOD(hash_algorithm_set_t, destroy, void, + private_hash_algorithm_set_t *this) +{ + array_destroy(this->algorithms); + free(this); +} + +/** + * Described in header + */ +hash_algorithm_set_t *hash_algorithm_set_create() +{ + private_hash_algorithm_set_t *this; + + INIT(this, + .public = { + .add = _add, + .contains = _contains, + .count = _count, + .create_enumerator = _create_enumerator, + .destroy = _destroy, + }, + .algorithms = array_create(sizeof(hash_algorithm_t), 0), + ); + + return &this->public; +}
\ No newline at end of file diff --git a/src/libstrongswan/crypto/hashers/hash_algorithm_set.h b/src/libstrongswan/crypto/hashers/hash_algorithm_set.h new file mode 100644 index 000000000..00e90cc2e --- /dev/null +++ b/src/libstrongswan/crypto/hashers/hash_algorithm_set.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * 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. + */ + +/** + * @defgroup hash_algorithm_set hash_algorithm_set + * @{ @ingroup crypto + */ + +#ifndef HASH_ALGORITHM_SET_H_ +#define HASH_ALGORITHM_SET_H_ + +typedef struct hash_algorithm_set_t hash_algorithm_set_t; + +#include <library.h> +#include <crypto/hashers/hasher.h> + +/** + * A set of hash algorithms + */ +struct hash_algorithm_set_t { + + /** + * Add the given algorithm to the set. + * + * @param alg hash algorithm + */ + void (*add)(hash_algorithm_set_t *this, hash_algorithm_t alg); + + /** + * Check if the given algorithm is contained in the set. + * + * @param alg hash algorithm + * @return TRUE if contained in set + */ + bool (*contains)(hash_algorithm_set_t *this, hash_algorithm_t alg); + + /** + * Number of hash algorithms contained in the set. + * + * @return number of algorithms + */ + int (*count)(hash_algorithm_set_t *this); + + /** + * Enumerate the algorithms contained in the set. + * + * @return enumerator over hash_algorithm_t (sorted by identifier) + */ + enumerator_t *(*create_enumerator)(hash_algorithm_set_t *this); + + /** + * Destroy a hash_algorithm_set_t instance + */ + void (*destroy)(hash_algorithm_set_t *this); +}; + +/** + * Create a set of hash algorithms. + * + * @return hash_algorithm_set_t instance + */ +hash_algorithm_set_t *hash_algorithm_set_create(); + +#endif /** HASH_ALGORITHM_SET_H_ @}*/ diff --git a/src/libstrongswan/crypto/hashers/hasher.c b/src/libstrongswan/crypto/hashers/hasher.c index a56930325..38eebea9c 100644 --- a/src/libstrongswan/crypto/hashers/hasher.c +++ b/src/libstrongswan/crypto/hashers/hasher.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -19,29 +19,31 @@ #include <asn1/oid.h> -ENUM(hash_algorithm_names, HASH_UNKNOWN, HASH_SHA512, +ENUM_BEGIN(hash_algorithm_names, HASH_SHA1, HASH_SHA512, + "HASH_SHA1", + "HASH_SHA256", + "HASH_SHA384", + "HASH_SHA512"); +ENUM_NEXT(hash_algorithm_names, HASH_UNKNOWN, HASH_SHA224, HASH_SHA512, "HASH_UNKNOWN", "HASH_MD2", "HASH_MD4", "HASH_MD5", - "HASH_SHA1", - "HASH_SHA224", - "HASH_SHA256", - "HASH_SHA384", - "HASH_SHA512" -); + "HASH_SHA224"); +ENUM_END(hash_algorithm_names, HASH_SHA224); -ENUM(hash_algorithm_short_names, HASH_UNKNOWN, HASH_SHA512, +ENUM_BEGIN(hash_algorithm_short_names, HASH_SHA1, HASH_SHA512, + "sha1", + "sha256", + "sha384", + "sha512"); +ENUM_NEXT(hash_algorithm_short_names, HASH_UNKNOWN, HASH_SHA224, HASH_SHA512, "unknown", "md2", "md4", "md5", - "sha1", - "sha224", - "sha256", - "sha384", - "sha512" -); + "sha224"); +ENUM_END(hash_algorithm_short_names, HASH_SHA224); /* * Described in header. @@ -249,6 +251,28 @@ integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, /* * Described in header. */ +bool hasher_algorithm_for_ikev2(hash_algorithm_t alg) +{ + switch (alg) + { + case HASH_SHA1: + case HASH_SHA256: + case HASH_SHA384: + case HASH_SHA512: + return TRUE; + case HASH_UNKNOWN: + case HASH_MD2: + case HASH_MD4: + case HASH_MD5: + case HASH_SHA224: + break; + } + return FALSE; +} + +/* + * Described in header. + */ int hasher_algorithm_to_oid(hash_algorithm_t alg) { int oid; @@ -340,3 +364,39 @@ int hasher_signature_algorithm_to_oid(hash_algorithm_t alg, key_type_t key) } } +/* + * Defined in header. + */ +hash_algorithm_t hasher_from_signature_scheme(signature_scheme_t scheme) +{ + switch (scheme) + { + case SIGN_UNKNOWN: + case SIGN_RSA_EMSA_PKCS1_NULL: + case SIGN_ECDSA_WITH_NULL: + break; + case SIGN_RSA_EMSA_PKCS1_MD5: + return HASH_MD5; + case SIGN_RSA_EMSA_PKCS1_SHA1: + case SIGN_ECDSA_WITH_SHA1_DER: + return HASH_SHA1; + case SIGN_RSA_EMSA_PKCS1_SHA224: + return HASH_SHA224; + case SIGN_RSA_EMSA_PKCS1_SHA256: + case SIGN_ECDSA_WITH_SHA256_DER: + case SIGN_ECDSA_256: + case SIGN_BLISS_WITH_SHA256: + return HASH_SHA256; + case SIGN_RSA_EMSA_PKCS1_SHA384: + case SIGN_ECDSA_WITH_SHA384_DER: + case SIGN_ECDSA_384: + case SIGN_BLISS_WITH_SHA384: + return HASH_SHA384; + case SIGN_RSA_EMSA_PKCS1_SHA512: + case SIGN_ECDSA_WITH_SHA512_DER: + case SIGN_ECDSA_521: + case SIGN_BLISS_WITH_SHA512: + return HASH_SHA512; + } + return HASH_UNKNOWN; +} diff --git a/src/libstrongswan/crypto/hashers/hasher.h b/src/libstrongswan/crypto/hashers/hasher.h index 37ef0b6ab..772586308 100644 --- a/src/libstrongswan/crypto/hashers/hasher.h +++ b/src/libstrongswan/crypto/hashers/hasher.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -32,19 +32,19 @@ typedef struct hasher_t hasher_t; #include <credentials/keys/public_key.h> /** - * Algorithms to use for hashing. + * Hash algorithms as defined for IKEv2 by RFC 7427 */ enum hash_algorithm_t { - /** not specified hash function */ - HASH_UNKNOWN = 0, - HASH_MD2 = 1, - HASH_MD4 = 2, - HASH_MD5 = 3, - HASH_SHA1 = 4, - HASH_SHA224 = 5, - HASH_SHA256 = 6, - HASH_SHA384 = 7, - HASH_SHA512 = 8 + HASH_SHA1 = 1, + HASH_SHA256 = 2, + HASH_SHA384 = 3, + HASH_SHA512 = 4, + /* use private use range for algorithms not defined/permitted by RFC 7427 */ + HASH_UNKNOWN = 1024, + HASH_MD2 = 1025, + HASH_MD4 = 1026, + HASH_MD5 = 1027, + HASH_SHA224 = 1028, }; #define HASH_SIZE_MD2 16 @@ -163,6 +163,14 @@ integrity_algorithm_t hasher_algorithm_to_integrity(hash_algorithm_t alg, size_t length); /** + * Check if the given algorithm may be used for IKEv2 signature authentication. + * + * @param alg hash algorithm + * @return TRUE if algorithm may be used, FALSE otherwise + */ +bool hasher_algorithm_for_ikev2(hash_algorithm_t alg); + +/** * Conversion of hash algorithm into ASN.1 OID. * * @param alg hash algorithm @@ -179,4 +187,12 @@ int hasher_algorithm_to_oid(hash_algorithm_t alg); */ int hasher_signature_algorithm_to_oid(hash_algorithm_t alg, key_type_t key); +/** + * Determine the hash algorithm associated with a given signature scheme. + * + * @param scheme signature scheme + * @return hash algorithm (could be HASH_UNKNOWN) + */ +hash_algorithm_t hasher_from_signature_scheme(signature_scheme_t scheme); + #endif /** HASHER_H_ @}*/ diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index abca4620e..85a854456 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Tobias Brunner + * Copyright (C) 2013-2015 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,6 +18,7 @@ #include <library.h> #include <utils/utils.h> #include <ipsec/ipsec_types.h> +#include <credentials/keys/public_key.h> #include <time.h> @@ -695,6 +696,44 @@ START_TEST(test_mark_from_string) } END_TEST +/******************************************************************************* + * signature_schemes_for_key + */ + +static struct { + key_type_t type; + int size; + signature_scheme_t expected[4]; +} scheme_data[] = { + {KEY_RSA, 1024, { SIGN_RSA_EMSA_PKCS1_SHA256, SIGN_RSA_EMSA_PKCS1_SHA384, SIGN_RSA_EMSA_PKCS1_SHA512, SIGN_UNKNOWN }}, + {KEY_RSA, 2048, { SIGN_RSA_EMSA_PKCS1_SHA256, SIGN_RSA_EMSA_PKCS1_SHA384, SIGN_RSA_EMSA_PKCS1_SHA512, SIGN_UNKNOWN }}, + {KEY_RSA, 4096, { SIGN_RSA_EMSA_PKCS1_SHA384, SIGN_RSA_EMSA_PKCS1_SHA512, SIGN_UNKNOWN }}, + {KEY_RSA, 8192, { SIGN_RSA_EMSA_PKCS1_SHA512, SIGN_UNKNOWN }}, + {KEY_ECDSA, 256, { SIGN_ECDSA_WITH_SHA256_DER, SIGN_ECDSA_WITH_SHA384_DER, SIGN_ECDSA_WITH_SHA512_DER, SIGN_UNKNOWN }}, + {KEY_ECDSA, 384, { SIGN_ECDSA_WITH_SHA384_DER, SIGN_ECDSA_WITH_SHA512_DER, SIGN_UNKNOWN }}, + {KEY_ECDSA, 512, { SIGN_ECDSA_WITH_SHA512_DER, SIGN_UNKNOWN }}, + {KEY_BLISS, 128, { SIGN_BLISS_WITH_SHA256, SIGN_BLISS_WITH_SHA384, SIGN_BLISS_WITH_SHA512, SIGN_UNKNOWN }}, + {KEY_BLISS, 192, { SIGN_BLISS_WITH_SHA384, SIGN_BLISS_WITH_SHA512, SIGN_UNKNOWN }}, + {KEY_BLISS, 256, { SIGN_BLISS_WITH_SHA512, SIGN_UNKNOWN }}, +}; + +START_TEST(test_signature_schemes_for_key) +{ + enumerator_t *enumerator; + signature_scheme_t scheme; + int i; + + enumerator = signature_schemes_for_key(scheme_data[_i].type, scheme_data[_i].size); + for (i = 0; scheme_data[_i].expected[i] != SIGN_UNKNOWN; i++) + { + ck_assert(enumerator->enumerate(enumerator, &scheme)); + ck_assert_int_eq(scheme_data[_i].expected[i], scheme); + } + ck_assert(!enumerator->enumerate(enumerator, &scheme)); + enumerator->destroy(enumerator); +} +END_TEST + Suite *utils_suite_create() { Suite *s; @@ -777,5 +816,9 @@ Suite *utils_suite_create() tcase_add_loop_test(tc, test_mark_from_string, 0, countof(mark_data)); suite_add_tcase(s, tc); + tc = tcase_create("signature_schemes_for_key"); + tcase_add_loop_test(tc, test_signature_schemes_for_key, 0, countof(scheme_data)); + suite_add_tcase(s, tc); + return s; } |