Generate a Hashcode for a string that is platform independent - c#

We have an application that
Generates a hash code on a string
Saves that hash code into a DB along with associated data
Later, it queries the DB using the string hash code for retrieving the data
This is obviously a bug because the value returned from string.GetHashCode() varies from .NET versions and architectures (32/64 bit). To complicate matters, we're too close to a release to refactor our application to stop serializing hash codes and just query on the strings instead. What we'd like to do is come up with a quick and dirty fix for now, and refactor the code later to do it the right way.
The quick and dirty fix seems like creating a static GetInvariantHashCode(string s) helper method that is consistent across architectures.
Can suggest an algorithm for generating a hashcode on a string that is equivalent on 32 bit and 64 bit architecture?
A few more notes:
I'm aware that HashCodes are not unique. If a hashcode returns a match on two different strings, we post process the results to find the exact match. It is not used as a primary key.
I believe the architect's intent was to speed up the searches by querying on a long instead of an NVarChar

I'm aware that HashCodes are not unique. If a hashcode returns a match on two different strings, we post process the results to find the exact match. It is not used as a primary key.
I believe the architect's intent was to speed up the searches by querying on a long instead of an NVarChar
Then just let the database index the strings for you!
Look, I have no idea how large your domain is, but you're going to get collisions very rapidly with very high likelihood if it's of any decent size at all. It's the birthday problem with a lot of people relative to the number of birthdays. You're going to have collisions, and lose any gain in speed you might think you're gaining by not just indexing the strings in the first place.
Anyway, you don't need us if you're stuck a few days away from release and you really need an invariant hash code across platform. There are really dumb, really fast implementations of hash code out there that you can use. Hell, you could come up with one yourself in the blink of an eye:
string s = "Hello, world!";
int hash = 17;
foreach(char c in s) {
unchecked { hash = hash * 23 + c.GetHashCode(); }
}
Or you could use the old Bernstein hash. And on and on. Are they going to give you the performance gain you're looking for? I don't know, they weren't meant to be used for this purpose. They were meant to be used for balancing hash tables. You're not balancing a hash table. You're using the wrong concept.
Edit (the below was written before the question was edited with new salient information):
You can't do this, at all, theoretically, without some kind of restriction on your input space. Your problem is far more severe than String.GetHashCode differening from platform to platform.
There are a lot of instances of string. In fact, way more instances than there are instances of Int32. So, because of the piegonhole principle, you will have collisions. You can't avoid this: your strings are pigeons and your Int32 hash codes are piegonholes and there are too many pigeons to go in the pigeonholes without some pigeonhole getting more than one pigeon. Because of collision problems, you can't use hash codes as unique keys for strings. It doesn't work. Period.
The only way you can make your current proposed design work (using Int32 as an identifier for instances of string) is if you restrict your input space of strings to something that has at size less than or equal to the number of Int32s. Even then, you'll have difficulty coming up with an algorithm that maps your input space of strings to Int32 in a unique way.
Even if you try to increase the number of pigeonholes by using SHA-512 or whatever, you still have the possibility of collisions. I doubt you considered that possibility previously in your design; this design path is DOA. And that's not what SHA-512 is for anyway, it's not to be used for unique identification of messages. It's just to reduce the likelihood of message forgery.
To complicate matters, we're too close to a release to refactor our application to stop serializing hash codes and just query on the strings instead.
Well, then you have a tremendous amount of work ahead of you. I'm sorry you discovered this so late in the game.
I note the documentation for String.GetHashCode:
The behavior of GetHashCode is dependent on its implementation, which might change from one version of the common language runtime to another. A reason why this might happen is to improve the performance of GetHashCode.
And from Object.GetHashCode:
The GetHashCode method is suitable for use in hashing algorithms and data structures such as a hash table.
Hash codes are for balancing hash tables. They are not for identifying objects. You could have caught this sooner if you had used the concept for what it is meant to be used for.

You should just use SHA512.
Note that hashes are not (and cannot be) unique.
If you need it to be unique, just use the identity function as your hash.

