Sending COPP Status Requests

[The feature associated with this page, DirectShow, is a legacy feature. It has been superseded by MediaPlayer, IMFMediaEngine, and Audio/Video Capture in Media Foundation. Those features have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use MediaPlayer, IMFMediaEngine and Audio/Video Capture in Media Foundation instead of DirectShow, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]

To send a Certified Output Protection Protocol (COPP) status request, fill in an AMCOPPStatusInput structure with the request data. The structure members are:

  • rApp. A 128-bit random number, typed as a GUID. The same number is returned in the driver's response. You should allocate the random number on the heap and then copy it into structure. This guards against attacks where the attacker modifies the contents of the AMCOPPStatusInput structure.
  • guidStatusRequestID. A GUID that identifies the request. See COPP Query Reference.
  • dwSequence. The status sequence number. Increment this value after each status request. (In the section Initiating a COPP Session, this value is shown as uStatusSeq in the code examples.)
  • cbSizeData. The size, in bytes, of any additional data needed for the request.
  • StatusData. Data for the status request.

Pass the AMCOPPStatusInput structure to the IAMCertifiedOutputProtection::ProtectionStatus method. For example, the following code sends a status request that queries which protection mechanisms are available:

AMCOPPStatusInput input;
AMCOPPStatusOutput output;

// Create a 128-bit random number.
GUID *pGuid = new GUID();
if (pGuid == NULL)
{
    // Handle out-of-memory condition.
}
CryptGenRandom(hCSP, sizeof(GUID), (BYTE*)pGuid);  

// Copy the random number into the command structure.
memcpy(&input.rApp, pGuid, sizeof(GUID));

// Fill in the other data.
input.guidStatusRequestID = DXVA_COPPQueryProtectionType; // Request type.
input.dwSequence = uStatusSeq;  // Status sequence number.
input.cbSizeData = 0            // No other data for this query.

// Send the request.
hr = pCOPP->ProtectionStatus(&input, &output);

// Increment the sequence number each time.
++uStatusSeq;

The response is written into the COPPStatus member of the AMCOPPStatusOutput structure. The size of the valid data in the response is given in the cbSizeData member. To ensure the integrity of the message, the driver computes a message authentication code (MAC) using the OMAC 1 algorithm, and returns this value in the structure's macKDI member. The application should verify this value as follows:

  1. Calculate the OMAC tag for the block of data that appears after the macKDI member of the AMCOPPStatusOutput structure (in other words, cbSizeData plus COPPStatus).
  2. Compare this tag with the value in macKDI, using a straight memcmp.

The OMAC 1 algorithm is described in detail at https://www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html. COPP uses the following OMAC-1 parameters:

  • E = AES
  • t = 128 bits

The data returned from the status request always starts with two items:

  • The same value of rApp that was passed by the application. You should verify that this value matches the original value stored on the heap.
  • A COPP_StatusFlags value that indicates whether the output protection status has changed.

Because the connection can be lost or reconfigured, the application should periodically poll the driver for the current status. If the COPP_RenegotiationRequired flag is set, the application should attempt to reset the protection level. If the COPP_LinkLost flag is set, the application should stop playing the content. For example, the COPP_LinkLost flag can be returned because the user disconnected the output connector. The application should release the current instance of the VMR, create a new instance of the VMR, and establish a new COPP session (including key exchange and certificate validation).

Using Certified Output Protection Protocol (COPP)