A device appends a 32-bit CRC to each frame on a bus; why does a matching check field not prove the frame was unaltered?
answer
- no secret anywhere in the construction
- attacker recomputes rather than defeats
- linear, so differences add
- the patch needs only the flip mask
- noise has no plan; an attacker does
basics
~20 sA CRC is keyless and public, so anyone who edits the payload simply recomputes the field. Worse, it is linear: an attacker who knows only which bit positions they flipped can patch the check field without seeing the payload at all. It detects noise, not intent.
solid answer
~50 sTwo separate reasons, and the second is the sharper one. First, the generator and its parameters are published: an attacker who rewrites a frame runs the same division the sender would and writes the new remainder into the field, so a match proves only that *someone* computed it consistently. Second, the plain CRC is **linear over GF(2)**: `crc(a XOR b) = crc(a) XOR crc(b)` for equal-length inputs. So flipping a chosen set of bits changes the check field by a value that depends **only on the flip mask**, not on the payload. An attacker who cannot read an encrypted or unseen frame can still flip known positions and XOR the corresponding correction into the check field. Adding an initial register value or a final inversion makes the function affine, and the fixed offset cancels in the difference, so the patch still works. A CRC is a noise detector; tamper evidence needs a keyed authentication tag.
code
pseudocode · 8 lines# plain parameterisation: zero initial register, no final inversion
# crc(a XOR b) = crc(a) XOR crc(b) for equal-length inputs
mask = bits the attacker wants to flip, frame-length wide
frame_new = frame XOR mask
check_new = check XOR crc(mask) # payload never read
send(frame_new, check_new) # receiver divides, residue is cleango deeper
Hold on to the distinction: a frame check answers whether the bits were disturbed on the way, not whether a person changed them on purpose. Those are different questions with different mechanisms.
Explain both reasons. The construction is fully public so the field is recomputable, and it is linear, so the correction for a set of flipped bits depends only on which bits were flipped.
Show how the attack survives the defences teams reach for: a wider field, a secret generator, a non-zero initial register and a final inversion all leave the relative patch working. Then say what does the job and where it sits.
Own the wording in the specification. 'Integrity' covers two adversaries, and a design that names only one leaves reviewers and auditors reading the cheap noise check as a security control for years.
## What the check field was designed to do A frame check was designed against **noise**: thermal disturbance, a connector arcing, a motor switching beside the cable. Noise has no plan. It does not compute, it does not adapt, and it does not know what the check field means. Against that adversary a CRC is superb value — a few bits per frame and a shift register buy the guarantees on single bits and short bursts. An attacker is a different adversary entirely, and the mismatch is not a matter of degree. Nothing about widening the field closes it. ## Reason one: keyless means recomputable The generator polynomial, the width, the initial register, the final inversion, the covered byte range: all of it lives in the protocol specification, because both ends must agree or nothing ever validates. There is no secret anywhere in the construction. So an attacker who rewrites a payload runs the same division the sender ran and writes the fresh remainder into the field. The receiver's test passes, because the test asks "is this frame a multiple of the generator?" and the answer is honestly yes. **A matching check field proves that some party computed the field consistently with the bytes — not which party, and not that the bytes are the ones the sender intended.** ## Reason two: linearity, which is strictly worse The plain CRC — zero initial register, no final inversion — is a **linear map over GF(2)**: ``` crc(a XOR b) = crc(a) XOR crc(b) for a and b of equal length ``` Write the tampered frame as `frame XOR mask`, where `mask` marks the positions being flipped. Then the correct new check field is `check XOR crc(mask)`. The correction term depends on the **mask alone**. Consequences worth stating precisely: - The attacker does not need to know the payload. Positions are enough. - It follows that a frame the attacker cannot read is still patchable, as long as they know the frame's layout and length. - The work is one CRC over a mask, which is the same cost as computing any other CRC — there is no search, no collision hunting, no brute force. With a non-zero initial register or a final inversion the function becomes **affine** rather than linear: a fixed offset is added. The offset is identical for both frames of the same length and therefore cancels in the difference, so the patch is unchanged. Parameterisation buys interoperability hygiene, not resistance. ## What a passing check does and does not establish | Claim | Does a matching CRC support it? | | --- | --- | | The covered bytes probably did not pick up random noise on this hop | Yes, with the stated guarantees | | A burst shorter than the generator's degree did not corrupt them | Yes, guaranteed | | The frame came from the party you think it came from | No — the check is keyless | | The payload has not been deliberately modified | No — recomputation is free | | The payload was correct before the sender computed the field | No — it certifies the wire, not the source | ## "Then we will keep the generator secret" This recurs and it does not work. The construction is linear, so a handful of known frame-and-check pairs pins the polynomial down by solving over GF(2); an attacker who can observe traffic recovers it. Even if they could not, the scheme would stay linear, so relative edits remain patchable. A secret parameter in a linear function is not a key in any useful sense. ## Where a CRC still belongs None of this makes the field pointless — it makes it a component with a stated job: 1. Keep it at the link layer as the cheap filter that stops corrupt frames from reaching parsers and state machines at all. It costs almost nothing and catches the failure that actually happens most. 2. If the design needs to know that the bytes are the ones a trusted party sent, add a **keyed authentication tag** as a separate field with its own key management. That is a different mechanism with a different security argument, and it sits above the link check rather than replacing it. 3. Never let a specification, a review comment or a compliance table describe the frame check as protecting "integrity" without saying against which adversary. The word covers both jobs, and the whole failure lives in that ambiguity. ## The reviewer's question When someone proposes a check field as a defence, ask: *what does the attacker have to not know for this to work?* If the answer is "nothing — it is all in the specification", it is a noise detector, however wide the field.
- Why is linearity a sharper weakness than the check simply being keyless?Being keyless means an attacker who can read and rewrite a frame can recompute the field. Linearity removes the reading: because the correction term depends only on the flip mask, chosen bits in an unreadable frame can be flipped and the field repaired without ever learning the payload.
- Does making the generator polynomial secret help?No. The function is linear, so a small number of observed frame-and-check pairs determines the polynomial by solving over GF(2). Even before that succeeds the scheme stays linear, so relative edits remain patchable. A secret coefficient in a linear map is not a key.
- Should a design that needs tamper detection drop the CRC?No — keep it and add to it. The check field remains the cheapest way to stop noise-corrupted frames before they reach a parser, which is the failure that actually occurs on a bus. Tamper evidence is a separate keyed field with its own key management, layered above.
- A team reports that their check field 'has never failed in five years'. What does that tell you?Only that the link is quiet and the field is doing its noise-detection job. It says nothing about modification, because a tampered frame arrives with a perfectly consistent field and is indistinguishable from a clean one by this test.
saying these in an interview costs you the question
- Calls the check field a value an attacker cannot recompute
- Thinks a wider field, 64 bits say, resists forgery
- Believes the attacker must know the payload to repair the field
- Treats keeping the generator secret as a key
- Conflates detecting accidental corruption with detecting modification
- Assumes tampering requires finding a colliding frame