You can use one of the managed cryptography classes (such as SHA512Managed) to compute a platform independent hash via ComputeHash. This will require converting the string to a byte array (ie: using Encoding.GetBytes or some other method), and be slow, but be consistent.
That being said, a hash is not guaranteed unique, and is really not a proper mechanism for uniqueness in a database. Using a hash to store data is likely to cause data to get lost, as the first hash collision will overwrite old data (or throw away new data).

Related

Why do string hash codes change for each execution in .NET?

Consider the following code:
Console.WriteLine("Hello, World!".GetHashCode());
First run:
139068974
Second run:
-263623806
Now consider the same thing written in Kotlin:
println("Hello, World!".hashCode())
First run:
1498789909
Second run:
1498789909
Why do hash codes for string change for every execution in .NET, but not on other runtimes like the JVM?
Why do hash codes for string change for every execution in .NET
In short to prevent hash collision attacks. You can roughly find out the reason from the docs of the <UseRandomizedStringHashAlgorithm> configuration element:
The string lookup in a hash table is typically an O(1) operation. However, when a large number of collisions occur, the lookup can become an O(n²) operation. You can use the configuration element to generate a random hashing algorithm per application domain, which in turn limits the number of potential collisions, particularly when the keys from which the hash codes are calculated are based on data input by users.
but not on other runtimes like the JVM?
Not exactly, for example Python's hash function is random. C# also produces identity hash in .net framework, core 1.0 and core 2.0 when <UseRandomizedStringHashAlgorithm> is not enabled.
For Java maybe it's a historical issue because the arithmetic is public, and it's not good, read this.
Why do hash codes change for every execution in .NET?
Because changing the hash code of strings (and other objects!) on each run is a very strong hint to developers that hash codes do not have any meaning outside of the process that generated the hash.
Specifically, the documentation says:
Furthermore, .NET does not guarantee the default implementation of the GetHashCode method, and the value this method returns may differ between .NET implementations, such as different versions of .NET Framework and .NET Core, and platforms, such as 32-bit and 64-bit platforms. For these reasons, do not use the default implementation of this method as a unique object identifier for hashing purposes. Two consequences follow from this:
You should not assume that equal hash codes imply object equality.
You should never persist or use a hash code outside the application domain in which it was created, because the same object may hash across application domains, processes, and platforms.
By changing the hash code of a given object from one run to the next, the runtime is telling the developer not to use the hash code for anything that crosses a process/app-domain boundary. That will help to insulate developers from bugs stemming from changes to the GetHashCode algorithms used by standard classes.
Having hash codes change from one run to the next also discourages things like persisting the hash code for use as a "did this thing change" short-cut. This both prevents bugs from changes to the underlying algorithms and bugs from assuming that two objects of the same type with the same hash code are equal, when no such guarantee is made (in fact, no such guarantee can be made for any data structure which requires or allows more than 32 bits, due to the pigeonhole principle).
Why do other languages generate stable hash codes?
Without a thorough language-by-language review, I can only speculate, but the major reasons are likely to be some combination of:
historical inertia (read: "backwards compatibility")
the disadvantages of stable hash codes were insufficiently understood when the language spec was defined
adding instability to hash codes was too computationally expensive when the language spec was defined
hash codes were less visible to developers

GUIDS and site security [duplicate]

