Integer type name change.
[silc.git] / lib / silccrypt / silchmac.c
index d3484983c7ce2f0d39fae90c744d79e5232714f7..babadd05788bb6a4aac42eced701b0a0d147fb18 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  silchmac.c
+  silchmac.c 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1999 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 #include "silcincludes.h"
 
+/* HMAC context */
+struct SilcHmacStruct {
+  SilcHmacObject *hmac;
+  SilcHash hash;
+  bool allocated_hash;         /* TRUE if the hash was allocated */
+
+  unsigned char *key;
+  SilcUInt32 key_len;
+
+  unsigned char inner_pad[64];
+  unsigned char outer_pad[64];
+  void *hash_context;
+};
+
 /* List of dynamically registered HMACs. */
 SilcDList silc_hmac_list = NULL;
 
+/* Default hmacs for silc_hmac_register_default(). */
+SilcHmacObject silc_default_hmacs[] =
+{
+  { "hmac-sha1-96", 12 },
+  { "hmac-md5-96", 12 },
+  { "hmac-sha1", 20 },
+  { "hmac-md5", 16 },
+
+  { NULL, 0 }
+};
+
+static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
+                                   SilcUInt32 key_len)
+{
+  SilcHash hash = hmac->hash;
+  unsigned char hvalue[20];
+  int i;
+
+  memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
+  memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
+
+  /* If the key length is more than block size of the hash function, the
+     key is hashed. */
+  if (key_len > hash->hash->block_len) {
+    silc_hash_make(hash, key, key_len, hvalue);
+    key = hvalue;
+    key_len = hash->hash->hash_len;
+  }
+
+  /* Copy the key into the pads */
+  memcpy(hmac->inner_pad, key, key_len);
+  memcpy(hmac->outer_pad, key, key_len);
+
+  /* XOR the key with pads */
+  for (i = 0; i < hash->hash->block_len; i++) {
+    hmac->inner_pad[i] ^= 0x36;
+    hmac->outer_pad[i] ^= 0x5c;
+  }
+}
+
 /* Registers a new HMAC into the SILC. This function is used at the
    initialization of the SILC. */
 
-int silc_hmac_register(SilcHmacObject *hmac)
+bool silc_hmac_register(SilcHmacObject *hmac)
 {
   SilcHmacObject *new;
 
   SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
 
+  /* Check for existing */
+  if (silc_hmac_list) {
+    SilcHmacObject *entry;
+    silc_dlist_start(silc_hmac_list);
+    while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, hmac->name))
+       return FALSE;
+    }
+  }
+
   new = silc_calloc(1, sizeof(*new));
   new->name = strdup(hmac->name);
   new->len = hmac->len;
@@ -47,7 +110,7 @@ int silc_hmac_register(SilcHmacObject *hmac)
 
 /* Unregister a HMAC from the SILC. */
 
-int silc_hmac_unregister(SilcHmacObject *hmac)
+bool silc_hmac_unregister(SilcHmacObject *hmac)
 {
   SilcHmacObject *entry;
 
@@ -58,7 +121,7 @@ int silc_hmac_unregister(SilcHmacObject *hmac)
 
   silc_dlist_start(silc_hmac_list);
   while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
-    if (entry == hmac) {
+    if (hmac == SILC_ALL_HMACS || entry == hmac) {
       silc_dlist_del(silc_hmac_list, entry);
 
       if (silc_dlist_count(silc_hmac_list) == 0) {
@@ -73,12 +136,26 @@ int silc_hmac_unregister(SilcHmacObject *hmac)
   return FALSE;
 }
 
+/* Function that registers all the default hmacs (all builtin ones). 
+   The application may use this to register the default hmacs if
+   specific hmacs in any specific order is not wanted. */
+
+bool silc_hmac_register_default(void)
+{
+  int i;
+
+  for (i = 0; silc_default_hmacs[i].name; i++)
+    silc_hmac_register(&(silc_default_hmacs[i]));
+
+  return TRUE;
+}
+
 /* Allocates a new SilcHmac object of name of `name'.  The `hash' may
    be provided as argument.  If provided it is used as the hash function
    of the HMAC.  If it is NULL then the hash function is allocated and
    the name of the hash algorithm is derived from the `name'. */
 
-int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
+bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
 {
   SilcHmacObject *entry;
 
@@ -98,6 +175,8 @@ int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
 
     if (!silc_hash_alloc(hname, &hash)) {
       silc_free(tmp);
+      silc_free(*new_hmac);
+      *new_hmac = NULL;
       return FALSE;
     }
 
@@ -117,6 +196,8 @@ int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
     }
   }
 
+  silc_free(*new_hmac);
+  *new_hmac = NULL;
   return FALSE;
 }
 
@@ -127,20 +208,41 @@ void silc_hmac_free(SilcHmac hmac)
   if (hmac) {
     if (hmac->allocated_hash)
       silc_hash_free(hmac->hash);
+
+    if (hmac->key) {
+      memset(hmac->key, 0, hmac->key_len);
+      silc_free(hmac->key);
+    }
+
+    silc_free(hmac->hash_context);
     silc_free(hmac);
   }
 }
 
 /* Returns the length of the MAC that the HMAC will produce. */
 
-unsigned int silc_hmac_len(SilcHmac hmac)
+SilcUInt32 silc_hmac_len(SilcHmac hmac)
 {
   return hmac->hmac->len;
 }
 
+/* Get hash context */
+
+SilcHash silc_hmac_get_hash(SilcHmac hmac)
+{
+  return hmac->hash;
+}
+
+/* Return name of hmac */
+
+const char *silc_hmac_get_name(SilcHmac hmac)
+{
+  return hmac->hmac->name;
+}
+
 /* Returns TRUE if HMAC `name' is supported. */
 
-int silc_hmac_is_supported(const char *name)
+bool silc_hmac_is_supported(const char *name)
 {
   SilcHmacObject *entry;
 
@@ -178,17 +280,16 @@ char *silc_hmac_get_supported()
       memcpy(list + len, ",", 1);
       len++;
     }
+    list[len - 1] = 0;
   }
 
-  list[len - 1] = 0;
-
   return list;
 }
 
 /* Sets the HMAC key used in the HMAC creation */
 
 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
-                      unsigned int key_len)
+                      SilcUInt32 key_len)
 {
   if (hmac->key) {
     memset(hmac->key, 0, hmac->key_len);
@@ -199,84 +300,34 @@ void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
   memcpy(hmac->key, key, key_len);
 }
 
-/* Creates the HMAC. The created keyed hash value is returned to 
-   return_hash argument. */
-
-void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
-                            unsigned int data_len, unsigned char *key,
-                            unsigned int key_len, unsigned char *return_hash)
-{
-  SilcHash hash = hmac->hash;
-  unsigned char inner_pad[hash->hash->block_len + 1];
-  unsigned char outer_pad[hash->hash->block_len + 1];
-  unsigned char hvalue[hash->hash->hash_len];
-  unsigned char mac[128];
-  void *hash_context;
-  int i;
-
-  SILC_LOG_DEBUG(("Making HMAC for message"));
-
-  hash_context = silc_calloc(1, hash->hash->context_len());
-
-  memset(inner_pad, 0, sizeof(inner_pad));
-  memset(outer_pad, 0, sizeof(outer_pad));
-
-  /* If the key length is more than block size of the hash function, the
-     key is hashed. */
-  if (key_len > hash->hash->block_len) {
-    silc_hash_make(hash, key, key_len, hvalue);
-    key = hvalue;
-    key_len = hash->hash->hash_len;
-  }
-
-  /* Copy the key into the pads */
-  memcpy(inner_pad, key, key_len);
-  memcpy(outer_pad, key, key_len);
-
-  /* XOR the key with pads */
-  for (i = 0; i < hash->hash->block_len; i++) {
-    inner_pad[i] ^= 0x36;
-    outer_pad[i] ^= 0x5c;
-  }
-
-  /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
-  hash->hash->init(hash_context);
-  hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
-  hash->hash->update(hash_context, data, data_len);
-  hash->hash->final(hash_context, mac);
-  hash->hash->init(hash_context);
-  hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
-  hash->hash->update(hash_context, mac, hash->hash->hash_len);
-  hash->hash->final(hash_context, mac);
-  memcpy(return_hash, mac, hmac->hmac->len);
-  memset(mac, 0, sizeof(mac));
-}
-
 /* Create the HMAC. This is thee make_hmac function pointer.  This
    uses the internal key set with silc_hmac_set_key. */
 
 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
