Scrypt hashing is working now
authorCryptoManiac <balthazar@yandex.ru>
Wed, 19 Aug 2015 22:42:52 +0000 (01:42 +0300)
committerCryptoManiac <balthazar@yandex.ru>
Wed, 19 Aug 2015 22:42:52 +0000 (01:42 +0300)
Novacoin/Hash.cs
Novacoin/Novacoin.csproj
Novacoin/PBKDF2.cs [deleted file]
Novacoin/ScryptHash256.cs
NovacoinTest/Program.cs

index 731e795..1d23fec 100644 (file)
@@ -1,9 +1,6 @@
-\feffusing System;
-using System.Security.Cryptography;
+\feffusing System.Security.Cryptography;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Novacoin
 {
index 2e35588..2c5e1b2 100644 (file)
@@ -18,6 +18,7 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <ConsolePause>false</ConsolePause>
+    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>full</DebugType>
@@ -45,7 +46,6 @@
     <Compile Include="CScriptID.cs" />
     <Compile Include="Hash.cs" />
     <Compile Include="Interop.cs" />
-    <Compile Include="PBKDF2.cs" />
     <Compile Include="ScryptHash256.cs" />
     <Compile Include="VarInt.cs" />
     <Compile Include="VarStr.cs" />
diff --git a/Novacoin/PBKDF2.cs b/Novacoin/PBKDF2.cs
deleted file mode 100644 (file)
index 18155d4..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-\feff//Copyright (c) 2012 Josip Medved <jmedved@jmedved.com>
-
-//2012-04-12: Initial version.
-
-
-using System;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace Medo.Security.Cryptography
-{
-
-    /// <summary>
-    /// Generic PBKDF2 implementation.
-    /// </summary>
-    /// <example>This sample shows how to initialize class with SHA-256 HMAC.
-    /// <code>
-    /// using (var hmac = new HMACSHA256()) {
-    ///     var df = new Pbkdf2(hmac, "password", "salt");
-    ///     var bytes = df.GetBytes();
-    /// }
-    /// </code>
-    /// </example>
-    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pbkdf", Justification = "Spelling is correct.")]
-    public class Pbkdf2
-    {
-
-        /// <summary>
-        /// Creates new instance.
-        /// </summary>
-        /// <param name="algorithm">HMAC algorithm to use.</param>
-        /// <param name="password">The password used to derive the key.</param>
-        /// <param name="salt">The key salt used to derive the key.</param>
-        /// <param name="iterations">The number of iterations for the operation.</param>
-        /// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
-        public Pbkdf2(HMAC algorithm, Byte[] password, Byte[] salt, Int32 iterations)
-        {
-            if (algorithm == null) { throw new ArgumentNullException("algorithm", "Algorithm cannot be null."); }
-            if (salt == null) { throw new ArgumentNullException("salt", "Salt cannot be null."); }
-            if (password == null) { throw new ArgumentNullException("password", "Password cannot be null."); }
-            this.Algorithm = algorithm;
-            this.Algorithm.Key = password;
-            this.Salt = salt;
-            this.IterationCount = iterations;
-            this.BlockSize = this.Algorithm.HashSize / 8;
-            this.BufferBytes = new byte[this.BlockSize];
-        }
-
-        /// <summary>
-        /// Creates new instance.
-        /// </summary>
-        /// <param name="algorithm">HMAC algorithm to use.</param>
-        /// <param name="password">The password used to derive the key.</param>
-        /// <param name="salt">The key salt used to derive the key.</param>
-        /// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
-        public Pbkdf2(HMAC algorithm, Byte[] password, Byte[] salt)
-            : this(algorithm, password, salt, 1000)
-        {
-        }
-
-        /// <summary>
-        /// Creates new instance.
-        /// </summary>
-        /// <param name="algorithm">HMAC algorithm to use.</param>
-        /// <param name="password">The password used to derive the key.</param>
-        /// <param name="salt">The key salt used to derive the key.</param>
-        /// <param name="iterations">The number of iterations for the operation.</param>
-        /// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
-        public Pbkdf2(HMAC algorithm, String password, String salt, Int32 iterations) :
-            this(algorithm, UTF8Encoding.UTF8.GetBytes(password), UTF8Encoding.UTF8.GetBytes(salt), iterations)
-        {
-        }
-
-        /// <summary>
-        /// Creates new instance.
-        /// </summary>
-        /// <param name="algorithm">HMAC algorithm to use.</param>
-        /// <param name="password">The password used to derive the key.</param>
-        /// <param name="salt">The key salt used to derive the key.</param>
-        /// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
-        public Pbkdf2(HMAC algorithm, String password, String salt) :
-            this(algorithm, password, salt, 1000)
-        {
-        }
-
-
-        private readonly int BlockSize;
-        private uint BlockIndex = 1;
-
-        private byte[] BufferBytes;
-        private int BufferStartIndex = 0;
-        private int BufferEndIndex = 0;
-
-
-        /// <summary>
-        /// Gets algorithm used for generating key.
-        /// </summary>
-        public HMAC Algorithm { get; private set; }
-
-        /// <summary>
-        /// Gets salt bytes.
-        /// </summary>
-        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Byte array is proper return value in this case.")]
-        public Byte[] Salt { get; private set; }
-
-        /// <summary>
-        /// Gets iteration count.
-        /// </summary>
-        public Int32 IterationCount { get; private set; }
-
-
-        /// <summary>
-        /// Returns a pseudo-random key from a password, salt and iteration count.
-        /// </summary>
-        /// <param name="count">Number of bytes to return.</param>
-        /// <returns>Byte array.</returns>
-        public Byte[] GetBytes(int count)
-        {
-            byte[] result = new byte[count];
-            int resultOffset = 0;
-            int bufferCount = this.BufferEndIndex - this.BufferStartIndex;
-
-            if (bufferCount > 0)
-            { //if there is some data in buffer
-                if (count < bufferCount)
-                { //if there is enough data in buffer
-                    Buffer.BlockCopy(this.BufferBytes, this.BufferStartIndex, result, 0, count);
-                    this.BufferStartIndex += count;
-                    return result;
-                }
-                Buffer.BlockCopy(this.BufferBytes, this.BufferStartIndex, result, 0, bufferCount);
-                this.BufferStartIndex = this.BufferEndIndex = 0;
-                resultOffset += bufferCount;
-            }
-
-            while (resultOffset < count)
-            {
-                int needCount = count - resultOffset;
-                this.BufferBytes = this.Func();
-                if (needCount > this.BlockSize)
-                { //we one (or more) additional passes
-                    Buffer.BlockCopy(this.BufferBytes, 0, result, resultOffset, this.BlockSize);
-                    resultOffset += this.BlockSize;
-                }
-                else
-                {
-                    Buffer.BlockCopy(this.BufferBytes, 0, result, resultOffset, needCount);
-                    this.BufferStartIndex = needCount;
-                    this.BufferEndIndex = this.BlockSize;
-                    return result;
-                }
-            }
-            return result;
-        }
-
-
-        private byte[] Func()
-        {
-            var hash1Input = new byte[this.Salt.Length + 4];
-            Buffer.BlockCopy(this.Salt, 0, hash1Input, 0, this.Salt.Length);
-            Buffer.BlockCopy(GetBytesFromInt(this.BlockIndex), 0, hash1Input, this.Salt.Length, 4);
-            var hash1 = this.Algorithm.ComputeHash(hash1Input);
-
-            byte[] finalHash = hash1;
-            for (int i = 2; i <= this.IterationCount; i++)
-            {
-                hash1 = this.Algorithm.ComputeHash(hash1, 0, hash1.Length);
-                for (int j = 0; j < this.BlockSize; j++)
-                {
-                    finalHash[j] = (byte)(finalHash[j] ^ hash1[j]);
-                }
-            }
-            if (this.BlockIndex == uint.MaxValue) { throw new InvalidOperationException("Derived key too long."); }
-            this.BlockIndex += 1;
-
-            return finalHash;
-        }
-
-        private static byte[] GetBytesFromInt(uint i)
-        {
-            var bytes = BitConverter.GetBytes(i);
-            if (BitConverter.IsLittleEndian)
-            {
-                return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
-            }
-            else
-            {
-                return bytes;
-            }
-        }
-
-    }
-}
index be9db16..ae4f0dd 100644 (file)
@@ -1,13 +1,14 @@
 \feffusing System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
 
-using Medo.Security.Cryptography;
 using System.Security.Cryptography;
 
 namespace Novacoin
 {
+    /// <summary>
+    /// Representation of scrypt hash
+    /// </summary>
     public class ScryptHash256 : Hash
     {
         // 32 bytes
@@ -20,22 +21,20 @@ namespace Novacoin
         public ScryptHash256(byte[] bytesArray) : base(bytesArray) { }
         public ScryptHash256(IList<byte> bytesList) : base(bytesList) { }
 
+        /// <summary>
+        /// Calculate scrypt hash and return new instance of ScryptHash256 class
+        /// </summary>
+        /// <param name="inputBytes">Byte sequence to hash</param>
+        /// <returns>Hashing result instance</returns>
         public static ScryptHash256 Compute256(IEnumerable<byte> inputBytes)
         {
-            byte[] dataBytes = inputBytes.ToArray();
-
             uint[] V = new uint[(131072 + 63) / sizeof(uint)];
 
-            uint[] X = null;
-            using (HMACSHA256 hmac = new HMACSHA256())
-            {
-                Pbkdf2 df = new Pbkdf2(hmac, dataBytes, dataBytes, 1);
-                byte[] keyBytes1 = df.GetBytes(128);
-
-                X = Interop.ToUInt32Array(keyBytes1);
-            }
+            byte[] dataBytes = inputBytes.ToArray();
+            byte[] keyBytes1 = PBKDF2Sha256GetBytes(128, dataBytes, dataBytes, 1);
+            uint[] X = Interop.ToUInt32Array(keyBytes1);
 
-            ushort i, j, k;
+            uint i, j, k;
             for (i = 0; i < 1024; i++)
             {
                 Array.Copy(X, 0, V, i * 32, 32);
@@ -45,7 +44,7 @@ namespace Novacoin
             }
             for (i = 0; i < 1024; i++)
             {
-                j = (ushort)(32 * (X[16] & 1023));
+                j = 32 * (X[16] & 1023);
                 for (k = 0; k < 32; k++)
                     X[k] ^= V[j + k];
                 xor_salsa8(ref X, 0, ref X, 16);
@@ -53,15 +52,60 @@ namespace Novacoin
             }
 
             byte[] xBytes = Interop.LEBytes(X);
+            byte[] keyBytes2 = PBKDF2Sha256GetBytes(32, dataBytes, xBytes, 1);
 
-            byte[] keyBytes2 = null;
-            using (HMACSHA256 hmac = new HMACSHA256())
+            return new ScryptHash256(keyBytes2);
+        }
+
+        private static byte[] PBKDF2Sha256GetBytes(int dklen, byte[] password, byte[] salt, int iterationCount)
+        {
+            using (var hmac = new HMACSHA256(password))
             {
-                Pbkdf2 df = new Pbkdf2(hmac, dataBytes, xBytes, 1);
-                keyBytes2 = df.GetBytes(32);
+                int hashLength = hmac.HashSize / 8;
+                if ((hmac.HashSize & 7) != 0)
+                    hashLength++;
+                int keyLength = dklen / hashLength;
+                if ((long)dklen > (0xFFFFFFFFL * hashLength) || dklen < 0)
+                    throw new ArgumentOutOfRangeException("dklen");
+                if (dklen % hashLength != 0)
+                    keyLength++;
+                byte[] extendedkey = new byte[salt.Length + 4];
+                Buffer.BlockCopy(salt, 0, extendedkey, 0, salt.Length);
+                using (var ms = new System.IO.MemoryStream())
+                {
+                    for (int i = 0; i < keyLength; i++)
+                    {
+                        extendedkey[salt.Length] = (byte)(((i + 1) >> 24) & 0xFF);
+                        extendedkey[salt.Length + 1] = (byte)(((i + 1) >> 16) & 0xFF);
+                        extendedkey[salt.Length + 2] = (byte)(((i + 1) >> 8) & 0xFF);
+                        extendedkey[salt.Length + 3] = (byte)(((i + 1)) & 0xFF);
+                        byte[] u = hmac.ComputeHash(extendedkey);
+                        Array.Clear(extendedkey, salt.Length, 4);
+                        byte[] f = u;
+                        for (int j = 1; j < iterationCount; j++)
+                        {
+                            u = hmac.ComputeHash(u);
+                            for (int k = 0; k < f.Length; k++)
+                            {
+                                f[k] ^= u[k];
+                            }
+                        }
+                        ms.Write(f, 0, f.Length);
+                        Array.Clear(u, 0, u.Length);
+                        Array.Clear(f, 0, f.Length);
+                    }
+                    byte[] dk = new byte[dklen];
+                    ms.Position = 0;
+                    ms.Read(dk, 0, dklen);
+                    ms.Position = 0;
+                    for (long i = 0; i < ms.Length; i++)
+                    {
+                        ms.WriteByte(0);
+                    }
+                    Array.Clear(extendedkey, 0, extendedkey.Length);
+                    return dk;
+                }
             }
-
-            return new ScryptHash256(keyBytes2);
         }
 
         private static void xor_salsa8(ref uint[] B, int indexB, ref uint[] Bx, int indexBx)
index 924d659..b74e14a 100644 (file)
@@ -99,13 +99,12 @@ namespace NovacoinTest
             CNovacoinAddress donationAddress = new CNovacoinAddress(strDonationAddress);
             Console.WriteLine("Address reserialization is OK: {0}", donationAddress.ToString() == pubKeyTest.GetKeyID().ToString());
 
-            /*
+            /// Block header hashing test
             IEnumerable<byte> dataBytesForScrypt = b1.header.ToBytes();
             ScryptHash256 scryptHash = ScryptHash256.Compute256(dataBytesForScrypt);
 
-            Console.WriteLine(scryptHash.ToString());
+            Console.WriteLine("block1 header hash: {0}", scryptHash.ToString());
 
-            */
             Console.ReadLine();
         }
     }