DRMEncrypt function (msdrm.h)

[The AD RMS SDK leveraging functionality exposed by

the client in Msdrm.dll is available for use in Windows Server 2008, Windows Vista, Windows Server 2008 R2, Windows 7, Windows Server 2012, and Windows 8. It may be altered or

unavailable in subsequent versions. Instead, use Active Directory Rights Management Services SDK 2.1,

which leverages functionality exposed by the client in Msipc.dll.]

The DRMEncrypt function encrypts data.

Syntax

DRMEXPORT HRESULT UDAPICALL DRMEncrypt(
  [in]      DRMHANDLE hCryptoProvider,
  [in]      UINT      iPosition,
  [in]      UINT      cNumInBytes,
  [in]      BYTE      *pbInData,
  [in, out] UINT      *pcNumOutBytes,
  [out]     BYTE      *pbOutData
);

Parameters

[in] hCryptoProvider

A handle to an AD RMS encrypting object created by using the DRMCreateEnablingBitsEncryptor function.

[in] iPosition

Position in the buffer at which to start encrypting. 0 corresponds to the first block in a buffer, 1 corresponds to the second block, and so on.

Note  If you use the AES key when you sign the issuance license, iPosition can always be set to 0.
 

[in] cNumInBytes

The number of bytes to encrypt.

[in] pbInData

A pointer to a buffer that contains the bytes to encrypt.

[in, out] pcNumOutBytes

The number of encrypted bytes.

[out] pbOutData

A pointer to the encrypted bytes.

Return value

If the function succeeds, the function returns S_OK.

If the function fails, it returns an HRESULT value that indicates the error. For a list of common error codes, see Common HRESULT Values.

Remarks

Memory allocation and release of the encrypted content is the responsibility of the calling function. The following code sample, from Encrypting Content, shows how to encrypt content in blocks. This particular example already knows the size of the content to encrypt and allocates memory beforehand. If you must determine the number of bytes to allocate, however, the required buffer size is returned in the pcNumOutBytes parameter after the first call. Allocate memory and call the function again with pbOutData set to point to the new memory.


#include "EncryptingContent.h"

/*===================================================================
File:      Encryption_EncryptContent.cpp

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Copyright (C) Microsoft.  All rights reserved.
===================================================================*/

/////////////////////////////////////////////////////////////////////
// The EncryptContent function encrypts content. In this example,
// the content to be encrypted is defined in the header file.
//   #define PLAINTEXT L"This is the content to be encrypted."
//
HRESULT EncryptContent(
          DRMENVHANDLE   hEnv,
          DRMHANDLE      hLib,
          PWSTR          pwszRAC,
          PWSTR          pwszGUID,
          DRMHANDLE      hIssuanceLic,
          PWSTR          pwszSignedIL,
          BYTE**         ppbEncrypted)

