Sha1/HmacSha1/OneTimePassword and Sha256 hashing

I have ported two libraries from open source projects that I needed for porting an app from Xamarin to Fuse. I confirmed that the results in Fuse for some basic tests were the same as what I was seeing in my Xamarin app (ie… so the Sha256 hasing results match)

Examples of usage:

    public  class PasswordTools
    {

        public static string HashPassword (string password, string salt)
        {

            string salt_start = "app_salt_here" + salt.ToLower ();

            string salt_end = salt.ToLower()+ "app_pepper_here";


            // merge password and salt together
            string sHashWithSalt = salt_start + password + salt_end;
            // convert this merged value to a byte array
            byte[] saltedHashBytes = Utf8.GetBytes (sHashWithSalt);
            // use hash algorithm to compute the hash
            var algorithm = new SHA256 ();
            byte[] result = algorithm.ComputeHash (saltedHashBytes);
            // return the has as a base 64 encoded string
            return Base64.GetString (result).Replace ('/', '%');
        }

        public string GetOneTimePassword(int instance, string challenge)
        {
            OneTimePassword otp = new OneTimePassword(instance, "SOME SALT" + challenge + "SOME PEPPER");
            return otp.GetCurrent();

        }
    }

using Uno;
using Uno.Collections;
using Uno.IO;
using Uno.Text;
using Fuse;

namespace Community.Cryptography

{

    /CODE ORIGINALLY 
    FROM CODEPROJECT ARTICLE : http://www.codeproject.com/Articles/592275/OTP-One-Time-Password-Demystified
    UNDER LICENSE: http://www.codeproject.com/info/cpol10.aspx

    PORTED TO UNO WITH BASIC REFACTORING: Sean McKay, 2015
    /
        struct SHA_TRANSF
        {
            public  long A;
            public  long B;
            public  long C;
            public  long D;
            public  long E;

            public  long T;
            public  long[] W;
            public  int idxW;

            public new string ToString()
            {
                return "::" + A + "::" + B + "::" + C + "::" + D + "::" + E + "::" + T + "::" + idxW;
            }

        }

    public class mem
    {
        public static void _set(ref byte[] data, int first, byte val, int count)
        {
            for (int nI = 0; nI < count; nI++)
            {
                data[nI + first] = val;
            }
        }

        public static void _cpy(ref byte[] dest, int dest_first, byte[] srce, int srce_first, int count)
        {
            for (int nI = 0; nI < count; nI++)
            {
                dest[dest_first + nI] = srce[nI + srce_first];
            }
        }
    }

   /// <summary>
    /// This class implements the SHA1 hash algorithm
    /// </summary>
    public class Sha1
    {

        #region SHA f()-functions

        static long andOrNot(long x, long y, long z)
        {
            return ((x & y) | (~x & z));
        }

        static long xorEm(long x, long y, long z)
        {
            return (x ^ y ^ z);
        }

        static long andOr(long x, long y, long z)
        {
            return ((x & y) | (x & z) | (y & z));
        }

        static long xorEm2(long x, long y, long z)
        {
            return (x ^ y ^ z);
        }

        static long f(long n, long x, long y, long z)
        {
            switch (n)
            {
                case 1:
                    {
                        return andOrNot(x, y, z);
                    }

                case 2:
                    {
                        return xorEm(x, y, z);
                    }

                case 3:
                    {
                        return andOr(x, y, z);
                    }

                case 4:
                    {
                        return xorEm2(x, y, z);
                    }

                default:
                    throw new Exception("Wrong parameter");
            }
        }

        #endregion

        #region SHA constants

        static uint[] CONST = new uint[4]
        {
            0x5a827999, 
            0x6ed9eba1,
            0x8f1bbcdc,
            0xca62c1d6
        };

        #endregion

        static long Mask32Bit(long x)
        {
            unchecked
            {
                return (x & 0xFFFFFFFF);
            }
        }

        static long Rotate32Bit(long x, int n)
        {
            return Mask32Bit(((x << n) | (x >> (32 - n))));
        }

        #region Unraveled Rotation functions

        static void ModifyTB(int n, ref SHA_TRANSF t)
        {
            t.T = Mask32Bit(Rotate32Bit(t.A, 5) + f(n, t.B, t.C, t.D) + t.E + t.W[t.idxW++] + CONST[n - 1]); 
            t.B = Rotate32Bit(t.B, 30);
        }

        static void ModifyEA(int n, ref SHA_TRANSF t)
        {
            t.E = Mask32Bit(Rotate32Bit(t.T, 5) + f(n, t.A, t.B, t.C) + t.D + t.W[t.idxW++] + CONST[n - 1]); 
            t.A = Rotate32Bit(t.A, 30);
        }

        static void ModifyDT(int n, ref SHA_TRANSF t)
        {
            t.D = Mask32Bit(Rotate32Bit(t.E, 5) + f(n, t.T, t.A, t.B) + t.C + t.W[t.idxW++] + CONST[n - 1]); 
            t.T = Rotate32Bit(t.T, 30);
        }

        static void ModifyCE(int n, ref SHA_TRANSF t)
        {
            t.C = Mask32Bit(Rotate32Bit(t.D, 5) + f(n, t.E, t.T, t.A) + t.B + t.W[t.idxW++] + CONST[n - 1]); 
            t.E = Rotate32Bit(t.E, 30);
        }

