Pixseal 1.0.0__pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Pixseal/__init__.py +34 -0
- Pixseal/imageSigner.py +342 -0
- Pixseal/imageValidator.py +399 -0
- Pixseal/keyInput.py +91 -0
- Pixseal/simpleImage.py +35 -0
- Pixseal/simpleImage_ext.pypy39-pp73-x86_64-linux-gnu.so +0 -0
- Pixseal/simpleImage_py.py +770 -0
- pixseal-1.0.0.dist-info/METADATA +285 -0
- pixseal-1.0.0.dist-info/RECORD +11 -0
- pixseal-1.0.0.dist-info/WHEEL +6 -0
- pixseal-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: Pixseal
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Image integrity and authenticity verification tool
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: cryptography>=41.0.0
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="https://raw.githubusercontent.com/kyj9447/Pixseal/main/assets/logo/Pixseal.png" width="200px"/>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
# Pixseal
|
|
14
|
+
### Prove what you published — and what you didn’t.
|
|
15
|
+
Pixseal is a Python-based **image integrity and authenticity verification tool**
|
|
16
|
+
designed to **detect whether an image has been modified since signing.**
|
|
17
|
+
|
|
18
|
+
Pixseal embeds a **cryptographically verifiable integrity seal** into an image in an
|
|
19
|
+
invisible manner. During verification, **any modification** — including editing,
|
|
20
|
+
filtering, cropping, resizing, re-encoding — will cause verification to **fail**.
|
|
21
|
+
|
|
22
|
+
Pixseal signs the payload and image hash with an RSA private key. Verification uses
|
|
23
|
+
the matching RSA public key or an X.509 certificate that contains it.
|
|
24
|
+
|
|
25
|
+
Pixseal is not a visual watermarking or branding tool.
|
|
26
|
+
The watermark exists solely as a **means to achieve strict, deterministic image
|
|
27
|
+
tamper detection**.
|
|
28
|
+
Pixseal prioritizes tamper sensitivity over robustness against intentional adversarial manipulation.
|
|
29
|
+
|
|
30
|
+
- GitHub: https://github.com/kyj9447/Pixseal
|
|
31
|
+
- Changelog: https://github.com/kyj9447/Pixseal/blob/main/CHANGELOG.md
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
- **Image Integrity Verification**
|
|
35
|
+
- Cryptographically proves that an image remains in its original, unmodified state
|
|
36
|
+
- Detects single-pixel changes with deterministic verification results
|
|
37
|
+
|
|
38
|
+
- **Tamper Detection**
|
|
39
|
+
- Detects image modifications such as:
|
|
40
|
+
- editing
|
|
41
|
+
- filters and color adjustments
|
|
42
|
+
- cropping and resizing
|
|
43
|
+
- re-encoding and recompression
|
|
44
|
+
- pixel-level changes
|
|
45
|
+
|
|
46
|
+
- **Invisible Integrity Seal**
|
|
47
|
+
- Embeds verification data without any visible watermark
|
|
48
|
+
- Preserves the original visual appearance of the image
|
|
49
|
+
|
|
50
|
+
- **RSA Signatures + Certificate Support**
|
|
51
|
+
- Signs payloads and image hashes with an RSA private key
|
|
52
|
+
- Validates with RSA public keys or X.509 certificates (PEM/DER)
|
|
53
|
+
|
|
54
|
+
- **Flexible Key Inputs**
|
|
55
|
+
- Accepts key/cert objects, PEM/DER bytes, or file paths
|
|
56
|
+
|
|
57
|
+
- **Fully Local & Offline**
|
|
58
|
+
- No external servers or network dependencies
|
|
59
|
+
- Pure Python implementation
|
|
60
|
+
|
|
61
|
+
- **Lossless Format Support**
|
|
62
|
+
- Supports PNG and BMP (24-bit) images
|
|
63
|
+
- Lossy formats (e.g., JPEG, WebP) are intentionally excluded to preserve integrity guarantees
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install Pixseal
|
|
69
|
+
# or for local development
|
|
70
|
+
pip install -e ./pip_package
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Python 3.8+ is required. Wheels published to PyPI already include the compiled
|
|
74
|
+
Cython extension, so `pip install Pixseal` automatically selects the right build
|
|
75
|
+
for your operating system and CPU.
|
|
76
|
+
|
|
77
|
+
### Building the Cython extension
|
|
78
|
+
|
|
79
|
+
If you cloned the repository (or downloaded the source), run the helper script
|
|
80
|
+
to compile the `simpleImage_ext` extension for your environment:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/kyj9447/Pixseal.git
|
|
84
|
+
cd Pixseal
|
|
85
|
+
python3 -m pip install -r requirements.txt
|
|
86
|
+
./compile_extension.sh
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This command regenerates the C source via Cython and invokes your local C
|
|
90
|
+
compiler (`clang` or `gcc`) to produce `pip_package/Pixseal/simpleImage_ext*.so`.
|
|
91
|
+
You still need a working build toolchain (`gcc`/`clang` and Python headers)
|
|
92
|
+
installed through your OS package manager. If you skip this step, Pixseal falls
|
|
93
|
+
back to the pure Python implementation, which works but is significantly slower.
|
|
94
|
+
|
|
95
|
+
## Quick start
|
|
96
|
+
|
|
97
|
+
### Sign an image
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from Pixseal import signImage
|
|
101
|
+
|
|
102
|
+
signed = signImage(
|
|
103
|
+
imageInput="assets/original.png",
|
|
104
|
+
payload="AutoTest123!",
|
|
105
|
+
private_key="assets/CA/pixseal-dev-final.key",
|
|
106
|
+
)
|
|
107
|
+
signed.save("assets/signed_original.png")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- The payload is looped if it runs out before the image ends, so even small files carry the full sentinel/payload/end pattern.
|
|
111
|
+
|
|
112
|
+
### Validate a signed image
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from Pixseal import validateImage
|
|
116
|
+
|
|
117
|
+
report = validateImage(
|
|
118
|
+
imageInput="assets/signed_original.png",
|
|
119
|
+
publicKey="assets/CA/pixseal-dev-final.crt", # cert or public key
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
print(report["verdict"])
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Key and certificate inputs
|
|
126
|
+
|
|
127
|
+
Pixseal accepts multiple input formats so you can keep the calling code minimal.
|
|
128
|
+
|
|
129
|
+
- `signImage(..., private_key=...)` accepts:
|
|
130
|
+
- `RSAPrivateKey`
|
|
131
|
+
- PEM/DER bytes (`bytes`, `bytearray`, `memoryview`)
|
|
132
|
+
- file path (`str` or `Path`)
|
|
133
|
+
|
|
134
|
+
- `validateImage(..., publicKey=...)` accepts:
|
|
135
|
+
- `RSAPublicKey`
|
|
136
|
+
- `x509.Certificate`
|
|
137
|
+
- PEM/DER bytes (`bytes`, `bytearray`, `memoryview`)
|
|
138
|
+
- file path (`str` or `Path`)
|
|
139
|
+
|
|
140
|
+
If a certificate is provided, Pixseal extracts the embedded RSA public key and
|
|
141
|
+
verifies the signatures. Certificate chain validation is the responsibility of
|
|
142
|
+
the calling application.
|
|
143
|
+
|
|
144
|
+
## Payload structure
|
|
145
|
+
|
|
146
|
+
Pixseal embeds a compact JSON payload with the signed data and image hash:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"payload": "AutoTest123!",
|
|
151
|
+
"payloadSig": "BASE64_SIGNATURE",
|
|
152
|
+
"imageHash": "SHA256_HEX",
|
|
153
|
+
"imageHashSig": "BASE64_SIGNATURE"
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- `payload`: user-provided text
|
|
158
|
+
- `payloadSig`: RSA signature of `payload` (Base64)
|
|
159
|
+
- `imageHash`: SHA256 hex digest computed over the signed image buffer.
|
|
160
|
+
- `imageHashSig`: RSA signature of `imageHash` (Base64)
|
|
161
|
+
|
|
162
|
+
## Embedded sequence layout
|
|
163
|
+
|
|
164
|
+
Pixseal writes the following newline-delimited sequence into the image:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
<START-VALIDATION signature>
|
|
168
|
+
<payload JSON>
|
|
169
|
+
<payload JSON>
|
|
170
|
+
<payload JSON>
|
|
171
|
+
<payload JSON>
|
|
172
|
+
...(Repeated until it fills the entire image)...
|
|
173
|
+
<payload JSON> # truncated tail (prefix of payload JSON)
|
|
174
|
+
<END-VALIDATION signature>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
During extraction, Pixseal deduplicates the sequence and typically returns four
|
|
178
|
+
lines in order: start signature, full payload JSON, truncated payload prefix,
|
|
179
|
+
and end signature.
|
|
180
|
+
|
|
181
|
+
For a valid image, deduplication results in four extracted
|
|
182
|
+
lines.
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
<START-VALIDATION signature>
|
|
186
|
+
<payload JSON>
|
|
187
|
+
<payload JSON> # truncated tail
|
|
188
|
+
<END-VALIDATION signature>
|
|
189
|
+
```
|
|
190
|
+
<sub>※ In rare edge cases, the truncated payload prefix may be absent, in which
|
|
191
|
+
case only three lines are returned.</sub>
|
|
192
|
+
|
|
193
|
+
## Validation output
|
|
194
|
+
|
|
195
|
+
Validation Report
|
|
196
|
+
|
|
197
|
+
- `lengthCheck`
|
|
198
|
+
- `length` : Length of deduplication result array.
|
|
199
|
+
- `result` : True for 4 or 3 (valid deduplication cases).
|
|
200
|
+
- `tailCheck`
|
|
201
|
+
- `full` : Full payload intact. (output truncated)
|
|
202
|
+
- `tail` : Truncated payload intact. (output truncated)
|
|
203
|
+
- `result` : True when the full and truncated payload portions match.
|
|
204
|
+
- `startVerify` : Verification result of the first SIG against "START-VALIDATION"
|
|
205
|
+
- `endtVerify` : Verification result of the last SIG against "END-VALIDATION"
|
|
206
|
+
- `payloadVerify` : Verification result of the "payload" against "payloadSig"
|
|
207
|
+
- `imageHashVerify` : Verification result of the "imageHash" against "imageHashSig"
|
|
208
|
+
- `imageHashCompareCheck`
|
|
209
|
+
- `extractedHash` : Value of "imageHash" from extracted payload
|
|
210
|
+
- `computedHash` : Image hash computed directly from the image
|
|
211
|
+
- `result` : True when extractedHash and computedHash are identical
|
|
212
|
+
- `verdict` : True when all validation checks pass.
|
|
213
|
+
|
|
214
|
+
## CLI demo script
|
|
215
|
+
|
|
216
|
+
`python testRun.py` offers an interactive flow:
|
|
217
|
+
|
|
218
|
+
Before the menu, it prompts for the SimpleImage backend
|
|
219
|
+
(Enter/1=cython, 2=python fallback) and sets `PIXSEAL_SIMPLEIMAGE_BACKEND`.
|
|
220
|
+
|
|
221
|
+
1. Choose **1** to sign an image. It reads `assets/original.png` and writes `assets/signed_original.png`.
|
|
222
|
+
2. Choose **2** to validate. It reads `assets/signed_original.png` and prints the validation report.
|
|
223
|
+
3. Choose **3** to run the failure test. It reads `assets/currupted_signed_original.png`.
|
|
224
|
+
4. Choose **4** to benchmark performance (sign + validate with timings).
|
|
225
|
+
5. Choose **5** to test signing and validation using in-memory bytes.
|
|
226
|
+
6. Choose **6** to run the optional line-profiler demo.
|
|
227
|
+
7. Choose **7** to run validation multi-pass tests.
|
|
228
|
+
|
|
229
|
+
Option **6** requires the optional dependency `line_profiler` and must be run via
|
|
230
|
+
`kernprof -l testRun.py` so that `builtins.profile` is provided. Without
|
|
231
|
+
`line_profiler` installed the script will continue to work, but the profiling
|
|
232
|
+
option will display an informative message instead of running.
|
|
233
|
+
|
|
234
|
+
## API reference
|
|
235
|
+
|
|
236
|
+
| Function | Description |
|
|
237
|
+
| --- | --- |
|
|
238
|
+
| `signImage(imageInput, payload, private_key)` | Loads a PNG/BMP from a filesystem path or raw bytes, injects `payload` plus sentinels, and signs the payload/hash using the RSA private key. Returns a `SimpleImage` that you can `save()` or `saveBmp()`. |
|
|
239
|
+
| `validateImage(imageInput, publicKey)` | Reads the hidden bit stream from a path or raw bytes, rebuilds the payload JSON, verifies signatures and the computed image hash, and returns a validation report. Accepts RSA public keys or X.509 certificates. |
|
|
240
|
+
|
|
241
|
+
## Examples
|
|
242
|
+
|
|
243
|
+
| Original | Signed (`AutoTest123!`) |
|
|
244
|
+
| --- | --- |
|
|
245
|
+
| <img src="https://raw.githubusercontent.com/kyj9447/Pixseal/main/assets/original.png" width="400px"/> | <img src="https://raw.githubusercontent.com/kyj9447/Pixseal/main/assets/signed_original.png" width="400px"/> |
|
|
246
|
+
|
|
247
|
+
Validation output (success):
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
Validation Report
|
|
251
|
+
|
|
252
|
+
{'lengthCheck': {'length': 4, 'result': True},
|
|
253
|
+
'tailCheck': {'full': '{"payload":"AutoTest...lgu9lUM+s7OHUZywYqYYOYIFVTWCmq...',
|
|
254
|
+
'tail': '{"payload":"AutoTest...lgu9lUM+s7',
|
|
255
|
+
'result': True},
|
|
256
|
+
'startVerify': True,
|
|
257
|
+
'endtVerify': True,
|
|
258
|
+
'payloadVerify': True,
|
|
259
|
+
'imageHashVerify': True,
|
|
260
|
+
'imageHashCompareCheck': {'extractedHash': '2129e43456029f39b20bbe96340dce6827c0ad2288107cb92c0b92136fec48d6',
|
|
261
|
+
'computedHash': '2129e43456029f39b20bbe96340dce6827c0ad2288107cb92c0b92136fec48d6',
|
|
262
|
+
'result': True},
|
|
263
|
+
'verdict': True}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
| Corrupted after signing |
|
|
267
|
+
| --- |
|
|
268
|
+
| <img src="https://raw.githubusercontent.com/kyj9447/Pixseal/main/assets/currupted_signed_original.png" width="400px"/> |
|
|
269
|
+
|
|
270
|
+
Validation output (failure):
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
Validation Report
|
|
274
|
+
|
|
275
|
+
{'lengthCheck': {'length': 31, 'result': False},
|
|
276
|
+
'tailCheck': {'result': 'Not Required'},
|
|
277
|
+
'startVerify': True,
|
|
278
|
+
'endtVerify': True,
|
|
279
|
+
'payloadVerify': True,
|
|
280
|
+
'imageHashVerify': True,
|
|
281
|
+
'imageHashCompareCheck': {'extractedHash': '68d500c751dfa298d55dfc1cd2ab5c9f43ec139f02f6a11027211c4d144c2870',
|
|
282
|
+
'computedHash': '43fd2108f5aa16045f4b64d70a0ce05991043cba6878f66d82abd3e7edb9d51e',
|
|
283
|
+
'result': False},
|
|
284
|
+
'verdict': False}
|
|
285
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pixseal-1.0.0.dist-info/WHEEL,sha256=VbVV36H_sGWs2wDlSVuZCfrfUlk3NfehwD_wG4zKlug,162
|
|
2
|
+
pixseal-1.0.0.dist-info/top_level.txt,sha256=q35ICL7vJyo5hOpL4YbL5t_g4297gwUPIuLy82kgL3s,8
|
|
3
|
+
pixseal-1.0.0.dist-info/METADATA,sha256=HQJKCdanhScu8WEww9YeQLQFzyVxVyUiMQwHQjPDnCY,10214
|
|
4
|
+
pixseal-1.0.0.dist-info/RECORD,,
|
|
5
|
+
Pixseal/simpleImage_py.py,sha256=6U44kNUP-ygcf7Sz91fjG99CrVWcROIC4tneQXDdbOA,27583
|
|
6
|
+
Pixseal/keyInput.py,sha256=p49Y_ZKRMxF8aU53eB6GGvos-AaI_m5d5z5z1reMgmU,3523
|
|
7
|
+
Pixseal/imageValidator.py,sha256=aRBfEz6eHJlhExqMyw83mCnx_I05yTD217oeJzffPpo,11607
|
|
8
|
+
Pixseal/simpleImage.py,sha256=dkpMEhkT1z0pZOgy-AbgYlF9GC73eM2WGaYb5FiGNpk,1234
|
|
9
|
+
Pixseal/imageSigner.py,sha256=fWQ1o8MAOgRihT1rYKdht0W8eUoA8qJJ02YDcGpch-U,10636
|
|
10
|
+
Pixseal/__init__.py,sha256=J66ptDDimT5fe8AZCqeJv2xLUl-rbPqLFGJlMTsuwA8,646
|
|
11
|
+
Pixseal/simpleImage_ext.pypy39-pp73-x86_64-linux-gnu.so,sha256=i_L0LuOmWbhW-2HUVfmqX228jVEJt1RWwmgiHWmnJmw,320472
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Pixseal
|