Repository: python-pillow/Pillow

Problem statement:
PNG iCCP chunk profile compression type verification seems wrong
### What did you do?
Hello, I've been looking around at some PNG decoders and I think I've found a small bug in your iCCP chunk handling. 

### What did you expect to happen?
I would expect the library to read the iCCP chunk compression version byte correctly.

### What actually happened?
This library incorrectly assumes that the NUL terminator is the compression byte. This does not cause issues in practice, since the compression byte should currently always be 0, but I thought I'd report this anyways.

### What are your OS, Python and Pillow versions?

* OS: Windows
* Python: Python 3.11.8-1 (MSYS2)
* Pillow: 10.2.0-1 (MSYS2)

```python
from PIL import Image, ImageFile
import logging

logging.basicConfig(level=logging.DEBUG)

path = 'ArcTriomphe-iCCP-red-green-swap-mod.png'

with Image.open(path) as img:
    icc = img.info.get('icc_profile')
    print(icc)
    print(img)
```

Here are my 2 test files:
![ArcTriomphe-iCCP-red-green-swap](https://github.com/python-pillow/Pillow/assets/80441888/792cedba-3ab1-4d46-bfb9-87389e8337f7)
![ArcTriomphe-iCCP-red-green-swap-mod](https://github.com/python-pillow/Pillow/assets/80441888/65141037-59cf-4381-94d7-0d18824b0096)

The first one is a normal image with an embedded icc profile. The second is the same image, modified in a hex editor to change the compression version field from 0 to 1.

Here is the output from the first image:
```
DEBUG:PIL.PngImagePlugin:STREAM b'IHDR' 16 13
DEBUG:PIL.PngImagePlugin:STREAM b'iCCP' 41 460
DEBUG:PIL.PngImagePlugin:iCCP profile name b'Swapped red & green channel'
DEBUG:PIL.PngImagePlugin:Compression method 0
DEBUG:PIL.PngImagePlugin:STREAM b'PLTE' 513 759
DEBUG:PIL.PngImagePlugin:STREAM b'IDAT' 1284 3618
b'\x00\x00\x02\x84lcms\x02@\x00\x00mntrRGB XYZ \x07\xd5\x00\x08\x00\r\x00\x16\x00!\x00,acspSUNW\x00\x00\x00\x00none\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xd6\x00\x01\x00\x00\x00\x00\xd3-Greg^\xdf\x16\x9a"\xbb\xfd\xab\xb6@n\xf998\rz\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ndesc\x00\x00\x00\xfc\x00\x00\x00\x84cprt\x00\x00\x01\x80\x00\x00\x00\x90wtpt\x00\x00\x02\x10\x00\x00\x00\x14bkpt\x00\x00\x02$\x00\x00\x00\x14rXYZ\x00\x00\x028\x00\x00\x00\x14gXYZ\x00\x00\x02L\x00\x00\x00\x14bXYZ\x00\x00\x02`\x00\x00\x00\x14rTRC\x00\x00\x02t\x00\x00\x00\x0egTRC\x00\x00\x02t\x00\x00\x00\x0ebTRC\x00\x00\x02t\x00\x00\x00\x0edesc\x00\x00\x00\x00\x00\x00\x00\'"sGRB": red and green channels swapped\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0067 bytes of wasted space, courtesy of Apple Computer, dum de dum.\x00\x00text\x00\x00\x00\x00Copyright 2005 Greg Roelofs. Licensed under Creative Commons Attribution-NonCommercial, http://creativecommons.org/licenses/by-nc/2.5/ \x00XYZ \x00\x00\x00\x00\x00\x00\xf3Q\x00\x01\x00\x00\x00\x01\x16\xccXYZ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00XYZ \x00\x00\x00\x00\x00\x00b\x98\x00\x00\xb7\x86\x00\x00\x18\xdaXYZ \x00\x00\x00\x00\x00\x00o\xa1\x00\x008\xf5\x00\x00\x03\x90XYZ \x00\x00\x00\x00\x00\x00$\xa0\x00\x00\x0f\x84\x00\x00\xb6\xc9curv\x00\x00\x00\x00\x00\x00\x00\x01\x023\x00\x00'
<PIL.PngImagePlugin.PngImageFile image mode=P size=64x64 at 0x26C30640E10>
```

Here is the output from the second image:
```
DEBUG:PIL.PngImagePlugin:STREAM b'IHDR' 16 13
DEBUG:PIL.PngImagePlugin:STREAM b'iCCP' 41 460
DEBUG:PIL.PngImagePlugin:iCCP profile name b'Swapped red & green channel'
DEBUG:PIL.PngImagePlugin:Compression method 0
DEBUG:PIL.Image:Importing BlpImagePlugin
DEBUG:PIL.Image:Importing BmpImagePlugin
DEBUG:PIL.Image:Importing BufrStubImagePlugin
DEBUG:PIL.Image:Importing CurImagePlugin
DEBUG:PIL.Image:Importing DcxImagePlugin
DEBUG:PIL.Image:Importing DdsImagePlugin
DEBUG:PIL.Image:Importing EpsImagePlugin
DEBUG:PIL.Image:Importing FitsImagePlugin
DEBUG:PIL.Image:Importing FliImagePlugin
DEBUG:PIL.Image:Importing FpxImagePlugin
DEBUG:PIL.Image:Image: failed to import FpxImagePlugin: No module named 'olefile'
DEBUG:PIL.Image:Importing FtexImagePlugin
DEBUG:PIL.Image:Importing GbrImagePlugin
DEBUG:PIL.Image:Importing GifImagePlugin
DEBUG:PIL.Image:Importing GribStubImagePlugin
DEBUG:PIL.Image:Importing Hdf5StubImagePlugin
DEBUG:PIL.Image:Importing IcnsImagePlugin
DEBUG:PIL.Image:Importing IcoImagePlugin
DEBUG:PIL.Image:Importing ImImagePlugin
DEBUG:PIL.Image:Importing ImtImagePlugin
DEBUG:PIL.Image:Importing IptcImagePlugin
DEBUG:PIL.Image:Importing JpegImagePlugin
DEBUG:PIL.Image:Importing Jpeg2KImagePlugin
DEBUG:PIL.Image:Importing McIdasImagePlugin
DEBUG:PIL.Image:Importing MicImagePlugin
DEBUG:PIL.Image:Image: failed to import MicImagePlugin: No module named 'olefile'
DEBUG:PIL.Image:Importing MpegImagePlugin
DEBUG:PIL.Image:Importing MpoImagePlugin
DEBUG:PIL.Image:Importing MspImagePlugin
DEBUG:PIL.Image:Importing PalmImagePlugin
DEBUG:PIL.Image:Importing PcdImagePlugin
DEBUG:PIL.Image:Importing PcxImagePlugin
DEBUG:PIL.Image:Importing PdfImagePlugin
DEBUG:PIL.Image:Importing PixarImagePlugin
DEBUG:PIL.Image:Importing PngImagePlugin
DEBUG:PIL.Image:Importing PpmImagePlugin
DEBUG:PIL.Image:Importing PsdImagePlugin
DEBUG:PIL.Image:Importing QoiImagePlugin
DEBUG:PIL.Image:Importing SgiImagePlugin
DEBUG:PIL.Image:Importing SpiderImagePlugin
DEBUG:PIL.Image:Importing SunImagePlugin
DEBUG:PIL.Image:Importing TgaImagePlugin
DEBUG:PIL.Image:Importing TiffImagePlugin
DEBUG:PIL.Image:Importing WebPImagePlugin
DEBUG:PIL.Image:Importing WmfImagePlugin
DEBUG:PIL.Image:Importing XbmImagePlugin
DEBUG:PIL.Image:Importing XpmImagePlugin
DEBUG:PIL.Image:Importing XVThumbImagePlugin
<traceback omitted>
PIL.UnidentifiedImageError: cannot identify image file 'ArcTriomphe-iCCP-red-green-swap-mod.png'
```

As can be seen, PIL thinks the compression byte never changes. The issue is [here](https://github.com/python-pillow/Pillow/blob/380bc1766b7a3c23bde679be307ddfec1df252d0/src/PIL/PngImagePlugin.py#L395-L396), where there is an off-by-one error. `s[i]` should be `s[i + 1]` for the compression byte.


Hints:

Patch:
diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py
--- a/src/PIL/PngImagePlugin.py
+++ b/src/PIL/PngImagePlugin.py
@@ -392,8 +392,8 @@ def chunk_iCCP(self, pos, length):
         # Compressed profile    n bytes (zlib with deflate compression)
         i = s.find(b"\0")
         logger.debug("iCCP profile name %r", s[:i])
-        logger.debug("Compression method %s", s[i])
-        comp_method = s[i]
+        comp_method = s[i + 1]
+        logger.debug("Compression method %s", comp_method)
         if comp_method != 0:
             msg = f"Unknown compression method {comp_method} in iCCP chunk"
             raise SyntaxError(msg)


Test patch:
diff --git a/Tests/images/unknown_compression_method.png b/Tests/images/unknown_compression_method.png
new file mode 100644
Binary files /dev/null and b/Tests/images/unknown_compression_method.png differ
diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py
--- a/Tests/test_file_png.py
+++ b/Tests/test_file_png.py
@@ -619,6 +619,10 @@ def test_textual_chunks_after_idat(self) -> None:
         with Image.open("Tests/images/hopper_idat_after_image_end.png") as im:
             assert im.text == {"TXT": "VALUE", "ZIP": "VALUE"}
 
+    def test_unknown_compression_method(self) -> None:
+        with pytest.raises(SyntaxError, match="Unknown compression method"):
+            PngImagePlugin.PngImageFile("Tests/images/unknown_compression_method.png")
+
     def test_padded_idat(self) -> None:
         # This image has been manually hexedited
         # so that the IDAT chunk has padding at the end