Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1220 lines
31 KiB

// Copyright (c) 2002 Microsoft Corporation
//
// Encrypted string class
//
// 18 March 2002
#ifndef _PUBLIC_ENCRYPTEDSTRING_
#define _PUBLIC_ENCRYPTEDSTRING_
#include <strsafe.h>
#include <stddef.h> // For size_t.
#include <wincrypt.h> // For DATA_BLOB.
//
// A class that has a similar public interface as class Burnslib::String, but
// is represented as an encrypted blob, produced by data protection API.
//
// This class can be used to represent sensitive data like password strings
// in-memory, instead of holding them as cleartext, which is a security hole
// if the memory pages are swapped to disk, the machine is hibernated, etc.
//
// You should convert any cleartext data into an instance of this class, then
// scribble out your cleartext source copy with SecureZeroMemory.
//
// This implementation has the following requirements of the code in which it
// is used:
// a) An ASSERT() macro must be defined.
// b) It uses functions defined in strsafe.h. This header file deprecates many
// "unsafe" functions, and your compiler will let you know as much if you have any.
// If you do not want to change occurrences of unsafe functions in your code you can
// '#define STRSAFE_NO_DEPRECATE' to get your program to compile cleanly.
//
//
// Low memory behavior:
//
// - Encrypt() will return E_OUTOFMEMORY and the encrypted string will be set to empty.
// You can check for low memory by checking error code or by calling IsEmpty().
//
// - GetClearTextCopy() will return NULL on failure. The encrypted string itself will not
// be changed.
//
// - Copy constructor will set the newly constructed string to empty if there is not enough
// memory. Check with IsEmpty().
//
// - Assignment operator will similarly set the assignee (left hand side string) to empty if
// there is not enough memory. Check with IsEmpty().
//
// - Default constructor does not allocate any memory. If there is no stack memory there
// will be a stack overflow, and your program will definitely be notified. If there is no
// heap memory the call to the default constructor will fail before the constructor is
// entered. Be sure to check for NULL if you are allocating objects on the heap.
//
// **Of course if the clear text string is an empty string then the encrypted version will
// also be empty (IsEmpty() will return true). This is a pretty easy check to make when
// there would be any ambiguity.
//
//
// NB:
//
// By default this class uses Crypt[Un]ProtectMemory() to handle encryption. These functions
// are only available as of .NET Server. If your component might be installed on an OS
// predating .NET Server you can '#define ENCRYPT_WITH_CRYPTPROTECTDATA' to get an
// EncryptedString that will run on W2K and XP. Define the symbol before you include the
// header file.
//
// CryptProtectMemory() is the more robust API in that it will succeed even if the user's
// profile cannot be loaded. It is more suited to encrypting temporary buffers in memory.
// Regardless of which encryption mechanism you enable, be sure to check return values to
// see if encryption failed. If it fails, you basically have an empty string!
//
//
// MFC code uses DDX_* functions to perform data binding b/w dialogs and underlying
// data. Since none of these work with EncryptedString, we define our own. If you
// need this, just '#define _DDX_ENCRYPTED'.
//
// There are also a pair of utility functions to get and set encrypted data in a
// dialog window.
//
#ifdef _DDX_ENCRYPTED
class EncryptedString;
// Neither parameter is declared constant b/c (depending on direction of
// data exchange) either one can be updated.
inline HRESULT
DDX_EncryptedText(CDataExchange* pDX, int nIDC, EncryptedString& buff);
#include <windows.h>
inline HRESULT
GetEncryptedDlgItemText(HWND parentDialog, int itemResID, EncryptedString& cypher);
inline HRESULT
SetDlgItemText(HWND parentDialog, int itemResID, const EncryptedString& cypher);
#endif //_DDX_ENCRYPTED
class EncryptedString
{
public:
// constructs an empty string.
inline explicit
EncryptedString(void);
// constructs a copy of an existing, already encrypted string
inline
EncryptedString(const EncryptedString& rhs);
// scribbles out the text, and deletes it.
~EncryptedString()
{
// when the object dies, it had better not have any outstanding
// cleartext copies.
ASSERT(m_cleartextCopyCount == 0);
Reset();
}
// Scribbles out and de-allocates a clear text copy of the string. The
// caller is required to balance each call to GetClearTextCopy with a call
// to DestroyClearTextCopy lest he leak memory and leave cleartext hanging
// around.
//
// cleartext - the same pointer returned by a previous call to
// GetClearTextCopy on the same instance.
inline void
DestroyClearTextCopy(WCHAR* cleartext) const;
// Extracts the decoded cleartext representation of the text, including
// null terminator. The caller must invoke DestroyClearTextCopy on the
// result, even if the result is null.
//
// May return null on error.
//
// Example:
// WCHAR* cleartext = encrypted.GetClearTextCopy();
// if (cleartext)
// {
// // ... use the cleartext
// }
// encrypted.DestroyClearTextCopy(cleartext);
inline WCHAR*
GetClearTextCopy() const;
// Returns true if the string is zero-length, false if not.
bool
IsEmpty() const
{
return (GetLength() == 0);
}
// Sets the contents of self to the encrypted representation of the
// cleartext, replacing the previous value of self. The encrypted
// representation will be the same length, in characters, as the
// cleartext.
//
// clearText - in, un-encrypted text to be encrypted. May be empty string,
// but not a null pointer. The clearText must be null terminated.
//
// length - The character length of the clear text, not including null
// termination. The count should be in unicode characters.
//
// Returns S_OK on successful encryption, error code otherwise. If low
// memory causes failure the code will be E_OUTOFMEMORY.
inline HRESULT
Encrypt(const WCHAR* cleartext, size_t length);
inline HRESULT
Encrypt(const WCHAR* clearText);
// Returns the length, in unicode characters, of the cleartext, not
// including the null terminator.
inline size_t
GetLength() const;
void
Clear()
{
// when the object dies, it had better not have any outstanding
// cleartext copies.
ASSERT(m_cleartextCopyCount == 0);
Reset();
}
// Replaces the contents of self with a copy of the contents of rhs.
// Returns *this.
inline const EncryptedString&
operator= (const EncryptedString& rhs);
// Compares the cleartext representations of self and rhs, and returns
// true if they are lexicographically the same: the lengths are the same
// and all the characters are the same.
//
// If the program runs out of memory in this method it will always return
// false since we will be unable to determine if the strings are equal.
inline bool
operator== (const EncryptedString& rhs) const;
bool
operator!= (const EncryptedString& rhs) const
{
return !(operator==(rhs));
}
private:
inline HRESULT
InternalEncrypt(const WCHAR* clearText, size_t length);
inline WCHAR*
InternalDecrypt() const;
inline void
InternalDestroy(WCHAR* cleartext) const;
inline void
Reset();
bool
IsInNullBlobState() const;
inline static DWORD CalculateBlobSize(DWORD dataSize);
inline static HRESULT CopyBlob(DATA_BLOB& dest, const DATA_BLOB& source);
inline static void DestroyBlob(DATA_BLOB& blob);
inline static HRESULT EncryptData(const DATA_BLOB& clearText, DATA_BLOB& cypherText);
inline static HRESULT DecryptData(const DATA_BLOB& cypherText, DATA_BLOB& clearText);
// We deliberately do not implement conversion to or from WCHAR*.
// This is to force the user of the class to be very deliberate
// about decoding the string.
// deliberately commented out
//operator WCHAR* ();
size_t m_clearTextLength;
// In the course of en[de]crypting, and assigning to the instance, we
// may tweak the members of cypherBlob, but logically the string is still
// "const"
mutable DATA_BLOB m_cypherBlob;
// In debug versions there will be extra checking to make sure that
// all of the clear text copies are zeroed and freed.
#ifdef DBG
// a logically const instance may have cleartext copies made
mutable int m_cleartextCopyCount;
#endif
};
////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////
inline HRESULT
GetLastErrorAsHresult(void)
{
DWORD err = GetLastError();
return HRESULT_FROM_WIN32(err);
}
#ifdef _DDX_ENCRYPTED
//
// Get the text from a dialog into an encrypted string.
//
inline HRESULT
GetEncryptedDlgItemText(HWND parentDialog, int itemResID, EncryptedString& cypher)
{
ASSERT(IsWindow(parentDialog));
ASSERT(itemResID > 0);
HRESULT err = S_OK;
WCHAR* cleartext = 0;
int length = 0;
do
{
HWND item = GetDlgItem(parentDialog, itemResID);
if (!item)
{
err = GetLastErrorAsHresult();
break;
}
length = GetWindowTextLengthW(item);
if (length < 0)
{
err = GetLastErrorAsHresult();
break;
}
// add 1 to length for null-terminator
++length;
cleartext = new WCHAR[length];
if (NULL == cleartext)
{
err = E_OUTOFMEMORY;
break;
}
ZeroMemory(cleartext, length * sizeof WCHAR);
// length includes space for null terminator
// which is correct for this call.
int result = GetWindowText(item, cleartext, length);
ASSERT(result == length - 1);
if (result < 0)
{
err = GetLastErrorAsHresult();
break;
}
err = cypher.Encrypt(cleartext);
}
while (0);
// make sure we scribble out the cleartext.
if (cleartext)
{
SecureZeroMemory(cleartext, length * sizeof WCHAR);
delete[] cleartext;
}
return err;
}
//
// Set the text in a dialog from an encrypted string.
//
inline HRESULT
SetDlgItemText(
HWND parentDialog,
int itemResID,
const EncryptedString& cypherText)
{
ASSERT(IsWindow(parentDialog));
ASSERT(itemResID > 0);
HRESULT hr = S_OK;
WCHAR* cleartext = cypherText.GetClearTextCopy();
if (NULL != cleartext)
{
BOOL succeeded = SetDlgItemText(parentDialog, itemResID, cleartext);
if (!succeeded)
{
hr = GetLastErrorAsHresult();
}
}
else
{
hr = E_OUTOFMEMORY;
}
cypherText.DestroyClearTextCopy(cleartext);
// This shouldn't fail under any normal circumstances.
ASSERT(SUCCEEDED(hr));
return hr;
}
//
// DDX_EncryptedText:
//
// Function performs data binding between a dialog text control and an
// encrypted string.
//
// For information on DDX_* functions and how they work, see
// "Technical Note 26" in MSDN documentation.
//
// History:
// 2002-04-04 artm Created.
//
inline HRESULT DDX_EncryptedText(CDataExchange* pDX, int nIDC, EncryptedString& buff)
{
HRESULT err = S_OK;
do { // false while loop
// Make sure that we actually have a window with which to
// exchange data.
if (NULL == pDX ||
NULL == pDX->m_pDlgWnd)
{
err = E_INVALIDARG;
break;
}
HWND hWnd = pDX->m_pDlgWnd->GetSafeHwnd();
if (NULL == hWnd)
{
err = E_INVALIDARG;
break;
}
// Check which direction the data exchange is happening.
if (pDX->m_bSaveAndValidate)
{
//
// Save the text in the dialog to the encrypted buffer.
//
err = GetEncryptedDlgItemText(hWnd, nIDC, buff);
}
else
{
//
// Refresh the text in the dialog from the encrypted buffer.
//
err = SetDlgItemText(hWnd, nIDC, buff);
}
} while(false);
return err;
}
#endif //_DDX_ENCRYPTED
inline
EncryptedString::EncryptedString(void)
:
m_clearTextLength(0)
#ifdef DBG
,m_cleartextCopyCount(0)
#endif
{
::ZeroMemory(&m_cypherBlob, sizeof m_cypherBlob);
ASSERT(IsInNullBlobState());
}
//
// CalculateBlobSize():
//
// Given the size of the data in bytes that must fit in the blob,
// calculates how large the blob must be to hold that data and be
// a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE.
//
// History:
// 2002-06-08 artm Created (NTRAID#NTBUG9-635046)
//
inline DWORD
EncryptedString::CalculateBlobSize(DWORD dataSize)
{
// Round the dataSize down to a multiple of the block size.
DWORD blobSize =
(dataSize / CRYPTPROTECTMEMORY_BLOCK_SIZE) * CRYPTPROTECTMEMORY_BLOCK_SIZE;
// If blobSize cannot hold dataSize, increase blobSize by one memory block.
if (blobSize < dataSize)
{
blobSize += CRYPTPROTECTMEMORY_BLOCK_SIZE;
}
ASSERT(blobSize >= dataSize);
return blobSize;
}
//
// CopyBlob():
//
// Copies source to destination (make sure that you've freed any
// memory tied to destination before calling). If copy is
// successful returns S_OK. If memory cannot be allocated then
// returns E_OUTOFMEMORY and destination is set to an empty blob.
//
inline HRESULT
EncryptedString::CopyBlob(DATA_BLOB& dest, const DATA_BLOB& source)
{
HRESULT err = S_OK;
dest.cbData = source.cbData;
dest.pbData = 0;
if (dest.cbData)
{
// copy the blob data. We use LocalAlloc because that's what
// CryptProtectData does, thus Reset() can use LocalFree no matter
// how the blob was populated.
HLOCAL local = ::LocalAlloc(LPTR, dest.cbData);
if (local)
{
dest.pbData = (BYTE*) local;
::CopyMemory(
dest.pbData,
source.pbData,
source.cbData);
}
else
{
// out of memory means you get an empty instance
dest.cbData = 0;
dest.pbData = 0;
err = E_OUTOFMEMORY;
}
}
return err;
}
inline void
EncryptedString::DestroyBlob(DATA_BLOB& blob)
{
if (blob.cbData)
{
ASSERT(blob.pbData);
if (blob.pbData)
{
// We want to zero out the blob in case it held
// clear text data.
SecureZeroMemory(blob.pbData, blob.cbData);
::LocalFree(blob.pbData);
}
blob.pbData = 0;
blob.cbData = 0;
}
}
inline
EncryptedString::EncryptedString(const EncryptedString& rhs)
:
m_clearTextLength(rhs.m_clearTextLength)
#ifdef DBG
// although the rhs instance may have outstanding copies, we don't
,m_cleartextCopyCount(0)
#endif
{
// Copy the blob, which is already encrypted.
HRESULT hr = CopyBlob(m_cypherBlob, rhs.m_cypherBlob);
// Can't return error from constructor, but maybe we can detect program
// bugs during debug build.
ASSERT(SUCCEEDED(hr));
if (FAILED(hr))
{
Reset();
}
#ifdef DBG
if (rhs.IsInNullBlobState())
{
ASSERT(IsInNullBlobState());
}
#endif
}
inline const EncryptedString&
EncryptedString::operator= (const EncryptedString& rhs)
{
// don't reset the instance unless you have destroyed all the cleartext
// copies. We assert this before checking for a=a, because the caller
// still has a logic error even if the result is "harmless"
ASSERT(m_cleartextCopyCount == 0);
// handle the a = a case.
if (this == &rhs)
{
return *this;
}
// dump our old contents
Reset();
HRESULT hr = CopyBlob(m_cypherBlob, rhs.m_cypherBlob);
if (SUCCEEDED(hr))
{
m_clearTextLength = rhs.m_clearTextLength;
}
else
{
// Copy failed! That means we have an empty blob.
// Reset() sets length to 0, so we don't need to
// do it here.
// Alert programmer in debug build. This function has
// no error code, so nothing we can do in release build.
ASSERT(false);
}
return *this;
}
// scribbles out and frees the internal string.
inline void
EncryptedString::Reset()
{
// don't reset the instance unless you have destroyed all the cleartext
// copies.
ASSERT(m_cleartextCopyCount == 0);
DestroyBlob(m_cypherBlob);
m_clearTextLength = 0;
ASSERT(IsInNullBlobState());
}
// A word on state:
//
// an instance can be in one of two internal states: null blob and non-null
// blob. These refer to whether or not the instance actually holds any
// encrypted text.
//
// For the sake of the public interface, the null blob state is the same as
// the non-null blob when the non-null blob holds an empty string. In other
// words, an instance in the null blob state is an empty string.
inline bool
EncryptedString::IsInNullBlobState() const
{
if ( !m_cypherBlob.cbData
&& !m_cypherBlob.pbData)
{
ASSERT(m_clearTextLength == 0);
return true;
}
return false;
}
// builds the internal encrypted representation from the cleartext.
//
// clearText - in, un-encrypted text to be encrypted. May be empty string, but
// not a null pointer.
//
// length - The character length of the clear text, not including null
// termination. The count should be in unicode characters.
//
// Returns S_OK on successful encryption, error code otherwise. If low
// memory causes failure the code will be E_OUTOFMEMORY.
//
// Note: At this point we trust that length is the _correct_ length of
// the string.
inline HRESULT
EncryptedString::InternalEncrypt(const WCHAR* clearText, size_t length)
{
ASSERT(clearText);
// don't reset the instance unless you have destroyed all the cleartext
// copies.
ASSERT(m_cleartextCopyCount == 0);
HRESULT err = S_OK;
Reset();
if (clearText)
{
DATA_BLOB dataIn;
// Determine size of the data blob, including space for null.
size_t blobSizeInBytes = (length + 1) * sizeof(WCHAR);
dataIn.cbData = (DWORD)blobSizeInBytes;
dataIn.pbData = (BYTE*)clearText;
// Encrypt the clear text. We use a temporary blob so
// that if the encryption fails, the old cypher blob is
// left untouched.
err = EncryptData(dataIn, m_cypherBlob);
if (SUCCEEDED(err))
{
m_clearTextLength = length;
}
else
{
// if the encryption bombed, make this the an empty string. One
// reason it can bomb is out-of-memory.
Reset();
}
}
ASSERT(m_cleartextCopyCount == 0);
return err;
}
// decrypts the blob and returns a copy of the cleartext, but does not
// bump up the outstanding copy counter. Result must be freed with
// LocalFree. May return null (if not enough memory available).
//
// Used internally to prevent infinite mutual recursion
inline WCHAR*
EncryptedString::InternalDecrypt() const
{
HRESULT hr = S_OK;
DATA_BLOB clearText;
::ZeroMemory(&clearText, sizeof(DATA_BLOB));
WCHAR* copy = NULL;
if (IsInNullBlobState())
{
// Return an empty string for the clear text since a null
// blob state should be indistinguishable from an encrypted L"".
// LocalAlloc() zeroes the memory for us (since we passed LPTR).
copy = (WCHAR*) ::LocalAlloc(LPTR, sizeof(WCHAR));
}
else
{
hr = DecryptData(m_cypherBlob, clearText);
if (SUCCEEDED(hr))
{
copy = (WCHAR*) clearText.pbData;
}
}
#ifdef DBG
if (clearText.pbData)
{
// An empty string should consist of at least a null terminator
ASSERT(clearText.cbData >= sizeof WCHAR);
// check for the null terminator
ASSERT(
( *(clearText.pbData + clearText.cbData - 2) == 0)
&& ( *(clearText.pbData + clearText.cbData - 1) == 0) );
// the decrypted version should be the same length as the encrypted
// version
#ifndef ENCRYPT_WITH_CRYPTPROTECTDATA
// We need a different version of this check since the CryptProtectMemory()
// API requires blobs whose sizes are a multiple of a block size.
size_t decryptedLength = 0;
hr = StringCchLength(
reinterpret_cast<LPCWSTR>(clearText.pbData),
m_clearTextLength + 1,
&decryptedLength);
ASSERT(SUCCEEDED(hr));
ASSERT(decryptedLength == m_clearTextLength);
#else
ASSERT(clearText.cbData / sizeof WCHAR == m_clearTextLength + 1);
#endif // ENCRYPT_WITH_CRYPTPROTECTDATA
}
#endif
return copy;
}
inline void
EncryptedString::InternalDestroy(WCHAR* cleartext) const
{
if (cleartext)
{
::SecureZeroMemory(cleartext, m_clearTextLength * sizeof WCHAR);
::LocalFree(cleartext);
}
}
inline WCHAR*
EncryptedString::GetClearTextCopy() const
{
#ifdef DBG
// Even if we fail the decryption, we bump the count so that it's easy for
// the caller to always balance GetClearTextCopy with DestroyClearTextCopy
++m_cleartextCopyCount;
#endif
WCHAR* copy = NULL;
copy = InternalDecrypt();
return copy;
}
inline HRESULT
EncryptedString::Encrypt(const WCHAR* clearText)
{
ASSERT(clearText);
// don't reset the instance unless you have destroyed all the cleartext
// copies.
ASSERT(m_cleartextCopyCount == 0);
HRESULT err = S_OK;
if (NULL != clearText)
{
// We need to figure out the length of clearText.
// Only option is to use an unbounded length function.
size_t length = wcslen(clearText);
err = InternalEncrypt(clearText, length);
}
else
{
err = E_INVALIDARG;
}
return err;
}
inline HRESULT
EncryptedString::Encrypt(const WCHAR* clearText, size_t length)
{
ASSERT(clearText);
// don't reset the instance unless you have destroyed all the cleartext
// copies.
ASSERT(m_cleartextCopyCount == 0);
HRESULT err = S_OK;
if (NULL != clearText)
{
// Even though the caller tells us the length of clearText,
// we don't really trust them and want to make sure that:
// a) The string really is null terminated, and
// b) That the null termination happens exactly where they
// say it does.
size_t lengthCheck;
err = StringCchLength(
clearText,
// Maximum length, including space for null
(length + 1),
&lengthCheck);
ASSERT(lengthCheck == length);
if (lengthCheck == length)
{
err = InternalEncrypt(clearText, length);
}
else
{
err = E_INVALIDARG;
}
}
else
{
err = E_INVALIDARG;
}
return err;
}
inline bool
EncryptedString::operator==(const EncryptedString& rhs) const
{
// handle the a == a case
if (this == &rhs)
{
return true;
}
if (GetLength() != rhs.GetLength())
{
// can't be the same if lengths differ...
return false;
}
// Two strings are the same if their decoded contents are the same.
WCHAR* clearTextThis = GetClearTextCopy();
WCHAR* clearTextThat = rhs.GetClearTextCopy();
// If memory is scarce either clear text copy could be null.
// In that case we cannot determine if the strings are equal we'll
// assume that they are not and return false.
bool result = false;
if (clearTextThis && clearTextThat)
{
result = (wcscmp(clearTextThis, clearTextThat) == 0);
}
DestroyClearTextCopy(clearTextThis);
rhs.DestroyClearTextCopy(clearTextThat);
return result;
}
inline size_t
EncryptedString::GetLength() const
{
#ifdef DBG
// we don't use GetClearTextCopy, that may result in infinite recursion
// since this function is called internally.
WCHAR* clearTextThis = InternalDecrypt();
size_t len = 0;
if (clearTextThis)
{
HRESULT hr =
StringCchLength(
clearTextThis,
// +1 for the null character
(m_clearTextLength + 1),
&len);
if (FAILED(hr))
{
// we should be guaranteeing that the result of GetClearTextCopy
// is always null-terminated, so a failure here represents a bug
// in our implementation.
ASSERT(false);
len = 0;
}
}
InternalDestroy(clearTextThis);
ASSERT(len == m_clearTextLength);
#endif
return m_clearTextLength;
}
inline void
EncryptedString::DestroyClearTextCopy(WCHAR* cleartext) const
{
// we expect that cleartext is usually non-null. It may not be, if
// GetClearTextCopy failed, however.
// ASSERT(cleartext);
// we should have some outstanding copies. If not, then the caller has
// called DestroyClearTextCopy more times than he called GetClearTextCopy,
// and therefore has a bug.
ASSERT(m_cleartextCopyCount);
InternalDestroy(cleartext);
#ifdef DBG
--m_cleartextCopyCount;
#endif
}
//
// EncryptData:
//
// Encrypts data in clearText into cypherText using data protection
// API. This function allocates memory to cypherText so make sure to
// release any memory tied to cypherText before calling EncryptData().
// Conversely, free the memory this function allocates by calling
// LocalFree(cypherText).
//
// History:
// 2002/04/01 artm Created.
// 2002/06/08 artm Changed implementation to include CryptProtectMemory().
// NTRAID#NTBUG9-635046
//
inline HRESULT
EncryptedString::EncryptData(const DATA_BLOB& clearText, DATA_BLOB& cypherText)
{
ASSERT(clearText.cbData);
ASSERT(clearText.pbData);
::ZeroMemory(&cypherText, sizeof cypherText);
HRESULT hr = S_OK;
BOOL succeeded = FALSE;
// Tell compiler that false while loop is okay.
#pragma warning( push )
#pragma warning( disable : 4127 )
do // false while loop
{
#ifndef ENCRYPT_WITH_CRYPTPROTECTDATA
// Allocate a cypher blob big enough for data and that is
// a multiple of the block size.
cypherText.cbData = CalculateBlobSize(clearText.cbData);
HLOCAL local = ::LocalAlloc(LPTR, cypherText.cbData);
if (!local)
{
hr = E_OUTOFMEMORY;
break;
}
// Copy the clear text to the cypher text buffer.
cypherText.pbData = (BYTE*)local;
::CopyMemory(
cypherText.pbData,
clearText.pbData,
clearText.cbData);
// Encrypt the cypher blob.
succeeded =
::CryptProtectMemory(
cypherText.pbData,
cypherText.cbData,
CRYPTPROTECTMEMORY_SAME_PROCESS);
#else
succeeded =
::CryptProtectData(
const_cast<DATA_BLOB*>(&clearText),
0,
0,
0,
0,
CRYPTPROTECT_UI_FORBIDDEN,
&cypherText);
#endif
if (!succeeded)
{
hr = GetLastErrorAsHresult();
break;
}
} while(false);
#pragma warning( pop )
if (FAILED(hr))
{
// Ensure that any clear text copied to the cypher blob is
// scratched out and then freed.
DestroyBlob(cypherText);
// should have a null result on failure, but just in case...
ASSERT(!cypherText.cbData && !cypherText.pbData);
}
// we don't assert success, as the API may have run out of memory.
return hr;
}
//
// DecryptData:
//
// Decrypts cypherText into clearText using data protection API.
// This function does not release any memory tied to clearText, so
// check that no memory in clearText needs to be released before calling.
// To release the memory allocated to clearText in this function call
// LocalFree().
//
// History:
// 2002/04/01 artm Created.
// 2002/06/08 artm Changed implementation to include CryptUnprotectMemory().
// NTRAID#NTBUG9-635046
//
inline HRESULT
EncryptedString::DecryptData(const DATA_BLOB& cypherText, DATA_BLOB& clearText)
{
ASSERT(cypherText.cbData);
ASSERT(cypherText.pbData);
::ZeroMemory(&clearText, sizeof clearText);
HRESULT hr = S_OK;
BOOL succeeded = FALSE;
// Tell compiler that false while loop is okay.
#pragma warning( push )
#pragma warning( disable : 4127 )
do // false while loop
{
#ifndef ENCRYPT_WITH_CRYPTPROTECTDATA
// Our cypher blob should always be a multiple of the block size;
// if it isn't then it is a bug in the implementation.
ASSERT( (cypherText.cbData % CRYPTPROTECTMEMORY_BLOCK_SIZE) == 0);
hr = CopyBlob(clearText, cypherText);
if (FAILED(hr))
{
break;
}
succeeded =
::CryptUnprotectMemory(
clearText.pbData,
clearText.cbData,
CRYPTPROTECTMEMORY_SAME_PROCESS);
#else
succeeded =
::CryptUnprotectData(
const_cast<DATA_BLOB*>(&cypherText),
0,
0,
0,
0,
CRYPTPROTECT_UI_FORBIDDEN,
&clearText);
#endif
if (!succeeded)
{
hr = GetLastErrorAsHresult();
break;
}
} while(false);
#pragma warning( pop )
if (FAILED(hr))
{
// Scratch out and free anything we put in clearText.
DestroyBlob(clearText);
// should have a null result on failure, but just in case...
ASSERT(!clearText.cbData && !clearText.pbData);
}
// we don't assert success, as the API may have run out of memory.
return hr;
}
#endif // _PUBLIC_ENCRYPTEDSTRING_