silcske.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
Copyright (C) 2000 - 2001 Pekka Riikonen
#include "silcske.h"
#include "groups_internal.h"
-/* Structure to hold all SKE callbacks-> */
+/* Structure to hold all SKE callbacks. */
struct SilcSKECallbacksStruct {
SilcSKESendPacketCb send_packet;
SilcSKECb payload_receive;
/* 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);
/* 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);
}
}
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_DEBUG(("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));
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 status = SILC_SKE_STATUS_OK;
SilcBuffer payload_buf;
- SilcMPInt *x, e;
+ SilcMPInt *x;
SilcSKEKEPayload *payload;
uint32 pk_len;
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 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));
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;
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);
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) {
}
/* Continue to final state */
+ ske->users++;
silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, NULL);
return SILC_SKE_STATUS_OK;
SilcSocketConnection sock,
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:
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);
+ status = silc_ske_group_get_by_name(start_payload->ke_grp_list, &group);
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) {
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)
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;
/* 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);
if (ske->callbacks->send_packet)
(*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_SUCCESS,
- ske->callbacks->context);
+ ske->callbacks->context);
silc_buffer_free(packet);
SilcSKEStatus
silc_ske_assemble_security_properties(SilcSKE ske,
- unsigned char flags,
+ SilcSKESecurityPropertyFlag flags,
char *version,
SilcSKEStartPayload **return_payload)
{
/* 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 +
/* 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,
+SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
uint32 len,
SilcMPInt *rnd)
{
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));
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);
+ /* XXX Backward support for 0.6.1 */
+ if (ske->backward_version == 1) {
+ SILC_LOG_DEBUG(("*********** Using old KE payload"));
+ buf = silc_buffer_alloc(ske->start_payload_copy->len +
+ ske->ke2_payload->pk_len + e_len +
+ f_len + KEY_len);
+ silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+
+ 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 {
+ /* Initiator is not required to send its public key */
+ SILC_LOG_DEBUG(("*********** Using new KE payload"));
+ buf = silc_buffer_alloc(ske->start_payload_copy->len +
+ ske->ke2_payload->pk_len +
+ ske->ke1_payload->pk_len +
+ e_len + f_len + KEY_len);
+ silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+
+ 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);
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);
+ ske->ke1_payload->pk_len + e_len);
silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
/* Format the buffer used to compute the hash value */
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) {
}
/* Processes the provided key material `data' as the SILC protocol
- specification specifies. */
+ specification defines. */
SilcSKEStatus
silc_ske_process_key_material_data(unsigned char *data,
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);
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);
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_free(buf);
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",
+ "Incorrect hash",
+
+ 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 "";
+}