GPU-Based 內容保護
本主題描述圖形驅動程式可以提供的影片內容保護功能。
介紹
下圖顯示受保護影片內容如何經過管線轉譯的簡化檢視。
注意
此圖未描述 受保護的媒體路徑 (PMP)。 此處顯示的數據流可能會發生在 PMP 進程或應用程式進程內。
譯碼器會從外部來源接收加密、壓縮的視訊數據。 也假設譯碼器也會收到密碼編譯密鑰來解密此數據。 本主題不會描述視訊來源與譯碼器之間的密鑰交換,但 PMP 會定義一個可能的機制。 此階段未涉及 GPU。
針對硬體加速譯碼,軟體譯碼器會將壓縮的視訊內容傳遞至 GPU。 為了保護此內容,譯碼器會先重新加密數據,通常是使用 AES-CTR,再將其傳遞至硬體加速器。 譯碼器和圖形驅動程式之間定義了密鑰交換機制。
已譯碼的視訊畫面會儲存在視訊記憶體中,通常是在清除中。 此時會處理框架,然後呈現。 簡報有兩個主要選項。
- 框架可以使用硬體重疊來呈現。 如需詳細資訊,請參閱 硬體重疊支援。
- 框架可以透過使用共用介面的桌面視窗管理 (DWM) 來呈現。
最後一個步驟是在監視器上顯示框架,這可能需要圖形卡與顯示裝置之間的鏈接保護。 鏈接保護的範例是 High-Bandwidth 數位內容保護 (HDCP)。 鏈接保護是使用 Output Protection Manager 設定 (OPM)。 本主題未描述 OPM;如需詳細資訊,請參閱使用 Output Protection Manager 。
譯碼程式概觀
在硬體加速譯碼期間,軟體譯碼器必須將壓縮的視訊數據傳遞至圖形卡。 針對進階內容,此數據通常必須先使用對稱密鑰加密來加密,才能傳送至 GPU。
為了加密影片進行譯碼,軟體譯碼器會使用下列介面:
- IDirectXVideoDecoder。 代表 DXVA 譯碼器裝置,也稱為加速器。
- IDirect3DCryptoSession9。 表示提供加密金鑰的密碼編譯會話。
- IDirect3DAuthenticatedChannel9。 表示已驗證的通道,可讓軟體譯碼器將密碼編譯會話與 DXVA 譯碼器產生關聯。
所有這些介面都是從 Direct3D 裝置取得,如下所示:
介面 | 創造 |
---|---|
IDirectXVideoDecoder | 呼叫 IDirectXVideoDecoderService::CreateVideoDecoder。 DXVA 譯碼器裝置是由 DXVA 配置檔 GUID 所識別。 |
IDirect3DCryptoSession9 | 呼叫 IDirect3DDevice9Video::CreateCryptoSession。 |
IDirect3DAuthenticatedChannel9 | 呼叫 IDirect3DDevice9Video::CreateAuthenticatedChannel。 |
注意
若要取得 IDirect3DDevice9Video 介面的指標,請在 D3D9Ex 裝置上呼叫 QueryInterface。
驗證的通道會在軟體譯碼器和驅動程式之間提供受信任的通道。 通道的運作方式如下:
- 驅動程式會提供 X.509 憑證鏈結,其跟證書是由 Microsoft 簽署。
- 憑證包含驅動程式的 RSA 公鑰。
- 軟體譯碼器會使用公鑰來傳送驅動程式 128 位 AES 會話密鑰。
- 軟體譯碼器會將查詢和命令傳送至已驗證的通道。
- 會話金鑰可用來計算查詢和命令的訊息驗證碼(MAC)。 驅動程式會使用 MAC 來驗證查詢/命令數據的完整性,而軟體譯碼器會使用它們來驗證驅動程式回應數據的完整性。
加密譯碼器的壓縮視訊緩衝區
以下是加密和譯碼程式的高階概觀:
軟體譯碼器會從視訊來源接收加密數據的數據流。 譯碼器會將此數據流解密。
軟體譯碼器會與密碼編譯會話交涉會話密鑰。
軟體譯碼器會使用已驗證的通道,將密碼編譯會話與 DXVA 譯碼器裝置產生關聯。
軟體譯碼器會將壓縮的數據放入 DXVA 緩衝區中,從 DXVA 譯碼器裝置 (accelerator) 取得。 針對受保護的內容,軟體編碼器會使用會話密鑰來加密放入 DXVA 緩衝區的數據。
注意
某些驅動程式會使用內容金鑰,而不是會話金鑰進行加密。 內容索引鍵可能會從一個畫面變更為下一個畫面。
譯碼器會將加密的壓縮緩衝區提交至加速器。 針對 AES-CTR,譯碼器也會傳遞初始化向量。 如果使用內容金鑰,譯碼器會傳遞內容金鑰,並使用會話密鑰加密。
Direct3D 具有 128 位 AES-CTR 的標準支援,但設計目的是要延伸至其他加密類型。
接下來的五節提供更詳細的步驟。
1.查詢驅動程式的內容保護功能
嘗試套用加密之前,請先取得驅動程式的內容保護功能。
- 取得 Direct3D 9 裝置的指標。
- 針對 IDirect3DDevice9Video 介面呼叫 QueryInterface。
- 呼叫 IDirect3DDevice9Video::GetContentProtectionCaps。 此方法會以驅動程式的內容保護功能填入 D3DCONTENTPROTECTIONCAPS 結構。
特別是,尋找下列功能:
- 如果 Caps 成員包含 D3DCPCAPS_SOFTWARE 或 D3DCPCAPS_HARDWARE 旗標,則驅動程式可以執行加密。
- KeyExchangeType 成員會指定如何執行會話密鑰的金鑰交換。
- 如果 Caps 成員包含 D3DCPCAPS_CONTENTKEY 旗標,驅動程式會使用個別的內容密鑰進行加密。 當您產生會話金鑰時,這很重要。
Caps 成員中會指出其他功能。
2.設定已驗證的通道
下一個步驟是設定已驗證的通道。
呼叫 IDirect3DDevice9Video::CreateAuthenticatedChannel 來建立已驗證的通道。 針對 ChannelType 參數,指定符合驅動程式功能的通道類型。
- D3DAUTHENTICATEDCHANNEL_DRIVER_SOFTWARE 通道類型對應至 D3DCPCAPS_SOFTWARE。
- D3DAUTHENTICATEDCHANNEL_DRIVER_HARDWARE 信道類型對應至 D3DCPCAPS_HARDWARE。
CreateAuthenticatedChannel 方法會傳回 IDirect3DAuthenticatedChannel9 介面的指標,以及通道的句柄。 稍後會使用句柄將密碼編譯會話與已驗證的通道產生關聯。
呼叫 IDirect3DAuthenticatedChannel9::GetCertificateSize,以取得驅動程式 X.509 憑證的大小。 配置所需大小的緩衝區。
呼叫 IDirect3DAuthenticatedChannel9::GetCertificate 以取得憑證。 方法會將憑證複製到上一個步驟中配置的緩衝區。
確認驅動程序的憑證已由Microsoft簽署,且尚未撤銷。
從憑證取得公鑰。
產生隨機 RSA 工作階段金鑰。 此工作階段金鑰用來簽署傳送至已驗證通道的數據。 使用驅動程式的公鑰來加密會話金鑰。
呼叫 IDirect3DAuthenticatedChannel9::NegotiateKeyExchange,將加密的會話密鑰傳送給驅動程式。
初始化安全通道,如下所示:
- 如檔所述,填入 D3DAUTHENTICATEDCHANNEL_CONFIGUREINITIALIZE 結構。
- 呼叫 IDirect3DAuthenticatedChannel9::Configure,如 傳送已驗證通道命令一節所述,傳送 D3DAUTHENTICATEDCONFIGURE_INITIALIZE 命令。 此命令包含傳送至已驗證通道之命令和查詢的起始序號。
將 D3DAUTHENTICATEDQUERY_CHANNELTYPE 查詢傳送至已驗證通道,以確認通道類型,如傳送已驗證通道查詢 一節所述。 檢查通道類型是否符合您在 CreateAuthenticatedChannel 方法中指定的專案。
3.設定密碼編譯會話
接下來,設定密碼編譯會話並建立會話密鑰。
- 呼叫 IDirect3DDevice9Video::CreateCryptoSession 來建立密碼編譯會話。 這個方法會傳回 IDirect3DCryptoSession9 介面的指標,以及密碼編譯會話的句柄。
- 呼叫 IDirect3DCryptoSession9::GetCertificateSize,以取得驅動程式 X.509 憑證的大小。 配置所需大小的緩衝區。
- 呼叫 IDirect3DCryptoSession9::GetCertificate 以取得憑證。 方法會將憑證複製到上一個步驟中配置的緩衝區。
- 確認驅動程序的憑證已由Microsoft簽署,且尚未撤銷。
- 從憑證取得公鑰。
- 產生隨機 RSA 工作階段金鑰。 這是與已驗證通道會話金鑰不同的會話金鑰。 使用驅動程式的公鑰來加密會話金鑰。
- 呼叫 IDirect3DCryptoSession9::NegotiateKeyExchange,將加密的會話密鑰傳送給驅動程式。
- 如果內容保護功能包含 D3DCPCAPS_CONTENTKEY,請建立隨機 RSA 內容金鑰。 這會在譯碼程式中稍後使用。
4.取得 DXVA 譯碼器裝置的句柄
在下一個步驟中,您需要 DXVA 譯碼器裝置的句柄。 若要取得此句柄,請填入DXVA2_DecodeExecuteParams結構,如下所示:
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 裝置和密碼編譯會話產生關聯,如下所示:
- 取得 DXVA 譯碼器裝置的句柄,如上一節所述。
- 將 D3DAUTHENTICATEDQUERY_DEVICEHANDLE 查詢傳送至已驗證通道,以取得 Direct3D 裝置的句柄。
- 以下列資訊填入 D3DAUTHENTICATEDCHANNEL_CONFIGURECRYPTOSESSION 結構:
- 將 DXVA2DecodeHandle 成員設定為 DXVA 譯碼器裝置的句柄。
- 將 CryptoSessionHandle 成員設定為密碼編譯會話的句柄。 IDirect3DDevice9Video::CreateCryptoSession方法會傳回此句柄。
- 將 DeviceHandle 成員設定為 Direct3D 裝置句柄。
- 呼叫 IDirect3DAuthenticatedChannel9::Configure,將 D3DAUTHENTICATEDCONFIGURE_CRYPTOSESSION 命令傳送至已驗證的通道。
下圖說明句柄的交換:
軟體譯碼器現在可以使用密碼編譯會話密鑰來加密壓縮的視訊緩衝區。 每個壓縮的緩衝區都會在 pvPVPState 中指定自己的初始化向量 (IV),DXVA2_DecodeBufferDesc 結構 成員。
傳送已驗證通道命令
定義一組命令來設定已驗證的通道,以及設定各種內容保護。 如需命令清單,請參閱 Content Protection Commands。
若要將命令傳送至已驗證的通道,請執行下列步驟。
- 填入輸入數據結構。 此數據結構一律是 D3DAUTHENTICATEDCHANNEL_CONFIGURE_INPUT 結構,後面接著其他欄位。 填入 D3DAUTHENTICATEDCHANNEL_CONFIGURE_INPUT 結構,如下表所示。
成員 | 描述 |
---|---|
omac | 暫時略過此欄位。 |
ConfigureType | 識別命令的 GUID。 如需命令清單,請參閱 Content Protection Commands。 |
hChannel | 已驗證通道的句柄。 |
SequenceNumber | 序號。 第一個序號是藉由傳送 D3DAUTHENTICATEDCONFIGURE_INITIALIZE 命令來指定。 每次傳送另一個命令時,請將這個數位遞增 1。 序號可防範重新執行攻擊。
注意: 使用兩個不同的序號,一個用於命令,另一個用於查詢。 |
- 計算在輸入結構 omac 成員之後所顯示之數據區塊的 OMAC 標籤。 然後將此標籤值複製到 omac 成員中。
- 呼叫 IDirect3DAuthenticatedChannel9::Configure。
- 驅動程式會將命令的輸出放在 D3DAUTHENTICATEDCHANNEL_CONFIGURE_OUTPUT 結構中。
- 計算輸出結構 omac 成員之後所顯示之數據區塊的 OMAC 標籤。 將此值與 omac 成員的值進行比較。 如果不符合,則失敗。
- 比較 ConfigureType的值、hChannel,以及針對這些成員的值,SequenceNumber 成員。 如果不符合,則失敗。
- 遞增下一個命令的序號。
傳送已驗證的通道查詢
定義一組查詢來擷取已驗證通道的相關信息。 如需查詢清單,請參閱 內容保護查詢。
若要將命令傳送至已驗證的通道,請執行下列步驟。
- 填入輸入數據結構。 此數據結構一律是 D3DAUTHENTICATEDCHANNEL_QUERY_INPUT 結構,可能後面接著其他欄位。 填入 D3DAUTHENTICATEDCHANNEL_QUERY_INPUT 結構,如下表所示。
成員 | 描述 |
---|---|
QueryType | 識別查詢的 GUID。 如需查詢清單,請參閱 內容保護查詢。 |
hChannel | 已驗證通道的句柄。 |
SequenceNumber | 序號。 第一個序號是藉由傳送 D3DAUTHENTICATEDCONFIGURE_INITIALIZE 命令來指定。 每次傳送另一個查詢時,請將這個數位遞增 1。 序號可防範重新執行攻擊。
注意: 使用兩個不同的序號,一個用於命令,另一個用於查詢。 |
- 呼叫 IDirect3DAuthenticatedChannel9::Query。
- 驅動程式會將查詢的輸出放在 D3DAUTHENTICATEDCHANNEL_QUERY_OUTPUT 結構中。 根據查詢類型,此結構後面接著其他欄位。
- 計算輸出結構 omac 成員之後所顯示之數據區塊的 OMAC 標籤。 將此值與 omac 成員的值進行比較。 如果不符合,則失敗。
- 比較 ConfigureType的值、hChannel,以及針對這些成員的值,SequenceNumber 成員。 如果不符合,則失敗。
- 遞增下一個查詢的序號。