Compartilhar via


Initiating a COPP Session

 
Microsoft DirectShow 9.0

Initiating a COPP Session

To initiate a Certified Output Protection Protocol (COPP) session, you must prepare a signature, which is an array that contains the concatenation of the following numbers:

  • The 128-bit random number returned by the driver. (This value is shown as guidRandom in Obtaining the Driver's Certificate Chain.)
  • A 128-bit symmetric AES key.
  • A 32-bit starting sequence number for status requests.
  • A 32-bit starting sequence number for COPP commands.

Generate a symmetric AES key as follows:

DWORD dwFlag = 0x80;         // Bit length: 128-bit AES.
dwFlag <<= 16;               // Move this value to the upper 16 bits.
dwFlag |= CRYPT_EXPORTABLE;  // We want to export the key.
CryptGenKey(
    hCSP,           // Handle to the CSP.
    CALG_AES_128,   // Use 128-bit AES block encryption algorithm.
    dwFlag,
    &m_hAESKey      // Receives a handle to the AES key.
);

The CryptGenKey function creates the symmetric key, but the key is still held in the CSP. To export the key into a byte array, use the CryptExportKey function. This is the reason for using the CRYPT_EXPORTABLE flag when calling CryptGenKey. The following code exports the key and copies it into the pData array.

DWORD cbData = 0; 
BYTE *pData = NULL;
// Get the size of the blob.
CryptExportKey(hAESKey, 0, PLAINTEXTKEYBLOB, 0, NULL, &cbData);  

// Allocate the array and call again.
pData = new BYTE[cbData];
CryptExportKey(hAESKey, 0, PLAINTEXTKEYBLOB, 0, pData, &cbData);  

The data returned in pData has the following layout:

BLOBHEADER header
DWORD      cbSize
BYTE       key[]

However, no structure that matches this layout is defined in the CryptoAPI headers. You can either define one or do some pointer arithmetic. For example, to verify the size of the key:

DWORD *pcbKey = (DWORD*)(pData + sizeof(BLOBHEADER));
if (*pcbKey != 16)
{
    // Wrong size! Should be 16 bytes (128 bits).
}

To get the pointer to the key itself:

BYTE *pKey = pData + sizeof(BLOBHEADER) + sizeof(DWORD);

Next, generate a 32-bit random number to use as the starting sequence for COPP status requests. The recommended way to create a random number is to call the CryptGenRandom function. Do not use the rand function in the C run-time library, because it is not truly random. Generate a second 32-bit random number to use as the starting sequence for COPP commands.

UINT uStatusSeq;     // Status sequence number.
UINT uCommandSeq;    // Command sequence number.
CryptGenRandom(hCSP, sizeof(UINT), &uStatusSeq);
CryptGenRandom(hCSP, sizeof(UINT), &uCommandSeq);

Now you can prepare the COPP signature. This is a 256-byte array, defined as the AMCOPPSignature structure. Initialize the contents of the array to zero. Then copy the four numbers into the array—the driver's random number, the AES key, the status sequence number, and the command sequence number, in that order. Finally, swap the byte order of the entire array.

According to the documentation for CryptEncrypt:

The length of plaintext data that can be encrypted with a call to CryptEncrypt with an RSA key is the length of the key modulus minus eleven bytes.

In this case, the modulus is 256 bytes, so the maximum message length is 245 bytes (256 – 11). The AMCOPPSignature structure is 256 bytes, but the meaningful data in the signature is only 40 bytes. The following code encrypts the signature and provides the result in CoppSig.

AMCOPPSignature CoppSig;
ZeroMemory(&CoppSig, sizeof(CoppSig));
// Copy the signature data into CoppSig. (Not shown.)

// Encrypt the signature:
const DWORD RSA_PADDING = 11;    // 11-byte padding.
DWORD cbDataOut = sizeof(AMCOPPSignature);
DWORD cbDataIn = cbDataOut - RSA_PADDING;
CryptEncrypt(
    hRSAKey, 
    NULL,     // No hash object.
    TRUE,     // Final block to encrypt.
    0,        // Reserved.
    &CoppSig, // COPP signature.
    &cbDataOut, 
    cbDataIn
);

Now pass the encrypted array to IAMCertifiedOutputProtection::SessionSequenceStart:

hr = pCOPP->SessionSequenceStart(&CoppSig);
if (SUCCEEDED(hr))
{
    // Ready to send COPP commands and status requests.
}

If this method succeeds, you are ready to send COPP commands and status requests to the driver.

See Also