xref: /freebsd/crypto/libecc/src/examples/hash/mdc2.c (revision f0865ec9906d5a18fa2a3b61381f22ce16e606ad)
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