updates.
[silc.git] / lib / silcske / silcske.c
index 3fef422210bcd3fba9afb023b94889c95c721b85..7d34d61aa2c426e706921c6064fdb51b3bb88bdf 100644 (file)
@@ -71,11 +71,11 @@ void silc_ske_free(SilcSKE ske)
     if (ske->pk)
       silc_free(ske->pk);
     if (ske->x) {
-      silc_mp_clear(ske->x);
+      silc_mp_uninit(ske->x);
       silc_free(ske->x);
     }
     if (ske->KEY) {
-      silc_mp_clear(ske->KEY);
+      silc_mp_uninit(ske->KEY);
       silc_free(ske->KEY);
     }
     if (ske->hash)
@@ -228,7 +228,7 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
-  SilcInt *x, e;
+  SilcMPInt *x, e;
   SilcSKEKEPayload *payload;
   uint32 pk_len;
 
@@ -242,7 +242,7 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
                        silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
                        x);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(x);
+    silc_mp_uninit(x);
     silc_free(x);
     ske->status = status;
     return status;
@@ -252,8 +252,8 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
   /* Do the Diffie Hellman computation, e = g ^ x mod p */
   silc_mp_init(&e);
-  silc_mp_powm(&e, &ske->prop->group->generator, x, 
-              &ske->prop->group->group);
+  silc_mp_pow_mod(&e, &ske->prop->group->generator, x, 
+                 &ske->prop->group->group);
 
   /* Encode the result to Key Exchange Payload. */
 
@@ -266,9 +266,9 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
   if (public_key) {
     payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
     if (!payload->pk_data) {
-      silc_mp_clear(x);
+      silc_mp_uninit(x);
       silc_free(x);
-      silc_mp_clear(&e);
+      silc_mp_uninit(&e);
       silc_free(payload);
       ske->status = SILC_SKE_STATUS_OK;
       return ske->status;
@@ -303,9 +303,9 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
   status = silc_ske_payload_ke_encode(ske, payload, &payload_buf);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(x);
+    silc_mp_uninit(x);
     silc_free(x);
-    silc_mp_clear(&e);
+    silc_mp_uninit(&e);
     silc_free(payload->pk_data);
     silc_free(payload);
     ske->status = status;
@@ -323,62 +323,53 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
   return status;
 }
 
-/* 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. */
+/* An initiator finish final callback that is called to indicate that
+   the SKE protocol may continue. */
 
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
-                                       SilcBuffer ke_payload,
-                                       SilcSKEVerifyCb verify_key,
-                                       void *verify_context,
-                                       SilcSKECb callback,
-                                       void *context)
+typedef struct {
+  SilcSKECb callback;
+  void *context;
+} *SKEInitiatorFinish;
+
+static void silc_ske_initiator_finish_final(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEKEPayload *payload;
-  SilcPublicKey public_key = NULL;
-  SilcInt *KEY;
+  SKEInitiatorFinish finish = (SKEInitiatorFinish)context;
+  SilcSKEKEPayload *payload = ske->ke2_payload;
   unsigned char hash[32];
   uint32 hash_len;
+  SilcPublicKey public_key = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  /* 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;
 
-  /* Decode the payload */
-  status = silc_ske_payload_ke_decode(ske, ke_payload, &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;
-    return status;
+    if (finish->callback)
+      finish->callback(ske, finish->context);
+    silc_free(finish);
+    return;
   }
-  ske->ke2_payload = payload;
-
-  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_powm(KEY, &payload->x, ske->x, &ske->prop->group->group);
-  ske->KEY = KEY;
 
   if (payload->pk_data) {
-    SILC_LOG_DEBUG(("Verifying public key"));
-    
+    /* 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;
-      goto err;
+      if (finish->callback)
+       finish->callback(ske, finish->context);
+      silc_free(finish);
+      return;
     }
-  }
-
-  if (verify_key) {
-    status = (*verify_key)(ske, payload->pk_data, payload->pk_len,
-                          payload->pk_type, verify_context);
-    if (status != SILC_SKE_STATUS_OK)
-      goto err;
 
     SILC_LOG_DEBUG(("Public key is authentic"));
-  }
 
-  if (payload->pk_data) {
     /* Compute the hash value */
     status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
     if (status != SILC_SKE_STATUS_OK)
@@ -408,18 +399,21 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
     memset(hash, 'F', hash_len);
   }
 
-  /* Call the callback. */
-  if (callback)
-    (*callback)(ske, context);
+  ske->status = SILC_SKE_STATUS_OK;
 
-  return status;
+  /* Call the callback. The caller may now continue the SKE protocol. */
+  if (finish->callback)
+    finish->callback(ske, finish->context);
+
+  silc_free(finish);
+  return;
 
  err:
   memset(hash, 'F', sizeof(hash));
   silc_ske_payload_ke_free(payload);
   ske->ke2_payload = NULL;
 
-  silc_mp_clear(ske->KEY);
+  silc_mp_uninit(ske->KEY);
   silc_free(ske->KEY);
   ske->KEY = NULL;
 
@@ -432,6 +426,102 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
     ske->hash = NULL;
   }
 
