Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

MD5 in WSA (Metro) and others (but no WP8)

Discussion in 'Windows' started by unimechanic, Jan 8, 2014.

  1. unimechanic

    unimechanic

    Joined:
    Jan 9, 2013
    Posts:
    155
    Here is how to calculate MD5 in Windows Store Applications (Metro) and other platforms, except Windows Phone 8.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. #if UNITY_METRO
    5. using Windows.Security.Cryptography;
    6. using Windows.Security.Cryptography.Core;
    7. using Windows.Storage.Streams;
    8. #else
    9. using System.Text;
    10. using System.Security.Cryptography;
    11. #endif
    12.  
    13. public class MD5Test : MonoBehaviour {
    14.     string md5val;
    15.  
    16.     void Start () {
    17.         md5val = Md5Sum("Hello World!");
    18.         Debug.Log(md5val);
    19.     }
    20.    
    21.     void OnGUI () {
    22.         GUILayout.Label(md5val);
    23.     }
    24.  
    25.     public string Md5Sum(string strToEncrypt)
    26.     {
    27. #if UNITY_METRO
    28.         // Convert the message string to binary data.
    29.         IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(strToEncrypt, BinaryStringEncoding.Utf8);
    30.        
    31.         // Create a HashAlgorithmProvider object.
    32.         HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
    33.        
    34.         // Demonstrate how to retrieve the name of the hashing algorithm.
    35.         string strAlgNameUsed = objAlgProv.AlgorithmName;
    36.        
    37.         // Hash the message.
    38.         IBuffer buffHash = objAlgProv.HashData(buffUtf8Msg);
    39.        
    40.         // Verify that the hash length equals the length specified for the algorithm.
    41.         if (buffHash.Length != objAlgProv.HashLength)
    42.             return null;
    43.  
    44.         // Convert the hash to a string (for display).
    45.         string strHashBase64 = CryptographicBuffer.EncodeToBase64String(buffHash);
    46.        
    47.         // Return the encoded string
    48.         return strHashBase64.PadLeft(32, '0');
    49. #else
    50.         UTF8Encoding ue = new UTF8Encoding();
    51.         byte[] bytes = ue.GetBytes(strToEncrypt);
    52.        
    53.         // encrypt bytes
    54.         MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
    55.         byte[] hashBytes = md5.ComputeHash(bytes);
    56.        
    57.         // Convert the encrypted bytes back to a string (base 16)
    58.         string hashString = "";
    59.        
    60.         for (int i = 0; i < hashBytes.Length; i++)
    61.         {
    62.             hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');
    63.         }
    64.        
    65.         return hashString.PadLeft(32, '0');
    66. #endif
    67.     }
    68. }
    69.  
    This is based in this documentation and example:
    http://msdn.microsoft.com/en-us/lib....cryptography.core.hashalgorithmprovider.aspx

    MD5 is not available in Windows Phone 8:
    http://social.msdn.microsoft.com/Fo...fa6a0e3ecae/how-to-md5-in-wp8?forum=wpdevelop
     
  2. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
  3. unimechanic

    unimechanic

    Joined:
    Jan 9, 2013
    Posts:
    155
    That will be very useful indeed, several users have asked about this to Unity Support.

    I tested Faraz Mahmood's implementation and it seems to work fine, but the hashes generated with long strings don't always match those of the API. Also found this one from Microsoft:

    http://archive.msdn.microsoft.com/SilverlightMD5

    In the comments a user says the output doesn't match neither. I would still recommend most libraries out there to calculate MD5, but people might have problems when comparing their values with those from other services.
     
  4. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Jeff Wilcox made one for Silverlight 2 Beta. :) It returns a string but the reason it doesn't match is because it's returning a Hex encoded string. I modified his version and added a "GetMd5Bytes" method which returns the raw byte array. Then I converted it to a Base64 string and re-encoded the original string using the built in MD5CryptoServiceProvider from Microsoft (calling the ComputHash method) and Base64 encoded that. Upon comparison, both results match.

    Here is the test behavior I wrote to test it:

    Code (csharp):
    1.  
    2. using System;
    3. using System.IO;
    4. using System.Text;
    5. using UnityEngine;
    6. using JeffWilcox.Utilities.Silverlight;
    7.  
    8. public class MD5TestBehavior : MonoBehaviour {
    9.  
    10.     void Start ()
    11.     {
    12.         var testString = "This is a test string";
    13.  
    14.         var hash = MD5CryptoServiceProvider.GetMd5Bytes(testString);
    15.         Debug.Log(Convert.ToBase64String(hash));
    16.  
    17.         using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(testString)))
    18.         {
    19.             var netcsp = new MD5CryptoServiceProvider();
    20.             var hash2 = netcsp.ComputeHash(stream);
    21.  
    22.             Debug.Log(Convert.ToBase64String(hash2));
    23.  
    24.         }    
    25.     }
    26. }
    27.  
    And the license by Jeff Wilcox says you can use and are free to modify and change the namespace. I left the namespace as is so change it to whatever you like. Here is the source for the original:

    http://www.jeff.wilcox.name/2008/03/silverlight-2-md5/

    And here is my modified code to allow retrieving the raw byte array:

    Code (csharp):
    1.  
    2. /****************************************************************************
    3.      Copyright 2002-2005 GL Conseil/Flow Group SAS.  All rights reserved.
    4.  
    5.     Permission is granted to anyone to use this software for any purpose on
    6.     any computer system, and to alter it and redistribute it, subject
    7.     to the following restrictions:
    8.  
    9.     1. The author is not responsible for the consequences of use of this
    10.        software, no matter how awful, even if they arise from flaws in it.
    11.  
    12.     2. The origin of this software must not be misrepresented, either by
    13.        explicit claim or by omission.  Since few users ever read sources,
    14.        credits must appear in the documentation.
    15.  
    16.     3. Altered versions must be plainly marked as such, and must not be
    17.        misrepresented as being the original software.  Since few users
    18.        ever read sources, credits must appear in the documentation.
    19.  
    20.     4. This notice may not be removed or altered.
    21.  
    22.  ----------------------------------------------------------------------------
    23.  This software is derived from the RSA Data Security, Inc.
    24.  MD5 Message-Digest Algorithm.
    25.  
    26.  Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990.
    27.  All rights reserved.
    28.  
    29.  
    30.  RSA Data Security, Inc. makes no representations concerning either
    31.  the merchantability of this software or the suitability of this
    32.  software for any particular purpose. It is provided ""as is""
    33.  without express or implied warranty of any kind.
    34.  
    35.  These notices must be retained in any copies of any part of this
    36.  documentation and/or software.
    37.  
    38.  Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
    39.  All rights reserved.
    40.  ****************************************************************************/
    41.  
    42. // src: http://www.flowgroup.fr/en/kb/technical/md5.aspx
    43. // 03/17/07
    44. // Changed namespace
    45.  
    46. using System;
    47. using System.IO;
    48. using System.Text;
    49.  
    50. namespace JeffWilcox.Utilities.Silverlight
    51. {
    52.     public class MD5CryptoServiceProvider : MD5
    53.     {
    54.         public MD5CryptoServiceProvider()
    55.             : base()
    56.         {
    57.         }
    58.     }
    59.     /// <summary>
    60.     /// Summary description for MD5.
    61.     /// </summary>
    62.     public class MD5 : IDisposable
    63.     {
    64.         static public MD5 Create(string hashName)
    65.         {
    66.             if (hashName == "MD5")
    67.                 return new MD5();
    68.             else
    69.                 throw new NotSupportedException();
    70.         }
    71.  
    72.         /// <summary>
    73.         /// In accordance with the license requirement of
    74.         /// documenting changes, this method has been added by
    75.         /// Dustin Horne (http://www.dustinhorne.com) to allow
    76.         /// retrieving the raw bytes of the computed hash
    77.         /// </summary>
    78.         /// <param name="source"></param>
    79.         /// <returns></returns>
    80.         public static byte[] GetMd5Bytes(String source)
    81.         {
    82.             MD5 md = MD5CryptoServiceProvider.Create();
    83.             byte[] hash;
    84.  
    85.             //Create a new instance of ASCIIEncoding to
    86.             //convert the string into an array of Unicode bytes.
    87.             UTF8Encoding enc = new UTF8Encoding();
    88.             //            ASCIIEncoding enc = new ASCIIEncoding();
    89.  
    90.             //Convert the string into an array of bytes.
    91.             byte[] buffer = enc.GetBytes(source);
    92.  
    93.             //Create the hash value from the array of bytes.
    94.             hash = md.ComputeHash(buffer);
    95.  
    96.             return hash;
    97.         }
    98.  
    99.         /// In accordance with the license requirement of
    100.         /// documenting changes, this method has modified
    101.         /// by Dustin Horne (http://www.dustinhorne.com)
    102.         /// to call the new GetMd5Bytes method
    103.         static public string GetMd5String(String source)
    104.         {
    105.             var hash = GetMd5Bytes(source);
    106.  
    107.             StringBuilder sb = new StringBuilder();
    108.             foreach (byte b in hash)
    109.                 sb.Append(b.ToString("x2"));
    110.             return sb.ToString();
    111.         }
    112.  
    113.         static public MD5 Create()
    114.         {
    115.             return new MD5();
    116.         }
    117.  
    118.         #region base implementation of the MD5
    119.         #region constants
    120.         private const byte S11 = 7;
    121.         private const byte S12 = 12;
    122.         private const byte S13 = 17;
    123.         private const byte S14 = 22;
    124.         private const byte S21 = 5;
    125.         private const byte S22 = 9;
    126.         private const byte S23 = 14;
    127.         private const byte S24 = 20;
    128.         private const byte S31 = 4;
    129.         private const byte S32 = 11;
    130.         private const byte S33 = 16;
    131.         private const byte S34 = 23;
    132.         private const byte S41 = 6;
    133.         private const byte S42 = 10;
    134.         private const byte S43 = 15;
    135.         private const byte S44 = 21;
    136.         static private byte[] PADDING = new byte[] {
    137.                                                         0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    138.                                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    139.                                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    140.                                                     };
    141.         #endregion
    142.  
    143.         #region F, G, H and I are basic MD5 functions.
    144.         static private uint F(uint x, uint y, uint z)
    145.         {
    146.             return (((x)  (y)) | ((~x)  (z)));
    147.         }
    148.         static private uint G(uint x, uint y, uint z)
    149.         {
    150.             return (((x)  (z)) | ((y)  (~z)));
    151.         }
    152.         static private uint H(uint x, uint y, uint z)
    153.         {
    154.             return ((x) ^ (y) ^ (z));
    155.         }
    156.         static private uint I(uint x, uint y, uint z)
    157.         {
    158.             return ((y) ^ ((x) | (~z)));
    159.         }
    160.         #endregion
    161.  
    162.         #region rotates x left n bits.
    163.         /// <summary>
    164.         /// rotates x left n bits.
    165.         /// </summary>
    166.         /// <param name="x"></param>
    167.         /// <param name="n"></param>
    168.         /// <returns></returns>
    169.         static private uint ROTATE_LEFT(uint x, byte n)
    170.         {
    171.             return (((x) << (n)) | ((x) >> (32 - (n))));
    172.         }
    173.         #endregion
    174.  
    175.         #region FF, GG, HH, and II transformations
    176.         /// FF, GG, HH, and II transformations
    177.         /// for rounds 1, 2, 3, and 4.
    178.         /// Rotation is separate from addition to prevent recomputation.
    179.         static private void FF(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac)
    180.         {
    181.             (a) += F((b), (c), (d)) + (x) + (uint)(ac);
    182.             (a) = ROTATE_LEFT((a), (s));
    183.             (a) += (b);
    184.         }
    185.         static private void GG(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac)
    186.         {
    187.             (a) += G((b), (c), (d)) + (x) + (uint)(ac);
    188.             (a) = ROTATE_LEFT((a), (s));
    189.             (a) += (b);
    190.         }
    191.         static private void HH(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac)
    192.         {
    193.             (a) += H((b), (c), (d)) + (x) + (uint)(ac);
    194.             (a) = ROTATE_LEFT((a), (s));
    195.             (a) += (b);
    196.         }
    197.         static private void II(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac)
    198.         {
    199.             (a) += I((b), (c), (d)) + (x) + (uint)(ac);
    200.             (a) = ROTATE_LEFT((a), (s));
    201.             (a) += (b);
    202.         }
    203.         #endregion
    204.  
    205.         #region context info
    206.         /// <summary>
    207.         /// state (ABCD)
    208.         /// </summary>
    209.         uint[] state = new uint[4];
    210.  
    211.         /// <summary>
    212.         /// number of bits, modulo 2^64 (lsb first)
    213.         /// </summary>
    214.         uint[] count = new uint[2];
    215.  
    216.         /// <summary>
    217.         /// input buffer
    218.         /// </summary>
    219.         byte[] buffer = new byte[64];
    220.         #endregion
    221.  
    222.         internal MD5()
    223.         {
    224.             Initialize();
    225.         }
    226.  
    227.         /// <summary>
    228.         /// MD5 initialization. Begins an MD5 operation, writing a new context.
    229.         /// </summary>
    230.         /// <remarks>
    231.         /// The RFC named it "MD5Init"
    232.         /// </remarks>
    233.         public virtual void Initialize()
    234.         {
    235.             count[0] = count[1] = 0;
    236.  
    237.             // Load magic initialization constants.
    238.             state[0] = 0x67452301;
    239.             state[1] = 0xefcdab89;
    240.             state[2] = 0x98badcfe;
    241.             state[3] = 0x10325476;
    242.         }
    243.  
    244.         /// <summary>
    245.         /// MD5 block update operation. Continues an MD5 message-digest
    246.         /// operation, processing another message block, and updating the
    247.         /// context.
    248.         /// </summary>
    249.         /// <param name="input"></param>
    250.         /// <param name="offset"></param>
    251.         /// <param name="count"></param>
    252.         /// <remarks>The RFC Named it MD5Update</remarks>
    253.         protected virtual void HashCore(byte[] input, int offset, int count)
    254.         {
    255.             int i;
    256.             int index;
    257.             int partLen;
    258.  
    259.             // Compute number of bytes mod 64
    260.             index = (int)((this.count[0] >> 3)  0x3F);
    261.  
    262.             // Update number of bits
    263.             if ((this.count[0] += (uint)((uint)count << 3)) < ((uint)count << 3))
    264.                 this.count[1]++;
    265.             this.count[1] += ((uint)count >> 29);
    266.  
    267.             partLen = 64 - index;
    268.  
    269.             // Transform as many times as possible.
    270.             if (count >= partLen)
    271.             {
    272.                 Buffer.BlockCopy(input, offset, this.buffer, index, partLen);
    273.                 Transform(this.buffer, 0);
    274.  
    275.                 for (i = partLen; i + 63 < count; i += 64)
    276.                     Transform(input, offset + i);
    277.  
    278.                 index = 0;
    279.             }
    280.             else
    281.                 i = 0;
    282.  
    283.             // Buffer remaining input
    284.             Buffer.BlockCopy(input, offset + i, this.buffer, index, count - i);
    285.         }
    286.  
    287.         /// <summary>
    288.         /// MD5 finalization. Ends an MD5 message-digest operation, writing the
    289.         /// the message digest and zeroizing the context.
    290.         /// </summary>
    291.         /// <returns>message digest</returns>
    292.         /// <remarks>The RFC named it MD5Final</remarks>
    293.         protected virtual byte[] HashFinal()
    294.         {
    295.             byte[] digest = new byte[16];
    296.             byte[] bits = new byte[8];
    297.             int index, padLen;
    298.  
    299.             // Save number of bits
    300.             Encode(bits, 0, this.count, 0, 8);
    301.  
    302.             // Pad out to 56 mod 64.
    303.             index = (int)((uint)(this.count[0] >> 3)  0x3f);
    304.             padLen = (index < 56) ? (56 - index) : (120 - index);
    305.             HashCore(PADDING, 0, padLen);
    306.  
    307.             // Append length (before padding)
    308.             HashCore(bits, 0, 8);
    309.  
    310.             // Store state in digest
    311.             Encode(digest, 0, state, 0, 16);
    312.  
    313.             // Zeroize sensitive information.
    314.             count[0] = count[1] = 0;
    315.             state[0] = 0;
    316.             state[1] = 0;
    317.             state[2] = 0;
    318.             state[3] = 0;
    319.  
    320.             // initialize again, to be ready to use
    321.             Initialize();
    322.  
    323.             return digest;
    324.         }
    325.  
    326.         /// <summary>
    327.         /// MD5 basic transformation. Transforms state based on 64 bytes block.
    328.         /// </summary>
    329.         /// <param name="block"></param>
    330.         /// <param name="offset"></param>
    331.         private void Transform(byte[] block, int offset)
    332.         {
    333.             uint a = state[0], b = state[1], c = state[2], d = state[3];
    334.             uint[] x = new uint[16];
    335.             Decode(x, 0, block, offset, 64);
    336.  
    337.             // Round 1
    338.             FF(ref a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
    339.             FF(ref d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
    340.             FF(ref c, d, a, b, x[2], S13, 0x242070db); /* 3 */
    341.             FF(ref b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
    342.             FF(ref a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
    343.             FF(ref d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
    344.             FF(ref c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
    345.             FF(ref b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
    346.             FF(ref a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
    347.             FF(ref d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
    348.             FF(ref c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
    349.             FF(ref b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
    350.             FF(ref a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
    351.             FF(ref d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
    352.             FF(ref c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
    353.             FF(ref b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
    354.  
    355.             // Round 2
    356.             GG(ref a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
    357.             GG(ref d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
    358.             GG(ref c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
    359.             GG(ref b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
    360.             GG(ref a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
    361.             GG(ref d, a, b, c, x[10], S22, 0x2441453); /* 22 */
    362.             GG(ref c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
    363.             GG(ref b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
    364.             GG(ref a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
    365.             GG(ref d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
    366.             GG(ref c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
    367.             GG(ref b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
    368.             GG(ref a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
    369.             GG(ref d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
    370.             GG(ref c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
    371.             GG(ref b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
    372.  
    373.             // Round 3
    374.             HH(ref a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
    375.             HH(ref d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
    376.             HH(ref c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
    377.             HH(ref b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
    378.             HH(ref a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
    379.             HH(ref d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
    380.             HH(ref c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
    381.             HH(ref b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
    382.             HH(ref a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
    383.             HH(ref d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
    384.             HH(ref c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
    385.             HH(ref b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
    386.             HH(ref a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
    387.             HH(ref d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
    388.             HH(ref c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
    389.             HH(ref b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
    390.  
    391.             // Round 4
    392.             II(ref a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
    393.             II(ref d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
    394.             II(ref c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
    395.             II(ref b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
    396.             II(ref a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
    397.             II(ref d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
    398.             II(ref c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
    399.             II(ref b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
    400.             II(ref a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
    401.             II(ref d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
    402.             II(ref c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
    403.             II(ref b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
    404.             II(ref a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
    405.             II(ref d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
    406.             II(ref c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
    407.             II(ref b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
    408.  
    409.             state[0] += a;
    410.             state[1] += b;
    411.             state[2] += c;
    412.             state[3] += d;
    413.  
    414.             // Zeroize sensitive information.
    415.             for (int i = 0; i < x.Length; i++)
    416.                 x[i] = 0;
    417.         }
    418.  
    419.         /// <summary>
    420.         /// Encodes input (uint) into output (byte). Assumes len is
    421.         ///  multiple of 4.
    422.         /// </summary>
    423.         /// <param name="output"></param>
    424.         /// <param name="outputOffset"></param>
    425.         /// <param name="input"></param>
    426.         /// <param name="inputOffset"></param>
    427.         /// <param name="count"></param>
    428.         private static void Encode(byte[] output, int outputOffset, uint[] input, int inputOffset, int count)
    429.         {
    430.             int i, j;
    431.             int end = outputOffset + count;
    432.             for (i = inputOffset, j = outputOffset; j < end; i++, j += 4)
    433.             {
    434.                 output[j] = (byte)(input[i]  0xff);
    435.                 output[j + 1] = (byte)((input[i] >> 8)  0xff);
    436.                 output[j + 2] = (byte)((input[i] >> 16)  0xff);
    437.                 output[j + 3] = (byte)((input[i] >> 24)  0xff);
    438.             }
    439.         }
    440.  
    441.         /// <summary>
    442.         /// Decodes input (byte) into output (uint). Assumes len is
    443.         /// a multiple of 4.
    444.         /// </summary>
    445.         /// <param name="output"></param>
    446.         /// <param name="outputOffset"></param>
    447.         /// <param name="input"></param>
    448.         /// <param name="inputOffset"></param>
    449.         /// <param name="count"></param>
    450.         static private void Decode(uint[] output, int outputOffset, byte[] input, int inputOffset, int count)
    451.         {
    452.             int i, j;
    453.             int end = inputOffset + count;
    454.             for (i = outputOffset, j = inputOffset; j < end; i++, j += 4)
    455.                 output[i] = ((uint)input[j]) | (((uint)input[j + 1]) << 8) | (((uint)input[j + 2]) << 16) | (((uint)input[j + 3]) << 24);
    456.         }
    457.         #endregion
    458.  
    459.         #region expose the same interface as the regular MD5 object
    460.  
    461.         protected byte[] HashValue;
    462.         protected int State;
    463.         public virtual bool CanReuseTransform
    464.         {
    465.             get
    466.             {
    467.                 return true;
    468.             }
    469.         }
    470.  
    471.         public virtual bool CanTransformMultipleBlocks
    472.         {
    473.             get
    474.             {
    475.                 return true;
    476.             }
    477.         }
    478.         public virtual byte[] Hash
    479.         {
    480.             get
    481.             {
    482.                 if (this.State != 0)
    483.                     throw new InvalidOperationException();
    484.                 return (byte[])HashValue.Clone();
    485.             }
    486.         }
    487.         public virtual int HashSize
    488.         {
    489.             get
    490.             {
    491.                 return HashSizeValue;
    492.             }
    493.         }
    494.         protected int HashSizeValue = 128;
    495.  
    496.         public virtual int InputBlockSize
    497.         {
    498.             get
    499.             {
    500.                 return 1;
    501.             }
    502.         }
    503.         public virtual int OutputBlockSize
    504.         {
    505.             get
    506.             {
    507.                 return 1;
    508.             }
    509.         }
    510.  
    511.         public void Clear()
    512.         {
    513.             Dispose(true);
    514.         }
    515.  
    516.         public byte[] ComputeHash(byte[] buffer)
    517.         {
    518.             return ComputeHash(buffer, 0, buffer.Length);
    519.         }
    520.         public byte[] ComputeHash(byte[] buffer, int offset, int count)
    521.         {
    522.             Initialize();
    523.             HashCore(buffer, offset, count);
    524.             HashValue = HashFinal();
    525.             return (byte[])HashValue.Clone();
    526.         }
    527.  
    528.         public byte[] ComputeHash(Stream inputStream)
    529.         {
    530.             Initialize();
    531.             int count;
    532.             byte[] buffer = new byte[4096];
    533.             while (0 < (count = inputStream.Read(buffer, 0, 4096)))
    534.             {
    535.                 HashCore(buffer, 0, count);
    536.             }
    537.             HashValue = HashFinal();
    538.             return (byte[])HashValue.Clone();
    539.         }
    540.  
    541.         public int TransformBlock(
    542.             byte[] inputBuffer,
    543.             int inputOffset,
    544.             int inputCount,
    545.             byte[] outputBuffer,
    546.             int outputOffset
    547.             )
    548.         {
    549.             if (inputBuffer == null)
    550.             {
    551.                 throw new ArgumentNullException("inputBuffer");
    552.             }
    553.             if (inputOffset < 0)
    554.             {
    555.                 throw new ArgumentOutOfRangeException("inputOffset");
    556.             }
    557.             if ((inputCount < 0) || (inputCount > inputBuffer.Length))
    558.             {
    559.                 throw new ArgumentException("inputCount");
    560.             }
    561.             if ((inputBuffer.Length - inputCount) < inputOffset)
    562.             {
    563.                 throw new ArgumentOutOfRangeException("inputOffset");
    564.             }
    565.             if (this.State == 0)
    566.             {
    567.                 Initialize();
    568.                 this.State = 1;
    569.             }
    570.  
    571.             HashCore(inputBuffer, inputOffset, inputCount);
    572.             if ((inputBuffer != outputBuffer) || (inputOffset != outputOffset))
    573.             {
    574.                 Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount);
    575.             }
    576.             return inputCount;
    577.         }
    578.         public byte[] TransformFinalBlock(
    579.             byte[] inputBuffer,
    580.             int inputOffset,
    581.             int inputCount
    582.             )
    583.         {
    584.             if (inputBuffer == null)
    585.             {
    586.                 throw new ArgumentNullException("inputBuffer");
    587.             }
    588.             if (inputOffset < 0)
    589.             {
    590.                 throw new ArgumentOutOfRangeException("inputOffset");
    591.             }
    592.             if ((inputCount < 0) || (inputCount > inputBuffer.Length))
    593.             {
    594.                 throw new ArgumentException("inputCount");
    595.             }
    596.             if ((inputBuffer.Length - inputCount) < inputOffset)
    597.             {
    598.                 throw new ArgumentOutOfRangeException("inputOffset");
    599.             }
    600.             if (this.State == 0)
    601.             {
    602.                 Initialize();
    603.             }
    604.             HashCore(inputBuffer, inputOffset, inputCount);
    605.             HashValue = HashFinal();
    606.             byte[] buffer = new byte[inputCount];
    607.             Buffer.BlockCopy(inputBuffer, inputOffset, buffer, 0, inputCount);
    608.             this.State = 0;
    609.             return buffer;
    610.         }
    611.         #endregion
    612.  
    613.         protected virtual void Dispose(bool disposing)
    614.         {
    615.             if (!disposing)
    616.                 Initialize();
    617.         }
    618.         public void Dispose()
    619.         {
    620.             Dispose(true);
    621.         }
    622.     }
    623. }
    624.  
    Cheers!
     
    Last edited: Jan 8, 2014
  5. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    If I get time to work on it in the next day or two I'll restructure Jeff's code so usage and implementation matches that of the built in provider. Then I'll wrap this all up in a tidy #if UNITY_WP8 directives so it will be as simple as dropping in the script and the same code will then work across all platforms without any fiddling.
     
  6. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,875
  7. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    @Thomas - I tested all three implementations with a long string and they all produced the same results. Thanks for pointing out the Crypto stuff, I was unaware of it.

    That being said, I think the modified Jeff Wilcox implementation might still be handy. The UnityEngine.Windows namespace isn't even available unless you're targeting Windows Store Apps or Windows Phone so you'd have to wrap your code in:

    #if UNITY_WINRT

    #endif

    By modifying Jeff Willcox's implementation to match MD5CryptoServiceProvider's signature a user could write a piece of MD5 hash code that would target both iOS and Windows Store Apps for example and not have to use any directives themselves because implementation and usage would be the same.