{
  ///////////////////////////////////////////////////////////////////
  // Declare variables:
  //   hr........................Return value                     
  //   pwszOwnerLicense..........License with OWNER right
  //   pwszOutFile...............Name of file for encrypted content
  //   pbEncrypted...............Buffer to hold encrypted content
  //   uiOwnerLicenseLength......Owner licensee length, in characters
  //   uiBlock...................Decryption block size (16 bytes)
  //   uiBytes...................Size, in bytes, of uiBlock
  //   uiPlainText...............Number of bytes in the plain text
  //   uiPadding.................Blank space added to plain text
  //   uiBuffer..................uiPlainText + uiPadding
  //   uiEncrypted...............Size, bytes, of encrypted content
  //   uiOffset..................Offset into encryption buffer
  //   pbPlainText...............Pointer to unencrypted content
  //   hBoundLicense.............Handle to license bound to OWNER
  //   hEP.......................Enabling principal for binding   
  //   hEBEncryptor..............Handle to encrypting object
  //   dwBytesWritten............WriteFile function bytes written 
  //   idNULL....................DRMID structure
  //   idContent.................Structure for binding a license
  //   idStandardEP..............Structure for enabling principal
  //   oParams...................Structure for binding a license
  //   eType.....................Structure for license encoding
  //   
  HRESULT       hr                     = E_FAIL;
  PWSTR         pwszOwnerLicense       = NULL;
  PWSTR         pwszOutFile            = L"EncryptedContent.bin";
  BYTE*         pbEncrypted            = NULL;
  UINT          uiOwnerLicenseLength   = 0;
  UINT          uiBlock                = 0;
  UINT          uiBytes                = 0;
  UINT          uiPlainText            = 0;
  UINT          uiBuffer               = 0;
  UINT          uiPadding              = 0;
  UINT          uiEncrypted            = 0;
  UINT          uiOffset               = 0;
  BYTE*         pbPlainText            = NULL;
  DRMHANDLE     hBoundLicense          = NULL; 
  DRMHANDLE     hEP                    = NULL; 
  DRMHANDLE     hEBEncryptor           = NULL;
  DWORD         dwBytesWritten         = 0;
  DRMID         idNULL;
  DRMID         idContent;
  DRMID         idStandardEP;
  DRMBOUNDLICENSEPARAMS oParams;
  DRMENCODINGTYPE eType;              

  wprintf(L"\r\nEntering EncryptContent.\r\n");
 
  // Validate the input parameters.
  if ( NULL==hEnv ||
       NULL==hLib ||
       NULL==pwszRAC ||
       NULL==pwszGUID ||
       NULL==hIssuanceLic ||
       NULL==pwszSignedIL ||
       NULL==ppbEncrypted)
       return E_INVALIDARG;

  // Create an enabling principal.
  SecureZeroMemory(&idNULL, sizeof(idNULL));
  SecureZeroMemory(&idStandardEP, sizeof(idStandardEP));
  idStandardEP.wszIDType = L"ASCII Tag"; 
  idStandardEP.wszID     = L"UDStdPlg Enabling Principal"; 

  hr = DRMCreateEnablingPrincipal ( 
          hEnv,                     // Secure environment handle 
          hLib,                     // Library handle 
          idStandardEP.wszID,       // Enabling principal type 
          &idNULL,                  // DRMID structure 
          pwszRAC,                  // Rights account certificate
          &hEP);                    // enabling principal handle 
  if(FAILED(hr)) return hr;
  wprintf(L"DRMCreateEnablingPrincipal: hEP %i \r\n", hEP);

  // Call DRMGetOwnerLicense to get a license to bind to. An owner
  // license contains the OWNER right which allows the user to
  // exercise all rights regardless of whether they have been
  // explicitly granted.
  //   - Call DRMGetOwnerLicense once to retrieve the length, in
  //     characters, of the owner license.
  //   - Allocate memory for the license.
  //   - Call DRMGetOwnerLicense again to retrieve the license.
  //   - You must release the memory before leaving this function.
  hr = DRMGetOwnerLicense( 
           hIssuanceLic,               // Issuance license handle
           &uiOwnerLicenseLength,      // License length
           NULL);                      // Not used
  if(FAILED(hr)) goto e_Exit;

  pwszOwnerLicense = new WCHAR[uiOwnerLicenseLength];
  if(NULL == pwszOwnerLicense)
  {
    hr = E_OUTOFMEMORY; 
    goto e_Exit; 
  }
  hr = DRMGetOwnerLicense( 
          hIssuanceLic, 
          &uiOwnerLicenseLength, 
          pwszOwnerLicense);
  if(FAILED(hr)) goto e_Exit;
  wprintf(L"DRMGetOwnerLicense (pwszOwnerLicense) succeeded.\r\n");

  // Bind the owner license to the OWNER right.
  SecureZeroMemory(&idContent, sizeof(idContent));
  idContent.wszIDType        = L"MS-GUID"; 
  idContent.wszID            = pwszGUID;

  SecureZeroMemory(&oParams, sizeof(oParams));
  oParams.hEnablingPrincipal = hEP; 
  oParams.wszRightsRequested = L"OWNER";
  oParams.wszRightsGroup     = L"Main-Rights"; 
  oParams.idResource         = idContent; 

  hr = DRMCreateBoundLicense ( 
          hEnv,                     // Secure environment handle 
          &oParams,                 // Additional license options 
          pwszOwnerLicense,         // Owner license 
          &hBoundLicense,           // Handle to the bound license 
          NULL );                   // Reserved
  if(FAILED(hr)) goto e_Exit;
  wprintf(L"DRMCreateBoundLicense: (hBoundLicense) succeeded.\r\n");

  // Create an encrypting object. 
  hr = DRMCreateEnablingBitsEncryptor( 
          hBoundLicense,              // Bound license handle
          oParams.wszRightsRequested, // Requested right
          NULL,                       // Reserved
          NULL,                       // Reserved
          &hEBEncryptor);             // Encrypting object handle
  if(FAILED(hr)) goto e_Exit;
  wprintf(L"DRMCreateEnablingBitsEncryptor succeeded.\r\n");

  // Retrieve the size, in bytes, of the memory block that must 
  // be passed to DRMEncrypt.
  uiBytes= sizeof(uiBlock);
  hr = DRMGetInfo(
          hEBEncryptor,               // Encrypting object handle
          g_wszQUERY_BLOCKSIZE,       // Attribute to query for
          &eType,                     // Type of encoding to apply
          &uiBytes,                   // Size of uiBlock variable
          (BYTE*)&uiBlock);           // Size of memory block
  if(FAILED(hr)) goto e_Exit;
  wprintf(L"DRMGetInfo: uiBlock = %u\r\n", uiBlock);

  // Determine the size of the buffer needed to store the
  // encrypted content. The buffer must be an even multiple of 
  // the buffer size used during the [typically] iterative 
  // encryption process. In this example, the plain text
  // is 72 bytes long and the encryption buffer is 16 bytes. 
  // However 72 % 16 = 8. The plain text must therefore be padded
  // to 80 bytes (80 % 16 = 0).
  uiPlainText = (UINT)(sizeof(WCHAR) * wcslen(PLAINTEXT));
  uiPadding = uiBlock - (uiPlainText % uiBlock);
  uiBuffer = uiPlainText + uiPadding;

  pbPlainText = new BYTE[uiBuffer];
  *ppbEncrypted = new BYTE[uiBuffer];

  SecureZeroMemory(pbPlainText, sizeof(pbPlainText));
  memcpy_s(
          pbPlainText,
          uiBuffer,
          (BYTE*)PLAINTEXT,
          uiPlainText);

  // Encrypt the plain text.
  for ( int j = 0; (UINT)j * uiBlock < uiBuffer; j++ )
  {
    hr = DRMEncrypt( 
          hEBEncryptor,               // Encrypting object handle
          j * uiBlock,                // Position in buffer
          uiBlock,                    // Number of bytes to encrypt
          pbPlainText + (j*uiBlock),  // Bytes to encrypt
          &uiEncrypted,               // Number of bytes encrypted
          NULL);                      // NULL on first call
    if(FAILED(hr)) goto e_Exit;

    hr = DRMEncrypt( 
          hEBEncryptor,               // Encrypting object handle 
          j * uiBlock,                // Position in buffer 
          uiBlock,                    // Number of bytes to encrypt
          pbPlainText + (j*uiBlock),  // Bytes to encrypt
          &uiEncrypted,               // Number of bytes encrypted
          *ppbEncrypted + uiOffset);  // Encrypted content
    if(FAILED(hr)) goto e_Exit;

    uiOffset += uiEncrypted;          // Increment the buffer offset
  }

  // Create a new file to write the encrypted content and issuance
  // license to.
  HANDLE hFile = CreateFile(
          pwszOutFile,                // File name
          GENERIC_WRITE,              // Access type
          0,                          // Require exclusive access
          NULL,                       // Default security attributes
          CREATE_ALWAYS,              // Allow write-only access
          0,                          // No file attributes or flags
          NULL);                      // No open file as template
  if(INVALID_HANDLE_VALUE == hFile)
  {
    hr = HRESULT_FROM_WIN32(GetLastError());
    goto e_Exit;
  }
  wprintf(L"CreateFile: hFile = %i\r\n", hFile);

  // Write the encrypted content and relevant data to the file.
  if(!WriteFile(hFile, 
                &uiBuffer, 
                sizeof(UINT), 
                &dwBytesWritten, 
                NULL))
    goto e_Exit;
  size_t uiIssuanceLicenseLgth = (UINT)(sizeof(WCHAR) * 
                                 wcslen(pwszSignedIL));
  if(!WriteFile(hFile, 
                &uiIssuanceLicenseLgth, 
                sizeof(size_t), 
                &dwBytesWritten, 
                NULL))
    goto e_Exit;
  if(!WriteFile(hFile, 
                *ppbEncrypted, 
                uiBuffer, 
                &dwBytesWritten, 
                NULL))
    goto e_Exit;
  if(!WriteFile(hFile, 
                pwszSignedIL, 
                uiIssuanceLicenseLgth, 
                &dwBytesWritten, 
                NULL))
    goto e_Exit;
  wprintf(L"Writing to file succeeded.\r\n");

e_Exit:

  if (INVALID_HANDLE_VALUE != hFile)
  {
    CloseHandle(hFile);
  }
  if (NULL != hEP)
  {
    hr = DRMCloseHandle(hEP);
    hEP = NULL;
  }
  if (NULL != hBoundLicense)
  {
    hr = DRMCloseHandle(hBoundLicense);
    hBoundLicense = NULL;
  }
  if (NULL != hEBEncryptor)
  {
    hr = DRMCloseHandle(hEBEncryptor);
    hEBEncryptor = NULL;
  }
  if (NULL != pwszOwnerLicense)
  {
    delete [] pwszOwnerLicense;
    pwszOwnerLicense = NULL;
  }

  wprintf(L"Leaving EncryptContent: hr = %x \r\n", hr);
  return hr;
}

