Collision-Resistant Hash Functions
You and a friend each have a copy of a very large file. How can you be sure your files match exactly? Your friend could send their entire copy of the file to you, and you could compare both copies, but this is wasteful and impractical. Wouldn't it be easier if your friend could send some much shorter “fingerprint” of their copy of the file? You could compute the fingerprint of your copy and just compare the fingerprints.
This hypothetical fingerprint function must be deterministic, so that both of you compute the same fingerprint of the same file. Its output should be short, otherwise sending the fingerprint is no better than sending the entire file. Finally, the name “fingerprint” suggests that different files should have different fingerprints. Indeed, this interaction between you and your friend is vulnerable to false-positives: You and your friend might have different copies of the file whose fingerprints happen to be the same, leading you to conclude (wrongly) that the copies are identical. A pair of different inputs with the same fingerprint is called a collision.
In this scenario, we don't care what information the fingerprint leaks about the data (both users already know the data); we don't need the fingerprints to be pseudorandom; we don't need the two users to have any secrets. The only thing we care about is avoiding collisions. Collision resistance is a completely new cryptographic goal, and the focus of this chapter.
10.1. Defining collision resistance
A collision in a function is a pair where but . If has more inputs than possible outputs—and that is indeed the most interesting situation—then collisions are inevitable; this is a simple consequence of the pigeonhole principle. We can't hope for a total absence of collisions; the best we can hope is that collisions are hard to find. We call this property collision resistance.
A hash function usually refers to a function with long inputs and short, fixed-length outputs. When people talk about hash functions, they usually mean a function that supports inputs of arbitrary, variable length. It can also be useful to consider collision resistance of functions with fixed-length inputs (see section 10.4). The important properties are that the function has more inputs than outputs (so collisions do exist), and that the outputs are of a fixed length (often 256 or 512 bits).
A word of warning: Out of all the cryptographic primitives we study in this book, collision-resistant hash functions have the biggest mismatch between theory and practice. In this chapter, I'll try to address the gap as best I can.
First Attempt Formalizing Collision Resistance: It's difficult to formally define collision resistance, because hash functions involve no secrets. Below is a first attempt: We might say that a hash function is collision-resistant if the following libraries are indistinguishable:
If the libraries are indistinguishable, then comparing two hash outputs (fingerprints) has the same effect as comparing the two strings in their entirety. Consider this chapter's motivating scenario, where you and a friend want to know whether you hold identical copies of a file. describes the logical comparison you would like to perform, and describes a much more efficient way of doing it. If the libraries are indistinguishable, then it is safe to perform the more efficient comparison to achieve the same result.
Another way to understand the libraries is to notice that they give different answers only when (so in returns ) but (so in returns )—in other words, when is called on a collision. A calling program that can find any collision (it doesn't matter whether and have any particular meaning or interpretation) will have an easy strategy to distinguish the libraries. So by asking for the libraries to be indistinguishable, we are really asking for collisions to be hard to find.
Unfortunately, this definition falls short because these two libraries can in fact never be indistinguishable, except in the trivial and uninteresting case that has no collisions at all. The reason is due to subtle technicalities in how we define security: First, Kerckhoffs's principle allows an adversary to depend arbitrarily on the algorithms being attacked. Second, our security definitions care only about the running time of the calling program, and not the time needed to write the calling program's source code.
Suppose a candidate hash function is chosen, with output length , and consider the following precomputation attack: I evaluate on any distinct inputs and am guaranteed (by the pigeonhole principle) to find a collision . Then I can write down the source code of an algorithm : “return ”, where and are constant strings, hard-coded into this source code. That is, the source code of really looks like
return ,
for some suitable pair of strings that required exponential time to find. Running this algorithm is fast, certainly polynomial-time. Finding this algorithm is hard, requiring exponential time. But only its running time is relevant when it comes to indistinguishability. This is a polynomial-time algorithm that distinguishes the libraries with advantage 1, so we must conclude that the libraries are not indistinguishable.
The “attack” I just described is successful against any function that has any collisions at all. These two libraries can never be indistinguishable.
Add a Dash of Salt: To avoid the issues with the previous definition, we modify the interface of a hash function to take an additional argument called a salt. A salt is a string sampled uniformly and used for all calls to the hash function; but, unlike a key, a salt can be made public. We can define collision resistance for a salted hash function as follows:
A function is a collision-resistant hash function (CRHF) if the following libraries are indistinguishable:
The libraries sample a salt at the beginning of time, and make it available to the adversary via the subroutine.
This security definition is not vulnerable to the kind of precomputation attack described above, thanks to the salt. In definition 10.1.1, the source code of a calling program is allowed to depend arbitrarily on the choice of the hash function , but it cannot depend on the choice of salt , because is chosen only after the combined program (calling program + library) starts.
I like to think of salt as a “personalization” string. We can all agree on a good hash function , but each of us can use our own personalized variant . The adversary can't predict which personalized hash function it needs to attack until the start of the execution. Since there are exponentially many possible salts, precomputation is not helpful.
(You might wonder about an adversary program consisting of a huge, hard-coded lookup table that contains, for any salt , a precomputed collision in . Of course, such a lookup table would be exponentially large. For technical reasons, you should think of the “running time” of a program as including the time needed to read/parse the program's description. Thus, this lookup-table-based attack is not considered polynomial-time, no matter how efficiently the huge table can be accessed. By the way, this issue applies to more than just hash functions: Essentially all cryptographic algorithms can be defeated by an exponentially large, precomputed lookup table.)
Brute Force Attacks: A brute-force attack against the collision resistance of a hash function would compute , for any sequence of distinct strings until it finds for some . How long would this attack take, if has -bit outputs?
has the best chance of avoiding collisions is if the values act like independent, uniformly sampled -bit strings. (Any bias away from uniform only increases the chance of a collision.) But in that case, the question becomes: How many independent samples must we take from before encountering a repeat? This is exactly the birthday problem! We know that after roughly samples, there is a good probability of observing a repeat. So a brute-force attack against collision resistance—usually called a birthday attack—has a good probability of success after steps.
For this reason, if we want a CRHF to have bits of security—that is, if breaking the hash function is supposed to require roughly effort—then its output length should be at least . This is why you will (hopefully!) never see a 128-bit hash function in the wild; the most popular hash functions have output length 256, 384, or 512 bits.
Other Security Properties: This chapter focuses on collision resistance, but other properties of hash functions are sometimes relevant:
-
One-wayness: Given and , it should be hard to find an such that . This property is especially important when hashing passwords. Pay attention to the distinction between the following problems:
-
Given and , find .
-
Given and , find some with .
It is easy to make a function for which the first problem is hard; just define , so that gives no information at all about . The second problem is how we define one-wayness.
-
-
Second-preimage resistance: Given and , it should be hard to find such that . If collision-resistance is about the problem of finding any two people with the same birthday, then second-preimage resistance is about the problem of finding someone with the same birthday as yourself. As such, a brute-force attack against second-preimage resistance requires steps for an -bit hash function.
10.2. Hash functions in the real world
10.2.1. Theory vs. practice
Unsalted Hash Functions: Hash functions that you encounter in the wild are standardized as unsalted functions. They have only a single input argument: a string to be hashed.
Definition 10.1.1 does not capture the collision resistance of an unsalted hash function, and indeed we have seen that it is difficult to place unsalted hash functions on solid footing in provable security. Since real-world hash functions accept inputs of any length, it is best practice to simply prepend salt to the input of an unsalted hash, as . It would then be reasonable to conjecture that this method of adding salting to an unsalted is secure according to definition 10.1.1.
Salt neutralizes precomputation: The precomputation “attack” in section 10.1 is not just an academic technicality. It reflects an important real-world concern about precomputation attacks.
Suppose a server stores hashes of users' passwords, with an unsalted hash function. (We will have more to say about hashing passwords later in this section.) Knowing what hash function the server uses, an adversary can precompute the hash of, say, the trillion most common passwords. Later, after stealing the server's password file, the adversary will be able to instantly identify all users whose passwords are among the trillion most common. All of the hard work of a password-cracking attack can be done beforehand, and it can be shared among adversaries and reused against any server who uses the same unsalted hash. There is even a line of interesting research on specialized data structures called rainbow tables, that can compress enormous collections of precomputed hash outputs.
If the hash function is salted, and the salts are stored with the password file—that is to say, the adversary doesn't know the salts until obtaining the password file—then precomputation is useless. The adversary can't precompute any candidate password hashes before knowing what salt to use.
If the server hashes all users' passwords with the same salt (including the case of no salt), then an adversary can attack a large number of victims for the price of one. Every time the adversary tests a candidate password by computing , there is a potential to learn that is the password of any user who uses salt . On the other hand, if the server uses a different salt for each user, then an adversary must choose which user it wants to attack; attacking two victims requires twice the effort as attacking one.
Recommendations: The most common standardized hash functions are from the Secure Hash Algorithm (SHA) family. SHA-1 is an unsalted Merkle-Damgård (section 10.4) hash with 160-bit output, but has been deprecated as a standard in 2011 and should no longer be used. Attacks against SHA-1 were discovered, which can generate collisions with roughly effort. Remember, a good -bit hash function should require roughly effort to find a collision.
SHA-2 is a family of unsalted Merkle-Damgård hashes, with a variety of output lengths, the most common of which are SHA-256 and SHA-512. SHA-3 is the leading state-of-the-art hash, standardized in 2015. Notably, it is not a Merkle-Damgård hash, but uses a methodology called the sponge construction, which we discuss later in chapter 12. SHA-3 is a family of hashes with varying output lengths from 224 to 512 bits.
10.2.2. Hashing passwords
One of the most common uses of hash functions is for password-based authentication. Here we briefly review various methods of authenticating using a password.
In the clear: The worst way to handle passwords is for the server to store them in the clear. We usually compare authentication methods by considering what happens when an adversary compromises the server's storage. If the server stores passwords in the clear, then an adversary who compromises the server immediately learns the passwords of all users, with no effort.
Encrypting passwords sounds like a better idea, but it isn't. In order to verify a user's password, the server must have a way to decrypt these encrypted passwords. Thus, the server must also store the decryption key. Encryption achieves nothing when ciphertexts are stored alongside the key. Don't encrypt passwords!
Unsalted hash: For a user with password , the server can store the value , where is a hash function. When a user attempts to log in, it presents a password and the server can check whether .
This method of authentication is far superior to storing passwords in the clear. An adversary who compromises the server learns only the hashed passwords, and must expend some effort to recover the actual passwords: It must compute for many password guesses . However, when the hash function is unsalted, as we discussed above:
-
The adversary's effort can be precomputed, before compromising the server's storage.
-
The adversary's effort can be reused for many victims. Attacking users requires no more effort than attacking one.
Salted hash: For a user with password , the server can store both a user-specific salt and the value . When a user attempts to log in, it presents a password and the server can retrieve that user's salt and check whether .
Salted passwords ensure that the adversary must expend unique effort to attack each user: The effort must happen after compromising the server, and each victim/user requires separate effort. If you authenticate using passwords, always use salt!
Specialized Password-Hashing Functions: Typical collision-resistant hash functions like SHA-2 and SHA-3 are designed to be fast. But password hashing is one realm where it's better to be slow! If a server's collection of password hashes is leaked, then the only thing standing between an adversary and a victim's password is the speed at which the adversary can compute hashes of candidate passwords. Making a password hash a hundred or even a thousand times slower than SHA-3 would have minimal effect on the user experience (authentication would still require mere milliseconds ) but would make an adversary's task a thousand times more expensive.
A simple way to make a hash function slower is to iterate it many times; for example, if denotes a standard CRHF like SHA-3, then the server might hash its passwords using , with a few thousand 's. However, this may not slow down an adversary as much as you might expect, because a determined adversary can build special-purpose hardware that can evaluate thousands or even millions of times faster than the general-purpose CPU that you and I use to evaluate . This is not a hypothetical prospect: Some hash functions are used in proof-of-work cryptocurrency, which incentivizes people to build special-purpose hardware for computing hashes as fast as possible (trillions per second).
A more recent trend is to favor password hashing functions that require a large amount of memory rather than just computation time. There is no obvious way to build memory that is specialized for password hashing, in the same way one can build processors specialized for hashing. When it comes to memory technology, adversaries and users are on a more equal footing. A recent competition selected Argon2 as a standard for password hashing that is designed to consume significant memory.
10.3. Composing collision-resistant functions
In this section, we develop intuition about collision resistance by exploring successful and unsuccessful methods of combining CRHFs.
If is collision-resistant, then so is the function .
(The figures in this chapter will not include the salt as argument to .)
It is possible to prove this claim using the language of libraries. But collision resistance is usually much easier to think about in the contrapositive:
Our reasoning will follow the latter logical structure. More precisely, we will prove the following:
Given any collision in , it is possible to efficiently (in polynomial time) compute a collision in .
To understand why this establishes the security of , suppose there exists an efficient calling program that distinguishes from with nonnegligible advantage . In other words, breaks the collision resistance of . Then we can construct a new calling program which distinguishes from with the same advantage. That is, it breaks the collision resistance of . simply runs internally and acts as a wrapper around it. Whenever calls , the wrapper can detect whether is a collision. If so, can use to compute a collision under , using the steps described below, and send the -collision to its library. Thus, and have essentially the same running time, and sends a collision under to its library with the same probability that sends a collision under to its library.
So let be a collision in with salt . Then and
Name the intermediate values in the computation of , as and .
We can consider two cases:
-
Case 1: . Then is a collision in .
-
Case 2: . Then is a collision in .
In both cases, we obtain a collision in .
Below is an erroneous way to combine CRHFs, in the style of CBC-MAC. We show the case of 2 rounds of CBC.
Let be a hash function with output bits. The following function is not collision-resistant:
We will describe how to easily compute a collision in . Translating this attack into a formal calling program is trivial: Just compute the collision and use it as an argument to .
Draw two copies of a schematic diagram for , giving names to the intermediate values:
Our goal is to choose such that . First, observe that and are direct outputs of . We are assuming that is collision-resistant, so the only way we can reasonably expect two calls to to produce the same output is if they also have the same inputs—that is, if . (Remember, we are not attacking the collision resistance of , but the erroneous way that uses !)
Now our goal is to choose inputs to such that . More specifically, we want:
But we can rearrange this equation to get the following:
This suggests the following method of computing a collision:
-
Choose arbitrarily, with .
-
Solve for .
Then and both hash to the same value under . Since , these strings form a collision in .
10.4. The Merkle-Damgård construction
If we have a collision-resistant hash function with fixed input length, can we use it to build one with variable input length? This section describes a recipe for doing so, called the Merkle-Damgård construction after its creators.
As a warm-up, let us first show how to increase the input length of a collision-resistant function from bits to bits.
Let be a salted CRHF. Then the following function is also collision-resistant:
As usual, we will prove that given any collision in , we can efficiently compute a collision in .
Suppose we are given a collision in . These three-block strings are different, but they may have blocks in common, so our analysis must be careful. Define as the intermediate values computed while hashing these strings under :
Since these strings are a collision, . We consider two cases:
-
Case 1: and : Then and are a collision in .
-
Case 2: : Then and are a collision in .
In both cases, we obtain a collision in .
We complete the proof by observing that these two cases are exhaustive. To see why, suppose for sake of contradiction that we are in neither case. Since we are not in case 2, and . Given that , the only way to avoid case 1 is then . But now , which is a contradiction because we originally assumed that these strings are distinct. Therefore, the two cases listed above are exhaustive.
Claim 10.4.1 describes a valid approach to increase the input length of a collision-resistant function, which can be generalized in the natural way to increase the input length by any amount. However, the approach works only for fixed input lengths; it does not provide collision resistance for variable-length inputs. The situation is similar to that of CBC-MAC.
Let be a salted hash function. For any fixed , the following function is is a secure fixed-input-length CRHF, if is. However, is not a secure variable-input-length CRHF, even if is:
For the case of a fixed input length, the proof generalizes that of claim 10.4.1 in a natural way, and is left as an exercise.
For the case of variable input lengths, take an arbitrary string . The computation corresponds to the picture shown in the statement of claim 10.4.2. But there is another way to interpret the same picture.
Let , which is called in the computation of . An adversary can compute since the salt is public. But then , a collision in .
Fortunately, the fix for claim 10.4.2 is simple. Just add an additional block, containing the length of the input (encoded in binary). This process is called Merkle-Damgård padding. We can even support inputs whose length is not a multiple of , by appropriately padding the input with s:
Let be a fixed-input-length hash function. Then the Merkle-Damgård hash function is defined as:
is called the Merkle-Damgård compression function.
This construction as written is limited to inputs of length less than bits, since it must write the length of the input as an -bit integer. This is not a serious limitation since in practice .
In this setting, “compression” doesn't have its typical meaning as in other parts of computer science (where we expect there to be a decompression algorithm). Here, it just refers to the fact that inputs are longer than outputs.
is called the Merkle-Damgård initialization vector (IV). It's a traditional feature of the construction, but not strictly necessary for our collision-resistance proof.
The Merkle-Damgård construction (construction 10.4.3) is collision-resistant if its compression function is collision-resistant.
As usual, we will prove the contrapositive: Given any collision in , we can efficiently compute a collision in .
First, run and and name the intermediate values in the natural way: , , , . Suppose and have different lengths, and focus on the last call to during the computation of :
We assume that these two values are equal. But , since these are just encodings of the lengths of and , which are different. So are a collision in .
On the other hand, if and have the same length, then they are also split into the same number of blocks. The situation exactly reduces to the reasoning in claim 10.4.2. Given a collision in involving strings of the same number of blocks, it is possible to efficiently compute a collision in .
10.4.1. Merkle-Damgård in practice
Construction 10.4.3 and its proof are written in terms of a salted hash function, built from a salted compression function.
Until recently, the vast majority of real-world hash functions were unsalted Merkle-Damgård functions; the most popular standards include MD5 (MD here stands for message digest, not Merkle-Damgård), SHA1, SHA2 (SHA stands for secure hash algorithm). Collisions have been found in MD5 and SHA1. SHA2 is still in widespread use, but is being phased out in favor of its successor SHA3, which does not use the Merkle-Damgård paradigm (for reasons discussed in the next section).
Claim 10.4.4 can be interpreted as justification for the general design principle of Merkle-Damgård hash functions. We proved its security in a way that makes sense even for real-world unsalted hash functions: Given any collision in the hash function, it is possible to easily find a collision in its compression function.
10.5. PRFs from Merkle-Damgård: Length extension, NMAC, and HMAC
Suppose the only cryptographic algorithm you have available is an unsalted Merkle-Damgård hash function and you would like to implement a variable-length PRF. This section discusses several possible approaches, and some important pitfalls.
10.5.1. Length-extension attacks
We usually incorporate salt into an unsalted hash function by prepending it to the input, as . For the purpose of collision resistance, the salt is allowed to be public. But it is natural to wonder what happens if we keep the salt private and interpret it as a secret key. Could the result be a secure variable-input-length PRF? Unfortunately, the answer is no when the hash function has the Merkle-Damgård structure.
Suppose is an unsalted Merkle-Damgård hash function. Then the function is not a secure variable-input-length PRF.
The input to is first padded with Merkle-Damgård padding, which we write as . The problem with using as a PRF is that if you know , then you can compute the hash of any string that begins with , even if you don't know . The attack is called a length extension attack.
For example, suppose is a string with blocks, whose length is an exact multiple of the blocklength , and let be an encoding of its length as an -bit integer. So . Then the computations and compute the same intermediate values for the first calls to the compression function. The computation stops at that point, but the computation of continues. An adversary who has the output has all the information needed complete the computation of , even if it doesn't know !
We now describe the attack more formally, as a calling program that can distinguish the libraries for the function . To make the attack easier to write, we assume that is exactly bits long, and that are exact multiples of the blocklength .
The attack works by using to predict . It is an attack against pseudorandomness, but not against collision-resistance. There is no reason to expect that the two inputs used in this attack ( and ) are a collision in the length-extension attack.
10.5.2. Hash-then-PRF
As a stepping stone to the next constructions, we show that a fixed-input-length PRF can be promoted into a variable-input-length PRF by first hashing its input.
Let be a variable-input-length salted CRHF with output length , and be a PRF with (fixed) input length . Then the following function is a secure variable-input-length PRF:
One interesting feature of this construction is that part of the PRF key () is used as a salt for the hash function. A salt is allowed to be public for the sake of collision resistance, but in this case it remains secret since a PRF key is secret. Chapter 11 explores how this construction may be optimized by taking advantage of the fact that it keeps the salt secret.
The proof has simple intuition: Suppose is invoked on distinct inputs. Then since is collision-resistant, will produce distinct outputs. That means the inputs to are also distinct, and since is a secure PRF, it will produce pseudorandom outputs.
This intuition is formalized in the proof in the following way. The security definition for collision resistance is about equality tests: Testing equality of hash outputs is indistinguishable from directly testing equality of hash inputs. Thus, an important step in the security proof is to express the logic of a dictionary data structure (the kind that appears in the library) explicitly in terms of equality tests, so we can apply the collision resistance of .
10.5.3. NMAC and HMAC
Recall the motivation for this section: to construct a variable-length PRF using only an implementation of an unsalted Merkle-Damgård hash function. In this section, let be the hash function, with compression function .
The NMAC construction uses the following recipe:
-
Build a salted CRHF from an unsalted Merkle-Damgård hash function.
-
Build a fixed-input-length PRF from an unsalted Merkle-Damgård hash function.
-
Combine these two primitives using the hash-then-PRF construction (claim 10.5.2).
Recall that the Merkle-Damgård construction uses an initialization vector (IV), which we have usually just taken to be all-zeros, and which is some specific string in a standardized hash function. In step 1 of NMAC, we use the salt as the IV; thus, NMAC requires an implementation of the Merkle-Damgård function that lets the user specify a particular IV. Not all implementations may support nonstandard IVs, but we will address this issue a bit later. We write to denote the Merkle-Damgård hash of , using as the IV.
For step 2 of NMAC, we simply assume that is a fixed-input-length PRF on inputs of one block, treating the IV/salt as the PRF key. If you are nervous about treating a Merkle-Damgård hash as a PRF in light of claim 10.5.1, remember: These length-extension attacks apply only when using Merkle-Damgård as a variable-input-length PRF. It can still be reasonable to assume that a Merkle-Damgård hash is a secure PRF for a fixed input length.
Combining these steps gives us the full NMAC construction:
Let be a compression function, then NMAC is defined as follows:
Because NMAC is an instance of the hash-then-PRF paradigm, we have the following:
If is a salted CRHF (interpreting its first argument as the salt), and is a secure PRF for 1-block inputs (interpreting its first argument as the PRF key), then NMAC is a secure variable-length-input PRF.
HMAC: What if you have an implementation of a Merkle-Damgård hash that does not allow you to specify a particular IV? HMAC is a variant of NMAC designed for this case.
Suppose the NMAC keys are derived from a single key in a special way:
where and are some fixed constants, is the fixed IV of the standardized, unsalted Merkle-Damgård hash, and is its compression function. (In the HMAC standard, and are the hexadecimal strings and .)
This value of is precisely what the first round of the standard-IV, unsalted hash computes, when the first block of its input is . In fact, we have:
In other words, if NMAC keys are chosen this way, then we can evaluate NMAC using an implementation of with its standard, fixed IV. The result is HMAC:
In this picture, the highlighted parts show HMAC deriving NMAC keys and from a common .
There is actually a slight difference between and If has length , then the former computation will add Merkle-Damgård length padding , but the latter computation will add length padding — it “sees” an input that is one block longer than . But the details of the Merkle-Damgård padding do not seem relevant to the assumptions on that NMAC requires; presumably the assumptions are equally valid when an extra is always added to the length padding.
Thus, assuming that the following function is a PRG:
then HMAC is indistinguishable from NMAC—or, at least a variant of NMAC that adds an extra to its length padding.
Exercises
-
A birthday attack on collision resistance invokes the hash function times. But the attack also needs to realize when it has seen a collision, and output the values that lead to the collision. How would you implement a birthday attack so that its total, overall running time is , not just the number of calls to it makes?
-
Let be a salted hash function with bits of output, -bit salt, and variable-length input. Suppose that whenever is bits long we have . Thus, it is guaranteed that there are no collisions among -bit inputs. Show that cannot be collision-resistant in general.
-
If and are strings of the same length, write to mean that encodes an integer that is less than or equal to that of . Suppose is a hash function with the following property: For all and , if then . Show that cannot be collision-resistant.
-
Suppose is a CRHF and define the new function
Here and are the same length. Is also a CRHF? Either prove or describe an attack.
-
Let be a PRP with blocklength . Prove that CBC-MAC (below), using an all-zeros IV and the salt as the PRP key, is not a CRHF:
-
h Let be a CRHF with output bits, and define the following function:
The function has variable input length, so can be any value. In , integer is encoded as a -bit string. Show that is not a variable-length collision-resistant hash function.
Interpret as an -dimensional vector space, so that each output of is a vector in that space. With enough such vectors, you can find nontrivial linear dependencies.
-
Let be a secure PRP with blocklength and key size .
-
Show how to efficiently find a collision in the (unsalted) function .
-
Show how to efficiently find a collision in the (unsalted) function .
-
Show how to efficiently find a collision in the (unsalted) function .
-
-
Let be a salted hash function with output bits. Show how to efficiently find a collision in the following function :
-
Prove the first half of claim 10.4.2. For any fixed , the function described there is a secure CRHF for inputs of fixed length .
-
Let be a salted hash function with fixed input length. Prove that if is collision-resistant, then the following salted hash function is collision-resistant for variable-length inputs that are multiples of the blocklength:
-
-
In an AEAD scheme, it is convenient to compute a hash of the pair of strings where is the associated data and is the CPA ciphertext. Let be a CRHF. Show that the following function is not collision-resistant for pairs of strings:
Describe how to construct two distinct pairs that collide under .
-
Propose a better method to hash pairs of strings and prove that it is collision-resistant.
-
-
Construction 10.4.3 presents the Merkle-Damgård construction based on a compression function with input bits and output bits. Describe how to modify the construction to support a compression function with input bits and output bits, for any .
-
Let be a Merkle-Damgård hash function with compression function .
-
Describe how to find (with good probability) four distinct strings of the form
that all hash to the same output under , using running time .
-
Describe how to find distinct messages that all hash to the same output under , using running time .
-
Suppose is a Merkle-Damgård hash function, and is some other hash function, not necessarily Merkle-Damgård, both with output bits. Define the new function
Since has outputs of length , it is tempting to think that it could offer bits of security (i.e., that collisions in require effort).
Show how to find a collision in in running time only . Your attack should not assume anything about .
-
-
Let be an unsalted hash function with output length , and let denote CTR-mode encryption (construction 8.5.4). Show that the following scheme is not CCA-secure:
-
Let be an unsalted Merkle-Damgård hash, and define (i.e., the bitwise complement of ). Show how to modify the length-extension attack from section 10.5.1 to work against .
-
There are two natural ways to incorporate salt into an unsalted Merkle-Damgård hash function: (1) prepend it to the input; (2) use the salt as the IV, instead of the standardized IV value. NMAC uses the latter method.
In the text we showed that method (1) does not lead to a secure variable-input-length PRF if you interpret the salt as the PRF key. Show that the same holds for method (2).
-
Prove that HMAC is collision-resistant if we treat the key as a public salt, under the same assumptions that make HMAC a secure PRF.
Chapter Notes
Our first attempt at defining collision resistance fails because we allow adversaries to be nonuniform algorithms. In computational complexity, nonuniform means that a different algorithm may be used for each input length (security parameter in our case), and there is no limit to how much computation can be used to discover the “correct” algorithm for a given input length. Koblitz and Menezes have criticized the use of nonuniformity in cryptographic definitions [134]. We introduced salt to avoid the problems with our first collision resistance definition, although there have been some proposals to formalize collision resistance for unsalted functions; see Rogaway [192].
The first version of UNIX that used salted password hashing appears to be V7, released in early 1979 (according to my understanding of historical source code from the Unix Heritage Society [210]), where the source code indeed describes the relevant value as salt. Around the same time, Morris and Thompson [167] describe the practice of salting in a retrospective article about password security in UNIX. There seems to be no consensus on why the word “salt” was chosen to describe this technique.
The first formal definitions for collision resistance are due to Damgård [78]. Rogaway and Shrimpton later provided a more modern treatment of collision resistance and other desirable properties of hash functions [194].
The first collision in SHA-1 was found by Wang, Yin, and Yu [216].
Exercise 10.6 is due to Bellare and Micciancio [23].
The Merkle-Damgård construction was proposed independently by Merkle [161] and Damgård [79].
The weakness of Merkle-Damgård hashes explored in exercise 10.13 was first observed by Joux [128].
Length extension attacks on Merkle-Damgård hashes were first observed by Solo and Kent, as attributed by Tsudik [212]. Duong and Rizzo exploited length extension in a notable attack against the Flickr website [90].
NMAC and HMAC were proposed by Bellare, Canetti, and Krawczyk [14]. Our security treatment follows theirs, although a subsequent analysis by Bellare [12] gives an alternative security proof that does not rely on collision resistance.