+ if (ctx->pfs == TRUE) {
+ /*
+ * Use Perfect Forward Secrecy, ie. negotiate the key material
+ * using the SKE protocol.
+ */
+
+ if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+ /* Error in protocol */
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute(protocol, server->schedule, 0, 300000);
+ return;
+ }
+
+ ctx->ske = silc_ske_alloc(server->rng, server);
+ ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+ silc_ske_group_get_by_number(idata->rekey->ske_group,
+ &ctx->ske->prop->group);
+
+ silc_ske_set_callbacks(ctx->ske,
+ silc_server_protocol_rekey_send_packet,
+ NULL, NULL, NULL, silc_ske_check_version,
+ context);
+
+ status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
+ silc_ske_map_status(status)));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute(protocol, server->schedule, 0, 300000);
+ return;
+ }
+
+ /* Advance the protocol state */
+ protocol->state++;
+ silc_protocol_execute(protocol, server->schedule, 0, 0);
+ } else {
+ /*
+ * Do normal and simple re-key.
+ */
+
+ /* Send the REKEY_DONE to indicate we will take new keys into use */
+ silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
+ 0, NULL, 0, FALSE);
+
+ /* After we send REKEY_DONE we must set the sending encryption
+ key to the new key since all packets after this packet must
+ encrypted with the new key. */
+ silc_server_protocol_rekey_generate(server, ctx, TRUE);
+
+ /* The protocol ends in next stage. */
+ protocol->state = SILC_PROTOCOL_STATE_END;
+ }
+
+ } else {
+ /*
+ * We are the initiator of this protocol
+ */
+
+ /* Start the re-key by sending the REKEY packet */
+ silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY,
+ 0, NULL, 0, FALSE);
+
+ if (ctx->pfs == TRUE) {
+ /*
+ * Use Perfect Forward Secrecy, ie. negotiate the key material
+ * using the SKE protocol.
+ */
+ ctx->ske = silc_ske_alloc(server->rng, server);
+ ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+ silc_ske_group_get_by_number(idata->rekey->ske_group,
+ &ctx->ske->prop->group);
+
+ silc_ske_set_callbacks(ctx->ske,
+ silc_server_protocol_rekey_send_packet,
+ NULL, NULL, NULL, silc_ske_check_version,
+ context);
+
+ status = silc_ske_initiator_phase_2(ctx->ske, NULL, NULL, 0);
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
+ silc_ske_map_status(status)));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute(protocol, server->schedule, 0, 300000);
+ return;
+ }
+
+ /* Advance the protocol state */
+ protocol->state++;
+ } else {
+ /*
+ * Do normal and simple re-key.
+ */
+
+ /* Send the REKEY_DONE to indicate we will take new keys into use
+ now. */
+ silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
+ 0, NULL, 0, FALSE);
+
+ /* After we send REKEY_DONE we must set the sending encryption
+ key to the new key since all packets after this packet must
+ encrypted with the new key. */
+ silc_server_protocol_rekey_generate(server, ctx, TRUE);
+
+ /* The protocol ends in next stage. */
+ protocol->state = SILC_PROTOCOL_STATE_END;
+ }
+ }
+ }
+ break;
+
+ case 2:
+ /*
+ * Second state, used only when oding re-key with PFS.
+ */
+ if (ctx->responder == TRUE) {
+ if (ctx->pfs == TRUE) {
+ /*
+ * Send our KE packe to the initiator now that we've processed
+ * the initiator's KE packet.
+ */
+ status = silc_ske_responder_finish(ctx->ske, NULL, NULL,
+ SILC_SKE_PK_TYPE_SILC);
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
+ silc_ske_map_status(status)));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute(protocol, server->schedule, 0, 300000);
+ return;
+ }
+ }
+
+ } else {
+ if (ctx->pfs == TRUE) {
+ /*
+ * The packet type must be KE packet
+ */
+ if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
+ /* Error in protocol */
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute(protocol, server->schedule, 0, 300000);
+ return;
+ }
+
+ status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
+ silc_ske_map_status(status)));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute(protocol, server->schedule, 0, 300000);
+ return;
+ }
+ }
+ }
+
+ /* Send the REKEY_DONE to indicate we will take new keys into use
+ now. */
+ silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
+ 0, NULL, 0, FALSE);
+
+ /* After we send REKEY_DONE we must set the sending encryption
+ key to the new key since all packets after this packet must
+ encrypted with the new key. */
+ silc_server_protocol_rekey_generate_pfs(server, ctx, TRUE);
+
+ /* The protocol ends in next stage. */
+ protocol->state = SILC_PROTOCOL_STATE_END;
+ break;
+
+ case SILC_PROTOCOL_STATE_END:
+ /*
+ * End protocol
+ */
+
+ if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
+ /* Error in protocol */
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute(protocol, server->schedule, 0, 300000);
+ return;
+ }
+
+ /* We received the REKEY_DONE packet and all packets after this is
+ encrypted with the new key so set the decryption key to the new key */
+ silc_server_protocol_rekey_generate(server, ctx, FALSE);
+
+ /* Assure that after calling final callback there cannot be pending
+ executions for this protocol anymore. This just unregisters any
+ timeout callbacks for this protocol. */
+ silc_protocol_cancel(protocol, server->schedule);
+
+ /* Protocol has ended, call the final callback */
+ if (protocol->final_callback)
+ silc_protocol_execute_final(protocol, server->schedule);
+ else
+ silc_protocol_free(protocol);
+ break;
+
+ case SILC_PROTOCOL_STATE_ERROR:
+ /*
+ * Error occured
+ */
+
+ if (ctx->pfs == TRUE) {
+ /* Send abort notification */
+ silc_ske_abort(ctx->ske, ctx->ske->status);
+ }
+
+ /* Assure that after calling final callback there cannot be pending
+ executions for this protocol anymore. This just unregisters any
+ timeout callbacks for this protocol. */
+ silc_protocol_cancel(protocol, server->schedule);
+