Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

@parent and @grandparent: The chunk CRC-32 does cover the chunk type.

> A four-byte CRC (Cyclic Redundancy Code) calculated on the preceding bytes in the chunk, including the chunk type field and chunk data fields, but not including the length field. -- https://www.w3.org/TR/2003/REC-PNG-20031110/#5Chunk-layout



Thank you for the correction!

It has been many years since I looked into this matter, and I seem to have forgotten that detail.

Digging through my sent box, to png-mng-misc, I see that knew that back in 2012!

I also wrote that most of the tools I checked didn't verify the CRC:

> I tried a PNG with an IDAT chunk with an invalid CRC on various software on the my Mac. Unless I messed up my testing, the desktop, email preview, OmniGraffle, and Pixen.app all used the chunk with the invalid CRC.

My thread was "I would like some insight about PNG CRC and other experience" in case someone wants to dig it up.

I was able to make an PNG with a length field which, if changed, would produce another PNG showing a different result. At least one of the PNGs had an invalid checksum, but was still displayed.

EDIT: I found a public copy of the thread at https://png-mng-misc.narkive.com/YgUoUekk/i-would-like-some-... .


For what it's worth, here's an example of two PNGs which differ by 1 bit, in the length.

http://dalkescientific.com/xo.png is a PNG for an "O" with two IDAT blocks:

  'IHDR' 13  (this is the chunk data size, excluding the 4 bytes of crc)
  'xtra' 0
  'IDAT' 1012  <-- this one is displayed
  'IDAT' 1090  <-- this one is ignored
  'IEND' 0
http://dalkescientific.com/xo_bad.png is an invalid PNG for an "X" with one IDAT block and with an invalid checksum:

  'IHDR' 13
  'xtra' 1024  <-- the extra length contains what was the first IDAT
  'IDAT' 1090  <-- this one is displayed
  'IEND' 0
Both display in Preview.app, Firefox, and Safari.

They differ only in byte offset 35, where one has a chr(0) and the other a chr(4):

  % python -c 'print(set(enumerate(open("xo.png", "rb").read()))\
                .symmetric_difference(\
                     set(enumerate(open("xo_bad.png", "rb").read()))))'
  {(35, 0), (35, 4)}
With a bit more work I could probably construct PNGs which are both valid, and which differ only by a bit.

It would require brute-forcing some chunk data so the CRCs would get a match.

I have some ideas on how to make it so there's only one valid (and different) IDAT block as well, but that's more than I can do in an evening.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: