Thu Apr 5 17:42:30 EEST 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+ * Added SilcServerRekey context into silcd/idlist.h.
+
* Added the PFS support as defined in the specification to the
SKE protocol. Affected files lib/silcske/*.c.
SilcClientConnection structure; not needed. Affected file is
lib/silcclient/client.h.
+ * Updated TODO.
+
Wed Apr 4 16:32:31 EEST 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
* Do not ask whether user wants to use the negotiated private key
TODO/bugs In SILC Libraries
===========================
- o Implement PFS (Perfect Forward Secrecy) flag in SKE (and in client and
- server, actually). If PFS is set, re-key must cause new key exchange.
- This is required by the SILC protocol.
-
- o Re-key in general is actually missing (from everywhere) and must be done.
-
o Compression routines are missing. The protocol supports packet
compression thus it must be implemented. SILC Comp API must be
defined. zlib package is already included into the lib dir (in CVS,
uint32 key_len;
} *SilcServerChannelRekey;
+/* Generic rekey context for connections */
+typedef struct {
+ /* Current sending encryption key, provided for re-key. The `pfs'
+ is TRUE if the Perfect Forward Secrecy is performed in re-key. */
+ unsigned char *send_enc_key;
+ uint32 enc_key_len;
+ int ske_group;
+ bool pfs;
+ uint32 timeout;
+ void *context;
+} *SilcServerRekey;
+
/*
Generic ID list data structure.
SilcCipher send_key;
SilcCipher receive_key;
- /* Current sending encryption key, provided for re-key. The `pfs'
- is TRUE if the Perfect Forward Secrecy is performed in re-key. */
- unsigned char *send_enc_key;
- uint32 enc_key_len;
- bool pfs;
+ /* Re-key context */
+ SilcServerRekey rekey;
/* Hash selected in the SKE protocol, NULL if not needed at all */
SilcHash hash;
Logical name of the server. There is no limit of the length of the
server name. This is usually the same name as defined in DNS.
- int server_type
+ uint8 server_type
Type of the server. SILC_SERVER or SILC_ROUTER are the possible
choices for this.
SilcIDListDataStruct data;
char *server_name;
- int server_type;
+ uint8 server_type;
SilcServerID *id;
char *server_info;
char *motd;
nickname. Nickname is not relevant information that would need to be
saved as plain.
- int mode
+ uint32 mode
Client's mode. Client maybe for example server operator or
router operator (SILC operator).
char *username;
char *userinfo;
SilcClientID *id;
- int mode;
+ uint32 mode;
long last_command;
uint8 fast_command;
{
SilcProtocol protocol;
SilcServerRekeyInternalContext *proto_ctx;
+ SilcIDListData idata = (SilcIDListData)sock->user_data;
SILC_LOG_DEBUG(("Start"));
proto_ctx->server = (void *)server;
proto_ctx->sock = sock;
proto_ctx->responder = TRUE;
+ proto_ctx->pfs = idata->rekey->pfs;
/* Perform rekey protocol. Will call the final callback after the
protocol is over. */
&protocol, proto_ctx, silc_server_rekey_final);
sock->protocol = protocol;
- /* Run the protocol */
- protocol->execute(server->timeout_queue, 0, protocol, sock->sock, 0, 0);
+ if (proto_ctx->pfs == FALSE)
+ /* Run the protocol */
+ protocol->execute(server->timeout_queue, 0, protocol, sock->sock, 0, 0);
}
SILC_TASK_CALLBACK(silc_server_protocol_connection_auth);
SILC_TASK_CALLBACK(silc_server_protocol_key_exchange);
+SILC_TASK_CALLBACK(silc_server_protocol_rekey);
extern char *silc_version_string;
SilcPKCS pkcs,
SilcHash hash,
SilcHmac hmac,
+ SilcSKEDiffieHellmanGroup group,
bool is_responder)
{
SilcUnknownEntry conn_data;
conn_data = silc_calloc(1, sizeof(*conn_data));
idata = (SilcIDListData)conn_data;
- if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
- idata->pfs = TRUE;
-
/* Allocate cipher to be used in the communication */
if (!silc_cipher_alloc(cipher->cipher->name, &idata->send_key)) {
silc_free(conn_data);
silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
}
- /* Note that for responder the initiator's sending key is receiving key */
- idata->send_enc_key = silc_calloc(keymat->enc_key_len / 8,
- sizeof(*idata->send_enc_key));
- memcpy(idata->send_enc_key, keymat->send_enc_key, keymat->enc_key_len / 8);
- idata->enc_key_len = keymat->enc_key_len / 8;
+ idata->rekey = silc_calloc(1, sizeof(*idata->rekey));
+ idata->rekey->send_enc_key =
+ silc_calloc(keymat->enc_key_len / 8,
+ sizeof(*idata->rekey->send_enc_key));
+ memcpy(idata->rekey->send_enc_key,
+ keymat->send_enc_key, keymat->enc_key_len / 8);
+ idata->rekey->enc_key_len = keymat->enc_key_len / 8;
+
+ if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
+ idata->rekey->pfs = TRUE;
+ idata->rekey->ske_group = silc_ske_group_get_number(group);
/* Save the remote host's public key */
silc_pkcs_public_key_decode(ske->ke1_payload->pk_data,
* Re-key protocol routines
*/
+/* Actually takes the new keys into use. */
+
+static void
+silc_server_protocol_rekey_validate(SilcServer server,
+ SilcServerRekeyInternalContext *ctx,
+ SilcIDListData idata,
+ SilcSKEKeyMaterial *keymat)
+{
+ if (ctx->responder == TRUE) {
+ silc_cipher_set_key(idata->send_key, keymat->receive_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(idata->send_key, keymat->receive_iv);
+ silc_cipher_set_key(idata->receive_key, keymat->send_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(idata->receive_key, keymat->send_iv);
+ } else {
+ silc_cipher_set_key(idata->send_key, keymat->send_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(idata->send_key, keymat->send_iv);
+ silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
+ }
+
+ silc_hmac_set_key(idata->hmac, keymat->hmac_key, keymat->hmac_key_len);
+
+ /* Save the current sending encryption key */
+ memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
+ silc_free(idata->rekey->send_enc_key);
+ idata->rekey->send_enc_key =
+ silc_calloc(keymat->enc_key_len / 8,
+ sizeof(*idata->rekey->send_enc_key));
+ memcpy(idata->rekey->send_enc_key, keymat->send_enc_key,
+ keymat->enc_key_len / 8);
+ idata->rekey->enc_key_len = keymat->enc_key_len / 8;
+}
+
/* This function actually re-generates (when not using PFS) the keys and
takes them into use. */
/* Generate the new key */
keymat = silc_calloc(1, sizeof(*keymat));
- silc_ske_process_key_material_data(idata->send_enc_key,
- idata->enc_key_len,
+ silc_ske_process_key_material_data(idata->rekey->send_enc_key,
+ idata->rekey->enc_key_len,
16, key_len, hash_len,
idata->hash, keymat);
/* Set the keys into use */
+ silc_server_protocol_rekey_validate(server, ctx, idata, keymat);
- if (ctx->responder == TRUE) {
- silc_cipher_set_key(idata->send_key, keymat->receive_enc_key,
- keymat->enc_key_len);
- silc_cipher_set_iv(idata->send_key, keymat->receive_iv);
- silc_cipher_set_key(idata->receive_key, keymat->send_enc_key,
- keymat->enc_key_len);
- silc_cipher_set_iv(idata->receive_key, keymat->send_iv);
- } else {
- silc_cipher_set_key(idata->send_key, keymat->send_enc_key,
- keymat->enc_key_len);
- silc_cipher_set_iv(idata->send_key, keymat->send_iv);
- silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key,
- keymat->enc_key_len);
- silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
- }
+ silc_ske_free_key_material(keymat);
+}
- silc_hmac_set_key(idata->hmac, keymat->hmac_key, keymat->hmac_key_len);
+/* This function actually re-generates (with PFS) the keys and
+ takes them into use. */
- /* Save the current sending encryption key */
- memset(idata->send_enc_key, 0, idata->enc_key_len);
- silc_free(idata->send_enc_key);
- idata->send_enc_key = silc_calloc(keymat->enc_key_len / 8,
- sizeof(*idata->send_enc_key));
- memcpy(idata->send_enc_key, keymat->send_enc_key, keymat->enc_key_len / 8);
- idata->enc_key_len = keymat->enc_key_len / 8;
+void
+silc_server_protocol_rekey_generate_pfs(SilcServer server,
+ SilcServerRekeyInternalContext *ctx)
+{
+ SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
+ SilcSKEKeyMaterial *keymat;
+ uint32 key_len = silc_cipher_get_key_len(idata->send_key);
+ uint32 hash_len = idata->hash->hash->hash_len;
+ unsigned char *tmpbuf;
+ uint32 klen;
+
+ SILC_LOG_DEBUG(("Generating new session keys (with PFS)"));
+
+ /* Encode KEY to binary data */
+ tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen);
+ /* Generate the new key */
+ keymat = silc_calloc(1, sizeof(*keymat));
+ silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len,
+ idata->hash, keymat);
+
+ /* Set the keys into use */
+ silc_server_protocol_rekey_validate(server, ctx, idata, keymat);
+
+ memset(tmpbuf, 0, klen);
+ silc_free(tmpbuf);
silc_ske_free_key_material(keymat);
}
-/* Performs re-key as defined the SILC protocol specification. */
+/* Packet sending callback. This function is provided as packet sending
+ routine to the Key Exchange functions. */
+
+static void
+silc_server_protocol_rekey_send_packet(SilcSKE ske,
+ SilcBuffer packet,
+ SilcPacketType type,
+ void *context)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcServerRekeyInternalContext *ctx =
+ (SilcServerRekeyInternalContext *)protocol->context;
+ SilcServer server = (SilcServer)ctx->server;
+
+ /* Send the packet immediately */
+ silc_server_packet_send(server, ctx->sock,
+ type, 0, packet->data, packet->len, TRUE);
+}
+
+/* Performs re-key as defined in the SILC protocol specification. */
SILC_TASK_CALLBACK(silc_server_protocol_rekey)
{
SilcServerRekeyInternalContext *ctx =
(SilcServerRekeyInternalContext *)protocol->context;
SilcServer server = (SilcServer)ctx->server;
+ SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
+ SilcSKEStatus status;
SILC_LOG_DEBUG(("Start"));
* using the SKE protocol.
*/
+ if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+ /* Error in protocol */
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(server->timeout_queue, 0, protocol, fd,
+ 0, 300000);
+ }
+
+ ctx->ske = silc_ske_alloc();
+ ctx->ske->rng = server->rng;
+ ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+ silc_ske_get_group_by_number(idata->rekey->ske_group,
+ &ctx->ske->prop->group);
+
+ status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer,
+ NULL, NULL, NULL, NULL);
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+ status));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(server->timeout_queue, 0,
+ protocol, fd, 0, 300000);
+ return;
+ }
+
+ /* Advance the protocol state */
+ protocol->state++;
+ protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
} else {
/*
* Do normal and simple re-key.
* 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, TRUE);
+
if (ctx->pfs == TRUE) {
/*
* Use Perfect Forward Secrecy, ie. negotiate the key material
* using the SKE protocol.
*/
+ ctx->ske = silc_ske_alloc();
+ ctx->ske->rng = server->rng;
+ ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+ silc_ske_get_group_by_number(idata->rekey->ske_group,
+ &ctx->ske->prop->group);
+
+ status =
+ silc_ske_initiator_phase_2(ctx->ske, NULL, NULL,
+ silc_server_protocol_rekey_send_packet,
+ context);
+
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+ status));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(server->timeout_queue, 0,
+ protocol, fd, 0, 300000);
+ return;
+ }
+ /* Advance the protocol state */
+ protocol->state++;
} else {
/*
* Do normal and simple re-key.
*/
- /* Start the re-key by sending the REKEY packet */
- silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY,
- 0, NULL, 0, TRUE);
-
- /* The protocol ends in next stage. */
+ /* The protocol ends in next stage. We have sent the REKEY packet
+ and now we just wait that the responder send REKEY_DONE and
+ the we'll generate the new key, simple. */
protocol->state = SILC_PROTOCOL_STATE_END;
}
}
-
}
break;
- case SILC_PROTOCOL_STATE_END:
- /*
- * End protocol
+ case 2:
+ /*
+ * Second state, used only when oding re-key with PFS.
*/
-
if (ctx->responder == TRUE) {
-
if (ctx->pfs == TRUE) {
/*
- *
- */
-
- } else {
- /*
- * We must have received the REKEY_DONE from the initiator.
+ * 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,
+ silc_server_protocol_rekey_send_packet,
+ context);
- if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
- /* Error in protocol */
- protocol->state = SILC_PROTOCOL_STATE_ERROR;
- protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
- }
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+ status));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(server->timeout_queue, 0,
+ protocol, fd, 0, 300000);
+ return;
+ }
}
} else {
-
if (ctx->pfs == TRUE) {
/*
- *
+ * The packet type must be KE packet
*/
-
- } else {
- /*
- * We must have received the REKEY_DONE from the responder.
- */
-
- if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
+ if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
/* Error in protocol */
protocol->state = SILC_PROTOCOL_STATE_ERROR;
- protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
+ protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
}
+
+ status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer,
+ NULL, NULL, NULL, NULL);
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+ status));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(server->timeout_queue, 0,
+ protocol, fd, 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, 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;
+ protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
+ }
+ if (ctx->responder == FALSE) {
+ if (ctx->pfs == FALSE) {
/* 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,
bool responder; /* TRUE if we are receiving party */
bool pfs; /* TRUE if PFS is to be used */
SilcSKE ske; /* Defined if PFS is used */
- SilcSKEKeyMaterial *keymat; /* Defined if PFS is used */
SilcPacketContext *packet;
} SilcServerRekeyInternalContext;
silc_protocol_cancel(server->timeout_queue, protocol);
silc_protocol_free(protocol);
sock->protocol = NULL;
- if (ctx->keymat)
- silc_ske_free_key_material(ctx->keymat);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
/* Cleanup */
silc_protocol_free(protocol);
sock->protocol = NULL;
- if (ctx->keymat)
- silc_ske_free_key_material(ctx->keymat);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
server or server is connecting to the router the Mutual Authentication
flag is not necessary.
+When performing re-key with PFS selected this is the only payload that
+is sent in the SKE protocol. The Key Exchange Start Payload is not sent
+at all. However, this payload does not have all the fields present.
+In re-key with PFS the public key and a possible signature data should
+not be present. If they are present they must be ignored. The only
+field that is present is the public data that is used to create the
+new key material. In the re-key the Mutual Authentication flag must
+also be ignored.
+
This payload is sent inside SILC_PACKET_KEY_EXCHANGE_1 and inside
SILC_PACKET_KEY_EXCHANGE_2 packet types. The initiator uses the
SILC_PACKET_KEY_EXCHANGE_1 and the responder the latter.
information. After the SILC_PACKET_REKEY packet is sent the sender
will perform the SKE protocol.
+If PFS flag was set the resulted key material is processed as described
+in the section Processing the Key Material in [SILC3]. The difference
+with re-key in the processing is that the initial data for the hash
+function is just the resulted key material and not the HASH as it
+is not computed at all with re-key. Other than that, the key processing
+it equivalent to normal SKE negotiation.
+
If PFS flag was not set, which is the default case, then re-key is done
without executing SKE protocol. In this case, the new key is created by
providing the current sending encryption key to the SKE protocol's key
silc_protocol_cancel(client->timeout_queue, protocol);
silc_protocol_free(protocol);
sock->protocol = NULL;
- if (ctx->keymat)
- silc_ske_free_key_material(ctx->keymat);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
/* Cleanup */
silc_protocol_free(protocol);
sock->protocol = NULL;
- if (ctx->keymat)
- silc_ske_free_key_material(ctx->keymat);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
#include "command.h"
#include "silcapi.h"
+/* Generic rekey context for connections */
+typedef struct {
+ /* Current sending encryption key, provided for re-key. The `pfs'
+ is TRUE if the Perfect Forward Secrecy is performed in re-key. */
+ unsigned char *send_enc_key;
+ uint32 enc_key_len;
+ int ske_group;
+ bool pfs;
+ uint32 timeout;
+ void *context;
+} *SilcClientRekey;
+
/* Connection structure used in client to associate all the important
connection specific data to this structure. */
struct SilcClientConnectionStruct {
SilcCipher send_key;
SilcCipher receive_key;
SilcHmac hmac;
- unsigned char *hmac_key;
- uint32 hmac_key_len;
SilcHash hash;
/* Client ID and Channel ID cache. Messages transmitted in SILC network
/* Set away message */
SilcClientAway *away;
+ /* Re-key context */
+ SilcClientRekey rekey;
+
/* Pointer back to the SilcClient. This object is passed to the application
and the actual client object is accesible through this pointer. */
SilcClient client;
SILC_TASK_CALLBACK(silc_client_protocol_connection_auth);
SILC_TASK_CALLBACK(silc_client_protocol_key_exchange);
+SILC_TASK_CALLBACK(silc_client_protocol_rekey);
extern char *silc_version_string;
/* Send the packet immediately */
silc_client_packet_send(client, ske->sock, type, NULL, 0, NULL, NULL,
packet->data, packet->len, TRUE);
-
}
/* Callback that is called when we have received KE2 payload from
SilcCipher cipher,
SilcPKCS pkcs,
SilcHash hash,
- SilcHmac hmac)
+ SilcHmac hmac,
+ SilcSKEDiffieHellmanGroup group)
{
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
ske->ke2_payload->pk_len);
#endif
+ conn->rekey = silc_calloc(1, sizeof(*conn->rekey));
+ conn->rekey->send_enc_key =
+ silc_calloc(keymat->enc_key_len / 8,
+ sizeof(*conn->rekey->send_enc_key));
+ memcpy(conn->rekey->send_enc_key,
+ keymat->send_enc_key, keymat->enc_key_len / 8);
+ conn->rekey->enc_key_len = keymat->enc_key_len / 8;
+
+ if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
+ conn->rekey->pfs = TRUE;
+ conn->rekey->ske_group = silc_ske_group_get_number(group);
+
/* Save HMAC key to be used in the communication. */
silc_hmac_alloc(hmac->hmac->name, NULL, &conn->hmac);
silc_hmac_set_key(conn->hmac, keymat->hmac_key, keymat->hmac_key_len);
}
}
+/*
+ * Re-key protocol routines
+ */
+
+/* Actually takes the new keys into use. */
+
+static void
+silc_client_protocol_rekey_validate(SilcClient client,
+ SilcClientRekeyInternalContext *ctx,
+ SilcSocketConnection sock,
+ SilcSKEKeyMaterial *keymat)
+{
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+
+ if (ctx->responder == TRUE) {
+ silc_cipher_set_key(conn->send_key, keymat->receive_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(conn->send_key, keymat->receive_iv);
+ silc_cipher_set_key(conn->receive_key, keymat->send_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(conn->receive_key, keymat->send_iv);
+ } else {
+ silc_cipher_set_key(conn->send_key, keymat->send_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(conn->send_key, keymat->send_iv);
+ silc_cipher_set_key(conn->receive_key, keymat->receive_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(conn->receive_key, keymat->receive_iv);
+ }
+
+ silc_hmac_set_key(conn->hmac, keymat->hmac_key, keymat->hmac_key_len);
+
+ /* Save the current sending encryption key */
+ memset(conn->rekey->send_enc_key, 0, conn->rekey->enc_key_len);
+ silc_free(conn->rekey->send_enc_key);
+ conn->rekey->send_enc_key =
+ silc_calloc(keymat->enc_key_len / 8,
+ sizeof(*conn->rekey->send_enc_key));
+ memcpy(conn->rekey->send_enc_key, keymat->send_enc_key,
+ keymat->enc_key_len / 8);
+ conn->rekey->enc_key_len = keymat->enc_key_len / 8;
+}
+
+/* This function actually re-generates (when not using PFS) the keys and
+ takes them into use. */
+
+void silc_client_protocol_rekey_generate(SilcClient client,
+ SilcClientRekeyInternalContext *ctx)
+{
+ SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+ SilcSKEKeyMaterial *keymat;
+ uint32 key_len = silc_cipher_get_key_len(conn->send_key);
+ uint32 hash_len = conn->hash->hash->hash_len;
+
+ SILC_LOG_DEBUG(("Generating new session keys (no PFS)"));
+
+ /* Generate the new key */
+ keymat = silc_calloc(1, sizeof(*keymat));
+ silc_ske_process_key_material_data(conn->rekey->send_enc_key,
+ conn->rekey->enc_key_len,
+ 16, key_len, hash_len,
+ conn->hash, keymat);
+
+ /* Set the keys into use */
+ silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat);
+
+ silc_ske_free_key_material(keymat);
+}
+
+/* This function actually re-generates (with PFS) the keys and
+ takes them into use. */
+
+void
+silc_client_protocol_rekey_generate_pfs(SilcClient client,
+ SilcClientRekeyInternalContext *ctx)
+{
+ SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+ SilcSKEKeyMaterial *keymat;
+ uint32 key_len = silc_cipher_get_key_len(conn->send_key);
+ uint32 hash_len = conn->hash->hash->hash_len;
+ unsigned char *tmpbuf;
+ uint32 klen;
+
+ SILC_LOG_DEBUG(("Generating new session keys (with PFS)"));
+
+ /* Encode KEY to binary data */
+ tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen);
+
+ /* Generate the new key */
+ keymat = silc_calloc(1, sizeof(*keymat));
+ silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len,
+ conn->hash, keymat);
+
+ /* Set the keys into use */
+ silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat);
+
+ memset(tmpbuf, 0, klen);
+ silc_free(tmpbuf);
+ silc_ske_free_key_material(keymat);
+}
+
+/* Packet sending callback. This function is provided as packet sending
+ routine to the Key Exchange functions. */
+
+static void
+silc_client_protocol_rekey_send_packet(SilcSKE ske,
+ SilcBuffer packet,
+ SilcPacketType type,
+ void *context)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcClientRekeyInternalContext *ctx =
+ (SilcClientRekeyInternalContext *)protocol->context;
+ SilcClient client = (SilcClient)ctx->client;
+
+ /* Send the packet immediately */
+ silc_client_packet_send(client, ctx->sock, type, NULL, 0, NULL, NULL,
+ packet->data, packet->len, TRUE);
+}
+
+/* Performs re-key as defined in the SILC protocol specification. */
+
+SILC_TASK_CALLBACK(silc_client_protocol_rekey)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcClientRekeyInternalContext *ctx =
+ (SilcClientRekeyInternalContext *)protocol->context;
+ SilcClient client = (SilcClient)ctx->client;
+ SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+ SilcSKEStatus status;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
+ protocol->state = SILC_PROTOCOL_STATE_START;
+
+ SILC_LOG_DEBUG(("State=%d", protocol->state));
+
+ switch(protocol->state) {
+ case SILC_PROTOCOL_STATE_START:
+ {
+ /*
+ * Start protocol.
+ */
+
+ if (ctx->responder == TRUE) {
+ /*
+ * We are receiving party
+ */
+
+ 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;
+ protocol->execute(client->timeout_queue, 0, protocol, fd,
+ 0, 300000);
+ }
+
+ ctx->ske = silc_ske_alloc();
+ ctx->ske->rng = client->rng;
+ ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+ silc_ske_get_group_by_number(conn->rekey->ske_group,
+ &ctx->ske->prop->group);
+
+ status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer,
+ NULL, NULL, NULL, NULL);
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+ status));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(client->timeout_queue, 0,
+ protocol, fd, 0, 300000);
+ return;
+ }
+
+ /* Advance the protocol state */
+ protocol->state++;
+ protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+ } else {
+ /*
+ * Do normal and simple re-key.
+ */
+
+ /* Send the REKEY_DONE to indicate we will take new keys into use */
+ silc_client_packet_send(client, ctx->sock,
+ SILC_PACKET_REKEY_DONE,
+ NULL, 0, NULL, NULL, NULL, 0, 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_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY,
+ NULL, 0, NULL, NULL, NULL, 0, TRUE);
+
+ if (ctx->pfs == TRUE) {
+ /*
+ * Use Perfect Forward Secrecy, ie. negotiate the key material
+ * using the SKE protocol.
+ */
+ ctx->ske = silc_ske_alloc();
+ ctx->ske->rng = client->rng;
+ ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+ silc_ske_get_group_by_number(conn->rekey->ske_group,
+ &ctx->ske->prop->group);
+
+ status =
+ silc_ske_initiator_phase_2(ctx->ske, NULL, NULL,
+ silc_client_protocol_rekey_send_packet,
+ context);
+
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+ status));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(client->timeout_queue, 0,
+ protocol, fd, 0, 300000);
+ return;
+ }
+
+ /* Advance the protocol state */
+ protocol->state++;
+ } else {
+ /*
+ * Do normal and simple re-key.
+ */
+
+ /* The protocol ends in next stage. We have sent the REKEY packet
+ and now we just wait that the responder send REKEY_DONE and
+ the we'll generate the new key, simple. */
+ 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,
+ silc_client_protocol_rekey_send_packet,
+ context);
+
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+ status));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(client->timeout_queue, 0,
+ protocol, fd, 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;
+ protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 300000);
+ }
+
+ status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer,
+ NULL, NULL, NULL, NULL);
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+ status));
+
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(client->timeout_queue, 0,
+ protocol, fd, 0, 300000);
+ return;
+ }
+ }
+ }
+
+ /* Send the REKEY_DONE to indicate we will take new keys into use
+ now. */
+ silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY_DONE,
+ NULL, 0, NULL, NULL, NULL, 0, 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;
+ protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+ }
+
+ if (ctx->responder == FALSE) {
+ if (ctx->pfs == FALSE) {
+ /* Send the REKEY_DONE to indicate we will take new keys into use
+ now. */
+ silc_client_packet_send(client, ctx->sock,
+ SILC_PACKET_REKEY_DONE,
+ NULL, 0, NULL, NULL, NULL, 0, TRUE);
+ }
+ }
+
+ /* Protocol has ended, call the final callback */
+ if (protocol->final_callback)
+ protocol->execute_final(client->timeout_queue, 0, protocol, fd);
+ 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,
+ silc_client_protocol_ke_send_packet,
+ context);
+ }
+
+ /* On error the final callback is always called. */
+ if (protocol->final_callback)
+ protocol->execute_final(client->timeout_queue, 0, protocol, fd);
+ else
+ silc_protocol_free(protocol);
+ break;
+
+ case SILC_PROTOCOL_STATE_FAILURE:
+ /*
+ * We have received failure from remote
+ */
+
+ /* On error the final callback is always called. */
+ if (protocol->final_callback)
+ protocol->execute_final(client->timeout_queue, 0, protocol, fd);
+ else
+ silc_protocol_free(protocol);
+ break;
+
+ case SILC_PROTOCOL_STATE_UNKNOWN:
+ break;
+ }
+
+}
+
/* Registers protocols used in client */
void silc_client_protocols_register(void)
silc_client_protocol_connection_auth);
silc_protocol_register(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
silc_client_protocol_key_exchange);
+ silc_protocol_register(SILC_PROTOCOL_CLIENT_REKEY,
+ silc_client_protocol_rekey);
}
/* Unregisters protocols */
silc_client_protocol_connection_auth);
silc_protocol_unregister(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
silc_client_protocol_key_exchange);
+ silc_protocol_unregister(SILC_PROTOCOL_CLIENT_REKEY,
+ silc_client_protocol_rekey);
}
bool responder; /* TRUE if we are receiving party */
bool pfs; /* TRUE if PFS is to be used */
SilcSKE ske; /* Defined if PFS is used */
- SilcSKEKeyMaterial *keymat; /* Defined if PFS is used */
SilcPacketContext *packet;
} SilcClientRekeyInternalContext;
protocol->final_callback(qptr, 0, context, fd);
}
+
+/* Cancels the execution of the next state of the protocol. */
+
+void silc_protocol_cancel(void *qptr, void *context)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ silc_task_unregister_by_callback(qptr, protocol->protocol->callback);
+}
long secs, long usecs);
void silc_protocol_execute_final(void *qptr, int type,
void *context, int fd);
+void silc_protocol_cancel(void *qptr, void *context);
#endif
return list;
}
+
+/* Returns the number of the `group'. */
+
+int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group)
+{
+ return group->number;
+}
SilcSKEStatus silc_ske_get_group_by_name(const char *name,
SilcSKEDiffieHellmanGroup *ret);
char *silc_ske_get_supported_groups();
+int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group);
#endif
if (!payload)
return SILC_SKE_STATUS_ERROR;
- if (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL &&
+ if (ske->start_payload &&
+ ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL &&
!payload->sign_data) {
SILC_LOG_DEBUG(("Signature data is missing"));
return SILC_SKE_STATUS_ERROR;
goto err;
}
- if (payload->pk_len < 5) {
+ if (payload->pk_type == 0) {
status = SILC_SKE_STATUS_BAD_PAYLOAD;
goto err;
}
goto err;
}
- if ((ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) &&
+ if (ske->start_payload &&
+ (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) &&
(payload->sign_len < 3 || !payload->sign_data)) {
SILC_LOG_DEBUG(("The signature data is missing - both parties are "
"required to do authentication"));
payload->x = e;
/* Get public key */
- payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
- if (!payload->pk_data) {
- silc_mp_clear(x);
- silc_free(x);
- silc_mp_clear(&e);
- silc_free(payload);
- ske->status = SILC_SKE_STATUS_OK;
- return ske->status;
+ if (public_key) {
+ payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
+ if (!payload->pk_data) {
+ silc_mp_clear(x);
+ silc_free(x);
+ silc_mp_clear(&e);
+ silc_free(payload);
+ ske->status = SILC_SKE_STATUS_OK;
+ return ske->status;
+ }
+ payload->pk_len = pk_len;
}
- payload->pk_len = pk_len;
payload->pk_type = SILC_SKE_PK_TYPE_SILC;
/* Compute signature data if we are doing mutual authentication */
- if (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+ if (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
unsigned char hash[32], sign[1024];
uint32 hash_len, sign_len;
silc_mp_powm(KEY, &payload->x, ske->x, &ske->prop->group->group);
ske->KEY = KEY;
- SILC_LOG_DEBUG(("Verifying public key"));
-
- if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len,
- &public_key)) {
- status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
- goto err;
+ if (payload->pk_data) {
+ SILC_LOG_DEBUG(("Verifying public key"));
+
+ if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len,
+ &public_key)) {
+ status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+ goto err;
+ }
}
if (verify_key) {
payload->pk_type, verify_context);
if (status != SILC_SKE_STATUS_OK)
goto err;
- }
-
- SILC_LOG_DEBUG(("Public key is authentic"));
- /* Compute the hash value */
- status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
+ SILC_LOG_DEBUG(("Public key is authentic"));
+ }
- ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
- memcpy(ske->hash, hash, hash_len);
- ske->hash_len = hash_len;
+ if (payload->pk_data) {
+ /* Compute the hash value */
+ status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+ if (status != SILC_SKE_STATUS_OK)
+ goto err;
- SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
+ ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
+ memcpy(ske->hash, hash, hash_len);
+ ske->hash_len = hash_len;
- /* Verify signature */
- silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk,
- public_key->pk_len);
- if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data,
- payload->sign_len, hash, hash_len) == FALSE) {
+ SILC_LOG_DEBUG(("Verifying signature (HASH)"));
- SILC_LOG_DEBUG(("Signature don't match"));
+ /* Verify signature */
+ silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk,
+ public_key->pk_len);
+ if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data,
+ payload->sign_len, hash, hash_len) == FALSE) {
+
+ SILC_LOG_DEBUG(("Signature don't match"));
+
+ status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+ goto err;
+ }
- status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
- goto err;
+ SILC_LOG_DEBUG(("Signature is Ok"));
+
+ silc_pkcs_public_key_free(public_key);
+ memset(hash, 'F', hash_len);
}
- SILC_LOG_DEBUG(("Signature is Ok"));
-
- silc_pkcs_public_key_free(public_key);
- memset(hash, 'F', hash_len);
-
/* Call the callback. */
if (callback)
(*callback)(ske, context);
}
/* This function receives the Key Exchange Payload from the initiator.
- After processing the payload this then selects random number x,
- such that 1 < x < q and computes f = g ^ x mod p. This then puts
- the result f to a Key Exchange Payload which is later processed
- in ske_responder_finish function. The callback function should
- not touch the payload (it should merely call the ske_responder_finish
- function). */
+ This also performs the mutual authentication if required. Then, this
+ function first generated a random number x, such that 1 < x < q
+ and computes f = g ^ x mod p. This then puts the result f to a Key
+ Exchange Payload. */
SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
SilcBuffer ke_payload,
/* Verify the received public key and verify the signature if we are
doing mutual authentication. */
- if (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+ if (ske->start_payload &&
+ ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
SilcPublicKey public_key = NULL;
unsigned char hash[32];
uint32 hash_len;
if (status != SILC_SKE_STATUS_OK)
return status;
- SILC_LOG_DEBUG(("Verifying signature"));
+ SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
/* Verify signature */
silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk,
return status;
}
-/* This function computes the secret shared key KEY = e ^ x mod p, and,
- a hash value to be signed and sent to the other end. This then
- encodes Key Exchange Payload and sends it to the other end. */
+/* This functions generates the secret key KEY = e ^ x mod p, and, a hash
+ value to be signed and sent to the other end. This then encodes Key
+ Exchange Payload and sends it to the other end. */
SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
SilcPublicKey public_key,
SILC_LOG_DEBUG(("Start"));
- if (!public_key || !private_key) {
- status = SILC_SKE_STATUS_ERROR;
- goto err;
- }
-
SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
/* Compute the shared secret key */
&ske->prop->group->group);
ske->KEY = KEY;
- SILC_LOG_DEBUG(("Getting public key"));
+ if (public_key && private_key) {
+ SILC_LOG_DEBUG(("Getting public key"));
+
+ /* Get the public key */
+ pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+ if (!pk) {
+ status = SILC_SKE_STATUS_ERROR;
+ goto err;
+ }
+ ske->ke2_payload->pk_data = pk;
+ ske->ke2_payload->pk_len = pk_len;
+
+ SILC_LOG_DEBUG(("Computing HASH value"));
+
+ /* Compute the hash value */
+ memset(hash, 0, sizeof(hash));
+ status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+ if (status != SILC_SKE_STATUS_OK)
+ goto err;
- /* Get the public key */
- pk = silc_pkcs_public_key_encode(public_key, &pk_len);
- if (!pk) {
- status = SILC_SKE_STATUS_ERROR;
- goto err;
+ ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
+ memcpy(ske->hash, hash, hash_len);
+ ske->hash_len = hash_len;
+
+ SILC_LOG_DEBUG(("Signing HASH value"));
+
+ /* Sign the hash value */
+ silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv,
+ private_key->prv_len);
+ silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len);
+ ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
+ memcpy(ske->ke2_payload->sign_data, sign, sign_len);
+ memset(sign, 0, sizeof(sign));
+ ske->ke2_payload->sign_len = sign_len;
}
- ske->ke2_payload->pk_data = pk;
- ske->ke2_payload->pk_len = pk_len;
ske->ke2_payload->pk_type = pk_type;
- SILC_LOG_DEBUG(("Computing HASH value"));
-
- /* Compute the hash value */
- memset(hash, 0, sizeof(hash));
- status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
-
- ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
- memcpy(ske->hash, hash, hash_len);
- ske->hash_len = hash_len;
-
- SILC_LOG_DEBUG(("Signing HASH value"));
-
- /* Sign the hash value */
- silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv,
- private_key->prv_len);
- silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len);
- ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
- memcpy(ske->ke2_payload->sign_data, sign, sign_len);
- memset(sign, 0, sizeof(sign));
- ske->ke2_payload->sign_len = sign_len;
-
/* Encode the Key Exchange Payload */
status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
&payload_buf);