unsigned char *return_hash,
SilcUInt32 *return_hash_len,
int initiator);
+static SilcSKEStatus
+silc_ske_select_security_properties(SilcSKE ske,
+ const char *version,
+ SilcSKEStartPayload payload,
+ SilcSKEStartPayload remote_payload);
+SilcSKEKeyMaterial
+silc_ske_process_key_material_data(unsigned char *data,
+ SilcUInt32 data_len,
+ SilcUInt32 req_iv_len,
+ SilcUInt32 req_enc_key_len,
+ SilcUInt32 req_hmac_key_len,
+ SilcHash hash);
+SilcSKEKeyMaterial
+silc_ske_process_key_material(SilcSKE ske,
+ SilcUInt32 req_iv_len,
+ SilcUInt32 req_enc_key_len,
+ SilcUInt32 req_hmac_key_len);
+static void silc_ske_packet_receive(SilcPacketEngine engine,
+ SilcPacketStream stream,
+ SilcPacket packet,
+ void *callback_context,
+ void *app_context);
+static void silc_ske_packet_eos(SilcPacketEngine engine,
+ SilcPacketStream stream,
+ void *callback_context,
+ void *app_context);
+static void silc_ske_packet_error(SilcPacketEngine engine,
+ SilcPacketStream stream,
+ SilcPacketError error,
+ void *callback_context,
+ void *app_context);
/* Structure to hold all SKE callbacks. */
struct SilcSKECallbacksStruct {
- SilcSKESendPacketCb send_packet;
- SilcSKECb payload_receive;
SilcSKEVerifyCb verify_key;
- SilcSKECb proto_continue;
- SilcSKECheckVersion check_version;
+ SilcSKECheckVersionCb check_version;
+ SilcSKECompletionCb completed;
void *context;
};
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_ske_stream_cbs =
+{
+ silc_ske_packet_receive,
+ silc_ske_packet_eos,
+ silc_ske_packet_error
+};
+
/* Allocates new SKE object. */
-SilcSKE silc_ske_alloc(SilcRng rng, void *context)
+SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, void *context)
{
SilcSKE ske;
SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
+ if (!rng || !schedule)
+ return NULL;
+
ske = silc_calloc(1, sizeof(*ske));
if (!ske)
return NULL;
ske->status = SILC_SKE_STATUS_OK;
ske->rng = rng;
ske->user_data = context;
+ ske->schedule = schedule;
ske->users = 1;
return ske;
}
}
-/* Sets the callback functions for the SKE session.
-
- The `send_packet' callback is a function that sends the packet to
- network. The SKE library will call it at any time packet needs to
- be sent to the remote host.
-
- The `payload_receive' callback is called when the remote host's Key
- Exchange Start Payload has been processed. The payload is saved
- to ske->start_payload if the application would need it. The application
- must also provide the payload to the next state of the SKE.
+/* Return user context */
- The `verify_key' callback is called to verify the received public key
- or certificate. The verification process is most likely asynchronous.
- That is why the application must call the completion callback when the
- verification process has been completed. The library then calls the user
- callback (`proto_continue'), if it is provided to indicate that the SKE
- protocol may continue.
-
- The `proto_continue' callback is called to indicate that it is
- safe to continue the execution of the SKE protocol after executing
- an asynchronous operation, such as calling the `verify_key' callback
- function, which is asynchronous. The application should check the
- ske->status in this function to check whether it is Ok to continue
- the execution of the protocol.
-
- The `check_version' callback is called to verify the remote host's
- version. The application may check its own version against the remote
- host's version and determine whether supporting the remote host
- is possible.
+void *silc_ske_get_context(SilcSKE ske)
+{
+ return ske->user_data;
+}
- The `context' is passed as argument to all of the above callback
- functions. */
+/* Sets protocol callbacks */
void silc_ske_set_callbacks(SilcSKE ske,
- SilcSKESendPacketCb send_packet,
- SilcSKECb payload_receive,
SilcSKEVerifyCb verify_key,
- SilcSKECb proto_continue,
- SilcSKECheckVersion check_version,
+ SilcSKECheckVersionCb check_version,
+ SilcSKECompletionCb completed,
void *context)
{
if (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;
- ske->callbacks->proto_continue = proto_continue;
ske->callbacks->check_version = check_version;
+ ske->callbacks->completed = completed;
ske->callbacks->context = context;
}
-/* Starts the SILC Key Exchange protocol for initiator. The connection
- to the remote end must be established before calling this function
- and the connecting socket must be sent as argument. This function
- creates the Key Exchange Start Payload which includes all our
- 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. 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. */
-
-SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
- SilcSocketConnection sock,
- SilcSKEStartPayload *start_payload)
+/* Aborts SKE protocol */
+
+static void silc_ske_abort(SilcAsyncOperation op, void *context)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcSKE ske = context;
+ ske->aborted = TRUE;
+}
+
+/* Public key verification completion callback */
+
+static void silc_ske_pk_verified(SilcSKE ske, SilcSKEStatus status,
+ void *completion_context)
+{
+ SILC_FSM_CALL_CONTINUE(&ske->fsm);
+}
+
+/* Initiator state machine */
+SILC_FSM_STATE(silc_ske_st_initiator_start);
+SILC_FSM_STATE(silc_ske_st_initiator_phase1);
+SILC_FSM_STATE(silc_ske_st_initiator_phase2);
+SILC_FSM_STATE(silc_ske_st_initiator_phase3);
+SILC_FSM_STATE(silc_ske_st_initiator_phase4);
+SILC_FSM_STATE(silc_ske_st_initiator_end);
+SILC_FSM_STATE(silc_ske_st_initiator_aborted);
+SILC_FSM_STATE(silc_ske_st_initiator_error);
+
+/* Start protocol. Send our proposal */
+
+SILC_FSM_STATE(silc_ske_st_initiator_start)
+{
+ SilcSKE ske = fsm_context;
SilcBuffer payload_buf;
+ SilcStatus status;
SILC_LOG_DEBUG(("Start"));
- ske->sock = sock;
- ske->rng = rng;
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
+ }
/* Encode the payload */
- status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
- if (status != SILC_SKE_STATUS_OK)
- return status;
-
- /* Send the packet. */
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE,
- ske->callbacks->context);
+ status = silc_ske_payload_start_encode(ske, ske->start_payload,
+ &payload_buf);
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error encoding Start Payload */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
/* Save the the payload buffer for future use. It is later used to
compute the HASH value. */
ske->start_payload_copy = payload_buf;
- ske->start_payload = start_payload;
- return status;
+ /* Send the packet */
+ /* XXX */
+
+ /** Wait for responder proposal */
+ SILC_LOG_DEBUG(("Waiting for reponder proposal"));
+ silc_fsm_next(ske, silc_ske_st_initiator_phase1);
+ return SILC_FSM_WAIT;
}
-/* Function called after ske_initiator_start fuction. This receives
- the remote ends Key Exchange Start payload which includes the
- security properties selected by the responder from our payload
- sent in the silc_ske_initiator_start function. */
+/* Phase-1. Receives responder's proposal */
-SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
- SilcBuffer start_payload)
+SILC_FSM_STATE(silc_ske_st_initiator_phase1)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcSKEStartPayload *payload;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEStartPayload payload;
SilcSKESecurityProperties prop;
SilcSKEDiffieHellmanGroup group;
SILC_LOG_DEBUG(("Start"));
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
+ }
+
/* Decode the payload */
- status = silc_ske_payload_start_decode(ske, start_payload, &payload);
+ status = silc_ske_payload_start_decode(ske, ske->packet_buf, &payload);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error decoding Start Payload */
ske->status = status;
- silc_ske_payload_start_free(ske->start_payload);
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
/* Check that the cookie is returned unmodified */
if (memcmp(ske->start_payload->cookie, payload->cookie,
ske->start_payload->cookie_len)) {
+ /** Invalid cookie */
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;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
/* Check version string */
payload->version_len,
ske->callbacks->context);
if (status != SILC_SKE_STATUS_OK) {
+ /** Version mismatch */
ske->status = status;
- silc_ske_payload_start_free(ske->start_payload);
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
}
/* Free our KE Start Payload context, we don't need it anymore. */
silc_ske_payload_start_free(ske->start_payload);
+ ske->start_payload = NULL;
/* 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. */
+ the key exchange. This is used only while doing the key
+ exchange. */
ske->prop = prop = silc_calloc(1, sizeof(*prop));
if (!ske->prop)
goto err;
status = SILC_SKE_STATUS_UNKNOWN_PKCS;
goto err;
}
-
if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
goto err;
}
-
if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
goto err;
}
-
if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_HMAC;
goto err;
/* Save remote's KE Start Payload */
ske->start_payload = payload;
- /* Return the received payload by calling the callback function. */
- if (ske->callbacks->payload_receive)
- (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
-
- return status;
+ /** Send KE Payload */
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase2);
+ return SILC_FSM_CONTINUE;
err:
if (payload)
ske->prop = NULL;
if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
+ status = SILC_SKE_STATUS_ERROR;
+ /** Error */
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
-/* This function creates random number x, such that 1 < x < q and
- computes e = g ^ x mod p and sends the result to the remote end in
- Key Exchange Payload. */
+/* Phase-2. Send KE payload */
-SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
- SilcPublicKey public_key,
- SilcPrivateKey private_key,
- SilcSKEPKType pk_type)
+SILC_FSM_STATE(silc_ske_st_initiator_phase2)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
SilcBuffer payload_buf;
SilcMPInt *x;
- SilcSKEKEPayload *payload;
+ SilcSKEKEPayload payload;
SilcUInt32 pk_len;
SILC_LOG_DEBUG(("Start"));
/* Create the random number x, 1 < x < q. */
x = silc_calloc(1, sizeof(*x));
if (!x){
+ /** Out of memory */
ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
- return ske->status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
silc_mp_init(x);
status =
silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
x);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error generating random number */
silc_mp_uninit(x);
silc_free(x);
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
/* Encode the result to Key Exchange Payload. */
payload = silc_calloc(1, sizeof(*payload));
if (!payload) {
+ /** Out of memory */
silc_mp_uninit(x);
silc_free(x);
ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
- return ske->status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
ske->ke1_payload = payload;
&ske->prop->group->group);
/* Get public key */
- if (public_key) {
- payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
+ if (ske->public_key) {
+ payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
if (!payload->pk_data) {
+ /** Error encoding public key */
silc_mp_uninit(x);
silc_free(x);
silc_mp_uninit(&payload->x);
silc_free(payload);
ske->ke1_payload = NULL;
- ske->status = SILC_SKE_STATUS_OK;
- return ske->status;
+ ske->status = SILC_SKE_STATUS_ERROR;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
payload->pk_len = pk_len;
}
- payload->pk_type = pk_type;
+ payload->pk_type = ske->pk_type;
/* Compute signature data if we are doing mutual authentication */
- if (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+ if (ske->private_key &&
+ ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1];
SilcUInt32 hash_len, sign_len;
SILC_LOG_DEBUG(("Signing 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_private_key_data_set(ske->prop->pkcs, ske->private_key->prv,
+ ske->private_key->prv_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)) {
+ /** Error computing signature */
silc_mp_uninit(x);
silc_free(x);
silc_mp_uninit(&payload->x);
silc_free(payload);
ske->ke1_payload = NULL;
ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR;
- return ske->status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
payload->sign_data = silc_memdup(sign, sign_len);
payload->sign_len = sign_len;
status = silc_ske_payload_ke_encode(ske, payload, &payload_buf);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error encoding KE payload */
silc_mp_uninit(x);
silc_free(x);
silc_mp_uninit(&payload->x);
silc_free(payload);
ske->ke1_payload = NULL;
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
}
ske->x = x;
/* Send the packet. */
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf,
- SILC_PACKET_KEY_EXCHANGE_1,
- ske->callbacks->context);
+ /* XXX */
silc_buffer_free(payload_buf);
- return status;
+ /** Waiting responder's KE payload */
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase3);
+ return SILC_FSM_WAIT;
}
-/* An initiator finish final callback that is called to indicate that
- the SKE protocol may continue. */
+/* Phase-3. Process responder's KE payload */
-static void silc_ske_initiator_finish_final(SilcSKE ske,
- SilcSKEStatus status,
- void *context)
+SILC_FSM_STATE(silc_ske_st_initiator_phase3)
{
- SilcSKEKEPayload *payload;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEKEPayload payload;
+ SilcMPInt *KEY;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* Decode the payload */
+ status = silc_ske_payload_ke_decode(ske, ske->packet_buf, &payload);
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error decoding KE payload */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+ ske->ke2_payload = payload;
+
+ if (!payload->pk_data && ske->callbacks->verify_key) {
+ SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
+ "even though we require it"));
+ ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+ goto err;
+ }
+
+ SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+
+ /* Compute the shared secret key */
+ KEY = silc_calloc(1, sizeof(*KEY));
+ silc_mp_init(KEY);
+ silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
+ ske->KEY = KEY;
+
+ if (payload->pk_data && ske->callbacks->verify_key) {
+ SILC_LOG_DEBUG(("Verifying public key"));
+
+ /** Waiting public key verification */
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
+ SILC_FSM_CALL(ske->callbacks->verify_key(ske, payload->pk_data,
+ payload->pk_len,
+ payload->pk_type,
+ ske->callbacks->context,
+ silc_ske_pk_verified, NULL));
+ /* NOT REACHED */
+ }
+
+ /** Process key material */
+ silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
+ return SILC_FSM_CONTINUE;
+
+ err:
+ silc_ske_payload_ke_free(payload);
+ ske->ke2_payload = NULL;
+
+ silc_mp_uninit(ske->KEY);
+ silc_free(ske->KEY);
+ ske->KEY = NULL;
+
+ if (status == SILC_SKE_STATUS_OK)
+ return SILC_SKE_STATUS_ERROR;
+
+ /** Error */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+}
+
+/* Process key material */
+
+SILC_FSM_STATE(silc_ske_st_initiator_phase4)
+{
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEKEPayload payload;
unsigned char hash[SILC_HASH_MAXLEN];
SilcUInt32 hash_len;
SilcPublicKey public_key = NULL;
+ int key_len, block_len;
- /* 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;
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
}
- /* 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;
+ /* Check result of public key verification */
+ if (ske->status != SILC_SKE_STATUS_OK) {
+ /** Public key not verified */
+ SILC_LOG_DEBUG(("Public key verification failed"));
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
- 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) {
- ske->status = status;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
- }
-
if (payload->pk_data) {
/* Decode the public key */
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;
+ status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+ goto err;
}
SILC_LOG_DEBUG(("Public key is authentic"));
ske->status = SILC_SKE_STATUS_OK;
- /* Call the callback. The caller may now continue the SKE protocol. */
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
+ /* Process key material */
+ key_len = silc_cipher_get_key_len(ske->prop->cipher);
+ block_len = silc_cipher_get_key_len(ske->prop->cipher);
+ hash_len = silc_hash_len(ske->prop->hash);
+ ske->keymat = silc_ske_process_key_material(ske, block_len,
+ key_len, hash_len);
+ if (!ske->keymat) {
+ SILC_LOG_ERROR(("Error processing key material"));
+ status = SILC_SKE_STATUS_ERROR;
+ goto err;
+ }
- return;
+ /* Send SUCCESS packet */
+ /* XXX */
+
+ /** Waiting completion */
+ silc_fsm_next(fsm, silc_ske_st_initiator_end);
+ return SILC_FSM_WAIT;
err:
memset(hash, 'F', sizeof(hash));
}
if (status == SILC_SKE_STATUS_OK)
- ske->status = SILC_SKE_STATUS_ERROR;
+ status = SILC_SKE_STATUS_ERROR;
+ /** Error */
ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+}
+
+/* Protocol completed */
+
+SILC_FSM_STATE(silc_ske_st_initiator_end)
+{
+ SilcSKE ske = fsm_context;
+
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* Call the completion callback */
+ if (ske->callbacks->completed)
+ ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
- /* Call the callback. */
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
+ return SILC_FSM_FINISH;
}
-/* Receives Key Exchange Payload from responder consisting responders
- public key, f, and signature. This function verifies the public key,
- computes the secret shared key and verifies the signature.
-
- 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
- if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
- However, note that when the library calls the callback the ske->status
- may be error.
-
- This calls the `verify_key' callback to verify the received public
- key or certificate. If the `verify_key' is provided then the remote
- must send public key and it is considered to be an error if remote
- does not send its public key. If caller is performing a re-key with
- SKE then the `verify_key' is usually not provided when it is not also
- required for the remote to send its public key. */
-
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
- SilcBuffer ke_payload)
+/* Aborted by application */
+
+SILC_FSM_STATE(silc_ske_st_initiator_aborted)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcSKEKEPayload *payload;
- SilcMPInt *KEY;
- SILC_LOG_DEBUG(("Start"));
+ return SILC_FSM_FINISH;
+}
- /* Decode the payload */
- status = silc_ske_payload_ke_decode(ske, ke_payload, &payload);
- if (status != SILC_SKE_STATUS_OK) {
- ske->status = status;
- return status;
- }
- ske->ke2_payload = payload;
+/* Error occurred */
- if (!payload->pk_data && ske->callbacks->verify_key) {
- SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
- "even though we require it"));
- ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
- goto err;
- }
+SILC_FSM_STATE(silc_ske_st_initiator_error)
+{
- SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+ return SILC_FSM_FINISH;
+}
- /* Compute the shared secret key */
- KEY = silc_calloc(1, sizeof(*KEY));
- silc_mp_init(KEY);
- silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
- ske->KEY = KEY;
- if (payload->pk_data && ske->callbacks->verify_key) {
- SILC_LOG_DEBUG(("Verifying public key"));
+static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context,
+ void *destructor_context)
+{
- ske->users++;
- (*ske->callbacks->verify_key)(ske, payload->pk_data, payload->pk_len,
- payload->pk_type, ske->callbacks->context,
- silc_ske_initiator_finish_final, NULL);
+}
- /* We will continue to the final state after the public key has
- been verified by the caller. */
- return SILC_SKE_STATUS_PENDING;
- }
+/* Starts the protocol as initiator */
- /* Continue to final state */
- ske->users++;
- silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, NULL);
+SilcAsyncOperation
+silc_ske_initiator_start(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcSKEStartPayload start_payload)
+{
+ SILC_LOG_DEBUG(("Start SKE as initiator"));
- return SILC_SKE_STATUS_OK;
+ if (!ske || !stream || !start_payload)
+ return NULL;
- err:
- silc_ske_payload_ke_free(payload);
- ske->ke2_payload = NULL;
+ if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+ return NULL;
- silc_mp_uninit(ske->KEY);
- silc_free(ske->KEY);
- ske->KEY = NULL;
+ if (!silc_fsm_init(&ske->fsm, ske, silc_ske_initiator_finished, ske,
+ ske->schedule))
+ return NULL;
- if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
+ ske->start_payload = start_payload;
- ske->status = status;
- return status;
+ /* Link to packet stream to get key exchange packets */
+ ske->stream = stream;
+ silc_packet_stream_ref(ske->stream);
+ silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+
+ /* Start SKE as initiator */
+ silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
+
+ return &ske->op;
}
-/* Starts Key Exchange protocol for responder. Responder receives
- Key Exchange Start Payload from initiator consisting of all the
- security properties the initiator supports. This function decodes
- the payload and parses the payload further and selects the right
- security properties. */
-
-SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
- SilcSocketConnection sock,
- const char *version,
- SilcBuffer start_payload,
- SilcSKESecurityPropertyFlag flags)
+/* Responder state machine */
+SILC_FSM_STATE(silc_ske_st_responder_start);
+SILC_FSM_STATE(silc_ske_st_responder_phase1);
+SILC_FSM_STATE(silc_ske_st_responder_phase2);
+SILC_FSM_STATE(silc_ske_st_responder_phase3);
+SILC_FSM_STATE(silc_ske_st_responder_phase4);
+SILC_FSM_STATE(silc_ske_st_responder_end);
+SILC_FSM_STATE(silc_ske_st_responder_aborted);
+SILC_FSM_STATE(silc_ske_st_responder_error);
+
+/* Start protocol as responder. Decode initiator's start payload */
+
+SILC_FSM_STATE(silc_ske_st_responder_start)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcSKEStartPayload *remote_payload = NULL, *payload = NULL;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEStartPayload remote_payload = NULL, payload = NULL;
SILC_LOG_DEBUG(("Start"));
- ske->sock = sock;
- ske->rng = rng;
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+ return SILC_FSM_CONTINUE;
+ }
/* Decode the payload */
- status = silc_ske_payload_start_decode(ske, start_payload, &remote_payload);
+ status = silc_ske_payload_start_decode(ske, ske->packet_buf,
+ &remote_payload);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error decoding Start Payload */
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
/* 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(start_payload);
+ ske->start_payload_copy = silc_buffer_copy(ske->packet_buf);
/* Force the mutual authentication flag if we want to do it. */
- if (flags & SILC_SKE_SP_FLAG_MUTUAL) {
+ if (ske->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) {
+ if (ske->flags & SILC_SKE_SP_FLAG_PFS) {
SILC_LOG_DEBUG(("Force PFS"));
remote_payload->flags |= SILC_SKE_SP_FLAG_PFS;
}
/* Disable IV Included flag if requested */
if (remote_payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED &&
- !(flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) {
+ !(ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) {
SILC_LOG_DEBUG(("We do not support IV Included flag"));
remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED;
}
/* Parse and select the security properties from the payload */
payload = silc_calloc(1, sizeof(*payload));
- status = silc_ske_select_security_properties(ske, version,
+ status = silc_ske_select_security_properties(ske, ske->version,
payload, remote_payload);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error selecting proposal */
+ if (remote_payload)
+ silc_ske_payload_start_free(remote_payload);
+ silc_free(payload);
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
ske->start_payload = payload;
- /* Call the callback function. */
- if (ske->callbacks->payload_receive)
- (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
-
silc_ske_payload_start_free(remote_payload);
- return status;
-
- err:
- if (remote_payload)
- silc_ske_payload_start_free(remote_payload);
- silc_free(payload);
-
- if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
-
- ske->status = status;
- return status;
+ /** Send proposal to initiator */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase1);
+ return SILC_FSM_CONTINUE;
}
-/* The selected security properties from the initiator payload is now
- encoded into Key Exchange Start Payload and sent to the initiator. */
+/* Phase-1. Send Start Payload */
-SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
+SILC_FSM_STATE(silc_ske_st_responder_phase1)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
SilcBuffer payload_buf;
SilcSKESecurityProperties prop;
SilcSKEDiffieHellmanGroup group = NULL;
status = SILC_SKE_STATUS_UNKNOWN_PKCS;
goto err;
}
-
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(ske->start_payload->hash_alg_list,
&prop->hash) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
goto err;
}
-
if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL,
&prop->hmac) == FALSE) {
status = SILC_SKE_STATUS_UNKNOWN_HMAC;
goto err;
/* Send the packet. */
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE,
- ske->callbacks->context);
+ /* XXX */
silc_buffer_free(payload_buf);
- return status;
+ /** Waiting initiator's KE payload */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase2);
+ return SILC_FSM_WAIT;
err:
if (group)
ske->prop = NULL;
if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
+ status = SILC_SKE_STATUS_ERROR;
+ /** Error */
ske->status = status;
- return status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
-/* An responder phase 2 final callback that is called to indicate that
- the SKE protocol may continue. */
+/* Phase-2. Decode initiator's KE payload */
-static void silc_ske_responder_phase2_final(SilcSKE ske,
- SilcSKEStatus status,
- void *context)
+SILC_FSM_STATE(silc_ske_st_responder_phase2)
{
- SilcSKEKEPayload *recv_payload, *send_payload;
- 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;
- }
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEKEPayload recv_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;
+ SILC_LOG_DEBUG(("Start"));
- ske->users--;
- recv_payload = ske->ke1_payload;
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+ return SILC_FSM_CONTINUE;
+ }
- /* If the status is an error then the public key that was verified
- by the caller is not authentic. */
+ /* Decode Key Exchange Payload */
+ status = silc_ske_payload_ke_decode(ske, ske->packet_buf, &recv_payload);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error decoding KE payload */
ske->status = status;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
+ ske->ke1_payload = recv_payload;
+
+ /* Verify the received public key and verify the signature if we are
+ doing mutual authentication. */
+ if (ske->start_payload &&
+ ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+
+ SILC_LOG_DEBUG(("We are doing mutual authentication"));
+
+ if (!recv_payload->pk_data && ske->callbacks->verify_key) {
+ /** Public key not provided */
+ 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;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ if (recv_payload->pk_data && ske->callbacks->verify_key) {
+ SILC_LOG_DEBUG(("Verifying public key"));
+
+ /** Waiting public key verification */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+ SILC_FSM_CALL(ske->callbacks->verify_key(ske, recv_payload->pk_data,
+ recv_payload->pk_len,
+ recv_payload->pk_type,
+ ske->callbacks->context,
+ silc_ske_pk_verified, NULL));
+ /* NOT REACHED */
+ }
+ }
+
+ /** Generate KE2 payload */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+ return SILC_FSM_CONTINUE;
+}
+
+/* Phase-3. Generate KE2 payload */
+
+SILC_FSM_STATE(silc_ske_st_responder_phase3)
+{
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
+ SilcSKEKEPayload recv_payload, send_payload;
+ SilcMPInt *x, *KEY;
+
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* Check result of public key verification */
+ if (ske->status != SILC_SKE_STATUS_OK) {
+ /** Public key not verified */
+ SILC_LOG_DEBUG(("Public key verification failed"));
+ silc_fsm_next(fsm, silc_ske_st_initiator_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ recv_payload = ske->ke1_payload;
+
/* The public key verification was performed only if the Mutual
Authentication flag is set. */
if (ske->start_payload &&
if (!silc_pkcs_public_key_decode(recv_payload->pk_data,
recv_payload->pk_len,
&public_key)) {
- ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+ /** Error decoding public key */
SILC_LOG_ERROR(("Unsupported/malformed public key received"));
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
SILC_LOG_DEBUG(("Public key is authentic"));
/* Compute the hash value */
status = silc_ske_make_hash(ske, hash, &hash_len, TRUE);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error computing hash */
ske->status = status;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
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) {
+ /** Incorrect signature */
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);
- return;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
SILC_LOG_DEBUG(("Signature is Ok"));
silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
x);
if (status != SILC_SKE_STATUS_OK) {
+ /** Error generating random number */
silc_mp_uninit(x);
silc_free(x);
ske->status = status;
- if (ske->callbacks->proto_continue)
- ske->callbacks->proto_continue(ske, ske->callbacks->context);
- return;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
/* Save the results for later processing */
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->callbacks->proto_continue(ske, ske->callbacks->context);
-}
-
-/* This function receives the Key Exchange Payload from the initiator.
- 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.
-
- 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
- if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
- However, note that when the library calls the callback the ske->status
- may be error.
-
- This calls the `verify_key' callback to verify the received public
- key or certificate if the Mutual Authentication flag is set. If the
- `verify_key' is provided then the remote must send public key and it
- is considered to be an error if remote does not send its public key. */
-
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
- SilcBuffer ke_payload)
-{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
- SilcSKEKEPayload *recv_payload;
-
- SILC_LOG_DEBUG(("Start"));
-
- /* Decode Key Exchange Payload */
- status = silc_ske_payload_ke_decode(ske, ke_payload, &recv_payload);
- if (status != SILC_SKE_STATUS_OK) {
- ske->status = status;
- return status;
- }
-
- ske->ke1_payload = recv_payload;
-
- /* Verify the received public key and verify the signature if we are
- doing mutual authentication. */
- if (ske->start_payload &&
- ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-
- SILC_LOG_DEBUG(("We are doing mutual authentication"));
-
- if (!recv_payload->pk_data && ske->callbacks->verify_key) {
- 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;
- }
-
- if (recv_payload->pk_data && ske->callbacks->verify_key) {
- SILC_LOG_DEBUG(("Verifying public key"));
-
- 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);
-
- /* We will continue to the final state after the public key has
- been verified by the caller. */
- return SILC_SKE_STATUS_PENDING;
- }
- }
+ SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
- /* Continue to final state */
- ske->users++;
- silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, NULL);
+ /* Compute the shared secret key */
+ KEY = silc_calloc(1, sizeof(*KEY));
+ silc_mp_init(KEY);
+ silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x,
+ &ske->prop->group->group);
+ ske->KEY = KEY;
- return SILC_SKE_STATUS_OK;
+ /** Send KE2 payload */
+ silc_fsm_next(fsm, silc_ske_st_responder_phase4);
+ return SILC_FSM_CONTINUE;
}
-/* 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. */
+/* Phase-4. Send KE2 payload */
-SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
- SilcPublicKey public_key,
- SilcPrivateKey private_key,
- SilcSKEPKType pk_type)
+SILC_FSM_STATE(silc_ske_st_responder_phase4)
{
- SilcSKEStatus status = SILC_SKE_STATUS_OK;
+ SilcSKE ske = fsm_context;
+ SilcSKEStatus status;
SilcBuffer payload_buf;
- SilcMPInt *KEY;
unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1], *pk;
SilcUInt32 hash_len, sign_len, pk_len;
SILC_LOG_DEBUG(("Start"));
- SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
-
- /* Compute the shared secret key */
- KEY = silc_calloc(1, sizeof(*KEY));
- silc_mp_init(KEY);
- silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x,
- &ske->prop->group->group);
- ske->KEY = KEY;
-
- if (public_key && private_key) {
+ if (ske->public_key && ske->private_key) {
SILC_LOG_DEBUG(("Getting public key"));
/* Get the public key */
- pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+ pk = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
if (!pk) {
+ /** Error encoding public key */
status = SILC_SKE_STATUS_OUT_OF_MEMORY;
- goto err;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
ske->ke2_payload->pk_data = pk;
ske->ke2_payload->pk_len = pk_len;
/* 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;
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error computing hash */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
ske->hash = silc_memdup(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_private_key_data_set(ske->prop->pkcs, ske->private_key->prv,
+ ske->private_key->prv_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)) {
+ /** Error computing signature */
status = SILC_SKE_STATUS_SIGNATURE_ERROR;
- goto err;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
}
ske->ke2_payload->sign_data = silc_memdup(sign, sign_len);
ske->ke2_payload->sign_len = sign_len;
memset(sign, 0, sizeof(sign));
}
- ske->ke2_payload->pk_type = pk_type;
+ ske->ke2_payload->pk_type = ske->pk_type;
/* Encode the Key Exchange Payload */
status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
&payload_buf);
- if (status != SILC_SKE_STATUS_OK)
- goto err;
+ if (status != SILC_SKE_STATUS_OK) {
+ /** Error encoding KE payload */
+ ske->status = status;
+ silc_fsm_next(fsm, silc_ske_st_responder_error);
+ return SILC_FSM_CONTINUE;
+ }
/* Send the packet. */
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, payload_buf,
- SILC_PACKET_KEY_EXCHANGE_2,
- ske->callbacks->context);
+ /* XXX */
silc_buffer_free(payload_buf);
- return status;
+ /** Waiting completion */
+ silc_fsm_next(fsm, silc_ske_st_responder_end);
+ return SILC_FSM_WAIT;
+}
- err:
- silc_mp_uninit(ske->KEY);
- silc_free(ske->KEY);
- ske->KEY = NULL;
- silc_ske_payload_ke_free(ske->ke2_payload);
+/* Protocol completed */
- if (status == SILC_SKE_STATUS_OK)
- return SILC_SKE_STATUS_ERROR;
+SILC_FSM_STATE(silc_ske_st_responder_end)
+{
+ SilcSKE ske = fsm_context;
- ske->status = status;
- return status;
+ if (ske->aborted) {
+ /** Aborted */
+ silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+ return SILC_FSM_CONTINUE;
+ }
+
+ /* Call the completion callback */
+ if (ske->callbacks->completed)
+ ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
+
+ return SILC_FSM_FINISH;
}
-/* The Key Exchange protocol is ended by calling this function. This
- must not be called until the keys are processed like the protocol
- defines. This function is for both initiator and responder. */
+/* Aborted by application */
-SilcSKEStatus silc_ske_end(SilcSKE ske)
+SILC_FSM_STATE(silc_ske_st_responder_aborted)
{
- SilcBufferStruct packet;
- unsigned char data[4];
- SILC_LOG_DEBUG(("Start"));
+ /* Send FAILURE */
- SILC_PUT32_MSB((SilcUInt32)SILC_SKE_STATUS_OK, data);
- silc_buffer_set(&packet, data, 4);
+ return SILC_FSM_FINISH;
+}
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, &packet, SILC_PACKET_SUCCESS,
- ske->callbacks->context);
+/* Error occurred */
- return SILC_SKE_STATUS_OK;
+SILC_FSM_STATE(silc_ske_st_responder_error)
+{
+
+ /* Send FAILURE */
+
+ return SILC_FSM_FINISH;
}
-/* Aborts the Key Exchange protocol. This is called if error occurs
- while performing the protocol. The status argument is the error
- status and it is sent to the remote end. */
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status)
+static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context,
+ void *destructor_context)
{
- SilcBufferStruct packet;
- unsigned char data[4];
- SILC_LOG_DEBUG(("Start"));
+}
- if (status > SILC_SKE_STATUS_INVALID_COOKIE)
- status = SILC_SKE_STATUS_BAD_PAYLOAD;
+/* Starts the protocol as responder. */
- SILC_PUT32_MSB((SilcUInt32)status, data);
- silc_buffer_set(&packet, data, 4);
+SilcAsyncOperation
+silc_ske_responder_start(SilcSKE ske,
+ SilcPacketStream stream,
+ const char *version,
+ SilcBuffer start_payload,
+ SilcSKESecurityPropertyFlag flags)
+{
+ SILC_LOG_DEBUG(("Start SKE as responder"));
- if (ske->callbacks->send_packet)
- (*ske->callbacks->send_packet)(ske, &packet, SILC_PACKET_FAILURE,
- ske->callbacks->context);
+ if (!ske || !stream || !start_payload)
+ return NULL;
- return SILC_SKE_STATUS_OK;
+ if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+ return NULL;
+
+ if (!silc_fsm_init(&ske->fsm, ske, silc_ske_responder_finished, ske,
+ ske->schedule))
+ return NULL;
+
+ ske->packet_buf = start_payload;
+ ske->flags = flags;
+ ske->version = strdup(version);
+
+ /* Link to packet stream to get key exchange packets */
+ ske->stream = stream;
+ silc_packet_stream_ref(ske->stream);
+ silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+
+ /* Start SKE as responder */
+ silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
+
+ return &ske->op;
+}
+
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start);
+
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start)
+{
+
+}
+
+/* Starts rekey protocol as initiator */
+
+SilcAsyncOperation
+silc_ske_rekey_initiator_start(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcSKERekeyMaterial rekey)
+{
+ SILC_LOG_DEBUG(("Start SKE rekey as initator"));
+
+ if (!ske || !stream || !rekey)
+ return NULL;
+
+ if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+ return NULL;
+
+ if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+ return NULL;
+
+ ske->rekey = rekey;
+
+ /* Link to packet stream to get key exchange packets */
+ ske->stream = stream;
+ silc_packet_stream_ref(ske->stream);
+ silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+
+ /* Start SKE rekey as initiator */
+ silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start);
+
+ return &ske->op;
+}
+
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
+
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
+{
+
+}
+
+/* Starts rekey protocol as responder */
+
+SilcAsyncOperation
+silc_ske_rekey_responder_start(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcBuffer ke_payload,
+ SilcSKERekeyMaterial rekey)
+{
+ SILC_LOG_DEBUG(("Start SKE rekey as responder"));
+
+ if (!ske || !stream || !rekey)
+ return NULL;
+ if (rekey->pfs && !ke_payload)
+ return NULL;
+
+ if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+ return NULL;
+
+ if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+ return NULL;
+
+ ske->packet_buf = ke_payload;
+ ske->rekey = rekey;
+
+ /* Link to packet stream to get key exchange packets */
+ ske->stream = stream;
+ silc_packet_stream_ref(ske->stream);
+ silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+
+ /* Start SKE rekey as responder */
+ silc_fsm_start(&ske->fsm, silc_ske_st_rekey_responder_start);
+
+ return &ske->op;
}
-/* Assembles security properties to Key Exchange Start Payload to be
- sent to the remote end. This checks system wide (SILC system, that is)
- settings and chooses from those. However, if other properties
- should be used this function is easy to replace by another function,
- as, this function is called by the caller of the protocol and not
- by the protocol itself. */
+/* Assembles security properties */
-SilcSKEStatus
+SilcSKEStartPayload
silc_ske_assemble_security_properties(SilcSKE ske,
SilcSKESecurityPropertyFlag flags,
- const char *version,
- SilcSKEStartPayload **return_payload)
+ const char *version)
{
- SilcSKEStartPayload *rp;
+ SilcSKEStartPayload rp;
int i;
SILC_LOG_DEBUG(("Assembling KE Start Payload"));
2 + rp->enc_alg_len + 2 + rp->hash_alg_len +
2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
- *return_payload = rp;
-
- return SILC_SKE_STATUS_OK;
+ return rp;
}
/* Selects the supported security properties from the remote end's Key
Exchange Start Payload. */
-SilcSKEStatus
+static SilcSKEStatus
silc_ske_select_security_properties(SilcSKE ske,
const char *version,
- SilcSKEStartPayload *payload,
- SilcSKEStartPayload *remote_payload)
+ SilcSKEStartPayload payload,
+ SilcSKEStartPayload remote_payload)
{
SilcSKEStatus status;
- SilcSKEStartPayload *rp;
+ SilcSKEStartPayload rp;
char *cp;
int len;
KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
/* Format the buffer used to compute the hash value */
- buf = silc_buffer_alloc_size(ske->start_payload_copy->len +
+ buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
ske->ke2_payload->pk_len +
ske->ke1_payload->pk_len +
e_len + f_len + KEY_len);
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->start_payload_copy->data,
+ silc_buffer_len(ske->start_payload_copy)),
SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
ske->ke2_payload->pk_len),
SILC_STR_UI_XNSTRING(e, e_len),
} 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->start_payload_copy->data,
+ silc_buffer_len(ske->start_payload_copy)),
SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
ske->ke2_payload->pk_len),
SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
} else {
e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
- buf = silc_buffer_alloc_size(ske->start_payload_copy->len +
- ske->ke1_payload->pk_len + e_len);
+ buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
+ ske->ke1_payload->pk_len + e_len);
if (!buf)
return SILC_SKE_STATUS_OUT_OF_MEMORY;
ret =
silc_buffer_format(buf,
SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
- ske->start_payload_copy->len),
+ silc_buffer_len(ske->start_payload_copy)),
SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
ske->ke1_payload->pk_len),
SILC_STR_UI_XNSTRING(e, e_len),
}
/* Make the hash */
- silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
+ silc_hash_make(ske->prop->hash, buf->data, silc_buffer_len(buf),
+ return_hash);
*return_hash_len = silc_hash_len(ske->prop->hash);
if (initiator == FALSE) {
/* Processes the provided key material `data' as the SILC protocol
specification defines. */
-SilcSKEStatus
+SilcSKEKeyMaterial
silc_ske_process_key_material_data(unsigned char *data,
SilcUInt32 data_len,
SilcUInt32 req_iv_len,
SilcUInt32 req_enc_key_len,
SilcUInt32 req_hmac_key_len,
- SilcHash hash,
- SilcSKEKeyMaterial *key)
+ SilcHash hash)
{
SilcBuffer buf;
unsigned char hashd[SILC_HASH_MAXLEN];
SilcUInt32 hash_len = req_hmac_key_len;
SilcUInt32 enc_key_len = req_enc_key_len / 8;
+ SilcSKEKeyMaterial key;
SILC_LOG_DEBUG(("Start"));
if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
- return SILC_SKE_STATUS_ERROR;
+ return NULL;
buf = silc_buffer_alloc_size(1 + data_len);
if (!buf)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return NULL;
silc_buffer_format(buf,
SILC_STR_UI_CHAR(0),
SILC_STR_UI_XNSTRING(data, data_len),
/* Take IVs */
memset(hashd, 0, sizeof(hashd));
buf->data[0] = 0;
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->send_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
memcpy(key->send_iv, hashd, req_iv_len);
memset(hashd, 0, sizeof(hashd));
buf->data[0] = 1;
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->receive_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
memcpy(key->receive_iv, hashd, req_iv_len);
key->iv_len = req_iv_len;
/* XXX */
if (enc_key_len > (3 * hash_len))
- return SILC_SKE_STATUS_ERROR;
+ return NULL;
/* Take first round */
memset(k1, 0, sizeof(k1));
- silc_hash_make(hash, buf->data, buf->len, k1);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), k1);
/* Take second round */
dist = silc_buffer_alloc_size(data_len + hash_len);
if (!dist)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return NULL;
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);
+ silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
/* Take third round */
dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
SILC_STR_END);
silc_buffer_push(dist, data_len + hash_len);
memset(k3, 0, sizeof(k3));
- silc_hash_make(hash, dist->data, dist->len, k3);
+ silc_hash_make(hash, dist->data, silc_buffer_len(dist), k3);
/* Then, save the keys */
dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
} else {
/* Take normal hash as key */
memset(hashd, 0, sizeof(hashd));
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
memcpy(key->send_enc_key, hashd, enc_key_len);
key->enc_key_len = req_enc_key_len;
/* XXX */
if (enc_key_len > (3 * hash_len))
- return SILC_SKE_STATUS_ERROR;
+ return NULL;
/* Take first round */
memset(k1, 0, sizeof(k1));
- silc_hash_make(hash, buf->data, buf->len, k1);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), k1);
/* Take second round */
dist = silc_buffer_alloc_size(data_len + hash_len);
if (!dist)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return NULL;
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);
+ silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
/* Take third round */
dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
SILC_STR_END);
silc_buffer_push(dist, data_len + hash_len);
memset(k3, 0, sizeof(k3));
- silc_hash_make(hash, dist->data, dist->len, k3);
+ silc_hash_make(hash, dist->data, silc_buffer_len(dist), k3);
/* Then, save the keys */
dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
} else {
/* Take normal hash as key */
memset(hashd, 0, sizeof(hashd));
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
memcpy(key->receive_enc_key, hashd, enc_key_len);
key->enc_key_len = req_enc_key_len;
/* Take HMAC keys */
memset(hashd, 0, sizeof(hashd));
buf->data[0] = 4;
- silc_hash_make(hash, buf->data, buf->len, hashd);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
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);
+ silc_hash_make(hash, buf->data, silc_buffer_len(buf), 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;
silc_buffer_clear(buf);
silc_buffer_free(buf);
- return SILC_SKE_STATUS_OK;
+ return key;
}
/* Processes negotiated key material as protocol specifies. This returns
the actual keys to be used in the SILC. */
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
- SilcUInt32 req_iv_len,
- SilcUInt32 req_enc_key_len,
- SilcUInt32 req_hmac_key_len,
- SilcSKEKeyMaterial *key)
+SilcSKEKeyMaterial
+silc_ske_process_key_material(SilcSKE ske,
+ SilcUInt32 req_iv_len,
+ SilcUInt32 req_enc_key_len,
+ SilcUInt32 req_hmac_key_len)
{
SilcSKEStatus status;
SilcBuffer buf;
unsigned char *tmpbuf;
SilcUInt32 klen;
+ SilcSKEKeyMaterial key;
/* Encode KEY to binary data */
tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
buf = silc_buffer_alloc_size(klen + ske->hash_len);
if (!buf)
- return SILC_SKE_STATUS_OUT_OF_MEMORY;
+ return NULL;
silc_buffer_format(buf,
SILC_STR_UI_XNSTRING(tmpbuf, klen),
SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
SILC_STR_END);
/* Process the key material */
- status = silc_ske_process_key_material_data(buf->data, buf->len,
- req_iv_len, req_enc_key_len,
- req_hmac_key_len,
- ske->prop->hash, key);
+ key = silc_ske_process_key_material_data(buf->data, silc_buffer_len(buf),
+ req_iv_len, req_enc_key_len,
+ req_hmac_key_len,
+ ske->prop->hash);
memset(tmpbuf, 0, klen);
silc_free(tmpbuf);
silc_buffer_clear(buf);
silc_buffer_free(buf);
- return status;
+ return key;
}
/* Free key material structure */
-void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
+void silc_ske_free_key_material(SilcSKEKeyMaterial key)
{
if (!key)
return;
/*
- silcske.h
+ silcske.h
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2000 - 2002 Pekka Riikonen
+ Copyright (C) 2000 - 2005 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
*
* DESCRIPTION
*
- * Implementation of the SILC Key Exchange Protocol (SKE). The SKE protocol
+ * The SILC Key Exchange (SKE) protocol interface. The SKE protocol
* is used to negotiate secret key material between two parties, to be used
* as session key or some other key. For example, when client connects to
* server SKE is performed to exchange public keys, and to generate the key
* two create secret key material for securing for example file transfer
* stream.
*
- * SKE is based on Diffie-Hellman, and it derives its functionality from
- * SSH2 Key Exchange protocol, OAKLEY Key Determination protocol and
- * Station-To-Station (STS) protocols.
- *
* This SKE implementation provides easy interface for application
- * that wants to use SKE. In fact, the interface is designed to be
+ * that wants to use SKE. In fact, the interface is designed to be
* application independent, and does not expect that the application using
* SKE would actually relate in any way to SILC. Hence, the interface
* can be used in any kind of application needing to perform key exchange
* protocol with two parties. The network connection is also handled
- * outside the SKE interface. For the interface application must provide
- * a packet sending function which SKE library can call when it wants
- * to send packet to the remote host. The actual network connection
- * therefore is handled in the application and not by the SKE library.
+ * outside the SKE interface.
*
* The protocol has initiator and responder. The initiator is the one
* that starts the protocol, and the responder is the one that receives
#include "silcske_status.h"
-/****s* silcske/SilcSKEAPI/SilcSKE
+/* Length of cookie in Start Payload */
+#define SILC_SKE_COOKIE_LEN 16
+
+/* Forward declarations */
+typedef struct SilcSKECallbacksStruct *SilcSKECallbacks;
+typedef struct SilcSKEStruct *SilcSKE;
+
+#include "silcske_groups.h"
+#include "silcske_payload.h"
+
+/****d* silcske/SilcSKEAPI/SilcSKESecurityPropertyFlag
*
* NAME
*
- * typedef struct SilcSKEStruct *SilcSKE;
+ * typedef enum { ... } SilcSKESecurityPropertyFlag
*
* DESCRIPTION
*
- * This context is forward declaration for the SilcSKEStruct.
- * This is allocated by the silc_ske_alloc and freed by the
- * silc_ske_free function. This context represents the SKE session.
+ * SKE security property flags as defined by the SK protocol.
*
- ***/
-typedef struct SilcSKEStruct *SilcSKE;
+ * SOURCE
+ */
+typedef enum {
+ SILC_SKE_SP_FLAG_NONE = 0x00, /* No flags */
+ SILC_SKE_SP_FLAG_IV_INCLUDED = 0x01, /* IV included in packet */
+ SILC_SKE_SP_FLAG_PFS = 0x02, /* Perfect Forward Secrecy */
+ SILC_SKE_SP_FLAG_MUTUAL = 0x04, /* Mutual authentication */
+} SilcSKESecurityPropertyFlag;
+/***/
/****s* silcske/SilcSKEAPI/SilcSKESecurityProperties
*
* NAME
*
- * typedef struct SilcSKESecurityPropertiesStruct
- * *SilcSKESecurityProperties;
+ * typedef struct { ... } *SilcSKESecurityProperties;
*
* DESCRIPTION
*
- * This context is forward declaration for the
- * SilcSKESecurityPropertiesStruct structure. It is allocated by the
- * library, and it represents the security properties selected during
- * the SKE negotiation.
+ * Security Properties negotiated between key exchange parties. This
+ * structure is filled from the Key Exchange Start Payload which is used
+ * to negotiate what security properties should be used in the
+ * communication.
*
- ***/
-typedef struct SilcSKESecurityPropertiesStruct *SilcSKESecurityProperties;
-
-/* Forward declaration for SKE callbacks structure, which is internal. */
-typedef struct SilcSKECallbacksStruct *SilcSKECallbacks;
+ * SOURCE
+ */
+typedef struct {
+ SilcSKESecurityPropertyFlag flags; /* Flags */
+ SilcSKEDiffieHellmanGroup group; /* Selected Diffie Hellman group */
+ SilcPKCS pkcs; /* Selected PKCS algorithm */
+ SilcCipher cipher; /* Selected cipher */
+ SilcHash hash; /* Selected hash algorithm */
+ SilcHmac hmac; /* Selected HMAC */
+} *SilcSKESecurityProperties;
+/***/
-/****d* silcske/SilcSKEAPI/SilcSKEPKType
+/****s* silcske/SilcSKEAPI/SilcSKEKeyMaterial
*
* NAME
*
- * typedef enum { ... } SilcSKEPKType;
+ * typedef struct { ... } *SilcSKEKeyMaterial;
*
* DESCRIPTION
*
- * Public key and certificate types defined by the SKE protocol.
+ * This is the key material structure, and is passed as argument by the
+ * application to silc_ske_process_key_material* functions. It includes
+ * the processed key material which can be used as SILC session keys.
*
- * SOURCE
*/
-typedef enum {
- SILC_SKE_PK_TYPE_SILC = 1, /* Mandatory type */
- /* Optional types. These are not implemented currently */
- SILC_SKE_PK_TYPE_SSH2 = 2,
- SILC_SKE_PK_TYPE_X509V3 = 3,
- SILC_SKE_PK_TYPE_OPENPGP = 4,
- SILC_SKE_PK_TYPE_SPKI = 5
-} SilcSKEPKType;
+typedef struct {
+ unsigned char *send_iv;
+ unsigned char *receive_iv;
+ SilcUInt32 iv_len;
+ unsigned char *send_enc_key;
+ unsigned char *receive_enc_key;
+ SilcUInt32 enc_key_len;
+ unsigned char *send_hmac_key;
+ unsigned char *receive_hmac_key;
+ SilcUInt32 hmac_key_len;
+} *SilcSKEKeyMaterial;
/***/
-/****f* silcske/SilcSKEAPI/SilcSKESendPacketCb
+/****s* silcske/SilcSKEAPI/SilcSKERekeyMaterial
*
- * SYNOPSIS
+ * NAME
*
- * typedef void (*SilcSKESendPacketCb)(SilcSKE ske, SilcBuffer packet,
- * SilcPacketType type, void *context);
+ * typedef struct { ... } *SilcSKERekeyMaterial;
*
* DESCRIPTION
*
- * Packet sending callback. Caller of the SKE routines must provide
- * a routine to send packets to negotiation parties. See the
- * silc_ske_set_callbacks for more information.
+ * This context is returned after key exchange protocol to application
+ * in the completion callback. Application may save it and use it later
+ * to perform the rekey with silc_ske_rekey_initiator_start and/or
+ * silc_ske_rekey_responder_start functions. If application does not
+ * need the context, it may free it with silc_free function.
*
- ***/
-typedef void (*SilcSKESendPacketCb)(SilcSKE ske, SilcBuffer packet,
- SilcPacketType type, void *context);
+ * Application may save application specific data to `user_context'.
+ *
+ */
+typedef struct {
+ void *user_context; /* Application specific data */
+ unsigned char *send_enc_key;
+ unsigned int enc_key_len : 23;
+ unsigned int ske_group : 8;
+ unsigned int pfs : 1;
+} *SilcSKERekeyMaterial;
+/***/
-/****f* silcske/SilcSKEAPI/SilcSKECb
+/****d* silcske/SilcSKEAPI/SilcSKEPKType
*
- * SYNOPSIS
+ * NAME
*
- * typedef void (*SilcSKECb)(SilcSKE ske, void *context);
+ * typedef enum { ... } SilcSKEPKType;
*
* DESCRIPTION
*
- * Generic SKE callback function. This is called in various SKE
- * routines. The SilcSKE object sent as argument provides all the data
- * callers routine might need (payloads etc). This is usually called
- * to indicate that the application may continue the execution of the
- * SKE protocol. The application should check the ske->status in this
- * callback function. This callback is also called when Start Payload
- * is processed. See silc_ske_set_callbacks function for more information.
+ * Public key and certificate types defined by the SKE protocol.
*
- ***/
-typedef void (*SilcSKECb)(SilcSKE ske, void *context);
+ * SOURCE
+ */
+typedef enum {
+ SILC_SKE_PK_TYPE_SILC = 1, /* SILC Public Key, mandatory */
+ SILC_SKE_PK_TYPE_SSH2 = 2, /* SSH2 Public key, not supported */
+ SILC_SKE_PK_TYPE_X509V3 = 3, /* X.509v3 certificate, not supported */
+ SILC_SKE_PK_TYPE_OPENPGP = 4, /* OpenPGP certificate, not supported */
+ SILC_SKE_PK_TYPE_SPKI = 5 /* SPKI certificate, not supported */
+} SilcSKEPKType;
+/***/
/****f* silcske/SilcSKEAPI/SilcSKEVerifyCbCompletion
*
* SYNOPSIS
*
* typedef void (*SilcSKEVerifyCb)(SilcSKE ske,
- * unsigned char *pk_data,
+ * const unsigned char *pk_data,
* SilcUInt32 pk_len,
* SilcSKEPKType pk_type,
* void *context,
* DESCRIPTION
*
* Callback function used to verify the received public key or certificate.
- * The verification process is most likely asynchronous. That's why the
+ * The verification process is most likely asynchronous. That's why the
* application must call the `completion' callback when the verification
- * process has been completed. The library then calls the user callback
- * (SilcSKECb), if it was provided for the function that takes this callback
- * function as argument, to indicate that the SKE protocol may continue.
- * See silc_ske_set_callbacks for more information.
+ * process has been completed. The `context' is the context given as
+ * arugment to silc_ske_set_callbacks. See silc_ske_set_callbacks for
+ * more information.
*
***/
typedef void (*SilcSKEVerifyCb)(SilcSKE ske,
- unsigned char *pk_data,
+ const unsigned char *pk_data,
SilcUInt32 pk_len,
SilcSKEPKType pk_type,
void *context,
*
* SYNOPSIS
*
- * typedef SilcSKEStatus (*SilcSKECheckVersion)(SilcSKE ske,
- * unsigned char *version,
- * SilcUInt32 len, void *context);
+ * typedef SilcSKEStatus
+ * (*SilcSKECheckVersionCb)(SilcSKE ske,
+ * const unsigned char *version,
+ * SilcUInt32 len, void *context);
*
* DESCRIPTION
*
* Callback function used to check the version of the remote SKE server.
* The SKE library will call this function so that the appliation may
- * check its version against the remote host's version. This returns
+ * check its version against the remote host's version. This returns
* SILC_SKE_STATUS_OK if the version string is Ok, and returns
* SILC_SKE_STATUS_BAD_VERSION if the version was not acceptable.
*
***/
-typedef SilcSKEStatus (*SilcSKECheckVersion)(SilcSKE ske,
- unsigned char *version,
- SilcUInt32 len, void *context);
+typedef SilcSKEStatus (*SilcSKECheckVersionCb)(SilcSKE ske,
+ const unsigned char *version,
+ SilcUInt32 len, void *context);
-/****s* silcske/SilcSKEAPI/SilcSKEKeyMaterial
+/****f* silcske/SilcSKEAPI/SilcSKECompletionCb
*
- * NAME
+ * SYNOPSIS
*
- * typedef struct { ... } SilcSKEKeyMaterial;
*
* DESCRIPTION
*
- * This is the key material structure, and is passed as argument by the
- * application to silc_ske_process_key_material* functions. It includes
- * the processed key material which can be used as SILC session keys.
*
***/
-typedef struct {
- unsigned char *send_iv;
- unsigned char *receive_iv;
- SilcUInt32 iv_len;
- unsigned char *send_enc_key;
- unsigned char *receive_enc_key;
- SilcUInt32 enc_key_len;
- unsigned char *send_hmac_key;
- unsigned char *receive_hmac_key;
- SilcUInt32 hmac_key_len;
-} SilcSKEKeyMaterial;
-
-/* Length of cookie in Start Payload */
-#define SILC_SKE_COOKIE_LEN 16
-
-#include "silcske_groups.h"
-#include "silcske_payload.h"
-
-/****d* silcske/SilcSKEAPI/SilcSKESecurityPropertyFlag
- *
- * NAME
- *
- * typedef enum { ... } SilcSKESecurityPropertyFlag
- *
- * DESCRIPTION
- *
- * SKE security property flags as defined by the SK protocol.
- *
- * SOURCE
- */
-typedef enum {
- SILC_SKE_SP_FLAG_NONE = 0x00, /* No flags */
- SILC_SKE_SP_FLAG_IV_INCLUDED = 0x01, /* IV included in ciphertexts */
- SILC_SKE_SP_FLAG_PFS = 0x02, /* Perfect Forward Secrecy */
- SILC_SKE_SP_FLAG_MUTUAL = 0x04, /* Mutual authentication */
-} SilcSKESecurityPropertyFlag;
-/***/
-
-/****s* silcske/SilcSKEAPI/SilcSKESecurityPropertiesStruct
- *
- * NAME
- *
- * struct SilcSKESecurityPropertiesStruct { ... };
- *
- * DESCRIPTION
- *
- * Security Properties negotiated between key exchange parties. This
- * structure is filled from the Key Exchange Start Payload which is used
- * to negotiate what security properties should be used in the
- * communication.
- *
- * SOURCE
- */
-struct SilcSKESecurityPropertiesStruct {
- SilcSKESecurityPropertyFlag flags; /* Flags */
- SilcSKEDiffieHellmanGroup group; /* Selected Diffie Hellman group */
- SilcPKCS pkcs; /* Selected PKCS algorithm */
- SilcCipher cipher; /* Selected cipher */
- SilcHash hash; /* Selected hash algorithm */
- SilcHmac hmac; /* Selected HMAC */
-};
-/***/
+typedef void (*SilcSKECompletionCb)(SilcSKE ske,
+ SilcSKEStatus status,
+ SilcSKESecurityProperties prop,
+ SilcSKEKeyMaterial keymat,
+ SilcSKERekeyMaterial rekey,
+ void *context);
/****s* silcske/SilcSKEAPI/SilcSKEStruct
*
*
* This structure is the SKE session context, and has a type definition
* to SilcSKE. The structure includes the network connection socket,
- * securit properties collected during the SKE negotiation, payloads
+ * security properties collected during the SKE negotiation, payloads
* sent and received during the negotiation, and the actual raw key
* material too. The application usually does not need to reference
* to the inside of this structure. However, checking the current
* SOURCE
*/
struct SilcSKEStruct {
- /* The connection object. This is initialized by the caller. */
-#if 0
- SilcSocketConnection sock;
-#endif
+ /* The network socket connection stream. Set by application. */
+ SilcPacketStream stream;
- /* Security properties negotiated */
+ /* Negotiated Security properties. May be NULL in case of error. */
SilcSKESecurityProperties prop;
/* Key Exchange payloads filled during key negotiation with
remote data. Responder may save local data here as well. */
- SilcSKEStartPayload *start_payload;
- SilcSKEKEPayload *ke1_payload;
- SilcSKEKEPayload *ke2_payload;
+ SilcSKEStartPayload start_payload;
+ SilcSKEKEPayload ke1_payload;
+ SilcSKEKEPayload ke2_payload;
unsigned char *remote_version;
/* Temporary copy of the KE Start Payload used in the
/* Random number x, 1 < x < q. This is the secret exponent
used in Diffie Hellman computations. */
SilcMPInt *x;
-
+
/* The secret shared key */
SilcMPInt *KEY;
/* Backwards support version indicator */
SilcUInt32 backward_version;
+
+ char *version;
+ SilcPublicKey public_key;
+ SilcPrivateKey private_key;
+ SilcSKEPKType pk_type;
+ SilcBuffer packet_buf;
+ SilcSKESecurityPropertyFlag flags;
+ SilcSKEKeyMaterial keymat;
+ SilcSKERekeyMaterial rekey;
+ SilcSchedule schedule;
+ SilcFSMStruct fsm;
+ SilcAsyncOperationStruct op;
+ bool aborted;
};
/***/
*
* SYNOPSIS
*
- * SilcSKE silc_ske_alloc(SilcRng rng, void *context);
+ * SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
+ * void *context);
*
* DESCRIPTION
*
* Allocates the SKE session context and returns it. The `rng' is
* the random number generator the SKE is going to use when it needs
* random number generation during the SKE session. The `context' is
- * user context that the libary will not touch. The application can
- * access that context with the ske->user_context if needed. The
+ * user context that the libary will not touch. Application can get the
+ * context by calling the fuction silc_ske_get_context function. The
* application is responsible of freeing the `context'. After the
* SKE session context is allocated application must call the
* silc_ske_set_callbacks.
*
+ * EXMPALE
+ *
+ * // Initiator example
+ * ske = silc_ske_alloc(rng, scheduler, app);
+ * silc_ske_set_callbacks(ske, verify_public_key, check_version, app);
+ * start_payload =
+ * silc_ske_assemble_security_properties(ske, SILC_SKE_SP_FLAG_PFS |
+ * SILC_SKE_SP_FLAG_MUTUAL,
+ * version);
+ * silc_ske_initiator_start(ske);
+ *
***/
-SilcSKE silc_ske_alloc(SilcRng rng, void *context);
+SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, void *context);
/****f* silcske/SilcSKEAPI/silc_ske_free
*
***/
void silc_ske_free(SilcSKE ske);
+/****f* silcske/SilcSKEAPI/silc_ske_get_context
+ *
+ * SYNOPSIS
+ *
+ * void *silc_ske_get_context(SilcSKE ske);
+ *
+ * DESCRIPTION
+ *
+ * Returns the context that was given as argument to silc_ske_alloc.
+ *
+ ***/
+void *silc_ske_get_context(SilcSKE ske);
+
/****f* silcske/SilcSKEAPI/silc_ske_set_callbacks
*
* SYNOPSIS
*
* void silc_ske_set_callbacks(SilcSKE ske,
- * SilcSKESendPacketCb send_packet,
- * SilcSKECb payload_receive,
* SilcSKEVerifyCb verify_key,
- * SilcSKECb proto_continue,
* SilcSKECheckVersion check_version,
+ * SilcSKECompletion completed,
* void *context);
*
* DESCRIPTION
*
* Sets the callback functions for the SKE session.
*
- * The `send_packet' callback is a function that sends the packet to
- * network. The SKE library will call it at any time packet needs to
- * be sent to the remote host.
- *
- * The `payload_receive' callback is called when the remote host's Key
- * Exchange Start Payload has been processed. The payload is saved
- * to ske->start_payload if the application would need it. The application
- * must also provide the payload to the next state of the SKE.
- *
* The `verify_key' callback is called to verify the received public key
* or certificate. The verification process is most likely asynchronous.
* That is why the application must call the completion callback when the
- * verification process has been completed. The library then calls the user
- * callback (`proto_continue'), if it is provided to indicate that the SKE
- * protocol may continue. If this SKE session context is used to perform
- * rekey, this callback usually is not provided as argument since sending
- * public key in rekey is not mandatory. Setting this callback implies
- * that remote end MUST send its public key, and this could cause
- * problems when performing rekey. When doing normal SKE session this
- * callback should be set.
- *
- * The `proto_continue' callback is called to indicate that it is
- * safe to continue the execution of the SKE protocol after executing
- * an asynchronous operation, such as calling the `verify_key' callback
- * function, which is asynchronous. The application should check the
- * ske->status in this function to check whether it is Ok to continue
- * the execution of the protocol.
+ * verification process has been completed. If this SKE session context
+ * is used to perform rekey, this callback usually is not provided as
+ * argument since sending public key in rekey is not mandatory. Setting
+ * this callback implies that remote end MUST send its public key.
*
* The `check_version' callback is called to verify the remote host's
- * version. The application may check its own version against the remote
+ * version. The application may check its own version against the remote
* host's version and determine whether supporting the remote host
* is possible.
*
+ * The `completed' callback will be called once the protocol has completed,
+ * either successfully or with an error. The status of the protocol is
+ * delivered to application with the callback.
+ *
* The `context' is passed as argument to all of the above callback
* functions.
*
***/
void silc_ske_set_callbacks(SilcSKE ske,
- SilcSKESendPacketCb send_packet,
- SilcSKECb payload_receive,
SilcSKEVerifyCb verify_key,
- SilcSKECb proto_continue,
- SilcSKECheckVersion check_version,
+ SilcSKECheckVersionCb check_version,
+ SilcSKECompletionCb completed,
void *context);
/****f* silcske/SilcSKEAPI/silc_ske_initiator_start
*
* SYNOPSIS
*
- * SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
- * SilcSocketConnection sock,
- * SilcSKEStartPayload
- * *start_payload);
+ * SilcAsyncOperation
+ * silc_ske_initiator_start(SilcSKE ske,
+ * SilcPacketStream stream,
+ * SilcSKEStartPayload start_payload);
*
* DESCRIPTION
*
- * Starts the SILC Key Exchange protocol for initiator. The connection
- * to the responder end must be established before calling this function
- * and the connecting socket must be sent as argument. This function
- * creates the Key Exchange Start Payload which includes all our
- * configured security properties. This payload is then sent to the
- * responder 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. The caller must not free
- * the `start_payload' since the SKE library will save it.
+ * Starts the SILC Key Exchange protocol as initiator. The completion
+ * callback that was set in silc_ske_set_callbacks will be called once
+ * the protocol has completed.
*
- * Before calling this function application calls the
- * silc_ske_assemble_security_properties which returns the `start_payload'
- * which application must provide for this function.
+ * The `stream' is the network connection to the remote host. Note that
+ * SKE library will take over the packet stream `stream' while the
+ * protocol is in process. The application will not receive any packets
+ * for `stream' after this function is called. The `stream' is turned
+ * over to application once the completion callback is called.
*
- * After calling this function the application must wait for reply
- * from the responder.
+ * The `start_payload' includes all configured security properties that
+ * will be sent to the responder. The `start_payload' must be provided.
+ * It can be created by calling silc_ske_assemble_security_properties
+ * function. The caller must not free the payload once it has been
+ * given as argument to this function.
*
- ***/
-SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
-#if 0
- SilcSocketConnection sock,
-#endif
- SilcSKEStartPayload *start_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_initiator_phase_1
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
- * SilcBuffer start_payload);
- *
- * DESCRIPTION
- *
- * Function called after ske_initiator_start fuction. This receives
- * the responder's Key Exchange Start payload which includes the
- * security properties selected by the responder from our payload
- * sent in the silc_ske_initiator_start function. The `start_payload'
- * is the received payload and the application must send it as argument.
- *
- * After calling this function the application must call immediately,
- * or with short timeout, the silc_ske_initiator_phase_2 function.
+ * This function returns SilcAsyncOperation operation context which can
+ * be used to control the protocol from the application. Application may
+ * for example safely abort the protocol at any point, if needed. Returns
+ * NULL on error.
*
***/
-SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
- SilcBuffer start_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_initiator_phase_2
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
- * SilcPublicKey public_key,
- * SilcPrivateKey private_key,
- * SilcSKEPKType pk_type)
- *
- * DESCRIPTION
- *
- * This function continues the SKE session after the initiator has
- * called the silc_ske_initiator_phase_1. After that function returns
- * the application should call immediately, or with short timeout, this
- * function which will continue with the session, and sends next phase
- * packet to the responder. The caller must provide the caller's
- * public key and private key as argument, since the public key is
- * sent to the responder, and the private key is be used to generate
- * digital signature.
- *
- * After this function the application must wait for reply from the
- * responder.
- *
- ***/
-SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
- SilcPublicKey public_key,
- SilcPrivateKey private_key,
- SilcSKEPKType pk_type);
-
-/****f* silcske/SilcSKEAPI/silc_ske_initiator_finish
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
- * SilcBuffer ke_payload);
- *
- * DESCRIPTION
- *
- * Receives the reply from the responder and processes it. The
- * `ke_payload' is the reply and application must provide it as argument.
- * This function will verify the responder's public key, by calling
- * the `verify_key' callback that was set with silc_ske_set_callbacks
- * function.
- *
- * If this function returns error, no callbacks will be called. If
- * this function needs to verify remote end's public key, this will
- * return SILC_SKE_STATUS_PENDING, which indicates application that
- * SKE is performing asynchronous operation and is in pending status.
- * When in this status application must not continue with calling
- * any other SKE routine. The asynchronous operation is the `verify_key'
- * callback, which application completes by calling its completion
- * callback. After completion the SKE libary will call the
- * `proto_continue' callback, to indicate application that pending
- * status is over and it is safe to continue the execution of SKE,
- * which application does by calling the silc_ske_end function.
- *
- * If this function returns SILC_SKE_STATUS_OK, it will not call the
- * `verify_key' callback, however, it will or has already called the
- * `proto_continue' callback.
- *
- * Application must not continue execution of the SKE before library
- * has called the `proto_continue' callback. After it is called
- * the application finishes SKE session by calling silc_ske_end
- * function.
- *
- ***/
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
- SilcBuffer ke_payload);
+SilcAsyncOperation
+silc_ske_initiator_start(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcSKEStartPayload start_payload);
/****f* silcske/SilcSKEAPI/silc_ske_responder_start
*
* SYNOPSIS
*
- * SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
- * SilcSocketConnection sock,
- * const char *version,
- * SilcBuffer start_payload,
- * SilcSKESecurityPropertyFlag
- * flags);
- *
- * DESCRIPTION
- *
- * Starts Key Exchange protocol for responder. The application has
- * received initiator's first packet from network and it must provide
- * it as `start_payload' argument to this function. The function
- * processes the packet and makes security property selection from
- * the initiator's proposal. The `version' is the responder's version
- * that will be sent in reply to the initiator. The `flags' indicates
- * SilcSKESecurityPropertyFlag flags that responder enforces for the
- * initiator. Responder may, for example, enforce that the PFS
- * will be performed in rekey. This example can be done by providing
- * SILC_SKE_SP_FLAG_PFS as `flags'. The `flags' is a bit mask of
- * enforced flags.
- *
- * After this function the responder calls immediately, or with short
- * timeout the silc_ske_responder_phase_1 function.
- *
- ***/
-SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
-#if 0
- SilcSocketConnection sock,
-#endif
- const char *version,
- SilcBuffer start_payload,
- SilcSKESecurityPropertyFlag flags);
-
-/****f* silcske/SilcSKEAPI/silc_ske_responder_phase_1
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske);
- *
- * DESCRIPTION
- *
- * This function is called after the silc_ske_responder_start, and
- * is used to send our reply to the initiator. This function is
- * called either immediately, or with short timeout, after the
- * silc_ske_responder_start function returned.
- *
- * After this function the responder must wait for reply from the
- * initiator.
- *
- ***/
-SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske);
-
-/****f* silcske/SilcSKEAPI/silc_ske_responder_phase_2
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
- * SilcBuffer ke_payload);
- *
- * DESCRIPTION
- *
- * Receives the reply from the initiator and procedses it. The
- * `ke_payload' is the reply and application must provide it as argument.
- * This function will verify the remote host's public key, by calling
- * the `verify_key' callback that was set with silc_ske_set_callbacks
- * function.
- *
- * If this function returns error, no callbacks will be called. If
- * this function needs to verify remote end's public key, this will
- * return SILC_SKE_STATUS_PENDING, which indicates application that
- * SKE is performing asynchronous operation and is in pending status.
- * When in this status application must not continue with calling
- * any other SKE routine. The asynchronous operation is the `verify_key'
- * callback, which application completes by calling its completion
- * callback. After completion the SKE libary will call the
- * `proto_continue' callback, to indicate application that pending
- * status is over and it is safe to continue the execution of SKE,
- * which application does by calling the silc_ske_responder_finish
- * function.
- *
- * If this function returns SILC_SKE_STATUS_OK, it will not call the
- * `verify_key' callback, however, it will or has already called the
- * `proto_continue' callback.
- *
- * Application must not continue execution of the SKE before library
- * has called the `proto_continue' callback. After it is called
- * the application calls the silc_ske_responder_finish function.
- *
- ***/
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
- SilcBuffer ke_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_responder_finish
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
- * SilcPublicKey public_key,
- * SilcPrivateKey private_key,
- * SilcSKEPKType pk_type);
- *
- * DESCRIPTION
- *
- * This function finishes the responder's SKE session, and this function
- * is called either immediately, or with short timeout, after the
- * silc_ske_responder_phase_2 returned. This will send our reply to
- * the initiator. The caller must provide the caller's public key and
- * private key as argument, since the public key is sent to the responder,
- * and the private key is be used to generate digital signature.
- *
- * After this function the application must wait for the end indication
- * from the intiator, and when it is received the silc_ske_end is called.
- *
- ***/
-SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
- SilcPublicKey public_key,
- SilcPrivateKey private_key,
- SilcSKEPKType pk_type);
-
-/****f* silcske/SilcSKEAPI/silc_ske_end
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_end(SilcSKE ske);
+ * SilcAsyncOperation
+ * silc_ske_responder_start(SilcSKE ske,
+ * SilcPacketStream stream,
+ * const char *version,
+ * SilcBuffer start_payload,
+ * SilcSKESecurityPropertyFlag flags);
*
* DESCRIPTION
*
- * The Key Exchange protocol is ended by calling this function. This
- * must not be called until the keys are processed by calling the
- * silc_ske_process_key_material function. The protocol prohibits
- * calling this function before key material is processed properly.
+ * Starts SILC Key Exchange protocol as responder. The completion
+ * callback that was set in silc_ske_set_callbacks will be called once
+ * the protocol has completed.
*
- * This function is for both initiator and responder. After calling
- * this function initiator must wait for end indication from the
- * responder. After that the silc_ske_free may be called. The responder
- * calls this function after it has received the intiator's end
- * indication.
+ * The `stream' is the network connection to the remote host. Note that
+ * SKE library will take over the packet stream `stream' while the
+ * protocol is in process. The application will not receive any packets
+ * for `stream' after this function is called. The `stream' is turned
+ * over to application once the completion callback is called.
*
- * NOTES
+ * The application has received initiator's first packet from network
+ * and it must provide it as `start_payload' argument to this function.
+ * The function processes the packet and makes security property selection
+ * from the initiator's proposal. The `version' is the responder's version
+ * that will be sent in reply to the initiator. The `flags' indicates
+ * SilcSKESecurityPropertyFlag flags that responder supports and enforces
+ * for the initiator. Responder may, for example, enforce that the PFS
+ * will be performed in rekey.
*
- * Initiator must not start using the negotiated key material before
- * calling this function or before remote end has sent its end
- * indication. Only after that the key material may be taken in use.
+ * This function returns SilcAsyncOperation operation context which can
+ * be used to control the protocol from the application. Application may
+ * for example safely abort the protocol at any point, if needed. Returns
+ * NULL on error.
*
***/
-SilcSKEStatus silc_ske_end(SilcSKE ske);
-
-/****f* silcske/SilcSKEAPI/silc_ske_abort
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status);
- *
- * DESCRIPTION
- *
- * Aborts the Key Exchange protocol. This is called if error occurs
- * while performing the protocol. The status argument is the error
- * status and it is sent to the remote end.
- *
- ***/
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status);
+SilcAsyncOperation
+silc_ske_responder_start(SilcSKE ske,
+ SilcPacketStream stream,
+ const char *version,
+ SilcBuffer start_payload,
+ SilcSKESecurityPropertyFlag flags);
+
+SilcAsyncOperation
+silc_ske_rekey_initiator_start(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcSKERekeyMaterial rekey);
+
+SilcAsyncOperation
+silc_ske_rekey_responder_start(SilcSKE ske,
+ SilcPacketStream stream,
+ SilcBuffer ke_payload,
+ SilcSKERekeyMaterial rekey);
/****f* silcske/SilcSKEAPI/silc_ske_assemble_security_properties
*
* SYNOPSIS
*
- * SilcSKEStatus
+ * SilcSKEStartPayload
* silc_ske_assemble_security_properties(SilcSKE ske,
* SilcSKESecurityPropertyFlag flags,
- * const char *version,
- * SilcSKEStartPayload
- * **return_payload);
+ * const char *version);
*
* DESCRIPTION
*
* Assembles security properties to Key Exchange Start Payload to be
- * sent to the remote end. This checks system wide (SILC system, that is)
- * settings and chooses from those. However, if other properties
+ * sent to the remote end. This checks system wide (SILC system, that is)
+ * settings and chooses from those. However, if other properties
* should be used this function is easy to replace by another function,
* as, this function is called by the caller of the library and not
- * by the SKE library itself. The assembled payload is returned into
- * the `return_payload' pointer.
+ * by the SKE library itself. Returns NULL on error.
*
***/
-SilcSKEStatus
+SilcSKEStartPayload
silc_ske_assemble_security_properties(SilcSKE ske,
SilcSKESecurityPropertyFlag flags,
- const char *version,
- SilcSKEStartPayload **return_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_select_security_properties
- *
- * SYNOPSIS
- *
- * SilcSKEStatus
- * silc_ske_select_security_properties(SilcSKE ske,
- * const char *version,
- * SilcSKEStartPayload *payload,
- * SilcSKEStartPayload *remote_payload);
- *
- * DESCRIPTION
- *
- * Parses the Key Exchange Start Payload indicated by `remote_payload',
- * and selects the security properties properties from it, and puts the
- * selection into the `payload'. This always attempts to select the
- * best security properties from the payload, and it always selects
- * one of each kind of security property, as this is dictated by the
- * protocol. The `version' is our version, that we will put to the
- * `payload', since the `payload' is usually sent to the remote end.
- * the `check_version' callback will be called in this function so
- * that application can do version check with the remote end.
- *
- ***/
-SilcSKEStatus
-silc_ske_select_security_properties(SilcSKE ske,
- const char *version,
- SilcSKEStartPayload *payload,
- SilcSKEStartPayload *remote_payload);
-
-/****f* silcske/SilcSKEAPI/silc_ske_process_key_material
- *
- * SYNOPSIS
- *
- * SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
- * SilcUInt32 req_iv_len,
- * SilcUInt32 req_enc_key_len,
- * SilcUInt32 req_hmac_key_len,
- * SilcSKEKeyMaterial *key);
- *
- * DESCRIPTION
- *
- * This function is used by the application to process the key material
- * negotiated with the SKE session, to actually produce the keys that
- * is to be used in SILC protocol. The key processing is defined by the
- * protocol. The `req_iv_len', `req_enc_key_len' and `req_hmac_key_len'
- * are the request IV length, requested encryption/decrypt key length,
- * and requested HMAC key length, respectively, and they cannot be
- * zero (0). They tell the function how long the keys should be, and
- * it will produce the requested length keys for the application.
- * The key material is returned in to the `key', which the caller must
- * free.
- *
- ***/
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
- SilcUInt32 req_iv_len,
- SilcUInt32 req_enc_key_len,
- SilcUInt32 req_hmac_key_len,
- SilcSKEKeyMaterial *key);
-
-/****f* silcske/SilcSKEAPI/silc_ske_process_key_material_data
- *
- * SYNOPSIS
- *
- * SilcSKEStatus
- * silc_ske_process_key_material_data(unsigned char *data,
- * SilcUInt32 data_len,
- * SilcUInt32 req_iv_len,
- * SilcUInt32 req_enc_key_len,
- * SilcUInt32 req_hmac_key_len,
- * SilcHash hash,
- * SilcSKEKeyMaterial *key);
- *
- * DESCRIPTION
- *
- * This function is equivalent to silc_ske_process_key_material, except
- * that the caller provides the raw key material as argument, the `data'
- * and `data_len'. This is special utility function provided for the
- * application, if it needs to generate key material as the protocol
- * defines for some other purpose than strictly SILC session key usage.
- * Hence, this function can be used outside SKE protocol to just produce
- * key material from some raw data. The `hash' is a hash algorithm that
- * is used as part of key processing, and caller must provide it.
- *
- ***/
-SilcSKEStatus
-silc_ske_process_key_material_data(unsigned char *data,
- SilcUInt32 data_len,
- SilcUInt32 req_iv_len,
- SilcUInt32 req_enc_key_len,
- SilcUInt32 req_hmac_key_len,
- SilcHash hash,
- SilcSKEKeyMaterial *key);
-
-/****f* silcske/SilcSKEAPI/silc_ske_free_key_material
- *
- * SYNOPSIS
- *
- * void silc_ske_free_key_material(SilcSKEKeyMaterial *key);
- *
- * DESCRIPTION
- *
- * Frees the key material indicated by `key', and all data in it.
- *
- ***/
-void silc_ske_free_key_material(SilcSKEKeyMaterial *key);
+ const char *version);
/****f* silcske/SilcSKEAPI/silc_ske_parse_version
*
* SYNOPSIS
*
- * bool silc_ske_parse_version(SilcSKE ske,
+ * bool silc_ske_parse_version(SilcSKE ske,
* SilcUInt32 *protocol_version,
* char **protocol_version_string,
- * SilcUInt32 *software_version,
+ * SilcUInt32 *software_version,
* char **software_version_string,
* char **vendor_version);
*
* string was successfully parsed.
*
***/
-bool silc_ske_parse_version(SilcSKE ske,
+bool silc_ske_parse_version(SilcSKE ske,
SilcUInt32 *protocol_version,
char **protocol_version_string,
- SilcUInt32 *software_version,
+ SilcUInt32 *software_version,
char **software_version_string,
char **vendor_version);