/*
- silcske.c
+ silcske.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2000 - 2001 Pekka Riikonen
+ Copyright (C) 2000 - 2002 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
+ the Free Software Foundation; version 2 of the License.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#include "silcske.h"
#include "groups_internal.h"
-/* Structure to hold all SKE callbacks-> */
+/* Static functions */
+static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
+ SilcUInt32 len,
+ SilcMPInt *rnd);
+static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
+ unsigned char *return_hash,
+ SilcUInt32 *return_hash_len,
+ int initiator);
+
+/* Structure to hold all SKE callbacks. */
struct SilcSKECallbacksStruct {
SilcSKESendPacketCb send_packet;
SilcSKECb payload_receive;
/* Allocates new SKE object. */
-SilcSKE silc_ske_alloc()
+SilcSKE silc_ske_alloc(SilcRng rng, void *context)
{
SilcSKE ske;
SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
ske = silc_calloc(1, sizeof(*ske));
+ if (!ske)
+ return NULL;
ske->status = SILC_SKE_STATUS_OK;
+ ske->rng = rng;
+ ske->user_data = context;
ske->users = 1;
return ske;
/* Free KE payload */
if (ske->ke1_payload)
silc_ske_payload_ke_free(ske->ke1_payload);
+ if (ske->ke2_payload)
+ silc_ske_payload_ke_free(ske->ke2_payload);
+ silc_free(ske->remote_version);
/* Free rest */
if (ske->prop) {
if (ske->prop->group)
- silc_free(ske->prop->group);
+ silc_ske_group_free(ske->prop->group);
if (ske->prop->pkcs)
silc_pkcs_free(ske->prop->pkcs);
if (ske->prop->cipher)
}
if (ske->start_payload_copy)
silc_buffer_free(ske->start_payload_copy);
- if (ske->pk)
- silc_free(ske->pk);
if (ske->x) {
silc_mp_uninit(ske->x);
silc_free(ske->x);
silc_mp_uninit(ske->KEY);
silc_free(ske->KEY);
}
- if (ske->hash)
- silc_free(ske->hash);
+ silc_free(ske->hash);
+ silc_free(ske->callbacks);
silc_free(ske);
}
}
if (ske->callbacks)
silc_free(ske->callbacks);
ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks));
+ if (!ske->callbacks)
+ return;
ske->callbacks->send_packet = send_packet;
ske->callbacks->payload_receive = payload_receive;
ske->callbacks->verify_key = verify_key;
configured security properties. This payload is then sent to the
remote end for further processing. This payload must be sent as
argument to the function, however, it must not be encoded
- already, it is done by this function.
+ already, it is done by this function. The caller must not free
+ the `start_payload' since the SKE library will save it.
The packet sending is done by calling a callback function. Caller
must provide a routine to send the packet. */
/* Take a copy of the payload buffer for future use. It is used to
compute the HASH value. */
ske->start_payload_copy = silc_buffer_copy(payload_buf);
+ ske->start_payload = start_payload;
/* Send the packet. */
if (ske->callbacks->send_packet)
(*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE,
- ske->callbacks->context);
+ ske->callbacks->context);
silc_buffer_free(payload_buf);
status = silc_ske_payload_start_decode(ske, start_payload, &payload);
if (status != SILC_SKE_STATUS_OK) {
ske->status = status;
+ silc_ske_payload_start_free(ske->start_payload);
+ return status;
+ }
+
+ /* Check that the cookie is returned unmodified */
+ if (memcmp(ske->start_payload->cookie, payload->cookie,
+ ske->start_payload->cookie_len)) {
+ SILC_LOG_ERROR(("Responder modified our cookie and it must not do it"));
+ ske->status = SILC_SKE_STATUS_INVALID_COOKIE;
+ silc_ske_payload_start_free(ske->start_payload);
return status;
}
+ /* Check version string */
+ if (ske->callbacks->check_version) {
+ status = ske->callbacks->check_version(ske, payload->version,
+ payload->version_len,
+ ske->callbacks->context);
+ if (status != SILC_SKE_STATUS_OK) {
+ ske->status = status;
+ silc_ske_payload_start_free(ske->start_payload);
+ return status;
+ }
+ }
+
+ /* Free our KE Start Payload context, we don't need it anymore. */
+ silc_ske_payload_start_free(ske->start_payload);
+
/* Take the selected security properties into use while doing
the key exchange. This is used only while doing the key
exchange. The same data is returned to upper levels by calling
the callback function. */
ske->prop = prop = silc_calloc(1, sizeof(*prop));
+ if (!ske->prop)
+ goto err;
prop->flags = payload->flags;
- status = silc_ske_get_group_by_name(payload->ke_grp_list, &group);
+ status = silc_ske_group_get_by_name(payload->ke_grp_list, &group);
if (status != SILC_SKE_STATUS_OK)
goto err;
goto err;
}
+ /* Save remote's KE Start Payload */
ske->start_payload = payload;
/* Return the received payload by calling the callback function. */
if (payload)
silc_ske_payload_start_free(payload);
- silc_free(group);
+ silc_ske_group_free(group);
if (prop->pkcs)
silc_pkcs_free(prop->pkcs);
SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
SilcPublicKey public_key,
- SilcPrivateKey private_key)
+ SilcPrivateKey private_key,
+ SilcSKEPKType pk_type)
{
SilcSKEStatus status = SILC_SKE_STATUS_OK;
SilcBuffer payload_buf;
- SilcMPInt *x, e;
+ SilcMPInt *x;
SilcSKEKEPayload *payload;
- uint32 pk_len;
+ SilcUInt32 pk_len;
SILC_LOG_DEBUG(("Start"));
/* Create the random number x, 1 < x < q. */
x = silc_calloc(1, sizeof(*x));
+ if (!x){
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return ske->status;
+ }
silc_mp_init(x);
status =
- silc_ske_create_rnd(ske, ske->prop->group->group_order,
+ silc_ske_create_rnd(ske, &ske->prop->group->group_order,
silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
x);
if (status != SILC_SKE_STATUS_OK) {
return status;
}
- SILC_LOG_DEBUG(("Computing e = g ^ x mod p"));
-
- /* Do the Diffie Hellman computation, e = g ^ x mod p */
- silc_mp_init(&e);
- silc_mp_pow_mod(&e, &ske->prop->group->generator, x,
- &ske->prop->group->group);
-
/* Encode the result to Key Exchange Payload. */
payload = silc_calloc(1, sizeof(*payload));
+ if (!payload) {
+ silc_mp_uninit(x);
+ silc_free(x);
+ ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return ske->status;
+ }
ske->ke1_payload = payload;
- payload->x = e;
+ SILC_LOG_DEBUG(("Computing e = g ^ x mod p"));
+
+ /* Do the Diffie Hellman computation, e = g ^ x mod p */
+ silc_mp_init(&payload->x);
+ silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x,
+ &ske->prop->group->group);
/* Get public key */
if (public_key) {
if (!payload->pk_data) {
silc_mp_uninit(x);
silc_free(x);
- silc_mp_uninit(&e);
+ silc_mp_uninit(&payload->x);
silc_free(payload);
ske->status = SILC_SKE_STATUS_OK;
return ske->status;
}
payload->pk_len = pk_len;
}
- payload->pk_type = SILC_SKE_PK_TYPE_SILC;
+ payload->pk_type = pk_type;
/* Compute signature data if we are doing mutual authentication */
if (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
- unsigned char hash[32], sign[1024];
- uint32 hash_len, sign_len;
+ unsigned char hash[32], sign[2048];
+ SilcUInt32 hash_len, sign_len;
SILC_LOG_DEBUG(("We are doing mutual authentication"));
SILC_LOG_DEBUG(("Computing HASH_i 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);
+ if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
+ !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
+ silc_mp_uninit(x);
+ silc_free(x);
+ silc_mp_uninit(&payload->x);
+ silc_free(payload->pk_data);
+ silc_free(payload);
+ ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR;
+ return ske->status;
+ }
payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
memcpy(payload->sign_data, sign, sign_len);
memset(sign, 0, sizeof(sign));
if (status != SILC_SKE_STATUS_OK) {
silc_mp_uninit(x);
silc_free(x);
- silc_mp_uninit(&e);
+ silc_mp_uninit(&payload->x);
silc_free(payload->pk_data);
silc_free(payload);
ske->status = status;
/* Send the packet. */
if (ske->callbacks->send_packet)
(*ske->callbacks->send_packet)(ske, payload_buf,
- SILC_PACKET_KEY_EXCHANGE_1,
- ske->callbacks->context);
+ SILC_PACKET_KEY_EXCHANGE_1,
+ ske->callbacks->context);
silc_buffer_free(payload_buf);
{
SilcSKEKEPayload *payload;
unsigned char hash[32];
- uint32 hash_len;
+ SilcUInt32 hash_len;
SilcPublicKey public_key = NULL;
/* If the SKE was freed during the async call then free it really now,
if (ske->status == SILC_SKE_STATUS_FREED) {
silc_ske_free(ske);
return;
- } else {
- ske->users--;
}
- payload = ske->ke2_payload;
-
/* If the caller returns PENDING status SKE library will assume that
the caller will re-call this callback when it is not anymore in
PENDING status. */
if (status == SILC_SKE_STATUS_PENDING)
return;
+ ske->users--;
+ payload = ske->ke2_payload;
+
/* If the status is an error then the public key that was verified
by the caller is not authentic. */
if (status != SILC_SKE_STATUS_OK) {
if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len,
&public_key)) {
status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+ SILC_LOG_ERROR(("Unsupported/malformed public key received"));
if (ske->callbacks->proto_continue)
ske->callbacks->proto_continue(ske, ske->callbacks->context);
return;
silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data,
payload->sign_len, hash, hash_len) == FALSE) {
-
- SILC_LOG_DEBUG(("Signature don't match"));
-
+ SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
goto err;
}
public key, f, and signature. This function verifies the public key,
computes the secret shared key and verifies the signature.
- The `callback' will be called to indicate that the caller may
+ The `proto_continue' will be called to indicate that the caller may
continue with the SKE protocol. The caller must not continue
before the SKE libary has called that callback. If this function
returns an error the callback will not be called. It is called
}
/* Continue to final state */
+ ske->users++;
silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, NULL);
return SILC_SKE_STATUS_OK;
SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
SilcSocketConnection sock,
- char *version,
+ const char *version,
SilcBuffer start_payload,
- bool mutual_auth)
+ SilcSKESecurityPropertyFlag flags)
{
SilcSKEStatus status = SILC_SKE_STATUS_OK;
SilcSKEStartPayload *remote_payload = NULL, *payload = NULL;
ske->start_payload_copy = silc_buffer_copy(start_payload);
/* Force the mutual authentication flag if we want to do it. */
- if (mutual_auth) {
+ if (flags & SILC_SKE_SP_FLAG_MUTUAL) {
SILC_LOG_DEBUG(("Force mutual authentication"));
remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL;
}
+ /* Force PFS flag if we require it */
+ if (flags & SILC_SKE_SP_FLAG_PFS) {
+ SILC_LOG_DEBUG(("Force PFS"));
+ remote_payload->flags |= SILC_SKE_SP_FLAG_PFS;
+ }
+
/* Parse and select the security properties from the payload */
payload = silc_calloc(1, sizeof(*payload));
status = silc_ske_select_security_properties(ske, version,
if (ske->callbacks->payload_receive)
(*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
+ silc_ske_payload_start_free(remote_payload);
+
return status;
err:
/* The selected security properties from the initiator payload is now
encoded into Key Exchange Start Payload and sent to the initiator. */
-SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
- SilcSKEStartPayload *start_payload)
+SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
{
SilcSKEStatus status = SILC_SKE_STATUS_OK;
SilcBuffer payload_buf;
/* Allocate security properties from the payload. These are allocated
only for this negotiation and will be free'd after KE is over. */
ske->prop = prop = silc_calloc(1, sizeof(*prop));
- prop->flags = start_payload->flags;
- status = silc_ske_get_group_by_name(start_payload->ke_grp_list, &group);
+ prop->flags = ske->start_payload->flags;
+ status = silc_ske_group_get_by_name(ske->start_payload->ke_grp_list, &group);
if (status != SILC_SKE_STATUS_OK)
goto err;
prop->group = group;
- if (silc_pkcs_alloc(start_payload->pkcs_alg_list,
+ if (silc_pkcs_alloc(ske->start_payload->pkcs_alg_list,
&prop->pkcs) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_PKCS;
goto err;
}
- if (silc_cipher_alloc(start_payload->enc_alg_list,
+ if (silc_cipher_alloc(ske->start_payload->enc_alg_list,
&prop->cipher) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
goto err;
}
- if (silc_hash_alloc(start_payload->hash_alg_list,
+ if (silc_hash_alloc(ske->start_payload->hash_alg_list,
&prop->hash) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
goto err;
}
- if (silc_hmac_alloc(start_payload->hmac_alg_list, NULL,
+ if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL,
&prop->hmac) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_HMAC;
goto err;
}
/* Encode the payload */
- status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
+ status = silc_ske_payload_start_encode(ske, ske->start_payload,
+ &payload_buf);
if (status != SILC_SKE_STATUS_OK)
goto err;
/* Send the packet. */
if (ske->callbacks->send_packet)
(*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE,
- ske->callbacks->context);
+ ske->callbacks->context);
silc_buffer_free(payload_buf);
err:
if (group)
- silc_free(group);
+ silc_ske_group_free(group);
if (prop->pkcs)
silc_pkcs_free(prop->pkcs);
void *context)
{
SilcSKEKEPayload *recv_payload, *send_payload;
- SilcMPInt *x, f;
+ SilcMPInt *x;
/* If the SKE was freed during the async call then free it really now,
otherwise just decrement the reference counter. */
if (ske->status == SILC_SKE_STATUS_FREED) {
silc_ske_free(ske);
return;
- } else {
- ske->users--;
}
- recv_payload = ske->ke1_payload;
-
/* If the caller returns PENDING status SKE library will assume that
the caller will re-call this callback when it is not anymore in
PENDING status. */
if (status == SILC_SKE_STATUS_PENDING)
return;
+ ske->users--;
+ recv_payload = ske->ke1_payload;
+
/* If the status is an error then the public key that was verified
by the caller is not authentic. */
if (status != SILC_SKE_STATUS_OK) {
ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
SilcPublicKey public_key = NULL;
unsigned char hash[32];
- uint32 hash_len;
+ SilcUInt32 hash_len;
/* Decode the public key */
if (!silc_pkcs_public_key_decode(recv_payload->pk_data,
recv_payload->pk_len,
&public_key)) {
ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+ SILC_LOG_ERROR(("Unsupported/malformed public key received"));
if (ske->callbacks->proto_continue)
ske->callbacks->proto_continue(ske, ske->callbacks->context);
return;
silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
if (silc_pkcs_verify(ske->prop->pkcs, recv_payload->sign_data,
recv_payload->sign_len, hash, hash_len) == FALSE) {
-
- SILC_LOG_DEBUG(("Signature don't match"));
-
+ SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
if (ske->callbacks->proto_continue)
ske->callbacks->proto_continue(ske, ske->callbacks->context);
x = silc_calloc(1, sizeof(*x));
silc_mp_init(x);
status =
- silc_ske_create_rnd(ske, ske->prop->group->group_order,
+ silc_ske_create_rnd(ske, &ske->prop->group->group_order,
silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
x);
if (status != SILC_SKE_STATUS_OK) {
return;
}
- SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
-
- /* Do the Diffie Hellman computation, f = g ^ x mod p */
- silc_mp_init(&f);
- silc_mp_pow_mod(&f, &ske->prop->group->generator, x,
- &ske->prop->group->group);
-
/* Save the results for later processing */
send_payload = silc_calloc(1, sizeof(*send_payload));
- send_payload->x = f;
ske->x = x;
ske->ke2_payload = send_payload;
+ SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
+
+ /* Do the Diffie Hellman computation, f = g ^ x mod p */
+ silc_mp_init(&send_payload->x);
+ silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x,
+ &ske->prop->group->group);
+
/* Call the callback. The caller may now continue with the SKE protocol. */
ske->status = SILC_SKE_STATUS_OK;
if (ske->callbacks->proto_continue)
and computes f = g ^ x mod p. This then puts the result f to a Key
Exchange Payload.
- The `callback' will be called to indicate that the caller may
+ The `proto_continue' will be called to indicate that the caller may
continue with the SKE protocol. The caller must not continue
before the SKE libary has called that callback. If this function
returns an error the callback will not be called. It is called
SILC_LOG_DEBUG(("We are doing mutual authentication"));
if (!recv_payload->pk_data && ske->callbacks->verify_key) {
- SILC_LOG_DEBUG(("Remote end did not send its public key (or "
+ SILC_LOG_ERROR(("Remote end did not send its public key (or "
"certificate), even though we require it"));
ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
return status;
ske->users++;
(*ske->callbacks->verify_key)(ske, recv_payload->pk_data,
- recv_payload->pk_len,
- recv_payload->pk_type,
- ske->callbacks->context,
- silc_ske_responder_phase2_final, NULL);
+ recv_payload->pk_len,
+ recv_payload->pk_type,
+ ske->callbacks->context,
+ silc_ske_responder_phase2_final, NULL);
/* We will continue to the final state after the public key has
been verified by the caller. */
}
/* Continue to final state */
+ ske->users++;
silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, NULL);
return SILC_SKE_STATUS_OK;
SilcSKEStatus status = SILC_SKE_STATUS_OK;
SilcBuffer payload_buf;
SilcMPInt *KEY;
- unsigned char hash[32], sign[1024], *pk;
- uint32 hash_len, sign_len, pk_len;
+ unsigned char hash[32], sign[2048], *pk;
+ SilcUInt32 hash_len, sign_len, pk_len;
SILC_LOG_DEBUG(("Start"));
/* Get the public key */
pk = silc_pkcs_public_key_encode(public_key, &pk_len);
if (!pk) {
- status = SILC_SKE_STATUS_ERROR;
+ status = SILC_SKE_STATUS_OUT_OF_MEMORY;
goto err;
}
ske->ke2_payload->pk_data = pk;
/* 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);
+ if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
+ !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
+ status = SILC_SKE_STATUS_SIGNATURE_ERROR;
+ goto err;
+ }
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));
/* Send the packet. */
if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE_2,
- ske->callbacks->context);
+ (*ske->callbacks->send_packet)(ske, payload_buf,
+ SILC_PACKET_KEY_EXCHANGE_2,
+ ske->callbacks->context);
silc_buffer_free(payload_buf);
SILC_LOG_DEBUG(("Start"));
- packet = silc_buffer_alloc(4);
- silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ packet = silc_buffer_alloc_size(4);
+ if (!packet)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
silc_buffer_format(packet,
- SILC_STR_UI_INT((uint32)SILC_SKE_STATUS_OK),
+ SILC_STR_UI_INT((SilcUInt32)SILC_SKE_STATUS_OK),
SILC_STR_END);
if (ske->callbacks->send_packet)
(*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_SUCCESS,
- ske->callbacks->context);
+ ske->callbacks->context);
silc_buffer_free(packet);
SILC_LOG_DEBUG(("Start"));
- packet = silc_buffer_alloc(4);
- silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ if (status > SILC_SKE_STATUS_INVALID_COOKIE)
+ status = SILC_SKE_STATUS_BAD_PAYLOAD;
+
+ packet = silc_buffer_alloc_size(4);
+ if (!packet)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
silc_buffer_format(packet,
- SILC_STR_UI_INT((uint32)status),
+ SILC_STR_UI_INT((SilcUInt32)status),
SILC_STR_END);
if (ske->callbacks->send_packet)
SilcSKEStatus
silc_ske_assemble_security_properties(SilcSKE ske,
- unsigned char flags,
- char *version,
+ SilcSKESecurityPropertyFlag flags,
+ const char *version,
SilcSKEStartPayload **return_payload)
{
SilcSKEStartPayload *rp;
rp = silc_calloc(1, sizeof(*rp));
/* Set flags */
- rp->flags = flags;
+ rp->flags = (unsigned char)flags;
/* Set random cookie */
rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie));
/* XXX */
/* Get supported compression algorithms */
- rp->comp_alg_list = "";
+ rp->comp_alg_list = strdup("");
rp->comp_alg_len = 0;
rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
SilcSKEStatus
silc_ske_select_security_properties(SilcSKE ske,
- char *version,
+ const char *version,
SilcSKEStartPayload *payload,
SilcSKEStartPayload *remote_payload)
{
}
}
+ ske->remote_version = silc_memdup(rp->version, rp->version_len);
+
/* Flags are returned unchanged. */
payload->flags = rp->flags;
- /* Take cookie */
+ /* Take cookie, we must return it to sender unmodified. */
payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
payload->cookie_len = SILC_SKE_COOKIE_LEN;
memcpy(payload->cookie, rp->cookie, SILC_SKE_COOKIE_LEN);
SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
- if (silc_ske_get_group_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
+ if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
SILC_LOG_DEBUG(("Found KE group `%s'", item));
payload->ke_grp_len = len;
/* Creates random number such that 1 < rnd < n and at most length
of len bits. The rnd sent as argument must be initialized. */
-SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt n,
- uint32 len,
- SilcMPInt *rnd)
+static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
+ SilcUInt32 len,
+ SilcMPInt *rnd)
{
SilcSKEStatus status = SILC_SKE_STATUS_OK;
unsigned char *string;
SILC_LOG_DEBUG(("Creating random number"));
/* Get the random number as string */
- string = silc_rng_get_rn_data(ske->rng, (len / 8));
+ string = silc_rng_get_rn_data(ske->rng, ((len - 1) / 8));
if (!string)
- return SILC_SKE_STATUS_ERROR;
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
/* Decode the string into a MP integer */
- silc_mp_bin2mp(string, (len / 8), rnd);
+ silc_mp_bin2mp(string, ((len - 1) / 8), rnd);
silc_mp_mod_2exp(rnd, rnd, len);
/* Checks */
if (silc_mp_cmp_ui(rnd, 1) < 0)
status = SILC_SKE_STATUS_ERROR;
-
- if (silc_mp_cmp(rnd, &n) >= 0)
+ if (silc_mp_cmp(rnd, n) >= 0)
status = SILC_SKE_STATUS_ERROR;
memset(string, 'F', (len / 8));
hash value defined in the protocol. If it is FALSE then this is used
to create the HASH value defined by the protocol. */
-SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
- unsigned char *return_hash,
- uint32 *return_hash_len,
- int initiator)
+static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
+ unsigned char *return_hash,
+ SilcUInt32 *return_hash_len,
+ int initiator)
{
SilcSKEStatus status = SILC_SKE_STATUS_OK;
SilcBuffer buf;
unsigned char *e, *f, *KEY;
- uint32 e_len, f_len, KEY_len;
+ SilcUInt32 e_len, f_len, KEY_len;
int ret;
SILC_LOG_DEBUG(("Start"));
f = silc_mp_mp2bin(&ske->ke2_payload->x, 0, &f_len);
KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
- buf = silc_buffer_alloc(ske->start_payload_copy->len +
- ske->pk_len + e_len + f_len + KEY_len);
- silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
-
/* Format the buffer used to compute the hash value */
- ret =
- silc_buffer_format(buf,
- SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
- ske->start_payload_copy->len),
- SILC_STR_UI_XNSTRING(ske->pk, ske->pk_len),
- SILC_STR_UI_XNSTRING(e, e_len),
- SILC_STR_UI_XNSTRING(f, f_len),
- SILC_STR_UI_XNSTRING(KEY, KEY_len),
- SILC_STR_END);
+ buf = silc_buffer_alloc_size(ske->start_payload_copy->len +
+ ske->ke2_payload->pk_len +
+ ske->ke1_payload->pk_len +
+ e_len + f_len + KEY_len);
+ if (!buf)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
+
+ /* Initiator is not required to send its public key */
+ if (!ske->ke1_payload->pk_data) {
+ ret =
+ silc_buffer_format(buf,
+ SILC_STR_UI_XNSTRING(ske->start_payload_copy->
+ data,
+ ske->start_payload_copy->
+ len),
+ SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
+ ske->ke2_payload->pk_len),
+ SILC_STR_UI_XNSTRING(e, e_len),
+ SILC_STR_UI_XNSTRING(f, f_len),
+ SILC_STR_UI_XNSTRING(KEY, KEY_len),
+ SILC_STR_END);
+ } else {
+ ret =
+ silc_buffer_format(buf,
+ SILC_STR_UI_XNSTRING(ske->start_payload_copy->
+ data,
+ ske->start_payload_copy->
+ len),
+ SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
+ ske->ke2_payload->pk_len),
+ SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
+ ske->ke1_payload->pk_len),
+ SILC_STR_UI_XNSTRING(e, e_len),
+ SILC_STR_UI_XNSTRING(f, f_len),
+ SILC_STR_UI_XNSTRING(KEY, KEY_len),
+ SILC_STR_END);
+ }
if (ret == -1) {
silc_buffer_free(buf);
memset(e, 0, e_len);
} else {
e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
- buf = silc_buffer_alloc(ske->start_payload_copy->len +
- ske->pk_len + e_len);
- silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+ buf = silc_buffer_alloc_size(ske->start_payload_copy->len +
+ ske->ke1_payload->pk_len + e_len);
+ if (!buf)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
/* Format the buffer used to compute the hash value */
ret =
silc_buffer_format(buf,
SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
ske->start_payload_copy->len),
- SILC_STR_UI_XNSTRING(ske->pk, ske->pk_len),
+ SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
+ ske->ke1_payload->pk_len),
SILC_STR_UI_XNSTRING(e, e_len),
SILC_STR_END);
if (ret == -1) {
/* Make the hash */
silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
- *return_hash_len = ske->prop->hash->hash->hash_len;
+ *return_hash_len = silc_hash_len(ske->prop->hash);
if (initiator == FALSE) {
SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len);
}
/* Processes the provided key material `data' as the SILC protocol
- specification specifies. */
+ specification defines. */
SilcSKEStatus
silc_ske_process_key_material_data(unsigned char *data,
- uint32 data_len,
- uint32 req_iv_len,
- uint32 req_enc_key_len,
- uint32 req_hmac_key_len,
+ SilcUInt32 data_len,
+ SilcUInt32 req_iv_len,
+ SilcUInt32 req_enc_key_len,
+ SilcUInt32 req_hmac_key_len,
SilcHash hash,
SilcSKEKeyMaterial *key)
{
SilcBuffer buf;
unsigned char hashd[32];
- uint32 hash_len = req_hmac_key_len;
- uint32 enc_key_len = req_enc_key_len / 8;
+ SilcUInt32 hash_len = req_hmac_key_len;
+ SilcUInt32 enc_key_len = req_enc_key_len / 8;
SILC_LOG_DEBUG(("Start"));
if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
return SILC_SKE_STATUS_ERROR;
- buf = silc_buffer_alloc(1 + data_len);
- silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+ buf = silc_buffer_alloc_size(1 + data_len);
+ if (!buf)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
silc_buffer_format(buf,
SILC_STR_UI_CHAR(0),
SILC_STR_UI_XNSTRING(data, data_len),
silc_hash_make(hash, buf->data, buf->len, k1);
/* Take second round */
- dist = silc_buffer_alloc(data_len + hash_len);
- silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
+ dist = silc_buffer_alloc_size(data_len + hash_len);
+ if (!dist)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
silc_buffer_format(dist,
SILC_STR_UI_XNSTRING(data, data_len),
SILC_STR_UI_XNSTRING(k1, hash_len),
SILC_STR_END);
memset(k2, 0, sizeof(k2));
silc_hash_make(hash, dist->data, dist->len, k2);
-
+
/* Take third round */
dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
silc_buffer_pull_tail(dist, hash_len);
dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
memcpy(dtmp, k1, hash_len);
memcpy(dtmp + hash_len, k2, hash_len);
- memcpy(dtmp + hash_len, k3, hash_len);
+ memcpy(dtmp + hash_len + hash_len, k3, hash_len);
key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
memcpy(key->send_enc_key, dtmp, enc_key_len);
memset(k2, 0, sizeof(k2));
memset(k3, 0, sizeof(k3));
silc_free(dtmp);
+ silc_buffer_clear(dist);
silc_buffer_free(dist);
} else {
/* Take normal hash as key */
silc_hash_make(hash, buf->data, buf->len, k1);
/* Take second round */
- dist = silc_buffer_alloc(data_len + hash_len);
- silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
+ dist = silc_buffer_alloc_size(data_len + hash_len);
+ if (!dist)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
silc_buffer_format(dist,
SILC_STR_UI_XNSTRING(data, data_len),
SILC_STR_UI_XNSTRING(k1, hash_len),
dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
memcpy(dtmp, k1, hash_len);
memcpy(dtmp + hash_len, k2, hash_len);
- memcpy(dtmp + hash_len, k3, hash_len);
+ memcpy(dtmp + hash_len + hash_len, k3, hash_len);
key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
memcpy(key->receive_enc_key, dtmp, enc_key_len);
memset(k2, 0, sizeof(k2));
memset(k3, 0, sizeof(k3));
silc_free(dtmp);
+ silc_buffer_clear(dist);
silc_buffer_free(dist);
} else {
/* Take normal hash as key */
key->enc_key_len = req_enc_key_len;
}
- /* Take HMAC key */
+ /* Take HMAC keys */
memset(hashd, 0, sizeof(hashd));
buf->data[0] = 4;
silc_hash_make(hash, buf->data, buf->len, hashd);
- key->hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
- memcpy(key->hmac_key, hashd, req_hmac_key_len);
+ key->send_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
+ memcpy(key->send_hmac_key, hashd, req_hmac_key_len);
+ memset(hashd, 0, sizeof(hashd));
+ buf->data[0] = 5;
+ silc_hash_make(hash, buf->data, buf->len, hashd);
+ key->receive_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
+ memcpy(key->receive_hmac_key, hashd, req_hmac_key_len);
key->hmac_key_len = req_hmac_key_len;
+ memset(hashd, 0, sizeof(hashd));
+ silc_buffer_clear(buf);
silc_buffer_free(buf);
return SILC_SKE_STATUS_OK;
the actual keys to be used in the SILC. */
SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
- uint32 req_iv_len,
- uint32 req_enc_key_len,
- uint32 req_hmac_key_len,
+ SilcUInt32 req_iv_len,
+ SilcUInt32 req_enc_key_len,
+ SilcUInt32 req_hmac_key_len,
SilcSKEKeyMaterial *key)
{
SilcSKEStatus status;
SilcBuffer buf;
unsigned char *tmpbuf;
- uint32 klen;
+ SilcUInt32 klen;
/* Encode KEY to binary data */
tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
- buf = silc_buffer_alloc(klen + ske->hash_len);
- silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+ buf = silc_buffer_alloc_size(klen + ske->hash_len);
+ if (!buf)
+ return SILC_SKE_STATUS_OUT_OF_MEMORY;
silc_buffer_format(buf,
SILC_STR_UI_XNSTRING(tmpbuf, klen),
SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
memset(tmpbuf, 0, klen);
silc_free(tmpbuf);
+ silc_buffer_clear(buf);
silc_buffer_free(buf);
return status;
memset(key->receive_enc_key, 0, key->enc_key_len / 8);
silc_free(key->receive_enc_key);
}
- if (key->hmac_key) {
- memset(key->hmac_key, 0, key->hmac_key_len);
- silc_free(key->hmac_key);
+ if (key->send_hmac_key) {
+ memset(key->send_hmac_key, 0, key->hmac_key_len);
+ silc_free(key->send_hmac_key);
+ }
+ if (key->receive_hmac_key) {
+ memset(key->receive_hmac_key, 0, key->hmac_key_len);
+ silc_free(key->receive_hmac_key);
}
silc_free(key);
}
+
+const char *silc_ske_status_string[] =
+{
+ /* Official */
+ "Ok",
+ "Unkown error occurred",
+ "Bad payload in packet",
+ "Unsupported group",
+ "Unsupported cipher",
+ "Unsupported PKCS",
+ "Unsupported hash function",
+ "Unsupported HMAC",
+ "Unsupported public key (or certificate)",
+ "Incorrect signature",
+ "Bad or unsupported version",
+ "Invalid cookie",
+
+ /* Other errors */
+ "Pending",
+ "Remote did not provide public key",
+ "Key exchange protocol is not active",
+ "Bad reserved field in packet",
+ "Bad payload length in packet",
+ "Error computing signature",
+ "System out of memory",
+
+ NULL
+};
+
+/* Maps status to readable string and returns the string. If string is not
+ found and empty character string ("") is returned. */
+
+const char *silc_ske_map_status(SilcSKEStatus status)
+{
+ int i;
+
+ for (i = 0; silc_ske_status_string[i]; i++)
+ if (status == i)
+ return silc_ske_status_string[i];
+
+ return "";
+}
+
+/* Parses remote host's version string. */
+
+bool silc_ske_parse_version(SilcSKE ske,
+ SilcUInt32 *protocol_version,
+ char **protocol_version_string,
+ SilcUInt32 *software_version,
+ char **software_version_string,
+ char **vendor_version)
+{
+ return silc_parse_version_string(ske->remote_version,
+ protocol_version,
+ protocol_version_string,
+ software_version,
+ software_version_string,
+ vendor_version);
+}