基于GPU的(视频)内容保护

本主题介绍图形驱动程序可以提供的视频内容保护功能。

简介

下图显示了受保护视频内容如何通过管道呈现的简化视图。

 备注

此图中未描述 受保护的媒体路径 (PMP) 。 此处显示的数据流可能在 PMP 进程或应用程序进程中发生。

解码器从外部源接收加密的压缩视频数据。 此外,解码器还会收到用于解密此数据的加密密钥。 本主题不介绍视频源和解码器之间的密钥交换,但 PMP 定义了一种可能的机制。 此阶段不涉及 GPU。

对于硬件加速解码,软件解码器会将压缩的视频内容传递到 GPU。 为了保护此内容,解码器在将数据传递到硬件加速器之前,通常会使用 AES-CTR 重新加密数据。 解码器和图形驱动程序之间定义了密钥交换机制。

解码的视频帧存储在视频内存中,通常存储在清除中。 此时,将处理帧,然后呈现。 演示有两个主要选项。

  • 可以使用硬件覆盖来呈现帧。 有关详细信息,请参阅 硬件覆盖支持
  • 桌面窗口可以使用共享图面管理 (DWM) 来呈现帧。

最后一步是在显示器上显示帧,这可能需要在图形卡和显示设备之间建立链接保护。 链接保护的示例是High-Bandwidth数字内容保护 (HDCP) 。 使用 输出保护管理器 配置链接保护 (OPM) 。 本主题不介绍 OPM;有关详细信息,请参阅 使用 Output Protection Manager

解码过程概述

在硬件加速解码期间,软件解码器必须将压缩的视频数据传递给图形卡。 对于高级内容,在发送到 GPU 之前,通常必须使用对称密钥加密来加密此数据。

若要加密视频进行解码,软件解码器使用以下接口:

所有这些接口均从 Direct3D 设备获取,如下所示:

接口创建
IDirectXVideoDecoder调用 IDirectXVideoDecoderService::CreateVideoDecoder。 DXVA 解码器设备由 DXVA 配置文件 GUID 标识。
IDirect3DCryptoSession9调用 IDirect3DDevice9Video::CreateCryptoSession
IDirect3DAuthenticatedChannel9调用 IDirect3DDevice9Video::CreateAuthenticatedChannel

 备注

若要获取指向 IDirect3DDevice9Video 接口的指针,请在 D3D9Ex 设备上调用 QueryInterface 。

经过身份验证的通道在软件解码器和驱动程序之间提供受信任的通信通道。 通信通道的工作原理如下:

  • 驱动程序提供 X.509 证书链,其根证书由 Microsoft 签名。
  • 证书包含驱动程序的 RSA 公钥。
  • 软件解码器使用公钥向驱动程序发送 128 位 AES 会话密钥。
  • 软件解码器将查询和命令发送到经过身份验证的通道。
  • 会话密钥用于计算查询和命令 (MAC) 的消息身份验证代码。 驱动程序使用 MAC 来验证查询/命令数据的完整性,软件解码器使用它们来验证驱动程序响应数据的完整性。

加密解码器的压缩视频缓冲区

下面是加密和解码过程的概要概述:

  1. 软件解码器从视频源接收加密的数据流。 解码器解密此流。

  2. 软件解码器使用加密会话协商会话密钥。

  3. 软件解码器使用经过身份验证的通道将加密会话与 DXVA 解码器设备相关联。

  4. 软件解码器将压缩的数据置于 DXVA 缓冲区中,该缓冲区从 DXVA 解码器设备 (加速器) 。 对于受保护的内容,软件编码器使用会话密钥加密放入 DXVA 缓冲区的数据。

     备注

    某些驱动程序使用内容密钥而不是会话密钥进行加密。 内容密钥可能会从一个帧更改为下一帧。

  5. 解码器将加密的压缩缓冲区提交到加速器。 对于 AES-CTR,解码器还会传递初始化向量。 如果使用内容密钥,解码器会传递使用会话密钥加密的内容密钥。

Direct3D 对 128 位 AES-CTR 具有标准支持,但旨在扩展到其他加密类型。

接下来的五个部分提供了更详细的步骤。

1. 查询驱动程序的内容保护功能

在尝试应用加密之前,请获取驱动程序的内容保护功能。

  1. 获取指向 Direct3D 9 设备的指针。
  2. 为 IDirect3DDevice9Video 接口调用 QueryInterface
  3. 调用 IDirect3DDevice9Video::GetContentProtectionCaps。 此方法使用驱动程序的内容保护功能填充 D3DCONTENTPROTECTIONCAPS 结构。

具体而言,请查找以下功能:

  • 如果 Caps 成员包含 D3DCPCAPS_SOFTWARE 或 D3DCPCAPS_HARDWARE 标志,驱动程序可以执行加密。
  • KeyExchangeType 成员指定如何对会话密钥执行密钥交换。
  • 如果 Caps 成员包含 D3DCPCAPS_CONTENTKEY 标志,驱动程序将使用单独的内容密钥进行加密。 生成会话密钥时,这一点很重要。

