Message Digests and Digital Fingerprints

1 ABSTRACT

A one-way hash function, also known as a message digest, takes an input string of arbitrary length and produces a fixed length hash. You could think of it as a digital fingerprint. It is generally thought that no two keys (e.g. pass phrases) can produce the same hash value. Therefore, when a one-way cryptographic hash is used as a password authentication method, it does not try to decrypt the hash and compare the resulting plain text but rather encrypt the user-supplied plain text (password), hash it, and then compare the hash values.

MD5 (Message Digest version 5) is a one-way hash function designed by Ron Rivest of RSA fame. After processing a message of arbitrary length, it produces a 128-bit hash as a representation of the original plain text.

2 AIM

To implement the MD5 Message-Digest algorithm based on RFC1321 in the C++ programming language using the Standard Template Library (STL). The resulting application must produce message digests, or digital fingerprints, of both text and binary files and text strings. The focus is not optimisation, but rather the integration of STL containers and algorithms into an object-oriented MD5 implementation and achieve a better understanding of how the MD5 algorithm works.

This will not simply be a copy of Ron Rivest's C code found in [1] pasted into a class called MD5, which, unfortunately a lot of articles seem to do.

The rest of the document will refer to a program, jmd5, which is the author's implementation of the MD5 algorithm. The prefix j simply represents the author's initial.

3 CONVENTIONS USED IN THIS DOCUMENT

Italic: Reserved for program variables.

BOLD: Used for headings such as the ones on this page.

Constant width: Used for code snippets and examples throughout the paper.

Underlined words: Reserved for sub-headings or sections related to the headings in bold.

MD5

The MD5 algorithm was designed and first implemented in C by Ron Rivest of RSA Data Security. It is an open standard (patent and trademark free) and is documented well in RFC1321. Compared to other cryptographic algorithms, MD5 is quite compact and not a lot of code is needed to implement it.

The input message is first padded to be sure it is 64 bits short of a multiple of 512. A single '1' bit is first appended and then a series of '0' bits until the message is congruent to 448 modulo 512. The remaining 64 bits are used to store the message size prior to the padding. At this stage, the message should now be an exact multiple of 512. Along with the initialisation of four 32-bit constants, a set of 64 'magic' numbers is constructed according to the following formula:

Where S is the set, i is a subscript, a is a function that calculates the absolute value of its input, and s calculates the sine of its input which, in this case, is i .These constants are used along with several bitwise operations to process the message in blocks resulting in a 128-bit message digest.

This C++ implementation is based heavily on the original C code by Ron Rivest. However, many design changes have been made to incorporate the C++STL. The resulting application was tested against the tool 'md5sum' available on the majority of Linux distributions and, although slower (C++ STL overhead), produces the same hash.

jmd5—The MD5 class

Diagram 1: The UML Class diagram of jmd5

The code associated with the above class diagram can be found in Appendix A. Other than swapping arrays for vectors, memset() for fill(), and memcpy() for copy(), and so forth, the class design tries to keep in with the framework of STL-based containers and templates.

The class constructor uses the STL template class istreambuf_iterator <char> to copy the input into a vector <char> if the input is a text or binary file and a simple copy() if the input is a text string from stdin or a time stamp:

if (file || input == "-") {
   istream *stream = &cin;

   if (input != "-")
      stream = new ifstream (input.c_str (), ios::in);

   if (!stream ->good ())
      throw ErrorMessage ("Failed to open '%s' for reading.",
                          input.c_str ());

   copy (istreambuf_iterator<char> (*stream),
         istreambuf_iterator<char> (), 
         back_inserter (plaintext));

   if (!file)
      plaintext.pop_back ();
   if (stream && stream != &cin)
      delete stream;
} else
   copy (input.begin (), input.end (), back_inserter (plaintext));

