/* derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm */ #include #include #include "md5.h" // opceniti pointer typedef unsigned char *POINTER; // 16 bitni word typedef unsigned short int UINT2; // 32 bitni word typedef unsigned long int UINT4; // Konstante za MD5Transform funkciju #define S11 7 #define S12 12 #define S13 17 #define S14 22 #define S21 5 #define S22 9 #define S23 14 #define S24 20 #define S31 4 #define S32 11 #define S33 16 #define S34 23 #define S41 6 #define S42 10 #define S43 15 #define S44 21 // struktura za spremanje stanja typedef struct { UINT4 state[4]; // sazetak (ABCD) UINT4 count[2]; // duljina poruke u bitovima, modulo 2^64 unsigned char buffer[64]; // ulazni blok poruke (512 bita) } MD5_CTX; // dodatak na poruku unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // pomocne funkcije #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) // ROTATE_LEFT rotira x ulijevo za n bitova #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) // naredba za 1. rundu #define FF(a, b, c, d, x, s, ac) { \ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } // naredba za 2. rundu #define GG(a, b, c, d, x, s, ac) { \ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } // naredba za 3. rundu #define HH(a, b, c, d, x, s, ac) { \ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } // naredba za 3. rundu #define II(a, b, c, d, x, s, ac) { \ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } // Uzima po 4 okteta sa inputa, te ih zapisuje u output po little-endian sistemu (na najnizu adresu // zapisuje najmanje znacajni oktet). // Pretpostavlja da je len visekratnik broja 4 void Encode (unsigned char *output, UINT4 *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char)(input[i] & 0xff); output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); } } // Uzima po 4 okteta sa inputa, te ih zapisuje u output po big-endian sistemu (na najnizu adresu zapisuje // najvise znacajni oktet). // Pretpostavlja da je len visekratnik broja 4 void Decode (UINT4 *output, unsigned char *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); } // Kopira podatke sa adrese input na output. len je broj podataka koje treba kopirati void MD5_memcpy (POINTER output, POINTER input, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) output[i] = input[i]; } // Pocevsi od adrese output, postavlja len okteta na vrijedost value void MD5_memset (POINTER output, int value, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) ((char *)output)[i] = (char)value; } // Inicijalizacija void MD5Init (MD5_CTX *context) { context->count[0] = context->count[1] = 0; context->state[0] = 0x67452301; // A context->state[1] = 0xefcdab89; // B context->state[2] = 0x98badcfe; // C context->state[3] = 0x10325476; // D } // Osnovna transformacija. Mijenja trenutni sazetak ovisno o bloku. void MD5Transform (UINT4 state[4], unsigned char block[64]) { UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; Decode (x, block, 64); // 1. runda FF (a, b, c, d, x[ 0], S11, T[ 0]); /* 1 */ FF (d, a, b, c, x[ 1], S12, T[ 1]); /* 2 */ FF (c, d, a, b, x[ 2], S13, T[ 2]); /* 3 */ FF (b, c, d, a, x[ 3], S14, T[ 3]); /* 4 */ FF (a, b, c, d, x[ 4], S11, T[ 4]); /* 5 */ FF (d, a, b, c, x[ 5], S12, T[ 5]); /* 6 */ FF (c, d, a, b, x[ 6], S13, T[ 6]); /* 7 */ FF (b, c, d, a, x[ 7], S14, T[ 7]); /* 8 */ FF (a, b, c, d, x[ 8], S11, T[ 8]); /* 9 */ FF (d, a, b, c, x[ 9], S12, T[ 9]); /* 10 */ FF (c, d, a, b, x[10], S13, T[10]); /* 11 */ FF (b, c, d, a, x[11], S14, T[11]); /* 12 */ FF (a, b, c, d, x[12], S11, T[12]); /* 13 */ FF (d, a, b, c, x[13], S12, T[13]); /* 14 */ FF (c, d, a, b, x[14], S13, T[14]); /* 15 */ FF (b, c, d, a, x[15], S14, T[15]); /* 16 */ // 2. runda GG (a, b, c, d, x[ 1], S21, T[16]); /* 17 */ GG (d, a, b, c, x[ 6], S22, T[17]); /* 18 */ GG (c, d, a, b, x[11], S23, T[18]); /* 19 */ GG (b, c, d, a, x[ 0], S24, T[19]); /* 20 */ GG (a, b, c, d, x[ 5], S21, T[20]); /* 21 */ GG (d, a, b, c, x[10], S22, T[21]); /* 22 */ GG (c, d, a, b, x[15], S23, T[22]); /* 23 */ GG (b, c, d, a, x[ 4], S24, T[23]); /* 24 */ GG (a, b, c, d, x[ 9], S21, T[24]); /* 25 */ GG (d, a, b, c, x[14], S22, T[25]); /* 26 */ GG (c, d, a, b, x[ 3], S23, T[26]); /* 27 */ GG (b, c, d, a, x[ 8], S24, T[27]); /* 28 */ GG (a, b, c, d, x[13], S21, T[28]); /* 29 */ GG (d, a, b, c, x[ 2], S22, T[29]); /* 30 */ GG (c, d, a, b, x[ 7], S23, T[30]); /* 31 */ GG (b, c, d, a, x[12], S24, T[31]); /* 32 */ // 3. runda HH (a, b, c, d, x[ 5], S31, T[32]); /* 33 */ HH (d, a, b, c, x[ 8], S32, T[33]); /* 34 */ HH (c, d, a, b, x[11], S33, T[34]); /* 35 */ HH (b, c, d, a, x[14], S34, T[35]); /* 36 */ HH (a, b, c, d, x[ 1], S31, T[36]); /* 37 */ HH (d, a, b, c, x[ 4], S32, T[37]); /* 38 */ HH (c, d, a, b, x[ 7], S33, T[38]); /* 39 */ HH (b, c, d, a, x[10], S34, T[39]); /* 40 */ HH (a, b, c, d, x[13], S31, T[40]); /* 41 */ HH (d, a, b, c, x[ 0], S32, T[41]); /* 42 */ HH (c, d, a, b, x[ 3], S33, T[42]); /* 43 */ HH (b, c, d, a, x[ 6], S34, T[43]); /* 44 */ HH (a, b, c, d, x[ 9], S31, T[44]); /* 45 */ HH (d, a, b, c, x[12], S32, T[45]); /* 46 */ HH (c, d, a, b, x[15], S33, T[46]); /* 47 */ HH (b, c, d, a, x[ 2], S34, T[47]); /* 48 */ // 4. runda II (a, b, c, d, x[ 0], S41, T[48]); /* 49 */ II (d, a, b, c, x[ 7], S42, T[49]); /* 50 */ II (c, d, a, b, x[14], S43, T[50]); /* 51 */ II (b, c, d, a, x[ 5], S44, T[51]); /* 52 */ II (a, b, c, d, x[12], S41, T[52]); /* 53 */ II (d, a, b, c, x[ 3], S42, T[53]); /* 54 */ II (c, d, a, b, x[10], S43, T[54]); /* 55 */ II (b, c, d, a, x[ 1], S44, T[55]); /* 56 */ II (a, b, c, d, x[ 8], S41, T[56]); /* 57 */ II (d, a, b, c, x[15], S42, T[57]); /* 58 */ II (c, d, a, b, x[ 6], S43, T[58]); /* 59 */ II (b, c, d, a, x[13], S44, T[59]); /* 60 */ II (a, b, c, d, x[ 4], S41, T[60]); /* 61 */ II (d, a, b, c, x[11], S42, T[61]); /* 62 */ II (c, d, a, b, x[ 2], S43, T[62]); /* 63 */ II (b, c, d, a, x[ 9], S44, T[63]); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; // obrisi privremene podatke MD5_memset ((POINTER)x, 0, sizeof (x)); } // Obradjuje zadanu poruku, te modificira context void MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen) { unsigned int i, index, partLen; // izracunaj broj okteta modulo 64 index = (unsigned int)((context->count[0] >> 3) & 0x3F); // dodaj broj bitova bloka if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) context->count[1]++; context->count[1] += ((UINT4)inputLen >> 29); partLen = 64 - index; // tranfsormiraj koliko god puta moze if (inputLen >= partLen) { MD5_memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); MD5Transform (context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) MD5Transform (context->state, &input[i]); index = 0; } else i = 0; // spremi neobradjeni dio poruke (ostatak) MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); } // Finalizacija: lijepi PADDING na kraj poruke, te jos dodaje duljinu poruke // Racuna konacni sazetak void MD5Final (unsigned char digest[16], MD5_CTX *context) { unsigned char bits[8]; unsigned int index, padLen; // Sacuvaj broj bitova kao 64 bitni blok Encode (bits, context->count, 8); // nalijepi jedinicu i potrebni broj nula index = (unsigned int)((context->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); MD5Update (context, PADDING, padLen); // nalijepi duljinu originalne poruke MD5Update (context, bits, 8); // spremi sazetak u varijablu digest Encode (digest, context->state, 16); // obrisi podatke MD5_memset ((POINTER)context, 0, sizeof (*context)); } // Ucitava poruku iz filename_in, racuna sazetak te ga ispisuje u filename_out (string u tekstualni file) void MD5File (char *filename_in, char *filename_out) { FILE *file; MD5_CTX context; int len; unsigned char buffer[1024], digest[16]; unsigned int i; if ((file = fopen (filename_in, "rb")) == NULL) printf ("Datoteka %s se ne moze otvoriti za citanje!\n", filename_in); else { // inicijaliziraj MD5Init (&context); // dok ima podataka, racunaj sazetak while (len = fread (buffer, 1, 1024, file)) MD5Update (&context, buffer, len); // finaliziraj MD5Final (digest, &context); // zatvori ulazni file fclose (file); if ((file = fopen (filename_out, "w")) == NULL) printf ("Datoteka %s se ne moze otvoriti za pisanje!\n", filename_out); { for (i = 0; i < 16; i++) fprintf (file, "%02x", digest[i]); } } } // glavni program int main (int argc, char *argv[]) { int i; if (argc != 3) { printf("usage: ./md5.exe file_in file_out\n"); printf(" file_in - ulazna poruka\n"); printf(" file_out - odrediste za sazetak poruke\n"); } else MD5File (argv[1], argv[2]); return (0); }