In August 2021, Apple announced plans to scan every photo on your iPhone before uploading it to iCloud. By September of the same year—after two weeks of intense backlash—it withdrew the proposal. In December 2022, the initiative was officially declared dead. In the meantime, the European Commission proposed legislation in 2022 requiring platforms like WhatsApp, Signal, and Telegram to do exactly what Apple had backed away from. That proposal, known as Chat Control, is still being debated in the European Council.
Both attempts rely on the same underlying algorithm: a 64-bit perceptual hash implemented in just 40 lines of Python. That’s not an exaggeration. These 40 lines are the foundation for a system you can replicate yourself by the time you finish reading this article.
Most public debates—both inside and outside Europe—unfold without participants understanding how the underlying mechanism works. The regulation is discussed in terms of politics (“privacy vs. child safety”), while the technical core remains a black box. This post aims to break that down. By the end, you’ll be equipped with the technical knowledge needed to form an informed opinion. And if that’s not your goal, you’ll at least understand exactly what’s at stake.
The Problem: SHA-256 Can’t Recognize Two Similar Photos
The most straightforward solution for any developer encountering this problem is to use SHA-256. If two images produce the same hash, they are identical. It’s simple, fast, and cryptographically secure.
But it doesn’t work.
import hashlib
def sha256(path):
with open(path, "rb") as f:
return hashlib.sha256(f.read()).hexdigest()
# Same photo, two different compressions
sha256("photo.jpg") # "a7f3..."
sha256("photo_reencoded.jpg") # "9c2e..." (completely different)
Change a single bit—like compressing a JPEG with quality 95 instead of 90, switching from WebP to JPEG, or slightly adjusting the brightness—and SHA-256 produces a completely different hash. Cryptographic similarity is, by design, highly intolerant. This is a feature, not a bug.
To detect “the same photo despite compression differences,” you need a tolerant algorithm, a perceptual hash: a 64-bit fingerprint that survives transformations that don’t alter the image’s content.
aHash: Average Brightness
The first useful approach is called Average Hash or aHash. This idea is astonishingly simple:
import numpy as np
from PIL import Image
def average_hash(path, size=8):
img = Image.open(path).convert("L").resize((size, size))
arr = np.asarray(img, dtype=np.float32)
mean = arr.mean()
bits = (arr.flatten() > mean).astype(int)
return "".join(str(b) for b in bits)
def hamming(a, b):
return sum(x != y for x, y in zip(a, b))
The image is reduced to an 8×8 grayscale array, the brightness average is calculated, and each pixel is set to 1 if it’s above the average or 0 if it’s below. The result is a 64-bit fingerprint that represents the global brightness distribution.
Two “identical” images produce hashes with a low Hamming distance (≤ 5 bits of difference). Distinct images produce hashes with a large distance (40+ bits). It works well for resized photos, minor crops, or re-encodings—but breaks with global changes in contrast or brightness. It’s a starting point.
dHash: Relative Gradients
The next step was introduced by Neal Krawetz in 2011, called Difference Hash or dHash:
def difference_hash(path, size=8):
img = Image.open(path).convert("L").resize((size + 1, size))
arr = np.asarray(img, dtype=np.float32)
diff = arr[:, 1:] > arr[:, :-1]
return "".join("1" if v else "0" for v in diff.flatten())
Instead of measuring absolute brightness, it analyzes horizontal gradients: whether each pixel is brighter than the pixel to its right. Even if the entire image darkens by 30%, the relative gradients remain intact. dHash handles changes in contrast, gamma, and slight color adjustments where aHash fails.
Forty lines of Python, about two weeks with a library like PIL, and you’ve built the fundamental tool at the heart of the most significant regulatory debate in Europe this year.
From 40 Lines to Global Infrastructure
What you’ve implemented with average_hash and difference_hash is, with modifications, the foundation of global image processing systems.
Microsoft PhotoDNA (2009). Developed by Hany Farid (Dartmouth) in collaboration with Microsoft Research for detecting child sexual abuse material (CSAM). It’s a sophisticated variant of the same principle: reducing an image to a short signature that survives re-encoding, allowing comparison by similarity. Though proprietary, its conceptual roots are shared with perceptual hashes. Microsoft claims PhotoDNA processes billions of images monthly.
Meta PDQ (2019). An open-source variation developed at Meta (formerly Facebook) and published in the ThreatExchange repository on GitHub. Unlike PhotoDNA, it’s readable, executable, and transparent. Meta argued that security should rely on system robustness, not the secrecy of the algorithm.
YouTube Content ID (2007). Uses perceptual hashes adapted for video: every upload is fingerprinted frame by frame and compared to a database of registered copyrighted content. It detects reuploads, mashups, or embedded clips. It tolerates re-encoding, cropping, or minor logo alterations—a noticeable improvement over SHA.
Google Photos, Apple Photos, Amazon Photos. When your phone informs you, “This photo is already uploaded to iCloud from your iPad,” it’s not comparing bytes. It uses perceptual hash variants or deep learning-based embeddings to identify “the same photo” despite differences in format, compression, or slight edits.
Apple NeuralHash (2021). This withdrawn proposal wasn’t a classic pHash; it was a convolutional neural network (CNN) trained to output the same embedding under invariant transformations (rotation, cropping, compression). The embedding was reduced to 96 bits via locality-sensitive hashing. Technically more advanced than PhotoDNA, but conceptually similar: short fingerprint, robust, and distance-comparable.
The trend is clear: this algorithm—under different names and iterations—has become a silent foundation of internet infrastructure. Most users are unaware. Most developers have heard of it without implementing it. And now, the European Parliament is preparing to legislate its mandatory deployment in encrypted communications.
Fault Lines
If the algorithm were flawless, the debate would be straightforward. But it’s not. It has four structural issues that need to be understood before forming an opinion.
False Positives
Completely different images can produce identical hashes. With a 64-bit hash, there are 2^64 = 18 trillion theoretical possibilities. While this is a vast space, the “reasonable” universe of human-made images (as opposed to random noise) is smaller, and these regularities can cause collisions. Rare, but not impossible.
In 2021, during Apple’s NeuralHash controversy, users documented accidental collisions between everyday images: for example, one between a photo of a dog and an abstract sculpture. While Apple claimed a false positive rate of “1 in 1 trillion accounts per year,” researchers demonstrated that minimal tweaks could drastically increase collision rates.
False Negatives
Rotate an image by 10 degrees, crop 20% from the right, or reflect it horizontally, and most perceptual hashes lose the trail. More sophisticated versions (like pHash using DCT or NeuralHash with trained invariances) perform better, but all fail beyond certain thresholds.
This means motivated users can circumvent detection with basic tools like rotating, compressing at low quality, or adding a small border—built-in features on nearly every phone.
Adversarial Collisions
This is the most severe issue but the least discussed in mainstream reporting. On August 18, 2021—less than two weeks after Apple’s announcement—a researcher using the alias Asuhariet Ygvar reverse-engineered NeuralHash from iOS binaries and published the implementation on GitHub. Days later, adversarial collisions were demonstrated: completely different images deliberately crafted to generate the same hash.
In 2021, Jonathan Prokos, Matthew Green, and collaborators from Johns Hopkins formalized the attack in their paper “Squint Hard Enough: Attacking Perceptual Hashing with Adversarial Machine Learning”. They showed that for any known perceptual hash, it’s possible to create:
- Arbitrary collisions: Two completely different images with the same hash.
- Targeted preimages: A new image that looks different but generates an existing target hash.
The operational implications are severe. An attacker could send an innocent-looking image that matches a known CSAM hash. The recipient’s device raises an alert, prompting human review which clears them. But at scale, attackers could automate these false alarms to overwhelm systems and tip them into failure. This flaw isn’t just Apple being incompetent—it’s a structural weakness of perceptual hashes.
Who Controls the Hash Database
The final issue is political. The database of hashes against which images are compared is controlled by NCMEC (National Center for Missing & Exploited Children), a private U.S.-based organization. While the database is federated with providers in Europe and elsewhere, ultimate authority over what gets added or removed rests with NCMEC.
This creates a global single point of truth with underexplored consequences. What happens if NCMEC adds non-CSAM hashes—images of political dissidents, LGBTQ content, or leaked journalism? Or if a government pressures platforms to comply with its own agenda? A perceptual hash doesn’t make these distinctions; it detects only what it’s told to detect.
The Debate: Apple 2021 and the EU 2026
This is why the technical issue demands urgent attention.
August 5, 2021. Apple announces three “child safety” features in iOS 15. Among them: on-device scanning of all photos before they’re uploaded to iCloud Photos, comparing NeuralHash outputs to NCMEC’s database. An alert would trigger after 30 matches, leading to human review and potential reporting to law enforcement.
Over the next two weeks, the backlash was overwhelming. Matthew Green (Johns Hopkins) and Ross Anderson (Cambridge) led the academic criticism with their paper, “Bugs in Our Pockets: The Risks of Client-Side Scanning”, endorsed by 14 leading cryptography and security researchers. Their central argument: on-device scanning technology itself is the backdoor—not the hash database. Once implemented, governments could demand its use for other purposes. Apple wouldn’t be in a strong position to refuse.
Multiple flaws in NeuralHash were publicly demonstrated within days, including false positives and adversarial collisions. A coalition of over 90 organizations (EFF, ACLU, Access Now, Freedom of the Press Foundation) signed an open letter calling for its withdrawal. On September 3, Apple announced a “delay.” By December 2021, all references to CSAM Detection had been removed from their website. A year later, the program was officially terminated, replaced by Advanced Data Protection (E2E encryption for iCloud Photos)—effectively the polar opposite strategy.
May 11, 2022. European Commissioner Ylva Johansson introduced the Regulation to Prevent and Combat Child Sexual Abuse (CSAR), often referred to as “Chat Control.” This proposal obligates messaging services such as WhatsApp, Signal, Telegram, and iMessage to detect CSAM in their users’ encrypted communications, using judicial orders to implement detection. Permitted techniques include on-device scanning with perceptual hashes.
The parallels with Apple’s abandoned proposal are unmistakable, and critics—including the same academics who opposed Apple—have noted the similarities. Since its introduction, the proposal has undergone revisions, with elements softened by the European Parliament and debates in the Council (with countries like Germany, the Netherlands, and Poland among its staunchest critics). Yet as of 2026, the proposal remains on the table in an increasingly close negotiation process. The strategic direction remains unchanged: the EU wants to standardize on-device scanning as an enforcement tool.
What You Can Do
The facts are clear: Understanding the algorithm is a prerequisite for informed participation in this debate. This isn’t about opinions—it’s about sound, informed reasoning.
Concrete actions, ordered by effort required:
- Run the code in this post. Copy
average_hashanddifference_hash, apply them to two of your photos, and measure the Hamming distances. See how brightness noticeably breaks aHash, but dHash holds firm. A 40-line experiment in 30 minutes. - Read the PDQ whitepaper in the
facebook/ThreatExchangerepository on GitHub. It’s real, production-grade, well-documented code. You can familiarize yourself with the evolution from pHash to PDQ in a few hours, which equips you to critically engage with any media coverage. - Read “Bugs in Our Pockets” (Anderson et al., 2021, arXiv:2110.07450). At 40 dense pages, it’s the definitive academic counterargument to on-device scanning from leading cryptographers.
- Follow the CSA Regulation votes in the European Parliament and Council. Public tools like chatcontrol.eu (maintained by Pirate Party MEP Patrick Breyer) track the proposal’s status and developments.
What not to do: offer uninformed opinions without at least understanding how the algorithm works. It’s not about being a technical purist—it’s about intellectual integrity. The algorithm at the core of this debate fits in 40 lines of a language every developer knows. If you haven’t studied it, your stance on the legislation is, at best, an echo of someone else’s tweet.
The question isn’t whether the EU will regulate encrypted communications. It’s whether those of us weighing in will do so with or without a technical foundation.
References
- Krawetz, Neal. “Looking Up Images”. HackerFactor Blog, 2011. The classic introduction to pHash and dHash.
- Meta. “PDQ — Perceptual Hashing for Image Similarity”. GitHub:
facebook/ThreatExchange, 2019. - Prokos, J.; Fendley, N.; Green, M.; Jois, T. M.; Cao, Y. “Squint Hard Enough: Attacking Perceptual Hashing with Adversarial Machine Learning”. USENIX Security Symposium, 2023 (arXiv:2112.09283).
- Abelson, H.; Anderson, R.; Bellovin, S. M.; et al. “Bugs in Our Pockets: The Risks of Client-Side Scanning”. arXiv:2110.07450, 2021.
- Apple. “CSAM Detection Technical Summary”, August 2021. Archived; no longer officially published.
- Ygvar, A. “AppleNeuralHash2ONNX”. GitHub, August 2021. Reverse engineering and collision generator.
- European Commission. “Proposal for a Regulation laying down rules to prevent and combat child sexual abuse”, COM(2022) 209 final, May 11, 2022.
- NCMEC. CyberTipline Reports, annual statistics.
This article was originally published in Spanish and translated with the help of AI.