The above code determines whether the input is a text string or file and whether the source (file) is a file on disk or the stdin stream. Once the input has been copied to the class attribute plaintext, jmd5 processes the plain text in blocks of 64 bytes (512 bits) using the following algorithm:

byte *data        = NULL;
const uint block  = 64;
uint total        = 0, bytes = 0, last = 0;
const uint length = plaintext.size ();
while ((total += bytes) < length) {
   if ((total + block) > length)
      bytes = length - total;
   else
      bytes = block;

   if (!data)
      data = new byte [bytes ];
   else if (bytes != last) {
      delete [] data;
      data = NULL;
      data = new byte [bytes ];
   }
   if (!data)
      throw ErrorMessage ("Failed to allocate %d bytes of heap
                           storage.", bytes);

   fill (data, data + bytes, 0);
   copy (plaintext.begin () + total, (plaintext.begin () + total)
         + bytes, data);

   update data, bytes);
   last = bytes;
}
finalise ();

The data type 'byte' shown above is one of two typedefs:

typedef unsigned char byte;
typedef unsigned long int word;

You will later see a mathematical formula that describes how 64 'magic' numbers are generated. The C++ code that does this can be found within the init() function:

<snip>

static vector<word> magic (64);    // global static variable

</snip>

static const double p = pow (2, 32);

for (uint r = 0 ; r < magic.size () ; r++)
   magic [r ] = static_cast<word> (p * fabs (sin (r  + 1)));

The update() member function is the starting point of the actual MD5 algorithm implemented within jmd5. Update() is called for every block of data and upon exit of the while loop the function finalise() is called. It is inside this function where the padding is done (and its length appended) and the final digest is encoded according to the four internal states (32-bit words) of the class set by the transform() function. Most of the work is done by transform() and the other member functions it calls: decode(), op(), f(), g(), h(), i(), and rl(). The functions f(), g() ,h(), and i() are all bit-manipulators and rl() simply left-rotates all the bits in a word:

inline word jmd5::rl (const word &x, const word &n) const {
   return ((x << n) | (x >> (32 - n)));
}

inline word jmd5::f (const word &x, const word &y, const word &z) const {
   return x & y | ~x & z;
}

inline word jmd5::g (const word &x, const word &y, const word &z) const {
   return x & z | y & ~z;
}

inline word jmd5::h (const word &x, const word &y, const word &z) const {
   return x ^ y ^ z;
}

inline word jmd5::i (const word &x, const word &y, const word &z) const {
   return y ^ (x | ~z);
}

The transform() function calls the op() function 16 times for each of the four rounds passing as parameters the current states (each time in a different order) and two magic numbers (one being an integer constant less than 24 and the other produced by the formula described later).

inline void jmd5::op (const char &func, const word &a, const word &b,
                      const word c&, word &d, const word &x,
                      const word &s, const word &m) const {
   switch (func) {
      case 'f':
         a += f (b, c, d) + x + m;
         break;
      case 'g':
         a += g (b, c, d) + x + m;
         break;
      case 'h':
         a += h (b, c, d) + x + m;
         break;
      case 'i':
         a += i (b, c, d) + x + m;
         break;
   }
   a = rl (a, s) + b;
}

A snippet of the transform() member function:

inline void jmd5::transform (byte block [64 ]) {
   word a = state [0 ], b = state [1 ], c = state [2 ],
        d = state [3 ], x [16 ];

   decode  (block, x, 64);

   op ('f', a, b, c, d, x [ 0 ], S11, magic.at (0));   // Round 1
   ...    // 15 more calls to op() here
   op ('g', a, b, c, d, x [ 1 ], S21, magic.at (16));  // Round 2
   ...    // 15 more calls to op() here
   op ('h', a, b, c, d, x [ 5 ], S31, magic.at (32));  // Round 3
   ...    // 15 more calls to op() here
   op ('i', a, b, c, d, x [ 0 ], S41, magic.at (48));  // Round 4
   ... // 15  more  calls  to  op() here

   state [0 ] += a;
   state [1 ] += b;
   state [2 ] += c;
   state [3 ] += d;
}

