keymat_v2: Add support for PPKs
authorTobias Brunner <tobias@strongswan.org>
Wed, 25 Jul 2018 14:43:01 +0000 (16:43 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 10 Sep 2018 16:03:01 +0000 (18:03 +0200)
src/charon-tkm/src/tkm/tkm_keymat.c
src/conftest/hooks/pretend_auth.c
src/conftest/hooks/rebuild_auth.c
src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
src/libcharon/sa/ikev2/authenticators/psk_authenticator.c
src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
src/libcharon/sa/ikev2/keymat_v2.c
src/libcharon/sa/ikev2/keymat_v2.h

index 71ad821..1107c22 100644 (file)
@@ -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,
index 4be6f45..5a86c53 100644 (file)
@@ -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);
index bc20292..5676e30 100644 (file)
@@ -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);
index bcf2627..c7630bd 100644 (file)
@@ -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;
        }
index c1decb1..99219ec 100644 (file)
@@ -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;
                }
index 652b837..fbf4c80 100644 (file)
@@ -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;
index f8b23b6..db46b81 100644 (file)
@@ -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"), <msg octets>) */
        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,
index 5dc9cda..3cc071a 100644 (file)
@@ -58,6 +58,16 @@ struct keymat_v2_t {
                                                        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.
         *
         * The keys for the CHILD_SA are allocated in the integ and encr chunks.
@@ -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);
 
        /**