        static void ModifyBD(int n, ref SHA_TRANSF t)
        {
            t.B = Mask32Bit(Rotate32Bit(t.C, 5) + f(n, t.D, t.E, t.T) + t.A + t.W[t.idxW++] + CONST[n - 1]); 
            t.D = Rotate32Bit(t.D, 30);
        }

        static void ModifyAT(int n, ref SHA_TRANSF t)
        {
            t.A = Mask32Bit(Rotate32Bit(t.B, 5) + f(n, t.C, t.D, t.E) + t.T + t.W[t.idxW++] + CONST[n - 1]); 
            t.C = Rotate32Bit(t.C, 30);
        }

        #endregion

        private void sha_transform()
        {
            int 
            i,
            idx = 0;

            SHA_TRANSF tf = new SHA_TRANSF();
            tf.W = new long[80];

            / SHA_BYTE_ORDER == 1234 /
            for (i = 0; i < 16; ++i)
            {
                tf.T = ((long)data[idx++]) & 0x000000ff;
                tf.T += (((long)data[idx++]) << 8) & 0x0000ff00;
                tf.T += (((long)data[idx++]) << 16) & 0x00ff0000;
                tf.T += (((long)data[idx++]) << 24) & 0xff000000;

                tf.W[i] = ((tf.T << 24) & 0xff000000) | ((tf.T << 8) & 0x00ff0000) |
                ((tf.T >> 8) & 0x0000ff00) | ((tf.T >> 24) & 0x000000ff);
            }

            for (i = 16; i < 80; ++i)
            {
                tf.W[i] = tf.W[i - 3] ^ tf.W[i - 8] ^ tf.W[i - 14] ^ tf.W[i - 16];
                tf.W[i] = Rotate32Bit(tf.W[i], 1);
            }

            tf.A = digest[0];
            tf.B = digest[1];
            tf.C = digest[2];
            tf.D = digest[3];
            tf.E = digest[4];
            tf.idxW = 0;

            // UNRAVEL
            //debug_log "::BEFORE::MODIFYTB::" + tf.ToString();
            ModifyTB(1, ref tf);
            //debug_log "::AFTER::MODIFYTB::" + tf.ToString();            ModifyEA(1, ref tf);
            ModifyDT(1, ref tf);
            ModifyCE(1, ref tf);
            ModifyBD(1, ref tf);
            ModifyAT(1, ref tf);
            ModifyTB(1, ref tf);
            ModifyEA(1, ref tf);
            ModifyDT(1, ref tf);
            ModifyCE(1, ref tf);
            ModifyBD(1, ref tf);
            ModifyAT(1, ref tf);
            ModifyTB(1, ref tf);
            ModifyEA(1, ref tf);
            ModifyDT(1, ref tf);
            ModifyCE(1, ref tf);
            ModifyBD(1, ref tf);
            ModifyAT(1, ref tf);
            ModifyTB(1, ref tf);
            ModifyEA(1, ref tf);

            ModifyDT(2, ref tf);
            ModifyCE(2, ref tf);
            ModifyBD(2, ref tf);
            ModifyAT(2, ref tf);
            ModifyTB(2, ref tf);
            ModifyEA(2, ref tf);
            ModifyDT(2, ref tf);
            ModifyCE(2, ref tf);
            ModifyBD(2, ref tf);
            ModifyAT(2, ref tf);
            ModifyTB(2, ref tf);
            ModifyEA(2, ref tf);
            ModifyDT(2, ref tf);
            ModifyCE(2, ref tf);
            ModifyBD(2, ref tf);
            ModifyAT(2, ref tf);
            ModifyTB(2, ref tf);
            ModifyEA(2, ref tf);
            ModifyDT(2, ref tf);
            ModifyCE(2, ref tf);

            ModifyBD(3, ref tf);
            ModifyAT(3, ref tf);
            ModifyTB(3, ref tf);
            ModifyEA(3, ref tf);
            ModifyDT(3, ref tf);
            ModifyCE(3, ref tf);
            ModifyBD(3, ref tf);
            ModifyAT(3, ref tf);
            ModifyTB(3, ref tf);
            ModifyEA(3, ref tf);
            ModifyDT(3, ref tf);
            ModifyCE(3, ref tf);
            ModifyBD(3, ref tf);
            ModifyAT(3, ref tf);
            ModifyTB(3, ref tf);
            ModifyEA(3, ref tf);
            ModifyDT(3, ref tf);
            ModifyCE(3, ref tf);
            ModifyBD(3, ref tf);
            ModifyAT(3, ref tf);

            ModifyTB(4, ref tf);
            ModifyEA(4, ref tf);
            ModifyDT(4, ref tf);
            ModifyCE(4, ref tf);
            ModifyBD(4, ref tf);
            ModifyAT(4, ref tf);
            ModifyTB(4, ref tf);
            ModifyEA(4, ref tf);
            ModifyDT(4, ref tf);
            ModifyCE(4, ref tf);
            ModifyBD(4, ref tf);
            ModifyAT(4, ref tf);
            ModifyTB(4, ref tf);
            ModifyEA(4, ref tf);
            ModifyDT(4, ref tf);
            ModifyCE(4, ref tf);
            ModifyBD(4, ref tf);
            ModifyAT(4, ref tf);
            ModifyTB(4, ref tf);
            ModifyEA(4, ref tf);

            HmacSha1.debugBytes(digest, "sha_transform::PRESET");
            //debug_log "--> TF HAS: " + tf.ToString();
            digest[0] = Mask32Bit(digest[0] + tf.E);
            digest[1] = Mask32Bit(digest[1] + tf.T);
            digest[2] = Mask32Bit(digest[2] + tf.A);
            digest[3] = Mask32Bit(digest[3] + tf.B);
            digest[4] = Mask32Bit(digest[4] + tf.C);
            HmacSha1.debugBytes(digest, "sha_transform::POSTSET");
        }

