Implement second exchange in IKEv1 main mode
authorMartin Willi <martin@revosec.ch>
Thu, 17 Nov 2011 12:47:08 +0000 (13:47 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 20 Mar 2012 16:30:42 +0000 (17:30 +0100)
src/libcharon/sa/tasks/main_mode.c

index e8bd625..530568a 100644 (file)
@@ -54,6 +54,34 @@ struct private_main_mode_t {
         * selected IKE proposal
         */
        proposal_t *proposal;
         * selected IKE proposal
         */
        proposal_t *proposal;
+
+       /**
+        * DH exchange
+        */
+       diffie_hellman_t *dh;
+
+       /**
+        * Received public DH value from peer
+        */
+       chunk_t dh_value;
+
+       /**
+        * Initiators nonce
+        */
+       chunk_t nonce_i;
+
+       /**
+        * Responder nonce
+        */
+       chunk_t nonce_r;
+
+       /** states of main mode */
+       enum {
+               MM_INIT,
+               MM_SA,
+               MM_KE,
+               MM_ID,
+       } state;
 };
 
 METHOD(task_t, build_i, status_t,
 };
 
 METHOD(task_t, build_i, status_t,
@@ -66,43 +94,128 @@ METHOD(task_t, build_i, status_t,
 METHOD(task_t, process_r,  status_t,
        private_main_mode_t *this, message_t *message)
 {
 METHOD(task_t, process_r,  status_t,
        private_main_mode_t *this, message_t *message)
 {
-       this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
-       DBG0(DBG_IKE, "%H is initiating a Main Mode", message->get_source(message));
-       this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
-
-       if (!this->proposal)
+       switch (this->state)
        {
        {
-               linked_list_t *list;
-               sa_payload_t *sa_payload;
-
-               sa_payload = (sa_payload_t*)message->get_payload(message,
-                                                                                               SECURITY_ASSOCIATION_V1);
-               if (!sa_payload)
+               case MM_INIT:
                {
                {
-                       DBG1(DBG_IKE, "SA payload missing");
-                       return FAILED;
-               }
-               list = sa_payload->get_proposals(sa_payload);
-               this->proposal = this->config->select_proposal(this->config, list, FALSE);
 
 
-               if (!this->proposal)
+                       linked_list_t *list;
+                       sa_payload_t *sa_payload;
+
+                       this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
+                       DBG0(DBG_IKE, "%H is initiating a Main Mode",
+                                message->get_source(message));
+                       this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+                       sa_payload = (sa_payload_t*)message->get_payload(message,
+                                                                                                       SECURITY_ASSOCIATION_V1);
+                       if (!sa_payload)
+                       {
+                               DBG1(DBG_IKE, "SA payload missing");
+                               return FAILED;
+                       }
+                       list = sa_payload->get_proposals(sa_payload);
+                       this->proposal = this->config->select_proposal(this->config,
+                                                                                                                  list, FALSE);
+                       if (!this->proposal)
+                       {
+                               DBG1(DBG_IKE, "no proposal found");
+                               return FAILED;
+                       }
+                       this->state = MM_SA;
+                       return NEED_MORE;
+               }
+               case MM_SA:
                {
                {
-                       DBG1(DBG_IKE, "no proposal found");
-                       return FAILED;
+                       ke_payload_t *ke_payload;
+                       nonce_payload_t *nonce_payload;
+                       u_int16_t group;
+
+                       ke_payload = (ke_payload_t*)message->get_payload(message,
+                                                                                                                        KEY_EXCHANGE_V1);
+                       if (!ke_payload)
+                       {
+                               DBG1(DBG_IKE, "KE payload missing");
+                               return FAILED;
+                       }
+                       this->dh_value = ke_payload->get_key_exchange_data(ke_payload);
+                       this->dh_value = chunk_clone(this->dh_value);
+
+                       if (!this->proposal->get_algorithm(this->proposal,
+                                                                               DIFFIE_HELLMAN_GROUP, &group, NULL))
+                       {
+                               DBG1(DBG_IKE, "DH group selection failed");
+                               return FAILED;
+                       }
+                       this->dh = lib->crypto->create_dh(lib->crypto, group);
+                       if (!this->dh)
+                       {
+                               DBG1(DBG_IKE, "negotiated DH group not supported");
+                               return FAILED;
+                       }
+                       this->dh->set_other_public_value(this->dh, this->dh_value);
+
+
+                       nonce_payload = (nonce_payload_t*)message->get_payload(message,
+                                                                                                                                  NONCE_V1);
+                       if (!nonce_payload)
+                       {
+                               DBG1(DBG_IKE, "Nonce payload missing");
+                               return FAILED;
+                       }
+                       this->nonce_i = nonce_payload->get_nonce(nonce_payload);
+                       /* TODO-IKEv1: verify nonce length */
+
+                       this->state = MM_KE;
+                       return NEED_MORE;
                }
                }
+               default:
+                       return FAILED;
        }
        }
-       return NEED_MORE;
 }
 
 METHOD(task_t, build_r, status_t,
        private_main_mode_t *this, message_t *message)
 {
 }
 
 METHOD(task_t, build_r, status_t,
        private_main_mode_t *this, message_t *message)
 {
-       sa_payload_t *sa_payload;
+       switch (this->state)
+       {
+               case MM_SA:
+               {
+                       sa_payload_t *sa_payload;
+
+                       sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION_V1,
+                                                                                                                this->proposal);
+                       message->add_payload(message, &sa_payload->payload_interface);
+                       return NEED_MORE;
+               }
+               case MM_KE:
+               {
+                       ke_payload_t *ke_payload;
+                       nonce_payload_t *nonce_payload;
+                       rng_t *rng;
+
+                       ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1,
+                                                                                                                          this->dh);
+                       message->add_payload(message, &ke_payload->payload_interface);
 
 
-       sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION_V1,
-                                                                                                this->proposal);
-       message->add_payload(message, &sa_payload->payload_interface);
-       return NEED_MORE;
+                       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+                       if (!rng)
+                       {
+                               DBG1(DBG_IKE, "no RNG found to create nonce");
+                               return FAILED;
+                       }
+                       /* TODO-IKEv1: nonce size? */
+                       rng->allocate_bytes(rng, 20, &this->nonce_r);
+                       rng->destroy(rng);
+
+                       nonce_payload = nonce_payload_create(NONCE_V1);
+                       nonce_payload->set_nonce(nonce_payload, this->nonce_r);
+                       message->add_payload(message, &nonce_payload->payload_interface);
+                       return NEED_MORE;
+               }
+               default:
+                       return FAILED;
+       }
 }
 
 METHOD(task_t, process_i, status_t,
 }
 
 METHOD(task_t, process_i, status_t,
@@ -128,6 +241,10 @@ METHOD(task_t, destroy, void,
        private_main_mode_t *this)
 {
        DESTROY_IF(this->proposal);
        private_main_mode_t *this)
 {
        DESTROY_IF(this->proposal);
+       DESTROY_IF(this->dh);
+       free(this->dh_value.ptr);
+       free(this->nonce_i.ptr);
+       free(this->nonce_r.ptr);
        free(this);
 }
 
        free(this);
 }
 
@@ -148,6 +265,7 @@ main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator)
                },
                .ike_sa = ike_sa,
                .initiator = initiator,
                },
                .ike_sa = ike_sa,
                .initiator = initiator,
+               .state = MM_INIT,
        );
 
        if (initiator)
        );
 
        if (initiator)