The following code sample shows how to use the AES algorithm with the cipher-block chaining (CBC) cipher mode to encrypt content. This functionality is available only in Windows 7.


/*===================================================================
Functions:      PerformCbcEncryption and PerformCbcDecryption

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Copyright (C) Microsoft.  All rights reserved.
===================================================================*/
#include "msdrm.h"
#include "msdrmgetinfo.h"

#define SZ_CBC_KEY_TYPE L"AES_CBC4K"
#define DW_WAIT_TIME 60000
#define SZ_ENABLING_PRINCIPAL_TYPE L"UDStdPlg Enabling Principal"
#define SZ_MAIN_RIGHTS_GROUP L"Main-Rights"
#define SZ_EDIT_RIGHT L"EDIT"
#define SZ_VIEW_RIGHT L"VIEW"

extern HRESULT __stdcall StatusCallback(DRM_STATUS_MSG msg,
                                        HRESULT hr,
                                        void* pvParam,
                                        void* pvContext);

//
// Struct to hold DRMGetSignedIssuanceLicense callback information
//
typedef struct Drm_Context
{
 HANDLE  hEvent;
 HRESULT hr;
 PWSTR   wszData;
} DRM_CONTEXT, *PDRM_CONTEXT;

//
// This helper function will perform steps required to get a bound
// license when provided the required information. It is listed
// here to demonstrate that this part of the CBC encryption and
// decryption flow is unchanged from the standard flow. In practice,
// since creating a new bound license for each encryption and
// decryption request is costly, you would instead obtain all
// requested rights at once in a comma-separated list and reuse
// a single bound license when possible.
//
inline HRESULT GetBoundLicense(__in DRMENVHANDLE hEnv,
                               __in PWCHAR pwszRac,
                               __in PWSTR pwszUseLicense,
                               __in PWSTR pwszRightsRequested,
                               __in DRMHANDLE hIssuanceLicense,
                               __out DRMHANDLE* phBoundLicense)
{
    HRESULT hr = S_OK;
    DRMHANDLE hEnablingPrincipal = NULL;
    DRMID idNULL(NULL, NULL);
    UINT uiContentId = 0;
    UINT uiContentIdType = 0;
    PWSTR pwszContentId = NULL;
    PWSTR pwszContentIdType = NULL;
    DRMID idContent;
    DRMBOUNDLICENSEPARAMS bParams;

    hr = DRMCreateEnablingPrincipal(
        hEnv,                       // Environment handle
        NULL,                       // No library specified
        SZ_ENABLING_PRINCIPAL_TYPE, // Enabling principal type
        &idNULL,                    // DRMID structure
        pwszRac,                    // Current user certificate / RAC
        &hEnablingPrincipal);       // Enabling principal handle
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    // Obtain the sizes of the buffers needed for the
    // content ID and content ID type
    hr = DRMGetMetaData(
          hIssuanceLicense,        // Handle to the license
          &uiContentId,            // Content Id Length
          NULL,                    // Content Id
          &uiContentIdType,        // Content Id Type Length
          NULL,                    // Content Id Type
          NULL,                    // SKU ID Length
          NULL,                    // SKU ID
          NULL,                    // SKU ID Type Length
          NULL,                    // SKU ID Type
          NULL,                    // Content Type Length
          NULL,                    // Content Type
          NULL,                    // Content Name Length
          NULL);                   // Content Name
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    pwszContentId = new WCHAR[uiContentId];
    if(NULL == pwszContentId) 
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }

    pwszContentIdType = new WCHAR[uiContentIdType];
    if(NULL == pwszContentIdType) 
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }

    // Obtain the content ID and content ID type
    hr = DRMGetMetaData(
          hIssuanceLicense,        // Handle to the license
          &uiContentId,            // Content Id Length
          pwszContentId,           // Content Id
          &uiContentIdType,        // Content Id Type Length
          pwszContentIdType,       // Content Id Type
          NULL,                    // SKU ID Length
          NULL,                    // SKU ID
          NULL,                    // SKU ID Type Length
          NULL,                    // SKU ID Type
          NULL,                    // Content Type Length
          NULL,                    // Content Type
          NULL,                    // Content Name Length
          NULL);                   // Content Name
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    idContent.uVersion = 0;
    idContent.wszIDType = pwszContentIdType;
    idContent.wszID = pwszContentId;

    bParams.hEnablingPrincipal = hEnablingPrincipal;
    bParams.hSecureStore = NULL;
    bParams.wszRightsRequested = pwszRightsRequested;
    bParams.wszRightsGroup = SZ_MAIN_RIGHTS_GROUP;
    bParams.idResource = idContent;
    bParams.wszDefaultEnablingPrincipalCredentials = NULL;
    bParams.cAuthenticatorCount = 0;

    hr = DRMCreateBoundLicense(
        hEnv,                      // Environment handle
        &bParams,                  // Additional license options
        pwszUseLicense,            // Use license
        phBoundLicense,            // Bound license handle
        NULL);                     // Reserved