        public const ushort LITTLE_INDIAN = 1234;
        public const ushort BYTE_ORDER = LITTLE_INDIAN;
        public const int SHA_BLOCKSIZE = 64;
        public const int SHA_DIGESTSIZE = 20;

        #region Replaces the SHA_INFO structure

        private long[] digest;
        / message digest /
        private long count_lo, count_hi;
        / 64-bit bit count /
        private byte[] data;
        / SHA data buffer /
        private int local;
        / unprocessed amount in data /

        #endregion

        public Sha1()
        {
        }

        /// <summary>
        /// Initialize the SHA digest
        /// </summary>
        public void Init()
        {
            data = new byte[SHA_BLOCKSIZE];
            digest = new long[5];

            digest[0] = 1732584193L;
            digest[1] = 4023233417L;
            digest[2] = 2562383102L;
            digest[3] = 271733878L;
            digest[4] = 3285377520L;
            //LOG: ::INIT::DIGEST::1732584193:-271733879:-1732584194:271733878:-1009589776:
            HmacSha1.debugBytes(digest, "::INIT::DIGEST");
            count_lo = 0L;
            count_hi = 0L;
            local = 0;
        }

        /// <summary>
        /// Update the SHA digest
        /// </summary>
        /// <param name="buffer">Data to be processed</param>
        public void Update(byte[]buffer)
        {
            int i;
            long clo;
            int count = buffer.Length;
            int buf_idx = 0;

            clo = Mask32Bit(count_lo + ((long)count << 3));
            if (clo < count_lo)
            {
                ++count_hi;
            }
            count_lo = clo;
            count_hi += (long)count >> 29;
            if (local != 0)
            {
                i = SHA_BLOCKSIZE - local;
                if (i > count)
                {
                    i = count;
                }

                //mem._cpy(ref data, local, buffer, buf_idx, i);
                for (int nI = 0; nI < i; nI++)
                {
                    data[local + nI] = buffer[nI + buf_idx];
                }
                count -= i;
                buf_idx += i;

                local += i;
                if (local == SHA_BLOCKSIZE)
                {
                    sha_transform();
                }
                else
                {
                    return;
                }
            }
            while (count >= SHA_BLOCKSIZE)
            {
                //mem._cpy(ref data, 0, buffer, buf_idx, SHA_BLOCKSIZE);
                for (int nI = 0; nI < SHA_BLOCKSIZE; nI++)
                {
                    data[nI] = buffer[nI + buf_idx];
                }
                buf_idx += SHA_BLOCKSIZE;
                count -= SHA_BLOCKSIZE;
                sha_transform();
            }

            //mem._cpy(ref data, 0, buffer, buf_idx, count);
            for (int nI = 0; nI < count; nI++)
            {
                data[nI] = buffer[nI + buf_idx];
            }
            local = count;
        }