Caps 成员中指示了其他功能。

2.配置经过身份验证的通道

下一步是配置经过身份验证的通道。

  1. 调用 IDirect3DDevice9Video::CreateAuthenticatedChannel 以创建经过身份验证的通道。 对于 ChannelType 参数,请指定与驱动程序的功能匹配的通道类型。

    • D3DAUTHENTICATEDCHANNEL_DRIVER_SOFTWARE通道类型对应于D3DCPCAPS_SOFTWARE
    • D3DAUTHENTICATEDCHANNEL_DRIVER_HARDWARE通道类型对应于D3DCPCAPS_HARDWARE

    CreateAuthenticatedChannel 方法返回指向 IDirect3DAuthenticatedChannel9 接口以及通道句柄的指针。 句柄稍后用于将加密会话与经过身份验证的通道相关联。

  2. 调用 IDirect3DAuthenticatedChannel9::GetCertificateSize 以获取驱动程序的 X.509 证书的大小。 分配所需大小的缓冲区。

  3. 调用 IDirect3DAuthenticatedChannel9::GetCertificate 以获取证书。 该方法将证书复制到上一步中分配的缓冲区中。

  4. 验证驱动程序的证书是否已由 Microsoft 签名,并且尚未吊销。

  5. 从证书获取公钥。

  6. 生成随机 RSA 会话密钥。 此会话密钥用于对发送到经过身份验证的通道的数据进行签名。 使用驱动程序的公钥加密会话密钥。

  7. 调用 IDirect3DAuthenticatedChannel9::NegotiateKeyExchange 将加密的会话密钥发送到驱动程序。

  8. 初始化安全通道,如下所示:

    1. 按照文档中所述填写 D3DAUTHENTICATEDCHANNEL_CONFIGUREINITIALIZE 结构。
    2. 调用 IDirect3DAuthenticatedChannel9::Configure,如“发送经过身份验证的通道命令”部分中所述,发送D3DAUTHENTICATEDCONFIGURE_INITIALIZE命令。 此命令包含发送到经过身份验证的通道的命令和查询的起始序列号。
  9. 通过向经过身份验证的通道发送 D3DAUTHENTICATEDQUERY_CHANNELTYPE 查询来验证通道类型,如 “发送经过身份验证的通道查询”部分中所述。 检查通道类型是否与 CreateAuthenticatedChannel 方法中指定的内容匹配。

3.配置加密会话

接下来,配置加密会话并建立会话密钥。

  1. 调用 IDirect3DDevice9Video::CreateCryptoSession 以创建加密会话。 此方法返回指向 IDirect3DCryptoSession9 接口以及加密会话句柄的指针。
  2. 调用 IDirect3DCryptoSession9::GetCertificateSize 以获取驱动程序 X.509 证书的大小。 分配所需大小的缓冲区。
  3. 调用 IDirect3DCryptoSession9::GetCertificate 以获取证书。 该方法将证书复制到上一步中分配的缓冲区中。
  4. 验证驱动程序的证书是否已由 Microsoft 签名,并且尚未吊销。
  5. 从证书获取公钥。
  6. 生成随机 RSA 会话密钥。 这是与经过身份验证的通道会话密钥不同的会话密钥。 使用驱动程序的公钥加密会话密钥。
  7. 调用 IDirect3DCryptoSession9::NegotiateKeyExchange 将加密会话密钥发送到驱动程序。
  8. 如果内容保护功能包括 D3DCPCAPS_CONTENTKEY,请创建随机 RSA 内容密钥。 此操作将在解码过程中稍后使用。

4.获取 DXVA 解码器设备的句柄

下一步需要 DXVA 解码器设备的句柄。 若要获取此句柄,请填写DXVA2_DecodeExecuteParams结构,如下所示:

C++复制

HANDLE hDecodeDeviceHandle;

DXVA2_DecodeExecuteParams execParams = {0};
DXVA2_DecodeExtensionData ExtensionExecute = {0};
    
execParams.NumCompBuffers = 0;
execParams.pCompressedBuffers = NULL;
execParams.pExtensionData = &ExtensionExecute;

ExtensionExecute.Function = DXVA2_DECODE_GET_DRIVER_HANDLE;
ExtensionExecute.pPrivateInputData = NULL;
ExtensionExecute.PrivateInputDataSize = 0;
ExtensionExecute.pPrivateOutputData = &hDecodeDeviceHandle;
ExtensionExecute.PrivateOutputDataSize = sizeof(HANDLE);

DXVA2_DecodeExecuteParams结构的 pExtensionData 成员设置为DXVA2_DecodeExtensionData结构的地址。

在 DXVA2_DecodeExtensionData 结构中,将 函数 成员设置为 DXVA2_DECODE_GET_DRIVER_HANDLE。 将 pPrivateOutputData 设置为足以存储 HANDLE 值的缓冲区的地址。 (在前面的示例中,此缓冲区是 hDecodeDeviceHandle 变量.)

