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 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. */ 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. */ 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 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.*/ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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