        /// <summary>
        /// Finish computing the SHA digest
        /// </summary>
        /// <param name="result"></param>
        public byte[] Final()
        {
            byte[] result = new byte[SHA_DIGESTSIZE];

            int count;
            long lo_bit_count, hi_bit_count;

            lo_bit_count = count_lo;
            hi_bit_count = count_hi;
            count = (int)((lo_bit_count >> 3) & 0x3f);
            data[count++] = 0x80;
            if (count > SHA_BLOCKSIZE - 8)
            {
                //mem._set(ref data, count, 0, SHA_BLOCKSIZE - count);
                for (int nI = 0; nI < SHA_BLOCKSIZE - count; nI++)
                {
                    data[nI + count] = 0;
                }
                sha_transform();
                //mem._set(ref data, 0, 0, SHA_BLOCKSIZE - 8);
                for (int nI = 0; nI < SHA_BLOCKSIZE - 8; nI++)
                {
                    data[nI ] = 0;
                }
            }
            else
            {
                //mem._set(ref data, count, 0, SHA_BLOCKSIZE - 8 - count);
                for (int nI = 0; nI < SHA_BLOCKSIZE - 8 - count; nI++)
                {
                    data[nI + count] = 0;
                }
            }

            data[56] = (byte)((hi_bit_count >> 24) & 0xff);
            data[57] = (byte)((hi_bit_count >> 16) & 0xff);
            data[58] = (byte)((hi_bit_count >> 8) & 0xff);
            data[59] = (byte)((hi_bit_count >> 0) & 0xff);
            data[60] = (byte)((lo_bit_count >> 24) & 0xff);
            data[61] = (byte)((lo_bit_count >> 16) & 0xff);
            data[62] = (byte)((lo_bit_count >> 8) & 0xff);
            data[63] = (byte)((lo_bit_count >> 0) & 0xff);
HmacSha1.debugBytes(digest, "BEFORE FINAL::MEM::TRANSFORM::DIGEST");

            sha_transform();
HmacSha1.debugBytes(digest, "After FINAL::MEM::TRANSFORM::DIGEST");
            result[0] = (byte)((digest[0] >> 24) & 0xff);
            result[1] = (byte)((digest[0] >> 16) & 0xff);
            result[2] = (byte)((digest[0] >> 8) & 0xff);
            result[3] = (byte)((digest[0]) & 0xff);
            result[4] = (byte)((digest[1] >> 24) & 0xff);
            result[5] = (byte)((digest[1] >> 16) & 0xff);
            result[6] = (byte)((digest[1] >> 8) & 0xff);
            result[7] = (byte)((digest[1]) & 0xff);
            result[8] = (byte)((digest[2] >> 24) & 0xff);
            result[9] = (byte)((digest[2] >> 16) & 0xff);
            result[10] = (byte)((digest[2] >> 8) & 0xff);
            result[11] = (byte)((digest[2]) & 0xff);
            result[12] = (byte)((digest[3] >> 24) & 0xff);
            result[13] = (byte)((digest[3] >> 16) & 0xff);
            result[14] = (byte)((digest[3] >> 8) & 0xff);
            result[15] = (byte)((digest[3]) & 0xff);
            result[16] = (byte)((digest[4] >> 24) & 0xff);
            result[17] = (byte)((digest[4] >> 16) & 0xff);
            result[18] = (byte)((digest[4] >> 8) & 0xff);
            result[19] = (byte)((digest[4]) & 0xff);
HmacSha1.debugBytes(result, "After FINAL::RESULT");

            return result;
        }

        public byte[] Final_dss_padding()
        {
            byte[] result = new byte[SHA_DIGESTSIZE];

            int count;
            long lo_bit_count, hi_bit_count;

            lo_bit_count = count_lo;
            hi_bit_count = count_hi;
            count = (int)((lo_bit_count >> 3) & 0x3f);
            if (count > SHA_BLOCKSIZE)
            {
                //mem._set(ref data, count, 0, SHA_BLOCKSIZE - count);
                for (int nI = 0; nI < SHA_BLOCKSIZE-count; nI++)
                {
                    data[nI + count] = 0;
                }
               sha_transform();
                //mem._set(ref data, 0, 0, SHA_BLOCKSIZE);
                for (int nI = 0; nI < SHA_BLOCKSIZE; nI++)
                {
                    data[nI] = 0;
                }
            }
            else
            {
                //mem._set(ref data, count, 0, SHA_BLOCKSIZE - count);
                for (int nI = 0; nI < SHA_BLOCKSIZE -count; nI++)
                {
                    data[nI + count] = 0;
                }
            }

            sha_transform();
            result[0] = (byte)((digest[0] >> 24) & 0xff);
            result[1] = (byte)((digest[0] >> 16) & 0xff);
            result[2] = (byte)((digest[0] >> 8) & 0xff);
            result[3] = (byte)((digest[0]) & 0xff);
            result[4] = (byte)((digest[1] >> 24) & 0xff);
            result[5] = (byte)((digest[1] >> 16) & 0xff);
            result[6] = (byte)((digest[1] >> 8) & 0xff);
            result[7] = (byte)((digest[1]) & 0xff);
            result[8] = (byte)((digest[2] >> 24) & 0xff);
            result[9] = (byte)((digest[2] >> 16) & 0xff);
            result[10] = (byte)((digest[2] >> 8) & 0xff);
            result[11] = (byte)((digest[2]) & 0xff);
            result[12] = (byte)((digest[3] >> 24) & 0xff);
            result[13] = (byte)((digest[3] >> 16) & 0xff);
            result[14] = (byte)((digest[3] >> 8) & 0xff);
            result[15] = (byte)((digest[3]) & 0xff);
            result[16] = (byte)((digest[4] >> 24) & 0xff);
            result[17] = (byte)((digest[4] >> 16) & 0xff);
            result[18] = (byte)((digest[4] >> 8) & 0xff);
            result[19] = (byte)((digest[4]) & 0xff);

            return result;
        }

        /// <summary>
        /// Returns the version
        /// </summary>
        /// <returns></returns>
        public static string    version()
        {
            return "SHA-1";
        }
    }


    /// <summary>
    /// This class provides the HMAC SHA1 algorithm
    /// </summary>
    public class HmacSha1
    {
        private const int HMAC_SHA1_PAD_SIZE = 64;
        private const int HMAC_SHA1_DIGEST_SIZE = 20;
        private const int HMAC_SHA1_128_DIGEST_SIZE = 16;

        private Sha1 sha_ctx;
        private byte[] key_ctx;
        private int key_len_ctx;
        private byte[] temp_key_ctx = new byte[Sha1.SHA_DIGESTSIZE];
        / in case key exceeds 64 bytes  /

        public static byte[] GetHmacSha1Bytes(byte[] key, byte[] text)
        {

            HmacSha1 sha = new HmacSha1(key, text);
            return sha.Final();
        }

