Initial version, with MD5
[libs/crypto.git] / source / md5.cpp
1 #include <cmath>
2 #include <stdexcept>
3 #include "md5.h"
4
5 using namespace std;
6
7 namespace Msp {
8 namespace Crypto {
9
10 namespace {
11
12 template<unsigned N>
13 inline UInt32 func(UInt32, UInt32, UInt32);
14
15 template<>
16 UInt32 func<1>(UInt32 x, UInt32 y, UInt32 z)
17 {
18         return (y&x) | (z&~x);
19 }
20
21 template<>
22 UInt32 func<2>(UInt32 x, UInt32 y, UInt32 z)
23 {
24         return (x&z) | (y&~z);
25 }
26
27 template<>
28 UInt32 func<3>(UInt32 x, UInt32 y, UInt32 z)
29 {
30         return x^y^z;
31 }
32
33 template<>
34 UInt32 func<4>(UInt32 x, UInt32 y, UInt32 z)
35 {
36         return y^(x|~z);
37 }
38
39 inline UInt32 rotate_left(UInt32 x, UInt32 b)
40 {
41         return (x<<b) | (x>>(32-b));
42 }
43
44 }
45
46
47 UInt32 MD5::sin_table[64] = { 0 };
48 unsigned MD5::rotate_table[16] =
49 {
50         7, 12, 17, 22,
51         5, 9, 14, 20,
52         4, 11, 16, 23,
53         6, 10, 15, 21
54 };
55
56 MD5::MD5()
57 {
58         init();
59 }
60
61 MD5::MD5(const char *data, unsigned len)
62 {
63         init();
64         update(data, len);
65 }
66
67 MD5::MD5(const string &str)
68 {
69         init();
70         update(str);
71 }
72
73 void MD5::init()
74 {
75         buffer[0] = 0x67452301;
76         buffer[1] = 0xefcdab89;
77         buffer[2] = 0x98badcfe;
78         buffer[3] = 0x10325476;
79         processed_bytes = 0;
80         unprocessed_bytes = 0;
81
82         if(!sin_table[0])
83                 for(unsigned i=0; i<64; ++i)
84                         sin_table[i] = 4294967296.0*abs(sin((i+1)*1.0));
85
86         if(!rotate_table[0])
87         {
88                 for(unsigned i=0; i<4; ++i)
89                         rotate_table[i] = 7+i*5;
90                 for(unsigned i=0; i<4; ++i)
91                         rotate_table[4+i] = 5+i*4+i*i/3;
92                 for(unsigned i=0; i<4; ++i)
93                         rotate_table[8+i] = 4+i*7;
94                 for(unsigned i=0; i<4; ++i)
95                         rotate_table[12+i] = 6+i*4+i*i/3;
96         }
97 }
98
99 void MD5::update(const char *data, unsigned len)
100 {
101         if(unprocessed_bytes && unprocessed_bytes+len>=64)
102         {
103                 unsigned needed = 64-unprocessed_bytes;
104                 copy(data, data+needed, unprocessed+unprocessed_bytes);
105                 process_block(unprocessed);
106                 data += needed;
107                 len -= needed;
108                 unprocessed_bytes = 0;
109         }
110
111         while(len>=64)
112         {
113                 process_block(data);
114                 data += 64;
115                 len -= 64;
116         }
117
118         if(len>0)
119         {
120                 copy(data, data+len, unprocessed+unprocessed_bytes);
121                 unprocessed_bytes += len;
122         }
123 }
124
125 unsigned MD5::get_digest(char *digest, unsigned len) const
126 {
127         if(len<16)
128                 throw invalid_argument("MD5::get_digest");
129
130         MD5 padded = *this;
131
132         char padding[64] = { static_cast<char>(0x80) };
133         padded.update(padding, 64-(unprocessed_bytes+8)%64);
134
135         UInt64 message_length = (processed_bytes+unprocessed_bytes)*8;
136         for(unsigned i=0; i<8; ++i)
137                 padding[i] = message_length>>(i*8);
138         padded.update(padding, 8);
139
140         for(unsigned i=0; i<16; ++i)
141                 digest[i] = padded.buffer[i/4]>>((i%4)*8);
142
143         return 16;
144 }
145
146 void MD5::process_block(const char *data)
147 {
148         UInt32 input_words[16];
149
150         const UInt8 *u8data = reinterpret_cast<const UInt8 *>(data);
151         for(unsigned i=0; i<16; ++i)
152                 input_words[i] = (u8data[i*4+3]<<24) | (u8data[i*4+2]<<16) | (u8data[i*4+1]<<8) | u8data[i*4];
153
154         UInt32 work_buffer[4];
155         copy(buffer, buffer+4, work_buffer);
156
157         perform_round<1, 0, 1>(work_buffer, input_words);
158         perform_round<2, 1, 5>(work_buffer, input_words);
159         perform_round<3, 5, 3>(work_buffer, input_words);
160         perform_round<4, 0, 7>(work_buffer, input_words);
161
162         for(unsigned i=0; i<4; ++i)
163                 buffer[i] += work_buffer[i];
164
165         processed_bytes += 64;
166 }
167
168 template<unsigned N, unsigned START, unsigned DELTA>
169 inline void MD5::perform_round(UInt32 *work_buffer, const UInt32 *input_words)
170 {
171         for(unsigned i=0; i<4; ++i)
172                 for(unsigned j=0; j<4; ++j)
173                 {
174                         unsigned k = i*4+j;
175                         UInt32 &a = work_buffer[(4-j)&3];
176                         UInt32 &b = work_buffer[(5-j)&3];
177                         UInt32 &c = work_buffer[(6-j)&3];
178                         UInt32 &d = work_buffer[(7-j)&3];
179                         UInt32 sum = a + func<N>(b, c, d) + input_words[(START+k*DELTA)&15] + sin_table[(N-1)*16+k];
180                         a = b + rotate_left(sum, rotate_table[(N-1)*4+j]);
181                 }
182 }
183
184 } // namespace Crypto
185 } // namespace Msp