Hashing and security
Applications that use hash-based data structures to store input
from untrusted users can be susceptible to "hash DoS", a class of
denial-of-service attack that uses deliberately chosen colliding
inputs to force an application into unexpectedly behaving with
quadratic time complexity.
At this time, the string hashing functions used in this library are
susceptible to such attacks and users are recommended to either use
a Map to store keys derived from untrusted input or to use a
hash function (e.g. SipHash) that's resistant to such attacks. A
future version of this library might ship with such hash functions.
Computing hash values
The class of types that can be converted to a hash value.
Minimal implementation: hashWithSalt.
Hashable is intended exclusively for use in in-memory data structures.
.
Hashable does not have a fixed standard.
This allows it to improve over time.
.
Because it does not have a fixed standard, different computers or computers on different versions of the code will observe different hash values.
As such, Hashable is not recommended for use other than in-memory datastructures.
Specifically, Hashable is not intended for network use or in applications which persist hashed values.
For stable hashing use named hashes: sha256, crc32, xxhash etc.
If you are looking for Hashable instance in time package,
check [time-compat](https:/hackage.haskell.orgpackage/time-compat)
Return a hash value for the argument, using the given salt.
The general contract of hashWithSalt is:
It is not required that if two values are unequal
according to the == method, then applying the
hashWithSalt method on each of the two values must produce
distinct integer results. However, the programmer should be
aware that producing distinct integer results for unequal
values may improve the performance of hashing-based data
structures.
hash :: a -> Int
Like hashWithSalt, but no salt is used. The default
implementation uses hashWithSalt with some default salt.
Instances might want to implement this method to provide a more
efficient implementation than the default implementation.
Creating new instances
| There are two ways to create new instances: by deriving
instances automatically using GHC's generic programming
support or by writing instances manually.
Generic instances
The recommended way to make instances of
Hashable for most types is to use the compiler's support for
automatically generating default instances using GHC.Generics.
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics (Generic)
import Data.Hashable
data Foo a = Foo a String
deriving (Eq, Generic)
instance Hashable a => Hashable (Foo a)
data Colour = Red | Green | Blue
deriving Generic
instance Hashable Colour
If you omit a body for the instance declaration, GHC will generate
a default instance that correctly and efficiently hashes every
constructor and parameter.
The default implementations are provided by
genericHashWithSalt and genericLiftHashWithSalt; those together with
the generic type class GHashable and auxiliary functions are exported
from the Data.Hashable.Generic module.
Understanding a compiler error
Suppose you intend to use the generic machinery to automatically
generate a Hashable instance.
data Oops = Oops
-- forgot to add "deriving Generic" here!
instance Hashable Oops
And imagine that, as in the example above, you forget to add a
"deriving Generic" clause to your data type. At compile time,
you will get an error message from GHC that begins roughly as
follows:
No instance for (GHashable (Rep Oops))
This error can be confusing, as GHashable is not exported (it is
an internal typeclass used by this library's generics machinery).
The correct fix is simply to add the missing "@deriving
Generic@".
Writing instances by hand
To maintain high quality hashes, new Hashable instances should be
built using existing Hashable instances, combinators, and hash
functions.
The functions below can be used when creating new instances of
Hashable. For example, for many string-like types the
hashWithSalt method can be defined in terms of either
hashPtrWithSalt or hashByteArrayWithSalt. Here's how you could
implement an instance for the ByteString data type, from the
bytestring package:
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Unsafe as B
import Data.Hashable
import Foreign.Ptr (castPtr)
instance Hashable B.ByteString where
hashWithSalt salt bs = B.inlinePerformIO $
B.unsafeUseAsCStringLen bs $ \(p, len) ->
hashPtrWithSalt p (fromIntegral len) salt
Hashing contructors with multiple fields
-ctors
For a type with several value constructors, there are a few
possible approaches to writing a Hashable instance.
If the type is an instance of Enum, the easiest path is to
convert it to an Int, and use the existing Hashable instance
for Int.
data Color = Red | Green | Blue
deriving Enum
instance Hashable Color where
hashWithSalt = hashUsing fromEnum
If the type's constructors accept parameters, it is important to
distinguish the constructors. To distinguish the constructors, add
a different integer to the hash computation of each constructor:
data Time = Days Int
| Weeks Int
| Months Int
instance Hashable Time where
hashWithSalt s (Days n) = s `hashWithSalt`
(0::Int) `hashWithSalt` n
hashWithSalt s (Weeks n) = s `hashWithSalt`
(1::Int) `hashWithSalt` n
hashWithSalt s (Months n) = s `hashWithSalt`
(2::Int) `hashWithSalt` n
Hashing types with multiple constructors
-ctors
For a type with several value constructors, there are a few
possible approaches to writing a Hashable instance.
If the type is an instance of Enum, the easiest path is to
convert it to an Int, and use the existing Hashable instance
for Int.
data Color = Red | Green | Blue
deriving Enum
instance Hashable Color where
hashWithSalt = hashUsing fromEnum
If the type's constructors accept parameters, it is important to
distinguish the constructors. To distinguish the constructors, add
a different integer to the hash computation of each constructor:
data Time = Days Int
| Weeks Int
| Months Int
instance Hashable Time where
hashWithSalt s (Days n) = s `hashWithSalt`
(0::Int) `hashWithSalt` n
hashWithSalt s (Weeks n) = s `hashWithSalt`
(1::Int) `hashWithSalt` n
hashWithSalt s (Months n) = s `hashWithSalt`
(2::Int) `hashWithSalt` n
hashUsing :: (Hashable b) => (a -> b) -> Int -> a -> Int
Transform a value into a Hashable value, then hash the
transformed value using the given salt.
This is a useful shorthand in cases where a type can easily be
mapped to another type that is already an instance of Hashable.
Example:
data Foo = Foo | Bar
deriving (Enum)
instance Hashable Foo where
hashWithSalt = hashUsing fromEnum
(a -> b)Transformation function.
hashPtr :: Ptr a -> Int -> IO Int
Compute a hash value for the content of this pointer.
pointer to the data to hash
hash value
hashPtrWithSalt :: Ptr a -> Int -> Salt -> IO Salt
Compute a hash value for the content of this pointer, using an
initial salt.
This function can for example be used to hash non-contiguous
segments of memory as if they were one contiguous segment, by using
the output of one hash as the salt for the next.
pointer to the data to hash
hash value
hashByteArray :: ByteArray# -> Int -> Int -> Int
Compute a hash value for the content of this ByteArray#,
beginning at the specified offset, using specified number of bytes.
hashByteArrayWithSalt :: ByteArray# -> Int -> Int -> Salt -> Salt
Compute a hash value for the content of this ByteArray#, using
an initial salt.
This function can for example be used to hash non-contiguous
segments of memory as if they were one contiguous segment, by using
the output of one hash as the salt for the next.
Since we support a generic implementation of hashWithSalt we
cannot also provide a default implementation for that method for
the non-generic instance use case. Instead we provide
defaultHashWith.
defaultHash :: Hashable a => a -> Int
Default implementation of hash based on hashWithSalt.
Caching hashes
A hashable value along with the result of the hash function.
hashed :: Hashable a => a -> Hashed a
Wrap a hashable value, caching the hash function result.
unhashed :: Hashed a -> a
mapHashed :: Hashable b => (a -> b) -> Hashed a -> Hashed b
traverseHashed :: (Hashable b, Functor f) => (a -> f b) -> Hashed a -> f (Hashed b)
Hashed cannot be Traversable
- Maintainer
- johan.tibell@gmail.com
- Portability
- portable
- Copyright
- (c) Milan Straka 2010 (c) Johan Tibell 2011 (c) Bryan O'Sullivan 2011, 2012 SPDX-License-Identifier : BSD-3-Clause