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); /**