        private HmacSha1(byte[] key, byte[] text)
        {
            byte[] k_ipad = new byte[HMAC_SHA1_PAD_SIZE];
            int i, key_len = key.Length;

            sha_ctx = new Sha1();

            / if key is longer than 64 bytes reset it to key=SHA-1(key) /
            if (key_len > HMAC_SHA1_PAD_SIZE)
            {
                sha_ctx.Init();
                sha_ctx.Update(key);
                temp_key_ctx = sha_ctx.Final();

                key = temp_key_ctx;
                key_len = HMAC_SHA1_DIGEST_SIZE;
            }

            /
                the HMAC_SHA1 transform looks like:
               
                SHA1(K XOR opad, SHA1(K XOR ipad, text))
               
                where K is an n byte keyUpdate
                ipad is the byte 0x36 repeated 64 times
                opad is the byte 0x5c repeated 64 times
                and text is the data being protected
               /

            / start out by storing key in pads /
            mem._set(ref k_ipad, 0, 0, k_ipad.Length);
            mem._cpy(ref k_ipad, 0, key, 0, key_len);

            byte xorKeyInit = (byte) 0x36;
            // XOR key with ipad and opad values /
            /
            for (i = 0; i < k_ipad.Length; i++)
            {
                k_ipad[i] ^= xorKeyInit;
            }
            /
            for (i = 0; i < k_ipad.Length; i++)
            {
                k_ipad[i] = (byte)(k_ipad[i] ^ xorKeyInit);
            }

            /
                 perform inner SHA1
                /
            sha_ctx.Init();            / init context for 1st pass /
            / start with inner pad      /
            sha_ctx.Update(k_ipad); 

            / Stash the key and it's length into the context. /
            key_ctx = key;
            key_len_ctx = key_len;

            sha_ctx.Update(text);
        }

        private byte[] Final()
        {
            byte[] digest;

            / outer padding -  key XORd with opad /
            byte[] k_opad = new byte[HMAC_SHA1_PAD_SIZE];            int i;
            byte xorKeyInit = (byte)0x5c;

            mem._set(ref k_opad, 0, 0, k_opad.Length);
            mem._cpy(ref k_opad, 0, key_ctx, 0, key_len_ctx);

            / XOR key with ipad and opad values /
            /
            for (i = 0; i < k_opad.Length; i++)
            {
                k_opad[i] ^= xorKeyInit;
            }
              /            for (i = 0; i < k_opad.Length; i++)
            {
                k_opad[i] =(byte) (k_opad[i] ^ xorKeyInit);
            }

            digest = sha_ctx.Final();         / finish up 1st pass /
   debugBytes(digest, "After DIGEST");            /
                perform outer SHA1
               /
            sha_ctx.Init();                  / init context for 2nd pass /
            / start with outer pad      /
            sha_ctx.Update(k_opad);
            / then results of 1st hash  /
            sha_ctx.Update(digest);            digest = sha_ctx.Final();         / finish up 2nd pass        /
                  debugBytes(digest, "After FINAL DIGEST");
            return digest;
        }

        public static void debugBytes<T>(T[] toPrint, string msg)
        {
            /
                    StringBuilder sb = new StringBuilder();
            foreach(T b in toPrint)
            {
                sb.Append(b.ToString());
                sb.Append(":");
            }

            debug_log msg + "::" + sb.ToString();
            /
        }
    }


    public class OneTimePassword
    {
        public const int SECRETLENGTH = 20;
        private const string MSG_SECRETLENGTH = "Secret must be at least 20 bytes";
        private const string MSG_COUNTER_MINVALUE = "Counter min value is 1";

