Using Output Protection Manager
This topic describes how to use Output Protection Manager (OPM) to protect video content as it travels over a physical connector to a display device. This topic contains the following sections:
- Video Outputs
- Initializing an OPM Session
- Sending OPM Status Requests
- Sending OPM Commands
- Handling a Disabled Video Output
- Using HDCP to Protect Content
Premium video content is usually encrypted to protect it from unauthorized duplication. Of course, the video must be decrypted before it is displayed. The decrypted, uncompressed frames must then travel across a physical connector to the display device. Content providers may require the video frames to be protected at this point, as they travel across the physical connector.
Various protection mechanisms exist for this purpose, including High-Bandwidth Digital Content Protection (HDCP) and DisplayPort Content Protection (DPCP) for digital outputs; and Copy Generation Management System - Analog (CGMS-A) for analog outputs. Generally, these mechanisms involve encrypting or scrambling the signal befores it goes to the display.
OPM enables an application to enforce content protection mechanisms on the video output. Using OPM, the application sends commands and status requests to the graphics driver through a trusted, secure channel. OPM enables an application to:
- Verify that a graphics driver has been signed by Microsoft.
- Set up a trusted communication channel with the driver.
- Enforce content protection mechanisms on the physical output.
OPM replaces Certified Output Protection Protcol (COPP) and uses a similar API. For backward compatibility, the OPM interface can emulate the COPP interface. Differences between OPM and COPP include the following:
- OPM uses X.509 certificates, while COPP uses a proprietary certificate format.
- OPM supports HDCP repeaters.
- Applications that use OPM do not have to parse HDCP system renewability messages (SRMs).
- OPM can be used when the graphics display is using clone mode. COPP does not support clone mode.
If your application uses the protected media path (PMP) to play video content, you do not have to use the OPM API, because the PMP makes all of the required OPM calls. The OPM API is available for applications that do not use the PMP.
OPM is available in Windows Vista and later, but the API was not made public until Windows 7. To use OPM in an application, you must have the headers and library files from the Windows 7 SDK. You do not have to redistribute any DLLs to use OPM in Windows Vista or Windows Server 2008.
A graphics adapter can have more than one physical output, each with its own capabilities. Before an application plays protected content, it must set the appropriate protection mechanisms on every video output associated with the graphics card that will display the video. Which protection mechanisms to apply will depend on the usage rules for the content.
Each video output is represented by an instance of the IOPMVideoOutput interface. You can use a Direct3D device or monitor handles to get the video outputs.
Using a Direct3D device:
- Get the IDirect3DDevice9 pointer for the Direct3D device that your application will use to create surfaces to hold the video frames.
- Call the OPMGetVideoOutputsFromIDirect3DDevice9Object function. This function allocates an array of IOPMVideoOutput pointers, one for each output.
Using monitor handles:
- Call EnumDisplayMonitors to get the HMONITOR handles that correspond to the video window. Several monitors can be associated with the same window, so you might get several HMONITOR handles.
- For each monitor handle, call OPMGetVideoOutputsFromHMONITOR. This function allocates an array of IOPMVideoOutput pointers, one for each output.
Initializing an OPM Session
Before the application sends OPM commands or status requests, it must verify that the video output is trusted and establish a session key. The session key is used to sign the data that is exchanged between the application and the graphics driver.
The OPM API defines a handshake that establishes trust and sets the session key. You must perform this handshake for each instance of the IOPMVideoOutput interface, as follows:
Call IOPMVideoOutput::StartInitialization. This method retrieves two pieces of data:
- A random number, generated by the driver. You will use this number to complete the handshake.
- The driver's X.509 certificate chain.
Verify that the certificate chain has been signed by Microsoft.
Certificate revocation is outside the scope of OPM.
Get the driver's public key from the certificate chain.
Generate a 128-bit AES session key.
Generate two random 32-bit numbers:
- The starting sequence number for OPM status requests.
- The starting sequence number for OPM commands.
These numbers must be generated using a cryptographically secure pseudo-random number generator, such as CryptGenRandom.
Copy the driver's random number (obtained in step 1), the session key, and the two sequence numbers into an OPM_ENCRYPTED_INITIALIZATION_PARAMETERS structure, as described in IOPMVideoOutput::FinishInitialization.
Encrypt the OPM_ENCRYPTED_INITIALIZATION_PARAMETERS structure with RSAES-OAEP, using the driver's public key, which is found in the driver's certificate.
Sending OPM Status Requests
OPM status requests return information about the video output, such as the type of physical connector and the current protection level. For a list of request types, see OPM Status Requests.
To send a status request, perform the following steps.
Initialize an OPM_GET_INFO_PARAMETERS structure as shown in the following table.
Member Description omac Skip this field for now. rnRandomNumber A cryptographically secure 128-bit random number. Whenever you make a status request, always generate a new random number, even if you are making the same request. Store the number in a variable, because you will need to refer to it later. guidInformation A GUID that identifies the status request. For a list of status requests, see OPM Status Requests. ulSequenceNumber The sequence number. For the first status request, use the starting sequence number that you specified in the IOPMVideoOutput::FinishInitialization method (step 5 of Initializing an OPM Session.) Each time you make another status request, increment this number by 1. abParameters A byte array that contains additional input data for the request. The format of the input data is listed in the reference topic for each status request. cbParametersSize The size of the valid data in the abParameters array. The contents of the rest of the array are undefined.
Calculate the one-key CBC MAC (OMAC-1) to calculate a hash for the block of data that appears after the omac member, and then set the omac member to this value. See OPM Example Code.
Call the IOPMVideoOutput::GetInformation method. Pass in a pointer to the OPM_GET_INFO_PARAMETERS structure and a pointer to an OPM_REQUESTED_INFORMATION structure. The driver's response is written to the OPM_REQUESTED_INFORMATION structure.
- The omac member of this structure contains an OMAC computed for the data that follows this member.
- The abRequestedInformation member is a byte array that contains output data for the response. The format of the output data is listed in the reference topic for each status request.
Calculate an OMAC for the OPM_REQUESTED_INFORMATION structure, not including the omac member. Verify that the OMAC matches the value in the omac member.
Make sure the cbRequestedInformationSize member of the OPM_REQUESTED_INFORMATION structure gives the correct size for the output data. For example, the output data for the OPM_GET_CONNECTOR_TYPE query is an OPM_STANDARD_INFORMATION structure, so the value of cbRequestedInformationSize should be
Cast the abRequestedInformation member of the OPM_REQUESTED_INFORMATION structure to the correct output data structure. For example, if the status request is OPM_GET_CONNECTOR_TYPE, cast abRequestedInformation to an OPM_STANDARD_INFORMATION structure.
Make sure the rnRandomNumber member of the output data structure matches the value of rnRandomNumber from step 1.
Check the ulStatusFlags member of the output data structure, as described in Handling a Disabled Video Output.
If any of the checks in steps 5–8 fails, the application must stop showing protected content.
Sending OPM Commands
OPM commands are used to set the protection level and other settings on the video output. Sending an OPM command is similar to sending a status request, except there is no response data from the driver. For a list of commands, see OPM Commands.
To send an OPM command, perform the following steps.
Fill in an OPM_CONFIGURE_PARAMETERS structure as shown in the following table.
Member Description omac Skip this field for now. guidSetting A GUID that identifies the command. For a list of commands, see OPM Commands. ulSequenceNumber The sequence number. For the first command, use the starting sequence number that you specified in the IOPMVideoOutput::FinishInitialization method (step 5 of Initializing an OPM Session.) Each time you send another command, increment this number by 1. abParameters A byte array that contains additional input data for the command. The format of the input data is listed in the reference topic for each command. cbSettingDataSize The size of the valid data in the abParameters array. The contents of the rest of the array are undefined.
Calculate an OMAC for the block of data that appears after the omac member, and then set the omac member to this value.
For most commands, there is a corresponding status request that returns the status of the command. For example, the OPM_SET_PROTECTION_LEVEL command sets the protection level, and the OPM_GET_VIRTUAL_PROTECTION_LEVEL command gets the current protection level.
Handling a Disabled Video Output
A video output might disable itself at any time to prevent the unauthorized use of video content. This might occur because a protection mechanism stops working, because the driver detects tampering, or because the display was disconnected from the physical connector. While a video output is disabled, no video frames are displayed.
While content protection is enabled, an application should periodically (at least once every 2 seconds) perform the following steps.
- Call IOPMVideoOutput::GetInformation to send either the OPM_GET_ACTUAL_PROTECTION_LEVEL or OPM_GET_VIRTUAL_PROTECTION_LEVEL status request. The return data for both commands is an OPM_STANDARD_INFORMATION structure.
- Check the ulInformation member of the OPM_STANDARD_INFORMATION structure. This member contains a flag indicating whether content protection is still enabled. If content protection is off, stop playing the video immediately.
- If content protection is on, check the ulStatusFlags member of the OPM_STANDARD_INFORMATION structure. If no flags are set, the video output is working correctly. Otherwise, the video output is disabled.
The following flags are defined for ulStatusFlags.
|OPM_STATUS_LINK_LOST||Output protection stopped working for some reason; for example, the display device might be unplugged from the conntector. Stop playback and turn off all output protection mechanisms.|
|OPM_STATUS_RENEGOTIATION_REQUIRED||The application must reestablish the OPM session. Respond as follows:
|OPM_STATUS_REVOKED_HDCP_DEVICE_ATTACHED||This flag applies only when HDCP is used, and indicates the presence of a revoked HDCP device. Stop playback and turn off all protection mechanisms on this video output. When this flag is set, the OPM_STATUS_LINK_LOST flag is also set.|
|OPM_STATUS_REVOKED_HDCP_DEVICE_ATTACHED||The driver has detected tampering. Stop playback, and do not play any more video using this video output. It is also a good idea to stop using any other video outputs, because the system might be compromised.|
Using HDCP to Protect Content
This section describes how to enable HDCP output protection using OPM. Here is a general outline of the steps the application must take. Details are given later in this section.
- The application might have to provide an SRM to the video output. The mechanism for receiving SRMs is outside the scope of the OPM interface. For example, SRMs might be delivered as part of a broadcast stream.
- The application enables HDCP output protection.
- The application plays the video content. Periodically, the application polls the driver to make sure HDCP is on.
- When playback is complete, the application turns off HDCP.
Setting the SRM
To set the SRM, perform the following steps.
- Initialize an OPM_SET_HDCP_SRM_PARAMETERS structure with the SRM version number.
- Store the SRM in a variable.
- Send an OPM_SET_HDCP_SRM command to the video output. Use the procedure described in Sending OPM Commands.
- Send an OPM_GET_CURRENT_HDCP_SRM_VERSION status request to the video output. Use the procedure described in Sending OPM Status Requests. This status request has no input data, so the contents of the abParameters member of the OPM_GET_INFO_PARAMETERS structure are undefined.
- When the IOPMVideoOutput::GetInformation method returns, the abRequestedInformation array in the OPM_REQUESTED_INFORMATION structure contains an OPM_STANDARD_INFORMATION structure. The ulInformation member of this structure contains the version number of the current SRM. This value must equal the value from step 2.
To enable HDCP, perform the following steps.
- Initialize an OPM_SET_PROTECTION_LEVEL_PARAMETERS structure with the following values:
- ulProtectionType = OPM_PROTECTION_TYPE_HDCP
- ulProtectionLevel = OPM_HDCP_ON
- Reserved = 0
- Reserved2 = 0
- Send an OPM_SET_PROTECTION_LEVEL command. The input data in the abParameters array is the OPM_SET_PROTECTION_LEVEL_PARAMETERS structure.
- Send an OPM_GET_VIRTUAL_PROTECTION_LEVEL status request to check whether HDCP is enabled. The first 4 bytes of the abParameters member of the OPM_GET_INFO_PARAMETERS structure contain the value OPM_PROTECTION_TYPE_HDCP.
When the GetInformation method returns, the abRequestedInformation array in the OPM_REQUESTED_INFORMATION structure contains an OPM_STANDARD_INFORMATION structure. The ulInformation member of this structure contains a value from the OPM_HDCP_PROTECTION_LEVEL enumeration. If the value equals OPM_HDCP_ON, it means HDCP is enabled. Otherwise, repeat steps 1–2 until HDCP is enabled, or an error occurs. (Remember to increment the sequence number and generate a new random number each time.)
It usually takes between 100 and 200 milliseconds to enable HDCP, but it can take longer. Do not assume that HDCP is enabled until you have verified it.
When the application finishes playing protected content, turn off HDCP. The steps are the same as for enabling HDCP, but in step 1, set ulProtectionLevel to OPM_HDCP_OFF.
Do not enable HDCP if the connector type is OPM_CONNECTOR_TYPE_DISPLAYPORT_EMBEDDED. (See OPM Connector Type Flags.)