+  if (status == SILC_SKE_STATUS_OK)
+    ske->status = SILC_SKE_STATUS_ERROR;
+
+  ske->status = status;
+
+  /* Call the callback. */
+  if (finish->callback)
+    finish->callback(ske, finish->context);
+  silc_free(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 `callback' 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,
+                                       SilcSKEVerifyCb verify_key,
+                                       void *verify_context,
+                                       SilcSKECb callback,
+                                       void *context)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKEKEPayload *payload;
+  SilcMPInt *KEY;
+  SKEInitiatorFinish finish;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* 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;
+
+  if (!payload->pk_data && 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;
+
+  finish = silc_calloc(1, sizeof(*finish));
+  finish->callback = callback;
+  finish->context = context;
+
+  if (payload->pk_data && verify_key) {
+    SILC_LOG_DEBUG(("Verifying public key"));
+    
+    (*verify_key)(ske, payload->pk_data, payload->pk_len,
+                 payload->pk_type, verify_context,
+                 silc_ske_initiator_finish_final, finish);
+    
+    /* We will continue to the final state after the public key has
+       been verified by the caller. */
+    return SILC_SKE_STATUS_PENDING;
+  }
+
+  /* Continue to final state */
+  silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, finish);
+
+  return SILC_SKE_STATUS_OK;
+
+ 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;
 
@@ -590,65 +680,70 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
   return status;
 }
 
-/* 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. */
+/* An responder phase 2 final callback that is called to indicate that
+   the SKE protocol may continue. */
 
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
-                                        SilcBuffer ke_payload,
-                                        SilcSKEVerifyCb verify_key,
-                                        void *verify_context,
-                                        SilcSKECb callback,
-                                        void *context)
+typedef struct {
+  SilcSKECb callback;
+  void *context;
+} *SKEResponderPhaseII;
+
+static void silc_ske_responder_phase2_final(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SKEResponderPhaseII finish = (SKEResponderPhaseII)context;
   SilcSKEKEPayload *recv_payload, *send_payload;
-  SilcInt *x, f;
+  SilcMPInt *x, f;
 
-  SILC_LOG_DEBUG(("Start"));
+  recv_payload = ske->ke1_payload;
 
-  /* Decode Key Exchange Payload */
-  status = silc_ske_payload_ke_decode(ske, ke_payload, &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;
+
+  /* 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;
-    return status;
+    if (finish->callback)
+      finish->callback(ske, finish->context);
+    silc_free(finish);
+    return;
   }
 
-  ske->ke1_payload = recv_payload;
-
-  /* Verify the received public key and verify the signature if we are
-     doing mutual authentication. */
+  /* The public key verification was performed only if the Mutual
+     Authentication flag is set. */
   if (ske->start_payload && 
       ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
     SilcPublicKey public_key = NULL;
     unsigned char hash[32];
     uint32 hash_len;
 
-    SILC_LOG_DEBUG(("We are doing mutual authentication"));
-    SILC_LOG_DEBUG(("Verifying public key"));
-    
+    /* Decode the public key */
     if (!silc_pkcs_public_key_decode(recv_payload->pk_data, 
                                     recv_payload->pk_len, 
                                     &public_key)) {
-      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
-      return status;
-    }
-
-    if (verify_key) {
-      status = (*verify_key)(ske, recv_payload->pk_data, recv_payload->pk_len,
-                            recv_payload->pk_type, verify_context);
-      if (status != SILC_SKE_STATUS_OK)
-       return status;
+      ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      if (finish->callback)
+       finish->callback(ske, finish->context);
+      silc_free(finish);
+      return;
     }
 
     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)
-      return status;
+    if (status != SILC_SKE_STATUS_OK) {
+      ske->status = status;
+      if (finish->callback)
+       finish->callback(ske, finish->context);
+      silc_free(finish);
+      return;
+    }
 
     SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
     
@@ -660,8 +755,11 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
       
       SILC_LOG_DEBUG(("Signature don't match"));
       
-      status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-      return status;
+      ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+      if (finish->callback)
+       finish->callback(ske, finish->context);
+      silc_free(finish);
+      return;
     }
     
     SILC_LOG_DEBUG(("Signature is Ok"));
@@ -678,17 +776,21 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
                        silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
                        x);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(x);
+    silc_mp_uninit(x);
     silc_free(x);
-    return status;
+    ske->status = status;
+    if (finish->callback)
+      finish->callback(ske, finish->context);
+    silc_free(finish);
+    return;
   }
 
   SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
 
   /* Do the Diffie Hellman computation, f = g ^ x mod p */
   silc_mp_init(&f);
-  silc_mp_powm(&f, &ske->prop->group->generator, x, 
-              &ske->prop->group->group);
+  silc_mp_pow_mod(&f, &ske->prop->group->generator, x, 
+                 &ske->prop->group->group);
   
   /* Save the results for later processing */
   send_payload = silc_calloc(1, sizeof(*send_payload));
@@ -696,11 +798,89 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
   ske->x = x;
   ske->ke2_payload = send_payload;
 
-  /* Call the callback. */
-  if (callback)
-    (*callback)(ske, context);
+  /* Call the callback. The caller may now continue with the SKE protocol. */
+  ske->status = SILC_SKE_STATUS_OK;
+  if (finish->callback)
+    finish->callback(ske, finish->context);
+  silc_free(finish);
+}
 
-  return status;
+/* 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 `callback' 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,
+                                        SilcSKEVerifyCb verify_key,
+                                        void *verify_context,
+                                        SilcSKECb callback,
+                                        void *context)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKEKEPayload *recv_payload;
+  SKEResponderPhaseII finish;
+
+  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;
+
+  finish = silc_calloc(1, sizeof(*finish));
+  finish->callback = callback;
+  finish->context = context;
+
+  /* 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 && 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;
+      return status;
+    }
+
+    if (recv_payload->pk_data && verify_key) {
+      SILC_LOG_DEBUG(("Verifying public key"));
+
+      (*verify_key)(ske, recv_payload->pk_data, recv_payload->pk_len,
+                   recv_payload->pk_type, verify_context,
+                   silc_ske_responder_phase2_final, finish);
+
+      /* We will continue to the final state after the public key has
+        been verified by the caller. */
+      return SILC_SKE_STATUS_PENDING;
+    }
+  }
+
+  /* Continue to final state */
+  silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, finish);
+
+  return SILC_SKE_STATUS_OK;
 }
 
 /* This functions generates the secret key KEY = e ^ x mod p, and, a hash
@@ -716,7 +896,7 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
-  SilcInt *KEY;
+  SilcMPInt *KEY;
   unsigned char hash[32], sign[1024], *pk;
   uint32 hash_len, sign_len, pk_len;
 
@@ -727,8 +907,8 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
   /* Compute the shared secret key */
   KEY = silc_calloc(1, sizeof(*KEY));
   silc_mp_init(KEY);
-  silc_mp_powm(KEY, &ske->ke1_payload->x, ske->x, 
-              &ske->prop->group->group);
+  silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x, 
+                 &ske->prop->group->group);
   ske->KEY = KEY;
 
   if (public_key && private_key) {
@@ -783,7 +963,7 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
   return status;
 
  err:
-  silc_mp_clear(ske->KEY);
+  silc_mp_uninit(ske->KEY);
   silc_free(ske->KEY);
   ske->KEY = NULL;
   silc_ske_payload_ke_free(ske->ke2_payload);
@@ -1280,9 +1460,9 @@ silc_ske_select_security_properties(SilcSKE ske,
 /* Creates random number such that 1 < rnd < n and at most length
    of len bits. The rnd sent as argument must be initialized. */
 
-SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n, 
+SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt n, 
                                  uint32 len, 
-                                 SilcInt *rnd)
+                                 SilcMPInt *rnd)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   unsigned char *string;
@@ -1425,6 +1605,9 @@ silc_ske_process_key_material_data(unsigned char *data,
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
+    return SILC_SKE_STATUS_ERROR;
+
   buf = silc_buffer_alloc(1 + data_len);
   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
   silc_buffer_format(buf,