VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
md5.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // md5.cpp
19 //
20 // An implementation of the RFC-1321 message digest algorithm.
21 //
22 // This code was developed from main body of RFC 1321 without reference to the
23 // RSA reference implementation in the appendix.
24 //
25 // A minor portability advantage over the RSA implementation is that there is no
26 // need to define a datatype that is exactly 32 bits: the requirement is that
27 // 'big_t' is at least 32 bits, but it can be more.
28 //
29 // Stylistic advantages are that it is written in C++ with an enclosing namespace,
30 // it does not use preprocessor macros, and there is an element of layering with
31 // digest_stream built on top of the low-level digest class.
32 
33 #include "md5.h"
34 
36 {
37  init() ;
38 }
39 
41  a(d_in.a) ,
42  b(d_in.b) ,
43  c(d_in.c) ,
44  d(d_in.d)
45 {
46 }
47 
49 {
50  big_t mask = 0 ;
51  small_t thirty_two = 32U ;
52  small_t sizeof_thirty_two_bits = 4U ; // 4x8=32
53  if( sizeof(mask) > sizeof_thirty_two_bits )
54  {
55  mask = ~0U ;
56  mask <<= thirty_two ; // ignore warnings here
57  }
58  state_type result = { a & ~mask , b & ~mask , c & ~mask , d & ~mask } ;
59  return result ;
60 }
61 
63 {
64  init() ;
65  small_t n = block::blocks( s.length() ) ;
66  for( small_t i = 0U ; i < n ; ++i )
67  {
68  block blk( s , i , block::end(s.length()) ) ;
69  add( blk ) ;
70  }
71 }
72 
73 void md5::digest::add( const block & m )
74 {
75  digest old( *this ) ;
76  round1( m ) ;
77  round2( m ) ;
78  round3( m ) ;
79  round4( m ) ;
80  add( old ) ;
81 }
82 
83 void md5::digest::init()
84 {
85  a = 0x67452301UL ;
86  b = 0xefcdab89UL ;
87  c = 0x98badcfeUL ;
88  d = 0x10325476UL ;
89 }
90 
91 void md5::digest::add( const digest & other )
92 {
93  a += other.a ;
94  b += other.b ;
95  c += other.c ;
96  d += other.d ;
97 }
98 
99 void md5::digest::round1( const block & m )
100 {
101  digest & r = *this ;
102  r(m,F,ABCD, 0, 7, 1); r(m,F,DABC, 1,12, 2); r(m,F,CDAB, 2,17, 3); r(m,F,BCDA, 3,22, 4);
103  r(m,F,ABCD, 4, 7, 5); r(m,F,DABC, 5,12, 6); r(m,F,CDAB, 6,17, 7); r(m,F,BCDA, 7,22, 8);
104  r(m,F,ABCD, 8, 7, 9); r(m,F,DABC, 9,12,10); r(m,F,CDAB,10,17,11); r(m,F,BCDA,11,22,12);
105  r(m,F,ABCD,12, 7,13); r(m,F,DABC,13,12,14); r(m,F,CDAB,14,17,15); r(m,F,BCDA,15,22,16);
106 }
107 
108 void md5::digest::round2( const block & m )
109 {
110  digest & r = *this ;
111  r(m,G,ABCD, 1, 5,17); r(m,G,DABC, 6, 9,18); r(m,G,CDAB,11,14,19); r(m,G,BCDA, 0,20,20);
112  r(m,G,ABCD, 5, 5,21); r(m,G,DABC,10, 9,22); r(m,G,CDAB,15,14,23); r(m,G,BCDA, 4,20,24);
113  r(m,G,ABCD, 9, 5,25); r(m,G,DABC,14, 9,26); r(m,G,CDAB, 3,14,27); r(m,G,BCDA, 8,20,28);
114  r(m,G,ABCD,13, 5,29); r(m,G,DABC, 2, 9,30); r(m,G,CDAB, 7,14,31); r(m,G,BCDA,12,20,32);
115 }
116 
117 void md5::digest::round3( const block & m )
118 {
119  digest & r = *this ;
120  r(m,H,ABCD, 5, 4,33); r(m,H,DABC, 8,11,34); r(m,H,CDAB,11,16,35); r(m,H,BCDA,14,23,36);
121  r(m,H,ABCD, 1, 4,37); r(m,H,DABC, 4,11,38); r(m,H,CDAB, 7,16,39); r(m,H,BCDA,10,23,40);
122  r(m,H,ABCD,13, 4,41); r(m,H,DABC, 0,11,42); r(m,H,CDAB, 3,16,43); r(m,H,BCDA, 6,23,44);
123  r(m,H,ABCD, 9, 4,45); r(m,H,DABC,12,11,46); r(m,H,CDAB,15,16,47); r(m,H,BCDA, 2,23,48);
124 }
125 
126 void md5::digest::round4( const block & m )
127 {
128  digest & r = *this ;
129  r(m,I,ABCD, 0, 6,49); r(m,I,DABC, 7,10,50); r(m,I,CDAB,14,15,51); r(m,I,BCDA, 5,21,52);
130  r(m,I,ABCD,12, 6,53); r(m,I,DABC, 3,10,54); r(m,I,CDAB,10,15,55); r(m,I,BCDA, 1,21,56);
131  r(m,I,ABCD, 8, 6,57); r(m,I,DABC,15,10,58); r(m,I,CDAB, 6,15,59); r(m,I,BCDA,13,21,60);
132  r(m,I,ABCD, 4, 6,61); r(m,I,DABC,11,10,62); r(m,I,CDAB, 2,15,63); r(m,I,BCDA, 9,21,64);
133 }
134 
135 void md5::digest::operator()( const block & m , aux_fn_t aux , Permutation p , small_t k , small_t s , small_t i )
136 {
137  if( p == ABCD ) a = op( m , aux , a , b , c , d , k , s , i ) ;
138  if( p == DABC ) d = op( m , aux , d , a , b , c , k , s , i ) ;
139  if( p == CDAB ) c = op( m , aux , c , d , a , b , k , s , i ) ;
140  if( p == BCDA ) b = op( m , aux , b , c , d , a , k , s , i ) ;
141 }
142 
143 md5::big_t md5::digest::op( const block & m , aux_fn_t aux , big_t a , big_t b , big_t c , big_t d ,
144  small_t k , small_t s , small_t i )
145 {
146  return b + rot32( s , ( a + (*aux)( b , c , d ) + m.X(k) + T(i) ) ) ;
147 }
148 
149 md5::big_t md5::digest::rot32( small_t places , big_t n )
150 {
151  // circular rotate of 32 LSBs, with corruption of higher bits
152  big_t overflow_mask = ( 1UL << places ) - 1UL ; // in case big_t is more than 32 bits
153  big_t overflow = ( n >> ( 32U - places ) ) ;
154  return ( n << places ) | ( overflow & overflow_mask ) ;
155 }
156 
157 md5::big_t md5::digest::F( big_t x , big_t y , big_t z )
158 {
159  return ( x & y ) | ( ~x & z ) ;
160 }
161 
162 md5::big_t md5::digest::G( big_t x , big_t y , big_t z )
163 {
164  return ( x & z ) | ( y & ~z ) ;
165 }
166 
167 md5::big_t md5::digest::H( big_t x , big_t y , big_t z )
168 {
169  return x ^ y ^ z ;
170 }
171 
172 md5::big_t md5::digest::I( big_t x , big_t y , big_t z )
173 {
174  return y ^ ( x | ~z ) ;
175 }
176 
177 md5::big_t md5::digest::T( small_t i )
178 {
179  // T = static_cast<big_t>( 4294967296.0 * std::fabs(std::sin(static_cast<double>(i))) ) for 1 <= i <= 64
180  //
181  static big_t t_map[] =
182  {
183  0xd76aa478UL ,
184  0xe8c7b756UL ,
185  0x242070dbUL ,
186  0xc1bdceeeUL ,
187  0xf57c0fafUL ,
188  0x4787c62aUL ,
189  0xa8304613UL ,
190  0xfd469501UL ,
191  0x698098d8UL ,
192  0x8b44f7afUL ,
193  0xffff5bb1UL ,
194  0x895cd7beUL ,
195  0x6b901122UL ,
196  0xfd987193UL ,
197  0xa679438eUL ,
198  0x49b40821UL ,
199  0xf61e2562UL ,
200  0xc040b340UL ,
201  0x265e5a51UL ,
202  0xe9b6c7aaUL ,
203  0xd62f105dUL ,
204  0x02441453UL ,
205  0xd8a1e681UL ,
206  0xe7d3fbc8UL ,
207  0x21e1cde6UL ,
208  0xc33707d6UL ,
209  0xf4d50d87UL ,
210  0x455a14edUL ,
211  0xa9e3e905UL ,
212  0xfcefa3f8UL ,
213  0x676f02d9UL ,
214  0x8d2a4c8aUL ,
215  0xfffa3942UL ,
216  0x8771f681UL ,
217  0x6d9d6122UL ,
218  0xfde5380cUL ,
219  0xa4beea44UL ,
220  0x4bdecfa9UL ,
221  0xf6bb4b60UL ,
222  0xbebfbc70UL ,
223  0x289b7ec6UL ,
224  0xeaa127faUL ,
225  0xd4ef3085UL ,
226  0x04881d05UL ,
227  0xd9d4d039UL ,
228  0xe6db99e5UL ,
229  0x1fa27cf8UL ,
230  0xc4ac5665UL ,
231  0xf4292244UL ,
232  0x432aff97UL ,
233  0xab9423a7UL ,
234  0xfc93a039UL ,
235  0x655b59c3UL ,
236  0x8f0ccc92UL ,
237  0xffeff47dUL ,
238  0x85845dd1UL ,
239  0x6fa87e4fUL ,
240  0xfe2ce6e0UL ,
241  0xa3014314UL ,
242  0x4e0811a1UL ,
243  0xf7537e82UL ,
244  0xbd3af235UL ,
245  0x2ad7d2bbUL ,
246  0xeb86d391UL } ;
247  return t_map[i-1UL] ;
248 }
249 
250 // ===
251 
253 {
254  string_type result ;
255  result.reserve( 16U ) ;
256  result.append( raw(d.a) ) ;
257  result.append( raw(d.b) ) ;
258  result.append( raw(d.c) ) ;
259  result.append( raw(d.d) ) ;
260  return result ;
261 }
262 
264 {
265  string_type result ;
266  result.reserve( 4U ) ;
267  result.append( 1U , static_cast<char>(n&0xffUL) ) ; n >>= 8U ;
268  result.append( 1U , static_cast<char>(n&0xffUL) ) ; n >>= 8U ;
269  result.append( 1U , static_cast<char>(n&0xffUL) ) ; n >>= 8U ;
270  result.append( 1U , static_cast<char>(n&0xffUL) ) ;
271  return result ;
272 }
273 
275 {
276  return rfc( d.state() ) ;
277 }
278 
280 {
281  string_type result ;
282  result.reserve( 32U ) ;
283  result.append( str(d.a) ) ;
284  result.append( str(d.b) ) ;
285  result.append( str(d.c) ) ;
286  result.append( str(d.d) ) ;
287  return result ;
288 }
289 
290 md5::string_type md5::format::str( big_t n )
291 {
292  string_type result ;
293  result.reserve( 8U ) ;
294  result.append( str2(n&0xffUL) ) ; n >>= 8U ;
295  result.append( str2(n&0xffUL) ) ; n >>= 8U ;
296  result.append( str2(n&0xffUL) ) ; n >>= 8U ;
297  result.append( str2(n&0xffUL) ) ;
298  return result ;
299 }
300 
301 md5::string_type md5::format::str2( small_t n )
302 {
303  return str1((n>>4U)&0xfU) + str1(n&0xfU) ;
304 }
305 
306 md5::string_type md5::format::str1( small_t n )
307 {
308  n = n <= 15U ? n : 0U ;
309  const char * map = "0123456789abcdef" ;
310  return string_type( 1U , map[n] ) ;
311 }
312 
313 // ===
314 
316  m_s(s) ,
317  m_block(block) ,
318  m_end_value(end_value)
319 {
320 }
321 
323 {
324  big_t result = length ;
325  result *= 8UL ;
326  return result ;
327 }
328 
329 md5::small_t md5::block::rounded( small_t raw_byte_count )
330 {
331  small_t n = raw_byte_count + 64U ;
332  return n - ( ( raw_byte_count + 8U ) % 64U ) ;
333 }
334 
336 {
337  small_t byte_count = rounded(raw_byte_count) + 8U ;
338  return byte_count / 64UL ;
339 }
340 
341 md5::big_t md5::block::X( small_t dword_index ) const
342 {
343  small_t byte_index = ( m_block * 64U ) + ( dword_index * 4U ) ;
344  big_t result = x( byte_index + 3U ) ;
345  result <<= 8U ; result += x( byte_index + 2U ) ;
346  result <<= 8U ; result += x( byte_index + 1U ) ;
347  result <<= 8U ; result += x( byte_index + 0U ) ;
348  return result ;
349 }
350 
351 md5::small_t md5::block::x( small_t i ) const
352 {
353  small_t length = m_s.length() ;
354  if( i < length )
355  {
356  return static_cast<unsigned char>(m_s[i]) ;
357  }
358  else if( i < rounded(length) )
359  {
360  return i == length ? 128U : 0U ;
361  }
362  else
363  {
364  small_t byte_shift = i - rounded(length) ;
365  if( byte_shift >= sizeof(big_t) )
366  {
367  return 0U ;
368  }
369  else
370  {
371  small_t bit_shift = byte_shift * 8U ;
372 
373  big_t end_value = m_end_value >> bit_shift ;
374  return static_cast<small_t>( end_value & 0xffUL ) ;
375  }
376  }
377 }
378 
379 // ===
380 
382  m_length(0U)
383 {
384 }
385 
387  m_digest(dd) ,
388  m_length(length)
389 {
390 }
391 
393 {
394  m_buffer.append( s ) ;
395  m_length += s.length() ;
396 
397  while( m_buffer.length() >= 64U )
398  {
399  block m( m_buffer , 0U , 0UL ) ;
400  m_digest.add( m ) ;
401  m_buffer.erase( 0U , 64U ) ;
402  }
403 }
404 
406 {
407  block b( m_buffer , 0U , block::end(m_length) ) ;
408  m_digest.add( b ) ;
409  m_buffer.erase() ;
410 }
411 
413 {
414  state_type result ;
415  result.d = m_digest.state() ;
416  result.n = m_length ;
417  result.s = m_buffer ;
418  return result ;
419 }
420 
422 {
423  return m_length ;
424 }
425 
426 /// \file md5.cpp
void add(const string_type &)
Adds more message data.
Definition: md5.cpp:392
size_type big_t
To hold at least 32 bits, maybe more. Try unsigned long on small systems.
Definition: md5.h:44
void close()
Called after the last add().
Definition: md5.cpp:405
size_type small_t
To hold at least a size_t. Must fit in a big_t.
Definition: md5.h:45
big_t X(small_t) const
Returns a value from within the block. See RFC 1321.
Definition: md5.cpp:341
block(const string_type &s, small_t block_offset, big_t end_value)
Constructor.
Definition: md5.cpp:315
digest()
Default constructor.
Definition: md5.cpp:35
Holds the md5 algorithm state. Used by md5::digest.
Definition: md5.h:80
A helper class used by the md5::digest implementation to represent a 64-character data block...
Definition: md5.h:167
std::string string_type
A string type.
Definition: md5.h:42
static small_t blocks(small_t data_length)
Takes the total number of bytes in the input message and returns the number of 64-byte blocks...
Definition: md5.cpp:335
A class that calculates an md5 digest from one or more 64-byte blocks of data using the algorithm des...
Definition: md5.h:77
small_t size() const
Returns how many data bytes have been accumulated so far.
Definition: md5.cpp:421
state_type state() const
Returns the internal state.
Definition: md5.cpp:48
state_type state() const
Returns the current state.
Definition: md5.cpp:412
static big_t end(small_t data_length)
Takes the total number of bytes in the input message and returns a value which can be passed to the c...
Definition: md5.cpp:322
digest_stream()
Default constructor.
Definition: md5.cpp:381
static string_type rfc(const digest &)
Returns the digest string in the RFC format.
Definition: md5.cpp:274
void add(const block &)
Adds a 64-byte block of the message.
Definition: md5.cpp:73
Holds the state of an md5 digest stream. Used by md5::digest_stream.
Definition: md5.h:241
static string_type raw(const digest::state_type &)
Returns the raw digest data as a std::string.
Definition: md5.cpp:252