The purpose of this task to code and validate an implementation of the MD5 Message Digest Algorithm by coding the algorithm directly (not using a call to a built-in or external hashing library). For details of the algorithm refer to MD5 on Wikipedia or the MD5 definition in IETF RFC (1321).

 The implementation needs to implement the key functionality namely producing a correct message digest for an input string. It is not necessary to mimic all of the calling modes such as adding to a digest one block at a time over subsequent calls. 
 In addition to coding and verifying your implementation, note any challenges your language presented implementing the solution, implementation choices made, or limitations of your solution. 
 Solutions on this page should implement MD5 directly and NOT use built in (MD5) functions, call outs to operating system calls or library routines written in other languages as is common in the original MD5 task.
 The following are acceptable:
 An original implementation from the specification, reference implementation, or pseudo-code
 A translation of a correct implementation from another language
 A library routine in the same language; however, the source must be included here.


The solutions shown here will provide practical illustrations of bit manipulation, unsigned integers, working with little-endian data. Additionally, the task requires an attention to details such as boundary conditions since being out by even 1 bit will produce dramatically different results. Subtle implementation bugs can result in some hashes being correct while others are wrong. Not only is it critical to get the individual sub functions working correctly, even small errors in padding, endianness, or data layout will result in failure.

RFC 1321
                            hash code <== string 
   0xd41d8cd98f00b204e9800998ecf8427e <== ""  
   0x0cc175b9c0f1b6a831c399e269772661 <== "a"
   0x900150983cd24fb0d6963f7d28e17f72 <== "abc"
   0xf96b697d7cb7938d525a2f31aaf161d0 <== "message digest"
   0xc3fcd3d76192e4007dfb496cca67e13b <== "abcdefghijklmnopqrstuvwxyz"
   0xd174ab98d277d9f5a5611c2c9f419d9f <== "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
   0x57edf4a22be3c955ac49da2e2107b67a <== "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
In addition, intermediate outputs to aid in developing an implementation can be found here.

The MD5 Message-Digest Algorithm was developed by RSA Data Security, Inc. in 1991.



 Warning
Rosetta Code is not a place you should rely on for examples of code in critical roles, including security.
Also, note that MD5 has been broken and should not be used in applications requiring security. For these consider SHA2 or the upcoming SHA3.

**FREE
Ctl-opt MAIN(Main);
Ctl-opt DFTACTGRP(*NO) ACTGRP(*NEW);
 
dcl-pr QDCXLATE EXTPGM('QDCXLATE');
  dataLen         packed(5 : 0) CONST;
  data            char(32767) options(*VARSIZE);
  conversionTable char(10) CONST;
end-pr;
 
dcl-c MASK32 CONST(4294967295);
dcl-s SHIFT_AMTS int(3) dim(16) CTDATA PERRCD(16);
dcl-s MD5_TABLE_T int(20) dim(64) CTDATA PERRCD(4);
 
dcl-proc Main;
  dcl-s inputData char(45);
  dcl-s inputDataLen int(10) INZ(0);
  dcl-s outputHash char(16);
  dcl-s outputHashHex char(32);
 
  DSPLY 'Input: ' '' inputData;
  inputData = %trim(inputData);
  inputDataLen = %len(%trim(inputData));
  DSPLY ('Input=' + inputData);
  DSPLY ('InputLen=' + %char(inputDataLen));
 
  // Convert from EBCDIC to ASCII
  if inputDataLen > 0;
    QDCXLATE(inputDataLen : inputData : 'QTCPASC');
  endif;
  CalculateMD5(inputData : inputDataLen : outputHash);
  // Convert to hex
  ConvertToHex(outputHash : 16 : outputHashHex);
  DSPLY ('MD5: ' + outputHashHex);
  return;
end-proc;
 
