1/* LibTomCrypt, modular cryptographic library -- Tom St Denis
2 *
3 * LibTomCrypt is a library that provides various cryptographic
4 * algorithms in a highly modular and flexible manner.
5 *
6 * The library is free for all purposes without any express
7 * guarantee it works.
8 *
9 * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com
10 */
11
12#include "tomcrypt.h"
13
14/**
15  @file chc.c
16  CHC support. (Tom St Denis)
17*/
18
19#ifdef CHC_HASH
20
21#define UNDEFED_HASH  -17
22
23/* chc settings */
24static int            cipher_idx=UNDEFED_HASH,        /* which cipher */
25                      cipher_blocksize;               /* blocksize of cipher */
26
27
28const struct ltc_hash_descriptor chc_desc = {
29   "chc_hash", 12, 0, 0, { 0 }, 0,
30   &chc_init,
31   &chc_process,
32   &chc_done,
33   &chc_test,
34   NULL
35};
36
37/**
38  Initialize the CHC state with a given cipher
39  @param cipher  The index of the cipher you wish to bind
40  @return CRYPT_OK if successful
41*/
42int chc_register(int cipher)
43{
44   int err, kl, idx;
45
46   if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
47      return err;
48   }
49
50   /* will it be valid? */
51   kl = cipher_descriptor[cipher].block_length;
52
53   /* must be >64 bit block */
54   if (kl <= 8) {
55      return CRYPT_INVALID_CIPHER;
56   }
57
58   /* can we use the ideal keysize? */
59   if ((err = cipher_descriptor[cipher].keysize(&kl)) != CRYPT_OK) {
60      return err;
61   }
62   /* we require that key size == block size be a valid choice */
63   if (kl != cipher_descriptor[cipher].block_length) {
64      return CRYPT_INVALID_CIPHER;
65   }
66
67   /* determine if chc_hash has been register_hash'ed already */
68   if ((err = hash_is_valid(idx = find_hash("chc_hash"))) != CRYPT_OK) {
69      return err;
70   }
71
72   /* store into descriptor */
73   hash_descriptor[idx].hashsize  =
74   hash_descriptor[idx].blocksize = cipher_descriptor[cipher].block_length;
75
76   /* store the idx and block size */
77   cipher_idx       = cipher;
78   cipher_blocksize = cipher_descriptor[cipher].block_length;
79   return CRYPT_OK;
80}
81
82/**
83   Initialize the hash state
84   @param md   The hash state you wish to initialize
85   @return CRYPT_OK if successful
86*/
87int chc_init(hash_state *md)
88{
89   symmetric_key *key;
90   unsigned char  buf[MAXBLOCKSIZE];
91   int            err;
92
93   LTC_ARGCHK(md != NULL);
94
95   /* is the cipher valid? */
96   if ((err = cipher_is_valid(cipher_idx)) != CRYPT_OK) {
97      return err;
98   }
99
100   if (cipher_blocksize != cipher_descriptor[cipher_idx].block_length) {
101      return CRYPT_INVALID_CIPHER;
102   }
103
104   if ((key = XMALLOC(sizeof(*key))) == NULL) {
105      return CRYPT_MEM;
106   }
107
108   /* zero key and what not */
109   zeromem(buf, cipher_blocksize);
110   if ((err = cipher_descriptor[cipher_idx].setup(buf, cipher_blocksize, 0, key)) != CRYPT_OK) {
111      XFREE(key);
112      return err;
113   }
114
115   /* encrypt zero block */
116   cipher_descriptor[cipher_idx].ecb_encrypt(buf, md->chc.state, key);
117
118   /* zero other members */
119   md->chc.length = 0;
120   md->chc.curlen = 0;
121   zeromem(md->chc.buf, sizeof(md->chc.buf));
122   XFREE(key);
123   return CRYPT_OK;
124}
125
126/*
127   key    <= state
128   T0,T1  <= block
129   T0     <= encrypt T0
130   state  <= state xor T0 xor T1
131*/
132static int chc_compress(hash_state *md, unsigned char *buf)
133{
134   unsigned char  T[2][MAXBLOCKSIZE];
135   symmetric_key *key;
136   int            err, x;
137
138   if ((key = XMALLOC(sizeof(*key))) == NULL) {
139      return CRYPT_MEM;
140   }
141   if ((err = cipher_descriptor[cipher_idx].setup(md->chc.state, cipher_blocksize, 0, key)) != CRYPT_OK) {
142      XFREE(key);
143      return err;
144   }
145   XMEMCPY(T[1], buf, cipher_blocksize);
146   cipher_descriptor[cipher_idx].ecb_encrypt(buf, T[0], key);
147   for (x = 0; x < cipher_blocksize; x++) {
148       md->chc.state[x] ^= T[0][x] ^ T[1][x];
149   }
150   XFREE(key);
151#ifdef LTC_CLEAN_STACK
152   zeromem(T, sizeof(T));
153   zeromem(&key, sizeof(key));
154#endif
155   return CRYPT_OK;
156}
157
158/* function for processing blocks */
159int _chc_process(hash_state * md, const unsigned char *buf, unsigned long len);
160HASH_PROCESS(_chc_process, chc_compress, chc, (unsigned long)cipher_blocksize)
161
162/**
163   Process a block of memory though the hash
164   @param md   The hash state
165   @param in   The data to hash
166   @param inlen  The length of the data (octets)
167   @return CRYPT_OK if successful
168*/
169int chc_process(hash_state * md, const unsigned char *in, unsigned long inlen)
170{
171   int err;
172
173   LTC_ARGCHK(md   != NULL);
174   LTC_ARGCHK(in  != NULL);
175
176   /* is the cipher valid? */
177   if ((err = cipher_is_valid(cipher_idx)) != CRYPT_OK) {
178      return err;
179   }
180   if (cipher_blocksize != cipher_descriptor[cipher_idx].block_length) {
181      return CRYPT_INVALID_CIPHER;
182   }
183
184   return _chc_process(md, in, inlen);
185}
186
187/**
188   Terminate the hash to get the digest
189   @param md   The hash state
190   @param out [out] The destination of the hash (length of the block size of the block cipher)
191   @return CRYPT_OK if successful
192*/
193int chc_done(hash_state *md, unsigned char *out)
194{
195    int err;
196
197    LTC_ARGCHK(md   != NULL);
198    LTC_ARGCHK(out  != NULL);
199
200    /* is the cipher valid? */
201    if ((err = cipher_is_valid(cipher_idx)) != CRYPT_OK) {
202       return err;
203    }
204    if (cipher_blocksize != cipher_descriptor[cipher_idx].block_length) {
205       return CRYPT_INVALID_CIPHER;
206    }
207
208    if (md->chc.curlen >= sizeof(md->chc.buf)) {
209       return CRYPT_INVALID_ARG;
210    }
211
212    /* increase the length of the message */
213    md->chc.length += md->chc.curlen * 8;
214
215    /* append the '1' bit */
216    md->chc.buf[md->chc.curlen++] = (unsigned char)0x80;
217
218    /* if the length is currently above l-8 bytes we append zeros
219     * then compress.  Then we can fall back to padding zeros and length
220     * encoding like normal.
221     */
222    if (md->chc.curlen > (unsigned long)(cipher_blocksize - 8)) {
223        while (md->chc.curlen < (unsigned long)cipher_blocksize) {
224            md->chc.buf[md->chc.curlen++] = (unsigned char)0;
225        }
226        chc_compress(md, md->chc.buf);
227        md->chc.curlen = 0;
228    }
229
230    /* pad upto l-8 bytes of zeroes */
231    while (md->chc.curlen < (unsigned long)(cipher_blocksize - 8)) {
232        md->chc.buf[md->chc.curlen++] = (unsigned char)0;
233    }
234
235    /* store length */
236    STORE64L(md->chc.length, md->chc.buf+(cipher_blocksize-8));
237    chc_compress(md, md->chc.buf);
238
239    /* copy output */
240    XMEMCPY(out, md->chc.state, cipher_blocksize);
241
242#ifdef LTC_CLEAN_STACK
243    zeromem(md, sizeof(hash_state));
244#endif
245    return CRYPT_OK;
246}
247
248/**
249  Self-test the hash
250  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
251*/
252int chc_test(void)
253{
254   static const struct {
255      unsigned char *msg,
256                     md[MAXBLOCKSIZE];
257      int            len;
258   } tests[] = {
259{
260   (unsigned char *)"hello world",
261   { 0xcf, 0x57, 0x9d, 0xc3, 0x0a, 0x0e, 0xea, 0x61,
262     0x0d, 0x54, 0x47, 0xc4, 0x3c, 0x06, 0xf5, 0x4e },
263   16
264}
265};
266   int x, oldhashidx, idx;
267   unsigned char out[MAXBLOCKSIZE];
268   hash_state md;
269
270   /* AES can be under rijndael or aes... try to find it */
271   if ((idx = find_cipher("aes")) == -1) {
272      if ((idx = find_cipher("rijndael")) == -1) {
273         return CRYPT_NOP;
274      }
275   }
276   oldhashidx = cipher_idx;
277   chc_register(idx);
278
279   for (x = 0; x < (int)(sizeof(tests)/sizeof(tests[0])); x++) {
280       chc_init(&md);
281       chc_process(&md, tests[x].msg, strlen((char *)tests[x].msg));
282       chc_done(&md, out);
283       if (XMEMCMP(out, tests[x].md, tests[x].len)) {
284          return CRYPT_FAIL_TESTVECTOR;
285       }
286   }
287   if (oldhashidx != UNDEFED_HASH) {
288      chc_register(oldhashidx);
289   }
290
291   return CRYPT_OK;
292}
293
294#endif
295
296/* $Source: /cvs/libtom/libtomcrypt/src/hashes/chc/chc.c,v $ */
297/* $Revision: 1.6 $ */
298/* $Date: 2006/11/01 09:28:17 $ */
299