然后调用 IDirectXVideoDecoder::Execute 并传入 DXVA2_DecodeExecuteParams 结构的地址。 DXVA 解码器的句柄在 pPrivateOutputData 中返回。

5. 将 DXVA 解码器与加密会话相关联

接下来,将 DXVA 解码器设备与 Direct3D 设备和加密会话相关联,如下所示:

  1. 获取 DXVA 解码器设备的句柄,如上一部分所述。
  2. 通过将 D3DAUTHENTICATEDQUERY_DEVICEHANDLE 查询发送到经过身份验证的通道,获取 Direct3D 设备的句柄。
  3. 使用以下信息填写 D3DAUTHENTICATEDCHANNEL_CONFIGURECRYPTOSESSION 结构:
    • 将 DXVA2DecodeHandle 成员设置为 DXVA 解码器设备的句柄。
    • 将 CryptoSessionHandle 成员设置为加密会话的句柄。 此句柄由 IDirect3DDevice9Video::CreateCryptoSession 方法返回。
    • 将 DeviceHandle 成员设置为 Direct3D 设备句柄。
  4. 调用 IDirect3DAuthenticatedChannel9::Configure 以将 D3DAUTHENTICATEDCONFIGURE_CRYPTOSESSION 命令发送到经过身份验证的通道。

下图说明了句柄的交换:

软件解码器现在可以使用加密会话密钥来加密压缩的视频缓冲区。 每个压缩缓冲区都有自己的初始化向量, (IV) 在DXVA2_DecodeBufferDesc结构的 pvPVPState 成员中指定的。

发送经过身份验证的通道命令

定义了一组命令,用于配置经过身份验证的通道和设置各种内容保护。 有关命令的列表,请参阅 内容保护命令

若要将命令发送到经过身份验证的通道,请执行以下步骤。

  1. 填写输入数据结构。 此数据结构始终是 一个D3DAUTHENTICATEDCHANNEL_CONFIGURE_INPUT 结构,后跟其他字段。 填写 D3DAUTHENTICATEDCHANNEL_CONFIGURE_INPUT 结构,如下表所示。
成员说明
omac暂时跳过此字段。
ConfigureType标识命令的 GUID。 有关命令的列表,请参阅 内容保护命令
hChannel经过身份验证的通道的句柄。
SequenceNumber序列号。 第一个序列号是通过发送 D3DAUTHENTICATEDCONFIGURE_INITIALIZE 命令指定的。 每次发送另一个命令时,将此数字递增 1。 序列号可防范重播攻击。
[!注意]
使用两个单独的序列号,一个用于命令,另一个用于查询。

 
  1. 计算在输入结构 omac 成员之后出现的数据块的 OMAC 标记。 然后将此标记值复制到 omac 成员中。
  2. 调用 IDirect3DAuthenticatedChannel9::Configure
  3. 驱动程序将命令的输出放置在 D3DAUTHENTICATEDCHANNEL_CONFIGURE_OUTPUT 结构中。
  4. 计算输出结构 omac 成员之后出现的数据块的 OMAC 标记。 将此值与 omac 成员的值进行比较。 如果不匹配,则失败。
  5. 将输出结构中的 ConfigureType、 hChannel 和 SequenceNumber 成员的值与这些成员的值进行比较。 如果不匹配,则失败。
  6. 递增下一个命令的序列号。

发送经过身份验证的通道查询

定义了一组查询,用于检索有关经过身份验证的通道的信息。 有关查询的列表,请参阅 内容保护查询

若要将命令发送到经过身份验证的通道,请执行以下步骤。

  1. 填写输入数据结构。 此数据结构始终是 D3DAUTHENTICATEDCHANNEL_QUERY_INPUT 结构,可能后跟其他字段。 填写 D3DAUTHENTICATEDCHANNEL_QUERY_INPUT 结构,如下表所示。
成员说明
QueryType标识查询的 GUID。 有关查询的列表,请参阅 内容保护查询
hChannel经过身份验证的通道的句柄。
SequenceNumber序列号。 第一个序列号是通过发送 D3DAUTHENTICATEDCONFIGURE_INITIALIZE 命令指定的。 每次发送另一个查询时,将此数字递增 1。 序列号可防范重播攻击。
[!注意]
使用两个单独的序列号,一个用于命令,一个用于查询。

 
  1. 调用 IDirect3DAuthenticatedChannel9::Query
  2. 驱动程序将查询的输出置于 D3DAUTHENTICATEDCHANNEL_QUERY_OUTPUT 结构中。 此结构后跟其他字段,具体取决于查询类型。
  3. 计算输出结构 omac 成员之后出现的数据块的 OMAC 标记。 将此与 omac 成员的值进行比较。 如果它们不匹配,则失败。
  4. 将输出结构中的 ConfigureType、 hChannel 和 SequenceNumber 成员的值与这些成员的值进行比较。 如果它们不匹配,则失败。
  5. 递增下一个查询的序列号。

Direct3D 9 视频 API

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值