        private static int[] _checksumSkipTable = new int[] { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
        private byte[] _secretKey;
        private ulong _counter = 0x0000000000000001;


        public OneTimePassword()
        {
            _secretKey = getDefaultSecretKey();
        }

        public OneTimePassword(ulong counter = 1, string secret = null)
        {

            if (secret != null)
            {
                byte[] secretKey = Uno.Text.Utf8.GetBytes(secret);
                if (secretKey.Length < SECRETLENGTH)
                {
                    throw new Exception(MSG_SECRETLENGTH);
                }

                this._secretKey = secretKey;
            }
            else
                this._secretKey = getDefaultSecretKey();

            if (counter < 1)
            {
                throw new Exception(MSG_COUNTER_MINVALUE);
            }

            this._counter = counter;
        }

        public OneTimePassword(ulong counter = 1, byte[] secretKey = null)
        {
            if (secretKey != null)
            {
                if (secretKey.Length < SECRETLENGTH)
                {
                    throw new Exception(MSG_SECRETLENGTH);
                }

                this._secretKey = secretKey;
            }
            else
                this._secretKey = getDefaultSecretKey();

            if (counter < 1)
            {
                throw new Exception(MSG_COUNTER_MINVALUE);
            }

            this._counter = counter;
        }

        byte[] getDefaultSecretKey()
        {
            return new byte[]
            {
                0x30,
                0x31,
                0x32,
                0x33,
                0x34,
                0x35,
                0x36,
                0x37,
                0x38,
                0x39,
                0x3A,
                0x3B,
                0x3C,
                0x3D,
                0x3E,
                0x3F,
                0x40,
                0x41,
                0x42,
                0x43
            };
        }


        private static int getChecksum(int codeDigits)
        {
            int digitMillions = (codeDigits / 1000000) % 10;
            int digitHundredThousands = (codeDigits / 100000) % 10;
            int digitTenThousands = (codeDigits / 10000) % 10;
            int digitThousands = (codeDigits / 1000) % 10;
            int digitHundreds = (codeDigits / 100) % 10;
            int digitTens = (codeDigits / 10) % 10;
            int digitOnes = codeDigits % 10;
            return (10 -
            ((_checksumSkipTable[digitMillions] + digitHundredThousands +
            _checksumSkipTable[digitTenThousands] + digitThousands +
            _checksumSkipTable[digitHundreds] + digitTens +
            _checksumSkipTable[digitOnes]) % 10)) % 10;
        }

        /// <summary>
        /// Formats the OneTimePassword. This is the OneTimePassword algorithm.
        /// </summary>
        /// <param name="hmac">HMAC value</param>
        /// <returns>8 digits OTP</returns>
        private static string FormatOneTimePassword(byte[] hmac)
        {

            StringBuilder sb = new StringBuilder();
            foreach(byte b in hmac)
            {
                sb.Append(b.ToString());
                sb.Append(":");
            }

            //debug_log "HMAC::" + sb.ToString();
            int offset = hmac[19] & 0xf;
            int bin_code = (hmac[offset] & 0x7f) << 24
                           | (hmac[offset + 1] & 0xff) << 16
                           | (hmac[offset + 2] & 0xff) << 8
                           | (hmac[offset + 3] & 0xff);

            int Code_Digits = bin_code % 10000000;
            int csum = getChecksum(Code_Digits);
            int OTP = Code_Digits  10 + csum;
            //debug_log "FOTP::" + offset + "::" + bin_code + "::" + Code_Digits + "::" + csum + "::" + OTP + "::" + hmac.Length;

            return string.Format("{0:d08}", OTP);
        }

        public static byte[] ToByteArray(string otp)
        {
            byte[] baOTP = new byte[otp.Length];
            char[] arOTP = otp.ToCharArray();

            for (int nI = 0; nI < otp.Length; nI++)
            {
                baOTP[nI] = (byte)arOTP[nI];
            }

            return baOTP;
        }

        public byte[] CounterArray
        {
            get
            {
                return BitConverter.GetBytes(_counter);
            }

            set
            {
                _counter = BitConverter.ToUInt64(value, 0);
            }
        }

        /// <summary>
        /// Set the OTP secret
        /// </summary>
        /// <param name="secret"></param>
        public byte[] Secret
        {
            set
            {
                if (value.Length < SECRETLENGTH)
                {
                    throw new Exception(MSG_SECRETLENGTH);
                }

                _secretKey = value;
            }
        }

        /// <summary>
        /// Get the current one time password value
        /// </summary>
        /// <returns></returns>
        public string GetCurrent()
        {
            return FormatOneTimePassword(HmacSha1.GetHmacSha1Bytes(_secretKey, CounterArray)); 
        }

        /// <summary>
        /// Get the next OTP value
        /// </summary>
        /// <returns></returns>
        public string GetNextOneTimePassword()
        {
            // increment the counter
            ++_counter;

            return GetCurrent();
        }

        /// <summary>
        /// Get the counter value
        /// </summary>
        /// <returns></returns>
        public ulong Counter
        {
            get
            {
                return _counter;
            }

            set
            {
                _counter = value;
            }
        } //Counter
    } //OnteTimePassword


public class BitConverter
    {
        public static void Clear(byte[] arr, int index, int length)
        {
            for(int i = index; i < index+length; i++)
            {
                arr[i] = 0;
            }
        }

        public static string ToString(byte[] toConvert)
        {
            StringBuilder sb = new StringBuilder();
            foreach (byte b in toConvert)
            {
                sb.Append(b.ToString());
                sb.Append("-");
            }
            return sb.ToString().TrimEnd('-');

        }

        public static byte[] GetBytes(ulong val)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (BinaryWriter bw = new BinaryWriter(ms))
                {
                    bw.Write(val);
                    byte[] buff= ms.GetBuffer();
                    byte[] longSizedBuff = new byte[8];
                    Array.Copy(buff,longSizedBuff,8);
                    return longSizedBuff;
                }
            }
            return null;
        }

        public static ulong ToUInt64(byte[] bytes, int startingIndex)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                ms.Write(bytes,startingIndex, bytes.Length);
                using (BinaryReader br = new BinaryReader(ms))
                {
                    return br.ReadULong();
                }
            }
            return 0;
        }//ToUInt64
    }//BitConverter
}//Namespace
'''

'''
using Uno;
using Uno.Collections;
using Uno.Text;
using Fuse;

namespace Community.Cryptography
{

    //Sha256 algorithm from: http://hashlib.codeplex.com/
    //License: http://hashlib.codeplex.com/license CDDL
    class Converters
    {
        public static string ConvertBytesToHexString (byte[] a_in, bool a_group = true)
        {
            string hex = BitConverter.ToString (a_in).ToUpper ();

            if (a_group) {

                string[] ar = BitConverter.ToString (a_in).ToUpper ().Split (new char[] { '-' });

                hex = "";

                for (int i = 0; i < ar.Length / 4; i++) {
                    if (i != 0)
                        hex += "-";
                    hex += ar [i  4] + ar [i  4 + 1] + ar [i  4 + 2] + ar [i  4 + 3];
                }
            } else
                hex = hex.Replace ("-", "");

            return hex;
        }

