From: Tobias Brunner Date: Mon, 23 Feb 2015 16:54:14 +0000 (+0100) Subject: ikev2: Handle RFC 7427 signature authentication in pubkey authenticator X-Git-Tag: 5.3.0dr1~39^2~24 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=295e37ab6654d383e9cda5dc8f2226f640a558c0;p=thirdparty%2Fstrongswan.git ikev2: Handle RFC 7427 signature authentication in pubkey authenticator --- diff --git a/src/libcharon/sa/authenticator.c b/src/libcharon/sa/authenticator.c index c6cf04869a..5ceae0d641 100644 --- a/src/libcharon/sa/authenticator.c +++ b/src/libcharon/sa/authenticator.c @@ -103,6 +103,7 @@ authenticator_t *authenticator_create_verifier( case AUTH_ECDSA_256: case AUTH_ECDSA_384: case AUTH_ECDSA_521: + case AUTH_DS: case AUTH_BLISS: return (authenticator_t*)pubkey_authenticator_create_verifier(ike_sa, sent_nonce, received_init, reserved); diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 2188fb2e8b..bd38196239 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 #include #include +#include +#include typedef struct private_pubkey_authenticator_t private_pubkey_authenticator_t; @@ -54,6 +56,112 @@ struct private_pubkey_authenticator_t { char reserved[3]; }; +/** + * 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 scheme = SIGN_UNKNOWN; + hash_algorithm_t hash; + char *plugin_name; + uintptr_t config; + auth_rule_t rule; + key_type_t key_type; + bool have_config = FALSE; + int oid; + + 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))) + { + scheme = config; + break; + } + } + enumerator->destroy(enumerator); + + if (scheme == SIGN_UNKNOWN && !have_config) + { + /* if no specific configuration, find a scheme supported by us, the + * other peer and the key */ + enumerator = lib->crypto->create_hasher_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &hash, &plugin_name)) + { + if (keymat->hash_algorithm_supported(keymat, hash)) + { + oid = hasher_signature_algorithm_to_oid(hash, key_type); + if (oid != OID_UNKNOWN) + { + scheme = signature_scheme_from_oid(oid); + break; + } + } + } + enumerator->destroy(enumerator); + } + return scheme; +} + METHOD(authenticator_t, build, status_t, private_pubkey_authenticator_t *this, message_t *message) { @@ -64,7 +172,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,58 +183,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; + 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)); + 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, @@ -158,11 +283,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; @@ -179,10 +303,15 @@ METHOD(authenticator_t, process, status_t, 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,