GUIDs get used a lot in creating session keys for web applications. I've always wondered about the safety of this practice. Since the GUID is generated based on information from the machine, and the time, along with a few other factors, how hard is it to guess of likely GUIDs that will come up in the future. Let's say you started 1000, or 10000 new sessions, to get a good dataset of the GUIDs being generated. Would this make it any easier to generate a GUID that might be used for another session. You wouldn't even have to guess a specific GUID, but just keep on trying GUIDs that might be generated at a specific period of time.
Here is some stuff from Wikipedia (original source):
V1 GUIDs which contain a MAC address
and time can be identified by the
digit "1" in the first position of the
third group of digits, for example
{2f1e4fc0-81fd-11da-9156-00036a0f876a}.
In my understanding, they don't really hide it.
V4 GUIDs use the later algorithm,
which is a pseudo-random number. These
have a "4" in the same position, for
example
{38a52be4-9352-453e-af97-5c3b448652f0}.
More specifically, the 'data3' bit
pattern would be 0001xxxxxxxxxxxx in
the first case, and 0100xxxxxxxxxxxx
in the second. Cryptanalysis of the
WinAPI GUID generator shows that,
since the sequence of V4 GUIDs is
pseudo-random, given the initial state
one can predict up to next 250 000
GUIDs returned by the function
UuidCreate1. This is why GUIDs
should not be used in cryptography, e.
g., as random keys.
GUIDs are guaranteed to be unique and that's about it. Not guaranteed to be be random or difficult to guess.
TO answer you question, at least for the V1 GUID generation algorithm if you know the algorithm, MAC address and the time of the creation you could probably generate a set of GUIDs one of which would be one that was actually generated. And the MAC address if it's a V1 GUID can be determined from sample GUIDs from the same machine.
Additional tidbit from wikipedia:
The OSF-specified algorithm for
generating new GUIDs has been widely
criticized. In these (V1) GUIDs, the
user's network card MAC address is
used as a base for the last group of
GUID digits, which means, for example,
that a document can be tracked back to
the computer that created it. This
privacy hole was used when locating
the creator of the Melissa worm. Most
of the other digits are based on the
time while generating the GUID.
.NET Web Applications call Guid.NewGuid() to create a GUID which is in turn ends up calling the CoCreateGuid() COM function a couple of frames deeper in the stack.
From the MSDN Library:
The CoCreateGuid function calls the
RPC function UuidCreate, which creates
a GUID, a globally unique 128-bit
integer. Use the CoCreateGuid function
when you need an absolutely unique
number that you will use as a
persistent identifier in a distributed
environment.To a very high degree of
certainty, this function returns a
unique value – no other invocation, on
the same or any other system
(networked or not), should return the
same value.
And if you check the page on UuidCreate:
The UuidCreate function generates a
UUID that cannot be traced to the
ethernet/token ring address of the
computer on which it was generated. It
also cannot be associated with other
UUIDs created on the same computer.
The last contains sentence is the answer to your question. So I would say, it is pretty hard to guess unless there is a bug in Microsoft's implementation.
If someone kept hitting a server with a continuous stream of GUIDs it would be more of a denial of service attack than anything else.
The possibility of someone guessing a GUID is next to nil.
Depends. It is hard if the GUIDs are set up sensibly, e.g. using salted secure hashes and you have plenty of bits. It is weak if the GUIDs are short and obvious.
You may well want to be taking steps to stop someone create 10000 new sessions anyway due to the server load this might create.
"GUIDs are guaranteed to be unique and that's about it". GUIDs are not garanteed to be unique. At least the ones generated by CoCreateGuid: "To a very high degree of certainty, this function returns a unique value – no other invocation, on the same or any other system (networked or not), should return the same value."

Why the GetHashCode does not take advantage of SK.exe tool's hashcode algorithm?

MSDN says:
"The default implementation of the GetHashCode method does not guarantee unique return values for different objects. "
But on the other hand, when I use the sn.exe tool it ensures a unique hash value to create a strongly-named assembly. If I did not get the point wrong, all the content of the assembly is converted to a hash value.
So, why GetHashCode()'s default implementation does not use the same algorithm used by sn.exe to create a unique hash values for objects and expects the developer to implent it?
Not enough bits. GetHashCode() returns 32 of them so there can never be more than 4 billion distinct values. The birthday paradox cuts that down considerably. The strong name generated by sn.exe (not sk.exe) uses a SHA1 hash. Which returns 160 bits, allowing for 2^160 distinct values.
Which is a Really Big Number (1.4E48), ensuring uniqueness by the sheer quantity. Somewhat similar to a Guid which uses 128 bits. Not the same, a Guid generator ensures that no duplicates can occur, SHA1 has no such guarantee.
GetHashCode has a limited number of bits because the primary requirement for the method is that it is fast. Short from providing the bucket index for hashed collections, its use is making equality testing fast. GetHashCode needs to be an order of magnitude faster than Equals(), give or take, to make it useful. That requires many corners to be cut, typically, the GetHashCode implementation for a struct that contains reference types for example only returns the GetHashCode value of the first member.
Those are two entirely different things.
The GetHashCode() function by definition returns (only) a 32 bits integer. It is supposed to use a fast algorithm and does not (can not) guarantee uniqueness. A PC can quickly generate enough strings to show a collision.
When you sign an application (document) you will end up with a lot larger hash (like 128 or 256 bits). While in theory you might still have a collision this has no practical implications.
There's no limit to the number of objects a program can create, call GetHashCode() upon, and abandon. There is, however, a limit of 4,294,967,296 different values GetHashCode() can return. If a program happens to call GetHashCode 4,294,967,297 times, at least one of those calls would have to return a value that had already been returned previously.
It would theoretically be possible for the system to keep a pool of hash-code values, and for objects which are abandoned to have their hash codes put back in the pool so that GetHashCode() could guarantee that it will never return the same value as any other live object (assuming there are no more than 4,294,967,296 live objects, at least). On the other hand, keeping such information would be expensive and not really offer much benefit. From a practical perspective, it's just as good to have the system generate an arbitrary number either when an object is constructed or the first time GetHashCode() is called upon it. There will be occasional collisions, but generally not enough to bother well-written code.
BTW, I've sometimes thought it would be useful for each object to have a 64-bit ID which would be guaranteed unique, and which would also rank objects in order of creation. A 64-bit ID would never overflow within the lifetime of any foreseeable program, and being able to assign objects a ranking could be helpful in some caching or interning scenarios. For example, if a program generates some large objects by reading data from files, and frequently scans them to find differences, it may often find objects that contain identical data but are distinct. If two distinct objects are found to be identical and interchangeable, replacing reference to the newer one with the older one may considerably expedite future comparisons among them; if many matching objects are compared among each other, many of the references to newer objects will get replaced with references to the oldest ones, without having to explicitly cache anything. Absent some means of determining "age", however, such an approaches wouldn't really work, since there would be no way to know which reference should be abandoned in favor of the other.
Unrelated. Wonder how you could relate these two!!
Still, to add more argument:
Hashcode for a value 'can not guarantee' uniqueness for different values. But it does 'guarantee' a same hash code for a given value/object!. That means:
var hashOne = "SO".GetHashCode();
var hastTwo = "SO".GetHashCode();
Debug.Assert(hashOne==hashTwo); //The assertion would succeed.
SN just just generates a random unique number, with no logic over an instance.

Getting unique hash for two different string URLs that are actually the same

