前段时间,我总是不经意间读到同一个说法:奢侈品牌正在把 NFC 芯片放进自家产品里,这样你就能用手机轻触一只包或一双球鞋,确认它是真货、不是仿冒品。每篇文章都在说同一句漂亮话,却没有一篇说清楚到底怎么做到的。究竟是什么,能拦住造假者在仿制手袋的同时把芯片也一并复制过去呢?
于是我做了每次对某种标签好奇时都会做的事。我上了 AliExpress,找到一个卖“NTAG 424 DNA”标签的链接,下单买了一小批,然后等着信封寄到。花了几欧元,等了两个星期,那些品牌防伪系统赖以搭建的同款硅片,就摆在了我的桌上。接着我轻触了一枚,看看它到底能做什么。
NTAG 424 DNA 标签究竟是什么
从外表看,它就是一枚普通的 NFC 标签。把它扔进一堆廉价标签里你根本挑不出来,任何手机都能毫无怨言地读到它。如果你读过我那篇 NFC 标签类型指南,它不过是又一枚 Type 4 标签,iPhone 照读不误。
不一样的地方在“DNA”这部分。芯片内部藏着几把 AES-128 密钥和一个小小的加密引擎,它能做一件普通 NTAG215 或者整包里随便一张贴纸都做不到的事:它能给每一次轻触签名。这个签名就是关键所在。有没有它,正是这两种标签的区别:一种只会说“这里有个链接”,另一种会说“这里有个链接,还附上一份加密证明,证明此时此刻正是我,这枚特定的、货真价实的芯片,在提供它”。
这才是奢侈品牌真正花钱买的东西:不是那个链接,而是“一枚真芯片正在提供它”的这份证明。
SUN 和 SDM 是怎么运作的:一条每次轻触都会重写自己的链接
接下来就是让我豁然开朗的那一刻。当我去看这枚标签实际发出的是什么时,我意识到,理解它所需的大部分机制,我其实早就做出来过了。
今年早些时候,我上线了一个 NFC 轻触计数器功能:一枚标签会统计自己被读取了多少次,并把这个数字放进 URL 里,这样一个链接就能知道这是第 47 次有人扫描它。一枚 NTAG 424 DNA 标签就是同一个思路,只不过在外面裹了一层加密,让它无法被伪造。
这套机制叫 SUN(Secure Unique NFC),如果你在读 NXP 的数据手册,它又叫 SDM(Secure Dynamic Messaging)。你在标签上存一个普通链接,类似 https://example.com 这样。但你会告诉芯片,每次被轻触时就即时重写这个链接的一部分。于是你手机实际收到的,更接近这样:
https://example.com/?picc_data=A1B2...&cmac=9F3C...
这两个值可不是摆设。picc_data 是标签真实 ID 加上一个轻触计数器的加密副本,用一把永远不会离开芯片的密钥打乱而成。cmac 则是针对这份数据的一个加密签名。两者每次轻触都会变。同一枚标签轻触两次,你会得到两个完全不同的 URL,每一个都是芯片当场新鲜签出来的。
我把一枚普通的 NFC 标签想象成橱窗里一块印好的招牌。谁都能把它拍下来,再印一份一模一样的复制品。而一枚 SUN 标签更像是一位保安:你每走进来一次,他就递给你一张全新的、单独编号并盖了章的收据。把昨天的收据复印一份对你没有任何用处,因为今天的号码不一样,而且只有保安那个章才是真的。
为什么一枚被克隆的 NTAG 424 DNA 标签会露馅
这一部分回答的正是我最初的那个问题。造假者当然能克隆一枚标签的内容。他们可以把 URL 读出来,一个字节一个字节地复制,再写进一枚空白芯片里。这一点向来如此。
他们做不到的,是拿出下一个有效的签名。签名密钥住在那枚真芯片内部,从不外露,哪怕在一次轻触过程中也不会。这意味着,一次轻触只有对真正握有密钥的一方才有价值。在一套真正的品牌防伪方案里,标签的链接指向厂商自己运行的一台服务器,正是这台服务器负责解密每一次轻触、重新计算签名以确认密钥吻合,并持续追踪那个一路攀升的计数器。
最后这一点,正是揪出克隆品的关键。造假者能放到仿品上的 URL,只可能是从某一次真实轻触里截获来的那一个,它被冻结在那次轻触碰巧带着的那个计数值上。把它重放一遍,服务器看到的就是一个它早已见过的数字,而一枚真芯片的计数器永远只会往前走,所以一次重复、或者一次倒退,就把这场重放暴露了。想要送出一个全新的、更大的计数值,还要配上一个依然验得过的签名,他们就得拿到密钥;而想拿到密钥,他们就得攻破 AES,或者从物理上把芯片开盖。为了一只假手袋,这两件事都不会发生。
这就是那句营销话术诚实的版本。芯片并不能让产品本身无法复制。它让真伪证明无法复制,并把这份证明转移到了造假者复制不出来的东西上。
芯片内部长什么样
只要脑子里有一张芯片的布局图,NFC.cool 对这些标签做的每一件事就都好理解得多。所以在写下第一行代码之前,我得先把这张图画出来,下面就是我画好的那张。
NTAG 424 DNA 是一枚 NFC Forum Type 4 标签,416 字节的内存全都归一个应用所有,里面固定放着三个文件。你没法像在 MIFARE DESFire 上那样新建或删除文件,能用的就这三个:
| 文件 | 大小 | 存放内容 |
|---|---|---|
| File 01 | 32 字节 | 能力容器,告诉手机去哪里找 NDEF 数据 |
| File 02 | 256 字节 | NDEF 消息,通常就是你的链接。每次读取时,SUN 都会把实时值镜像到这个文件里 |
| File 03 | 128 字节 | 一个芯片可以加密保存的专有文件。NFC.cool 把它用作 Vault,下文细说 |
文件旁边是五把 AES-128 密钥,编号 Key 0 到 Key 4。Key 0 是应用主密钥:不管是改链接、打开 SUN、更换其他任何一把密钥,还是碰芯片的配置,都要先用它认证。Key 1 到 Key 4 自己不干任何事,只有当某个文件的访问权限或者 SUN 的设置指向它们时,它们才派得上用场。一枚全新的标签,五把密钥都是十六个零字节,NDEF 文件谁都能写,所以往新标签上写一条普通链接不需要任何仪式感。
每一条会改动内容的命令,都在一个经过认证的会话里执行:手机和芯片先用其中一把密钥做一次双向的挑战应答,从中派生出会话密钥,之后的每条命令要么带上 MAC,要么整条加密。这就是本文后面反复提到的安全消息机制。NFC.cool 在 iPhone 和 Android 上都完整实现了它,下面讲到的每一次写入都走这条通道。
轻触一下能看到什么
把标签贴到手机上,iPhone 或 Android 上的 NFC.cool Tools 会做一次深度读取,什么都不用你输入:芯片的身份、它是不是 TagTamper 版本、链接、每个文件的设置和访问权限、哪些密钥槽位已经不再是出厂值,以及三项彼此独立的检查结果。
这是原装 NXP 硅片吗?
每一枚 NTAG 424 DNA 出厂时都带着一个原厂签名:NXP 用自己的私钥在 P-224 曲线上,对芯片七字节的 UID 做的一个 ECDSA 签名。NFC.cool 会读出它,并直接在手机上用 NXP 公开的公钥去验证,不需要你提供任何密钥。验得过,App 就显示“原装 NXP”。这回答了第一个问题:这到底是真正的 NXP 硅片,还是一枚只是顶着同一个名字的仿制芯片?
这一次轻触是真实的吗?
这就是 SUN 检查。App 从标签刚刚发来的链接里取出 picc_data 和 cmac,解密 PICC 数据得到 UID 和读取计数,重新计算 CMAC,再和标签发来的那个比对。两者吻合,你就会看到“真实”,计数器的值也会作为读取计数一并显示。
这项检查需要标签的密钥,因为这正是它存在的意义。仍停在出厂密钥上的标签,用全零密钥就能验过。你用自己的密钥锁过的标签,用 NFC.cool 在你设置密钥时存下的那把来验。而别人用你没有的密钥锁住的标签,会显示“未验证”,这本来就是正确的答案。
封条被破坏过吗?
这类芯片有一个版本叫 NTAG 424 DNA TagTamper,天生就是一道防拆封条。它是一张贴纸,里面穿着一根细细的导电回路。你把它横贴在任何想保护的地方,盖在盒子的封口上,或者绕瓶盖一圈,和今天那些“撕毁保修失效”的贴纸干的是同一件活。一打开物品,贴纸就会被撕破,回路也就断了。
关于这条回路,芯片记着两件事:一个永久性的锁存位,记着它是否曾经被打开过;还有此时此刻的实时状态。NFC.cool 每次轻触都读取这两项,并报告“密封”、“已开封”,或者最要紧的那一种,“已开封,已重新密封”:有人扯断过回路,之后又小心地把它合上了。锁存是单向的,所以一个重新封好的盒子,在芯片余下的一生里读出来都是已开封。加密证明的是芯片是真的,而这一项证明的是没人进过盒子。
自己动手写一枚:精简版
读取只是一半。另一半是,从 AliExpress 买来的那些空白标签随你来写,而最精简的设置只有三步。
写入你的链接。一次普通的 NDEF 写入,和写任何标签一样。
打开 SUN。App 会把你的链接连同占位符一起写进去,并告诉芯片,每次被读取时,把加密后的 UID、轻触计数和签名镜像到这些占位符里。从此每一次轻触都会产生一个独一无二、带签名的 URL。
设置你自己的 Key 0。用一把只有你知道的密钥换掉出厂的零,这样别人就再也没法重新配置这枚标签了。
最后这一步,你输入的是一句口令,而不是密钥本身。NFC.cool 会对口令做一次 SHA-256 哈希,取前 16 个字节作为 AES 密钥,iPhone 和 Android 上算法一样,所以你在一台设备上配置好的标签,换到另一台上用同一句口令就能打开。如果你更想用在别处生成的密钥,比如你自己的服务器生成的,也可以直接粘贴那 32 个十六进制字符。
密钥一旦丢失,这枚标签就再也没法重新配置了,所以 App 对密钥存在哪里格外小心。在 iPhone 上它存进系统钥匙串,并通过 iCloud Keychain 同步。在 Android 上它用一把由硬件保护的密钥加密后存好,并镜像到 Block Store,这样重装 App 或者换新手机都还在。新密钥会在变更发出之前先保存下来;如果轻触在变更途中被打断,新旧两个值都会保留,直到标签确认它现在持有的是哪一个。你在另一台设备上设的口令也可以在这里输入,App 会先拿它去标签上核对,通过了才保存。
有一件事 App 会刻意拒绝:通过普通的写入界面,往一枚已开启 SUN 的标签上写一条普通链接。镜像偏移量是按当初配置时那条 URL 固定下来的,换一条长度不同的 URL,芯片每次轻触都会把数据镜像到你新内容的中间去。NTAG 424 界面会先关掉 SUN,再写入。
芯片的其余部分
大多数教程到精简版就停下了,而在此之前,想再往前走一步,就得在电脑上插一个 USB 读卡器,打开 NXP 的 TagXplorer。我想让整份数据手册在手机上都触手可及,于是就一节一节地啃了下去。
五把密钥,一把不落
Key 0 有自己的界面,Key 1 到 Key 4 放在“高级”下面。每一把都可以用口令或十六进制来设置、重置回出厂默认值,或者在另一台设备上设好之后在这里录入。每一次变更都要用 Key 0 认证,五个槽位改不改,都由它说了算。
SUN,密钥任你挑
开启 SUN 不是拨一个开关那么简单。你要选模式:加密模式下,UID 藏在 picc_data 里,只有持有密钥的人才能读到;明文模式下,UID 和计数直接明明白白地出现在 URL 里,只有签名是保密的。你还要选由哪几把密钥来干活:一把元数据读取密钥负责加密 PICC 数据,一把文件读取密钥负责计算签名。两者可以是同一个槽位,也可以是两个不同的槽位。品牌方就是靠这一点,把用来验证轻触的密钥交给合作伙伴,而不必把解密 UID 的那把也一并交出去。
如果你选了一个还停在出厂零值的槽位,App 会提醒你,因为用一把人人皆知的密钥做出的签名什么都保护不了。验证这一侧也认得这些花样:一次用 Key 3 签名、用 Key 1 加密的轻触,只要这两把密钥都存在手机上,就能正确验过。
文件访问权限
每个文件带着四项权限:读取、写入、读写,以及更改,最后一项决定谁能修改前面三项。每项权限指向五把密钥中的一把,或者“自由”(任何人),或者“永不”(谁都不行,永远不行)。于是你可以规定“File 02 谁都能读,只有 Key 2 能写,只有 Key 0 能改这些规则”,芯片自己就会执行,不需要任何 App 参与。
NFC.cool 会显示每个文件当前的权限并让你编辑,里面内置了两道提醒。当某项权限指向一把这部手机上没有的密钥时,它会告诉你,因为你可能正在把自己锁在门外。而把更改权限设为“永不”之前,它会要求你通过单独的一步来确认,因为这一旦写进去,这个文件的规则在芯片的余生里就冻结了。
芯片配置
文件之下是芯片自身的配置,NXP 通过一条 SetConfiguration 命令把它暴露出来。NFC.cool 覆盖了这些选项:
随机 UID。正常情况下,芯片对每一台读取器都报同一个固定的 UID,任何人都能借此在多次轻触之间追踪一枚标签。打开随机 UID 后,它每次都用一个新的随机 ID 应答,只有在你认证之后才亮出真实的那个。这是实打实的隐私提升,而且是永久的。App 靠 UID 来识别标签,所以开启之后,它会用自己知道的每一把 Key 0 逐一尝试,通过认证后的 GetCardUID 把真实 UID 取回来,这样标签在配置它的那部手机上依然可管理。
认证失败次数上限。芯片在锁住 Key 0 之前能容忍多少次错误密钥的尝试。这是防止猜密钥的保护,但设得太低,几次失败的轻触就能把主密钥永久锁死。
反向调制强度。强或标准。标准在小天线上可能读不出来,所以留在默认值上就挺合理。
链式写入。可以禁用,这样单次写入就被限制在一帧以内。这一项也是永久的。
能力字节。NXP 留给你自己使用的两个空闲字节。
LRP。安全消息机制的开关,下面单独有一节。
Vault
File 03 是一个 128 字节的专有文件,芯片可以加密保存它,NFC.cool 把它变成了标签自带的一个小小私密存储区,也就是“Vault”。第一次往里存东西时,App 会把这个文件切换到全加密模式,并把每项访问权限都锁到 Key 0 上。此后,Vault 最多能装 126 字节的文本,只有你的密钥能把它读回来,换任何别的手机来做深度读取,得到的只有一个权限错误,别的什么都没有。
它是为那种该跟着物件走、而不是躺在谁的数据库里的秘密准备的:一个序列号、一条写给未来自己的备注、一个你自己的服务器等着核对的令牌。把 Key 0 重置回出厂值会抹掉它,这也是 Vault 唯一会消失的方式。
LRP 模式
正常情况下,芯片用普通的 AES 来守护它的密钥,想偷到密钥就得攻破 AES 本身。但还有一条更阴的攻击路线。把芯片架到实验台上,在它运行加密运算时测量功耗和电磁辐射里那点微弱的起伏,采到足够多这样的轨迹,单凭泄漏就能把密钥重建出来,根本不用去碰那些数学。LRP,也就是 Leakage-Resilient Primitive,是一条重新打造的安全通道,设计目的就是让那点泄漏无处着力。NXP 在 AN12304 里对它有详细说明。对一瓶葡萄酒上的一张贴纸来说,它实在是杀鸡用牛刀,所以大多数标签从不开启它,大多数工具也从没学会和它打交道。
在我为第一个版本写的设计笔记里,紧挨着“LRP 模式”,我写的是“不做”。可它一直在心里挠个不停,于是我把它做了出来。NFC.cool 能把一枚标签切换进 LRP 模式,更重要的是,切换之后依然能向它认证、继续管理它:密钥、文件权限、Vault、芯片配置,全都走 LRP 通道,而不是 AES。
拨这个开关之前要知道两件事。它是永久的:标签一旦进入 LRP 模式,它的 AES 安全消息机制就永远关闭,任何只懂 AES 的工具从此再也没法和它对话。另外,LRP 标签上没有 SUN,所以一枚本职就是给轻触签名的标签,应该留在 AES 模式。
哪些操作无法撤销
这里的很多命令都是永久性的,App 在你动手的当下会把话说得很重:每一个不可逆的操作,都得通过一条把确切后果讲清楚的警告来确认。不过在这里还是值得再列一遍。
启用 LRP。
启用随机 UID。
禁用链式写入。
把某个文件的更改权限设为“永不”。
弄丢密钥。芯片没有恢复出厂设置这回事。Key 0 没了,你重新配置这枚标签的能力也就没了。
认证失败次数上限设得太低,几次错误的轻触就能锁死 Key 0。
在动你真正在乎的那枚标签之前,先拿一枚备用的练练手。
防伪 NFC 标签究竟用在哪里
说实话?大多数轻触 NFC 标签的人,这一切都用不上,这也挺好。一张能打开链接的贴纸,本身就是一样美好、平淡而有用的东西。
可一旦你亲手拿过一枚这样的标签,它的用武之地就一目了然了。一只奢侈品包可以证明自己是真货。一瓶葡萄酒或威士忌可以表明它从没被悄悄开瓶、再灌回去,防拆封条撑起的正是这后半件事。一盒药既担保里面是真药,也担保封条没被任何人破坏过。门票不再是那种截个图就能转手传给别人的东西,而门边的一枚标签,能证明确实有人真的站在过那里,而不是窝在沙发上重放一个存好的链接。这和欧盟数字产品护照正从监管一侧兜着圈子想解决的,是同一个真伪问题,只不过这里是在单个物件的层面上把它解决掉的。
我做这个功能,并不是因为有一千个用户开口要它。我做它,是因为我出于好奇从网上买了些奇怪的标签,弄明白了它们的原理,然后就非得把数据手册的每一页都翻个遍不可。好功能,通常就是这么开始的。
关于 NTAG 424 DNA 标签,一句话的结论
NTAG 424 DNA 标签,是 NFC 里最接近防拆封条的东西。它们没法阻止有人复制一件产品,但它们让这件产品的真品证明无法伪造,因为那份证明是一个只有真芯片才能产生的、每次都新鲜的加密签名。
NFC.cool Tools 能读取它们,验证芯片、验证轻触、验证防拆封条,并把整枚芯片交到你手上任你配置:每一把密钥、每个文件的权限、芯片自身的设置,甚至 LRP,全都在手机上完成。如果你曾好奇一次轻触怎么就能分辨真假,那就在 iPhone 或 Android 上装上它,花几欧元订一批这样的标签,自己亲手轻触一枚。这绝对值得你一头扎进去,好好研究一番。