转发 DRM 内容 ID

DRMK 系统驱动程序会解译包含受保护内容的音频播放流。 DRMK 会实现 KS 筛选器,该筛选器采用包含乱序数据的输入流、解译它,并将解译后的流馈送到包含一些内核驻留模块的数据路径中。 这些模块可以是 KS 筛选器或其他类型的驱动程序。 数据路径通常以音频呈现设备结尾,将数字内容转换为可以通过扬声器播放的模拟信号。

在允许解译内容进入数据路径之前,DRMK 会验证数据路径是否安全。 为此,DRMK 对数据路径中的每个模块进行身份验证,从数据路径上游端的模块开始,并将下游移到数据路径的另一端。 下图阐释了此过程。

Diagram illustrating a secure data path with authentication process.

在上图中,实心箭头表示数据路径,虚线箭头表示验证数据路径是否安全所需的通信。 只有在 DRMK 完成对该路径中所有模块的身份验证后,解译数据才会进入该路径。

DRMK 对每个模块进行身份验证后,该模块会向 DRMK 提供有关数据路径中下一个模块的信息,以便还可以对其进行身份验证。 每个模块都经过身份验证后,它会收到标识流的 DRM 内容 ID。

从安全数据路径的上游端开始,DRMK 会将内容 ID 转发到模块 A,后者反过来又将内容 ID 转发到模块 B。此过程会一直持续到内容 ID 转发到模块 Z(安全数据路径中的最后一个模块)。

下图显示了数据路径中的一对相邻模块。

Diagram showing the process of forwarding a content ID between adjacent modules.

上游端的模块调用以下 DRM 函数之一,向 DRMK 提供有关下游模块的信息,并将内容 ID 转发到该模块:

DrmForwardContentToDeviceObject

DrmForwardContentToInterface

DrmAddContentHandlers

其中每个“转发”函数都为 DRMK 提供 DRM 内容 ID,用于标识受保护流,以及 DRMK 对下游模块进行身份验证所需的信息。 要调用的这三个函数中的哪一个取决于两个相邻模块用来相互通信的接口类型,因为它们管理受保护内容的传输:

  1. 如果上游模块调用 IoCallDriver 来与下游模块通信,则下游模块是 WDM 驱动程序的一部分。 在这种情况下,上游模块调用 DrmForwardContentToDeviceObject,以便为 DRMK 提供表示下游模块的设备对象。 DRMK 使用设备对象对下游模块进行身份验证。

  2. 如果两个模块通过下游模块实现的 COM 接口进行通信,则上游模块会调用 DrmForwardContentToInterface。 此调用为 DRMK 提供指向下游模块 COM 接口的指针。 DRMK 只调用此接口中的 IUnknown 方法,对其他方法没有假设,尽管两个模块本身必须就这些方法执行的操作达成一致。 DRMK 会验证接口中每个方法的入口点是否属于经过身份验证的模块。 如果入口点分布在多个模块之间,DRMK 会对所有这些模块进行身份验证。

  3. 如果两个模块既不使用 COM 接口也不使用 IoCallDriver 函数进行通信,则上游模块会调用 DrmAddContentHandlers,为 DRMK 提供下游模块中实现的“内容处理程序”入口点列表。 DRMK 不会调用内容处理程序,也不对它们执行的函数做出任何假设。 但是,DRMK 确实对入口点所在的一个或多个模块进行身份验证。

进行身份验证后,下游模块需要以下信息:

  • 标识包含受保护内容的流的 DRM 内容 ID。 该模块要求此 ID 通知 DRMK 它计划向其发送受保护内容的任何模块(下游)。

  • 与受保护内容关联的 DRM 内容权限。 该模块需要内容权限才能强制实施适当的安全级别。

这三个转发函数中的每一个都以略有不同的方式向模块提供此信息:

  1. DrmForwardContentToDeviceObject 函数将 KSPROPERTY_DRMAUDIOSTREAM_CONTENTID set-property 请求发送到下游模块的设备对象。 此请求将流的内容 ID 和内容权限转发到下游模块。

  2. DrmForwardContentToInterface 函数查询下游模块的 COM 接口以获取 IDrmAudioStream 接口。 如果查询成功,该函数将调用 IDrmAudioStream::SetContentId 方法,将内容 ID 和内容权限转发到下游模块。

  3. 对于 DrmAddContentHandlers 函数,调用方(上游模块)负责将流的内容 ID 和内容权限转发到下游模块。 DrmAddContentHandlers 返回一个成功代码,指示下游模块已经过身份验证后,上游模块通过调用其中一个内容处理程序将内容 ID 和内容权限传递给下游模块。

如果上游模块是 WaveCyclic 或 WavePci 微型端口驱动程序,则可以通过以下方法之一间接调用适当的 DRM 函数:

IDrmPort2::ForwardContentToDeviceObject

IDrmPort::ForwardContentToInterface

IDrmPort2::AddContentHandlers

有关详细信息,请参阅 DRM 函数

为简单起见,前面的讨论假定数据路径中的每个模块都接受来自单个源的流,并将该流转发到最多一个下游模块。 事实上,模块可以将流转发到两个或更多个下游模块,但它必须首先通过调用三个转发函数之一对每个下游模块进行身份验证。 同样,模块可以混合多个输入流,但它必须尊重输入流的内容权限,方法是向混合输出流提供适当的保护级别。 有关详细信息,请参阅内容 ID 和内容权限中对 DrmCreateContentMixed 函数的讨论。

典型的安全数据路径由 KMixer 系统驱动程序后跟表示音频呈现设备的波形筛选器组成。 该筛选器作为 WaveCyclic 或 WavePci 微型端口驱动程序实现,并结合相应的端口驱动程序。 为验证数据路径是否安全,DRMK 会将内容 ID 转发到 KMixer,后者又会将内容 ID 转发到筛选器。 实现通用筛选器功能的端口驱动程序会接收内容 ID 并将其转发到微型端口驱动程序。 具体而言,端口驱动程序会调用 DrmForwardContentToInterface 函数,将内容 ID 转发到微型端口驱动程序已实例化以表示音频呈现设备上的波形输出引脚的流对象。 此调用的参数值之一是指向流对象的 IMiniportWaveCyclicStreamIMiniportWavePciStream 接口的指针。 通过此接口,该函数会查询流对象以获取其 IDrmAudioStream 接口,并调用该接口的 SetContentId 方法。

有关详细信息,请参阅 Sysvad 示例驱动程序中 SetContentId 方法的实现,这在示例音频驱动程序中进行了讨论。