Other
Social DRM
A tool that engraves a QR mark into a document, so a leaked copy traces back to its source.
- Year
- 2022
- Role
- Solo — concept, PDF pipeline, forensic recovery
- Status
- Proof of concept
TL;DR
Social DRM does not lock a PDF to a reader app — the file stays an ordinary PDF that opens anywhere a PDF does. Instead, each user's copy gets QR marks scattered through its blank space in a pattern unique to that copy, faint enough not to hurt readability. Each mark carries a code that only the issuer, holding the secret key, can match back to the user — so a leaked copy names its owner.
What can you do with it?
Mark every copy with its own user, then find the user behind any leaked copy.
- Place QR marks in blank space — every empty area on each page is found, and as many QR marks as you choose are scattered through it at random, without touching the text.
- Identify the owner — upload a leaked document, or just the code read from one of its QRs, and get back the user it was issued to.


Why it matters
Hard DRM locks a book to one app, and the reader who paid carries the cost. Watermarking instead of locking already works in industry — the gap is that visible watermarks do not survive the way books actually leak.
- Tor Books dropped DRM in 2012 without the predicted collapse; O'Reilly and Pottermore sell watermarked, unlocked files.
- A footer watermark catches a whole-file torrent, but not the common leak: two pages screenshotted into a group chat. A crop carries no footer.
- Publishers are wary of anything called DRM. They need measured evidence, not assurances — so this project is built around an attack bench.
How it works
Four steps, from a clean PDF to a traceable copy.
- Find empty space — a summed-area table over the page's ink answers "is this patch blank?" in O(1), so every candidate slot is checked — thousands on a typical page.
- Choose slots from the user's token — placement is seeded by the copy's own token, so every copy's layout is different, yet the issuer can recompute it.
- Draw the QR as vector shapes at 85% grey — not as an image, so "delete every image in the PDF" does not remove it.
- Recover — decode the screenshot (raw, then contrast-stretched, then upscaled) and match the token to the user it was issued to. The QR holds only an 80-bit HMAC, so scanning it alone reveals nothing.

Implementation details
- Python, PyMuPDF for PDF geometry and drawing, zxing-cpp for decoding, NumPy for the ink mask, FastAPI for the live demo.
- Tokens are Base32 — a subset of QR alphanumeric mode — which keeps every symbol at the smallest QR version.
- Standard QR v1 over Micro QR: Micro is ~28% smaller but lost symbols at awkward render scales in calibration.
- If every symbol is destroyed, three smudges at reproducible coordinates still narrow the registry down, because placement is recomputable.
- The demo UI and the library share one code path: embedding yields progress events that the server streams over SSE.
- The signing secret is read from the environment, never from the command line.
Applications
- Paid knowledge and subscription content — mark each subscriber's copy of reports, newsletters and courses before it is shared.
- Internal confidential documents — board papers, strategy decks and other material sent inside a company.
- Ebook sales — personalised DRM lets publishers sell open, readable files with more confidence.
Future directions
- Survive targeted removal — computer vision can now find these marks and erase them cleanly. The next step is an additional layer of cryptographic marking that such a tool cannot see or strip out.
Behind the project — how it started and how it changed
The problem
I wanted to buy an ebook and read it wherever I liked. Hard DRM does not allow that: the file is bound to one reader app, it breaks when you change device, and the person it inconveniences most is the one who paid. Publishers keep it anyway, because the alternative on offer is nothing at all.
Social DRM is the middle path the industry already knows works — Tor Books dropped DRM entirely in 2012 without the collapse everyone predicted, and O'Reilly and Pottermore have sold watermarked, unlocked files for years. Instead of locking the file, you mark it, so a leak can be traced to the copy it came from. The file itself stays an ordinary PDF that any reader can open.
So the technical problem was not the hard part. The hard part was that publishers are sensitive about anything with DRM in its name, and 'trust me, it works' is not an argument. What was missing was evidence. This project is that evidence: a marking scheme, an attack bench that tries to destroy it, and a live demo that traces a leaked screenshot back to the order in front of you.
How it changed
Each version below exists because the one before it was wrong about something specific.
- v1
The visible layers
- Why
- The first two layers are the ones the buyer is supposed to see: an ex-libris line in the footer and a notice on the contents page. Their whole job is deterrence — social DRM works by the buyer knowing the copy is theirs, not by stopping anything.
- Change
- Stamp the copy with a human-readable ownership line and a plain-language notice about what the marking is for.
- Result
- Fine against a whole file put on a torrent, and useless against the thing that actually happens — someone screenshots two pages and drops them in a group chat. A crop carries no footer. The deterrent worked; the proof did not survive.
- v2
Marks that survive a cropped screenshot
- Why
- To prove a leak from a fragment, the mark has to be everywhere the fragment might be cut from, and invisible enough that a paid book still looks like a paid book.
- Change
- Find every patch of true whitespace on the page with a summed-area table — thousands per page, each an O(1) query — then let the copy's own token seed which patches it uses. A QR is drawn into the chosen slots as vector rectangles at 85% grey, so 'delete every image in the PDF' does not reach them. Recovery runs a decode ladder: raw, then contrast-stretched, then upscaled.
- Result
- Compression, rescaling and rotation are free — QR was built for that. Cropping is the only attack that matters, and density is the whole defence: at one mark per page a cropped screenshot is traced about 22% of the time, at twenty-four marks it is 100%. Since a leak is rarely one page, one mark still resolves ten leaked pages 92% of the time.

Every slot a mark could occupy on one page. Whitespace on a typeset page is mostly margin — which is also this scheme's blind spot.
The mark means nothing without the service
Anyone can scan the QR with a phone, so the mark must be worthless on its own. It carries an 80-bit HMAC of an internal order reference and nothing else — no name, no email, no order id. Scan it and you get sixteen characters of noise.
Only the issuer resolves it. The service re-derives each customer's token from the signing secret and matches: drop a cropped screenshot into the demo and it comes back Traced — DT3PFW6435CQ3BZS → order-4471. The buyer's identity never travels inside the document, which is what makes this legal to ship under GDPR and 개인정보보호법, and it is why commercial products like LemonInk mask the email they print.
There is a fallback below even that. Because placement is seeded by the token, the layout is unique per copy and recomputable by the issuer — so if every symbol is degraded past decoding, three smudges at reproducible coordinates still narrow the registry down.
I never got the meeting. The deck was for publishers and the conversation did not happen, so this stayed a proof of concept with two honest gaps: a crop containing only body text carries no mark at all, and two buyers who diff their copies can find every mark. Both are written into the README and one is pinned by a test, so the claims cannot quietly rot.
What did survive is the work itself. Everything I learned here about reading PDF structure — page geometry, text extraction, where a document keeps its empty space — became Label, which is the same file format asked a friendlier question.


Built with
- Python
- HMAC-SHA256
- QR
- PyMuPDF
- zxing-cpp
- NumPy
- FastAPI