The finalise() function concatenates the resulting four states to produce the 128-bit message digest. Jmd5 provides a friend function that overloads the <<(extraction) operator so std::ostream objects can print the digest similarly to other data types:

jmd5 digest ("PI ");
const double pi = 3.14159265358979323846;

std::cout << "PI is " << pi << " the MD5 hash of 'PI' is "
          << digest << endl;

The friend function is defined within the class declaration as:

friend ostream& operator << (ostream& stream, const jmd5 &digest) {
   string hash;
   char bits [3 ];

   fill (bits, bits + sizeof (bits), '\0');

   for (uint i = 0 ; i < digest.size () ; i++) {
      snprintf (bits, sizeof (bits), "%02x", digest [i ]);
      hash += bits;
   }

   return stream << hash;
};

It was originally planned to use std::hex along with std:cout; however, std::cout did not allow the same precision as the standard C function snprintf() (or printf()) and would have resulted in jmd5 being incompatible with other MD5 implementations.

The access operator, [ ], is used to access individual elements of the digest by passing it a subscript—just as you would with other STL containers:

const uint jmd5::operator [] (const uint &index) const {
   return digest [index ];
}

Message Digests and Digital Fingerprints

jmd5::ErrorMessage—The 'exception'class

Many functions throw exceptions should they perform an illegal operation. An exception class, which inherits from std::exception, is provided in the declaration of jmd5. When throwing an exception, a function may optionally pass parameters to the constructor because it supports the C variable argument list defined in the <cstdarg> header.

[diagram2.jpg]

Diagram 2: The ErrorMessage class diagram

The above UML class diagram as C++ code:

class ErrorMessage : public std::exception {
   private:
      string message;
   public:
      explicit ErrorMessage (const char *format, ...);
      ~ErrorMessage () throw ();
      const char* what (void) const throw ();
};

An example of how ErrorMessage is used can be found later. All exceptions should be caught by the try-catch block in the main() function.

The main program

The main() function is simple. It parses the command-line options and determines how it should instantiate a jmd5 class (create an object of type jmd5). The program accepts input from stdin (via a pipe for example) as well as file names or text strings given as arguments. The third option, -t, tells jmd5 to produce an MD5 hash of the current system date and time:

-- jimv@eeyore -- pts/1 -- Fri Jan 23
-- ~/code/projects/jmd5/src > jmd5 -v -t
Input: 'Fri Jan 23 15:59:31 2004'
Output: d1cccef6932a064d4f16d5dd6ab7016e

The -v flag follows UNIX tradition and will produce verbose output. Without the verbose flag, jmd5 will just print the message digest of the input:

-- jimv@eeyore -- pts/1 -- Fri Jan  23
-- ~/code/projects/jmd5/src > cat /etc/passwd | jmd5 -f -
  && jmd5 -f /etc/passwd
c6587134be3f58fe0049a0c5ff2260ff
c6587134be3f58fe0049a0c5ff2260ff

The two hashes above are identical because the input is identical. The first execution of jmd5 receives its input via the pipe and the second opens the file directly. When an exception is caught, it is echoed to stderr:

-- jimv@eeyore -- pts/1 -- Fri Jan 23
-- ~/code/projects/jmd5/src > jmd5 -f /etc/shadow
Error: Failed to open '/etc/shadow' for reading.

Finally, some examples of using combinations of jmd5 options:

-- jimv@eeyore -- pts/1 -- Fri Jan 23
-- ~/code/projects/jmd5/src > jmd5 -t | jmd5 -s -
85da2254cd7b3743468ed88b814a760

-- jimv@eeyore -- pts/1 -- Fri Jan 23
-- ~/code/projects/jmd5/src > echo "'date'" | jmd5 -v -s -
Input: 'stdin'
Output: 99ffe00b88305b4fe71457ba510cb461