I'm indexing some URLs based on their hash code and use this hash to retrieve them. I have 2 questions in this matter:
Do you think this is a good approach? I mean sometimes two different URLs can produce the same hash but I don't seem to have any other choice since URLs can be very long and I need to produce a file name for them.
[More important] Sometimes two different URLs are actually reffering to the same page (e.g. http://www.stackoverflow.com and http://stackoverflow.com and sometimes URLs with % characters) but I need to produce the same hash code for these URLs. What do you suggest?
Thanks.
Definitely don't use the .NET String hash code - there's no guarantee it'll do the same thing between versions (and did actually change between .NET 1.1 and .NET 2.0). It's also quite likely to have collisions, and is very short 32 bits).
If you really have to use a hash, use a cryptographic hash as that's much less likely to result in collisions - you could use SHA-256, for example. Note that crypto hashes generally work in terms of binary data, so you'll need to convert the URL to a byte array first, e.g. with Encoding.UTF8.GetBytes(text). This isn't foolproof, but it's at least "very unlikely" to produce collisions. Of course, as the hash is rather bigger, your output filename will be bigger too. (You'll need to convert from a byte[] to a string as well, I assume - I suggest you use Convert.ToBase64String).
Does your filename really have to be derived from the URL though? Couldn't you just generate random filenames (or increment a counter) and then store the mapping between URL and filename somewhere? That's a much more sensible approach IMO - and it's reversible (so you can tell which URL generated a particular file).
As for your second question - basically you'll need to find some way of deriving a canonical URL from any given URL, so that all "equivalent" URLs are converted to the same canonical one... and that's what you hash or store.
Indexing based on hash codes is a path to bugs. Hash codes are not unique and do have collisions. If you index on a hash code it will lead to a situation where two non-equal values end up retrieving the same mapped value from your data table.
After lots of discussion and thinking, since there is no answer that completely answers my questions, I'm going to answer my own question. The one thing important is that the comment posted by Morten Mertner is the closest thing to my answer but I cannot select it as an answser.
There is no other way for me except using a hash algorithm. But to reduce the risk of duplicate, I should use better algorithms like SHA-2 ones.
As Morten Mertner said, in some cases the mentioned URLs are NOT actually the same and I cannot assume that the website is configured correctly. The only thing I can do is to remove the bookmarks and either use ecoded/decoded version of the URL. (The versions with/without % characters).
Thanks for all of the help guys.

Hash quality and stability of String.GetHashCode() in .NET?

I am wondering about the hash quality and the hash stability produced by the String.GetHashCode() implementation in .NET?
Concerning the quality, I am focusing on algorithmic aspects (hence, the quality of the hash as it impacts large hash-tables, not for security concerns).
Then, concerning the stability, I wondering about the potential versionning issues that might arise from one .NET version to the next.
Some lights on those two aspects would be very appreciated.
I can't give you any details about the quality (though I would assume it is pretty good given that string is one of the framework's core classes that is likely to be used as a hash key).
However, regarding the stability, the hash code produced on different versions of the framework is not guaranteed to be the same, and it has changed in the past, so you absolutely must not rely on the hash code being stable between versions (see here for a reference that it changed between 1.1 and 2.0). In fact, it even differs between the 32-bit and 64-bit versions of the same framework version; from the docs:
The value returned by GetHashCode is platform-dependent. For a specific string value, it differs on the 32-bit and 64-bit versions of the .NET Framework.
This is an old question, but I'd like to contribute by mentionning this microsoft bug about hash quality.
Summary: On 64b, hash quality is very low when your string contains '\0' bytes. Basically, only the start of the string will be hashed.
If like me, you have to use .Net strings to represent binary data as key for high-performance dictionaries, you need to be aware of this bug.
Too bad, it's a WONTFIX... As a sidenote, I don't understand how they could say that modifying the hashcode being a breaking change, when the code includes
// We want to ensure we can change our hash function daily.
// This is perfectly fine as long as you don't persist the
// value from GetHashCode to disk or count on String A
// hashing before string B. Those are bugs in your code.
hash1 ^= ThisAssembly.DailyBuildNumber;
and the hashcode is already different in x86/64b anyway.
I just came across a related problem to this. On one of my computers (a 64 bit one) I had a problem that I tracked down to 2 different objects being identical except for the (stored) hashcode. That hashcode was created from a string....the same string!
m_storedhash = astring.GetHashCode();
I dont know how these two objects ended up with different hash codes given they were from the same string however I suspect what happened is that within the same .NET exe, one of the class library projects I depend upon has been set to x86 and another to ANYCPU and one of these objects was created in a method inside the x86 class lib and the other object (same input data, same everything) was created in a method inside the ANYCPU class library.
So, does this sound plausible: Within the same executable in memory (not between processes) some of the code could be running with the x86 Framework's string.GetHashCode() and other code x64 Framework's string.GetHashCode() ?
I know that this isn't really included the meanings of quality and stability that you specified, but it's worth being aware that hashing extremely large strings can produce an OutOfMemoryException.
https://connect.microsoft.com/VisualStudio/feedback/details/517457/stringcomparers-gethashcode-string-throws-outofmemoryexception-with-plenty-of-ram-available
The quality of the hash codes are good enough for their intended purpose, i.e. they doesn't cause too many collisions when you use strings as key in a dictionary. I suspect that it will only use the entire string for calculating the hash code if the string length is reasonably short, for huge strings it will brobably only use the first part.
There is no guarantee for stability across versions. The documentation clearly says that the hashing algorithm may change from one version to the next, so that the hash codes are for short term use.

Categories