hdcp部分加密算法介绍

hdcp简介

HDCP(High-bandwidth Digital Content Protection,高带宽数字内容保护)HDCP 是 Intel 公司制定的,能够避免数字接口传输的音视频被非法复制。这些数字接口包括 HDMI,DVI,
DisplayPort 等。HDCP 要求音视频在接收端与发送端之间进行加密传输。

hdcp算法介绍

1.密码学算法

本文简单介绍hdcp涉及的加密算法流程,主要是dh和sha-1,后续可能会补充下des,rsa,hmac,伪随机数发生器,lfsr等内容。

1.1.Diffie-Hellman密钥交换算法

DH算法属于公钥加密的算法,公钥加密,私钥解密。
DH算法基于离散对数,假设a,p均为素数,则有如下的等式:
eq?%5Cleft%20%5C%7B%20a%5E%7B1%7D%20mod%20p%2C%20a%5E%7B2%7D%20mod%20p%2C%20...%20%2C%20a%5E%7Bp%20-%201%7D%20mod%20p%5Cright%20%5C%7D%20%3D%20%5Cleft%20%5C%7B1%2C%202%2C%20...%2C%20p%20-%201%5Cright%20%5C%7D 

{}表示集合,这个公式很好理解,一个数mod一个素数p,其值一定不为0,只可能是1 和 p-1之间的数。

有 eq?%28%28a%5E%7Bx%7Dmod%20p%29%5E%7By%7D%29mod%20p%20%3D%20a%5E%7Bxy%7D%20mod%20p,证明如下:

eq?a%5E%7Bxy%7Dmodp%20%3D%20%28a%5E%7Bx%7D%20*%20a%5E%7Bx%7D%20...%20a%5E%7Bx%7D%29%20modp%20%3D%20a%5E%7Bx%7Dmodp%20*%20a%5E%7Bx%7Dmodp%20%3D%20%28a%20%5E%7Bx%7Dmodp%29%20%5E%20%7By%7Dmodp

3a953526ca9d4e58a3cd9e76721992df.jpeg

两者计算得到的一定相等,后面我们可以看到,hdcp第一阶段的交换过程的原理与之类似。

1.2 sha-1算法介绍及其c代码实现

  sha-1,全称Secure Hash Algorithm,sha-1生成160bit的数据,sha-256生成256bit的数据,相比与sha256而言,sha-1安全性是比不上的,但是sha-1和sha-256大致流程类似,sha-1更容易理解其流程,更容易实现。

  sha-1,输入为长度为0-2^64bit的消息,输出160bit的消息摘要,大致流程如下:

  将消息m分组,每组512bit,最后一组不够512bit,在最后一组补1,其余位补0,直到满足最后一组的L mod 512 = 448;512 - 448 = 64,64为预留的消息的长度。

770dc32993f845cbae3233c4f6dd3770.png

接着一组一组的进行处理,将每组的512bit分成16 * 32,也就是16份,每份32bit,进行预处理,按下图所示的方式处理成80组,

       73811589192c41babcb22199fee782b8.png

20265bb8006f4ddbb17d46201b8504c4.png

接着初始化5个常量,用于计算

将上面初始化的常量赋值给abced,再按如下图所示的方式进行80轮迭代

d3530703cdfb4f51994323a026a1b1f5.png

5a818c679692428789f72cea27c20e7d.png

ef1d420736794e5d83268e215f668d69.png

sha-1的代码如下:

#include<stdio.h>
/*定义用到的常量*/
unsigned int A = 0x67452301, B = 0xEFCDAB89, C = 0x98BADCFE, D = 0x10325476, E = 0xC3D2E1F0;        //第一次迭代的链接变量
unsigned int K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6};                              //循环中用到的常量
unsigned int A0 = 0x67452301,B0 = 0xEFCDAB89,C0 = 0x98BADCFE,D0 = 0x10325476,E0 = 0xC3D2E1F0;
/*字节转字*/
int CharToWord(unsigned char *character, int first)
{
	int temp = (((int)character[first] & 0x000000ff) << 24) |
		(((int)character[first + 1] & 0x000000ff) << 16) |
		(((int)character[first + 2] & 0x000000ff) << 8) |
		((int)character[first + 3] & 0x000000ff);
	return temp;
}
/*f函数*/
unsigned int f(int B, int C, int D, int t)
{
	return (t >= 0 && t <= 19) ? ((B&C) | (~B&D)) : ((t >= 20 && t <= 39) ? (B ^ C ^ D) : ((t >= 40 && t <= 59) ? ((B&C) | (B&D) | (C&D)) : ((t >= 60 && t <= 79) ? B ^ C ^ D : 0)));
}

