From 73583bd1ba302719fa687b8fa6b7619205ac4f33 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Thu, 5 Apr 2001 14:53:19 +0000 Subject: [PATCH] updates. --- CHANGES | 4 + TODO | 6 - apps/silcd/idlist.h | 27 +- apps/silcd/packet_receive.c | 7 +- apps/silcd/protocol.c | 285 ++++++++++++---- apps/silcd/protocol.h | 1 - apps/silcd/server.c | 4 - doc/draft-riikonen-silc-ke-auth-02.nroff | 9 + doc/draft-riikonen-silc-spec-02.nroff | 7 + lib/silcclient/client.c | 4 - lib/silcclient/client.h | 17 +- lib/silcclient/protocol.c | 396 ++++++++++++++++++++++- lib/silcclient/protocol.h | 1 - lib/silccore/silcprotocol.c | 11 + lib/silccore/silcprotocol.h | 1 + lib/silcske/groups.c | 7 + lib/silcske/groups.h | 1 + lib/silcske/payload.c | 8 +- lib/silcske/silcske.c | 176 +++++----- 19 files changed, 785 insertions(+), 187 deletions(-) diff --git a/CHANGES b/CHANGES index 12b4ed67..737be54b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ Thu Apr 5 17:42:30 EEST 2001 Pekka Riikonen + * 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. @@ -21,6 +23,8 @@ Thu Apr 5 17:42:30 EEST 2001 Pekka Riikonen SilcClientConnection structure; not needed. Affected file is lib/silcclient/client.h. + * Updated TODO. + Wed Apr 4 16:32:31 EEST 2001 Pekka Riikonen * Do not ask whether user wants to use the negotiated private key diff --git a/TODO b/TODO index f69b4ef6..8de20df2 100644 --- a/TODO +++ b/TODO @@ -56,12 +56,6 @@ TODO/bugs In SILC Server 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, diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index 73035241..b55820a1 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -40,6 +40,18 @@ typedef struct { 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. @@ -58,11 +70,8 @@ typedef struct { 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; @@ -106,7 +115,7 @@ typedef struct { 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. @@ -147,7 +156,7 @@ struct SilcServerEntryStruct { SilcIDListDataStruct data; char *server_name; - int server_type; + uint8 server_type; SilcServerID *id; char *server_info; char *motd; @@ -257,7 +266,7 @@ typedef struct SilcChannelClientEntryStruct { 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). @@ -300,7 +309,7 @@ struct SilcClientEntryStruct { char *username; char *userinfo; SilcClientID *id; - int mode; + uint32 mode; long last_command; uint8 fast_command; diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 2d254cfd..46b0aac1 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -2036,6 +2036,7 @@ void silc_server_rekey(SilcServer server, { SilcProtocol protocol; SilcServerRekeyInternalContext *proto_ctx; + SilcIDListData idata = (SilcIDListData)sock->user_data; SILC_LOG_DEBUG(("Start")); @@ -2045,6 +2046,7 @@ void silc_server_rekey(SilcServer server, 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. */ @@ -2052,6 +2054,7 @@ void silc_server_rekey(SilcServer server, &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); } diff --git a/apps/silcd/protocol.c b/apps/silcd/protocol.c index d31955fe..53521be2 100644 --- a/apps/silcd/protocol.c +++ b/apps/silcd/protocol.c @@ -27,6 +27,7 @@ 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; @@ -61,6 +62,7 @@ int silc_server_protocol_ke_set_keys(SilcSKE ske, SilcPKCS pkcs, SilcHash hash, SilcHmac hmac, + SilcSKEDiffieHellmanGroup group, bool is_responder) { SilcUnknownEntry conn_data; @@ -71,9 +73,6 @@ int silc_server_protocol_ke_set_keys(SilcSKE ske, 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); @@ -100,11 +99,17 @@ int silc_server_protocol_ke_set_keys(SilcSKE ske, 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, @@ -994,6 +999,43 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth) * 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. */ @@ -1009,43 +1051,69 @@ void silc_server_protocol_rekey_generate(SilcServer server, /* 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) { @@ -1053,6 +1121,8 @@ 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")); @@ -1079,6 +1149,34 @@ SILC_TASK_CALLBACK(silc_server_protocol_rekey) * 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. @@ -1097,71 +1195,126 @@ SILC_TASK_CALLBACK(silc_server_protocol_rekey) * 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, diff --git a/apps/silcd/protocol.h b/apps/silcd/protocol.h index d0c2bba3..c309aabc 100644 --- a/apps/silcd/protocol.h +++ b/apps/silcd/protocol.h @@ -89,7 +89,6 @@ typedef struct { 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; diff --git a/apps/silcd/server.c b/apps/silcd/server.c index c2dd90f4..12769c44 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -3688,8 +3688,6 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final) 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) @@ -3707,8 +3705,6 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final) /* 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) diff --git a/doc/draft-riikonen-silc-ke-auth-02.nroff b/doc/draft-riikonen-silc-ke-auth-02.nroff index 0a63644b..56f3d0bd 100644 --- a/doc/draft-riikonen-silc-ke-auth-02.nroff +++ b/doc/draft-riikonen-silc-ke-auth-02.nroff @@ -402,6 +402,15 @@ two SILC clients. In normal case, where client is connecting to the 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. diff --git a/doc/draft-riikonen-silc-spec-02.nroff b/doc/draft-riikonen-silc-spec-02.nroff index 734a32c3..d945f2aa 100644 --- a/doc/draft-riikonen-silc-spec-02.nroff +++ b/doc/draft-riikonen-silc-spec-02.nroff @@ -1787,6 +1787,13 @@ and the protocol results to new key material. See [SILC3] for more 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 diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index 58053e9d..a43baa11 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -1612,8 +1612,6 @@ SILC_TASK_CALLBACK(silc_client_rekey_final) 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) @@ -1631,8 +1629,6 @@ SILC_TASK_CALLBACK(silc_client_rekey_final) /* 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) diff --git a/lib/silcclient/client.h b/lib/silcclient/client.h index 660aae65..b16e308f 100644 --- a/lib/silcclient/client.h +++ b/lib/silcclient/client.h @@ -32,6 +32,18 @@ typedef struct SilcClientKeyAgreementStruct *SilcClientKeyAgreement; #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 { @@ -74,8 +86,6 @@ 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 @@ -107,6 +117,9 @@ struct SilcClientConnectionStruct { /* 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; diff --git a/lib/silcclient/protocol.c b/lib/silcclient/protocol.c index 5255b67a..a21ea9c4 100644 --- a/lib/silcclient/protocol.c +++ b/lib/silcclient/protocol.c @@ -27,6 +27,7 @@ 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; @@ -49,7 +50,6 @@ void silc_client_protocol_ke_send_packet(SilcSKE ske, /* 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 @@ -85,7 +85,8 @@ void silc_client_protocol_ke_set_keys(SilcSKE ske, SilcCipher cipher, SilcPKCS pkcs, SilcHash hash, - SilcHmac hmac) + SilcHmac hmac, + SilcSKEDiffieHellmanGroup group) { SilcClientConnection conn = (SilcClientConnection)sock->user_data; @@ -114,6 +115,18 @@ void silc_client_protocol_ke_set_keys(SilcSKE ske, 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); @@ -612,6 +625,381 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth) } } +/* + * 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) @@ -620,6 +1008,8 @@ 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 */ @@ -630,4 +1020,6 @@ void silc_client_protocols_unregister(void) 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); } diff --git a/lib/silcclient/protocol.h b/lib/silcclient/protocol.h index fc73f6de..71e25069 100644 --- a/lib/silcclient/protocol.h +++ b/lib/silcclient/protocol.h @@ -81,7 +81,6 @@ typedef struct { 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; diff --git a/lib/silccore/silcprotocol.c b/lib/silccore/silcprotocol.c index 38ca3c9d..9eb50898 100644 --- a/lib/silccore/silcprotocol.c +++ b/lib/silccore/silcprotocol.c @@ -144,3 +144,14 @@ void silc_protocol_execute_final(void *qptr, int type, 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); +} diff --git a/lib/silccore/silcprotocol.h b/lib/silccore/silcprotocol.h index 8336745d..534aa493 100644 --- a/lib/silccore/silcprotocol.h +++ b/lib/silccore/silcprotocol.h @@ -124,5 +124,6 @@ void silc_protocol_execute(void *qptr, int type, 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 diff --git a/lib/silcske/groups.c b/lib/silcske/groups.c index 2648f165..fd664a06 100644 --- a/lib/silcske/groups.c +++ b/lib/silcske/groups.c @@ -160,3 +160,10 @@ char *silc_ske_get_supported_groups() return list; } + +/* Returns the number of the `group'. */ + +int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group) +{ + return group->number; +} diff --git a/lib/silcske/groups.h b/lib/silcske/groups.h index 29e33be4..eacd3f25 100644 --- a/lib/silcske/groups.h +++ b/lib/silcske/groups.h @@ -36,5 +36,6 @@ SilcSKEStatus silc_ske_get_group_by_number(int 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 diff --git a/lib/silcske/payload.c b/lib/silcske/payload.c index fe03125f..835613ae 100644 --- a/lib/silcske/payload.c +++ b/lib/silcske/payload.c @@ -196,7 +196,8 @@ SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske, 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; @@ -273,7 +274,7 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske, goto err; } - if (payload->pk_len < 5) { + if (payload->pk_type == 0) { status = SILC_SKE_STATUS_BAD_PAYLOAD; goto err; } @@ -302,7 +303,8 @@ SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske, 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")); diff --git a/lib/silcske/silcske.c b/lib/silcske/silcske.c index 4d73f240..852ad4fd 100644 --- a/lib/silcske/silcske.c +++ b/lib/silcske/silcske.c @@ -263,20 +263,22 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske, 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; @@ -357,12 +359,14 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske, 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) { @@ -370,38 +374,40 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske, 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); @@ -585,12 +591,10 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske, } /* 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, @@ -616,7 +620,8 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske, /* 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; @@ -645,7 +650,7 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske, 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, @@ -698,9 +703,9 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske, 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, @@ -717,11 +722,6 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske, 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 */ @@ -731,41 +731,43 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske, &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); -- 2.24.0