Skip to main content

Encode an identifier into an image's pixels; verify a JSON record against any copy

Project description

Mememage

Mememage

Encode an identifier into an image's pixels; verify a JSON record against any copy.

Mememage writes a 2-pixel-tall bar into the bottom of an image. It carries an identifier (a short pointer to a JSON record you store anywhere) and a content hash (first 16 hex of SHA-256 over that record). verify recomputes the hash and compares it to the bar. Change any field, verification fails.

Core proves the record-to-image binding, by hash alone. It does not police the pixels (edit the image but leave the bar and it still verifies) and does not prove authorship (a signature's job, out of scope). Two keys stay outside the hash by design, signature and _-prefixed keys, and encode refuses both as your own field names.

The bar survives JPEG, resaves, screenshots, and re-uploads. Downscaling is the limit: images ≥ ~1000px wide survive a shrink to ~0.8× plus one recompression (59/60 real-image round-trips across three resamplers and JPEG q70 to q80). Past that, no promise.

pip install mememage                 # encode / decode / verify (Pillow included)
# pip install "mememage[encrypt]"    # adds AES-256 field encryption

Quickstart

import mememage

# encode: write the bar, build a record from your fields
result = mememage.encode("photo.png", {"title": "Morning fog", "by": "catmemes"})
result.identifier            # 'mememage-3dc5f03a747bb38e'  (derived from your fields)
result.save("photo.json")    # a record, stored or served separately

# decode: read the bar back out of any copy (PNG, JPEG, a screenshot)
bar = mememage.decode("photo.jpg")
bar.identifier, bar.content_hash

# verify: does a record match an image?
mememage.verify("photo.jpg", result.record)        # truthy if intact

Core does no networking. decode hands you an identifier; resolve the record wherever you kept it (a dict, a file, a DB, a URL), then verify.

Hash models (hash_version). Core implements the open model — it hashes every field except content_hash/signature, so whatever you put in the record is tamper-evident. encode stamps hash_version: "open". An application can define its own curated hash_version (a fixed inclusion set) that core doesn't implement; verify reports such a record as unsupported (Verification.supported == False) rather than a hash mismatch — it fails closed (bool() is False) but this is not tamper evidence. Verify those records with the application that defines the version (e.g. its own decoder). The CLI prints UNSUPPORTED and exits 3.

Inputs / outputs. encode, decode, and verify accept a path, bytes, a file-like, a PIL Image, or a numpy array (HEIC needs the [heic] extra). encode returns a barred Record.image and, given a destination, writes a lossless PNG (in place for a PNG path, a .png sibling otherwise, or out=<path/stream>); an in-memory input with no destination never touches disk. Record fields are yours (captions, credits, generation params, links) except a few reserved names: identifier, content_hash, hash_version, signature, encrypted_fields.

Encrypt private fields

Mark fields private to encrypt them (AES-256-GCM via PBKDF2) under a password. It still verifies without the password (the hash covers the ciphertext), and unlock reveals the fields. Passwords are never stored.

result = mememage.encode("photo.png", {"title": "Public", "gps": "45.5,-122.6"},
                         password="hunter2", private=["gps"])
mememage.verify("photo.png", result.record)              # matches, no password
mememage.unlock(result, "hunter2")["gps"]                # '45.5,-122.6'

Command line

mememage encode photo.png --field title="Morning fog" -o photo.json   # write the record
mememage decode photo.jpg --record photo.json                         # VERIFIED (0) / RECORD ALTERED (1) / UNSUPPORTED (3)
mememage decode photo.jpg                                             # read the identifier only

Without -o, the record lands beside the image as <identifier>.json. With --record, decode exits 0 on a match, 1 on a mismatch; without it, exit 0 just means a bar was read.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mememage-0.1.6.tar.gz (53.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mememage-0.1.6-py3-none-any.whl (39.5 kB view details)

Uploaded Python 3

File details

Details for the file mememage-0.1.6.tar.gz.

File metadata

  • Download URL: mememage-0.1.6.tar.gz
  • Upload date:
  • Size: 53.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mememage-0.1.6.tar.gz
Algorithm Hash digest
SHA256 bc46addf2e5cfd4f75b6b960e8fe85e7a3b8af52e257315809e88a10503bb46a
MD5 ee0b053b43d801f3f44c62d7b742676b
BLAKE2b-256 04f308b5055564b99247560ad9dda2c23d1814a79eafbef00d74491e6c176675

See more details on using hashes here.

Provenance

The following attestation bundles were made for mememage-0.1.6.tar.gz:

Publisher: publish.yml on sememtac/mememage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mememage-0.1.6-py3-none-any.whl.

File metadata

  • Download URL: mememage-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 39.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mememage-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 5da7a35af52fbf9895b609e1a435f527441560f02f63ded01741f3c14fd1e405
MD5 1312fe8fe3b02eb4dd9151e8ff5b8e48
BLAKE2b-256 d09babbb4d90b450a400616a32e5b22bdb6d032c511b5905bd851324512fbc73

See more details on using hashes here.

Provenance

The following attestation bundles were made for mememage-0.1.6-py3-none-any.whl:

Publisher: publish.yml on sememtac/mememage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page