From 0f423dda289fd8f3fe64d4a9cea2b68f57dedd99 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 25 Jul 2018 15:29:58 +0200 Subject: [PATCH 01/20] ikev2: Add notify types for Postquantum Preshared Keys --- src/libcharon/encoding/payloads/notify_payload.c | 14 +++++++++++--- src/libcharon/encoding/payloads/notify_payload.h | 8 +++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/libcharon/encoding/payloads/notify_payload.c b/src/libcharon/encoding/payloads/notify_payload.c index 0c6f010b5..a69db9357 100644 --- a/src/libcharon/encoding/payloads/notify_payload.c +++ b/src/libcharon/encoding/payloads/notify_payload.c @@ -1,7 +1,7 @@ /* + * Copyright (C) 2006-2018 Tobias Brunner * Copyright (C) 2005-2010 Martin Willi * Copyright (C) 2010 revosec AG - * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -114,7 +114,11 @@ ENUM_NEXT(notify_type_names, INITIAL_CONTACT, SIGNATURE_HASH_ALGORITHMS, MS_NOTI "SENDER_REQUEST_ID", "FRAGMENTATION_SUPPORTED", "SIGNATURE_HASH_ALGORITHMS"); -ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, SIGNATURE_HASH_ALGORITHMS, +ENUM_NEXT(notify_type_names, USE_PPK, NO_PPK_AUTH, SIGNATURE_HASH_ALGORITHMS, + "USE_PPK", + "PPK_IDENTITY", + "NO_PPK_AUTH"); +ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, NO_PPK_AUTH, "INITIAL_CONTACT"); ENUM_NEXT(notify_type_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1, "DPD_R_U_THERE", @@ -224,7 +228,11 @@ ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT, SIGNATURE_HASH_ALGORITHMS, M "SENDER_REQ_ID", "FRAG_SUP", "HASH_ALG"); -ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, SIGNATURE_HASH_ALGORITHMS, +ENUM_NEXT(notify_type_short_names, USE_PPK, NO_PPK_AUTH, SIGNATURE_HASH_ALGORITHMS, + "USE_PPK", + "PPK_ID", + "NO_PPK"); +ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, NO_PPK_AUTH, "INITIAL_CONTACT"); ENUM_NEXT(notify_type_short_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1, "DPD", diff --git a/src/libcharon/encoding/payloads/notify_payload.h b/src/libcharon/encoding/payloads/notify_payload.h index 39e4c915b..b0cf69d02 100644 --- a/src/libcharon/encoding/payloads/notify_payload.h +++ b/src/libcharon/encoding/payloads/notify_payload.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Tobias Brunner + * Copyright (C) 2006-2018 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -153,6 +153,12 @@ enum notify_type_t { FRAGMENTATION_SUPPORTED = 16430, /* Signature Hash Algorithms, RFC 7427 */ SIGNATURE_HASH_ALGORITHMS = 16431, + /* Use Postquantum Preshared Key (draft-ietf-ipsecme-qr-ikev2) */ + USE_PPK = 16435, + /* Postquantum Preshared Key Identity (draft-ietf-ipsecme-qr-ikev2) */ + PPK_IDENTITY = 16436, + /* No Postquantum Preshared Key Auth (draft-ietf-ipsecme-qr-ikev2) */ + NO_PPK_AUTH = 16437, /* IKEv1 initial contact */ INITIAL_CONTACT_IKEV1 = 24578, /* IKEv1 DPD */ From bac3ca232457d41c56f79a463a000617138fbfe3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 25 Jul 2018 15:30:05 +0200 Subject: [PATCH 02/20] shared-key: Add a new type for Postquantum Preshared Keys Using a separate type allows us to easily check if we have any PPKs available at all. --- src/libstrongswan/credentials/keys/shared_key.c | 6 ++++-- src/libstrongswan/credentials/keys/shared_key.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/credentials/keys/shared_key.c b/src/libstrongswan/credentials/keys/shared_key.c index 2294eaff7..97209953a 100644 --- a/src/libstrongswan/credentials/keys/shared_key.c +++ b/src/libstrongswan/credentials/keys/shared_key.c @@ -15,12 +15,14 @@ #include "shared_key.h" -ENUM(shared_key_type_names, SHARED_ANY, SHARED_PIN, +ENUM(shared_key_type_names, SHARED_ANY, SHARED_PPK, "ANY", "IKE", "EAP", "PRIVATE_KEY_PASS", "PIN", + "NTLM", + "PPK", ); typedef struct private_shared_key_t private_shared_key_t; @@ -93,7 +95,7 @@ shared_key_t *shared_key_create(shared_key_type_t type, chunk_t key) .get_key = _get_key, .get_ref = _get_ref, .destroy = _destroy, - }, + }, .type = type, .key = key, .ref = 1, diff --git a/src/libstrongswan/credentials/keys/shared_key.h b/src/libstrongswan/credentials/keys/shared_key.h index d97139de2..44e6f0460 100644 --- a/src/libstrongswan/credentials/keys/shared_key.h +++ b/src/libstrongswan/credentials/keys/shared_key.h @@ -43,6 +43,8 @@ enum shared_key_type_t { SHARED_PIN, /** Calculated NT Hash = MD4(UTF-16LE(password)) */ SHARED_NT_HASH, + /** Postquantum Preshared Key */ + SHARED_PPK, }; /** From 1ec938288039f2c5fac984bcd99ac479eea22304 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 25 Jul 2018 17:23:12 +0200 Subject: [PATCH 03/20] vici: Add support for PPKs --- src/libcharon/plugins/vici/vici_cred.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c index ec6c80a5b..038338805 100644 --- a/src/libcharon/plugins/vici/vici_cred.c +++ b/src/libcharon/plugins/vici/vici_cred.c @@ -442,6 +442,10 @@ CALLBACK(load_shared, vici_message_t*, { type = SHARED_NT_HASH; } + else if (strcaseeq(str, "ppk")) + { + type = SHARED_PPK; + } else { return create_reply("invalid shared key type: %s", str); From 3703dff2aaa47660567d0ca1c29199ed32b07194 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 17:44:12 +0200 Subject: [PATCH 04/20] swanctl: Add support for PPKs --- src/swanctl/commands/load_creds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index 15ef2f151..63a3bdcf8 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -665,6 +665,7 @@ static bool load_secret(load_ctx_t *ctx, char *section) "xauth", "ntlm", "ike", + "ppk", "private", "rsa", "ecdsa", @@ -688,7 +689,7 @@ static bool load_secret(load_ctx_t *ctx, char *section) return FALSE; } if (!streq(type, "eap") && !streq(type, "xauth") && !streq(type, "ntlm") && - !streq(type, "ike")) + !streq(type, "ike") && !streq(type, "ppk")) { /* skip non-shared secrets */ return TRUE; } From 3fbc95cf5454cd403c7b6c2b9147fb83eb6c5b6a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 25 Jul 2018 16:43:01 +0200 Subject: [PATCH 05/20] keymat_v2: Add support for PPKs --- src/charon-tkm/src/tkm/tkm_keymat.c | 8 +- src/conftest/hooks/pretend_auth.c | 4 +- src/conftest/hooks/rebuild_auth.c | 4 +- .../ikev2/authenticators/eap_authenticator.c | 8 +- .../ikev2/authenticators/psk_authenticator.c | 6 +- .../authenticators/pubkey_authenticator.c | 7 +- src/libcharon/sa/ikev2/keymat_v2.c | 136 ++++++++++++++++-- src/libcharon/sa/ikev2/keymat_v2.h | 20 ++- 8 files changed, 161 insertions(+), 32 deletions(-) diff --git a/src/charon-tkm/src/tkm/tkm_keymat.c b/src/charon-tkm/src/tkm/tkm_keymat.c index 71ad821dd..1107c2219 100644 --- a/src/charon-tkm/src/tkm/tkm_keymat.c +++ b/src/charon-tkm/src/tkm/tkm_keymat.c @@ -385,8 +385,8 @@ METHOD(keymat_t, get_aead, aead_t*, METHOD(keymat_v2_t, get_auth_octets, bool, private_tkm_keymat_t *this, bool verify, chunk_t ike_sa_init, - chunk_t nonce, identification_t *id, char reserved[3], chunk_t *octets, - array_t *schemes) + chunk_t nonce, chunk_t ppk, identification_t *id, char reserved[3], + chunk_t *octets, array_t *schemes) { sign_info_t *sign; @@ -428,7 +428,8 @@ METHOD(keymat_v2_t, get_skd, pseudo_random_function_t, METHOD(keymat_v2_t, get_psk_sig, bool, private_tkm_keymat_t *this, bool verify, chunk_t ike_sa_init, chunk_t nonce, - chunk_t secret, identification_t *id, char reserved[3], chunk_t *sig) + chunk_t secret, chunk_t ppk, identification_t *id, char reserved[3], + chunk_t *sig) { return FALSE; } @@ -522,6 +523,7 @@ tkm_keymat_t *tkm_keymat_create(bool initiator) .destroy = _destroy, }, .derive_ike_keys = _derive_ike_keys, + .derive_ike_keys_ppk = (void*)return_false, .derive_child_keys = _derive_child_keys, .get_skd = _get_skd, .get_auth_octets = _get_auth_octets, diff --git a/src/conftest/hooks/pretend_auth.c b/src/conftest/hooks/pretend_auth.c index 4be6f45db..5a86c5392 100644 --- a/src/conftest/hooks/pretend_auth.c +++ b/src/conftest/hooks/pretend_auth.c @@ -237,8 +237,8 @@ static bool build_auth(private_pretend_auth_t *this, return FALSE; } keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa); - if (!keymat->get_auth_octets(keymat, TRUE, this->ike_init, - this->nonce, this->id, this->reserved, + if (!keymat->get_auth_octets(keymat, TRUE, this->ike_init, this->nonce, + chunk_empty, this->id, this->reserved, &octets, NULL)) { private->destroy(private); diff --git a/src/conftest/hooks/rebuild_auth.c b/src/conftest/hooks/rebuild_auth.c index bc20292a1..5676e307b 100644 --- a/src/conftest/hooks/rebuild_auth.c +++ b/src/conftest/hooks/rebuild_auth.c @@ -136,8 +136,8 @@ static bool rebuild_auth(private_rebuild_auth_t *this, ike_sa_t *ike_sa, return FALSE; } keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa); - if (!keymat->get_auth_octets(keymat, FALSE, this->ike_init, - this->nonce, id, reserved, &octets, NULL)) + if (!keymat->get_auth_octets(keymat, FALSE, this->ike_init, this->nonce, + chunk_empty, id, reserved, &octets, NULL)) { private->destroy(private); id->destroy(id); diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c index bcf262725..c7630bd31 100644 --- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c @@ -460,8 +460,8 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, } other_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_psk_sig(keymat, TRUE, init, nonce, - this->msk, other_id, this->reserved, &auth_data)) + if (!keymat->get_psk_sig(keymat, TRUE, init, nonce, this->msk, chunk_empty, + other_id, this->reserved, &auth_data)) { return FALSE; } @@ -507,8 +507,8 @@ static bool build_auth(private_eap_authenticator_t *this, message_t *message, DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N", my_id, auth_class_names, AUTH_CLASS_EAP); - if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, - this->msk, my_id, this->reserved, &auth_data)) + if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, this->msk, chunk_empty, + my_id, this->reserved, &auth_data)) { return FALSE; } diff --git a/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c index c1decb130..99219ecf7 100644 --- a/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c @@ -74,7 +74,8 @@ METHOD(authenticator_t, build, status_t, return NOT_FOUND; } if (!keymat->get_psk_sig(keymat, FALSE, this->ike_sa_init, this->nonce, - key->get_key(key), my_id, this->reserved, &auth_data)) + key->get_key(key), chunk_empty, my_id, + this->reserved, &auth_data)) { key->destroy(key); return FAILED; @@ -119,7 +120,8 @@ METHOD(authenticator_t, process, status_t, keys_found++; if (!keymat->get_psk_sig(keymat, TRUE, this->ike_sa_init, this->nonce, - key->get_key(key), other_id, this->reserved, &auth_data)) + key->get_key(key), chunk_empty, other_id, + this->reserved, &auth_data)) { continue; } diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 652b837fe..fbf4c8006 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -227,8 +227,8 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, return FAILED; } - if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, - this->nonce, id, this->reserved, &octets, + if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, this->nonce, + chunk_empty, id, this->reserved, &octets, schemes)) { enumerator = array_create_enumerator(schemes); @@ -293,7 +293,8 @@ static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); if (keymat->get_auth_octets(keymat, verify, this->ike_sa_init, this->nonce, - id, this->reserved, octets, schemes) && + chunk_empty, id, this->reserved, octets, + schemes) && array_remove(schemes, 0, scheme)) { success = TRUE; diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index f8b23b66e..db46b816b 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -491,6 +491,93 @@ failure: return this->skp_build.len && this->skp_verify.len; } +/** + * Derives a key from the given key and a PRF that was initialized with a PPK + */ +static bool derive_ppk_key(prf_t *prf, char *name, chunk_t key, + chunk_t *new_key) +{ + prf_plus_t *prf_plus; + + prf_plus = prf_plus_create(prf, TRUE, key); + if (!prf_plus || + !prf_plus->allocate_bytes(prf_plus, key.len, new_key)) + { + DBG1(DBG_IKE, "unable to derive %s with PPK", name); + DESTROY_IF(prf_plus); + return FALSE; + } + prf_plus->destroy(prf_plus); + return TRUE; +} + +/** + * Use the given PPK to derive a new SK_pi/r + */ +static bool derive_skp_ppk(private_keymat_v2_t *this, chunk_t ppk, chunk_t skp, + chunk_t *new_skp) +{ + if (!this->prf->set_key(this->prf, ppk)) + { + DBG1(DBG_IKE, "unable to set PPK in PRF"); + return FALSE; + } + return derive_ppk_key(this->prf, "SK_p", skp, new_skp); +} + +METHOD(keymat_v2_t, derive_ike_keys_ppk, bool, + private_keymat_v2_t *this, chunk_t ppk) +{ + chunk_t skd = chunk_empty, new_skpi = chunk_empty, new_skpr = chunk_empty; + chunk_t *skpi, *skpr; + + if (!this->skd.ptr) + { + return FALSE; + } + + if (this->initiator) + { + skpi = &this->skp_build; + skpr = &this->skp_verify; + } + else + { + skpi = &this->skp_verify; + skpr = &this->skp_build; + } + + DBG4(DBG_IKE, "derive keys using PPK %B", &ppk); + + if (!this->prf->set_key(this->prf, ppk)) + { + DBG1(DBG_IKE, "unable to set PPK in PRF"); + return FALSE; + } + if (!derive_ppk_key(this->prf, "Sk_d", this->skd, &skd) || + !derive_ppk_key(this->prf, "Sk_pi", *skpi, &new_skpi) || + !derive_ppk_key(this->prf, "Sk_pr", *skpr, &new_skpr)) + { + chunk_clear(&skd); + chunk_clear(&new_skpi); + chunk_clear(&new_skpr); + return FALSE; + } + + DBG4(DBG_IKE, "Sk_d secret %B", &skd); + chunk_clear(&this->skd); + this->skd = skd; + + DBG4(DBG_IKE, "Sk_pi secret %B", &new_skpi); + chunk_clear(skpi); + *skpi = new_skpi; + + DBG4(DBG_IKE, "Sk_pr secret %B", &new_skpr); + chunk_clear(skpr); + *skpr = new_skpr; + return TRUE; +} + METHOD(keymat_v2_t, derive_child_keys, bool, private_keymat_v2_t *this, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, @@ -632,13 +719,23 @@ METHOD(keymat_t, get_aead, aead_t*, METHOD(keymat_v2_t, get_auth_octets, bool, private_keymat_v2_t *this, bool verify, chunk_t ike_sa_init, - chunk_t nonce, identification_t *id, char reserved[3], chunk_t *octets, - array_t *schemes) + chunk_t nonce, chunk_t ppk, identification_t *id, char reserved[3], + chunk_t *octets, array_t *schemes) { chunk_t chunk, idx; + chunk_t skp_ppk = chunk_empty; chunk_t skp; skp = verify ? this->skp_verify : this->skp_build; + if (ppk.ptr) + { + DBG4(DBG_IKE, "PPK %B", &ppk); + if (!derive_skp_ppk(this, ppk, skp, &skp_ppk)) + { + return FALSE; + } + skp = skp_ppk; + } chunk = chunk_alloca(4); chunk.ptr[0] = id->get_type(id); @@ -650,8 +747,10 @@ METHOD(keymat_v2_t, get_auth_octets, bool, if (!this->prf->set_key(this->prf, skp) || !this->prf->allocate_bytes(this->prf, idx, &chunk)) { + chunk_clear(&skp_ppk); return FALSE; } + chunk_clear(&skp_ppk); *octets = chunk_cat("ccm", ike_sa_init, nonce, chunk); DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", octets); return TRUE; @@ -665,41 +764,53 @@ METHOD(keymat_v2_t, get_auth_octets, bool, METHOD(keymat_v2_t, get_psk_sig, bool, private_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) + chunk_t secret, chunk_t ppk, identification_t *id, char reserved[3], + chunk_t *sig) { - chunk_t key_pad, key, octets; + chunk_t skp_ppk = chunk_empty, key = chunk_empty, octets = chunk_empty; + chunk_t key_pad; + bool success = FALSE; if (!secret.len) { /* EAP uses SK_p if no MSK has been established */ secret = verify ? this->skp_verify : this->skp_build; + if (ppk.ptr) + { + if (!derive_skp_ppk(this, ppk, secret, &skp_ppk)) + { + return FALSE; + } + secret = skp_ppk; + } } - if (!get_auth_octets(this, verify, ike_sa_init, nonce, id, reserved, + if (!get_auth_octets(this, verify, ike_sa_init, nonce, ppk, id, reserved, &octets, NULL)) { - return FALSE; + goto failure; } /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), ) */ key_pad = chunk_create(IKEV2_KEY_PAD, IKEV2_KEY_PAD_LENGTH); if (!this->prf->set_key(this->prf, secret) || !this->prf->allocate_bytes(this->prf, key_pad, &key)) { - chunk_free(&octets); - return FALSE; + goto failure; } if (!this->prf->set_key(this->prf, key) || !this->prf->allocate_bytes(this->prf, octets, sig)) { - chunk_free(&key); - chunk_free(&octets); - return FALSE; + goto failure; } DBG4(DBG_IKE, "secret %B", &secret); DBG4(DBG_IKE, "prf(secret, keypad) %B", &key); DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", sig); + success = TRUE; + +failure: + chunk_clear(&skp_ppk); chunk_free(&octets); chunk_free(&key); + return success; - return TRUE; } METHOD(keymat_v2_t, hash_algorithm_supported, bool, @@ -752,6 +863,7 @@ keymat_v2_t *keymat_v2_create(bool initiator) .destroy = _destroy, }, .derive_ike_keys = _derive_ike_keys, + .derive_ike_keys_ppk = _derive_ike_keys_ppk, .derive_child_keys = _derive_child_keys, .get_skd = _get_skd, .get_auth_octets = _get_auth_octets, diff --git a/src/libcharon/sa/ikev2/keymat_v2.h b/src/libcharon/sa/ikev2/keymat_v2.h index 5dc9cda38..3cc071aeb 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.h +++ b/src/libcharon/sa/ikev2/keymat_v2.h @@ -57,6 +57,16 @@ struct keymat_v2_t { pseudo_random_function_t rekey_function, chunk_t rekey_skd); + /** + * Derive SK_d, SK_pi and SK_pr after authentication using the given + * Postquantum Preshared Key and the previous values of these keys that + * were derived by derive_ike_keys(). + * + * @param ppk the postquantum preshared key + * @return TRUE on success + */ + bool (*derive_ike_keys_ppk)(keymat_v2_t *this, chunk_t ppk); + /** * Derive keys for a CHILD_SA. * @@ -95,9 +105,10 @@ struct keymat_v2_t { * key. PSK and EAP authentication include a secret into the data, use * the get_psk_sig() method instead. * - * @param verify TRUE to create for verfification, FALSE to sign + * @param verify TRUE to create for verification, FALSE to sign * @param ike_sa_init encoded ike_sa_init message * @param nonce nonce value + * @param ppk optional postquantum preshared key * @param id identity * @param reserved reserved bytes of id_payload * @param octests chunk receiving allocated auth octets @@ -107,7 +118,7 @@ struct keymat_v2_t { * @return TRUE if octets created successfully */ bool (*get_auth_octets)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init, - chunk_t nonce, identification_t *id, + chunk_t nonce, chunk_t ppk, identification_t *id, char reserved[3], chunk_t *octets, array_t *schemes); /** @@ -117,17 +128,18 @@ struct keymat_v2_t { * includes the secret into the signature. If no secret is given, SK_p is * used as secret (used for EAP methods without MSK). * - * @param verify TRUE to create for verfification, FALSE to sign + * @param verify TRUE to create for verification, FALSE to sign * @param ike_sa_init encoded ike_sa_init message * @param nonce nonce value * @param secret optional secret to include into signature + * @param ppk optional postquantum preshared key * @param id identity * @param reserved reserved bytes of id_payload * @param sign chunk receiving allocated signature octets * @return TRUE if signature created successfully */ bool (*get_psk_sig)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init, - chunk_t nonce, chunk_t secret, + chunk_t nonce, chunk_t secret, chunk_t ppk, identification_t *id, char reserved[3], chunk_t *sig); /** From 83dcc1f4cfe9695bddec55379d3f877de68ef268 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 11:47:46 +0200 Subject: [PATCH 06/20] ike-sa: Add flag for PPK extension --- src/libcharon/sa/ike_sa.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 316b713ee..ff1b7168c 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -156,6 +156,11 @@ enum ike_extension_t { * IKEv2 Message ID sync, RFC 6311 */ EXT_IKE_MESSAGE_ID_SYNC = (1<<14), + + /** + * Postquantum Preshared Keys, draft-ietf-ipsecme-qr-ikev2 + */ + EXT_PPK = (1<<15), }; /** From a2ff8b654da4ffb7d8c92b3b5b81c173a078e017 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 15:16:21 +0200 Subject: [PATCH 07/20] peer-cfg: Add properties for PPK ID and whether PPK is required --- src/libcharon/config/peer_cfg.c | 45 +++++++++++++++++++++++++++++---- src/libcharon/config/peer_cfg.h | 20 ++++++++++++++- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/libcharon/config/peer_cfg.c b/src/libcharon/config/peer_cfg.c index 95cbcc564..bbe45a4b5 100644 --- a/src/libcharon/config/peer_cfg.c +++ b/src/libcharon/config/peer_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2017 Tobias Brunner + * Copyright (C) 2007-2018 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -155,6 +155,16 @@ struct private_peer_cfg_t { */ linked_list_t *remote_auth; + /** + * PPK ID + */ + identification_t *ppk_id; + + /** + * Whether a PPK is required + */ + bool ppk_required; + #ifdef ME /** * Is this a mediation connection? @@ -577,6 +587,18 @@ METHOD(peer_cfg_t, create_auth_cfg_enumerator, enumerator_t*, return this->remote_auth->create_enumerator(this->remote_auth); } +METHOD(peer_cfg_t, get_ppk_id, identification_t*, + private_peer_cfg_t *this) +{ + return this->ppk_id; +} + +METHOD(peer_cfg_t, ppk_required, bool, + private_peer_cfg_t *this) +{ + return this->ppk_required; +} + #ifdef ME METHOD(peer_cfg_t, is_mediation, bool, private_peer_cfg_t *this) @@ -651,6 +673,14 @@ static bool auth_cfg_equal(private_peer_cfg_t *this, private_peer_cfg_t *other) return equal; } +/** + * Check if two identities are equal, or both are not set + */ +static bool id_equal(identification_t *this, identification_t *other) +{ + return this == other || (this && other && this->equals(this, other)); +} + METHOD(peer_cfg_t, equals, bool, private_peer_cfg_t *this, private_peer_cfg_t *other) { @@ -684,13 +714,13 @@ METHOD(peer_cfg_t, equals, bool, this->dpd == other->dpd && this->aggressive == other->aggressive && this->pull_mode == other->pull_mode && - auth_cfg_equal(this, other) + auth_cfg_equal(this, other) && + this->ppk_required == other->ppk_required && + id_equal(this->ppk_id, other->ppk_id) #ifdef ME && this->mediation == other->mediation && streq(this->mediated_by, other->mediated_by) && - (this->peer_id == other->peer_id || - (this->peer_id && other->peer_id && - this->peer_id->equals(this->peer_id, other->peer_id))) + id_equal(this->peer_id, other->peer_id) #endif /* ME */ ); } @@ -720,6 +750,7 @@ METHOD(peer_cfg_t, destroy, void, DESTROY_IF(this->peer_id); free(this->mediated_by); #endif /* ME */ + DESTROY_IF(this->ppk_id); this->lock->destroy(this->lock); free(this->name); free(this); @@ -774,6 +805,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_cfg_t *ike_cfg, .create_pool_enumerator = _create_pool_enumerator, .add_auth_cfg = _add_auth_cfg, .create_auth_cfg_enumerator = _create_auth_cfg_enumerator, + .get_ppk_id = _get_ppk_id, + .ppk_required = _ppk_required, .equals = (void*)_equals, .get_ref = _get_ref, .destroy = _destroy, @@ -799,6 +832,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_cfg_t *ike_cfg, .pull_mode = !data->push_mode, .dpd = data->dpd, .dpd_timeout = data->dpd_timeout, + .ppk_id = data->ppk_id, + .ppk_required = data->ppk_required, .vips = linked_list_create(), .pools = linked_list_create(), .local_auth = linked_list_create(), diff --git a/src/libcharon/config/peer_cfg.h b/src/libcharon/config/peer_cfg.h index 676157e5f..49c4d1492 100644 --- a/src/libcharon/config/peer_cfg.h +++ b/src/libcharon/config/peer_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2017 Tobias Brunner + * Copyright (C) 2007-2018 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -311,6 +311,20 @@ struct peer_cfg_t { */ enumerator_t* (*create_pool_enumerator)(peer_cfg_t *this); + /** + * Get the PPK ID to use with this peer. + * + * @return PPK id + */ + identification_t *(*get_ppk_id)(peer_cfg_t *this); + + /** + * Whether a PPK is required with this peer. + * + * @return TRUE, if a PPK is required + */ + bool (*ppk_required)(peer_cfg_t *this); + #ifdef ME /** * Is this a mediation connection? @@ -393,6 +407,10 @@ struct peer_cfg_create_t { uint32_t dpd; /** DPD timeout interval (IKEv1 only), if 0 default applies */ uint32_t dpd_timeout; + /** Postquantum Preshared Key ID (adopted) */ + identification_t *ppk_id; + /** TRUE if a PPK is required, FALSE if it's optional */ + bool ppk_required; #ifdef ME /** TRUE if this is a mediation connection */ bool mediation; From 7f94528061ef840d1db5bd86ab7dd84c62f847d7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 17:57:36 +0200 Subject: [PATCH 08/20] vici: Make PPK related options configurable --- src/libcharon/plugins/vici/vici_config.c | 16 ++++++++++++---- src/libcharon/plugins/vici/vici_query.c | 11 +++++++++++ src/swanctl/swanctl.opt | 6 ++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 8afefaa09..10c62dc89 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -2,7 +2,7 @@ * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * - * Copyright (C) 2015-2017 Tobias Brunner + * Copyright (C) 2015-2018 Tobias Brunner * Copyright (C) 2015-2018 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -304,6 +304,8 @@ typedef struct { bool mobike; bool send_certreq; bool pull; + identification_t *ppk_id; + bool ppk_required; cert_policy_t send_cert; uint64_t dpd_delay; uint64_t dpd_timeout; @@ -403,6 +405,8 @@ static void log_peer_data(peer_data_t *data) DBG2(DBG_CFG, " remote_port = %u", data->remote_port); DBG2(DBG_CFG, " send_certreq = %u", data->send_certreq); DBG2(DBG_CFG, " send_cert = %N", cert_policy_names, data->send_cert); + DBG2(DBG_CFG, " ppk_id = %Y", data->ppk_id); + DBG2(DBG_CFG, " ppk_required = %u", data->ppk_required); DBG2(DBG_CFG, " mobike = %u", data->mobike); DBG2(DBG_CFG, " aggressive = %u", data->aggressive); DBG2(DBG_CFG, " dscp = 0x%.2x", data->dscp); @@ -469,6 +473,7 @@ static void free_peer_data(peer_data_t *data) free(data->pools); free(data->local_addrs); free(data->remote_addrs); + DESTROY_IF(data->ppk_id); #ifdef ME free(data->mediated_by); DESTROY_IF(data->peer_id); @@ -1584,9 +1589,8 @@ CALLBACK(parse_hosts, bool, return TRUE; } -#ifdef ME /** - * Parse peer ID + * Parse peer/ppk ID */ CALLBACK(parse_peer_id, bool, identification_t **out, chunk_t v) @@ -1600,7 +1604,7 @@ CALLBACK(parse_peer_id, bool, *out = identification_create_from_string(buf); return TRUE; } -#endif /* ME */ + CALLBACK(cert_kv, bool, cert_data_t *cert, vici_message_t *message, char *name, chunk_t value) @@ -1744,6 +1748,8 @@ CALLBACK(peer_kv, bool, { "rekey_time", parse_time, &peer->rekey_time }, { "over_time", parse_time, &peer->over_time }, { "rand_time", parse_time, &peer->rand_time }, + { "ppk_id", parse_peer_id, &peer->ppk_id }, + { "ppk_required", parse_bool, &peer->ppk_required }, #ifdef ME { "mediation", parse_bool, &peer->mediation }, { "mediated_by", parse_string, &peer->mediated_by }, @@ -2480,6 +2486,8 @@ CALLBACK(config_sn, bool, .push_mode = !peer.pull, .dpd = peer.dpd_delay, .dpd_timeout = peer.dpd_timeout, + .ppk_id = peer.ppk_id ? peer.ppk_id->clone(peer.ppk_id) : NULL, + .ppk_required = peer.ppk_required, }; #ifdef ME cfg.mediation = peer.mediation; diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index f529902db..0b84458be 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -787,6 +787,7 @@ CALLBACK(list_conns, vici_message_t*, child_cfg_t *child_cfg; char *ike, *str, *interface; uint32_t manual_prio, dpd_delay, dpd_timeout; + identification_t *ppk_id; linked_list_t *list; traffic_selector_t *ts; lifetime_cfg_t *lft; @@ -849,6 +850,16 @@ CALLBACK(list_conns, vici_message_t*, b->add_kv(b, "dpd_timeout", "%u", dpd_timeout); } + ppk_id = peer_cfg->get_ppk_id(peer_cfg); + if (ppk_id) + { + b->add_kv(b, "ppk_id", "%Y", ppk_id); + } + if (peer_cfg->ppk_required(peer_cfg)) + { + b->add_kv(b, "ppk_required", "yes"); + } + build_auth_cfgs(peer_cfg, TRUE, b); build_auth_cfgs(peer_cfg, FALSE, b); diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 5b44c541b..82bc424b7 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -188,6 +188,12 @@ connections..send_cert = ifasked certificate payloads altogether, _always_ causes certificate payloads to be sent unconditionally whenever certificate authentication is used. +connections..ppk_id = + String identifying the Postquantum Preshared Key (PPK) to be used. + +connections..ppk_required = no + Whether a Postquantum Preshared Key (PPK) is required for this connection. + connections..keyingtries = 1 Number of retransmission sequences to perform during initial connect. From 1fb46f71196edd8176041757d639cdb429625605 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 27 Jul 2018 12:34:23 +0200 Subject: [PATCH 09/20] swanctl: Report PPK configuration in --list-conns --- src/swanctl/commands/list_conns.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/swanctl/commands/list_conns.c b/src/swanctl/commands/list_conns.c index ce903e50d..5f7dd8189 100644 --- a/src/swanctl/commands/list_conns.c +++ b/src/swanctl/commands/list_conns.c @@ -238,7 +238,7 @@ CALLBACK(conns, int, void *null, vici_res_t *res, char *name) { int ret; - char *version, *reauth_time, *rekey_time, *dpd_delay; + char *version, *reauth_time, *rekey_time, *dpd_delay, *ppk_id, *ppk_req; hashtable_t *ike; version = vici_find_str(res, "", "%s.version", name); @@ -282,6 +282,14 @@ CALLBACK(conns, int, } printf("\n"); + ppk_id = vici_find_str(res, NULL, "%s.ppk_id", name); + ppk_req = vici_find_str(res, NULL, "%s.ppk_required", name); + if (ppk_id || ppk_req) + { + printf(" ppk: %s%s%srequired\n", ppk_id ?: "", ppk_id ? ", " : "", + !ppk_req || !streq(ppk_req, "yes") ? "not " : ""); + } + ret = vici_parse_cb(res, conn_sn, NULL, conn_list, ike); free_hashtable(ike); return ret; From 600b10685280cc4d4d60db64093bc5d9c22882ca Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 15:20:30 +0200 Subject: [PATCH 10/20] ike-init: Send USE_PPK notify as appropriate --- src/libcharon/sa/ikev2/tasks/ike_init.c | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 295d4e9d9..28e28e410 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -269,6 +269,38 @@ static void handle_supported_hash_algorithms(private_ike_init_t *this, } } +/** + * Check whether to send a USE_PPK notify + */ +static bool send_use_ppk(private_ike_init_t *this) +{ + peer_cfg_t *peer; + enumerator_t *keys; + shared_key_t *key; + bool use_ppk = FALSE; + + if (this->initiator) + { + peer = this->ike_sa->get_peer_cfg(this->ike_sa); + if (peer->get_ppk_id(peer)) + { + use_ppk = TRUE; + } + } + else if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK)) + { + /* check if we have at least one PPK available */ + keys = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PPK, + NULL, NULL); + if (keys->enumerate(keys, &key, NULL, NULL)) + { + use_ppk = TRUE; + } + keys->destroy(keys); + } + return use_ppk; +} + /** * build the payloads for the message */ @@ -396,6 +428,11 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) chunk_empty); } } + /* notify the peer if we want to use/support PPK */ + if (!this->old_sa && send_use_ppk(this)) + { + message->add_notify(message, FALSE, USE_PPK, chunk_empty); + } return TRUE; } @@ -510,6 +547,13 @@ static void process_payloads(private_ike_init_t *this, message_t *message) handle_supported_hash_algorithms(this, notify); } break; + case USE_PPK: + if (!this->old_sa) + { + this->ike_sa->enable_extension(this->ike_sa, + EXT_PPK); + } + break; case REDIRECTED_FROM: { identification_t *gateway; From 7150fa70654948d9b309902a69c84b1f82d9efe9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 15:32:10 +0200 Subject: [PATCH 11/20] authenticator: Add optional method to set PPK --- src/libcharon/sa/authenticator.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/authenticator.h b/src/libcharon/sa/authenticator.h index 42d9ce32e..58a8ca04f 100644 --- a/src/libcharon/sa/authenticator.h +++ b/src/libcharon/sa/authenticator.h @@ -1,6 +1,6 @@ /* + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi - * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil * @@ -156,6 +156,17 @@ struct authenticator_t { */ status_t (*build)(authenticator_t *this, message_t *message); + /** + * Optional method to set a Postquantum Preshared Key (PPK) to be used + * during authentication. + * + * Has to be called before the final call to process()/build(). + * + * @param ppk PPK to use + * @param no_ppk_auth whether to add a NO_PPK_AUTH notify in build() + */ + void (*use_ppk)(authenticator_t *this, chunk_t ppk, bool no_ppk_auth); + /** * Check if the authenticator is capable of mutual authentication. * From 94f9f421bc255f5ebe98e353254218724f49bb8b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 17:27:13 +0200 Subject: [PATCH 12/20] ike-auth: Replace `== NULL` with `!` --- src/libcharon/sa/ikev2/tasks/ike_auth.c | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 5e284bc97..4fb266547 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -144,7 +144,7 @@ static status_t collect_my_init_data(private_ike_auth_t *this, /* get the nonce that was generated in ike_init */ nonce = (nonce_payload_t*)message->get_payload(message, PLV2_NONCE); - if (nonce == NULL) + if (!nonce) { return FAILED; } @@ -170,7 +170,7 @@ static status_t collect_other_init_data(private_ike_auth_t *this, /* get the nonce that was generated in ike_init */ nonce = (nonce_payload_t*)message->get_payload(message, PLV2_NONCE); - if (nonce == NULL) + if (!nonce) { return FAILED; } @@ -313,7 +313,7 @@ static bool load_cfg_candidates(private_ike_auth_t *this) continue; } peer_cfg->get_ref(peer_cfg); - if (this->peer_cfg == NULL) + if (!this->peer_cfg) { /* best match */ this->peer_cfg = peer_cfg; } @@ -414,7 +414,7 @@ METHOD(task_t, build_i, status_t, return collect_my_init_data(this, message); } - if (this->peer_cfg == NULL) + if (!this->peer_cfg) { this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); this->peer_cfg->get_ref(this->peer_cfg); @@ -441,7 +441,7 @@ METHOD(task_t, build_i, status_t, } /* check if an authenticator is in progress */ - if (this->my_auth == NULL) + if (!this->my_auth) { identification_t *idi, *idr = NULL; id_payload_t *id_payload; @@ -549,7 +549,7 @@ METHOD(task_t, process_r, status_t, return collect_other_init_data(this, message); } - if (this->my_auth == NULL && this->do_another_auth) + if (!this->my_auth && this->do_another_auth) { /* handle (optional) IDr payload, apply proposed identity */ id_payload = (id_payload_t*)message->get_payload(message, PLV2_ID_RESPONDER); @@ -586,7 +586,7 @@ METHOD(task_t, process_r, status_t, } } - if (this->other_auth == NULL) + if (!this->other_auth) { /* handle IDi payload */ id_payload = (id_payload_t*)message->get_payload(message, PLV2_ID_INITIATOR); @@ -601,7 +601,7 @@ METHOD(task_t, process_r, status_t, cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); cfg->add(cfg, AUTH_RULE_IDENTITY, id->clone(id)); - if (this->peer_cfg == NULL) + if (!this->peer_cfg) { if (!load_cfg_candidates(this)) { @@ -609,7 +609,7 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } } - if (message->get_payload(message, PLV2_AUTH) == NULL) + if (!message->get_payload(message, PLV2_AUTH)) { /* before authenticating with EAP, we need a EAP config */ cand = get_auth_cfg(this, FALSE); while (!cand || ( @@ -688,7 +688,7 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } - if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS) == NULL) + if (!message->get_notify(message, ANOTHER_AUTH_FOLLOWS)) { this->expect_another_auth = FALSE; if (!update_cfg_candidates(this, TRUE)) @@ -716,12 +716,12 @@ METHOD(task_t, build_r, status_t, return collect_my_init_data(this, message); } - if (this->authentication_failed || this->peer_cfg == NULL) + if (this->authentication_failed || !this->peer_cfg) { goto peer_auth_failed; } - if (this->my_auth == NULL && this->do_another_auth) + if (!this->my_auth && this->do_another_auth) { identification_t *id, *id_cfg; id_payload_t *id_payload; @@ -1032,7 +1032,7 @@ METHOD(task_t, process_i, status_t, if (this->expect_another_auth) { - if (this->other_auth == NULL) + if (!this->other_auth) { id_payload_t *id_payload; identification_t *id; @@ -1137,7 +1137,7 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "allow mutual EAP-only authentication"); } - if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS) == NULL) + if (!message->get_notify(message, ANOTHER_AUTH_FOLLOWS)) { this->expect_another_auth = FALSE; } From a9e60c96dc886b29b6afe7a5d6f0249c9f6e30b4 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 17:28:13 +0200 Subject: [PATCH 13/20] ike-auth: Add basic PPK support Some of the work will have to be done in the authenticators. --- src/libcharon/sa/ikev2/tasks/ike_auth.c | 306 +++++++++++++++++++++++- 1 file changed, 301 insertions(+), 5 deletions(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 4fb266547..8d9fa916b 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Tobias Brunner + * Copyright (C) 2012-2018 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,16 @@ struct private_ike_auth_t { */ chunk_t other_nonce; + /** + * PPK_ID sent or received + */ + identification_t *ppk_id; + + /** + * Optional PPK to use + */ + chunk_t ppk; + /** * IKE_SA_INIT message sent by us */ @@ -278,6 +289,29 @@ static bool do_another_auth(private_ike_auth_t *this) return do_another; } +/** + * Check if this is the first authentication round + */ +static bool is_first_round(private_ike_auth_t *this, bool local) +{ + enumerator_t *done; + auth_cfg_t *cfg; + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH)) + { + return TRUE; + } + + done = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, local); + if (done->enumerate(done, &cfg)) + { + done->destroy(done); + return FALSE; + } + done->destroy(done); + return TRUE; +} + /** * Get peer configuration candidates from backends */ @@ -404,6 +438,151 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict) return this->peer_cfg != NULL; } +/** + * Currently defined PPK_ID types + */ +#define PPK_ID_OPAQUE 1 +#define PPK_ID_FIXED 2 + +/** + * Parse the payload data of the given PPK_IDENTITY notify + */ +static bool parse_ppk_identity(notify_payload_t *notify, identification_t **id) +{ + chunk_t data; + + data = notify->get_notification_data(notify); + if (data.len < 2) + { + return FALSE; + } + switch (data.ptr[0]) + { + case PPK_ID_OPAQUE: + /* we currently don't support this */ + default: + return FALSE; + case PPK_ID_FIXED: + data = chunk_skip(data, 1); + break; + } + *id = identification_create_from_data(data); + return TRUE; +} + +/** + * Add a PPK_IDENTITY with the given PPK_ID to the given message + */ +static void add_ppk_identity(identification_t *id, message_t *msg) +{ + chunk_t data; + uint8_t type = PPK_ID_FIXED; + + /* we currently only support one type */ + data = chunk_cata("cc", chunk_from_thing(type), id->get_encoding(id)); + msg->add_notify(msg, FALSE, PPK_IDENTITY, data); +} + +/** + * Use the given PPK_ID to find a PPK and store it and the ID in the task + */ +static bool get_ppk(private_ike_auth_t *this, identification_t *ppk_id) +{ + shared_key_t *key; + + key = lib->credmgr->get_shared(lib->credmgr, SHARED_PPK, ppk_id, NULL); + if (!key) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but no PPK found for '%Y'", ppk_id); + return FALSE; + } + DBG1(DBG_CFG, "no PPK for '%Y' found, ignored because PPK is not " + "required", ppk_id); + return TRUE; + } + this->ppk = chunk_clone(key->get_key(key)); + this->ppk_id = ppk_id->clone(ppk_id); + key->destroy(key); + return TRUE; +} + +/** + * Check if we have a PPK available and, if not, whether we require one as + * initiator + */ +static bool get_ppk_i(private_ike_auth_t *this) +{ + identification_t *ppk_id; + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_PPK)) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but peer does not support PPK"); + return FALSE; + } + return TRUE; + } + + ppk_id = this->peer_cfg->get_ppk_id(this->peer_cfg); + if (!ppk_id) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but no PPK_ID configured"); + return FALSE; + } + return TRUE; + } + return get_ppk(this, ppk_id); +} + +/** + * Check if we have a PPK available and if not whether we require one as + * responder + */ +static bool get_ppk_r(private_ike_auth_t *this, message_t *msg) +{ + notify_payload_t *notify; + identification_t *ppk_id, *ppk_id_cfg; + bool result; + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_PPK)) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but peer does not support PPK"); + return FALSE; + } + return TRUE; + } + + notify = msg->get_notify(msg, PPK_IDENTITY); + if (!notify || !parse_ppk_identity(notify, &ppk_id)) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but no PPK_IDENTITY received"); + return FALSE; + } + return TRUE; + } + + ppk_id_cfg = this->peer_cfg->get_ppk_id(this->peer_cfg); + if (ppk_id_cfg && !ppk_id->matches(ppk_id, ppk_id_cfg)) + { + DBG1(DBG_CFG, "received PPK_ID '%Y', but require '%Y'", ppk_id, + ppk_id_cfg); + ppk_id->destroy(ppk_id); + return FALSE; + } + result = get_ppk(this, ppk_id); + ppk_id->destroy(ppk_id); + return result; +} + METHOD(task_t, build_i, status_t, private_ike_auth_t *this, message_t *message) { @@ -433,6 +612,12 @@ METHOD(task_t, build_i, status_t, /* indicate support for RFC 6311 Message ID synchronization */ message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC_SUPPORTED, chunk_empty); + /* only use a PPK in the first round */ + if (!get_ppk_i(this)) + { + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); + return FAILED; + } } if (!this->do_another_auth && !this->my_auth) @@ -508,6 +693,14 @@ METHOD(task_t, build_i, status_t, return FAILED; } } + /* for authentication methods that return NEED_MORE, the PPK will be reset + * in process_i() for messages without PPK_ID notify, so we always set it + * during the first round (afterwards the PPK won't be available) */ + if (this->ppk.ptr && this->my_auth->use_ppk) + { + this->my_auth->use_ppk(this->my_auth, this->ppk, + !this->peer_cfg->ppk_required(this->peer_cfg)); + } switch (this->my_auth->build(this->my_auth, message)) { case SUCCESS: @@ -522,6 +715,12 @@ METHOD(task_t, build_i, status_t, return FAILED; } + /* add a PPK_IDENTITY notify to the message that contains AUTH */ + if (this->ppk_id && message->get_payload(message, PLV2_AUTH)) + { + add_ppk_identity(this->ppk_id, message); + } + /* check for additional authentication rounds */ if (do_another_auth(this)) { @@ -655,6 +854,19 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } } + if (message->get_payload(message, PLV2_AUTH) && + is_first_round(this, FALSE)) + { + if (!get_ppk_r(this, message)) + { + this->authentication_failed = TRUE; + return NEED_MORE; + } + else if (this->ppk.ptr && this->other_auth->use_ppk) + { + this->other_auth->use_ppk(this->other_auth, this->ppk, FALSE); + } + } switch (this->other_auth->process(this->other_auth, message)) { case SUCCESS: @@ -700,6 +912,36 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } +/** + * Clear the PPK and PPK_ID + */ +static void clear_ppk(private_ike_auth_t *this) +{ + DESTROY_IF(this->ppk_id); + this->ppk_id = NULL; + chunk_clear(&this->ppk); +} + +/** + * Derive new keys and clear the PPK + */ +static bool apply_ppk(private_ike_auth_t *this) +{ + keymat_v2_t *keymat; + + if (this->ppk.ptr) + { + keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); + if (!keymat->derive_ike_keys_ppk(keymat, this->ppk)) + { + return FALSE; + } + DBG1(DBG_CFG, "using PPK for PPK_ID '%Y'", this->ppk_id); + } + clear_ppk(this); + return TRUE; +} + METHOD(task_t, build_r, status_t, private_ike_auth_t *this, message_t *message) { @@ -806,6 +1048,10 @@ METHOD(task_t, build_r, status_t, } if (this->my_auth) { + if (this->ppk.ptr && this->my_auth->use_ppk) + { + this->my_auth->use_ppk(this->my_auth, this->ppk, FALSE); + } switch (this->my_auth->build(this->my_auth, message)) { case SUCCESS: @@ -820,6 +1066,16 @@ METHOD(task_t, build_r, status_t, } } + /* add a PPK_IDENTITY notify and derive new keys and clear the PPK */ + if (this->ppk.ptr) + { + message->add_notify(message, FALSE, PPK_IDENTITY, chunk_empty); + if (!apply_ppk(this)) + { + goto local_auth_failed; + } + } + /* check for additional authentication rounds */ if (do_another_auth(this)) { @@ -955,7 +1211,7 @@ METHOD(task_t, process_i, status_t, enumerator_t *enumerator; payload_t *payload; auth_cfg_t *cfg; - bool mutual_eap = FALSE; + bool mutual_eap = FALSE, ppk_id_received = FALSE; if (message->get_exchange_type(message) == IKE_SA_INIT) { @@ -1011,6 +1267,9 @@ METHOD(task_t, process_i, status_t, this->ike_sa->enable_extension(this->ike_sa, EXT_IKE_MESSAGE_ID_SYNC); break; + case PPK_IDENTITY: + ppk_id_received = TRUE; + break; default: { if (type <= 16383) @@ -1072,6 +1331,11 @@ METHOD(task_t, process_i, status_t, } if (this->other_auth) { + if (ppk_id_received && is_first_round(this, FALSE) && + this->other_auth->use_ppk) + { + this->other_auth->use_ppk(this->other_auth, this->ppk, FALSE); + } switch (this->other_auth->process(this->other_auth, message)) { case SUCCESS: @@ -1107,6 +1371,14 @@ METHOD(task_t, process_i, status_t, if (this->my_auth) { + /* while we already set the PPK in build_i(), we MUST not use it if + * the peer did not reply with a PPK_ID notify */ + if (this->ppk.ptr && this->my_auth->use_ppk) + { + this->my_auth->use_ppk(this->my_auth, + ppk_id_received ? this->ppk : chunk_empty, + FALSE); + } switch (this->my_auth->process(this->my_auth, message)) { case SUCCESS: @@ -1122,11 +1394,29 @@ METHOD(task_t, process_i, status_t, case NEED_MORE: break; default: - charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); - send_auth_failed_informational(this, message); - return FAILED; + goto local_auth_failed; } } + + /* change keys and clear PPK after we are done with our authentication, so + * we only explicitly use it for the first round, afterwards we just use the + * changed SK_p keys implicitly */ + if (!this->my_auth && this->ppk_id) + { + if (ppk_id_received) + { + if (!apply_ppk(this)) + { + goto local_auth_failed; + } + } + else + { + DBG1(DBG_CFG, "peer didn't use PPK for PPK_ID '%Y'", this->ppk_id); + } + clear_ppk(this); + } + if (mutual_eap) { if (!this->my_auth || !this->my_auth->is_mutual(this->my_auth)) @@ -1175,6 +1465,10 @@ peer_auth_failed: charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); send_auth_failed_informational(this, message); return FAILED; +local_auth_failed: + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); + send_auth_failed_informational(this, message); + return FAILED; } METHOD(task_t, get_type, task_type_t, @@ -1186,6 +1480,7 @@ METHOD(task_t, get_type, task_type_t, METHOD(task_t, migrate, void, private_ike_auth_t *this, ike_sa_t *ike_sa) { + clear_ppk(this); chunk_free(&this->my_nonce); chunk_free(&this->other_nonce); DESTROY_IF(this->my_packet); @@ -1212,6 +1507,7 @@ METHOD(task_t, migrate, void, METHOD(task_t, destroy, void, private_ike_auth_t *this) { + clear_ppk(this); chunk_free(&this->my_nonce); chunk_free(&this->other_nonce); DESTROY_IF(this->my_packet); From 46bdeaf359eb8eb29b207392e287b42a42bc88d5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 26 Jul 2018 16:25:02 +0200 Subject: [PATCH 14/20] psk-authenticator: Add support for authentication with PPK --- .../ikev2/authenticators/psk_authenticator.c | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c index 99219ecf7..76571e702 100644 --- a/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2018 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -51,6 +52,16 @@ struct private_psk_authenticator_t { * Reserved bytes of ID payload */ char reserved[3]; + + /** + * PPK to use + */ + chunk_t ppk; + + /** + * Add a NO_PPK_AUTH notify + */ + bool no_ppk_auth; }; METHOD(authenticator_t, build, status_t, @@ -68,19 +79,19 @@ METHOD(authenticator_t, build, status_t, DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N", my_id, auth_method_names, AUTH_PSK); key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE, my_id, other_id); - if (key == NULL) + if (!key) { DBG1(DBG_IKE, "no shared key found for '%Y' - '%Y'", my_id, other_id); return NOT_FOUND; } if (!keymat->get_psk_sig(keymat, FALSE, this->ike_sa_init, this->nonce, - key->get_key(key), chunk_empty, my_id, + key->get_key(key), this->ppk, my_id, this->reserved, &auth_data)) { key->destroy(key); return FAILED; } - key->destroy(key); + DBG2(DBG_IKE, "successfully created shared key MAC"); auth_payload = auth_payload_create(); auth_payload->set_auth_method(auth_payload, AUTH_PSK); @@ -88,6 +99,21 @@ METHOD(authenticator_t, build, status_t, chunk_free(&auth_data); message->add_payload(message, (payload_t*)auth_payload); + if (this->no_ppk_auth) + { + if (!keymat->get_psk_sig(keymat, FALSE, this->ike_sa_init, this->nonce, + key->get_key(key), chunk_empty, my_id, + this->reserved, &auth_data)) + { + DBG1(DBG_IKE, "failed adding NO_PPK_AUTH notify"); + key->destroy(key); + return SUCCESS; + } + DBG2(DBG_IKE, "successfully created shared key MAC without PPK"); + message->add_notify(message, FALSE, NO_PPK_AUTH, auth_data); + chunk_free(&auth_data); + } + key->destroy(key); return SUCCESS; } @@ -97,6 +123,7 @@ METHOD(authenticator_t, process, status_t, chunk_t auth_data, recv_auth_data; identification_t *my_id, *other_id; auth_payload_t *auth_payload; + notify_payload_t *notify; auth_cfg_t *auth; shared_key_t *key; enumerator_t *enumerator; @@ -109,8 +136,20 @@ METHOD(authenticator_t, process, status_t, { return FAILED; } - keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); recv_auth_data = auth_payload->get_data(auth_payload); + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK) && + !this->ppk.ptr) + { /* look for a NO_PPK_AUTH notify if we have no PPK */ + notify = message->get_notify(message, NO_PPK_AUTH); + if (notify) + { + DBG1(DBG_IKE, "no PPK available, using NO_PPK_AUTH notify"); + recv_auth_data = notify->get_notification_data(notify); + } + } + + keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, @@ -120,7 +159,7 @@ METHOD(authenticator_t, process, status_t, keys_found++; if (!keymat->get_psk_sig(keymat, TRUE, this->ike_sa_init, this->nonce, - key->get_key(key), chunk_empty, other_id, + key->get_key(key), this->ppk, other_id, this->reserved, &auth_data)) { continue; @@ -152,6 +191,13 @@ METHOD(authenticator_t, process, status_t, return SUCCESS; } +METHOD(authenticator_t, use_ppk, void, + private_psk_authenticator_t *this, chunk_t ppk, bool no_ppk_auth) +{ + this->ppk = ppk; + this->no_ppk_auth = no_ppk_auth; +} + METHOD(authenticator_t, destroy, void, private_psk_authenticator_t *this) { @@ -172,6 +218,7 @@ psk_authenticator_t *psk_authenticator_create_builder(ike_sa_t *ike_sa, .authenticator = { .build = _build, .process = (void*)return_failed, + .use_ppk = _use_ppk, .is_mutual = (void*)return_false, .destroy = _destroy, }, @@ -199,6 +246,7 @@ psk_authenticator_t *psk_authenticator_create_verifier(ike_sa_t *ike_sa, .authenticator = { .build = (void*)return_failed, .process = _process, + .use_ppk = _use_ppk, .is_mutual = (void*)return_false, .destroy = _destroy, }, From 18f8249415b102c5d288c86db3a4bf74a7f99587 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 27 Jul 2018 10:49:30 +0200 Subject: [PATCH 15/20] pubkey-authenticator: Add support for authentication with PPK --- .../authenticators/pubkey_authenticator.c | 168 +++++++++++++----- 1 file changed, 124 insertions(+), 44 deletions(-) diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index fbf4c8006..1fcef03cc 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -56,6 +56,16 @@ struct private_pubkey_authenticator_t { * Reserved bytes of ID payload */ char reserved[3]; + + /** + * PPK to use + */ + chunk_t ppk; + + /** + * Add a NO_PPK_AUTH notify + */ + bool no_ppk_auth; }; /** @@ -203,18 +213,43 @@ CALLBACK(destroy_scheme, void, signature_params_destroy(params); } +/** + * Adds the given auth data to the message, either in an AUTH payload or + * a NO_PPK_AUTH notify. + * + * The data is freed. + */ +static void add_auth_to_message(message_t *message, auth_method_t method, + chunk_t data, bool notify) +{ + auth_payload_t *auth_payload; + + if (notify) + { + message->add_notify(message, FALSE, NO_PPK_AUTH, data); + } + else + { + auth_payload = auth_payload_create(); + auth_payload->set_auth_method(auth_payload, method); + auth_payload->set_data(auth_payload, data); + message->add_payload(message, (payload_t*)auth_payload); + } + chunk_free(&data); +} + /** * Create a signature using RFC 7427 signature authentication */ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, - auth_cfg_t *auth, private_key_t *private, - identification_t *id, chunk_t *auth_data) + auth_cfg_t *auth, private_key_t *private, + identification_t *id, message_t *message) { enumerator_t *enumerator; keymat_v2_t *keymat; signature_params_t *params = NULL; array_t *schemes; - chunk_t octets = chunk_empty; + chunk_t octets = chunk_empty, auth_data; status_t status = FAILED; keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); @@ -228,25 +263,45 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, } if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, this->nonce, - chunk_empty, id, this->reserved, &octets, - schemes)) + this->ppk, id, this->reserved, &octets, schemes)) { enumerator = array_create_enumerator(schemes); while (enumerator->enumerate(enumerator, ¶ms)) { - if (private->sign(private, params->scheme, params->params, octets, - auth_data) && - build_signature_auth_data(auth_data, params)) - { - status = SUCCESS; - break; - } - else + if (!private->sign(private, params->scheme, params->params, octets, + &auth_data) || + !build_signature_auth_data(&auth_data, params)) { DBG2(DBG_IKE, "unable to create %N signature for %N key", signature_scheme_names, params->scheme, key_type_names, private->get_type(private)); + continue; } + add_auth_to_message(message, AUTH_DS, auth_data, FALSE); + status = SUCCESS; + + if (this->no_ppk_auth) + { + chunk_free(&octets); + + if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, + this->nonce, chunk_empty, id, + this->reserved, &octets, schemes) && + private->sign(private, params->scheme, params->params, + octets, &auth_data) && + build_signature_auth_data(&auth_data, params)) + { + add_auth_to_message(message, AUTH_DS, auth_data, TRUE); + } + else + { + DBG2(DBG_IKE, "unable to create %N signature for %N key " + "without PPK", signature_scheme_names, params->scheme, + key_type_names, private->get_type(private)); + status = FAILED; + } + } + break; } enumerator->destroy(enumerator); } @@ -281,8 +336,8 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, * keymat). */ static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, - bool verify, identification_t *id, - chunk_t *octets, signature_params_t **scheme) + bool verify, identification_t *id, chunk_t ppk, + chunk_t *octets, signature_params_t **scheme) { keymat_v2_t *keymat; array_t *schemes; @@ -293,7 +348,7 @@ static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); if (keymat->get_auth_octets(keymat, verify, this->ike_sa_init, this->nonce, - chunk_empty, id, this->reserved, octets, + ppk, id, this->reserved, octets, schemes) && array_remove(schemes, 0, scheme)) { @@ -312,19 +367,19 @@ static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, */ static status_t sign_classic(private_pubkey_authenticator_t *this, auth_cfg_t *auth, private_key_t *private, - identification_t *id, auth_method_t *auth_method, - chunk_t *auth_data) + identification_t *id, message_t *message) { signature_scheme_t scheme; signature_params_t *params; - chunk_t octets = chunk_empty; + auth_method_t auth_method = AUTH_NONE; + chunk_t octets = chunk_empty, auth_data; status_t status = FAILED; switch (private->get_type(private)) { case KEY_RSA: scheme = SIGN_RSA_EMSA_PKCS1_SHA1; - *auth_method = AUTH_RSA; + auth_method = AUTH_RSA; break; case KEY_ECDSA: /* deduct the signature scheme from the keysize */ @@ -332,15 +387,15 @@ static status_t sign_classic(private_pubkey_authenticator_t *this, { case 256: scheme = SIGN_ECDSA_256; - *auth_method = AUTH_ECDSA_256; + auth_method = AUTH_ECDSA_256; break; case 384: scheme = SIGN_ECDSA_384; - *auth_method = AUTH_ECDSA_384; + auth_method = AUTH_ECDSA_384; break; case 521: scheme = SIGN_ECDSA_521; - *auth_method = AUTH_ECDSA_521; + auth_method = AUTH_ECDSA_521; break; default: DBG1(DBG_IKE, "%d bit ECDSA private key size not supported", @@ -357,17 +412,34 @@ static status_t sign_classic(private_pubkey_authenticator_t *this, INIT(params, .scheme = scheme, ); - if (get_auth_octets_scheme(this, FALSE, id, &octets, ¶ms) && - private->sign(private, params->scheme, NULL, octets, auth_data)) + if (get_auth_octets_scheme(this, FALSE, id, this->ppk, &octets, ¶ms) && + private->sign(private, params->scheme, NULL, octets, &auth_data)) { + add_auth_to_message(message, auth_method, auth_data, FALSE); status = SUCCESS; + + if (this->no_ppk_auth) + { + chunk_free(&octets); + if (get_auth_octets_scheme(this, FALSE, id, chunk_empty, &octets, + ¶ms) && + private->sign(private, params->scheme, NULL, octets, + &auth_data)) + { + add_auth_to_message(message, auth_method, auth_data, TRUE); + } + else + { + status = FAILED; + } + } } if (params) { signature_params_destroy(params); } DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id, - auth_method_names, *auth_method, + auth_method_names, auth_method, status == SUCCESS ? "successful" : "failed"); chunk_free(&octets); return status; @@ -379,10 +451,7 @@ METHOD(authenticator_t, build, status_t, private_key_t *private; identification_t *id; auth_cfg_t *auth; - chunk_t auth_data; status_t status; - auth_payload_t *auth_payload; - auth_method_t auth_method = AUTH_NONE; id = this->ike_sa->get_my_id(this->ike_sa); auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); @@ -395,24 +464,13 @@ METHOD(authenticator_t, build, status_t, if (this->ike_sa->supports_extension(this->ike_sa, EXT_SIGNATURE_AUTH)) { - auth_method = AUTH_DS; - status = sign_signature_auth(this, auth, private, id, &auth_data); + status = sign_signature_auth(this, auth, private, id, message); } else { - status = sign_classic(this, auth, private, id, &auth_method, - &auth_data); + status = sign_classic(this, auth, private, id, message); } private->destroy(private); - - if (status == SUCCESS) - { - 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); - } return status; } @@ -445,6 +503,7 @@ METHOD(authenticator_t, process, status_t, public_key_t *public; auth_method_t auth_method; auth_payload_t *auth_payload; + notify_payload_t *notify; chunk_t auth_data, octets; identification_t *id; auth_cfg_t *auth, *current_auth; @@ -460,9 +519,21 @@ METHOD(authenticator_t, process, status_t, { return FAILED; } - INIT(params); auth_method = auth_payload->get_auth_method(auth_payload); auth_data = auth_payload->get_data(auth_payload); + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK) && + !this->ppk.ptr) + { /* look for a NO_PPK_AUTH notify if we have no PPK */ + notify = message->get_notify(message, NO_PPK_AUTH); + if (notify) + { + DBG1(DBG_IKE, "no PPK available, using NO_PPK_AUTH notify"); + auth_data = notify->get_notification_data(notify); + } + } + + INIT(params); switch (auth_method) { case AUTH_RSA: @@ -492,7 +563,7 @@ METHOD(authenticator_t, process, status_t, return INVALID_ARG; } id = this->ike_sa->get_other_id(this->ike_sa); - if (!get_auth_octets_scheme(this, TRUE, id, &octets, ¶ms)) + if (!get_auth_octets_scheme(this, TRUE, id, this->ppk, &octets, ¶ms)) { return FAILED; } @@ -552,6 +623,13 @@ METHOD(authenticator_t, process, status_t, return status; } +METHOD(authenticator_t, use_ppk, void, + private_pubkey_authenticator_t *this, chunk_t ppk, bool no_ppk_auth) +{ + this->ppk = ppk; + this->no_ppk_auth = no_ppk_auth; +} + METHOD(authenticator_t, destroy, void, private_pubkey_authenticator_t *this) { @@ -572,6 +650,7 @@ pubkey_authenticator_t *pubkey_authenticator_create_builder(ike_sa_t *ike_sa, .authenticator = { .build = _build, .process = (void*)return_failed, + .use_ppk = _use_ppk, .is_mutual = (void*)return_false, .destroy = _destroy, }, @@ -599,6 +678,7 @@ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa, .authenticator = { .build = (void*)return_failed, .process = _process, + .use_ppk = _use_ppk, .is_mutual = (void*)return_false, .destroy = _destroy, }, From 66277067866a85a228ece9eda16851e3f7671603 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 27 Jul 2018 11:24:49 +0200 Subject: [PATCH 16/20] eap-authenticator: Add support for authentication with PPK --- .../ikev2/authenticators/eap_authenticator.c | 52 +++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c index c7630bd31..e1e6cd7ee 100644 --- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2018 Tobias Brunner * Copyright (C) 2006-2009 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -64,6 +64,16 @@ struct private_eap_authenticator_t { */ char reserved[3]; + /** + * PPK to use + */ + chunk_t ppk; + + /** + * Add a NO_PPK_AUTH notify + */ + bool no_ppk_auth; + /** * Current EAP method processing */ @@ -444,6 +454,7 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, chunk_t nonce, chunk_t init) { auth_payload_t *auth_payload; + notify_payload_t *notify; chunk_t auth_data, recv_auth_data; identification_t *other_id; auth_cfg_t *auth; @@ -458,14 +469,26 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, DBG1(DBG_IKE, "AUTH payload missing"); return FALSE; } + recv_auth_data = auth_payload->get_data(auth_payload); + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK) && + !this->ppk.ptr) + { /* look for a NO_PPK_AUTH notify if we have no PPK */ + notify = message->get_notify(message, NO_PPK_AUTH); + if (notify) + { + DBG1(DBG_IKE, "no PPK available, using NO_PPK_AUTH notify"); + recv_auth_data = notify->get_notification_data(notify); + } + } + other_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_psk_sig(keymat, TRUE, init, nonce, this->msk, chunk_empty, + if (!keymat->get_psk_sig(keymat, TRUE, init, nonce, this->msk, this->ppk, other_id, this->reserved, &auth_data)) { return FALSE; } - recv_auth_data = auth_payload->get_data(auth_payload); if (!auth_data.len || !chunk_equals_const(auth_data, recv_auth_data)) { DBG1(DBG_IKE, "verification of AUTH payload with%s EAP MSK failed", @@ -507,7 +530,7 @@ static bool build_auth(private_eap_authenticator_t *this, message_t *message, DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N", my_id, auth_class_names, AUTH_CLASS_EAP); - if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, this->msk, chunk_empty, + if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, this->msk, this->ppk, my_id, this->reserved, &auth_data)) { return FALSE; @@ -517,6 +540,18 @@ static bool build_auth(private_eap_authenticator_t *this, message_t *message, auth_payload->set_data(auth_payload, auth_data); message->add_payload(message, (payload_t*)auth_payload); chunk_free(&auth_data); + + if (this->no_ppk_auth) + { + if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, this->msk, + chunk_empty, my_id, this->reserved, &auth_data)) + { + DBG1(DBG_IKE, "failed adding NO_PPK_AUTH notify"); + return FALSE; + } + message->add_notify(message, FALSE, NO_PPK_AUTH, auth_data); + chunk_free(&auth_data); + } return TRUE; } @@ -698,6 +733,13 @@ METHOD(authenticator_t, is_mutual, bool, return TRUE; } +METHOD(authenticator_t, use_ppk, void, + private_eap_authenticator_t *this, chunk_t ppk, bool no_ppk_auth) +{ + this->ppk = ppk; + this->no_ppk_auth = no_ppk_auth; +} + METHOD(authenticator_t, destroy, void, private_eap_authenticator_t *this) { @@ -723,6 +765,7 @@ eap_authenticator_t *eap_authenticator_create_builder(ike_sa_t *ike_sa, .authenticator = { .build = _build_client, .process = _process_client, + .use_ppk = _use_ppk, .is_mutual = _is_mutual, .destroy = _destroy, }, @@ -753,6 +796,7 @@ eap_authenticator_t *eap_authenticator_create_verifier(ike_sa_t *ike_sa, .authenticator = { .build = _build_server, .process = _process_server, + .use_ppk = _use_ppk, .is_mutual = _is_mutual, .destroy = _destroy, }, From e4d85011e48858f6dd0b9e06db1a6b0dbc5df2ed Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 27 Jul 2018 12:14:18 +0200 Subject: [PATCH 17/20] ikev2: Mark IKE_SAs that used PPK during authentication --- src/libcharon/sa/ike_sa.h | 5 +++++ src/libcharon/sa/ikev2/tasks/ike_auth.c | 1 + 2 files changed, 6 insertions(+) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index ff1b7168c..c1d3e1d7a 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -232,6 +232,11 @@ enum ike_condition_t { * Online certificate revocation checking is suspended for this IKE_SA */ COND_ONLINE_VALIDATION_SUSPENDED = (1<<12), + + /** + * A Postquantum Preshared Key was used when this IKE_SA was created + */ + COND_PPK = (1<<13), }; /** diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 8d9fa916b..1e462f0ff 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -937,6 +937,7 @@ static bool apply_ppk(private_ike_auth_t *this) return FALSE; } DBG1(DBG_CFG, "using PPK for PPK_ID '%Y'", this->ppk_id); + this->ike_sa->set_condition(this->ike_sa, COND_PPK, TRUE); } clear_ppk(this); return TRUE; From c4d2fdd91559fcc4eb92b043b6acf02367df6dd8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 27 Jul 2018 12:50:22 +0200 Subject: [PATCH 18/20] vici: Return PPK state of an IKE_SA --- src/libcharon/plugins/vici/vici_query.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index 0b84458be..d7b61ca72 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -417,6 +417,7 @@ static void list_ike(private_vici_query_t *this, vici_builder_t *b, b->add_kv(b, "dh-group", "%N", diffie_hellman_group_names, alg); } } + add_condition(b, ike_sa, "ppk", COND_PPK); if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) { From 755985867e8d3b808361bf2eedcffdc1fef597c5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 27 Jul 2018 13:14:40 +0200 Subject: [PATCH 19/20] swanctl: Report the use of a PPK in --list-sas If we later decide the PPK_ID would be helpful, printing this on a separate line would probably make sense. --- src/swanctl/commands/list_sas.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/swanctl/commands/list_sas.c b/src/swanctl/commands/list_sas.c index 28602fc65..232f03cc2 100644 --- a/src/swanctl/commands/list_sas.c +++ b/src/swanctl/commands/list_sas.c @@ -266,6 +266,10 @@ CALLBACK(ike_sa, int, } printf("/%s", ike->get(ike, "prf-alg")); printf("/%s", ike->get(ike, "dh-group")); + if (streq(ike->get(ike, "ppk"), "yes")) + { + printf("/PPK"); + } printf("\n"); } From d1c5e6816d398968b3422fd6971612b8661d4659 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 30 Aug 2018 18:14:06 +0200 Subject: [PATCH 20/20] testing: Add some PPK scenarios --- .../tests/swanctl/rw-cert-ppk/description.txt | 11 +++++ .../tests/swanctl/rw-cert-ppk/evaltest.dat | 15 +++++++ .../hosts/carol/etc/strongswan.conf | 9 ++++ .../hosts/carol/etc/swanctl/rsa/carolKey.pem | 30 +++++++++++++ .../hosts/carol/etc/swanctl/swanctl.conf | 42 ++++++++++++++++++ .../hosts/dave/etc/strongswan.conf | 9 ++++ .../hosts/dave/etc/swanctl/swanctl.conf | 38 ++++++++++++++++ .../hosts/moon/etc/strongswan.conf | 9 ++++ .../hosts/moon/etc/swanctl/swanctl.conf | 33 ++++++++++++++ .../tests/swanctl/rw-cert-ppk/posttest.dat | 8 ++++ testing/tests/swanctl/rw-cert-ppk/pretest.dat | 11 +++++ testing/tests/swanctl/rw-cert-ppk/test.conf | 25 +++++++++++ .../rw-eap-md5-id-rsa-ppk/description.txt | 11 +++++ .../rw-eap-md5-id-rsa-ppk/evaltest.dat | 27 ++++++++++++ .../hosts/carol/etc/strongswan.conf | 9 ++++ .../hosts/carol/etc/swanctl/swanctl.conf | 41 +++++++++++++++++ .../hosts/dave/etc/strongswan.conf | 9 ++++ .../hosts/dave/etc/swanctl/swanctl.conf | 39 ++++++++++++++++ .../hosts/moon/etc/strongswan.conf | 9 ++++ .../hosts/moon/etc/swanctl/swanctl.conf | 44 +++++++++++++++++++ .../rw-eap-md5-id-rsa-ppk/posttest.dat | 8 ++++ .../swanctl/rw-eap-md5-id-rsa-ppk/pretest.dat | 13 ++++++ .../swanctl/rw-eap-md5-id-rsa-ppk/test.conf | 25 +++++++++++ .../tests/swanctl/rw-psk-ppk/description.txt | 11 +++++ testing/tests/swanctl/rw-psk-ppk/evaltest.dat | 15 +++++++ .../hosts/carol/etc/strongswan.conf | 14 ++++++ .../hosts/carol/etc/swanctl/swanctl.conf | 43 ++++++++++++++++++ .../rw-psk-ppk/hosts/dave/etc/strongswan.conf | 9 ++++ .../hosts/dave/etc/swanctl/swanctl.conf | 40 +++++++++++++++++ .../rw-psk-ppk/hosts/moon/etc/strongswan.conf | 9 ++++ .../hosts/moon/etc/swanctl/swanctl.conf | 42 ++++++++++++++++++ testing/tests/swanctl/rw-psk-ppk/posttest.dat | 8 ++++ testing/tests/swanctl/rw-psk-ppk/pretest.dat | 14 ++++++ testing/tests/swanctl/rw-psk-ppk/test.conf | 25 +++++++++++ 34 files changed, 705 insertions(+) create mode 100755 testing/tests/swanctl/rw-cert-ppk/description.txt create mode 100755 testing/tests/swanctl/rw-cert-ppk/evaltest.dat create mode 100755 testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/strongswan.conf create mode 100644 testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/swanctl/rsa/carolKey.pem create mode 100755 testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/swanctl/swanctl.conf create mode 100755 testing/tests/swanctl/rw-cert-ppk/hosts/dave/etc/strongswan.conf create mode 100755 testing/tests/swanctl/rw-cert-ppk/hosts/dave/etc/swanctl/swanctl.conf create mode 100755 testing/tests/swanctl/rw-cert-ppk/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/swanctl/rw-cert-ppk/hosts/moon/etc/swanctl/swanctl.conf create mode 100755 testing/tests/swanctl/rw-cert-ppk/posttest.dat create mode 100755 testing/tests/swanctl/rw-cert-ppk/pretest.dat create mode 100755 testing/tests/swanctl/rw-cert-ppk/test.conf create mode 100644 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/description.txt create mode 100644 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/evaltest.dat create mode 100644 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/carol/etc/strongswan.conf create mode 100755 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/carol/etc/swanctl/swanctl.conf create mode 100644 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/dave/etc/strongswan.conf create mode 100755 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/dave/etc/swanctl/swanctl.conf create mode 100644 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/moon/etc/swanctl/swanctl.conf create mode 100644 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/posttest.dat create mode 100644 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/pretest.dat create mode 100644 testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/test.conf create mode 100755 testing/tests/swanctl/rw-psk-ppk/description.txt create mode 100755 testing/tests/swanctl/rw-psk-ppk/evaltest.dat create mode 100755 testing/tests/swanctl/rw-psk-ppk/hosts/carol/etc/strongswan.conf create mode 100755 testing/tests/swanctl/rw-psk-ppk/hosts/carol/etc/swanctl/swanctl.conf create mode 100755 testing/tests/swanctl/rw-psk-ppk/hosts/dave/etc/strongswan.conf create mode 100755 testing/tests/swanctl/rw-psk-ppk/hosts/dave/etc/swanctl/swanctl.conf create mode 100755 testing/tests/swanctl/rw-psk-ppk/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/swanctl/rw-psk-ppk/hosts/moon/etc/swanctl/swanctl.conf create mode 100755 testing/tests/swanctl/rw-psk-ppk/posttest.dat create mode 100755 testing/tests/swanctl/rw-psk-ppk/pretest.dat create mode 100755 testing/tests/swanctl/rw-psk-ppk/test.conf diff --git a/testing/tests/swanctl/rw-cert-ppk/description.txt b/testing/tests/swanctl/rw-cert-ppk/description.txt new file mode 100755 index 000000000..53e103359 --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/description.txt @@ -0,0 +1,11 @@ +The roadwarriors carol and dave set up a connection each +to gateway moon. The authentication is based on X.509 certificates +and includes a Postquantum Preshared Key (PPK) that's also mixed into the +derived key material. The PPK_ID used by dave is unknown to moon +but since both peers don't enforce the use of a PPK they fall back to regular +authentication by use of the authentication data provided in the NO_PPK_AUTH +notify. +Upon the successful establishment of the IPsec tunnels, the updown script +automatically inserts iptables-based firewall rules that let pass the tunneled traffic. +In order to test both tunnel and firewall, both carol and dave ping +the client alice behind the gateway moon. diff --git a/testing/tests/swanctl/rw-cert-ppk/evaltest.dat b/testing/tests/swanctl/rw-cert-ppk/evaltest.dat new file mode 100755 index 000000000..4138124a7 --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/evaltest.dat @@ -0,0 +1,15 @@ +carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=carol@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ppk=yes.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES +dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.200 local-port=4500 local-id=dave@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.200/32] remote-ts=\[10.1.0.0/16]::YES +moon:: swanctl --list-sas --ike-id 1 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ppk=yes.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES +moon:: swanctl --list-sas --ike-id 2 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES +carol::cat /var/log/daemon.log::using PPK for PPK_ID 'ppk-carol@strongswan.org'::YES +dave:: cat /var/log/daemon.log::peer didn't use PPK for PPK_ID 'ppk-dave@strongswan.org'::YES +moon:: cat /var/log/daemon.log::using PPK for PPK_ID 'ppk-carol@strongswan.org'::YES +moon:: cat /var/log/daemon.log::no PPK for 'ppk-dave@strongswan.org' found, ignored because PPK is not required::YES +moon:: cat /var/log/daemon.log::no PPK available, using NO_PPK_AUTH notify::YES +alice::ping -c 1 192.168.0.100::64 bytes from 192.168.0.100: icmp_.eq=1::YES +alice::ping -c 1 192.168.0.200::64 bytes from 192.168.0.200: icmp_.eq=1::YES +moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES +moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES +moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES +moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/strongswan.conf b/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/strongswan.conf new file mode 100755 index 000000000..b415e075c --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce sha1 sha2 aes hmac pem pkcs1 x509 revocation constraints pubkey curve25519 gmp curl kernel-netlink socket-default updown vici +} diff --git a/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/swanctl/rsa/carolKey.pem b/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/swanctl/rsa/carolKey.pem new file mode 100644 index 000000000..1454ec54c --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/swanctl/rsa/carolKey.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,7E1D40A7901772BA4D22AF58AA2DC76F + +1jt4EsxtHvgpSLN8PA/kSVKgoAsBEBQb8RK6VGnZywMCnpJdLKdPisGGYKNPg53b +/0AFBmQVE60M8icbSAIUrAtyKxaBkoc9A7ibNCjobi0UzXTm3GcZZ1EC4/lE9PQZ +/2FbcPgQWN3kZraZDkeP9XBXl6PorES8xvQUxJ9pd4hL7/c28fIApGhEimkIZO8o +Qb7bR2cNCLYQAR6PeDoqhV39gvWoh77wp1WB3tQVbkS6MI/xl3wY2QVdq3Sbszh+ +f6lDU/SZS8BU0f44FRoInPp0GasgJ7MCiuEIshjuNPa50QkMcnNJsSgVEuw2hjN6 +LvAXx7vPt9pKpQfnu7YSJUsXDYN6PyXt7sZ8hDqraYIcI6eMpEBaTpItPSV2eckv +06KC24Oa66E1yufNFAY49S2OY+pJA0W5zmcCqCjdrfJ+wNQYKZpbrfGz4VRzlFJC +e3VkmAFwA5rcZdlp/mU2XREy+TaWsHMnpL0NcMHGmsfkTgaJIkRWalrdxlNTeitr +3boNHWk0ESyMcBYRpM3eNXsGpiYy93u0bhrPbnqJsV6miKqpbs1aBNjlJ9s1Y2fC +sko5/v7uMjb5tLF3lWQZfTu+bYtpGxFrqHJjhd8yd4gL1cFi30JcjczhwRY3Dily +c0BFekMGmPc1djn6tfIFu13X9xTxyidCpVaT9UGnOaQs9OF1u8XAnZDaQgPwjLiy +UlOE8xQ60LrhWLD582FsFnZz56bZ+QOQRWDMsB8nJeqnFXKfcRlnr0qlG6lTfA8h +XkK/qGpdVvivS+CpbhVP6ixdEfa91Rx4NjLj53LGqOYwFEkM/OAIuMJetBfx3v9T +iQfv594KE32nv9besnKlmJr2cGQWBYg1pUOtFj/aZ00yuXacv8qwzbrt4xGGDYGO +Aj5Yf93UEcVkTySO1xJ1yiC6GJv1lLm0i5StwykHypxFijKe/zOpgtHVa5v5igjO +v6cfhfJGGgIPTYrtt+EDKXcayvy2e2U/3HYVCHYiiMPX8AvP/R6m7MGrzYxm/WyO +t68EWXSDLfuR3qcIlpP4aSBxuSpKhY/dIkS/beKZ7Njx1s4jSuYDMbKuuCRFSU2H +8ISHS0kh3FetiS8IyIYzxab+KQZwnVtiGj4oaAhgFTIIoH26Fv5+xka74JdzOSUA +jR9puKuxaegVWQVBx4cCyg6hAdewRm64PAcbApZWrPvMPBfTZFnXeifmaurcdK8p +p/1eLrrPnNM6+Fh6lcKdX74yHPz3eWP3K1njZegzWnChhEWElPhJr6qYNQjd+lAS +7650RJ3CJLUxBffnRR9nTArxFNI5jGWg/plLJTaRT5x5qg1dGNMqntpoeiY++Ttk +GFDGVIOICBze6SOvzkZBbuXLJSWmWj5g9J2cYsLoOvlwsDT7FzKl8p6VY4V+SQb+ +4PN8qZWmOeczaLEhZ1QLmTKFpz9+wUZsXeBd1s78bWJR0zhraMPa0UJ9GBGq6uQ0 +yZ4Xm5KHKcgoewCUQMekU9ECsmR5NuC7VFDaa1OdPEVnEYR1xtaWUY0lYKOiixnd ++85fSq/yAXI/r0O4ISA55o9y1kDqVibTwJacb6xXGg8dHSH+TtigwD8fK9mekkDC +-----END RSA PRIVATE KEY----- diff --git a/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..4d0057c94 --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/hosts/carol/etc/swanctl/swanctl.conf @@ -0,0 +1,42 @@ +connections { + + home { + local_addrs = 192.168.0.100 + remote_addrs = 192.168.0.1 + + ppk_id = ppk-carol@strongswan.org + ppk_required = yes + + local { + auth = pubkey + certs = carolCert.pem + id = carol@strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +secrets { + + rsa-carol { + file = carolKey.pem + secret = "nH5ZQEWtku0RJEZ6" + } + ppk-carol { + id = ppk-carol@strongswan.org + secret = 0x98c61dd0f8c7daf07759617546f7b3ae156f19a7bc935c61523c7ca998e5eedb + } +} diff --git a/testing/tests/swanctl/rw-cert-ppk/hosts/dave/etc/strongswan.conf b/testing/tests/swanctl/rw-cert-ppk/hosts/dave/etc/strongswan.conf new file mode 100755 index 000000000..b415e075c --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/hosts/dave/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce sha1 sha2 aes hmac pem pkcs1 x509 revocation constraints pubkey curve25519 gmp curl kernel-netlink socket-default updown vici +} diff --git a/testing/tests/swanctl/rw-cert-ppk/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-cert-ppk/hosts/dave/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..63282479f --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/hosts/dave/etc/swanctl/swanctl.conf @@ -0,0 +1,38 @@ +connections { + + home { + local_addrs = 192.168.0.200 + remote_addrs = 192.168.0.1 + + ppk_id = ppk-dave@strongswan.org + ppk_required = no + + local { + auth = pubkey + certs = daveCert.pem + id = dave@strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +secrets { + + ppk-dave { + id = ppk-dave@strongswan.org + secret = 0x06918e49398db61094438a5be8b743894f155f4e8521e6bcd1d47690557e4b13 + } +} \ No newline at end of file diff --git a/testing/tests/swanctl/rw-cert-ppk/hosts/moon/etc/strongswan.conf b/testing/tests/swanctl/rw-cert-ppk/hosts/moon/etc/strongswan.conf new file mode 100755 index 000000000..b415e075c --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/hosts/moon/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce sha1 sha2 aes hmac pem pkcs1 x509 revocation constraints pubkey curve25519 gmp curl kernel-netlink socket-default updown vici +} diff --git a/testing/tests/swanctl/rw-cert-ppk/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-cert-ppk/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..f6c05695d --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,33 @@ +connections { + + rw { + local_addrs = 192.168.0.1 + + local { + auth = pubkey + certs = moonCert.pem + id = moon.strongswan.org + } + remote { + auth = pubkey + } + children { + net { + local_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +secrets { + + ppk-carol { + id = ppk-carol@strongswan.org + secret = 0x98c61dd0f8c7daf07759617546f7b3ae156f19a7bc935c61523c7ca998e5eedb + } +} diff --git a/testing/tests/swanctl/rw-cert-ppk/posttest.dat b/testing/tests/swanctl/rw-cert-ppk/posttest.dat new file mode 100755 index 000000000..b909ac76c --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/posttest.dat @@ -0,0 +1,8 @@ +carol::swanctl --terminate --ike home +dave::swanctl --terminate --ike home +carol::systemctl stop strongswan-swanctl +dave::systemctl stop strongswan-swanctl +moon::systemctl stop strongswan-swanctl +moon::iptables-restore < /etc/iptables.flush +carol::iptables-restore < /etc/iptables.flush +dave::iptables-restore < /etc/iptables.flush diff --git a/testing/tests/swanctl/rw-cert-ppk/pretest.dat b/testing/tests/swanctl/rw-cert-ppk/pretest.dat new file mode 100755 index 000000000..dd1a17ccb --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/pretest.dat @@ -0,0 +1,11 @@ +moon::iptables-restore < /etc/iptables.rules +carol::iptables-restore < /etc/iptables.rules +dave::iptables-restore < /etc/iptables.rules +moon::systemctl start strongswan-swanctl +carol::systemctl start strongswan-swanctl +dave::systemctl start strongswan-swanctl +moon::expect-connection rw +carol::expect-connection home +carol::swanctl --initiate --child home 2> /dev/null +dave::expect-connection home +dave::swanctl --initiate --child home 2> /dev/null diff --git a/testing/tests/swanctl/rw-cert-ppk/test.conf b/testing/tests/swanctl/rw-cert-ppk/test.conf new file mode 100755 index 000000000..1227b9d1c --- /dev/null +++ b/testing/tests/swanctl/rw-cert-ppk/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon carol winnetou dave" + +# Corresponding block diagram +# +DIAGRAM="a-m-c-w-d.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="moon" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon carol dave" + +# charon controlled by swanctl +# +SWANCTL=1 diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/description.txt b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/description.txt new file mode 100644 index 000000000..fb8449068 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/description.txt @@ -0,0 +1,11 @@ +The roadwarriors carol and dave set up a connection to gateway moon. +At the outset the gateway authenticates itself to the clients by sending +an IKEv2 RSA signature accompanied by a certificate. +The roadwarrios then use the Extensible Authentication Protocol +in association with an MD5 challenge and response protocol +(EAP-MD5) to authenticate against the gateway moon and includes +a Postquantum Preshared Key (PPK) that's also mixed into the +derived key material. The PPK_ID used by dave is unknown to moon +but since both peers don't enforce the use of a PPK they fall back to regular +authentication by use of the authentication data provided in the NO_PPK_AUTH +notify. diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/evaltest.dat b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/evaltest.dat new file mode 100644 index 000000000..b02c21257 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/evaltest.dat @@ -0,0 +1,27 @@ +carol::cat /var/log/daemon.log::authentication of 'moon.strongswan.org' with RSA.* successful::YES +carol::cat /var/log/daemon.log::server requested EAP_MD5 authentication::YES +carol::cat /var/log/daemon.log::authentication of 'moon.strongswan.org' with EAP successful::YES +carol::cat /var/log/daemon.log::using PPK for PPK_ID 'ppk-carol@strongswan.org'::YES +dave:: cat /var/log/daemon.log::authentication of 'moon.strongswan.org' with RSA.* successful::YES +dave:: cat /var/log/daemon.log::server requested EAP_MD5 authentication::YES +dave:: cat /var/log/daemon.log::authentication of 'moon.strongswan.org' with EAP successful::YES +dave:: cat /var/log/daemon.log::peer didn't use PPK for PPK_ID 'ppk-dave@strongswan.org'::YES +moon:: cat /var/log/daemon.log::received EAP identity.*carol +moon:: cat /var/log/daemon.log::EAP method EAP_MD5 succeeded, no MSK established +moon:: cat /var/log/daemon.log::authentication of '192.168.0.100' with EAP successful::YES +moon:: cat /var/log/daemon.log::received EAP identity.*dave +moon:: cat /var/log/daemon.log::EAP method EAP_MD5 succeeded, no MSK established +moon:: cat /var/log/daemon.log::authentication of '192.168.0.200' with EAP successful::YES +moon:: cat /var/log/daemon.log::using PPK for PPK_ID 'ppk-carol@strongswan.org'::YES +moon:: cat /var/log/daemon.log::no PPK for 'ppk-dave@strongswan.org' found, ignored because PPK is not required::YES +moon:: cat /var/log/daemon.log::no PPK available, using NO_PPK_AUTH notify::YES +carol::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_.eq=1::YES +carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=192.168.0.100 remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ppk=yes.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES +dave:: ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_.eq=1::YES +dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.200 local-port=4500 local-id=192.168.0.200 remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.200/32] remote-ts=\[10.1.0.0/16]::YES +moon:: swanctl --list-sas --ike-id 1 --raw 2> /dev/null::rw-eap.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=192.168.0.100.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ppk=yes.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES +moon:: swanctl --list-sas --ike-id 2 --raw 2> /dev/null::rw-eap.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=192.168.0.200.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES +moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES +moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES +moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES +moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/carol/etc/strongswan.conf b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/carol/etc/strongswan.conf new file mode 100644 index 000000000..d2cc789b3 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/carol/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes md5 sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default eap-identity eap-md5 updown +} diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/carol/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..b01ce7289 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/carol/etc/swanctl/swanctl.conf @@ -0,0 +1,41 @@ +connections { + + home { + local_addrs = 192.168.0.100 + remote_addrs = 192.168.0.1 + + ppk_id = ppk-carol@strongswan.org + ppk_required = yes + + local { + auth = eap + eap_id = carol + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +secrets { + + eap-carol { + id = carol + secret = Ar3etTnp + } + ppk-carol { + id = ppk-carol@strongswan.org + secret = 0x98c61dd0f8c7daf07759617546f7b3ae156f19a7bc935c61523c7ca998e5eedb + } +} diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/dave/etc/strongswan.conf b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/dave/etc/strongswan.conf new file mode 100644 index 000000000..d2cc789b3 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/dave/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes md5 sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default eap-identity eap-md5 updown +} diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/dave/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..f288788a9 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/dave/etc/swanctl/swanctl.conf @@ -0,0 +1,39 @@ +connections { + + home { + remote_addrs = 192.168.0.1 + + ppk_id = ppk-dave@strongswan.org + + local { + auth = eap + eap_id = dave + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +secrets { + + eap-dave { + id = dave + secret = W7R0g3do + } + ppk-dave { + id = ppk-dave@strongswan.org + secret = 0x06918e49398db61094438a5be8b743894f155f4e8521e6bcd1d47690557e4b13 + } +} diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/moon/etc/strongswan.conf b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..d2cc789b3 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/moon/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes md5 sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default eap-identity eap-md5 updown +} diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..f4485ced1 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,44 @@ +connections { + + rw-eap { + local_addrs = 192.168.0.1 + + local { + auth = pubkey + certs = moonCert.pem + id = moon.strongswan.org + } + remote { + auth = eap-md5 + eap_id = %any + } + children { + net { + local_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + send_certreq = no + proposals = aes128-sha256-x25519 + } +} + +secrets { + + eap-carol { + id = carol + secret = Ar3etTnp + } + eap-dave { + id = dave + secret = W7R0g3do + } + ppk-carol { + id = ppk-carol@strongswan.org + secret = 0x98c61dd0f8c7daf07759617546f7b3ae156f19a7bc935c61523c7ca998e5eedb + } +} + diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/posttest.dat b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/posttest.dat new file mode 100644 index 000000000..46536ef80 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/posttest.dat @@ -0,0 +1,8 @@ +carol::swanctl --terminate --ike home +carol::systemctl stop strongswan-swanctl +dave::swanctl --terminate --ike home +dave::systemctl stop strongswan-swanctl +moon::systemctl stop strongswan-swanctl +moon::iptables-restore < /etc/iptables.flush +carol::iptables-restore < /etc/iptables.flush +dave::iptables-restore < /etc/iptables.flush diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/pretest.dat b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/pretest.dat new file mode 100644 index 000000000..9ae476e64 --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/pretest.dat @@ -0,0 +1,13 @@ +moon::iptables-restore < /etc/iptables.rules +carol::iptables-restore < /etc/iptables.rules +dave::iptables-restore < /etc/iptables.rules +carol::cd /etc/swanctl; rm rsa/* x509/* +dave::cd /etc/swanctl; rm rsa/* x509/* +moon::systemctl start strongswan-swanctl +carol::systemctl start strongswan-swanctl +dave::systemctl start strongswan-swanctl +moon::expect-connection rw-eap +carol::expect-connection home +carol::swanctl --initiate --child home 2> /dev/null +dave::expect-connection home +dave::swanctl --initiate --child home 2> /dev/null diff --git a/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/test.conf b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/test.conf new file mode 100644 index 000000000..842f4b38d --- /dev/null +++ b/testing/tests/swanctl/rw-eap-md5-id-rsa-ppk/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice carol moon dave" + +# Corresponding block diagram +# +DIAGRAM="a-m-c-w-d.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="moon" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon carol dave" + +# charon controlled by swanctl +# +SWANCTL=1 diff --git a/testing/tests/swanctl/rw-psk-ppk/description.txt b/testing/tests/swanctl/rw-psk-ppk/description.txt new file mode 100755 index 000000000..b9535b95c --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/description.txt @@ -0,0 +1,11 @@ +The roadwarriors carol and dave set up a connection each +to gateway moon. The authentication is based on distinct pre-shared keys +and Fully Qualified Domain Names and includes a Postquantum Preshared Key (PPK) +that's also mixed into the derived key material. The PPK_ID used by dave is +unknown to moon but since both peers don't enforce the use of a PPK they fall back +to regular authentication by use of the authentication data provided in the NO_PPK_AUTH +notify. +Upon the successful establishment of the IPsec tunnels, +leftfirewall=yes automatically inserts iptables-based firewall rules that +let pass the tunneled traffic. In order to test both tunnel and firewall, both +carol and dave ping the client alice behind the gateway moon. diff --git a/testing/tests/swanctl/rw-psk-ppk/evaltest.dat b/testing/tests/swanctl/rw-psk-ppk/evaltest.dat new file mode 100755 index 000000000..b8b3fcb0e --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/evaltest.dat @@ -0,0 +1,15 @@ +carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=carol@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ppk=yes.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES +dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.200 local-port=4500 local-id=dave@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.200/32] remote-ts=\[10.1.0.0/16]::YES +moon:: swanctl --list-sas --ike-id 1 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ppk=yes.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES +moon:: swanctl --list-sas --ike-id 2 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES +carol::cat /var/log/daemon.log::using PPK for PPK_ID 'ppk-carol@strongswan.org'::YES +dave:: cat /var/log/daemon.log::peer didn't use PPK for PPK_ID 'ppk-dave@strongswan.org'::YES +moon:: cat /var/log/daemon.log::using PPK for PPK_ID 'ppk-carol@strongswan.org'::YES +moon:: cat /var/log/daemon.log::no PPK for 'ppk-dave@strongswan.org' found, ignored because PPK is not required::YES +moon:: cat /var/log/daemon.log::no PPK available, using NO_PPK_AUTH notify::YES +alice::ping -c 1 -W 1 192.168.0.100::64 bytes from 192.168.0.100: icmp_.eq=1::YES +alice::ping -c 1 -W 1 192.168.0.200::64 bytes from 192.168.0.200: icmp_.eq=1::YES +moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES +moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES +moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES +moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/swanctl/rw-psk-ppk/hosts/carol/etc/strongswan.conf b/testing/tests/swanctl/rw-psk-ppk/hosts/carol/etc/strongswan.conf new file mode 100755 index 000000000..b56cc0cbf --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/hosts/carol/etc/strongswan.conf @@ -0,0 +1,14 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = random openssl +} + +charon-systemd { + load = random nonce aes sha1 sha2 hmac curve25519 kernel-netlink socket-default updown vici + syslog { + daemon { + ike = 4 + } + } +} diff --git a/testing/tests/swanctl/rw-psk-ppk/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-psk-ppk/hosts/carol/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..935a39ea8 --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/hosts/carol/etc/swanctl/swanctl.conf @@ -0,0 +1,43 @@ +connections { + + home { + local_addrs = 192.168.0.100 + remote_addrs = 192.168.0.1 + + ppk_id = ppk-carol@strongswan.org + ppk_required = yes + + local { + auth = psk + id = carol@strongswan.org + } + remote { + auth = psk + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +secrets { + + ike-moon { + id = moon.strongswan.org + # hex value equal to base64 0sFpZAZqEN6Ti9sqt4ZP5EWcqx + secret = 0x16964066a10de938bdb2ab7864fe4459cab1 + } + ppk-carol { + id = ppk-carol@strongswan.org + secret = 0x98c61dd0f8c7daf07759617546f7b3ae156f19a7bc935c61523c7ca998e5eedb + } +} + diff --git a/testing/tests/swanctl/rw-psk-ppk/hosts/dave/etc/strongswan.conf b/testing/tests/swanctl/rw-psk-ppk/hosts/dave/etc/strongswan.conf new file mode 100755 index 000000000..dcef959ef --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/hosts/dave/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = random openssl +} + +charon-systemd { + load = random nonce aes sha1 sha2 hmac curve25519 kernel-netlink socket-default updown vici +} diff --git a/testing/tests/swanctl/rw-psk-ppk/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-psk-ppk/hosts/dave/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..84134fed3 --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/hosts/dave/etc/swanctl/swanctl.conf @@ -0,0 +1,40 @@ +connections { + + home { + local_addrs = 192.168.0.200 + remote_addrs = 192.168.0.1 + + ppk_id = ppk-dave@strongswan.org + + local { + auth = psk + id = dave@strongswan.org + } + remote { + auth = psk + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +secrets { + + ike-moon { + id = moon.strongswan.org + secret = 0sjVzONCF02ncsgiSlmIXeqhGN + } + ppk-dave { + id = ppk-dave@strongswan.org + secret = 0x06918e49398db61094438a5be8b743894f155f4e8521e6bcd1d47690557e4b13 + } +} diff --git a/testing/tests/swanctl/rw-psk-ppk/hosts/moon/etc/strongswan.conf b/testing/tests/swanctl/rw-psk-ppk/hosts/moon/etc/strongswan.conf new file mode 100755 index 000000000..dcef959ef --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/hosts/moon/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = random openssl +} + +charon-systemd { + load = random nonce aes sha1 sha2 hmac curve25519 kernel-netlink socket-default updown vici +} diff --git a/testing/tests/swanctl/rw-psk-ppk/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/swanctl/rw-psk-ppk/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..8552945bb --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,42 @@ +connections { + + rw { + local_addrs = 192.168.0.1 + + ppk_id = *@strongswan.org + + local { + auth = psk + id = moon.strongswan.org + } + remote { + auth = psk + } + children { + net { + local_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +secrets { + + ike-carol { + id = carol@strongswan.org + secret = 0sFpZAZqEN6Ti9sqt4ZP5EWcqx + } + ike-dave { + id = dave@strongswan.org + secret = 0sjVzONCF02ncsgiSlmIXeqhGN + } + ppk-carol { + id = ppk-carol@strongswan.org + secret = 0x98c61dd0f8c7daf07759617546f7b3ae156f19a7bc935c61523c7ca998e5eedb + } +} diff --git a/testing/tests/swanctl/rw-psk-ppk/posttest.dat b/testing/tests/swanctl/rw-psk-ppk/posttest.dat new file mode 100755 index 000000000..b909ac76c --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/posttest.dat @@ -0,0 +1,8 @@ +carol::swanctl --terminate --ike home +dave::swanctl --terminate --ike home +carol::systemctl stop strongswan-swanctl +dave::systemctl stop strongswan-swanctl +moon::systemctl stop strongswan-swanctl +moon::iptables-restore < /etc/iptables.flush +carol::iptables-restore < /etc/iptables.flush +dave::iptables-restore < /etc/iptables.flush diff --git a/testing/tests/swanctl/rw-psk-ppk/pretest.dat b/testing/tests/swanctl/rw-psk-ppk/pretest.dat new file mode 100755 index 000000000..48849c8b0 --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/pretest.dat @@ -0,0 +1,14 @@ +moon::iptables-restore < /etc/iptables.rules +carol::iptables-restore < /etc/iptables.rules +dave::iptables-restore < /etc/iptables.rules +moon::cd /etc/swanctl; rm rsa/* x509/* x509ca/* +carol::cd /etc/swanctl; rm rsa/* x509/* x509ca/* +dave::cd /etc/swanctl; rm rsa/* x509/* x509ca/* +moon::systemctl start strongswan-swanctl +carol::systemctl start strongswan-swanctl +dave::systemctl start strongswan-swanctl +moon::expect-connection rw +carol::expect-connection home +carol::swanctl --initiate --child home 2> /dev/null +dave::expect-connection home +dave::swanctl --initiate --child home 2> /dev/null diff --git a/testing/tests/swanctl/rw-psk-ppk/test.conf b/testing/tests/swanctl/rw-psk-ppk/test.conf new file mode 100755 index 000000000..1227b9d1c --- /dev/null +++ b/testing/tests/swanctl/rw-psk-ppk/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon carol winnetou dave" + +# Corresponding block diagram +# +DIAGRAM="a-m-c-w-d.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="moon" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon carol dave" + +# charon controlled by swanctl +# +SWANCTL=1