ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

HDMI黑屏排查:HDCP 1.4认证链路、密钥与中继器校验

HDMI黑屏排查:HDCP 1.4认证链路、密钥与中继器校验

1. 先把链路角色摆正:谁在加密,谁在解密

很多人第一次接触HDCP 1.4,都是被一块黑屏逼出来的。播放器、盒子、采集卡、显示器明明都点亮了,EDID 也读到了,分辨率、时序全部正常,可画面就是黑的,或者闪几秒之后突然变黑,系统日志里只留下一句“link integrity check failed”。这时候再去翻资料,才发现问题出在 HDCP 1.4 的认证链路上,跟分辨率、线材、EDID 的关系反而没那么大。

HDCP 1.4 是运行在 DVI/HDMI 链路上的内容保护协议,核心目标很明确:让发送端输出的音视频数据在链路上以密文形式传输,只有通过认证的接收端才能解出明文。它解决的问题不是“防拷贝”这么简单,而是把“谁能看”这件事变成一条可验证的信任链。适合阅读这篇内容的人,大致有三类:做 HDMI 产品固件的嵌入式工程师、负责音视频链路调试的测试人员,以及想搞清楚“为什么换根线就黑了”的硬件爱好者。下面我会按照实际调试时会遇到的顺序,把密钥体系、认证三阶段、内容加密、中继器校验、吊销列表和抓包排查逐层拆开,中间会给出可直接运行的读写代码和常见故障对照表。

1.1 三个角色与两条通路

HDCP 1.4 的拓扑里只有三种角色。发送端,规范里叫 Transmitter 或 Source,典型是播放器、机顶盒、显卡、游戏主机;接收端,叫 Receiver 或 Sink,典型是显示器、电视、功放、采集卡;中继器,叫 Repeater,典型是 AV 功放、HDMI 分配器、矩阵、带环出的回音壁。中继器特殊的地方在于它同时具备接收端和发送端的身份:对上游它是 Sink,对下游它是 Source,所以一次完整的认证可能包含多段独立的 HDCP 1.4 链路。

链路上有两条物理通路需要分开看。一条是TMDS 数据通道,承载视频像素和音频采样,HDCP 1.4 的加密就作用在这条通道上;另一条是DDC 通道,本质是 I2C,承载 EDID 和 HDCP 的寄存器读写。认证过程全部走 DDC,内容加密全部走 TMDS。这个分工很关键:DDC 只负责“验身份、换密钥”,TMDS 才负责“传密文”。所以 DDC 上出现误码,表现出来往往是认证失败或反复重认证;TMDS 上出现误码,表现出来往往是花屏、闪屏、间歇性黑屏。

提示:很多人把 HDCP 认证失败和 TMDS 信号完整性混为一谈。先看 DDC 上的认证有没有走完,再去看 TMDS 的误码率,顺序反了会浪费大量时间。

1.2 为什么 HDCP 1.4 的信任模型这么设计

HDCP 1.4 的信任模型基于一个很朴素的前提:每个合法设备出厂时都预置了一组私钥,私钥不直接暴露,而是通过密钥选择向量和矩阵运算间接参与认证。发送端不需要知道接收端的私钥,接收端也不需要知道发送端的私钥,双方只交换公开的KSV和一次性随机数An,就能各自算出同一个会话密钥。这个设计的巧妙之处在于“可验证但不可反推”:即使你抓到了 DDC 上的全部明文交互,也拿不到设备的私钥。

代价也很明显。HDCP 1.4 的密钥长度只有 56 bit,加密算法是基于 LFSR 的流密码,放到今天看强度并不高。它的定位从来不是对抗拥有物理访问权限的高级攻击者,而是防止普通用户通过中间设备把明文内容截下来。理解这一点很重要:你在调试时看到的“认证通过但画面异常”,往往不是密钥算错了,而是链路时序、寄存器读写或中继器拓扑校验出了问题。

1.3 1.3 到 1.4 的关键增量

HDCP 1.4 不是一次彻底重写,而是在 1.3 的基础上补齐了几块短板。最常被提到的是对HDMI 1.4 新增能力的支持,包括以太网通道和音频回传通道的场景;另一个重要变化是中继器拓扑校验更加严格,引入了 V 值和 SHA-1 哈希的双重校验;还有就是系统吊销列表,也就是 SRM,让上游可以拒绝已知被攻破的 KSV。

这些增量带来的直接影响是:同样是“认证通过”,1.3 时代可能只看 R0 校验,1.4 还要看中继器上报的 KSV FIFO、V 值和哈希。很多老设备升级固件后突然不兼容,问题就出在中继器校验这一层,而不是密钥算法本身。后面讲到中继器章节时,我会把这三个校验点拆开讲清楚。

2. 密钥体系:40 bit 的 KSV 和 56 bit 的 Km 是怎么来的

要理解 HDCP 1.4 的认证,先得把密钥体系搞清楚。这套体系里出现过好几个名字相近的量:KSV、BKSV、Aksv、Km、Ks、An、R0,第一次看很容易混。我用一句话先给它们定位:KSV 是设备身份证号,Km 是双方各自算出来的主密钥,Ks 是真正用于内容加密的会话密钥,An 是一次性随机数,R0 是校验值。

2.1 设备私钥矩阵与 KSV 选择

HDCP 1.4 的密钥矩阵是一个 40 行、40 列的方阵,每个元素是一个 56 bit 的密钥。每一台合法设备在出厂时,会被分配矩阵中的一行,也就是 40 个 56 bit 的私钥,合计 280 字节。同时设备还会获得一个 40 bit 的KSV,这个 KSV 有一个硬性约束:40 位里恰好有 20 位是 1,20 位是 0。这个平衡设计是为了让矩阵运算的复杂度保持稳定,不会因为某台设备的 KSV 全 0 或全 1 而退化。

在实际交互中,接收端会把自己的 KSV 放在寄存器里供发送端读取,这个值叫BKSV,B 代表 Receiver。发送端自己的 KSV 叫Aksv,A 代表 Transmitter,发送端会把它写到接收端的寄存器里。注意,Aksv 不是“发送端的身份证明”这么简单,它同时还承担了“告诉接收端该用矩阵里哪些行来参与运算”的作用。

2.2 Km 的计算与对称性

Km 的计算是 HDCP 1.4 里最容易被写错的一步。发送端手里有自己的 40 个私钥,接收端手里也有自己的 40 个私钥。双方各自用对方给的 KSV 做选择,把选中的私钥异或起来,就能得到同一个 Km。具体规则是:遍历对方 KSV 的每一位,如果第 i 位是 1,就把自己私钥向量里的第 i 个密钥异或进结果。

用伪代码表示就是:

// 发送端计算 Km uint8_t km[7] = {0}; for (int i = 0; i < 40; i++) { if (bksv[i / 8] & (1 << (i % 8))) { for (int j = 0; j < 7; j++) km[j] ^= tx_private_key[i][j]; } }

接收端做同样的操作,只不过遍历的是 Aksv,用的是自己的私钥:

// 接收端计算 Km' uint8_t km_prime[7] = {0}; for (int i = 0; i < 40; i++) { if (aksv[i / 8] & (1 << (i % 8))) { for (int j = 0; j < 7; j++) km_prime[j] ^= rx_private_key[i][j]; } }

由于矩阵的对称性,只要双方设备都是合法的,算出来的 Km 和 Km' 一定相等。这一步不需要传输 Km 本身,DDC 上只有 KSV 和 An 是明文,所以抓包也拿不到主密钥。调试时如果发现 Phase 1 之后双方算出的 Km 不一致,优先怀疑三件事:私钥烧录错误、KSV 的平衡性被破坏、DDC 读写时字节序搞反了。尤其是字节序,KSV 是 40 bit,跨 5 个字节,很多芯片的寄存器是按小端排列的,手册里不一定写得很清楚。

2.3 Ks 导出与混淆函数

Km 算出来之后还不能直接拿来加密,规范要求再做一次密钥导出,得到 56 bit 的Ks。这个导出过程是一组固定的混淆运算,输入是 Km,输出是 Ks,双方各自算,不需要交互。混淆函数的核心思路是把 56 bit 拆成 7 个字节,做多轮按位混合和循环移位,每一轮引入一个固定的轮常数。

我不建议在文章里贴完整的查表实现,因为不同芯片厂商的硬件加速器可能已经把这一步做进硅片里了,软件只需要把 Km 写进密钥寄存器。但如果你是做 FPGA 或者软件模拟,下面这个结构可以帮助理解它的计算量:

void hdcp_ks_from_km(const uint8_t km[7], uint8_t ks[7]) { uint8_t tmp[7]; memcpy(tmp, km, 7); for (int round = 0; round < 3; round++) { for (int i = 0; i < 8; i++) { // 每一轮做字节混合、异或轮常数、循环移位 hdcp_mix_byte(tmp, i, round); } } memcpy(ks, tmp, 7); }

这段代码只是结构示意,真正的混合函数要严格对照规范里的表。实际调试时,如果你怀疑 Ks 算错了,最直接的办法是找一台已知正常的发送端和接收端,抓下 Phase 2 的 R0 值,然后用自己的实现复算一遍,比对是否一致。R0 对 Ks 和 An 非常敏感,任何一位算错都会导致校验失败。

注意:Ks 是 56 bit,但很多芯片的密钥寄存器是按 64 bit 对齐的,写入时高位补 0 还是补随机值,要以芯片手册为准。补错会导致内容加密阶段出现规律性花屏,而不是直接认证失败,这种问题最难查。

3. 认证流程拆解:AKE 三个阶段到底在干什么

HDCP 1.4 的认证过程,规范里叫 AKE,Authentication and Key Exchange。它不是一个原子操作,而是分成三个阶段,每个阶段都有明确的输入、输出和校验点。很多调试工具只告诉你“认证失败”,不告诉你失败在哪个阶段,所以你需要自己知道每个阶段该看哪个寄存器。

3.1 Phase 1:An、Aksv 与 Km 的交换

Phase 1 的目标是让双方各自算出 Km。发送端先产生一个 64 bit 的随机数An,写到接收端的 An 寄存器里。然后发送端读接收端的 BKSV,再把自己的 Aksv 写到接收端的 Aksv 寄存器。接收端检测到 Aksv 写入后,用 Aksv 和自己的私钥算出 Km',同时保存 An 备用。

这一步在 DDC 上的交互顺序是有讲究的。An 必须先写,Aksv 后写,因为接收端看到 Aksv 写入就会触发内部计算,如果此时 An 还没准备好,算出来的 R0 就会错。有些接收端芯片对写入顺序很敏感,顺序反了不会报错,只会默默算出一个错误的值,然后 Phase 2 校验失败。我在调试时遇到过一台显示器,必须把 An 分两次写,每次 4 字节,一次性写 8 字节就会丢数据,后来查手册才发现它的 I2C 缓冲区只有 4 字节。

3.2 Phase 2:R0 校验

双方都有 Km 之后,各自用 Km 导出 Ks,再用 Ks 和 An 通过 HDCP 加密模块算出一个 16 bit 的R0。接收端把 R0 放在寄存器里,发送端读出来和自己的计算结果比对。如果一致,说明双方的密钥、随机数、加密模块全部对齐;如果不一致,认证就此终止。

R0 校验是整条链路里最灵敏的探针。它不依赖中继器拓扑,也不依赖下游设备,只跟当前这一段链路有关。所以排查时我习惯先看 R0:R0 过了,再去看中继器和 SRM;R0 没过,就不要浪费时间查拓扑,问题一定在密钥、时序或 DDC 读写上。

R0 的计算可以粗略理解为:

uint16_t r0 = hdcp_cipher_16bit(ks, an);

这里的hdcp_cipher_16bit不是简单的哈希,而是把 Ks 和 An 送进 HDCP 加密模块,跑固定轮数后取 16 bit 输出。具体轮数和取位方式以规范为准,不同实现如果有硬件加速器,直接调寄存器即可。

3.3 Phase 3:R0' 与进入加密

Phase 2 通过之后,接收端还会再报一个R0'。R0' 是接收端用自己的 Ks 和 An 算出来的另一个校验值,发送端读回来做最后确认。R0' 通过,认证才算真正完成,发送端可以开始往 TMDS 上送加密内容。

这里有一个容易忽略的细节:R0' 通过之前,发送端不应该发送加密内容,也不应该让接收端进入解密状态。有些方案为了缩短黑屏时间,会在 Phase 2 通过后就提前开加密,结果接收端还没准备好,前几帧解出来是花屏。规范允许发送端在认证完成前发送未加密的黑色画面或静音,这是更稳妥的做法。

3.4 时序与超时处理

HDCP 1.4 没有规定认证必须在多少毫秒内完成,但实际产品里通常会设一个超时,比如 100 ms 到 500 ms。超时之后如果还没走完,发送端会重新发起认证,或者向上层报告链路失败。调试时如果看到日志里反复出现“authentication restart”,不要只看认证本身,还要看 HPD 是不是在抖。HPD 一抖,整个认证状态机就会复位,看起来像是认证失败,实际是热插拔检测出了问题。

提示:用示波器或者逻辑分析仪同时抓 HPD 和 DDC 的 SCL/SDA,能很快区分“认证算错了”和“认证被打断了”。前者 DDC 上有完整的读写序列,后者往往在某个阶段突然中断。

4. 内容加密:LFSR、Ci 和 Ri/Rj 的同步机制

认证通过只是拿到了 Ks,真正的内容加密是另一套机制。HDCP 1.4 用的是基于 LFSR 的流密码,加密对象是 TMDS 上的像素数据。理解这套机制,才能解释为什么“认证通过但画面闪”这种问题会反复出现。

4.1 加密模块的结构与每像素 24 bit

HDCP 1.4 的加密模块由三个 LFSR 组成,长度分别是 24 bit、20 bit 和 20 bit。三个 LFSR 每个时钟输出 1 bit,组合之后得到 1 bit 密钥流。一个像素是 24 bit,所以加密一个像素需要跑 24 个时钟,产生 24 bit 密钥流,再和像素逐位异或。

这个设计的好处是硬件实现非常省资源,不需要大块 SRAM 做密钥扩展;缺点是它依赖严格的同步,发送端和接收端的 LFSR 状态必须完全一致,差一个时钟就会导致后续所有像素解错。实际产品里,LFSR 的初始状态由 Ks、An 和帧计数器共同决定,任何一项不一致,画面就会从某个位置开始花。

4.2 帧密钥与 Ri/Rj 更新

HDCP 1.4 不是用一个 Ks 加密到底,而是每 128 帧更新一次密钥。更新的时候会用到Ri和Rj两个寄存器。发送端和接收端各自维护一套 LFSR 状态,每过 128 帧,双方用当前状态算出新的 Ri,然后在 Ri 和 Rj 之间切换。接收端会把当前的 Ri 放在寄存器里,发送端可以读回来做同步校验。

这个机制的目的是限制单次密钥泄露的影响范围,但也带来了一个副作用:如果链路在 128 帧边界附近出现误码,双方的状态可能不同步,画面会突然黑一下,然后恢复。很多“每隔几秒闪一次”的问题,根源就在这里。排查时可以先降低分辨率或刷新率,看闪烁周期是否变化。如果周期跟着帧率变,基本可以确定是 Ri/Rj 同步问题,而不是 DDC 认证问题。

4.3 同步丢失的典型现象

同步丢失的表现很有规律:认证日志是正常的,DDC 上没有任何报错,但画面会周期性出现横条纹、花屏或者短暂黑屏。用逻辑分析仪抓 TMDS 的误码率,可能会看到误码集中在某个时间段。这时候不要急着换线,先确认发送端和接收端的加密使能时机是否一致,以及 Ri/Rj 的读取是否在正确的帧边界进行。

我的经验是,遇到这类问题先做三组对照:换一台已知正常的接收端,换一台已知正常的发送端,换一根短一点的线。如果三组对照里只有某一组异常,问题就锁定在那个设备上;如果三组都异常,问题可能在发送端的加密时序或者固件版本上。

5. 中继器与拓扑校验:KSV FIFO、V 值和 SHA-1

中继器是 HDCP 1.4 调试里最容易翻车的地方。普通接收端只需要走完 AKE 三阶段就能开加密,中继器还要额外上报下游拓扑,发送端要校验 KSV 列表、V 值和 SHA-1 哈希。这三个校验点任何一个不过,认证都会失败,而且失败日志往往只给一个笼统的错误码。

5.1 Repeater 的额外认证环节

中继器在上游看来是一个接收端,但它自己下面还挂着一个或多个设备。上游发送端需要知道整个下游拓扑里有哪些设备,才能判断有没有被吊销的设备,以及拓扑深度是否超限。这个信息通过KSV FIFO上报,FIFO 里按顺序存放下游所有设备的 KSV,每个 KSV 5 字节。

中继器在 AKE 三阶段通过之后,会把自己的 Ainfo 寄存器里的 Repeater 位置 1,告诉发送端“我是中继器”。发送端看到这个标志,就会进入中继器认证流程:先等中继器把下游设备枚举完,再读 KSV FIFO 和哈希。枚举时间取决于下游设备的数量和 HPD 状态,如果下游设备还在认证,中继器可能迟迟不置 READY 位,发送端就会一直等。

5.2 V 值计算与 SHA-1 校验

V 值是一个 40 bit 的校验值,计算方式是把发送端 BKSV、接收端 BKSV 以及 FIFO 里所有下游 KSV 逐位异或。中继器算出一个 V,放在 Bstatus 里上报;发送端自己也算一个 V,两者比对。这一步的目的是防止 FIFO 在传输过程中被篡改或者读错。

SHA-1 校验则是把 Bstatus 和整个 KSV FIFO 拼在一起做 SHA-1,取前 20 字节,和接收端上报的哈希寄存器比对。这一步比 V 值更严格,因为它覆盖了 FIFO 的顺序和全部内容。实际调试时,如果 V 值过了但 SHA-1 没过,优先怀疑 FIFO 读取长度不对,或者 Bstatus 的字节序反了。

校验项覆盖范围常见失败原因
V 值所有 KSV 的异或FIFO 少读、多读、KSV 字节序错误
SHA-1Bstatus 加完整 FIFOFIFO 顺序错、长度错、Bstatus 字段取错
吊销列表每个 KSV 是否在 SRM 中SRM 版本旧、接收端未正确加载 SRM

5.3 拓扑深度与设备数限制

HDCP 1.4 对拓扑深度和设备数都有上限。深度限制是为了防止中继器级联过多导致认证时间不可控,设备数限制则是为了避免 KSV FIFO 溢出。规范里给的上限是 127 台设备、7 层深度,但实际产品里很多芯片只支持更小的值,

返回列表