Appendix B shows a [hardly exhaustive] table of comparisons between hashes produced by the UNIX tool md5sum and jmd5.

5 REFERENCES

R, Rivest, RFC 1321—The MD5 Message-Digest Algorithm. http://www.faqs.org/rfcs/rfc1321.html

Message Digests and Digital Fingerprints

Appendix A

/*******************************************************************
*   Copyright (C) 2003 by James Vanns                              *
*   jimv@canterbury.ac.uk                                          *
*                                                                  *
*   Based on RFC1321, Copyright (C) 1990 -2 RSA Data Security, Inc.*
*   All rights reserved. MD5 algorithm by Ron Rivest.              *
*                                                                  *
*   This program is free software; you can redistribute it and/or  *
*   modify it under the terms of the GNU General Public License as *
*   published by the Free Software Foundation; either version 2 of *
*   the License, or (at your option) any later version.            *
*******************************************************************/

// Standard C++ headers
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <exception>

// Standard C headers
#include <cstdarg>
using std::fill;
using std::string;
using std::vector;
using std::ostream;
using std::exception;

#ifndef MD5_H
#define MD5_H

typedef unsigned char byte;
typedef unsigned long int word;

class jmd5 {
   public:
      jmd5 ();
      ~jmd5 ();
      jmd5 (const string &input, const bool &file = false);

      const uint operator [] (const uint &index) const;

      friend ostream& operator << (ostream& stream, const jmd5 &digest) {
         string hash;
         char bits [3 ];

         fill (bits, bits + sizeof (bits), '\0');

         for (uint i = 0 ; i < digest.size () ; i++) {
            snprintf (bits, sizeof (bits), "%02x", digest [i ]);
            hash += bits;
         }

         return stream << hash;
      };
      class ErrorMessage : public exception {
         private:
            string message;
         public:
            explicit ErrorMessage (const char *format, ...);
            ~ErrorMessage () throw ();
            const char* what (void) const throw ();
      };

   private:
      void init (void);
      void finalise (void);
      const uint size (void) const;
      inline void transform (byte block [64 ]);
      void update (byte *input, const uint length);
      void decode (byte *input, word *output, const uint &length) const;
      void encode (word *input, byte *output, const uint &length) const;

      inline word rl (const word &x, const word &n) const;
      inline word  f (const word &x, const word &y, const word &z) const;
      inline word  g (const word &x, const word &y, const word &z) const;
      inline word  h (const word &x, const word &y, const word &z) const;
      inline word  i (const word &x, const word &y, const word &z) const;

      inline void op (const char &func, word &a, const word &b,
                      const word &c, const word &d, const word &x,
                      const word &s, const word &m) const;
   private:
      word state [4 ];
      word count [2 ];
      byte buffer [64 ];
      byte digest [16 ];
      vector<char> plaintext;
};
#endif


/*******************************************************************
*   Copyright (C) 2003 by James Vanns                              *
*   jimv@canterbury.ac.uk                                          *
*                                                                  *
*   Based on RFC1321, Copyright (C) 1990 -2 RSA Data Security, Inc.*
*   All rights reserved. MD5 algorithm by Ron Rivest.              *
*                                                                  *
*   This program is free software; you can redistribute it and/or  *
*   modify it under the terms of the GNU General Public License    *
*   as published by the Free Software Foundation; either version 2 *
*   of the License, or (at your option) any later version.         *
*******************************************************************/

// Standard C++ headers
#include <fstream>
#include <iterator>

// jmd5 header file
#include "jmd5.h"

using std::ios;
using std::cin;
using std::copy;
using std::istream;
using std::ifstream;
using std::ios_base;
using std::back_inserter;
using std::istreambuf_iterator;

