+/* 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. */
+
+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;
+
+ /* Add to list */
+ if (silc_hmac_list == NULL)
+ silc_hmac_list = silc_dlist_init();
+ silc_dlist_add(silc_hmac_list, new);
+
+ return TRUE;
+}
+
+/* Unregister a HMAC from the SILC. */
+
+bool silc_hmac_unregister(SilcHmacObject *hmac)
+{
+ SilcHmacObject *entry;
+
+ SILC_LOG_DEBUG(("Unregistering HMAC"));
+
+ if (!silc_hmac_list)
+ return FALSE;