/*从W消息分组中导出*/
void GetExtraMessage(unsigned int w[])
{
	for (int i = 16; i < 80; i++)
		w[i] = ((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]) << 1) |
		((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]) >> 31);
}

/*分组*/
void DevideToGroup(unsigned char *plaintext,unsigned int *group,int length)
{
	//512 - 64 = 448;
	//448 - (length + 1) 0的个数
	//填充
	
	unsigned char temp[64] = {0};
	for (int i = 0; i < length; i++)
		temp[i] = plaintext[i];
	temp[length] = 0x80;
	int len = length * 8;
	for (int i = 0; i <= 7 && len; i++) {
		temp[63 - i] = len & 0xff;
		len = (len >> 8);
	}

	for (int i = 0; i < 16; i++)
		group[i] = CharToWord(temp, i * 4);
	
}
unsigned int GetK(int t)
{
	return (t >= 0 && t <= 19) ? K[0] : ((t >= 20 && t <= 39) ? K[1] : ((t >= 40 && t <= 59) ? K[2] : ((t >= 60 && t <= 79) ? K[3] : 0)));
}
/*步函数*/
unsigned int StepFunction(unsigned int w[],int t)
{
	unsigned int temp = ((A << 5)|(A >> 27)) + f(B, C, D, t) + E + w[t] + GetK(t);
	E = D,D = C,C = ((B << 30) | (B >> 2)),B = A,A = temp;
	printf("第%2d轮加密后的密文是%08X %08X %08X %08X %08X\n", t + 1, A, B, C, D, E);
}
/*密文组合*/
unsigned int CombineCipher(unsigned int cipher[])
{
	for (int i = 0; i <= 4; i == 0 ? cipher[i] = (A0 + A) : (i == 1 ? cipher[i] = (B0 + B) : (i == 2 ? cipher[i] = (C0 + C) : (i == 3 ? cipher[i] = (D0 + D) : (i == 4 ? cipher[i] = (E0 + E) : 0)))), i++);
}
int main()
{
	int i, t, j, length, temp;
	unsigned char plaintext[64] = "abc", hash[20] = { 0 };
	unsigned int group[80] = { 0 }, cipher[5] = { 0 };
	/*长度填充到最后*/
	printf("当前明文字符串为:");
	for (i = 0; plaintext[i]; length = i + 1,i++)
	{
		printf("%c", plaintext[i]);
		group[15] = (i + 1) * 8;          //长度是二进制位数		
	}
	/*分组填补*/
	printf("\n\n0x00 正在分组…………\n****************************");
	DevideToGroup(plaintext, group, length);
	/*从w消息分组中导出*/
	GetExtraMessage(group);
	/*80次步函数*/
	printf("\n0x01 SHA-1加密中…………\n****************************\n");
	for (t = 0; t < 80; t++)
	{
		StepFunction(group, t);
		if (!((t + 1) % 20)) printf("\n");
	}
	/*密文组合*/
	printf("\n0x02 hash值组合中…………\n****************************\n");
	CombineCipher(cipher);
	/*密文输出*/
	printf("\n0x03 SHA-1加密成功!!!\n****************************\nHash:");
	for (j = 0; j <= 4; j++) printf("%08X", cipher[j]);
	printf("\n");
	return 0;
}

运行结果如下:

684f8813979e4c5bb60d18695228d462.png

890c99a588bc4558b9c83dcbaf5bf817.png

d9cdde77218a43c59b4729080e4822cd.png

文献给的算例结果如下:

85671a6e2d634d46af11e5f9f3fa842a.png

53dd90108dfb4deb85cbced8088d188f.png

339c9fe4fb0b4366a832023d9824e08b.png

2fb0d03138f74d89bc8c706a1ddc2f10.png

270b6ffa2b0a4562acd09f29e063b5ad.png

d9cdde77218a43c59b4729080e4822cd.png

可以看到,编写的c代码算出的结果与sha文献中给出的结果一致,验证了代码的正确性。

