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