static byte padding [64 ];
static vector<word> magic (64);
static const uint S11 =  7;
static const uint S12 = 12;
static const uint S13 = 17;
static const uint S14 = 22;
static const uint S21 =  5;
static const uint S22 =  9;
static const uint S23 = 14;
static const uint S24 = 20;
static const uint S31 =  4;
static const uint S32 = 11;
static const uint S33 = 16;
static const uint S34 = 23;
static const uint S41 =  6;
static const uint S42 = 10;
static const uint S43 = 15;
static const uint S44 = 21;

jmd5::jmd5 () {
   init ();
}

jmd5::jmd5 (const string &input, const bool &file) {
   init ();

   if (file || input == "-") {
      istream *stream = &cin;

      if (input != "-")
         stream = new ifstream (input.c_str (), ios::in);

      if (!stream ->good ())
         throw ErrorMessage ("Failed to open '%s' for reading.",
                             input.c_str ());

      copy (istreambuf_iterator<char> (*stream),
            istreambuf_iterator<char> (), back_inserter (plaintext));

      if (!file)
         plaintext.pop_back ();

      if (stream && stream != &cin)
         delete stream;

   } else
      copy (input.begin (), input.end (), back_inserter (plaintext));
   byte *data = NULL;
   const uint block = 64;
   uint total = 0, bytes = 0, last = 0;
   const uint length = plaintext.size ();

   while ((total += bytes) < length) {
      if ((total + block) > length)
         bytes = length - total;
      else
         bytes = block;

      if (!data)
         data = new byte [bytes ];
      else if (bytes != last) {
         delete [] data;
         data = NULL;
         data = new byte [bytes ];
      }

      if (!data)
         throw ErrorMessage ("Failed to allocate %d bytes of heap
                              storage.", bytes);
      fill (data, data + bytes, 0);
      copy (plaintext.begin () + total, (plaintext.begin () + total)
            + bytes, data);
      update (data, bytes);
      last = bytes;
   }

   finalise ();
}
jmd5::~jmd5  () {
}

void jmd5::init (void) {
   static const double p = pow (2, 32);

   padding [0 ] = 0x80;
   state   [0 ] = 0x67452301;
   state   [1 ] = 0xefcdab89;
   state   [2 ] = 0x98badcfe;
   state   [3 ] = 0x10325476;

   count   [0 ] = count [1 ] = 0;

   fill (buffer, buffer + sizeof (buffer), '\0');
   fill (digest, digest + sizeof (digest), '\0');
   fill (padding + 1, padding + (sizeof (padding) - 1), 0);
   for  (uint r = 0 ; r < magic.size () ; r++)
      magic [r ] = static_cast<word> (p * fabs (sin (r + 1)));
}

inline word jmd5::rl (const word &x, const word &n) const {
   return ((x << n) | (x >> (32 . n)));
}

inline word jmd5::f (const word &x, const word &y, const word &z) const {
   return x & y | ~x & z;
}

inline word jmd5::g (const word &x, const word &y, const word &z) const {
   return x & z  | y & ~z;
}

inline word jmd5::h (const word &x, const word &y, const word &z) const {
   return x ^ y ^ z;
}

inline word jmd5::i (const word &x, const word &y, const word &z) const {
   return y ^ (x | ~z);
}

inline void jmd5::op (const char &func, word &a, const word &b,
                      const word &c, const word &d, const word &x,
                      const word &s, const word &m) const {

   switch (func) {
      case 'f':
         a += f (b, c, d) + x + m;
         break;
      case 'g':
         a += g (b, c, d) + x + m;
         break;
      case 'h':
         a += h (b, c, d) + x + m;
         break;
      case 'i':
         a += i (b, c, d) + x + m;
         break;
   }

   a = rl (a, s) + b;
}

void jmd5::update (byte *input, uint length) {
   uint index = count [0 ] >> 3 & 0x3f, n = 0;

   if ((count [0 ] += (length << 3)) < (length << 3))
      count [1 ]++;

   count [1 ] += (length >> 29);

   uint part = 64 - index;

   if (length >= part) {
      copy (input, input + part, &buffer [index ]);
      transform (buffer);

      for (n = part ; n + 63 < length ; n += 64)
         transform (&input [n ]);

      index = 0;
   }

   copy (input, input + (length - n), &buffer [index ]);
}

inline  void  jmd5::transform  (byte  block [64 ]) {
   word a = state [0 ], b = state [1 ], c = state [2 ],
        d = state [3 ], x [16 ];

   decode (block, x, 64);

   op ('f', a, b, c, d, x [ 0 ], S11, magic.at (0));
   op ('f', d, a, b, c, x [ 1 ], S12, magic.at (1));
   op ('f', c, d, a, b, x [ 2 ], S13, magic.at (2));
   op ('f', b, c, d, a, x [ 3 ], S14, magic.at (3));
   op ('f', a, b, c, d, x [ 4 ], S11, magic.at (4));
   op ('f', d, a, b, c, x [ 5 ], S12, magic.at (5));
   op ('f', c, d, a, b, x [ 6 ], S13, magic.at (6));
   op ('f', b, c, d, a, x [ 7 ], S14, magic.at (7));
   op ('f', a, b, c, d, x [ 8 ], S11, magic.at (8));
   op ('f', d, a, b, c, x [ 9 ], S12, magic.at (9));
   op ('f', c, d, a, b, x [10 ], S13, magic.at (10));
   op ('f', b, c, d, a, x [11 ], S14, magic.at (11));
   op ('f', a, b, c, d, x [12 ], S11, magic.at (12));
   op ('f', d, a, b, c, x [13 ], S12, magic.at (13));
   op ('f', c, d, a, b, x [14 ], S13, magic.at (14));
   op ('f', b, c, d, a, x [15 ], S14, magic.at (15));

   op ('g', a, b, c, d, x [ 1 ], S21, magic.at (16));
   op ('g', d, a, b, c, x [ 6 ], S22, magic.at (17));
   op ('g', c, d, a, b, x [11 ], S23, magic.at (18));
   op ('g', b, c, d, a, x [ 0 ], S24, magic.at (19));
   op ('g', a, b, c, d, x [ 5 ], S21, magic.at (20));
   op ('g', d, a, b, c, x [10 ], S22, magic.at (21));
   op ('g', c, d, a, b, x [15 ], S23, magic.at (22));
   op ('g', b, c, d, a, x [ 4 ], S24, magic.at (23));
   op ('g', a, b, c, d, x [ 9 ], S21, magic.at (24));
   op ('g', d, a, b, c, x [14 ], S22, magic.at (25));
   op ('g', c, d, a, b, x [ 3 ], S23, magic.at (26));
   op ('g', b, c, d, a, x [ 8 ], S24, magic.at (27));
   op ('g', a, b, c, d, x [13 ], S21, magic.at (28));
   op ('g', d, a, b, c, x [ 2 ], S22, magic.at (29));
   op ('g', c, d, a, b, x [ 7 ], S23, magic.at (30));
   op ('g', b, c, d, a, x [12 ], S24, magic.at (31));

   op ('h', a, b, c, d, x [ 5 ], S31, magic.at (32));
   op ('h', d, a, b, c, x [ 8 ], S32, magic.at (33));
   op ('h', c, d, a, b, x [11 ], S33, magic.at (34));
   op ('h', b, c, d, a, x [14 ], S34, magic.at (35));
   op ('h', a, b, c, d, x [ 1 ], S31, magic.at (36));
   op ('h', d, a, b, c, x [ 4 ], S32, magic.at (37));
   op ('h', c, d, a, b, x [ 7 ], S33, magic.at (38));
   op ('h', b, c, d, a, x [10 ], S34, magic.at (39));
   op ('h', a, b, c, d, x [13 ], S31, magic.at (40));
   op ('h', d, a, b, c, x [ 0 ], S32, magic.at (41));
   op ('h', c, d, a, b, x [ 3 ], S33, magic.at (42));
   op ('h', b, c, d, a, x [ 6 ], S34, magic.at (43));
   op ('h', a, b, c, d, x [ 9 ], S31, magic.at (44));
   op ('h', d, a, b, c, x [12 ], S32, magic.at (45));
   op ('h', c, d, a, b, x [15 ], S33, magic.at (46));
   op ('h', b, c, d, a, x [ 2 ], S34, magic.at (47));

   op ('i', a, b, c, d, x [ 0 ], S41, magic.at (48));
   op ('i', d, a, b, c, x [ 7 ], S42, magic.at (49));
   op ('i', c, d, a, b, x [14 ], S43, magic.at (50));
   op ('i', b, c, d, a, x [ 5 ], S44, magic.at (51));
   op ('i', a, b, c, d, x [12 ], S41, magic.at (52));
   op ('i', d, a, b, c, x [ 3 ], S42, magic.at (53));
   op ('i', c, d, a, b, x [10 ], S43, magic.at (54));
   op ('i', b, c, d, a, x [ 1 ], S44, magic.at (55));
   op ('i', a, b, c, d, x [ 8 ], S41, magic.at (56));
   op ('i', d, a, b, c, x [15 ], S42, magic.at (57));
   op ('i', c, d, a, b, x [ 6 ], S43, magic.at (58));
   op ('i', b, c, d, a, x [13 ], S44, magic.at (59));
   op ('i', a, b, c, d, x [ 4 ], S41, magic.at (60));
   op ('i', d, a, b, c, x [11 ], S42, magic.at (61));
   op ('i', c, d, a, b, x [ 2 ], S43, magic.at (62));
   op ('i', b, c, d, a, x [ 9 ], S44, magic.at (63));

   state [0 ] += a;
   state [1 ] += b;
   state [2 ] += c;
   state [3 ] += d;
}

void jmd5::finalise (void) {
   byte bits [8 ];

   encode (count, bits, sizeof (bits));

   uint index  = count [0 ] >> 3 & 0x3f;
   uint length = (index < 56) ? (56 - index) : (120 - index);

   update (padding, length);
   update (bits, sizeof (bits));

   encode (state, digest, sizeof (digest));

   fill (state, state + sizeof (state), 0);
   fill (count, count + sizeof (count), 0);
   fill (buffer, buffer + sizeof (buffer), '\0');
}

const uint jmd5::size (void) const {
   return sizeof (digest);
}

const uint jmd5::operator [] (const uint &index) const {
   return digest [index ];
}

void jmd5::decode (byte *input, word *output, const uint &length) const {
   uint j = 0, k = 0;
   for ( ; k < length ; j++, k += 4)
      output [j ] = input [k ] | input [k + 1 ] << 8
                               | input [k + 2 ] << 16
                               | input [k + 3 ] << 24;

}

void jmd5::encode (word *input, byte *output, const uint &length) const {
   uint j = 0, k = 0;
   for ( ; k < length ; j++, k += 4) {
      output [k ] = input [j ] & 0xff;
      output [k + 1 ] = input [j ] >>  8 & 0xff;
      output [k + 2 ] = input [j ] >> 16 & 0xff;
      output [k + 3 ] = input [j ] >> 24 & 0xff;
   }
}

jmd5::ErrorMessage::ErrorMessage (const char *format, ...) : message ("Error: ") {
   va_list args;
   char buffer [BUFSIZ ];

   memset (buffer, '\0', sizeof (buffer));

   va_start  (args, format);
   vsnprintf (buffer, sizeof (buffer), format, args);
   va_end    (args);

   message += buffer;
   message += '\n';
}

jmd5::ErrorMessage::~ErrorMessage () throw () {
}

const char* jmd5::ErrorMessage::what (void) const throw () {
   return message.c_str ();
}


/*******************************************************************
*   Copyright (C) 2003 by James Vanns                              *
*   jimv@canterbury.ac.uk                                          *
*                                                                  *
*   Based on RFC1321, Copyright (C) 1990 -2 RSA Data Security, Inc.*
*   All rights reserved. MD5 algorithm by Ron Rivest.              *
*                                                                  *
*   This program is free software; you can redistribute it and/or  *
*   modify it under the terms of the GNU General Public License as *
*   published by the Free Software Foundation; either version 2 of *
*   the  License, or (at your option) any later version.           *
*******************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

// Standard C headers
#include <ctime>
#include <cstdlib>
#include <getopt.h>

// jmd5 header
#include "jmd5.h"

using std::ios;
using std::cerr;
using std::cout;
using std::endl;

static bool verbose = false;
static const string program;

static void print_usage (void) {
   cout < program < ": [-v ] -s <\"text string \"> ||
                            -f <filename> || -t \n";
   exit (EXIT_FAILURE);
}

static void print_hash (const string &plaintext, const jmd5 &hash) {
   if (verbose)
      cout << "Input: '" << plaintext << "'\n" << "Output: ";

   cout << hash << endl;
}

int main (int argc, char *argv []) {
   string s;
   int opt = 0;
   extern char *optarg;
   extern bool verbose;
   extern string program;
   const time_t epoch = time (NULL);

   program = argv [0 ];

   if (argc < 2 || argc > 4)
      print_usage ();

   if (argc == 2 && (strcmp (argv [1 ], "-v") == 0))
      print_usage ();

   try {
      while ((opt = getopt (argc, argv, "f:s:tv")) != -1) {
         switch (opt) {
            case 'f':
               s = optarg;
               print_hash ((s == "-" ? "stdin" : s), jmd5 (s, true));
               break;
            case 's':
               s = optarg;
               print_hash ((s == "-" ? "stdin" : s), jmd5 (s, false));
               break;
            case 't':
               s = ctime (&epoch);
               s.erase (remove (s.begin (), s.end (), '\n'), s.end  ());
               print_hash (s, jmd5 (s, false));
               break;
            case 'v':
               verbose = true;
               break;
            case '?':
               print_usage ();
               break;
         }
      }
   } catch (const exception &error) {
      cerr << error.what ();
   }
   return  EXIT_SUCCESS;
}

Appendix B

Filename File type md5sum hash
/boot/vmlinuz-2.4.20-28.9 Compressed binary 998ec5ec93642fe9458aef800039dc0b
/etc/fstab ASCII Text dff3df646ae00685a15f7de806c80b84
/usr/bin/vim Binary 070fd8b623b374de74e445131245c978
~/.bash_profile ASCII Text 9d0268f2afbd18b58459b6c59d5f7ece
/usr/src/enlightenment-0.16.6-1.i386.rpm Binary (redhat RPM) 348d5a6c6ad90593aeb82322f7f83662

Filename File type jmd5 hash
/boot/vmlinuz-2.4.20-28.9 Compressed binary 998ec5ec93642fe9458aef800039dc0b
/etc/fstab ASCII Text dff3df646ae00685a15f7de806c80b84
/usr/bin/vim Binary 070fd8b623b374de74e445131245c978
~/.bash_profile ASCII Text 9d0268f2afbd18b58459b6c59d5f7ece
/usr/src/enlightenment-0.16.6-1.i386.rpm Binary (redhat RPM) 348d5a6c6ad90593aeb82322f7f83662


Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 13, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT APIs can be a great source of competitive advantage. The practice of exposing backend services as APIs has become pervasive, however their use varies widely across companies and industries. Some companies leverage APIs to create internal, operational and development efficiencies, while others use them to drive ancillary revenue channels. Many companies successfully support both public and private programs from the same API by varying levels …

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds