GPU-Based コンテンツ保護
このトピックでは、グラフィックス ドライバーが提供できるビデオ コンテンツ保護機能について説明します。
はじめに
次の図は、保護されたビデオ コンテンツがレンダリングされるパイプラインを通過する方法の簡略化されたビューを示しています。
注意
保護されたメディア パス (PMP) は、この図には示されていません。 ここに示すデータ フローは、PMP プロセス内またはアプリケーション プロセス内で発生する可能性があります。
デコーダーは、暗号化された圧縮されたビデオ データを外部ソースから受信します。 また、デコーダーは、このデータを復号化するための暗号化キーも受け取ると想定されます。 このトピックでは、ビデオ ソースとデコーダーの間のキー交換について説明しませんが、PMP では 1 つの可能なメカニズムが定義されています。 GPU は、この段階では関与しません。
ハードウェア高速デコードの場合、ソフトウェア デコーダーは圧縮されたビデオ コンテンツを GPU に渡します。 このコンテンツを保護するために、デコーダーは、ハードウェア アクセラレータに渡す前に、通常は AES-CTR を使用してデータを再暗号化します。 キー交換メカニズムは、デコーダーとグラフィックス ドライバーの間で定義されます。
デコードされたビデオ フレームは、一般にクリアにビデオ メモリに格納されます。 この時点で、フレームが処理され、表示されます。 プレゼンテーションには 2 つのメインオプションがあります。
- フレームは、ハードウェア オーバーレイを使用して表示できます。 詳細については、「 ハードウェア オーバーレイのサポート」を参照してください。
- フレームは、共有サーフェスを使用してデスクトップ ウィンドウ管理 (DWM) によって表示できます。
最後の手順では、モニターにフレームを表示します。グラフィックス カードとディスプレイ デバイス間のリンク保護が必要になる場合があります。 リンク保護の例として、High-Bandwidth Digital Content Protection (HDCP) があります。 リンク保護は、 Output Protection Manager (OPM) を使用して構成されます。 このトピックでは OPM について説明しません。詳細については、「 Output Protection Manager の使用」を参照してください。
デコード プロセスの概要
ハードウェア高速デコード中に、ソフトウェア デコーダーは圧縮されたビデオ データをグラフィックス カードに渡す必要があります。 Premium コンテンツの場合、このデータは通常、GPU に送信される前に対称キー暗号化を使用して暗号化する必要があります。
デコード用のビデオを暗号化するために、ソフトウェア デコーダーは次のインターフェイスを使用します。
- IDirectXVideoDecoder。 アクセラレータとも呼ばれる DXVA デコーダー デバイスを表します。
- IDirect3DCryptoSession9。 暗号化キーを提供する暗号化セッションを表します。
- IDirect3DAuthenticatedChannel9。 認証されたチャネルを表します。これにより、ソフトウェア デコーダーは暗号化セッションを DXVA デコーダーに関連付けることができます。
これらのインターフェイスはすべて、次のように Direct3D デバイスから取得されます。
インターフェイス | 作成 |
---|---|
IDirectXVideoDecoder | IDirectXVideoDecoderService::CreateVideoDecoder を呼び出します。 DXVA デコーダー デバイスは、DXVA プロファイル GUID によって識別されます。 |
IDirect3DCryptoSession9 | IDirect3DDevice9Video::CreateCryptoSession を呼び出します。 |
IDirect3DAuthenticatedChannel9 | IDirect3DDevice9Video::CreateAuthenticatedChannel を呼び出します。 |
注意
IDirect3DDevice9Video インターフェイスへのポインターを取得するには、D3D9Ex デバイスで QueryInterface を呼び出します。
認証されたチャネルは、ソフトウェア デコーダーとドライバーの間の信頼された通信チャネルを提供します。 通信チャネルは次のように機能します。
- ドライバーは、ルート証明書が Microsoft によって署名されている X.509 証明書チェーンを提供します。
- 証明書には、ドライバーの RSA 公開キーが含まれています。
- ソフトウェア デコーダーは公開キーを使用して、ドライバーに 128 ビット AES セッション キーを送信します。
- ソフトウェア デコーダーは、認証されたチャネルにクエリとコマンドを送信します。
- セッション キーは、クエリとコマンドのメッセージ認証コード (MAC) を計算するために使用されます。 ドライバーは MAC を使用してクエリ/コマンド データの整合性を検証し、ソフトウェア デコーダーはそれらを使用してドライバーからの応答データの整合性を検証します。
デコーダーの圧縮ビデオ バッファーの暗号化
暗号化とデコード プロセスの概要を次に示します。
ソフトウェア デコーダーは、ビデオ ソースから暗号化されたデータのストリームを受信します。 デコーダーは、このストリームを復号化します。
ソフトウェア デコーダーは、暗号化セッションとセッション キーをネゴシエートします。
ソフトウェア デコーダーは、認証されたチャネルを使用して、暗号化セッションを DXVA デコーダー デバイスに関連付けます。
ソフトウェア デコーダーは、DXVA デコーダー デバイス (アクセラレータ) から取得した DXVA バッファーに圧縮データを配置します。 保護されたコンテンツの場合、ソフトウェア エンコーダーは、暗号化のセッション キーを使用して、DXVA バッファーに格納されるデータを暗号化します。
注意
一部のドライバーでは、暗号化にセッション キーではなくコンテンツ キーが使用されます。 コンテンツ キーは、あるフレームから次のフレームに変更される可能性があります。
デコーダーは、暗号化された圧縮バッファーをアクセラレータに送信します。 AES-CTR の場合、デコーダーは初期化ベクトルも渡します。 コンテンツ キーが使用されている場合、デコーダーはセッション キーを使用して暗号化されたコンテンツ キーを渡します。
Direct3D では、128 ビット AES-CTR が標準でサポートされていますが、追加の暗号化の種類まで拡張するように設計されています。
次の 5 つのセクションでは、より詳細な手順について説明します。
- 1. ドライバーのコンテンツ保護機能のクエリを実行する
- 2. 認証済みチャネルを構成する
- 3. 暗号化セッションを構成する
- 4. DXVA デコーダー デバイスへのハンドルを取得する
- 5. DXVA デコーダーを暗号化セッションに関連付ける
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 インターフェイスへのポインターを返します。 このハンドルは、後で暗号化セッションを認証済みチャネルに関連付けるために使用されます。
ドライバーの X.509 証明書のサイズを取得するには、 IDirect3DAuthenticatedChannel9::GetCertificateSize を呼び出します。 必要なサイズのバッファーを割り当てます。
IDirect3DAuthenticatedChannel9::GetCertificate を呼び出して証明書を取得します。 メソッドは、前の手順で割り当てられたバッファーに証明書をコピーします。
ドライバーの証明書が Microsoft によって署名され、取り消されていないことを確認します。
証明書から公開キーを取得します。
ランダムな RSA セッション キーを生成します。 このセッション キーは、認証されたチャネルに送信されるデータに署名するために使用されます。 ドライバーの公開キーを使用してセッション キーを暗号化します。
IDirect3DAuthenticatedChannel9::NegotiateKeyExchange を呼び出して、暗号化されたセッション キーをドライバーに送信します。
セキュリティで保護されたチャネルを次のように初期化します。
- ドキュメントの説明に従って 、D3DAUTHENTICATEDCHANNEL_CONFIGUREINITIALIZE 構造体を入力します。
- 「認証されたチャネル コマンドの送信」セクションの説明に従って、IDirect3DAuthenticatedChannel9::Configure を呼び出して、D3DAUTHENTICATEDCONFIGURE_INITIALIZE コマンドを送信します。 このコマンドには、認証されたチャネルに送信されるコマンドとクエリの開始シーケンス番号が含まれています。
「認証されたチャネル クエリの送信」セクションで説明されているように、 D3DAUTHENTICATEDQUERY_CHANNELTYPE クエリを 認証済みチャネルに送信して、チャネルの種類を確認します。 チャネルの種類が CreateAuthenticatedChannel メソッドで指定した内容と一致することを確認します。
3. 暗号化セッションを構成する
次に、暗号化セッションを構成し、セッション キーを確立します。
- IDirect3DDevice9Video::CreateCryptoSession を呼び出して、暗号化セッションを作成します。 このメソッドは、 IDirect3DCryptoSession9 インターフェイスへのポインターと、暗号化セッションへのハンドルを返します。
- ドライバーの X.509 証明書のサイズを取得するには、 IDirect3DCryptoSession9::GetCertificateSize を呼び出します。 必要なサイズのバッファーを割り当てます。
- 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構造体で、Function メンバーを 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 コマンドを認証済みチャネルに送信します。
次の図は、ハンドルの交換を示しています。
ソフトウェア デコーダーは、暗号化セッション キーを使用して圧縮されたビデオ バッファーを暗号化できるようになりました。 各圧縮バッファーには、DXVA2_DecodeBufferDesc構造体の pvPVPState メンバーで独自の初期化ベクトル (IV) が指定されます。
認証済みチャネル コマンドの送信
一連のコマンドは、認証されたチャネルを構成し、さまざまなコンテンツ保護を設定するために定義されます。 コマンドの一覧については、「 コンテンツ保護コマンド」を参照してください。
認証されたチャネルにコマンドを送信するには、次の手順を実行します。
- 入力データ構造を入力します。 このデータ構造は、常に D3DAUTHENTICATEDCHANNEL_CONFIGURE_INPUT 構造であり、その後に追加のフィールドが続きます。 次の表に示すように 、D3DAUTHENTICATEDCHANNEL_CONFIGURE_INPUT 構造体を入力します。
メンバー | 説明 |
---|---|
omac | ここでは、このフィールドをスキップします。 |
ConfigureType | コマンドを識別する GUID。 コマンドの一覧については、「 コンテンツ保護コマンド」を参照してください。 |
hChannel | 認証されたチャネルへのハンドル。 |
SequenceNumber | シーケンス番号。 最初のシーケンス番号は、 D3DAUTHENTICATEDCONFIGURE_INITIALIZE コマンドを送信することによって指定されます。 別のコマンドを送信するたびに、この数値を 1 ずつ増やします。 シーケンス番号は、リプレイ攻撃から保護されます。
メモ: 2 つの個別のシーケンス番号が使用されます。1 つはコマンド用、もう 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 ずつ増やします。 シーケンス番号は、リプレイ攻撃から保護されます。
メモ: 2 つの個別のシーケンス番号が使用されます。1 つはコマンド用、もう 1 つはクエリ用です。 |
- IDirect3DAuthenticatedChannel9::Query を呼び出します。
- ドライバーは、クエリからの出力を D3DAUTHENTICATEDCHANNEL_QUERY_OUTPUT 構造に配置します。 この構造体の後には、クエリの種類に応じて追加のフィールドが続きます。
- 出力構造体の omac メンバーの後に表示されるデータ ブロックの OMAC タグを計算します。 これを omac メンバーの値と比較します。 一致しない場合は失敗します。
- 出力構造体の ConfigureType、 hChannel、 SequenceNumber メンバーの値を、それらのメンバーの値と比較します。 一致しない場合は失敗します。
- 次のクエリのシーケンス番号をインクリメントします。