Thread (15 messages) flat view 15 messages, 3 authors, 2014-12-09

Re: Where exactly will arch_fast_hash be used

From: George Spelvin <hidden>
Date: 2014-12-07 21:33:58
Also in: lkml

On Sun, 2014-12-07 at 15:06 +0100, Hannes Frederic Sowa wrote:
In case of openvswitch it shows a performance improvment. The seed
parameter could be used as an initial biasing of the crc32 function, but
in case of openvswitch it is only set to 0.
NACK.

This is the Fatal Error in thinking that Herbert was warning about.
The seed parameter doesn't affect CRC32 collisions *at all* if the inputs
are the same size.

For fixed-size inputs, a non-zero seed is equivalent to XORing a
constant into the output of the CRC computation.


for *different* sized inputs, a non-zero seed detects zero-padding
better than a zero one, but *which* non-zero value is also irrelevant;
all-ones is the traditional choice because it's simplest in hardware.


A CRC is inherently linear.  CRC(a^b) = CRC(a) ^ CRC(b).  This makes
them easy to analyze mathematically and gives them a number of nice
properties for detecting hardware corruption.

But that same simplicity makes it *ridiculously* easy to generate
collisions if you try.


One way of looking at a CRC is to say that each bit in the input
has a CRC.  The CRC of a message string is just the XOR of the CRCs
of the individual bits that are set in the message.

Now, a CRC polynomial is chosen so that all of the bits of a
message have different CRCs.  Obviously, there's a limit: when the
message is 2^n bits long, it's not possible for all the bits to
have different, non-zero n-bit CRCs.

But a CRC is a really efficient way of assigning different bit patterns
to different input bits up to that limit.

(Something like CRC32c is also chosen so that, for messages up to a
reasonable length, no 3-bit, 4-bit, etc. combinations have CRCs that
XOR to zero.)


But, and this might be what Herbert was trying to say and I was
misunderstanding, if you then *truncate* that CRC, the CRCs of the
message bits lose that uniqueness guarantee.  They're just pseudorandom
numbers, and a CRC loses its special collision-resistance properties.

It's just an ordinary random hash, and thanks to the birthday paradox,
you're likely to find two bits whose CRCs agree in any particular 8 bits
within roughly sqrt(2*256) or 22 bits.

Here are a few such collisions for the least significant 8 bits of CRC32c:

Msg1	CRC32c		Msg2	CRC32c		Match
1<<11	3fc5f181	1<<30	bf672381	81
1<<12	9d14c3b8	1<<31	dd45aab8	b8
1<<5	c79a971f	1<<44	6006181f	1f
1<<15	13a29877	1<<45	b2f53777	77

There's nothing special about the lsbits of the CRC.
Within 64 bits, the most significant 8 bits have it worse:

1<<5	c79a971f	1<<17	c76580d9	c7
1<<6	e13b70f7	1<<18	e144fb14	e1
1<<19	70a27d8a	1<<38	7022df58	70
1<<20	38513ec5	1<<39	38116fac	38
1<<13	4e8a61dc	1<<52	4e2dfd53	4e
1<<23	a541927e	1<<53	a5e0c5d1	a5


Now, I'd like to stress that this collision rate is no worse than any
*other* hash function.  A truncated CRC loses its special resistance to
the birthday paradox (you'd have been much smarter to use 8-bit CRC),
but it doesn't become especially bad.  A truncated SHA-1 will have
coillisions just as often.

The concern with a CRC is that, once you've found one collision, you've
found a huge number of them.  Just XOR the bit pattern of your choice
into both of the colliding messages, and you have a new collision.


For another example, if you consider the CRC32c of all possible 1-byte
messages *and then take only the low byte*, there are only 128 possible
values.

It turns out that the byte 0x5d has a CRC32c of 0xee0d9600.  This ends
in 00, so if I XOR 0x5d into anything, the low 8 bits of the CRC
don't change.

Likewise, the message "23 00" has a CRC32c of 0x00ee0d96.  So you can
XOR 0x23 into the second-last byte of anything, and the high 8 bits of
the CRC don't change.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help