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 "md4.h" 12 13 /* All the inner MD-4 operations */ 14 static const u32 C1_MD4[13] = { 15 0, 4, 8, 12, 0, 1, 2, 3, 3, 7, 11, 19, 0 16 }; 17 static const u32 C2_MD4[13] = { 18 0, 1, 2, 3, 0, 4, 8, 12, 3, 5, 9, 13, 0x5a827999 19 }; 20 static const u32 C3_MD4[13] = { 21 0, 2, 1, 3, 0, 8, 4, 12, 3, 9, 11, 15, 0x6ed9eba1 22 }; 23 24 #define F_MD4(x, y, z) (((x) & (y)) | ((~(x)) & (z))) 25 #define G_MD4(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) 26 #define H_MD4(x, y, z) ((x) ^ (y) ^ (z)) 27 28 /* SHA-2 core processing. Returns 0 on success, -1 on error. */ 29 ATTRIBUTE_WARN_UNUSED_RET static inline int md4_process(md4_context *ctx, 30 const u8 data[MD4_BLOCK_SIZE]) 31 { 32 u32 A, B, C, D; 33 u32 W[16]; 34 u32 idx; 35 int ret; 36 unsigned int i; 37 38 MUST_HAVE((data != NULL), ret, err); 39 MD4_HASH_CHECK_INITIALIZED(ctx, ret, err); 40 41 /* Init our inner variables */ 42 A = ctx->md4_state[0]; 43 B = ctx->md4_state[1]; 44 C = ctx->md4_state[2]; 45 D = ctx->md4_state[3]; 46 47 /* Load data */ 48 for (i = 0; i < 16; i++) { 49 GET_UINT32_LE(W[i], data, (4 * i)); 50 } 51 /* Proceed with the compression */ 52 for (i = 0; i < 4; i++) { 53 idx = (C1_MD4[i] + C1_MD4[4]); 54 A = ROTL_MD4((A + F_MD4(B, C, D) + W[idx] + C1_MD4[12]), C1_MD4[8]); 55 idx = (C1_MD4[i] + C1_MD4[5]); 56 D = ROTL_MD4((D + F_MD4(A, B, C) + W[idx] + C1_MD4[12]), C1_MD4[9]); 57 idx = (C1_MD4[i] + C1_MD4[6]); 58 C = ROTL_MD4((C + F_MD4(D, A, B) + W[idx] + C1_MD4[12]), C1_MD4[10]); 59 idx = (C1_MD4[i] + C1_MD4[7]); 60 B = ROTL_MD4((B + F_MD4(C, D, A) + W[idx] + C1_MD4[12]), C1_MD4[11]); 61 } 62 for (i = 0; i < 4; i++) { 63 idx = (C2_MD4[i] + C2_MD4[4]); 64 A = ROTL_MD4((A + G_MD4(B, C, D) + W[idx] + C2_MD4[12]), C2_MD4[8]); 65 idx = (C2_MD4[i] + C2_MD4[5]); 66 D = ROTL_MD4((D + G_MD4(A, B, C) + W[idx] + C2_MD4[12]), C2_MD4[9]); 67 idx = (C2_MD4[i] + C2_MD4[6]); 68 C = ROTL_MD4((C + G_MD4(D, A, B) + W[idx] + C2_MD4[12]), C2_MD4[10]); 69 idx = (C2_MD4[i] + C2_MD4[7]); 70 B = ROTL_MD4((B + G_MD4(C, D, A) + W[idx] + C2_MD4[12]), C2_MD4[11]); 71 } 72 for (i = 0; i < 4; i++) { 73 idx = (C3_MD4[i] + C3_MD4[4]); 74 A = ROTL_MD4((A + H_MD4(B, C, D) + W[idx] + C3_MD4[12]), C3_MD4[8]); 75 idx = (C3_MD4[i] + C3_MD4[5]); 76 D = ROTL_MD4((D + H_MD4(A, B, C) + W[idx] + C3_MD4[12]), C3_MD4[9]); 77 idx = (C3_MD4[i] + C3_MD4[6]); 78 C = ROTL_MD4((C + H_MD4(D, A, B) + W[idx] + C3_MD4[12]), C3_MD4[10]); 79 idx = (C3_MD4[i] + C3_MD4[7]); 80 B = ROTL_MD4((B + H_MD4(C, D, A) + W[idx] + C3_MD4[12]), C3_MD4[11]); 81 } 82 83 /* Update state */ 84 ctx->md4_state[0] += A; 85 ctx->md4_state[1] += B; 86 ctx->md4_state[2] += C; 87 ctx->md4_state[3] += D; 88 89 ret = 0; 90 91 err: 92 return ret; 93 } 94 95 /* Init hash function. Returns 0 on success, -1 on error. */ 96 ATTRIBUTE_WARN_UNUSED_RET int md4_init(md4_context *ctx) 97 { 98 int ret; 99 100 MUST_HAVE((ctx != NULL), ret, err); 101 102 /* Sanity check on size */ 103 MUST_HAVE((MD4_DIGEST_SIZE <= MAX_DIGEST_SIZE), ret, err); 104 105 ctx->md4_total = 0; 106 ctx->md4_state[0] = 0x67452301; 107 ctx->md4_state[1] = 0xEFCDAB89; 108 ctx->md4_state[2] = 0x98BADCFE; 109 ctx->md4_state[3] = 0x10325476; 110 111 /* Tell that we are initialized */ 112 ctx->magic = MD4_HASH_MAGIC; 113 114 ret = 0; 115 116 err: 117 return ret; 118 } 119 120 ATTRIBUTE_WARN_UNUSED_RET int md4_update(md4_context *ctx, const u8 *input, u32 ilen) 121 { 122 const u8 *data_ptr = input; 123 u32 remain_ilen = ilen; 124 u16 fill; 125 u8 left; 126 int ret; 127 128 MUST_HAVE((input != NULL) || (ilen == 0), ret, err); 129 MD4_HASH_CHECK_INITIALIZED(ctx, ret, err); 130 131 /* Nothing to process, return */ 132 if (ilen == 0) { 133 ret = 0; 134 goto err; 135 } 136 137 /* Get what's left in our local buffer */ 138 left = (ctx->md4_total & 0x3F); 139 fill = (u16)(MD4_BLOCK_SIZE - left); 140 141 ctx->md4_total += ilen; 142 143 if ((left > 0) && (remain_ilen >= fill)) { 144 /* Copy data at the end of the buffer */ 145 ret = local_memcpy(ctx->md4_buffer + left, data_ptr, fill); EG(ret, err); 146 ret = md4_process(ctx, ctx->md4_buffer); EG(ret, err); 147 data_ptr += fill; 148 remain_ilen -= fill; 149 left = 0; 150 } 151 152 while (remain_ilen >= MD4_BLOCK_SIZE) { 153 ret = md4_process(ctx, data_ptr); EG(ret, err); 154 data_ptr += MD4_BLOCK_SIZE; 155 remain_ilen -= MD4_BLOCK_SIZE; 156 } 157 158 if (remain_ilen > 0) { 159 ret = local_memcpy(ctx->md4_buffer + left, data_ptr, remain_ilen); EG(ret, err); 160 } 161 162 ret = 0; 163 164 err: 165 return ret; 166 } 167 168 /* Finalize. Returns 0 on success, -1 on error.*/ 169 ATTRIBUTE_WARN_UNUSED_RET int md4_final(md4_context *ctx, u8 output[MD4_DIGEST_SIZE]) 170 { 171 unsigned int block_present = 0; 172 u8 last_padded_block[2 * MD4_BLOCK_SIZE]; 173 int ret; 174 175 MUST_HAVE((output != NULL), ret, err); 176 MD4_HASH_CHECK_INITIALIZED(ctx, ret, err); 177 178 /* Fill in our last block with zeroes */ 179 ret = local_memset(last_padded_block, 0, sizeof(last_padded_block)); EG(ret, err); 180 181 /* This is our final step, so we proceed with the padding */ 182 block_present = ctx->md4_total % MD4_BLOCK_SIZE; 183 if (block_present != 0) { 184 /* Copy what's left in our temporary context buffer */ 185 ret = local_memcpy(last_padded_block, ctx->md4_buffer, 186 block_present); EG(ret, err); 187 } 188 189 /* Put the 0x80 byte, beginning of padding */ 190 last_padded_block[block_present] = 0x80; 191 192 /* Handle possible additional block */ 193 if (block_present > (MD4_BLOCK_SIZE - 1 - sizeof(u64))) { 194 /* We need an additional block */ 195 PUT_UINT64_LE(8 * ctx->md4_total, last_padded_block, 196 (2 * MD4_BLOCK_SIZE) - sizeof(u64)); 197 ret = md4_process(ctx, last_padded_block); EG(ret, err); 198 ret = md4_process(ctx, last_padded_block + MD4_BLOCK_SIZE); EG(ret, err); 199 } else { 200 /* We do not need an additional block */ 201 PUT_UINT64_LE(8 * ctx->md4_total, last_padded_block, 202 MD4_BLOCK_SIZE - sizeof(u64)); 203 ret = md4_process(ctx, last_padded_block); EG(ret, err); 204 } 205 206 /* Output the hash result */ 207 PUT_UINT32_LE(ctx->md4_state[0], output, 0); 208 PUT_UINT32_LE(ctx->md4_state[1], output, 4); 209 PUT_UINT32_LE(ctx->md4_state[2], output, 8); 210 PUT_UINT32_LE(ctx->md4_state[3], output, 12); 211 212 /* Tell that we are uninitialized */ 213 ctx->magic = WORD(0); 214 215 ret = 0; 216 217 err: 218 return ret; 219 } 220 221 222 /* 223 * Scattered version performing init/update/finalize on a vector of buffers 224 * 'inputs' with the length of each buffer passed via 'ilens'. The function 225 * loops on pointers in 'inputs' until it finds a NULL pointer. The function 226 * returns 0 on success, -1 on error. 227 */ 228 ATTRIBUTE_WARN_UNUSED_RET int md4_scattered(const u8 **inputs, const u32 *ilens, 229 u8 output[MD4_DIGEST_SIZE]) 230 { 231 md4_context ctx; 232 int ret, pos = 0; 233 234 MUST_HAVE((inputs != NULL) && (ilens != NULL) && (output != NULL), ret, err); 235 236 ret = md4_init(&ctx); EG(ret, err); 237 238 while (inputs[pos] != NULL) { 239 ret = md4_update(&ctx, inputs[pos], ilens[pos]); EG(ret, err); 240 pos += 1; 241 } 242 243 ret = md4_final(&ctx, output); 244 245 err: 246 return ret; 247 } 248 249 /* 250 * Single call version performing init/update/final on given input. 251 * Returns 0 on success, -1 on error. 252 */ 253 ATTRIBUTE_WARN_UNUSED_RET int md4(const u8 *input, u32 ilen, u8 output[MD4_DIGEST_SIZE]) 254 { 255 md4_context ctx; 256 int ret; 257 258 ret = md4_init(&ctx); EG(ret, err); 259 ret = md4_update(&ctx, input, ilen); EG(ret, err); 260 ret = md4_final(&ctx, output); 261 262 err: 263 return ret; 264 } 265