        public static byte[] ConvertUIntsToBytesSwapOrder (uint[] a_in, int a_index = 0, int a_length = -1)
        {
            if (a_length == -1)
                a_length = a_in.Length;

            byte[] result = new byte[a_length  4];

            for (int j = 0; a_length > 0;  a_index++) {
                result [j++] = (byte)(a_in [a_index] >> 24);
                result [j++] = (byte)(a_in [a_index] >> 16);
                result [j++] = (byte)(a_in [a_index] >> 8);
                result [j++] = (byte)a_in [a_index];
                a_length--;
            }

            return result;
        }

        public static void ConvertBytesToUIntsSwapOrder (byte[] a_in, int a_index, int a_length, uint[] a_result, int a_index_out)
        {
            for (int i = a_index_out; a_length > 0; a_length -= 4) {
                a_result [i++] =
                    ((uint)a_in [a_index++] << 24) |
                ((uint)a_in [a_index++] << 16) |
                ((uint)a_in [a_index++] << 8) |
                a_in [a_index++];
            }
        }

        public static void ConvertULongToBytesSwapOrder (ulong a_in, byte[] a_out, int a_index)
        {
            //Debug.Assert (a_index + 8 <= a_out.Length);

            a_out [a_index++] = (byte)(a_in >> 56);
            a_out [a_index++] = (byte)(a_in >> 48);
            a_out [a_index++] = (byte)(a_in >> 40);
            a_out [a_index++] = (byte)(a_in >> 32);
            a_out [a_index++] = (byte)(a_in >> 24);
            a_out [a_index++] = (byte)(a_in >> 16);
            a_out [a_index++] = (byte)(a_in >> 8);
            a_out [a_index++] = (byte)a_in;
        }

    }

    internal class SHA256
    {
        protected readonly uint[] m_state = new uint[8];

        protected readonly HashBuffer m_buffer;
        protected ulong m_processed_bytes;
        private readonly int m_block_size;
        private readonly int m_hash_size;

        public static int BUFFER_SIZE = 64  1024;


        #region Consts

        private static readonly uint[] s_K = new uint[] {
            0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
            0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
            0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
            0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
            0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 
            0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
            0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
            0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
        };

        #endregion

        #region ctors

        public SHA256 ()
            : this (32)
        {
        }



        public SHA256 (int a_hash_size)
            : this (a_hash_size, 64)
        {
            Initialize ();
        }


        protected SHA256 (int a_hash_size, int a_block_size, int a_buffer_size = -1)
        {
            if (a_buffer_size == -1)
                a_buffer_size = a_block_size;

            m_buffer = new HashBuffer (a_buffer_size);
            m_processed_bytes = 0;
            //Debug.Assert ((a_block_size > 0) || (a_block_size == -1));
            //Debug.Assert (a_hash_size > 0);

            m_block_size = a_block_size;
            m_hash_size = a_hash_size;
        }

        #endregion

        public void Initialize ()
        {
            m_state [0] = 0x6a09e667;
            m_state [1] = 0xbb67ae85;
            m_state [2] = 0x3c6ef372;
            m_state [3] = 0xa54ff53a;
            m_state [4] = 0x510e527f;
            m_state [5] = 0x9b05688c;
            m_state [6] = 0x1f83d9ab;
            m_state [7] = 0x5be0cd19;

            m_buffer.Initialize ();
            m_processed_bytes = 0;
        }

        public byte[] ComputeHash (byte[] input)
        {
            Initialize ();
            TransformBytes (input, 0, input.Length);
            return TransformFinal ();
        }

        protected byte[] GetResult ()
        {
            return Converters.ConvertUIntsToBytesSwapOrder (m_state);
        }



        protected void Finish ()
        {
            ulong bits = m_processed_bytes  8;
            int padindex = (m_buffer.Pos < 56) ? (56 - m_buffer.Pos) : (120 - m_buffer.Pos);

            byte[] pad = new byte[padindex + 8];
            pad [0] = 0x80;

            Converters.ConvertULongToBytesSwapOrder (bits, pad, padindex);
            padindex += 8;

            TransformBytes (pad, 0, padindex);
        }

        protected void TransformBlock (byte[] a_data, int a_index)
        {
            uint[] data = new uint[64];
            Converters.ConvertBytesToUIntsSwapOrder (a_data, a_index, m_block_size, data, 0);

            uint A = m_state [0];
            uint B = m_state [1];
            uint C = m_state [2];
            uint D = m_state [3];
            uint E = m_state [4];
            uint F = m_state [5];
            uint G = m_state [6];
            uint H = m_state [7];

            for (int r = 16; r < 64; r++) {
                uint T = data [r - 2];
                uint T2 = data [r - 15];
                data [r] = (((T >> 17) | (T << 15)) ^ ((T >> 19) | (T << 13)) ^ (T >> 10)) + data [r - 7] +
                (((T2 >> 7) | (T2 << 25)) ^ ((T2 >> 18) | (T2 << 14)) ^ (T2 >> 3)) + data [r - 16];
            }

            for (int r = 0; r < 64; r++) {
                uint T = s_K [r] + data [r] + H + (((E >> 6) | (E << 26)) ^ ((E >> 11) | (E << 21)) ^ ((E >> 25) |
                         (E << 7))) + ((E & F) ^ (~E & G));
                uint T2 = (((A >> 2) | (A << 30)) ^ ((A >> 13) | (A << 19)) ^
                          ((A >> 22) | (A << 10))) + ((A & B) ^ (A & C) ^ (B & C));
                H = G;
                G = F;
                F = E;
                E = D + T;
                D = C;
                C = B;
                B = A;
                A = T + T2;
            }

            m_state [0] += A;
            m_state [1] += B;
            m_state [2] += C;
            m_state [3] += D;
            m_state [4] += E;
            m_state [5] += F;
            m_state [6] += G;
            m_state [7] += H;
        }


        public void TransformBytes (byte[] a_data, int a_index, int a_length)
        {
            //Debug.Assert (a_index >= 0);
            //Debug.Assert (a_length >= 0);
            //Debug.Assert (a_index + a_length <= a_data.Length);

            if (!m_buffer.IsEmpty) {
                if (m_buffer.Feed (a_data, ref a_index, ref a_length, ref m_processed_bytes))
                    TransformBlock (m_buffer.GetBytes (), 0);
            }

            while (a_length >= m_buffer.Length) {
                m_processed_bytes += (ulong)m_buffer.Length;
                TransformBlock (a_data, a_index);
                a_index += m_buffer.Length;
                a_length -= m_buffer.Length;
            }

            if (a_length > 0)
                m_buffer.Feed (a_data, ref a_index, ref a_length, ref m_processed_bytes);
        }


        public byte[] TransformFinal ()
        {
            Finish ();
            //Debug.Assert (m_buffer.IsEmpty);
            byte[] result = GetResult ();
            //Debug.Assert (result.Length == HashSize);
            Initialize ();
            return result;
        }

    }

    internal class HashBuffer
    {
        private byte[] m_data;
        private int m_pos;

        public HashBuffer (int a_length)
        {
            //Debug.Assert (a_length > 0);

            m_data = new byte[a_length];

            Initialize ();
        }

        public void Initialize ()
        {
            m_pos = 0;
        }

        public byte[] GetBytes ()
        {
            //Debug.Assert (IsFull);

            m_pos = 0;
            return m_data;
        }

        public byte[] GetBytesZeroPadded ()
        {
            BitConverter.Clear (m_data, m_pos, m_data.Length - m_pos); 
            m_pos = 0;
            return m_data;
        }

        public bool Feed (byte[] a_data, ref int a_start_index, ref int a_length, ref ulong a_processed_bytes)
        {
            //Debug.Assert (a_start_index >= 0);
            //Debug.Assert (a_length >= 0);
            //Debug.Assert (a_start_index + a_length <= a_data.Length);
            //Debug.Assert (!IsFull);

            if (a_data.Length == 0)
                return false;

            if (a_length == 0)
                return false;

            int length = m_data.Length - m_pos;
            if (length > a_length)
                length = a_length;

            Array.Copy (a_data, a_start_index, m_data, m_pos, length);

            m_pos += length;
            a_start_index += length;
            a_length -= length;
            a_processed_bytes += (ulong)length;

            return IsFull;
        }

        public bool Feed (byte[] a_data, int a_length)
        {
            //Debug.Assert (a_length >= 0);
            //Debug.Assert (a_length <= a_data.Length);
            //Debug.Assert (!IsFull);

            if (a_data.Length == 0)
                return false;

            if (a_length == 0)
                return false;

            int length = m_data.Length - m_pos;
            if (length > a_length)
                length = a_length;

            Array.Copy (a_data, 0, m_data, m_pos, length);

            m_pos += length;

            return IsFull;
        }

        public bool IsEmpty {
            get {
                return m_pos == 0;
            }
        }

        public int Pos {
            get {
                return m_pos;
            }
        }

        public int Length {
            get {
                return m_data.Length;
            }
        }

        public bool IsFull {
            get {
                return (m_pos == m_data.Length);
            }
        }

        public override string ToString ()
        {
            return String.Format ("HashBuffer, Legth: {0}, Pos: {1}, IsEmpty: {2}", Length, Pos, IsEmpty);
        }
    }
}

Cool!! Thanks for sharing :smiley:

Sweet ! Thanks for sharing, that will come in handy, why not make it something on GitHub so it’s easy to use, reuse and share ! :slight_smile:

Added Sha512 to the github project : https://github.com/torial/fuse-community

I think this part is not correct:

public static string ToString(byte[] toConvert)
{
    StringBuilder sb = new StringBuilder();
    foreach (byte b in toConvert)
    {
        sb.Append(b.ToString());
        sb.Append("-");
    }
    return sb.ToString().TrimEnd('-');
}

This takes exact hex string from bytes:

public static string ToString(byte[] toConvert) {
    var result = new StringBuilder();
    foreach (byte b in toConvert) {
        result.Append(String.Format("{0:X}", b));
        result.Append("-");
    }
    return result.ToString().TrimEnd('-');
}