22 October 2026
One of the things I have always loved in cryptography is black-box separation results [RTV04]. Two classic examples are the separation of key agreement (and hence Public Key Encryption) from One-way Permutations (OWP) by Impagliazzo and Rudich [IR89], and the separation of Collision Resistant Hash Functions (CRHF) from OWPs by Simon [Sim98]. After Simon’s seminal paper, I started reading Hsiao and Reyzin’s paper on separating public-coin CRHFs from secret-coin CRHFs [HR04], which claims on the first page that
It is easy to see there is no meaningful way to formalize the notion of collision-resistance for a single fixed-output-length hash function.
As someone who works primarily in EasyCrypt [BGHZ11], I found this strange, as I thought we had a collision resistance proof for SHA3 with output length (more on this later!). Theory papers are almost always asymptotic, but they split on uniformity. For example, Goldwasser and Micali’s original definitions [GM84] are non-uniform, while Katz–Lindell [KL20] and most oracle separations [HR04] use uniform ones. Concrete security is mostly used to analyse specific constructions [BKR00, BDJR97]. EasyCrypt also works in the concrete setting, since its program logics produce explicit probability bounds rather than asymptotic statements.
Because of this and some free time on a flight, I thought this would be an interesting first blog post. It is about how quantifiers always seem to introduce irritating, nuanced pain points that, at least for me, are invisible at first sight.
We have two main flavours of security proofs, those done in the asymptotic uniform setting [KL20] and those done in the concrete setting [BKR00, BDJR97]. There is also an asymptotic non-uniform setting [GM84, Gol01], but we don’t really use it here. In my opinion, the difference between these notions lies almost entirely in the order of the quantifiers.
Here is the security parameter, which for now we assume to be a length, and measures the adversary’s size and running time together, including anything hard-coded into its code. PPT means that runs in probabilistic polynomial time in , and is a negligible function (one that eventually drops below for every polynomial ), which may depend on . denotes the game run with started on the given inputs, plus whatever the game itself hands it (keys, challenges, oracle answers).
Careful, though: swapping two “for all” quantifiers on its own changes nothing, since and say exactly the same thing. What really moves is the “there exists” hiding in the bound. In the concrete setting is fixed before we pick , so one bound has to hold for every adversary at once. In the uniform setting comes after , so it only has to work for that one adversary, and since being negligible only constrains large , it can be as bad as it likes on finitely many parameters.
In the concrete setting we fix first and only then quantify over the adversary, so the adversary may depend on . One can think of such a non-uniform adversary as a fixed machine together with an advice string that depends on . In the uniform setting we instead fix the adversary’s code once, and the same code then runs on every security parameter. A uniform adversary is therefore a non-uniform one whose advice does not depend on the security parameter. It still has a constant amount of advice, namely whatever is written into its code.
Notice that the definition says nothing about where the advice comes from! It could be the result of some arbitrarily long offline preprocessing phase that compresses something very useful into at most bits, such as a hard-coded collision. In the concrete setting there is only one , so whatever advice helps at that can simply be written into . This is why concrete security is, in effect, non-uniform [BL13].
Note, I prefer to think of non-uniform adversaries as Turing machines with an advice tape rather than as circuits. The two are equivalent [KL80, AB09], but the advice tape makes the possible preprocessing step much more visible, and this will matter further along.
Take a single hash function such as . Assume, intuitively, that it is not feasible for an adversary to find a collision in . Let us try to formalize this in the following game, which is the usual collision game [RoSh04] with the key removed.
In the concrete setting we would want the following.
This statement is false! (At least once is big enough to print two short strings, and “small” means anything below .) Since maps infinitely many inputs to finitely many outputs, a collision must exist, and in fact all but at most inputs collide with some other input. Note that is fixed before we quantify over . So there is an with that collision written into it, namely the one that returns . This single and annoying wins with probability . We don’t need to know , it is enough that it exists.
Adding a security parameter doesn’t help on its own. Let take as an extra input and output bits.
In the concrete setting, where is fixed, we have the following.
Again, this is false. is chosen after , so it can have a collision for written into it. That collision has size , well within the bound . This is annoying!
Finally, let us consider the asymptotic uniform case.
This statement is potentially true! is fixed before . To break the definition it has to win with probability at least , for some polynomial , on infinitely many , but finitely many bits of code can only hold finitely many collisions. Its code could contain a rule for producing collisions instead of a list, but then that rule is a collision-finding algorithm, and it has to run in polynomial time. Hsiao and Reyzin make exactly this point in a footnote [HR04], noting that such a function can only be collision resistant against uniform adversaries.
The flip side is that the definition says nothing about any particular . Take an that prints a stored collision when and gives up otherwise. It wins with probability at and everywhere else, which is negligible. So a family can satisfy the definition and still be completely broken at , the one parameter we actually use; , chosen after , can simply be there.
For a fixed-output-length function like SHA-256 there is no to grow. So the uniform option doesn’t exist, and we are back in the first case. Every failure above has the same shape, namely that is fixed, so a collision exists, and the adversary is chosen after it.
The standard fix, going back to Damgård [Dam87], is to make the hash depend on something chosen after the adversary is fixed, such as a key [RoSh04]. Here “after” is about the quantifiers, not the clock: the key is sampled independently of everything written into the adversary. It is not secret, though, as the adversary gets as input.
The adversary can still have collisions written into it, say a table of collisions for different keys, but that table only helps with probability , which is tiny for a large key space. The random key turns “a collision exists” into “you need a collision for this key”. This does not make secure by itself: a silly family like , which ignores its key, is still broken by one stored collision. What the key buys us is a definition that can actually be true, so that a proof or an assumption has something to say.
This is also why the EasyCrypt SHA-3 result [ABB+19] is meaningful. The theorem says that for every adversary with oracle access to a truly random permutation and to SHA3- instantiated with , making queries of total cost at most , the following holds.
The permutation is sampled inside the game, after the adversary is fixed, so it plays the role of the key. Unlike the key, though, the adversary never sees all of ; it can only query it. Of course, the real SHA3- uses one fixed permutation (Keccak-[1600]), so for the actual hash function the hard-coded adversary is back and Hsiao and Reyzin’s sentence still applies. The theorem is meaningful because it lives in the ideal permutation model; what happens when the adversary may preprocess is studied in [CDG18].
I argue that the hard-coded collision is a trivial attack. The adversary never finds anything, it just has the answer written in, which feels like it’s “cheating”. And the fact that a collision exists does not mean anyone knows it. It’s “collision resistance” and not “collision freeness”! For a hash function like SHA-256, what we actually care about is whether someone can find a collision, not whether one exists (in fact, one will exist for ANY compressing function). Seen this way, keys feel like a quirk of the theory that we just side-step around.
If the hard-coded adversary is the problem, why not just exclude it? The obvious attempt is to quantify only over adversaries that do not have a collision written into them. This does not work, because “has a collision written into it” is not something we can define. What if the advice is “the collision can be found between range X and range Y”? A collision can be stored encoded, split up, or reconstructed after a few steps of computation. Are those cryptographically “interesting” advice strings, or are they “cheating”? Any adversary that outputs a collision in time has, in some sense, a collision written into it. So excluding all of them means excluding every adversary that wins, and the definition becomes empty. More generally, excluding adversaries is a slippery slope that cryptography tries to avoid. It is easy to over-reach and exclude adversaries we actually care about.
This reminds me of how we deal with trivial wins in IND-CCA [RaSi91, BDPR98]. There, we handle them directly in the code of the game. An adversary could come preloaded with some decryption, but this is not a problem. The key, the challenge bit and the encryption randomness are all sampled after the adversary is fixed. So its code cannot be tailored to this particular key or challenge; whether it can still tell which message was encrypted is exactly what the security proof has to rule out. Two trivial wins are left, and the game guards against both: the two challenge messages must have the same length, since ciphertexts usually leak the length, and the decryption oracle refuses to decrypt the challenge ciphertext itself. So between the randomness and the guards, the game removes every trivial win.
The collision game for a fixed hash function has neither of these. Nothing is sampled after the adversary, so a preloaded collision is still valid when the game runs. And there is no guard we could write, because the game cannot tell a stored collision from a found one. Adding a key is exactly the move that gives the collision game what IND-CCA already has.
The fixes that do work avoid excluding anyone. The first is to put something random after the adversary, such as a key or an ideal permutation, so that a stored collision is (almost certainly) useless.
The second is to weaken what counts as a win, as in multi-collision resistance [BKP18]. There the hash is keyless, though still indexed by a security parameter, so this is not SHA-256 at one fixed length. An adversary only wins by finding considerably more mutually colliding inputs than it has bits of advice. The intuition is that it cannot simply have them all written in, but this is an assumption, not a counting argument: for iterated hashes, Joux’s multicollisions [Jou04] turn a handful of stored collisions into exponentially many colliding inputs.
Rogaway, in [Rog06], observes that common hash functions are keyless, while our formal definitions of collision resistance are keyed. His fix is to stop trying to directly calculate a proper collision resistance bound for a fixed . Instead, when a cryptosystem is built from , we prove a relative bound
for an explicit that runs and is about as efficient as . Crucially, must be written down explicitly, since if the theorem only said “there exists a ”, that could simply print a collision for . It also has to be efficient, otherwise an explicit brute-force collision search would do. Any attack on becomes an attack on , and whether anyone can attack is left as an assumption, not a theorem.
The auxiliary-input line of work [Unr07, DGK17, CDG18] is another answer, as it keeps precomputation in the model but bounds the advice and counts it explicitly. Admittedly, I still need to give this work a proper read, and maybe that’ll become another blog post in the future.
I think foundations are essential and should be studied by all aspiring cryptographers. However, I also, somewhat naively, think we should ask whether they need some altering when we cannot express something as basic as “it is hard to find a collision” without a list of caveats. Keys, uniformity, ideal models and explicit reductions each fix part of the problem, but none of them says exactly what we mean by “it is hard to find a collision”. I have no good answer to this.
In my interpretation, Bernstein and Lange make the same point for concrete security as a whole in “Non-uniform cracks in the concrete” [BL13]. Concrete security usually defines security as the nonexistence of a good attack running in time at most . They give strong evidence that, under this definition, attacks well below exist against, e.g., AES-128, but these attacks rely on a huge precomputation that the definition does not account for. They are not realistic and pose no threat to anyone, since finding them in the first place would cost far more than the usual attacks. The definition only counts the cost of running an attack, so it cannot separate them from real attacks, which Bernstein and Lange see as a flaw in the definitions, not in the primitives.
Decades of work have been built on the current foundations, and changing them may not be worth it, or even possible (both in a social and a mathematical sense). What I take away is that these quirks are worth understanding. They decide what a security theorem actually says, and that is easy to lose sight of when the proof checks.
I used Claude Opus 5.5 on high effort to format the text and references and to proofread this. Since I am someone who thinks better by talking, I also used the chat function as a bit of a back and forth to realize my idea.