Encryption and Decryption
Windows Media Device Manager requires encryption of files sent between the service provider and the application. This can be done in one of two ways:
- If the service provider supports only IMDSPObject::Read and IMDSPObject::Write, the data must be encrypted and decrypted by the application and service provider by using CSecureChannelClient and CSecureChannelServer methods respectively.
- If the service provider supports IMDSPObject2::ReadOnClearChannel and IMDSPObject2::WriteOnClearChannel, your application can avoid costly secure channel message authentication. (The secure channel is retained so that legacy service providers that do not implement IMDSPObject2 can still continue to function.)
The encryption requirement prevents malicious applications from obtaining data that is being passed between software components, and also protects the integrity of data being sent to or from the device.
The following three methods require encryption or decryption.
Method | Description |
IWMDMOperation::TransferObjectData | (Application) Encryption or decryption, depending on whether the application is sending or receiving data. |
IMDSPObject::Read | (Service provider) Encryption. |
IMDSPObject::Write | (Service Provider) Decryption. |
Encryption and decryption are both done by single method calls. Encryption is done by CSecureChannelClient::EncryptParam for applications or by CSecureChannelServer::EncryptParam for service providers. Decryption is done by CSecureChannelClient::DecryptParam for applications or CSecureChannelServer::DecryptParam for service providers. The parameters are identical between the client and server methods.
The following steps show how to encrypt and decrypt data. (These steps are important only if your application communicates with a legacy service provider that does not implement IWMDMOperation3::TransferObjectDataOnClearChannel.)
Encryption
- Create the MAC key for the encrypted data, as described in Message Authentication.
- Call EncryptParam with the data to encrypt, to perform in-place encryption.
The following code example demonstrates a service provider's implementation of IMDSPObject::Read. This method creates the MAC key using the data to encrypt and the size of the data, and sends them both to the application.
HRESULT CMyStorage::Read(
BYTE *pData,
DWORD *pdwSize,
BYTE abMac[WMDM_MAC_LENGTH])
{
HRESULT hr;
DWORD dwToRead; // Bytes to read.
DWORD dwRead = NULL; // Bytes read.
BYTE *pTmpData = NULL; // Temporary buffer to hold data before
// it is copied to pData.
// Use a global CSecureChannelServer member to verify that
// the client is authenticated.
if (!(g_pAppSCServer->fIsAuthenticated()))
{
return WMDM_E_NOTCERTIFIED;
}
// Verify that the handle to the file to read is valid.
if(m_hFile == INVALID_HANDLE_VALUE)
{
return E_FAIL;
}
// Create a buffer to hold the data read.
dwToRead = *pdwSize;
pTmpData = new BYTE [dwToRead] ;
if(!pTmpData)
return E_OUTOFMEMORY;
// Read data into the temporary buffer.
if(ReadFile(m_hFile,(LPVOID)pTmpData,dwToRead,&dwRead,NULL))
{
*pdwSize = dwRead;
if( dwRead )
{
// Create a MAC from all the parameters.
// CORg is a macro that goes to Error label on failure.
// MAC consists of data and size of data.
HMAC hMAC;
CORg(g_pAppSCServer->MACInit(&hMAC));
CORg(g_pAppSCServer->MACUpdate(hMAC, (BYTE*)(pTmpData), dwRead));
CORg(g_pAppSCServer->MACUpdate(hMAC, (BYTE*)(pdwSize), sizeof(DWORD)));
CORg(g_pAppSCServer->MACFinal(hMAC, abMac));
// Encrypt the data.
CORg(g_pAppSCServer->EncryptParam(pTmpData, dwRead));
// Copy data from the temporary buffer into the out parameter.
memcpy(pData, pTmpData, dwRead);
}
hr = S_OK;
}
else
{
*pdwSize = 0;
hr = E_FAIL;
}
Error:
if(pTmpData)
{
delete [] pTmpData;
}
return hr;
}
Decryption
- Call DecryptParam with the data to encrypt, to perform in-place decryption.
- Verify the MAC key for the decrypted data, as described in Message Authentication.
The following code example demonstrates a service provider's implementation of IMDSPObject::Write. This method creates the MAC key using the data to encrypt and the size of the data, and sends them both to the application.
HRESULT CMyStorage::Write(BYTE *pData, DWORD *pdwSize,
BYTE abMac[WMDM_MAC_LENGTH])
{
HRESULT hr;
DWORD dwWritten = 0;
BYTE *pTmpData = NULL; // Temporary buffer to hold the
// data during decryption.
BYTE pTempMac[WMDM_MAC_LENGTH]; // Temporary MAC that will be
// copied into the abMac
// out parameter.
if( m_hFile == INVALID_HANDLE_VALUE )
{
return E_FAIL;
}
// Allocate the temporary buffer and copy the encrypted data into it.
pTmpData = new BYTE [*pdwSize];
if(!pTmpData)
return E_OUTOFMEMORY;
memcpy(pTmpData, pData, *pdwSize);
// Decrypt the data.
CHRg(g_pAppSCServer->DecryptParam(pTmpData, *pdwSize));
// Check the MAC passed to the method. The MAC is built from
// the data and data size parameters.
// CORg is a macro that goes to the Error label on failure.
HMAC hMAC;
CORg(g_pAppSCServer->MACInit(&hMAC));
CORg(g_pAppSCServer->MACUpdate(hMAC, (BYTE*)(pTmpData), *pdwSize));
CORg(g_pAppSCServer->MACUpdate(hMAC, (BYTE*)(pdwSize), sizeof(*pdwSize)));
CORg(g_pAppSCServer->MACFinal(hMAC, pTempMac));
// If the MAC values don't match, return an error.
if (memcmp(abMac, pTempMac, WMDM_MAC_LENGTH) != 0)
{
hr = WMDM_E_MAC_CHECK_FAILED;
goto Error;
}
// The MAC values matched, so write the decrypted data to a local file.
if( WriteFile(m_hFile,pTmpData,*pdwSize,&dwWritten,NULL) )
{
hr = S_OK;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
*pdwSize = dwWritten;
Error:
if( pTmpData )
{
delete [] pTmpData;
}
return hr;
}
See Also