dcl-proc CalculateMD5;
  dcl-pi *N;
    message    char(65535) options(*VARSIZE) CONST;
    messageLen int(10) value;
    outputHash char(16);
  end-pi;
  dcl-s numBlocks int(10);
  dcl-s padding char(72);
  dcl-s a int(20) INZ(1732584193);
  dcl-s b int(20) INZ(4023233417);
  dcl-s c int(20) INZ(2562383102);
  dcl-s d int(20) INZ(271733878);
  dcl-s buffer int(20) dim(16) INZ(0);
  dcl-s i int(10);
  dcl-s j int(10);
  dcl-s k int(10);
  dcl-s multiplier int(20);
  dcl-s index int(10);
  dcl-s originalA int(20);
  dcl-s originalB int(20);
  dcl-s originalC int(20);
  dcl-s originalD int(20);
  dcl-s div16 int(10);
  dcl-s f int(20);
  dcl-s tempInt int(20);
  dcl-s bufferIndex int(10);
  dcl-ds byteToInt QUALIFIED;
    n int(5) INZ(0);
    c char(1) OVERLAY(n : 2);
  end-ds;
 
  numBlocks = (messageLen + 8) / 64 + 1;
  MD5_FillPadding(messageLen : numBlocks : padding);
  for i = 0 to numBlocks - 1;
    index = i * 64;
 
    // Read message as little-endian 32-bit words
    for j = 1 to 16;
      multiplier = 1;
      for k = 1 to 4;
        index += 1;
        if index <= messageLen;
          byteToInt.c = %subst(message : index : 1);
        else;
          byteToInt.c = %subst(padding : index - messageLen : 1);
        endif;
        buffer(j) += multiplier * byteToInt.n;
        multiplier *= 256;
      endfor;
    endfor;
 
    originalA = a;
    originalB = b;
    originalC = c;
    originalD = d;
 
    for j = 0 to 63;
      div16 = j / 16;
      select;
        when div16 = 0;
          f = %bitor(%bitand(b : c) : %bitand(%bitnot(b) : d));
          bufferIndex = j;
 
        when div16 = 1;
          f = %bitor(%bitand(b : d) : %bitand(c : %bitnot(d)));
          bufferIndex = %bitand(j * 5 + 1 : 15);
 
        when div16 = 2;
          f = %bitxor(b : %bitxor(c : d));
          bufferIndex = %bitand(j * 3 + 5 : 15);
 
        when div16 = 3;
          f = %bitxor(c : %bitor(b : Mask32Bit(%bitnot(d))));
          bufferIndex = %bitand(j * 7 : 15);
      endsl;
      tempInt = Mask32Bit(b + RotateLeft32Bit(a + f + buffer(bufferIndex + 1) + MD5_TABLE_T(j + 1) :
                                              SHIFT_AMTS(div16 * 4 + %bitand(j : 3) + 1)));
      a = d;
      d = c;
      c = b;
      b = tempInt;
    endfor;
    a = Mask32Bit(a + originalA);
    b = Mask32Bit(b + originalB);
    c = Mask32Bit(c + originalC);
    d = Mask32Bit(d + originalD);
  endfor;
 
  for i = 0 to 3;
    if i = 0;
      tempInt = a;
    elseif i = 1;
      tempInt = b;
    elseif i = 2;
      tempInt = c;
    else;
      tempInt = d;
    endif;
 
    for j = 0 to 3;
      byteToInt.n = %bitand(tempInt : 255);
      %subst(outputHash : i * 4 + j + 1 : 1) = byteToInt.c;
      tempInt /= 256;
    endfor;
  endfor;
  return;
end-proc;
 
dcl-proc MD5_FillPadding;
  dcl-pi *N;
    messageLen int(10);
    numBlocks int(10);
    padding char(72);
  end-pi;
  dcl-s totalLen int(10);
  dcl-s paddingSize int(10);
  dcl-ds *N;
    messageLenBits int(20);
    mlb_bytes char(8) OVERLAY(messageLenBits);
  end-ds;
  dcl-s i int(10);
 
  %subst(padding : 1 : 1) = X'80';
  totalLen = numBlocks * 64;
  paddingSize = totalLen - messageLen; // 9 to 72
  messageLenBits = messageLen;
  messageLenBits *= 8;
  for i = 1 to 8;
    %subst(padding : paddingSize - i + 1 : 1) = %subst(mlb_bytes : i : 1);
  endfor;
  for i = 2 to paddingSize - 8;
    %subst(padding : i : 1) = X'00';
  endfor;
  return;
end-proc;
 
dcl-proc RotateLeft32Bit;
  dcl-pi *N int(20);
    n int(20) value;
    amount int(3) value;
  end-pi;
  dcl-s i int(3);
 
  n = Mask32Bit(n);
  for i = 1 to amount;
    n *= 2;
    if n >= 4294967296;
      n -= MASK32;
    endif;
  endfor;
  return n;
end-proc;
 
dcl-proc Mask32Bit;
  dcl-pi *N int(20);
    n int(20) value;
  end-pi;
  return %bitand(n : MASK32);
end-proc;
 
dcl-proc ConvertToHex;
  dcl-pi *N;
    inputData    char(32767) options(*VARSIZE) CONST;
    inputDataLen int(10) value;
    outputData   char(65534) options(*VARSIZE);
  end-pi;
  dcl-c HEX_CHARS CONST('0123456789ABCDEF');
  dcl-s i int(10);
  dcl-s outputOffset int(10) INZ(1);
  dcl-ds dataStruct QUALIFIED;
    numField int(5) INZ(0);
    // IBM i is big-endian
    charField char(1) OVERLAY(numField : 2);
  end-ds;
 
  for i = 1 to inputDataLen;
    dataStruct.charField = %BitAnd(%subst(inputData : i : 1) : X'F0');
    dataStruct.numField /= 16;
    %subst(outputData : outputOffset : 1) = %subst(HEX_CHARS : dataStruct.numField + 1 : 1);
    outputOffset += 1;
    dataStruct.charField = %BitAnd(%subst(inputData : i : 1) : X'0F');
    %subst(outputData : outputOffset : 1) = %subst(HEX_CHARS : dataStruct.numField + 1 : 1);
    outputOffset += 1;
  endfor;
  return;
end-proc;
 
**CTDATA SHIFT_AMTS
  7 12 17 22  5  9 14 20  4 11 16 23  6 10 15 21
**CTDATA MD5_TABLE_T
          3614090360          3905402710           606105819          3250441966
          4118548399          1200080426          2821735955          4249261313
          1770035416          2336552879          4294925233          2304563134
          1804603682          4254626195          2792965006          1236535329
          4129170786          3225465664           643717713          3921069994
          3593408605            38016083          3634488961          3889429448
           568446438          3275163606          4107603335          1163531501
          2850285829          4243563512          1735328473          2368359562
          4294588738          2272392833          1839030562          4259657740
          2763975236          1272893353          4139469664          3200236656
           681279174          3936430074          3572445317            76029189
          3654602809          3873151461           530742520          3299628645
          4096336452          1126891415          2878612391          4237533241
          1700485571          2399980690          4293915773          2240044497
          1873313359          4264355552          2734768916          1309151649
          4149444226          3174756917           718787259          3951481745