2.hdcp协议流程&代码注解与分析

  hdcp流程参考上海交大的论文基于 HDMI HDCP 认证及加密的软件实现以及hdcp1.4的spec来进行讲解,介绍hdcp1.4的流程与实现,hdcp1.4一般是用硬件来实现的,软件做的话会比较慢。交大的这篇论文最后的实验结果是跑在intel core i5双核超线程2.8G处理器上,用优化后的软件版本,软件在1920x1080的高清分辨率下,达不到60帧。说明软件实现速度是不够了,距离这篇论文过去十几年,电脑以及芯片性能肯定有提升,但跑在芯片上,最多只能支持hdmi1.4,hdmi2.0的4k60用软件去跑负载肯定会非常重,更不用提hdmi2.1了。

2.1 hdcp1.4流程

2.1.1 hdcp1.4第一阶段

  上图是第一阶段的交互过程, HDCP 发送器设备 A 可以随时发起认证,即使 之前已经有一次认证已经结束。认证的发起从 HDCP 发送器发送一个初始消息开始,这 个初始的认证消息包括设备A 的密钥选择向量 AKSV 和一个 64bit 的随机数。这个 64bit 的随机数是由 hdcp 的函 hdcpRngCipher 来产生。这个 AKsv 64bit 的随机数 An 发送到 HDCP 接收器的设备 B。
   HDCP 的接收器设备 B 接着回应这个消息,传递设备 B 的密钥选择向量 Bksv 和一个REPEATER 位表示接收设备是否为一个 HDCP 中继器,设备 B 将 BKsv 和 REPEATER位传递给 HDCP 发送器设备 A。 HDCP 发送器设备 A 首先判断 HDCP 接收器设备 B 的密钥选择向量是否已经在泄漏列表里面。KSV 是一个 40bit 的值,其中包含 20 个 1,和20 个 0。如果这两个 HDCP 设备都有一个从 Digital Conetnt Protecton LLC 得到的有效的设备密钥数组。他们可各自计算一个 56bit 的密钥,Km 和 Km’(设备 B)。 Km 是通过把各自 KSV 所对应的密钥相加得出的,即在 KSV 里面相应 BIT 为 1 的位数对应的相应的设备密钥数组中的数据相加,严格来说就是无符号相加再取2的56次方的模。
   下面举个例子:
    
   以上表给出了rx和tx的密钥矩阵,hdcp提供了一个公钥矩阵,这个公钥矩阵的维数是40 * 40,rx和tx分别有一个ksv,称之为密钥选择子,40位,共20个0,20个1。下面给出了一个8 * 8类比的例子

看下spec给的例子:

0xb70361f714,选0,2,3,6,7,...,38,0x511ef21acd选2,4,8,9,10,...39,结果相加,写了个c代码验证,如下:

#include<stdio.h>
int main()
{
	unsigned long long k[20] = {0x4da4588f131e69, 0x8a6a47abb9980d, 0xf3181b52cbc5ca,
                                0xa0d064c8112c41, 0xb39d5a28242044, 0x91a47b4a6ce4f6,
                                0x8c7fb706ee3fa0, 0xc02d8c9d7cbc28, 0x482f8e46785498,
                                0xe2d231767b3a54, 0x4d581aede66125, 0x326082bf7b22f7,
								0xf61b463530ce6b, 0xa1e105618d49f9, 0xc98e9dd1053406,
                                0x20c36794426190, 0x964451ceac4fc3, 0x54c7ea5bdd7b43,
								0x05d3408a78fb01, 0x373b634a2c9e40};
	unsigned long long k1[20] = {0x24bf2185a36c60, 0xa72e69c5eb6388, 0x3b9857797d5103,
                                0x0dd170be615250, 0x1a748be4866bb1, 0x4bbb037899eea1,
                                0x190ecf9cc095a9, 0xa821c46897447f, 0x1a8a0bc4298a41,
                                0xaefc0853e62082, 0xe3053fa3e9fa69, 0x37d8002881c7d1,
								0x9e93d41e0811f7, 0x2c4074509eec6c, 0x1280161221df6d,
                                0xca31a5f2406589, 0x1d30e8cb198e6f, 0xcec7ec09245b43,
								0xb08129efedd583, 0xedeef9d099b78c};
	unsigned long long sum = 0;
	for (int i = 0; i < 20; i++) {
		sum = (sum + k[i]) & 0xffffffffffffff;
	}
	printf("sum = 0x%llx\n", sum);
	unsigned long long sum1 = 0;
	for (int i = 0; i < 20; i++) {
		sum1 = (sum1 + k1[i]) & 0xffffffffffffff;
	}
	printf("sum1 = 0x%llx\n", sum1);
}

