summaryrefslogtreecommitdiff
path: root/ss2017/cryptography/Notes11.txt
blob: 839d24be01306b22305d1f24491d4d6f52b042f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
11 Hashing, Merkle-Damgard transform, SHA, HMAC
-----------------------------------------------

Hash functions serve a related but slightly different purpose to that
of MACs. Unlike those (and private key encryption) they are not
immediately useful on their own but rather are building blocks and
indeed are "a workhorse of cryptography" like e.g. solving systems of
linear equations is to engineering.

Intuitively, a hash function H() transforms a long string m into a
short digest ("hash") t=H(m) such that it is extremely difficult to
produce two texts x =/= x' with H(x)=H(x'). Such pair x,x' is known as
a *collision*.

As such, the notion cannot really be made sense of in the context of
computational complexity for once we have x=/=x' with H(x)=H(x') then
we can just hardwire x,x' into a program. In order to get around this,
we introduce as before randomly chosen keys and security parameters.
This will allow us to formulate meaningful security goals and
meaningful security proofs for various constructions involving hash
functions. 

Definition: A *hash function* consists of a (probabilistic polytime)
key generation algorithm Gen(n) as before and a polytime function H
taking as input a key s ("seed") and m:{0,1}^* then
H_s(m):{0,1}^{l(n)} where l(n) is a fixed, and typically simple,
length function, e.g. l(n)=n.

There may also be another length function l'(n)>l(n) and
H_s:{0,1}^{l'(n)} -> {0,1}^{l(n)}. In that case, we have a
fixed-length hash function.

Definition: A hash function Pi=(Gen,H) as above is *collision resistant* if
for all probabilistic polytime adversaries A one has
 
            Pr[Hash-coll_{A,Pi}(n)=1] = negl(n)

for some negligible function negl(n). Here, Hash-coll_{A,Pi}(n) is the
following experiment:

1) s <-Gen(n) /* s = "seed" */
2) A is given s and then outputs x,x'
3) The result of the experiment is 1 if x=/=x' and H_s(x)=H_s(x') and
0 otherwise.

Thus, the adversary must provide a collision given a key s and the
probability that it succeeds in doing so should be negligible. In
contrast, in the case of MACs the adversary does not get access to the
key.



Weaker notions of security
- - - - - - - - - - - - - -

In this context one may ask why being able to produce a collision
should be considered dangerous. Firstly, if a hash function is
collision resistant then it follows that it is also not possible to
forge a message x' =/=x such that H(x)=H(x') given x. So collision
resistance is certainly a "good enough" notion. However, one could
instead ask for the weaker property of "second preimage resistance":
it should be difficult to, given x, produce x'=/=x such that
H(x)=(x').

A typical application of hash functions is message integrity: If we
send someone a message x via some untrusted channel (which can be
modified by an adversary) and the hash value t=H(x) via a trusted
channel then the receiver can check that the message has not been
tampered with by checking that t=H(x') with x' being the received
(possibly modified) message.

While being able to produce an arbitrary collision may be harmless,
being able to find a collision in a given set of texts of size ~
2^{l(n)} can be very dangerous and is not covered by "second preimage
resistance": Consider the following scenario: we produce a letter L,
have its hash signed by a responsible person, and then send it to some
recipient. Now, it is very easy to produce functions
f,g:{0,1}^{l(n)}->{0,1}^* such that for each z the string f(z) is,
say, a different rejection letter, and g(z) is a different acceptance
letter. E.g. by introducing various switches dear/esteemed, Sir/Madam
or pleased/delighted etc. Or, if one is dealing with images rather
than letters by slightly altering intensity values. Then if, one can
produce z, z' such that H(f(z))=H(g(z')) one can have the hash
t=H(f(z)) signed and communicate along with g(z'), for which the hash
is also valid due to the collision. While this still requires
collisions of a special kind it is sufficiently close to arbitrary
collisions and for this reason one considers collision resistance as
the "gold standard" for hash functions.



Birthday paradox
- - - - - - - - -

Let us see, how many steps a brute force attack requires. In order to
do that, we (simplifyingly) assume that H_s is a random function. Let
us write H(x)=H_s(x). So we assume that H is drawn uniformly at random
from {0,1}^*->{0,1}^{l} where l=l(n). 

Clearly, if we fix x then the likelihood for x' drawn uniformly at
random to satisfy H(x)=H(x') is 2^{-l(n)} and thus, we need to try out
ca N/2 values (N=2^l) to get a 50% success probability. This also
holds, if x has been drawn at random. Things are different, if we
randomly choose a set of values S and then *search* for a collision
within S. Of course, if |S|>N then we find a collision with certainty,
but perhaps surprisingly |S|>=2*sqrt(N) ensures that the likelihood of
a collision within S is above 50%.

So, to find a collision one can choose a set S of size 2*sqrt(N), sort
it according to hash values and then search (in linear time) for the
desired collision. If one does not find one, repeat with new data.

Thus, for example if l(n)=64 then trying out 2^63 values will (with
probability >=50%) lead to a collision with a fixed left hand side,
but trying out 2^33 values will suffice to get an arbitrary collision
using the birthday paradox. While 2^63 steps can only be carried out
with special hardware resources, carrying out ~2^33 steps is trivial on
today's computers. One must bear this in mind when trying to estimate
"bits of security" of a hash function. Even assuming that the hash
function does not contain systematic security holes we will only get
one half of the hash size (size of the digest) many bits of security.



Improved birthday attack
- - - - - - - - - - - - -  

The implementation of the "birthday attack" sketched above takes not
only roughly O(sqrt(N)) steps but also O(sqrt(N)) memory which is a
more expensive resource. Indeed, storing 2^33 thirty-two byte words
requires 64TB. We now describe a version of the birthday attack that
runs in constant space.

If we choose x0:{0,1}^l uniformly at random then, assuming as we did,
that H is a random function then

S={x0,H(x0),H(H(x0)),H(H(H(x0))),...,H^{2*sqrt(N)-1}(x0)}

is also random and thus contains a collision with >=50%
likelihood. Let us write xi:=H^i(x0). Suppose that i,j are such that
xi=xj, but x_{i-1} =/= x_{j-1}, so x_{i-1},x_{j-1} form a
collision. Note that xi=H(x_{i-1}). Assuming i<j and writing j=i+d we
then also have x_i=x_{i+kd} for all k>=0 and thus x_{i+z}=x_{i+kd+z}
for all k>=0 and z>=0. If we choose k such that kd>=i then
x_{kd}=x_{2kd} follows with z=kd-i.

This suggests the following
algorithm for finding a collision with high likelihood:

x0 <- {0,1}^l /* uniformly at random */
x = x0;y = x0; 
for (r=1..4*sqrt(N)) {
   x = H(x); /* x=H^r(x0) */
   y = H(H(y)); /* y=H^{2r}(x0) */
   if (x==y) break;}
if (x!=y) start over with new x0; else
y=x;x=x0; /* y=H^{kd}(x0) */
if (x==x0) start over with new x0; else /* very unlikely */ 
for (i=0..r) do {
   x = H(x); /* x=H^i(x0) */
   y = H(y); /* y=H^{i+kd}(x0) */
   if (x==y) break; /* must happen before loop runs out */}
return H^{i-1}(x0),H^{i+kd-1}(x0); /* can optimize recomputation */



Merkle-Damgard Transform
- - - - - - - - - - - - -

The Merkle-Damgard Transform constructs a collision-resistant hash
function (Gen,H) from a given *fixed length* collision-resistant hash
function (Gen,h) with length 2*l(n), i.e. the h-hash is half as long
as the input. The key generation remains unchanged and H is as follows.

1) Given key s and input x:{0,1}^* divide message into blocks x1,x2,...,x_B
of length l=l(n) possibly padding the last block with zeroes. Set x_{B+1}=L. 

2) Set z0=0...0 /* l zeroes */ 

3) for i=1..B+1 do zi = h_s(z_{i-1} || x_i)

4) Output z_{B+1}


  x1           x2              x_B          L  
0 |  _____     |  _____         |  _____    |  _____
| |_|     |    |_|     |        |_|     |   |_|     |
|___| h_s |______| h_s |_...._____| h_s |_____| h_s |-----> H_s(x)
    |_____|      |_____|          |_____|     |_____|


Theorem: If (Gen,h) is a collision-resistant fixed length hash
function of length l'(n)=2*l(n) then (Gen,H) is a collision-resistant
hash function.

Proof: We first show that any collision in H_s yields a collision in
h_s.  Suppose that H_s(x)=H_s(x') and write x_1..x_B,L for the blocks
and length of x and x'_1..x'_B' L' for x'. Also write z_1..z_{B+1},
z'_1..z'_B' for the intermediate results. We have H_s(x)=h_s(z_B||L)
and H_s(x')=h_s(z'_B'||L'), so, if L=/=L' we already have produced the
desired collision in h_s. This also holds, if L=L' and z_B =/= z'_B
(notice that B=B' in this case). If L=L' and z_B=z'_B then, since
z_B=h_s(z_{B-1}||x_B) we get a collision unless x_B=x'_B. Continuing
in this way, we eventually find our collision since x_i =/= x'_i for
at least one i.

Now, suppose that A is an adversary against H_s. We construct an
adversary A' against h_s as follows. If A outputs x,x' we check
whether H_s(x)=H_s(x') and if so, construct the corresponding
collision h_s(y)=h_s(y') in h_s as described above and output
y,y'. Otherwise, we output 0,0. The likelihood for succeeding, i.e.,
for the output to be a h_s-collision is negligible by assumption and
thus the probability that A output a collision is negligible as
well. Notice that the computations we carried out as A' were clearly
in polynomial time. QED

The well-known and standardized cryptographic hash functions MD5,
SHA-1, SHA-2 use the Merkle-Damgard transform. The underlying
fixed-length hash function (called compression function) in this
context is built using the heuristic principles that were described in
Section 7 (Practical block ciphers. Principles: s/p- and Feistel
networks).  We remark here that MD5 and SHA-1 are no longer consider
sufficiently secure whereas SHA-2 is.

The latest standard SHA-3 which is not yet widely used but might
eventually supersede SHA-2 does not use Merkle-Damgard but instead
relies on the so-called *sponge construction*, see Wikipedia. 



MACs from hash functions
- - - - - - - - - - - - - 

A natural question is whether one can use hash functions also for the
purpose of authentication, i.e. MAC. First, we note that naive
attempts (which according to KL have been actually used in practice!)


do not work. Consider for example the definition Mac_k(x) = H_s(k||x)
where s is some public seed. If H_s was constructed with the
Merkle-Damgard transform from h_s then we can easily forge MACs as
follows: if t is the MAC of x then t'=h_s(t||L') is the
MAC of (x || |x|) (glossing over the padding).

Suppose that we are given a *fixed-length* MAC (Gen,Mac) capable of
authenticating messages of length l(n). Suppose furthermore, that we
have a collision-resistant hash function (Gen',H) which produces
hashes of length l(n). We can then build a MAC (Gen'',Mac'') for
arbitrary length messages as follows:

Gen''(n): k<-Gen(n); s<-Gen'(n);return (k,s) /* use some encoding of pairs */
Mac''_{(k,s)}(m): return Mac_k(H_s(m))

Theorem: Assuming that (Gen,Mac) is secure and (Gen',H) is
collision-resistant then (Gen'',Mac'') is secure.

Proof: Suppose that an adversary A asks questions Q and then succeeds
in forging a MAC t=Mac''_{(k,s)}(x), i.e., x is not in Q. If H_s(x) is
different from all the H_s(y) for y:Q then A has succeeded in forging
a MAC for Mac_k. Namely then t=Mac_k(H_s(x)) and H_s(x) was not
previously asked. If, however, H_s(x) is contained in {H_s(y) | y:Q}
then A has produced a collision. Since both events occur with
negligible probability the probability that A can forge a MAC is also
negligible. QED 

Suppose that H_s() above has been constructed using the Merkle-Damgard
construction from the fixed-length hash function h_s(). For seed s
define a fixed-length MAC by

                    Mac_k(m) = h_s(k||m)

We now make the assumption that h_s() is such that this is a secure
fixed-length MAC. Notice that the abovedescribed extension attack on
MACs of this sort does not apply due to the fixed length.

If we now apply the above hash-and-MAC construction then it turns out
that the arbitrary-length MAC obtained satisfies "essentially"

Mac''_{(k,s)}(m) = H_s(k||H_s(m))

because the last H_s degenerates to h_s due to the shortness of
H_s(m). The quoted "essentially" refers to the fact that for this
equation to hold exactly one needs to assume some conventions about
how exactly the padding in the Merkle-Damgard construction is
performed. We want to ignore these details here and refer to the
literature.


The HMAC construction
- - - - - - - - - - -

The widely used and standardised HMAC construction is essentially the
above except that another key is added to the message m prior to
invoking the inner hash function and that the two keys that are thus
needed are derived from a single one by XOR-ing with certain fixed
words ipad and opad:

HMAC_{(k,s)}(m) = H_s( (k+opad) || H_s((k+ipad) || m))

In practice, ipad and opad are chosen to be 00110110 and 01011100 each
repeated as often as necessary.

The additional inner keying with k+ipad allows one to weaken the
assumptions on the collision freeness of H_s(). The details of these
assumptions and their practical relevance are still subject to debate
(see a 2012 paper by Koblitz and Menezes); however, no practical
attack has been found to date and HMAC is considered secure in
practice.