-                   unsigned int data_len, unsigned char *return_hash,
-                   unsigned int *return_len)
+                   SilcUInt32 data_len, unsigned char *return_hash,
+                   SilcUInt32 *return_len)
 {
-  silc_hmac_make_internal(hmac, data, data_len, hmac->key, 
-                         hmac->key_len, return_hash);
-  if (return_len)
-    *return_len = hmac->hmac->len;
+  SILC_LOG_DEBUG(("Making HMAC for message"));
+
+  silc_hmac_init(hmac);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_final(hmac, return_hash, return_len);
 }
 
 /* Creates HMAC just as above except that this doesn't use the internal
    key. The key is sent as argument to the function. */
 
 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
-                            unsigned int data_len, 
-                            unsigned char *key, unsigned int key_len,
+                            SilcUInt32 data_len, 
+                            unsigned char *key, SilcUInt32 key_len,
                             unsigned char *return_hash,
-                            unsigned int *return_len)
+                            SilcUInt32 *return_len)
 {
-  silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
-  if (return_len)
-    *return_len = hmac->hmac->len;
+  SILC_LOG_DEBUG(("Making HMAC for message"));
+
+  silc_hmac_init_with_key(hmac, key, key_len);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_final(hmac, return_hash, return_len);
 }
 
 /* Creates the HMAC just as above except that the hash value is truncated
@@ -285,14 +336,71 @@ void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
    routine allows these dangerous truncations. */
 
 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
-                             unsigned int data_len,
-                             unsigned int truncated_len,
+                             SilcUInt32 data_len,
+                             SilcUInt32 truncated_len,
                              unsigned char *return_hash)
 {
-  unsigned char hvalue[hmac->hash->hash->hash_len];
+  unsigned char hvalue[20];
+
+  SILC_LOG_DEBUG(("Making HMAC for message"));
 
-  silc_hmac_make_internal(hmac, data, data_len, 
-                         hmac->key, hmac->key_len, hvalue);
+  silc_hmac_init(hmac);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_final(hmac, return_hash, NULL);
   memcpy(return_hash, hvalue, truncated_len);
   memset(hvalue, 0, sizeof(hvalue));
 }
+
+/* Init HMAC for silc_hmac_update and silc_hmac_final. */
+
+void silc_hmac_init(SilcHmac hmac)
+{
+  silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
+}
+
+/* Same as above but with specific key */
+
+void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
+                            SilcUInt32 key_len)
+{
+  SilcHash hash = hmac->hash;
+
+  silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
+
+  if (!hmac->hash_context)
+    hmac->hash_context = silc_calloc(1, hash->hash->context_len());
+
+  hash->hash->init(hmac->hash_context);
+  hash->hash->update(hmac->hash_context, hmac->inner_pad, 
+                    hash->hash->block_len);
+}
+
+/* Add data to be used in the MAC computation. */
+
+void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
+                     SilcUInt32 data_len)
+{
+  SilcHash hash = hmac->hash;
+  hash->hash->update(hmac->hash_context, (unsigned char *)data, data_len);
+}
+
+/* Compute the final MAC. */
+
+void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
+                    SilcUInt32 *return_len)
+{
+  SilcHash hash = hmac->hash;
+  unsigned char mac[20];
+
+  hash->hash->final(hmac->hash_context, mac);
+  hash->hash->init(hmac->hash_context);
+  hash->hash->update(hmac->hash_context, hmac->outer_pad, 
+                    hash->hash->block_len);
+  hash->hash->update(hmac->hash_context, mac, hash->hash->hash_len);
+  hash->hash->final(hmac->hash_context, mac);
+  memcpy(return_hash, mac, hmac->hmac->len);
+  memset(mac, 0, sizeof(mac));
+
+  if (return_len)
+    *return_len = hmac->hmac->len;
+}