0

I have an algorithm use to generate and return a checkSum when an update operation is called. The second update need to send the given checksum, who is the proof that the object is exactly the same. Otherwise, an exception is throw.

Here is my class used to manage the checksum :

public class TokenUpdate
{
    internal readonly DateTime expireDateTime;
    private static readonly Object locker = new Object();
    internal readonly byte[] _dataTokenKey;

    public TokenUpdate(object insuranceAgreement, DateTime newExpireDateTime)
    {
        expireDateTime = newExpireDateTime;
        _dataTokenKey = GetSignedToken(insuranceAgreement);

    }

    public TokenUpdate(byte[] dataTokenKey, DateTime newExpireDateTime)
    {
        expireDateTime = newExpireDateTime;
        _dataTokenKey = dataTokenKey;

    }
    public bool IsExpired
    {
        get
        {
            return !(expireDateTime > DateTime.Now);
        }
    }

    public byte[] GetSignedToken(object insuranceAgreement)
    {
        byte[] tokenKey;

        byte[] tokenData;

        // serialize data in byte[]
        using (var ms = new MemoryStream())
        {
            using (var writer = new BinaryWriter(ms))
            {
                writer.Write(ObjectToByteArray(insuranceAgreement));
                tokenData = ms.ToArray();
            }
        }


        using (var sha1 = new SHA256Managed())
        {
            tokenKey = sha1.ComputeHash(tokenData);
        }


        return tokenKey;
    }

    public string GetTokenString()
    {
        byte[] tokenData;

        // serialize data in byte[]
        using (var ms = new MemoryStream())
        {
            using (var writer = new BinaryWriter(ms))
            {
                writer.Write(expireDateTime.Ticks);
                tokenData = ms.ToArray();
            }
        }


        return Convert.ToBase64String(tokenData.Concat(_dataTokenKey).ToArray());
    }

    public static TokenUpdate BuildToken(object insuranceAgreement, DateTime expireDateTime)
    {
        if (insuranceAgreement == null)
        {
            return null;
        }

        return new TokenUpdate(insuranceAgreement, expireDateTime);
    }

    public static bool AreEqual(TokenUpdate token, object toCompare)
    {
        var compareToken = new TokenUpdate(toCompare, DateTime.Now);


        return token._dataTokenKey.SequenceEqual(compareToken._dataTokenKey);
    }

    public static TokenUpdate GetTokenFromString(string tokenString)
    {
        TokenUpdate returnedToken = null;
        byte[] buffer;
        try
        {
            buffer = Convert.FromBase64String(tokenString);
        }
        catch (FormatException)
        {
            return null;
        }

        if (buffer.Length != 40)
        {
            return null;
        }

        var data = buffer.Take(buffer.Length - 32).ToArray();
        var sig = buffer.Skip(data.Length).Take(32).ToArray();
        using (var ms = new MemoryStream(data))
        using (var reader = new BinaryReader(ms))
        {
            var ticks = reader.ReadInt64();
            var expires = new DateTime(ticks);
            returnedToken = new TokenUpdate(sig, expires);
        }

        return returnedToken;
    }

    internal static byte[] ObjectToByteArray(object objectToSerialize)
    {
        if (objectToSerialize == null)
        {
            return null;
        }

        MemoryStream fs = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        try
        {
            //Here's the core functionality! One Line!
            //To be thread-safe we lock the object
            lock (locker)
            {
                formatter.Serialize(fs, objectToSerialize);
            }
            return fs.ToArray();
        }
        catch (SerializationException se)
        {
            throw;
        }
        finally
        {
            fs.Close();
        }
    }
}

With some unit test everything is fine, but running some stress-test show us that in 30% of case, the second update is rejected because the comparaison of the checksum sent with the one generated from the data in the database is failing.

I checked and I'm almost sure there is no inner-update between the two others.

I'm wondering, is the hash algorithm related to the SHA256Managed class related to the mac adresse of the system datetime ? It may cause the issue because we are load-balanced.

Xavier W.
  • 1,270
  • 3
  • 21
  • 47
  • not according to MS :https://technet.microsoft.com/en-us/library/system.security.cryptography.sha256managed(v=vs.105).aspx – GPW Sep 11 '18 at 14:33
  • 1
    I would initially change it to simply compare .ToString() on the objects or something; perhaps something is slightly different due to one server having a different date format or something? (as a debugging step I mean; not permanently) – GPW Sep 11 '18 at 14:36
  • @GPW, in the `AreEqual()` method ? – Xavier W. Sep 11 '18 at 14:39
  • 2
    SHA is perfectly deterministic. If there's a hash difference, then either the data being hashed is different, or the machine performing the hash is broken (overclocked processors and faulty RAM are the most popular causes). Software failure is still more likely than hardware failure, of course. From the code posted it's hard to tell how a data difference might arise. You can be sure that the problem is not in the `SHA256Managed` class, though. – Jeroen Mostert Sep 11 '18 at 14:42
  • I like this line: `sha1 = new SHA256Managed` – spender Sep 11 '18 at 14:44
  • @XavierW. yes, though it would probably help YOU to know exactly where the data differs; so rather than just comparing hashes, ensure you log the EXACT data being hashed when generating it the first time, then log the EXACT data being hashed the second time - then you can see where the difference is.. if the hashes don't match but the data does exactly, then you have a hashing problem. otherwise it's something else. – GPW Sep 11 '18 at 14:45

0 Answers0