可以看到,算出来的结果相等且同spec一致

   计算出km之后,根据km,repeat和An算ks,m0和r0,验证r0和rx端读到的值是不是相等,相等则继续,不等则失败。以上是第一阶段的流程。

 2.1.2 hdcp1.4第二阶段

   HDCP 的接收器是一个 HDCP 中继器的时候需要第二阶段的认证,只有 REPEATER 位被置上表明下游设备是一个 HDCP 中继器的时候,HDCP 的发 送器才会执行第二阶段的认证协议。在这个阶段通过一个 HDCP 的连接树,收集下游所有连接到 HDCP 中继器设备的 Ksv 列表。HDCP 中继器下游保护的 HDCP 接口端口完成和它连接的所有 HDCP 接收 设备的认证协协议之后,HDCP 中继器会收集一个它所连接的 HDCP 接收器的列表。这 个列表为一个连续的字节序列,每个 Ksv 占据 5 个字节,即 40 bit,按照小端的方式 排列。这个字节序列的长度为下游连接的包括 HDCP 中继器在内的 HDCP 设备的数量 的 5 倍字节。一个 HDCP 保护的接口端口如果没有活动的设备连接,那么它下面连接的 的设备不会有任何信息加入到这个列表。当然,在任何一级上的 HDCP 中继器上的 KSV 列表都不包括 HDCP 中继器本身。如果一个 HDCP 保护的接口端口连接的是一个 HDCP 接收器而不是一个 HDCP 中继器,那么就把 BKsv 加入到列表当中。如果一个 HDCP 保 护的接口端口连接的是一个 HDCP 中继器,那么就把下游 HDCP 中继器的 Ksv 列表加 入到当前列表,在加上这个下游的 HDCP 中继器的本身的Ksv

   HDCP 发送器通过计算 V SHA-1 哈希值 来验证 KSV 列表的完整性。如果这个被 SHA-1 哈希出来的值 V 不等于读取回来的 HDCP 中继器端计算的 SHA-1 哈希值 V ’,那么认证的协议会失败退出。如果 READY 状态位的有效位没有在最大可允许的 5 秒内使能,对 HDCP 中继器的认证失败。当认 证失败时,HDCP 的发送器从 HDCP 中继器的认证中退出,通信结束。当然,认证可以
通过由 HDCP 发送器向接收器发送一个 An Aksv 来随时重新进行,也就是是说重新 进行第一阶段的认证。
   本文已经详细介绍了sha-1算法是怎么算的,这里再多说几句。
    安全哈希算法(Secure Hash Algorithm)是主要应用于数字签名标准领域定义的数字签名算法。SHA1有如下特性:不能从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。任何输入消息的变化,哪怕仅有一位,都将导致散列结果的明显变化。

   HDCP协议中V(V’)计算如下:V(V’)=SHA1(ksv_list||binfo||Mo(Mo’))。

  KSV list存储于Repeater DPCD地址0x6802C~0x6803A的FIFO中,FIFO中有15字节,每个设备的KSV是40比特,也就是5字节,所以 FIFO中每次读取出来的是3个设备KSV。HDCP的Repeater最多支持下游有127个设备,也就是说FIFO最多需要读取127/3次。每次读取完后硬件会自动清掉被读走的数据,同时用下一组KSV填满。Binfo是存储下游设备拓扑结构的16比特数据,包括拓扑深度等。Mo(Mo’)是 64比特的私钥。也就是说计算V(V’)时,系统传输给SHA1的消息长度最大为127×40+16+64=5160(0x1428H)比特,对于长度小于2^64(0x40000000000000H)位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,这时候就会产生不同的消息摘要。 SHA1算法只接收位输入。字符串“KSV list||Binf||M0”消息摘要需转换成位字符串如下:01100001 01100010 01100011……010011010(5160位),SHA1根据这个消息来计算MD5。

2.1.3 hdcp认证第三阶段

   Ki,类似Ks,56bits,用于HDCP cipher对HDCP内容加密、解密。Mi,一个新的64bits 随机数,由加密函数生成,Ri,16bits,用于链路完整性检查,每128frame时更新一次。ransmitter和Receiver各自计算出 (Ki, Mi, Ri)还是通过hdcpBlockCipher计算出来。检测两端ri相不相等。

