1 /*
2 * Copyright (C) 2021 - This file is part of libecc project
3 *
4 * Authors:
5 * Ryad BENADJILA <ryadbenadjila@gmail.com>
6 * Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr>
7 *
8 * This software is licensed under a dual BSD and GPL v2 license.
9 * See LICENSE file at the root folder of the project.
10 */
11 #include "mdc2.h"
12
13 /* Include DES helpers */
14 #include "tdes.h"
15
mdc2_set_padding_type(mdc2_context * ctx,padding_type p)16 ATTRIBUTE_WARN_UNUSED_RET int mdc2_set_padding_type(mdc2_context *ctx,
17 padding_type p)
18 {
19 int ret;
20
21 MDC2_HASH_CHECK_INITIALIZED(ctx, ret, err);
22
23 /* We cannot change the padding type after the first update */
24 MUST_HAVE((ctx->mdc2_total == 0), ret, err);
25
26 if((p != ISOIEC10118_TYPE1) && (p != ISOIEC10118_TYPE2)){
27 ret = -1;
28 goto err;
29 }
30
31 ctx->padding = p;
32
33 ret = 0;
34
35 err:
36 return ret;
37 }
38
39 /* MDC-2 core processing. Returns 0 on success, -1 on error. */
mdc2_process(mdc2_context * ctx,const u8 data[MDC2_BLOCK_SIZE])40 ATTRIBUTE_WARN_UNUSED_RET static inline int mdc2_process(mdc2_context *ctx,
41 const u8 data[MDC2_BLOCK_SIZE])
42 {
43 int ret;
44 unsigned int j;
45 u8 V[8], W[8];
46 u8 *A, *B;
47 des_context des_ctx;
48
49 /* Get the current internal state in A and B */
50 A = (u8*)&(ctx->mdc2_state[0]);
51 B = (u8*)&(ctx->mdc2_state[8]);
52
53 A[0] = (u8)((A[0] & 0x9f) | 0x40);
54 B[0] = (u8)((B[0] & 0x9f) | 0x20);
55 /* Set odd parity */
56 for(j = 0; j < 8; j++){
57 A[j] = odd_parity[A[j]];
58 B[j] = odd_parity[B[j]];
59 }
60 /* Compute V_i = M_i + E(M_i, A_i) */
61 ret = local_memset(&des_ctx, 0, sizeof(des_context)); EG(ret, err);
62 ret = des_set_key(&des_ctx, A, DES_ENCRYPTION); EG(ret, err);
63 ret = des(&des_ctx, &data[0], V); EG(ret, err);
64 for(j = 0; j < 8; j++){
65 V[j] = (V[j] ^ data[j]);
66 }
67 /* Compute W_i = M_i + E(M_i, B_i) */
68 ret = local_memset(&des_ctx, 0, sizeof(des_context)); EG(ret, err);
69 ret = des_set_key(&des_ctx, B, DES_ENCRYPTION); EG(ret, err);
70 ret = des(&des_ctx, &data[0], W); EG(ret, err);
71 for(j = 0; j < 8; j++){
72 W[j] = (W[j] ^ data[j]);
73 }
74 /* Cross the results */
75 /* In A */
76 ret = local_memcpy(&A[0], &V[0], 4); EG(ret, err);
77 ret = local_memcpy(&A[4], &W[4], 4); EG(ret, err);
78 /* In B */
79 ret = local_memcpy(&B[0], &W[0], 4); EG(ret, err);
80 ret = local_memcpy(&B[4], &V[4], 4); EG(ret, err);
81
82 err:
83 return ret;
84 }
85
86 /* Init hash function. Returns 0 on success, -1 on error. */
mdc2_init(mdc2_context * ctx)87 ATTRIBUTE_WARN_UNUSED_RET int mdc2_init(mdc2_context *ctx)
88 {
89 int ret;
90
91 MUST_HAVE((ctx != NULL), ret, err);
92
93 /* Sanity check on size */
94 MUST_HAVE((MDC2_DIGEST_SIZE <= MAX_DIGEST_SIZE), ret, err);
95
96 ctx->mdc2_total = 0;
97 /* Initialize A1 */
98 ret = local_memset(&(ctx->mdc2_state[0]), 0x52, 8); EG(ret, err);
99 /* Initialize B1 */
100 ret = local_memset(&(ctx->mdc2_state[8]), 0x25, 8); EG(ret, err);
101 /* Initialize default padding type */
102 ctx->padding = ISOIEC10118_TYPE1;
103
104 /* Tell that we are initialized */
105 ctx->magic = MDC2_HASH_MAGIC;
106
107 ret = 0;
108
109 err:
110 return ret;
111 }
112
mdc2_update(mdc2_context * ctx,const u8 * input,u32 ilen)113 ATTRIBUTE_WARN_UNUSED_RET int mdc2_update(mdc2_context *ctx, const u8 *input, u32 ilen)
114 {
115 const u8 *data_ptr = input;
116 u32 remain_ilen = ilen;
117 u16 fill;
118 u8 left;
119 int ret;
120
121 MUST_HAVE((input != NULL) || (ilen == 0), ret, err);
122 MDC2_HASH_CHECK_INITIALIZED(ctx, ret, err);
123
124 /* Nothing to process, return */
125 if (ilen == 0) {
126 ret = 0;
127 goto err;
128 }
129
130 /* Get what's left in our local buffer */
131 left = (ctx->mdc2_total & 0xF);
132 fill = (u16)(MDC2_BLOCK_SIZE - left);
133
134 ctx->mdc2_total += ilen;
135
136 if ((left > 0) && (remain_ilen >= fill)) {
137 /* Copy data at the end of the buffer */
138 ret = local_memcpy(ctx->mdc2_buffer + left, data_ptr, fill); EG(ret, err);
139 ret = mdc2_process(ctx, ctx->mdc2_buffer); EG(ret, err);
140 data_ptr += fill;
141 remain_ilen -= fill;
142 left = 0;
143 }
144
145 while (remain_ilen >= MDC2_BLOCK_SIZE) {
146 ret = mdc2_process(ctx, data_ptr); EG(ret, err);
147 data_ptr += MDC2_BLOCK_SIZE;
148 remain_ilen -= MDC2_BLOCK_SIZE;
149 }
150
151 if (remain_ilen > 0) {
152 ret = local_memcpy(ctx->mdc2_buffer + left, data_ptr, remain_ilen); EG(ret, err);
153 }
154
155 ret = 0;
156
157 err:
158 return ret;
159 }
160
161 /* Finalize. Returns 0 on success, -1 on error.*/
mdc2_final(mdc2_context * ctx,u8 output[MDC2_DIGEST_SIZE])162 ATTRIBUTE_WARN_UNUSED_RET int mdc2_final(mdc2_context *ctx, u8 output[MDC2_DIGEST_SIZE])
163 {
164 int ret;
165 unsigned int i;
166 u8 pad_byte;
167
168 MUST_HAVE((output != NULL), ret, err);
169 MDC2_HASH_CHECK_INITIALIZED(ctx, ret, err);
170
171 if(ctx->padding == ISOIEC10118_TYPE1){
172 /* "Padding method 1" in ISO-IEC-10118 */
173 /* This is our final step, so we proceed with the padding: the last block
174 * is padded with zeroes.
175 */
176 pad_byte = 0x00;
177 if((ctx->mdc2_total % MDC2_BLOCK_SIZE) != 0){
178 for(i = (ctx->mdc2_total % MDC2_BLOCK_SIZE); i < MDC2_BLOCK_SIZE; i++){
179 ctx->mdc2_buffer[i] = pad_byte;
180 }
181 /* And process the block */
182 ret = mdc2_process(ctx, ctx->mdc2_buffer); EG(ret, err);
183 }
184 }
185 else if(ctx->padding == ISOIEC10118_TYPE2){
186 /* "Padding method 2" in ISO-IEC-10118 */
187 /* This is our final step, so we proceed with the padding: the last block
188 * is appended 0x80 and then padded with zeroes.
189 */
190 ctx->mdc2_buffer[(ctx->mdc2_total % MDC2_BLOCK_SIZE)] = 0x80;
191 pad_byte = 0x00;
192 for(i = ((unsigned int)(ctx->mdc2_total % MDC2_BLOCK_SIZE) + 1); i < MDC2_BLOCK_SIZE; i++){
193 ctx->mdc2_buffer[i] = pad_byte;
194 }
195 /* And process the block */
196 ret = mdc2_process(ctx, ctx->mdc2_buffer); EG(ret, err);
197 }
198 else{
199 /* Unkown padding */
200 ret = -1;
201 goto err;
202 }
203
204 /* Output the hash result */
205 ret = local_memcpy(output, ctx->mdc2_state, MDC2_DIGEST_SIZE); EG(ret, err);
206
207 /* Tell that we are uninitialized */
208 ctx->magic = WORD(0);
209
210 ret = 0;
211
212 err:
213 return ret;
214 }
215
216
217 /*
218 * Scattered version performing init/update/finalize on a vector of buffers
219 * 'inputs' with the length of each buffer passed via 'ilens'. The function
220 * loops on pointers in 'inputs' until it finds a NULL pointer. The function
221 * returns 0 on success, -1 on error.
222 */
mdc2_scattered(const u8 ** inputs,const u32 * ilens,u8 output[MDC2_DIGEST_SIZE],padding_type p)223 ATTRIBUTE_WARN_UNUSED_RET int mdc2_scattered(const u8 **inputs, const u32 *ilens,
224 u8 output[MDC2_DIGEST_SIZE], padding_type p)
225 {
226 mdc2_context ctx;
227 int ret, pos = 0;
228
229 MUST_HAVE((inputs != NULL) && (ilens != NULL) && (output != NULL), ret, err);
230
231 ret = mdc2_init(&ctx); EG(ret, err);
232
233 ret = mdc2_set_padding_type(&ctx, p); EG(ret, err);
234
235 while (inputs[pos] != NULL) {
236 ret = mdc2_update(&ctx, inputs[pos], ilens[pos]); EG(ret, err);
237 pos += 1;
238 }
239
240 ret = mdc2_final(&ctx, output);
241
242 err:
243 return ret;
244 }
245
246 /*
247 * Scattered version performing init/update/finalize on a vector of buffers
248 * 'inputs' with the length of each buffer passed via 'ilens'. The function
249 * loops on pointers in 'inputs' until it finds a NULL pointer. The function
250 * returns 0 on success, -1 on error.
251 */
mdc2_scattered_padding1(const u8 ** inputs,const u32 * ilens,u8 output[MDC2_DIGEST_SIZE])252 ATTRIBUTE_WARN_UNUSED_RET int mdc2_scattered_padding1(const u8 **inputs, const u32 *ilens,
253 u8 output[MDC2_DIGEST_SIZE])
254 {
255 return mdc2_scattered(inputs, ilens, output, ISOIEC10118_TYPE1);
256 }
257
258 /*
259 * Scattered version performing init/update/finalize on a vector of buffers
260 * 'inputs' with the length of each buffer passed via 'ilens'. The function
261 * loops on pointers in 'inputs' until it finds a NULL pointer. The function
262 * returns 0 on success, -1 on error.
263 */
mdc2_scattered_padding2(const u8 ** inputs,const u32 * ilens,u8 output[MDC2_DIGEST_SIZE])264 ATTRIBUTE_WARN_UNUSED_RET int mdc2_scattered_padding2(const u8 **inputs, const u32 *ilens,
265 u8 output[MDC2_DIGEST_SIZE])
266 {
267 return mdc2_scattered(inputs, ilens, output, ISOIEC10118_TYPE2);
268 }
269
270 /*
271 * Single call version performing init/update/final on given input.
272 * Returns 0 on success, -1 on error.
273 */
mdc2(const u8 * input,u32 ilen,u8 output[MDC2_DIGEST_SIZE],padding_type p)274 ATTRIBUTE_WARN_UNUSED_RET int mdc2(const u8 *input, u32 ilen, u8 output[MDC2_DIGEST_SIZE], padding_type p)
275 {
276 mdc2_context ctx;
277 int ret;
278
279 ret = mdc2_init(&ctx); EG(ret, err);
280 ret = mdc2_set_padding_type(&ctx, p); EG(ret, err);
281 ret = mdc2_update(&ctx, input, ilen); EG(ret, err);
282 ret = mdc2_final(&ctx, output);
283
284 err:
285 return ret;
286 }
287
288
289 /*
290 * Single call version performing init/update/final on given input.
291 * Returns 0 on success, -1 on error.
292 */
mdc2_padding1(const u8 * input,u32 ilen,u8 output[MDC2_DIGEST_SIZE])293 ATTRIBUTE_WARN_UNUSED_RET int mdc2_padding1(const u8 *input, u32 ilen, u8 output[MDC2_DIGEST_SIZE])
294 {
295 return mdc2(input, ilen, output, ISOIEC10118_TYPE1);
296 }
297
298 /*
299 * Single call version performing init/update/final on given input.
300 * Returns 0 on success, -1 on error.
301 */
mdc2_padding2(const u8 * input,u32 ilen,u8 output[MDC2_DIGEST_SIZE])302 ATTRIBUTE_WARN_UNUSED_RET int mdc2_padding2(const u8 *input, u32 ilen, u8 output[MDC2_DIGEST_SIZE])
303 {
304 return mdc2(input, ilen, output, ISOIEC10118_TYPE2);
305 }
306