e_Exit:
    if (NULL != hEnablingPrincipal)
    {
        DRMCloseHandle(hEnablingPrincipal);
    }
    if (NULL != pwszContentId)
    {
        delete [] pwszContentId;
    }
    if (NULL != pwszContentIdType)
    {
        delete [] pwszContentIdType;
    }

    return hr;
}

/////////////////////////////////////////////////////////////////////
// The PerformCbcEncryption function performs CBC encryption on the
// given unencrypted data buffer and returns the encrypted data
// in a single, freshly allocated buffer. In practice, you may want
// to modify this function so that larger amounts of data are
// streamed in, rather than having the entire block processed (and
// memory allocated) all at once.
//
// Arguments:
//        pbDataToEncrypt         - [in]  Buffer containing the data
//                                        that is to be encrypted
//        uiUnencryptedDataLength - [in]  Length of the unencrypted
//                                        data (in bytes)
//        hEnv                    - [in]  Environment handle
//        hIssuanceLicense        - [in]  Handle to an issuance
//                                        license
//        pwszCLC                 - [in]  CLC of the publishing user
//        pwszRac                 - [in]  RAC of the publishing user
//        ppbEncryptedData        - [out] Buffer holding the newly
//                                        encrypted data
//        puiEncryptedDataLength  - [out] Length of the encrypted
//                                        data (in bytes)
HRESULT PerformCbcEncryption(__in BYTE* pbDataToEncrypt,
                             __in UINT uiUnencryptedDataLength,
                             __in DRMENVHANDLE hEnv,
                             __in DRMPUBHANDLE hIssuanceLicense,
                             __in PWSTR pwszCLC,
                             __in PWCHAR pwszRac,
                             __out PBYTE* ppbEncryptedData,
                             __out UINT* puiEncryptedDataLength)
{
    HRESULT hr = S_OK;              // Return code
    DRM_CONTEXT context;            // To hold callback information
    DWORD dwWaitResult = 0;         // Result of wait operation
    PWSTR pwszOwnerLicense = NULL;  // Owner use license
    UINT uiOwnerLicenseLength = 0;  // Size of the owner license
    DRMHANDLE hBoundLicense = NULL; // Bound license handle
    DRMHANDLE hEBEncryptor = NULL;  // Encryptor handle
    UINT uiBlockSize = 0;           // Size of each block to be
                                    // passed to DRMEncrypt
    UINT uiBlockSizeInfo = 0;       // Size of the uiBlockSize
                                    // variable
    DRMENCODINGTYPE encodingType;   // Encoding type for DRMGetInfo
    UINT uiPaddingSize = 0;         // Length of padding needed to
                                    // match block size
    UINT uiOffset = 0;              // Offset in encryption buffer

    // For our example, we will be using the "EDIT" right
    // during encryption
    const PWSTR pwszRightsRequested = SZ_EDIT_RIGHT;

    // Validate parameters
    if ((NULL == pbDataToEncrypt)
        || (NULL == hEnv)
        || (NULL == hIssuanceLicense)
        || (NULL == pwszCLC)
        || (NULL == pwszRac)
        || (NULL == ppbEncryptedData)
        || (NULL == puiEncryptedDataLength))
    {
        hr = E_INVALIDARG;
        goto e_Exit;
    }

    // Create an event for the callback function
    context.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    if (NULL == context.hEvent)
    {
        hr = HRESULT_FROM_WIN32(::GetLastError());
        goto e_Exit;
    }
    context.wszData = NULL;

    hr = DRMGetSignedIssuanceLicense(
        hEnv,                         // Environment handle
        hIssuanceLicense,             // Issuance license handle
        (DRM_SIGN_OFFLINE             // Sign offline with a CLC
            | DRM_AUTO_GENERATE_KEY), // Generate a content key
        NULL,                         // No symmetric key specified
        0,                            // No length specified
        SZ_CBC_KEY_TYPE,              // CBC encryption key type
        pwszCLC,                      // Client licensor certificate
        &StatusCallback,              // Callback function
        NULL,                         // No licensing URL specified
        (void*)&context);             // Callback context parameter
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    // Wait for callback to return
    dwWaitResult = ::WaitForSingleObject(context.hEvent,
                                         DW_WAIT_TIME);
    if (WAIT_OBJECT_0 != dwWaitResult)
    {
        hr = HRESULT_FROM_WIN32(::GetLastError());
        goto e_Exit;
    }
    else if (FAILED(context.hr))
    {
        hr = context.hr;
        goto e_Exit;
    }

    // Obtain the length of the owner license
    hr = DRMGetOwnerLicense(
        hIssuanceLicense,             // Issuance license handle
        &uiOwnerLicenseLength,        // Length of owner license
        NULL);                        // Query for license length
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    pwszOwnerLicense = new WCHAR[uiOwnerLicenseLength];
    if (NULL == pwszOwnerLicense)
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }

    hr = DRMGetOwnerLicense(
        hIssuanceLicense,             // Issuance license handle
        &uiOwnerLicenseLength,        // Length of owner license
        pwszOwnerLicense);            // Owner use license
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    // Proceed normally (as you would for non-CBC encryption) to
    // obtain the bound license
    hr = GetBoundLicense(hEnv,
                         pwszRac,
                         pwszOwnerLicense,
                         pwszRightsRequested,
                         hIssuanceLicense,
                         &hBoundLicense);
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    hr = DRMCreateEnablingBitsEncryptor(
        hBoundLicense,                // Bound license handle
        pwszRightsRequested,          // Requested rights
        NULL,                         // Reserved
        NULL,                         // Reserved
        &hEBEncryptor);               // Encryptor handle
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    // Determine the block size you need to pass to DRMEncrypt
    uiBlockSizeInfo = sizeof(uiBlockSize);
    hr = DRMGetInfo(
        hEBEncryptor,                 // Encryptor handle
        g_wszQUERY_BLOCKSIZE,         // Queried attribute
        &encodingType,                // Type of encoding to apply
        &uiBlockSizeInfo,             // Size of uiBlockSize variable
        (BYTE*)&uiBlockSize);         // Block size to use
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    // Since the amount of data we encrypt must be a multiple of the
    // block size, we must determine the amount of padding we need
    // to add to our data and create a buffer of the appropriate
    // size (the size of our original data + padding)
    uiPaddingSize
        = uiBlockSize - (uiUnencryptedDataLength % uiBlockSize);
    *puiEncryptedDataLength
        = uiUnencryptedDataLength + uiPaddingSize;

    *ppbEncryptedData = new BYTE[*puiEncryptedDataLength];
    if (NULL == *ppbEncryptedData)
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }

    // Fill the padding area with zeros
    ::SecureZeroMemory((*ppbEncryptedData + uiUnencryptedDataLength),
                       uiPaddingSize);

    // In this example, we are encrypting our content with one call
    // to DRMEncrypt for each 4 KB block (4096 bytes). In practice,
    // you may want to reduce the number of calls by passing in
    // a quantity of data that is some larger multiple of 4096 bytes.
    for (int i = 0;
         ((UINT)i * uiBlockSize) < *puiEncryptedDataLength;
         i++)
    {
        UINT uiNumBytesEncrypted = 0;
        UINT uiBufferPosition = (i * uiBlockSize);

        hr = DRMEncrypt(
            // Encryptor handle
            hEBEncryptor,
            // Starting block number (1 block = uiBlockSize)
            i,
            // Number of bytes to encrypt
            uiBlockSize,
            // Starting location of bytes to encrypt
            (pbDataToEncrypt + uiBufferPosition),
            // Number of bytes encrypted
            &uiNumBytesEncrypted,
            // Starting location for encrypted content
            (*ppbEncryptedData + uiOffset));
        if (FAILED(hr))
        {
            goto e_Exit;
        }

        uiOffset += uiNumBytesEncrypted;
    }

