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