Moved silc_client_ch[u]mode[_char] to client library from silc/.
[silc.git] / lib / silccrypt / silchmac.c
1 /*
2
3   silchmac.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * $Id$
22  * $Log$
23  * Revision 1.3  2000/07/14 09:12:24  priikone
24  *      Fixed bug in silc_hmac_make.
25  *
26  * Revision 1.2  2000/07/05 06:08:43  priikone
27  *      Global cosmetic change.
28  *
29  * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
30  *      Imported from internal CVS/Added Log headers.
31  *
32  *
33  */
34
35 #include "silcincludes.h"
36
37 /* Allocates a new SilcHmac object. First argument is the hash function
38    object to tell the hmac which hash function should be used when creating
39    HMAC's. The new SilcHmac object is returned to new_hmac argument. */
40
41 int silc_hmac_alloc(SilcHash hash, SilcHmac *new_hmac)
42 {
43   SILC_LOG_DEBUG(("Allocating new hmac object"));
44
45   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
46   (*new_hmac)->hash = hash;
47   (*new_hmac)->set_key = silc_hmac_set_key;
48   (*new_hmac)->make_hmac = silc_hmac_make;
49   (*new_hmac)->make_hmac_with_key = silc_hmac_make_with_key;
50   (*new_hmac)->make_hmac_truncated = silc_hmac_make_truncated;
51
52   return 1;
53 }
54
55 /* Free's the SilcHmac object. */
56
57 void silc_hmac_free(SilcHmac hmac)
58 {
59   if (hmac)
60     silc_free(hmac);
61 }
62
63 /* Creates the HMAC. The created keyed hash value is returned to 
64    return_hash argument. */
65
66 void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
67                              unsigned int data_len, unsigned char *key,
68                              unsigned int key_len, unsigned char *return_hash)
69 {
70   SilcHash hash = hmac->hash;
71   unsigned char inner_pad[hash->hash->block_len + 1];
72   unsigned char outer_pad[hash->hash->block_len + 1];
73   unsigned char hvalue[hash->hash->hash_len];
74   void *hash_context;
75   int i;
76
77   SILC_LOG_DEBUG(("Making HMAC for message"));
78
79   hash_context = silc_calloc(1, hash->hash->context_len());
80
81   memset(inner_pad, 0, sizeof(inner_pad));
82   memset(outer_pad, 0, sizeof(outer_pad));
83
84   /* If the key length is more than block size of the hash function, the
85      key is hashed. */
86   if (key_len > hash->hash->block_len) {
87     hash->make_hash(hash, key, key_len, hvalue);
88     key = hvalue;
89     key_len = hash->hash->hash_len;
90   }
91
92   /* Copy the key into the pads */
93   memcpy(inner_pad, key, key_len);
94   memcpy(outer_pad, key, key_len);
95
96   /* XOR the key with pads */
97   for (i = 0; i < hash->hash->block_len; i++) {
98     inner_pad[i] ^= 0x36;
99     outer_pad[i] ^= 0x5c;
100   }
101
102   /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
103   hash->hash->init(hash_context);
104   hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
105   hash->hash->update(hash_context, data, data_len);
106   hash->hash->final(hash_context, return_hash);
107   hash->hash->init(hash_context);
108   hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
109   hash->hash->update(hash_context, return_hash, hash->hash->hash_len);
110   hash->hash->final(hash_context, return_hash);
111 }
112
113 /* Create the HMAC. This is thee make_hmac function pointer.  This
114    uses the internal key set with silc_hmac_set_key. */
115
116 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
117                     unsigned int data_len, unsigned char *return_hash)
118 {
119   silc_hmac_make_internal(hmac, data, data_len, hmac->key, 
120                           hmac->key_len, return_hash);
121 }
122
123 /* Creates the HMAC just as above except that the hash value is truncated
124    to the truncated_len sent as argument. NOTE: One should not truncate to
125    less than half of the length of original hash value. However, this 
126    routine allows these dangerous truncations. */
127
128 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
129                               unsigned int data_len,
130                               unsigned int truncated_len,
131                               unsigned char *return_hash)
132 {
133   unsigned char hvalue[hmac->hash->hash->hash_len];
134
135   silc_hmac_make_internal(hmac, data, data_len, 
136                           hmac->key, hmac->key_len, hvalue);
137   memcpy(return_hash, hvalue, truncated_len);
138   memset(hvalue, 0, sizeof(hvalue));
139 }
140
141 /* Creates HMAC just as above except that this doesn't use the internal
142    key. The key is sent as argument to the function. */
143
144 void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
145                              unsigned int data_len, 
146                              unsigned char *key, unsigned int key_len,
147                              unsigned char *return_hash)
148 {
149   silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
150 }
151
152 /* Sets the HMAC key used in the HMAC creation */
153
154 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
155                        unsigned int key_len)
156 {
157   hmac->key = silc_calloc(key_len, sizeof(unsigned char));
158   hmac->key_len = key_len;
159   memcpy(hmac->key, key, key_len);
160 }