e_Exit:
    if (NULL != context.hEvent)
    {
        ::CloseHandle(context.hEvent);
    }
    if (NULL != context.wszData)
    {
        delete [] context.wszData;
    }
    if (NULL != pwszOwnerLicense)
    {
        delete [] pwszOwnerLicense;
    }
    if (NULL != hBoundLicense)
    {
        DRMCloseHandle(hBoundLicense);
    }
    if (NULL != hEBEncryptor)
    {
        DRMCloseHandle(hEBEncryptor);
    }

    return hr;
}

/////////////////////////////////////////////////////////////////////
// The PerformCbcDecryption function performs decryption on the
// given CBC encrypted data buffer and returns the decrypted data
// in a single freshly allocated buffer. In practice, you may want
// to modify this function so that larger quantities of data are
// streamed in, rather than having the entire block processed (and
// memory allocated) all at once.
// Arguments:
//        pbEncryptedData         - [in]  Buffer containing the data
//                                        that is to be decrypted
//        uiEncryptedDataLength   - [in]  Length of the encrypted
//                                        data (in bytes)
//        hEnv                    - [in]  Environment handle
//        hIssuanceLicense        - [in]  Handle to an issuance
//                                        license
//        pwszUseLicense          - [in]  Use license
//        pwszRac                 - [in]  RAC of the publishing user
//        ppbDecryptedData        - [out] Buffer holding the newly
//                                        decrypted data
HRESULT PerformCbcDecryption(__in BYTE* pbEncryptedData,
                             __in UINT uiEncryptedDataLength,
                             __in DRMENVHANDLE hEnv,
                             __in DRMPUBHANDLE hIssuanceLicense,
                             __in PWSTR pwszUseLicense,
                             __in PWCHAR pwszRac,
                             __out PBYTE* ppbDecryptedData)
{
    HRESULT hr = S_OK;              // Return code
    DRMHANDLE hBoundLicense = NULL; // Bound license handle
    DRMHANDLE hEBDecryptor = NULL;  // Decryptor handle
    PWSTR pwszKeyType = NULL;       // Key type used
    UINT uiKeyTypeLength = 0;       // Length of pwszKeyType
    UINT uiBlockSize = 0;           // Size of each block to be
                                    // passed to DRMEncrypt
    UINT uiBlockSizeInfo = 0;       // Size of the uiBlockSize
                                    // variable
    DRMENCODINGTYPE encodingType;   // Encoding type for DRMGetInfo
    UINT uiPaddingSize = 0;         // Length of padding needed to
                                    // match block size
    UINT uiOffset = 0;              // Offset in encryption buffer

    // For the example, the "VIEW" right is used
    // during decryption
    const PWSTR pwszRightsRequested = SZ_VIEW_RIGHT;

    // Validate parameters
    if ((NULL == pbEncryptedData)
        || (NULL == hEnv)
        || (NULL == hIssuanceLicense)
        || (NULL == pwszUseLicense)
        || (NULL == pwszRac)
        || (NULL == ppbDecryptedData))
    {
        hr = E_INVALIDARG;
        goto e_Exit;
    }

    // Proceed normally (as you would for non-CBC decryption) to
    // obtain the bound license
    hr = GetBoundLicense(hEnv,
                         pwszRac,
                         pwszUseLicense,
                         pwszRightsRequested,
                         hIssuanceLicense,
                         &hBoundLicense);
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    hr = DRMCreateEnablingBitsDecryptor(
        hBoundLicense,                // Bound license handle
        pwszRightsRequested,          // Requested rights
        NULL,                         // Reserved
        NULL,                         // Reserved
        &hEBDecryptor);               // Decryptor handle
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    // Verify/Determine that CBC decryption is needed. In practice,
    // this may not be needed if you already know whether or not
    // the cipher mode used is CBC
    hr = DRMGetInfo(
        hEBDecryptor,                 // Decryptor handle
        g_wszQUERY_SYMMETRICKEYTYPE,  // Queried attribute
        &encodingType,                // Type of encoding to apply
        &uiKeyTypeLength,             // Size of key type variable
        NULL);                        // Query for length
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    pwszKeyType = new WCHAR[uiKeyTypeLength];
    if (NULL == pwszKeyType)
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }

    hr = DRMGetInfo(
        hEBDecryptor,                 // Decryptor handle
        g_wszQUERY_SYMMETRICKEYTYPE,  // Queried attribute
        &encodingType,                // Type of encoding to apply
        &uiKeyTypeLength,             // Size of key type variable
        (BYTE*)pwszKeyType);          // Key type used
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    if (!wcscmp(SZ_CBC_KEY_TYPE, pwszKeyType))
    {
        // Unexpected -- in practice, you may want to instead
        // handle decryption for this non-CBC key type
        hr = E_UNEXPECTED;
        goto e_Exit;
    }

    // Verify/Determine the block size you need to pass to DRMDecrypt
    uiBlockSizeInfo = sizeof(uiBlockSize);
    hr = DRMGetInfo(
        hEBDecryptor,                 // Decryptor handle
        g_wszQUERY_BLOCKSIZE,         // Queried attribute
        &encodingType,                // Type of encoding to apply
        &uiBlockSizeInfo,             // Size of uiBlockSize variable
        (BYTE*)&uiBlockSize);         // Block size to use
    if (FAILED(hr))
    {
        goto e_Exit;
    }

    *ppbDecryptedData = new BYTE[uiEncryptedDataLength];
    if (NULL == *ppbDecryptedData)
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }

    // In this example, the content is being decrypted with one call
    // to DRMDecrypt for each 4 KB block (4096 bytes). In practice,
    // you may want to reduce the number of calls by passing in
    // a quantity of data that is some larger multiple of 4096 bytes.
    for (int i = 0;
         ((UINT)i * uiBlockSize) < uiEncryptedDataLength;
         i++)
    {
        UINT uiNumBytesDecrypted = 0;
        UINT uiBufferPosition = (i * uiBlockSize);

        hr = DRMDecrypt(
            // Decryptor handle
            hEBDecryptor,
            // Starting block number (1 block = uiBlockSize)
            i,
            // Number of bytes to decrypt
            uiBlockSize,
            // Starting location of bytes to decrypt
            (pbEncryptedData + uiBufferPosition),
            // Number of bytes decrypted
            &uiNumBytesDecrypted,
            // Starting location for decrypted content
            (*ppbDecryptedData + uiOffset));
        if (FAILED(hr))
        {
            goto e_Exit;
        }

        uiOffset += uiNumBytesDecrypted;
    }

e_Exit:
    if (NULL != hBoundLicense)
    {
        DRMCloseHandle(hBoundLicense);
    }
    if (NULL != hEBDecryptor)
    {
        DRMCloseHandle(hEBDecryptor);
    }
    if (NULL != pwszKeyType)
    {
        delete [] pwszKeyType;
    }

    return hr;
}

Requirements

Requirement Value
Target Platform Windows
Header msdrm.h
Library Msdrm.lib
DLL Msdrm.dll

See also

AD RMS Functions

DRMCreateEnablingBitsEncryptor

DRMDecrypt

Encrypting Content

Encrypting Content Code Example

Encryption_EncryptContent.cpp