EdgeNFC
Security
A QR code is an image: photograph it and you have it. A static NFC tag is a string: read it and you have it. An EdgeNFC tag holds a key it will not disclose, and uses that key to sign a message that is different on every tap.
The claim, stated precisely. Forging a tap requires the tag's AES key. Replaying a captured tap fails a counter check. Neither is a matter of obscurity — both are the ordinary properties of a MAC and a monotonic counter.
Key hierarchy
The master key exists so that per-tag keys can be derived from it. It is never written to a tag, and it is not needed to answer a tap — only to re-derive the key of the tag that produced it.
Each tag's key is derived from your System Master Key and data that includes the chip's own 7-byte UID, using NXP's AN10922 AES-128 diversification. Two tags from the same master never share a key.
An attacker who successfully extracts a key from a chip in a lab learns that chip's key. The derivation is one-way: it does not run backwards to the master, and it says nothing about any other tag in the system.
The key that signs the tap (SDMFileReadKey) is derived per tag, from the master key and that chip's UID. The key that encrypts the tag's identity (SDMMetaReadKey) is derived from the master key and a role label alone — one per system — because a verifier has to decrypt the identity before it knows which tag it is talking to. Different labels, so the two capabilities are never interchangeable; see what that costs below.
SDM is a read operation. The chip performs the cryptography internally and emits the result; the key itself is never part of the NDEF payload and never appears in the URL a phone opens.
Threat model
Each row is a specific attack and the specific mechanism that answers it — not a posture statement.
Every tap carries an AES-CMAC computed with that tag's diversified key. Producing a code that verifies means possessing the key; copying the chip's memory does not produce it.
The chip's read counter is signed into the message and only increases. A URL captured from a real tap carries a counter we have already seen, and is rejected even though its signature is valid.
In encrypted-PICC mode the UID and counter are AES-encrypted into one opaque parameter, so the URL does not expose a stable identifier to anyone who reads it over your customer's shoulder.
Hosted master keys are envelope-encrypted under a KMS-held wrapping key and exist in plaintext only transiently in memory at verify time. No API returns key bytes after creation, and keys are never written to logs or error messages.
MAC comparison is constant-time with no early exit, so the time a rejection takes tells an attacker nothing about how close the guess was.
The expected SDM mode and key version are pinned per system server-side. A tag that presents a weaker configuration than the system expects does not get a pass because it asked for one.
Key custody
Custody is a decision you make, not a tier we assign. All hosted plans use hosted custody; the perpetual Edge License is how you take custody yourself.
When you provision from an account, the master key never reaches the device: the app sends a tag's UID and receives only that tag's derived keys. A lost or rooted phone therefore exposes one revocable tag, not your whole system. If you type a master key instead, for offline use without an account, it is held in memory only and zeroized after each provisioning operation — never written to storage, never logged, and never exported.
Stored envelope-encrypted: a KMS-held wrapping key encrypts the master key at rest. Plaintext exists only transiently in memory at verify time and is then zeroized.
With the Edge License, the master key is shown to you once at creation and is never persisted on our side. The self-host package ships public configuration only — it contains no key.
Primitives
The only thing we designed is how these pieces are composed. The pieces themselves are standard, and implemented with vetted libraries.
All ciphers are AES-128; all MACs are AES-CMAC as specified in NIST SP 800-38B. Key generation uses a cryptographically secure RNG.
Implemented to NXP's published specification and checked against NXP's published test vector — including its deliberate divergence from generic CMAC, which is a classic place for an implementation to be subtly and silently wrong.
Every tag and MAC comparison is constant-time. Derived and session keys are zeroized after use. The core returns non-secret error values and does not panic on attacker input.
Hosted and self-hosted run the same Rust/WASM build, and the Kotlin provisioning side is held to the same test vectors as the Rust verification side, so the two ends of the system cannot quietly disagree.
Privacy
Encrypted-PICC mode keeps the tag's UID out of the URL entirely: the chip encrypts its identity and counter into one blob that changes completely on every read. So a scan URL is not a durable identifier for the object it is attached to, and two taps of the same tag do not visibly belong together. This is checked on real hardware, not only in tests — how far that goes is below.
Per-tap verification URLs are transient and are excluded from indexing and from our sitemap, so an individual tap cannot end up in a search result.
Checking whether a product is genuine is the function of the page, so it runs without a cookie banner standing in the way. Nothing non-essential loads before you consent. See the Privacy Policy.
The verification event log retains outcomes and counters so you can see what happened. It never contains key material.
Straight answers
Not yet. We have not commissioned a third-party audit or obtained any security certification, and we will say so here until that changes. What we can point at today is the design above, standard primitives, and conformance against published NXP vectors.
Proven on hardware, and now switchable per Campaign — but the tag's mode is still set when the tag is written. On 6 August 2026 a physical NTAG 424 DNA was provisioned in encrypted-PICC mode and run end to end against a live deployment of the verifier. The tag decrypted its own read-back to the right UID and counter; a tap returned authentic with the UID recovered only by decrypting the blob; a second tap advanced the counter; and a replay of the first blob was refused as non_monotonic. The URL the phone opened carried no UID, and the blob changed completely between the two taps, so the two reads of that one tag are not correlatable by anyone who only saw the links. Since then, every Campaign has a UID privacy switch in the dashboard — free on every plan — which stops the verification page from printing a tag's serial number and marks the Campaign for encrypted-PICC provisioning; an encrypted tap never has its serial number printed even with the switch off. What is not yet true: the switch does not reach into tags that are already in the field, because a tag's mode is fixed in the chip when it is encoded, so changing modes means re-writing hardware; and the mode has been proven on one tag rather than exercised at volume. What you can check today without us: paste an encrypted tap into the SUN/SDM decoder with a key and watch it decrypt the UID and verify the MAC entirely in your browser.
They get privacy, not authenticity — and they get it system-wide. The key that encrypts the identity blob (SDMMetaReadKey) cannot be diversified per tag: a verifier has to decrypt the blob in order to learn which tag sent it, so a per-tag key would need the UID before it could reveal the UID. That key is therefore one per system, identical on every tag in it. Extracting it from a single tag would let an attacker decrypt the UID and counter out of any encrypted tap in that system that they can observe. It would not let them forge one: signing a tap still needs that tag's own SDMFileReadKey, which is derived from the master key and the chip's UID and never leaves the chip. A compromised meta-read key leaks who, never proof. So encrypted-PICC is not strictly stronger than the plaintext mirror — it trades one shared confidentiality secret for URL privacy, and leaves the authenticity guarantee exactly where it was.
Rotation changes the key every field tag was provisioned against. Today, tags provisioned under the old key need re-provisioning to verify again — plan a rotation as a re-provisioning exercise, not as a switch you flip. A rolling migration window is on the roadmap and is not shipped.
It proves the tag is genuine, present, and not replayed. Whether that transfers to the item depends on how the tag is attached — a tag that can be peeled off a real item and stuck on a fake one proves only that the tag is real. Design the attachment as carefully as the crypto.
Under self-hosted custody, no — that is the point of it. The key is shown once and never persisted by us, so a lost key means re-provisioning the tags. Back it up accordingly before you provision at scale.
Next
The demo runs the real verification core in your browser. Change a byte of the MAC, or reuse a counter, and watch it refuse.