2.2 代码注解

   HDCP 的加密机是整个 HDCP 系统的核心,完成 HDCP 加密机的设计等于完成了 HDCP 认证和数据加密的基础,重点研究它有很大的意义。总的来说, HDCP 加密机 (cipher)是一个特殊用途的加密机专门设计用来保证认证协议的的健壮性,同时满足对高 速的无压缩视频流作加密的需要。
HDCP 加密机从总体结构上看可以分为 3 层。 第一层由 4 个线形反馈寄存器组组成最终得到一个位输出。在 Rekey 信号使能的情 况下,这个一位输出被送入中间层。 中间层有两个相类似的结构组成,其中的一半为由 3 28 位的寄存器 Bx By Bz 组成轮函数 B 。而另一半使用类似轮函数的结构,只是在每个时钟脉冲的时候提供一个 轮密钥给轮函数 B ,这个轮密钥的输入来自第 28 位的 Ky 。 最底层把轮函数的 By Bz Ky Kz 作为 4 28bit 的输入,通过一个压缩函数在每个时钟周期产生一个 24 位的伪随机数,可以清楚地看出这个 24 bit 的值就是一个不 断变化的流加密密钥,和真正的数据进行异或以后在用同样的方式解密 。注意到这个随机数是以时钟为单位不断随机变化的,这就加大了攻击和破解的难度。
   加密机的结构如下:
   

2.2.1 基本模块接口

HDCPBlockCipher
实现 hdcpBlockCipher 的功能。具体由多个函数构成, BlockModule 函数负责完成 block模块里面的计算,又分为 RoundFunction RoundFunctionK 分别负责 block 模块里面轮函数 B K 的计算。 LFSRModule_init 函数负责完成线形反馈寄存器模块的计算 和初始化。在认证期间,HDCPBlockCiper 的输入为 Km An REAPETER 。输出为 Ks 。 在垂直消隐期内,HDCPBlockCiper 的输入为 Ks 。软件代码如下:
hdcpBlockCipher 这个操作很重要,在认证的第一部分用来建立会话密钥 Ks ,在每个加密桢之前的垂 直消隐期内建立帧密钥 Ki
hdcpBlockCipher 的操作过程在加密机里面分为以下几个步骤:
第一步: block 模块加载 B K 寄存器
第二步:在 Block 模块里面运行 48 个时钟周期 hdcpBlockCipher
第三步:把 B 寄存器的最低 56 bit 作为以后要使用的 Ks 或者 Ki
第四步:把 B 寄存器的 84 bit 的值送入 K 寄存器
第五步:重新加载 B 寄存器
第六步:初始化线形反馈寄存器,注意这里用到了第三步的 Ks 或者 Ki
第七步:密码重置使能
第八步:线形反馈寄存器和 block 模块运行 56 个时钟周期后保存最后 4 clock
面的 64 bit Mi
第九步:密码重置被失效
上面我对照代码一一注释了,可以自行对照论文研究其细节。论文给出的表很清晰
    可以看出 x y z 由低位到高位一次组成了 B K 的轮寄存器。当轮寄存器的输出 小于 84 bit 的时候,最低有效位被使用,其他的未被使用的被置为零。比如, 65 bit 的 REPEAT 位和 An 的组合,被加载到 Bx By 寄存器和 Bz 寄存器的从最低有效位开 始的 9 bit ,加起来正好 65 bit 。而 Bz 寄存器的从最高有效位开始的 19 bit 都 被置为零。与之类似的从 Bx By hdcpBlockCipher 的认证阶段 6-9 的时候从最低有 效位的 56 bit 保存为 Ks 或者 Ki 。 可以看出在第八步运行到第 53 个时钟的时候开始输出 Mi 的最高有效位的前 16 位, 以后依次处处了 Mi Ri 的值,在第 56 个周期的时候最终确定和 Mi Ri 的所有 64 bit 的值。
   
hdcpStreamChiper
   对于被 TMDS 数据使能信号所定义的每个视频像素数据,包括信息包数据都被加密 输出一个 24 bit 的数据,在这个状态下, LFSR Block 都处于工作状态,但是 rekey 使能信号无效。
hdcpRekeyCipher
    在紧跟着有效行数据的水平消隐期的间隔里, hdcpRekeyCipher 通过 LFSR 模块把新的密钥装入 Block 模块。在 rekey 期间,没有其他的对于加密机状态的初始化,也没有 任何从加密机的输出。LFSR Block 模块运行 56 个周期,在此期间, rekey 使能信号为1
  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值