Encrypted Image Watermark Injector / Validator
Project description
Pixseal
Prove what you published — and what you didn’t.
Pixseal is a Python-based image integrity and authenticity verification tool designed to detect whether an image has been modified since signing.
Pixseal embeds a cryptographically verifiable integrity seal into an image in an invisible manner. During verification, any modification — including editing, filtering, cropping, resizing, re-encoding — will cause verification to immediately fail.
If even a single pixel is altered after signing, Pixseal will detect it.
Pixseal is not a visual watermarking or branding tool.
The watermark exists solely as a means to achieve strict, deterministic image
tamper detection.
Pixseal prioritizes tamper sensitivity over robustness against intentional adversarial manipulation.
- GitHub: https://github.com/kyj9447/Pixseal
- Changelog: https://github.com/kyj9447/Pixseal/blob/main/CHANGELOG.md
Features
-
Image Integrity Verification
- Cryptographically proves that an image remains in its original, unmodified state
- Detects single-pixel changes with deterministic verification results
-
Tamper Detection
- Detects all forms of image modification, including:
- editing
- filters and color adjustments
- cropping and resizing
- re-encoding and recompression
- pixel-level changes
- Detects all forms of image modification, including:
-
Invisible Integrity Seal
- Embeds verification data without any visible watermark
- Preserves the original visual appearance of the image
-
RSA-Based Encryption (Optional)
- Supports RSA public/private key encryption for embedded verification data
- Allows separation of signing and verification roles
-
Verification & Extraction
- Payloads may be partially or fully extractable even after modification
- Automatically fails verification when tampering is detected
-
Fully Local & Offline
- No external servers or network dependencies
- Pure Python implementation
-
Lossless Format Support
- Supports PNG and BMP (24-bit) images
- Lossy formats (e.g., JPEG, WebP) are intentionally excluded to preserve integrity guarantees
Installation
pip install Pixseal
# or for local development
pip install -e ./pip_package
Python 3.8+ is required. The only runtime dependency is cryptography>=41.0.0.
Usage
Sign an image
from Pixseal import signImage
result = signImage(
imageInput="original.png", # accepts a file path or raw PNG/BMP bytes
hiddenString="!Validation:kyj9447@mailmail.com",
publicKeyPath="RSA/public_key.pem", # omit for plain-text embedding
)
result.save("signed_original.png")
- The payload is looped if it runs out before the image ends, so even small files carry the full sentinel/payload/end pattern.
- When
publicKeyPathis omitted, the payload remains plain text.
Validate and (optionally) decrypt
from Pixseal import validateImage
report = validateImage(
imageInput="signed_original.png", # accepts a file path or raw PNG/BMP bytes
privKeyPath="RSA/private_key.pem", # omit for plain-text payloads
)
print(report["extractedString1"])
print(report["validationReport"])
validateImage returns:
{
"extractedString1": "<payload or encrypted blob>",
"extractedString2": "<truncated payload or encrypted blob>",
"validationReport": {
"arrayLength": 4,
"lengthCheck": True,
"startCheck": True,
"endCheck": True,
"isDecrypted": True,
"tailCheckResult": True,
"verdict": True,
# decryptSkipMessage when a decrypt request was skipped
}
}
CLI demo script
python testRun.py offers an interactive flow:
- Choose 1 to sign an image. It reads
original.png, asks for a payload (default!Validation:kyj9447@mailmail.com), optionally encrypts withRSA/public_key.pem, and writessigned_<name>.png. - Choose 2 to validate. It reads
signed_original.png, optionally decrypts withRSA/private_key.pem, and prints both the extracted string and verdict. - Choose 3 to benchmark performance. It reads
original.png, encrypts it withRSA/public_key.pem, and writessigned_original.png, printing the elapsed signing time. Then it readssigned_original.png, performs extraction/decryption/validation, and prints the elapsed validation time along with the total elapsed time. - Choose 4 to test signing and validation with file-path input option.
- Choose 5 to test signing and validation with byte-stream input option.
Key management
Generate a test RSA pair (PKCS#8) with OpenSSL:
openssl genpkey -algorithm RSA -out RSA/private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in RSA/private_key.pem -out RSA/public_key.pem
Point publicKeyPath / privKeyPath to these files.
API reference
| Function | Description |
|---|---|
signImage(imageInput, hiddenString, publicKeyPath=None) |
Loads a PNG/BMP from a filesystem path or raw bytes, injects hiddenString plus sentinels, encrypting each chunk when publicKeyPath is provided. Returns a SimpleImage that you can save() or saveBmp(). |
validateImage(imageInput, privKeyPath=None) |
Reads the hidden bit stream from a path or raw bytes, splits by newlines, deduplicates, optionally decrypts each chunk (Base64 indicates ciphertext), and returns the payload plus a validation report. |
Examples
| Original | Signed (!Validation:kyj9447@mailmail.com) |
|---|---|
Validation output excerpt:
[Validate] verdict: True
[Validate] extracted string: !Validation:kyj9447@mailmail.com
[Validate] decrypted with private key: RSA/private_key.pem
Validation Report
{'extractedString1': '!Validation:kyj9447@mailmail.com',
'extractedString2': 'DMnWAzbd6NFycGAxcPkzzmGjL33WXovG...',
'validationReport': {'arrayLength': 4,
'endCheck': True,
'isDecrypted': True,
'lengthCheck': True,
'startCheck': True,
'tailCheckResult': True,
'verdict': True}}
(When encrypted, each line appears as Base64 until decrypted with the RSA private key.)
| Corrupted after signing |
|---|
Validation output excerpt:
...
string argument should contain only ASCII characters
string argument should contain only ASCII characters
string argument should contain only ASCII characters
[Validate] verdict: False
[Validate] extracted string: !Validation:kyj9447@mailmail.com
[Validate] decrypted with private key: RSA/private_key.pem
Validation Report
{'extractedString1': '!Validation:kyj9447@mailmail.com',
'extractedString2': 'hh78IWEsRgfTWMw3Rg02hTnCdErjx0O4...',
'validationReport': {'arrayLength': 400,
'decryptSkipMessage': 'Skip decrypt: payload was plain '
'or corrupted text despite decrypt '
'request.',
'endCheck': True,
'isDecrypted': True,
'lengthCheck': False,
'startCheck': True,
'tailCheckResult': 'Not Required',
'verdict': False}}
Related projects
https://github.com/kyj9447/imageSignerCamera
- Mobile camera that signs images on capture:
- Server-side validator that decrypts and verifies payloads.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pixseal-0.1.6.tar.gz.
File metadata
- Download URL: pixseal-0.1.6.tar.gz
- Upload date:
- Size: 13.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27ce62faaf7331a97e1a3eacd02c7fa28bfebc86c765f27ad452d73b6c0e95e7
|
|
| MD5 |
01ecba96121c11910b594b411f78579b
|
|
| BLAKE2b-256 |
6acd0ad1da7643dcd21a93d2647c4e0e5587ae55fb4949156bd56e52a2b604e7
|
File details
Details for the file pixseal-0.1.6-py3-none-any.whl.
File metadata
- Download URL: pixseal-0.1.6-py3-none-any.whl
- Upload date:
- Size: 11.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a05ae1e836ea19901368b04b796412b44b6ef4aa75b40461a4710b59133ddedc
|
|
| MD5 |
6fdc5eb4f01673b7827c069697d4295a
|
|
| BLAKE2b-256 |
e9398ca9a115903d4e9cf22e0a2061f984e0f80b4e28170d9e3ae4d70800c4ae
|