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

  1. // Copyright (c) 2002 Microsoft Corporation
  2. //
  3. // Encrypted string class
  4. //
  5. // 18 March 2002
  6. #ifndef _PUBLIC_ENCRYPTEDSTRING_
  7. #define _PUBLIC_ENCRYPTEDSTRING_
  8. #include <strsafe.h>
  9. #include <stddef.h> // For size_t.
  10. #include <wincrypt.h> // For DATA_BLOB.
  11. //
  12. // A class that has a similar public interface as class Burnslib::String, but
  13. // is represented as an encrypted blob, produced by data protection API.
  14. //
  15. // This class can be used to represent sensitive data like password strings
  16. // in-memory, instead of holding them as cleartext, which is a security hole
  17. // if the memory pages are swapped to disk, the machine is hibernated, etc.
  18. //
  19. // You should convert any cleartext data into an instance of this class, then
  20. // scribble out your cleartext source copy with SecureZeroMemory.
  21. //
  22. // This implementation has the following requirements of the code in which it
  23. // is used:
  24. // a) An ASSERT() macro must be defined.
  25. // b) It uses functions defined in strsafe.h. This header file deprecates many
  26. // "unsafe" functions, and your compiler will let you know as much if you have any.
  27. // If you do not want to change occurrences of unsafe functions in your code you can
  28. // '#define STRSAFE_NO_DEPRECATE' to get your program to compile cleanly.
  29. //
  30. //
  31. // Low memory behavior:
  32. //
  33. // - Encrypt() will return E_OUTOFMEMORY and the encrypted string will be set to empty.
  34. // You can check for low memory by checking error code or by calling IsEmpty().
  35. //
  36. // - GetClearTextCopy() will return NULL on failure. The encrypted string itself will not
  37. // be changed.
  38. //
  39. // - Copy constructor will set the newly constructed string to empty if there is not enough
  40. // memory. Check with IsEmpty().
  41. //
  42. // - Assignment operator will similarly set the assignee (left hand side string) to empty if
  43. // there is not enough memory. Check with IsEmpty().
  44. //
  45. // - Default constructor does not allocate any memory. If there is no stack memory there
  46. // will be a stack overflow, and your program will definitely be notified. If there is no
  47. // heap memory the call to the default constructor will fail before the constructor is
  48. // entered. Be sure to check for NULL if you are allocating objects on the heap.
  49. //
  50. // **Of course if the clear text string is an empty string then the encrypted version will
  51. // also be empty (IsEmpty() will return true). This is a pretty easy check to make when
  52. // there would be any ambiguity.
  53. //
  54. //
  55. // NB:
  56. //
  57. // By default this class uses Crypt[Un]ProtectMemory() to handle encryption. These functions
  58. // are only available as of .NET Server. If your component might be installed on an OS
  59. // predating .NET Server you can '#define ENCRYPT_WITH_CRYPTPROTECTDATA' to get an
  60. // EncryptedString that will run on W2K and XP. Define the symbol before you include the
  61. // header file.
  62. //
  63. // CryptProtectMemory() is the more robust API in that it will succeed even if the user's
  64. // profile cannot be loaded. It is more suited to encrypting temporary buffers in memory.
  65. // Regardless of which encryption mechanism you enable, be sure to check return values to
  66. // see if encryption failed. If it fails, you basically have an empty string!
  67. //
  68. //
  69. // MFC code uses DDX_* functions to perform data binding b/w dialogs and underlying
  70. // data. Since none of these work with EncryptedString, we define our own. If you
  71. // need this, just '#define _DDX_ENCRYPTED'.
  72. //
  73. // There are also a pair of utility functions to get and set encrypted data in a
  74. // dialog window.
  75. //
  76. #ifdef _DDX_ENCRYPTED
  77. class EncryptedString;
  78. // Neither parameter is declared constant b/c (depending on direction of
  79. // data exchange) either one can be updated.
  80. inline HRESULT
  81. DDX_EncryptedText(CDataExchange* pDX, int nIDC, EncryptedString& buff);
  82. #include <windows.h>
  83. inline HRESULT
  84. GetEncryptedDlgItemText(HWND parentDialog, int itemResID, EncryptedString& cypher);
  85. inline HRESULT
  86. SetDlgItemText(HWND parentDialog, int itemResID, const EncryptedString& cypher);
  87. #endif //_DDX_ENCRYPTED
  88. class EncryptedString
  89. {
  90. public:
  91. // constructs an empty string.
  92. inline explicit
  93. EncryptedString(void);
  94. // constructs a copy of an existing, already encrypted string
  95. inline
  96. EncryptedString(const EncryptedString& rhs);
  97. // scribbles out the text, and deletes it.
  98. ~EncryptedString()
  99. {
  100. // when the object dies, it had better not have any outstanding
  101. // cleartext copies.
  102. ASSERT(m_cleartextCopyCount == 0);
  103. Reset();
  104. }
  105. // Scribbles out and de-allocates a clear text copy of the string. The
  106. // caller is required to balance each call to GetClearTextCopy with a call
  107. // to DestroyClearTextCopy lest he leak memory and leave cleartext hanging
  108. // around.
  109. //
  110. // cleartext - the same pointer returned by a previous call to
  111. // GetClearTextCopy on the same instance.
  112. inline void
  113. DestroyClearTextCopy(WCHAR* cleartext) const;
  114. // Extracts the decoded cleartext representation of the text, including
  115. // null terminator. The caller must invoke DestroyClearTextCopy on the
  116. // result, even if the result is null.
  117. //
  118. // May return null on error.
  119. //
  120. // Example:
  121. // WCHAR* cleartext = encrypted.GetClearTextCopy();
  122. // if (cleartext)
  123. // {
  124. // // ... use the cleartext
  125. // }
  126. // encrypted.DestroyClearTextCopy(cleartext);
  127. inline WCHAR*
  128. GetClearTextCopy() const;
  129. // Returns true if the string is zero-length, false if not.
  130. bool
  131. IsEmpty() const
  132. {
  133. return (GetLength() == 0);
  134. }
  135. // Sets the contents of self to the encrypted representation of the
  136. // cleartext, replacing the previous value of self. The encrypted
  137. // representation will be the same length, in characters, as the
  138. // cleartext.
  139. //
  140. // clearText - in, un-encrypted text to be encrypted. May be empty string,
  141. // but not a null pointer. The clearText must be null terminated.
  142. //
  143. // length - The character length of the clear text, not including null
  144. // termination. The count should be in unicode characters.
  145. //
  146. // Returns S_OK on successful encryption, error code otherwise. If low
  147. // memory causes failure the code will be E_OUTOFMEMORY.
  148. inline HRESULT
  149. Encrypt(const WCHAR* cleartext, size_t length);
  150. inline HRESULT
  151. Encrypt(const WCHAR* clearText);
  152. // Returns the length, in unicode characters, of the cleartext, not
  153. // including the null terminator.
  154. inline size_t
  155. GetLength() const;
  156. void
  157. Clear()
  158. {
  159. // when the object dies, it had better not have any outstanding
  160. // cleartext copies.
  161. ASSERT(m_cleartextCopyCount == 0);
  162. Reset();
  163. }
  164. // Replaces the contents of self with a copy of the contents of rhs.
  165. // Returns *this.
  166. inline const EncryptedString&
  167. operator= (const EncryptedString& rhs);
  168. // Compares the cleartext representations of self and rhs, and returns
  169. // true if they are lexicographically the same: the lengths are the same
  170. // and all the characters are the same.
  171. //
  172. // If the program runs out of memory in this method it will always return
  173. // false since we will be unable to determine if the strings are equal.
  174. inline bool
  175. operator== (const EncryptedString& rhs) const;
  176. bool
  177. operator!= (const EncryptedString& rhs) const
  178. {
  179. return !(operator==(rhs));
  180. }
  181. private:
  182. inline HRESULT
  183. InternalEncrypt(const WCHAR* clearText, size_t length);
  184. inline WCHAR*
  185. InternalDecrypt() const;
  186. inline void
  187. InternalDestroy(WCHAR* cleartext) const;
  188. inline void
  189. Reset();
  190. bool
  191. IsInNullBlobState() const;
  192. inline static DWORD CalculateBlobSize(DWORD dataSize);
  193. inline static HRESULT CopyBlob(DATA_BLOB& dest, const DATA_BLOB& source);
  194. inline static void DestroyBlob(DATA_BLOB& blob);
  195. inline static HRESULT EncryptData(const DATA_BLOB& clearText, DATA_BLOB& cypherText);
  196. inline static HRESULT DecryptData(const DATA_BLOB& cypherText, DATA_BLOB& clearText);
  197. // We deliberately do not implement conversion to or from WCHAR*.
  198. // This is to force the user of the class to be very deliberate
  199. // about decoding the string.
  200. // deliberately commented out
  201. //operator WCHAR* ();
  202. size_t m_clearTextLength;
  203. // In the course of en[de]crypting, and assigning to the instance, we
  204. // may tweak the members of cypherBlob, but logically the string is still
  205. // "const"
  206. mutable DATA_BLOB m_cypherBlob;
  207. // In debug versions there will be extra checking to make sure that
  208. // all of the clear text copies are zeroed and freed.
  209. #ifdef DBG
  210. // a logically const instance may have cleartext copies made
  211. mutable int m_cleartextCopyCount;
  212. #endif
  213. };
  214. ////////////////////////////////////////////////////
  215. // Implementation
  216. ////////////////////////////////////////////////////
  217. inline HRESULT
  218. GetLastErrorAsHresult(void)
  219. {
  220. DWORD err = GetLastError();
  221. return HRESULT_FROM_WIN32(err);
  222. }
  223. #ifdef _DDX_ENCRYPTED
  224. //
  225. // Get the text from a dialog into an encrypted string.
  226. //
  227. inline HRESULT
  228. GetEncryptedDlgItemText(HWND parentDialog, int itemResID, EncryptedString& cypher)
  229. {
  230. ASSERT(IsWindow(parentDialog));
  231. ASSERT(itemResID > 0);
  232. HRESULT err = S_OK;
  233. WCHAR* cleartext = 0;
  234. int length = 0;
  235. do
  236. {
  237. HWND item = GetDlgItem(parentDialog, itemResID);
  238. if (!item)
  239. {
  240. err = GetLastErrorAsHresult();
  241. break;
  242. }
  243. length = GetWindowTextLengthW(item);
  244. if (length < 0)
  245. {
  246. err = GetLastErrorAsHresult();
  247. break;
  248. }
  249. // add 1 to length for null-terminator
  250. ++length;
  251. cleartext = new WCHAR[length];
  252. if (NULL == cleartext)
  253. {
  254. err = E_OUTOFMEMORY;
  255. break;
  256. }
  257. ZeroMemory(cleartext, length * sizeof WCHAR);
  258. // length includes space for null terminator
  259. // which is correct for this call.
  260. int result = GetWindowText(item, cleartext, length);
  261. ASSERT(result == length - 1);
  262. if (result < 0)
  263. {
  264. err = GetLastErrorAsHresult();
  265. break;
  266. }
  267. err = cypher.Encrypt(cleartext);
  268. }
  269. while (0);
  270. // make sure we scribble out the cleartext.
  271. if (cleartext)
  272. {
  273. SecureZeroMemory(cleartext, length * sizeof WCHAR);
  274. delete[] cleartext;
  275. }
  276. return err;
  277. }
  278. //
  279. // Set the text in a dialog from an encrypted string.
  280. //
  281. inline HRESULT
  282. SetDlgItemText(
  283. HWND parentDialog,
  284. int itemResID,
  285. const EncryptedString& cypherText)
  286. {
  287. ASSERT(IsWindow(parentDialog));
  288. ASSERT(itemResID > 0);
  289. HRESULT hr = S_OK;
  290. WCHAR* cleartext = cypherText.GetClearTextCopy();
  291. if (NULL != cleartext)
  292. {
  293. BOOL succeeded = SetDlgItemText(parentDialog, itemResID, cleartext);
  294. if (!succeeded)
  295. {
  296. hr = GetLastErrorAsHresult();
  297. }
  298. }
  299. else
  300. {
  301. hr = E_OUTOFMEMORY;
  302. }
  303. cypherText.DestroyClearTextCopy(cleartext);
  304. // This shouldn't fail under any normal circumstances.
  305. ASSERT(SUCCEEDED(hr));
  306. return hr;
  307. }
  308. //
  309. // DDX_EncryptedText:
  310. //
  311. // Function performs data binding between a dialog text control and an
  312. // encrypted string.
  313. //
  314. // For information on DDX_* functions and how they work, see
  315. // "Technical Note 26" in MSDN documentation.
  316. //
  317. // History:
  318. // 2002-04-04 artm Created.
  319. //
  320. inline HRESULT DDX_EncryptedText(CDataExchange* pDX, int nIDC, EncryptedString& buff)
  321. {
  322. HRESULT err = S_OK;
  323. do { // false while loop
  324. // Make sure that we actually have a window with which to
  325. // exchange data.
  326. if (NULL == pDX ||
  327. NULL == pDX->m_pDlgWnd)
  328. {
  329. err = E_INVALIDARG;
  330. break;
  331. }
  332. HWND hWnd = pDX->m_pDlgWnd->GetSafeHwnd();
  333. if (NULL == hWnd)
  334. {
  335. err = E_INVALIDARG;
  336. break;
  337. }
  338. // Check which direction the data exchange is happening.
  339. if (pDX->m_bSaveAndValidate)
  340. {
  341. //
  342. // Save the text in the dialog to the encrypted buffer.
  343. //
  344. err = GetEncryptedDlgItemText(hWnd, nIDC, buff);
  345. }
  346. else
  347. {
  348. //
  349. // Refresh the text in the dialog from the encrypted buffer.
  350. //
  351. err = SetDlgItemText(hWnd, nIDC, buff);
  352. }
  353. } while(false);
  354. return err;
  355. }
  356. #endif //_DDX_ENCRYPTED
  357. inline
  358. EncryptedString::EncryptedString(void)
  359. :
  360. m_clearTextLength(0)
  361. #ifdef DBG
  362. ,m_cleartextCopyCount(0)
  363. #endif
  364. {
  365. ::ZeroMemory(&m_cypherBlob, sizeof m_cypherBlob);
  366. ASSERT(IsInNullBlobState());
  367. }
  368. //
  369. // CalculateBlobSize():
  370. //
  371. // Given the size of the data in bytes that must fit in the blob,
  372. // calculates how large the blob must be to hold that data and be
  373. // a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE.
  374. //
  375. // History:
  376. // 2002-06-08 artm Created (NTRAID#NTBUG9-635046)
  377. //
  378. inline DWORD
  379. EncryptedString::CalculateBlobSize(DWORD dataSize)
  380. {
  381. // Round the dataSize down to a multiple of the block size.
  382. DWORD blobSize =
  383. (dataSize / CRYPTPROTECTMEMORY_BLOCK_SIZE) * CRYPTPROTECTMEMORY_BLOCK_SIZE;
  384. // If blobSize cannot hold dataSize, increase blobSize by one memory block.
  385. if (blobSize < dataSize)
  386. {
  387. blobSize += CRYPTPROTECTMEMORY_BLOCK_SIZE;
  388. }
  389. ASSERT(blobSize >= dataSize);
  390. return blobSize;
  391. }
  392. //
  393. // CopyBlob():
  394. //
  395. // Copies source to destination (make sure that you've freed any
  396. // memory tied to destination before calling). If copy is
  397. // successful returns S_OK. If memory cannot be allocated then
  398. // returns E_OUTOFMEMORY and destination is set to an empty blob.
  399. //
  400. inline HRESULT
  401. EncryptedString::CopyBlob(DATA_BLOB& dest, const DATA_BLOB& source)
  402. {
  403. HRESULT err = S_OK;
  404. dest.cbData = source.cbData;
  405. dest.pbData = 0;
  406. if (dest.cbData)
  407. {
  408. // copy the blob data. We use LocalAlloc because that's what
  409. // CryptProtectData does, thus Reset() can use LocalFree no matter
  410. // how the blob was populated.
  411. HLOCAL local = ::LocalAlloc(LPTR, dest.cbData);
  412. if (local)
  413. {
  414. dest.pbData = (BYTE*) local;
  415. ::CopyMemory(
  416. dest.pbData,
  417. source.pbData,
  418. source.cbData);
  419. }
  420. else
  421. {
  422. // out of memory means you get an empty instance
  423. dest.cbData = 0;
  424. dest.pbData = 0;
  425. err = E_OUTOFMEMORY;
  426. }
  427. }
  428. return err;
  429. }
  430. inline void
  431. EncryptedString::DestroyBlob(DATA_BLOB& blob)
  432. {
  433. if (blob.cbData)
  434. {
  435. ASSERT(blob.pbData);
  436. if (blob.pbData)
  437. {
  438. // We want to zero out the blob in case it held
  439. // clear text data.
  440. SecureZeroMemory(blob.pbData, blob.cbData);
  441. ::LocalFree(blob.pbData);
  442. }
  443. blob.pbData = 0;
  444. blob.cbData = 0;
  445. }
  446. }
  447. inline
  448. EncryptedString::EncryptedString(const EncryptedString& rhs)
  449. :
  450. m_clearTextLength(rhs.m_clearTextLength)
  451. #ifdef DBG
  452. // although the rhs instance may have outstanding copies, we don't
  453. ,m_cleartextCopyCount(0)
  454. #endif
  455. {
  456. // Copy the blob, which is already encrypted.
  457. HRESULT hr = CopyBlob(m_cypherBlob, rhs.m_cypherBlob);
  458. // Can't return error from constructor, but maybe we can detect program
  459. // bugs during debug build.
  460. ASSERT(SUCCEEDED(hr));
  461. if (FAILED(hr))
  462. {
  463. Reset();
  464. }
  465. #ifdef DBG
  466. if (rhs.IsInNullBlobState())
  467. {
  468. ASSERT(IsInNullBlobState());
  469. }
  470. #endif
  471. }
  472. inline const EncryptedString&
  473. EncryptedString::operator= (const EncryptedString& rhs)
  474. {
  475. // don't reset the instance unless you have destroyed all the cleartext
  476. // copies. We assert this before checking for a=a, because the caller
  477. // still has a logic error even if the result is "harmless"
  478. ASSERT(m_cleartextCopyCount == 0);
  479. // handle the a = a case.
  480. if (this == &rhs)
  481. {
  482. return *this;
  483. }
  484. // dump our old contents
  485. Reset();
  486. HRESULT hr = CopyBlob(m_cypherBlob, rhs.m_cypherBlob);
  487. if (SUCCEEDED(hr))
  488. {
  489. m_clearTextLength = rhs.m_clearTextLength;
  490. }
  491. else
  492. {
  493. // Copy failed! That means we have an empty blob.
  494. // Reset() sets length to 0, so we don't need to
  495. // do it here.
  496. // Alert programmer in debug build. This function has
  497. // no error code, so nothing we can do in release build.
  498. ASSERT(false);
  499. }
  500. return *this;
  501. }
  502. // scribbles out and frees the internal string.
  503. inline void
  504. EncryptedString::Reset()
  505. {
  506. // don't reset the instance unless you have destroyed all the cleartext
  507. // copies.
  508. ASSERT(m_cleartextCopyCount == 0);
  509. DestroyBlob(m_cypherBlob);
  510. m_clearTextLength = 0;
  511. ASSERT(IsInNullBlobState());
  512. }
  513. // A word on state:
  514. //
  515. // an instance can be in one of two internal states: null blob and non-null
  516. // blob. These refer to whether or not the instance actually holds any
  517. // encrypted text.
  518. //
  519. // For the sake of the public interface, the null blob state is the same as
  520. // the non-null blob when the non-null blob holds an empty string. In other
  521. // words, an instance in the null blob state is an empty string.
  522. inline bool
  523. EncryptedString::IsInNullBlobState() const
  524. {
  525. if ( !m_cypherBlob.cbData
  526. && !m_cypherBlob.pbData)
  527. {
  528. ASSERT(m_clearTextLength == 0);
  529. return true;
  530. }
  531. return false;
  532. }
  533. // builds the internal encrypted representation from the cleartext.
  534. //
  535. // clearText - in, un-encrypted text to be encrypted. May be empty string, but
  536. // not a null pointer.
  537. //
  538. // length - The character length of the clear text, not including null
  539. // termination. The count should be in unicode characters.
  540. //
  541. // Returns S_OK on successful encryption, error code otherwise. If low
  542. // memory causes failure the code will be E_OUTOFMEMORY.
  543. //
  544. // Note: At this point we trust that length is the _correct_ length of
  545. // the string.
  546. inline HRESULT
  547. EncryptedString::InternalEncrypt(const WCHAR* clearText, size_t length)
  548. {
  549. ASSERT(clearText);
  550. // don't reset the instance unless you have destroyed all the cleartext
  551. // copies.
  552. ASSERT(m_cleartextCopyCount == 0);
  553. HRESULT err = S_OK;
  554. Reset();
  555. if (clearText)
  556. {
  557. DATA_BLOB dataIn;
  558. // Determine size of the data blob, including space for null.
  559. size_t blobSizeInBytes = (length + 1) * sizeof(WCHAR);
  560. dataIn.cbData = (DWORD)blobSizeInBytes;
  561. dataIn.pbData = (BYTE*)clearText;
  562. // Encrypt the clear text. We use a temporary blob so
  563. // that if the encryption fails, the old cypher blob is
  564. // left untouched.
  565. err = EncryptData(dataIn, m_cypherBlob);
  566. if (SUCCEEDED(err))
  567. {
  568. m_clearTextLength = length;
  569. }
  570. else
  571. {
  572. // if the encryption bombed, make this the an empty string. One
  573. // reason it can bomb is out-of-memory.
  574. Reset();
  575. }
  576. }
  577. ASSERT(m_cleartextCopyCount == 0);
  578. return err;
  579. }
  580. // decrypts the blob and returns a copy of the cleartext, but does not
  581. // bump up the outstanding copy counter. Result must be freed with
  582. // LocalFree. May return null (if not enough memory available).
  583. //
  584. // Used internally to prevent infinite mutual recursion
  585. inline WCHAR*
  586. EncryptedString::InternalDecrypt() const
  587. {
  588. HRESULT hr = S_OK;
  589. DATA_BLOB clearText;
  590. ::ZeroMemory(&clearText, sizeof(DATA_BLOB));
  591. WCHAR* copy = NULL;
  592. if (IsInNullBlobState())
  593. {
  594. // Return an empty string for the clear text since a null
  595. // blob state should be indistinguishable from an encrypted L"".
  596. // LocalAlloc() zeroes the memory for us (since we passed LPTR).
  597. copy = (WCHAR*) ::LocalAlloc(LPTR, sizeof(WCHAR));
  598. }
  599. else
  600. {
  601. hr = DecryptData(m_cypherBlob, clearText);
  602. if (SUCCEEDED(hr))
  603. {
  604. copy = (WCHAR*) clearText.pbData;
  605. }
  606. }
  607. #ifdef DBG
  608. if (clearText.pbData)
  609. {
  610. // An empty string should consist of at least a null terminator
  611. ASSERT(clearText.cbData >= sizeof WCHAR);
  612. // check for the null terminator
  613. ASSERT(
  614. ( *(clearText.pbData + clearText.cbData - 2) == 0)
  615. && ( *(clearText.pbData + clearText.cbData - 1) == 0) );
  616. // the decrypted version should be the same length as the encrypted
  617. // version
  618. #ifndef ENCRYPT_WITH_CRYPTPROTECTDATA
  619. // We need a different version of this check since the CryptProtectMemory()
  620. // API requires blobs whose sizes are a multiple of a block size.
  621. size_t decryptedLength = 0;
  622. hr = StringCchLength(
  623. reinterpret_cast<LPCWSTR>(clearText.pbData),
  624. m_clearTextLength + 1,
  625. &decryptedLength);
  626. ASSERT(SUCCEEDED(hr));
  627. ASSERT(decryptedLength == m_clearTextLength);
  628. #else
  629. ASSERT(clearText.cbData / sizeof WCHAR == m_clearTextLength + 1);
  630. #endif // ENCRYPT_WITH_CRYPTPROTECTDATA
  631. }
  632. #endif
  633. return copy;
  634. }
  635. inline void
  636. EncryptedString::InternalDestroy(WCHAR* cleartext) const
  637. {
  638. if (cleartext)
  639. {
  640. ::SecureZeroMemory(cleartext, m_clearTextLength * sizeof WCHAR);
  641. ::LocalFree(cleartext);
  642. }
  643. }
  644. inline WCHAR*
  645. EncryptedString::GetClearTextCopy() const
  646. {
  647. #ifdef DBG
  648. // Even if we fail the decryption, we bump the count so that it's easy for
  649. // the caller to always balance GetClearTextCopy with DestroyClearTextCopy
  650. ++m_cleartextCopyCount;
  651. #endif
  652. WCHAR* copy = NULL;
  653. copy = InternalDecrypt();
  654. return copy;
  655. }
  656. inline HRESULT
  657. EncryptedString::Encrypt(const WCHAR* clearText)
  658. {
  659. ASSERT(clearText);
  660. // don't reset the instance unless you have destroyed all the cleartext
  661. // copies.
  662. ASSERT(m_cleartextCopyCount == 0);
  663. HRESULT err = S_OK;
  664. if (NULL != clearText)
  665. {
  666. // We need to figure out the length of clearText.
  667. // Only option is to use an unbounded length function.
  668. size_t length = wcslen(clearText);
  669. err = InternalEncrypt(clearText, length);
  670. }
  671. else
  672. {
  673. err = E_INVALIDARG;
  674. }
  675. return err;
  676. }
  677. inline HRESULT
  678. EncryptedString::Encrypt(const WCHAR* clearText, size_t length)
  679. {
  680. ASSERT(clearText);
  681. // don't reset the instance unless you have destroyed all the cleartext
  682. // copies.
  683. ASSERT(m_cleartextCopyCount == 0);
  684. HRESULT err = S_OK;
  685. if (NULL != clearText)
  686. {
  687. // Even though the caller tells us the length of clearText,
  688. // we don't really trust them and want to make sure that:
  689. // a) The string really is null terminated, and
  690. // b) That the null termination happens exactly where they
  691. // say it does.
  692. size_t lengthCheck;
  693. err = StringCchLength(
  694. clearText,
  695. // Maximum length, including space for null
  696. (length + 1),
  697. &lengthCheck);
  698. ASSERT(lengthCheck == length);
  699. if (lengthCheck == length)
  700. {
  701. err = InternalEncrypt(clearText, length);
  702. }
  703. else
  704. {
  705. err = E_INVALIDARG;
  706. }
  707. }
  708. else
  709. {
  710. err = E_INVALIDARG;
  711. }
  712. return err;
  713. }
  714. inline bool
  715. EncryptedString::operator==(const EncryptedString& rhs) const
  716. {
  717. // handle the a == a case
  718. if (this == &rhs)
  719. {
  720. return true;
  721. }
  722. if (GetLength() != rhs.GetLength())
  723. {
  724. // can't be the same if lengths differ...
  725. return false;
  726. }
  727. // Two strings are the same if their decoded contents are the same.
  728. WCHAR* clearTextThis = GetClearTextCopy();
  729. WCHAR* clearTextThat = rhs.GetClearTextCopy();
  730. // If memory is scarce either clear text copy could be null.
  731. // In that case we cannot determine if the strings are equal we'll
  732. // assume that they are not and return false.
  733. bool result = false;
  734. if (clearTextThis && clearTextThat)
  735. {
  736. result = (wcscmp(clearTextThis, clearTextThat) == 0);
  737. }
  738. DestroyClearTextCopy(clearTextThis);
  739. rhs.DestroyClearTextCopy(clearTextThat);
  740. return result;
  741. }
  742. inline size_t
  743. EncryptedString::GetLength() const
  744. {
  745. #ifdef DBG
  746. // we don't use GetClearTextCopy, that may result in infinite recursion
  747. // since this function is called internally.
  748. WCHAR* clearTextThis = InternalDecrypt();
  749. size_t len = 0;
  750. if (clearTextThis)
  751. {
  752. HRESULT hr =
  753. StringCchLength(
  754. clearTextThis,
  755. // +1 for the null character
  756. (m_clearTextLength + 1),
  757. &len);
  758. if (FAILED(hr))
  759. {
  760. // we should be guaranteeing that the result of GetClearTextCopy
  761. // is always null-terminated, so a failure here represents a bug
  762. // in our implementation.
  763. ASSERT(false);
  764. len = 0;
  765. }
  766. }
  767. InternalDestroy(clearTextThis);
  768. ASSERT(len == m_clearTextLength);
  769. #endif
  770. return m_clearTextLength;
  771. }
  772. inline void
  773. EncryptedString::DestroyClearTextCopy(WCHAR* cleartext) const
  774. {
  775. // we expect that cleartext is usually non-null. It may not be, if
  776. // GetClearTextCopy failed, however.
  777. // ASSERT(cleartext);
  778. // we should have some outstanding copies. If not, then the caller has
  779. // called DestroyClearTextCopy more times than he called GetClearTextCopy,
  780. // and therefore has a bug.
  781. ASSERT(m_cleartextCopyCount);
  782. InternalDestroy(cleartext);
  783. #ifdef DBG
  784. --m_cleartextCopyCount;
  785. #endif
  786. }
  787. //
  788. // EncryptData:
  789. //
  790. // Encrypts data in clearText into cypherText using data protection
  791. // API. This function allocates memory to cypherText so make sure to
  792. // release any memory tied to cypherText before calling EncryptData().
  793. // Conversely, free the memory this function allocates by calling
  794. // LocalFree(cypherText).
  795. //
  796. // History:
  797. // 2002/04/01 artm Created.
  798. // 2002/06/08 artm Changed implementation to include CryptProtectMemory().
  799. // NTRAID#NTBUG9-635046
  800. //
  801. inline HRESULT
  802. EncryptedString::EncryptData(const DATA_BLOB& clearText, DATA_BLOB& cypherText)
  803. {
  804. ASSERT(clearText.cbData);
  805. ASSERT(clearText.pbData);
  806. ::ZeroMemory(&cypherText, sizeof cypherText);
  807. HRESULT hr = S_OK;
  808. BOOL succeeded = FALSE;
  809. // Tell compiler that false while loop is okay.
  810. #pragma warning( push )
  811. #pragma warning( disable : 4127 )
  812. do // false while loop
  813. {
  814. #ifndef ENCRYPT_WITH_CRYPTPROTECTDATA
  815. // Allocate a cypher blob big enough for data and that is
  816. // a multiple of the block size.
  817. cypherText.cbData = CalculateBlobSize(clearText.cbData);
  818. HLOCAL local = ::LocalAlloc(LPTR, cypherText.cbData);
  819. if (!local)
  820. {
  821. hr = E_OUTOFMEMORY;
  822. break;
  823. }
  824. // Copy the clear text to the cypher text buffer.
  825. cypherText.pbData = (BYTE*)local;
  826. ::CopyMemory(
  827. cypherText.pbData,
  828. clearText.pbData,
  829. clearText.cbData);
  830. // Encrypt the cypher blob.
  831. succeeded =
  832. ::CryptProtectMemory(
  833. cypherText.pbData,
  834. cypherText.cbData,
  835. CRYPTPROTECTMEMORY_SAME_PROCESS);
  836. #else
  837. succeeded =
  838. ::CryptProtectData(
  839. const_cast<DATA_BLOB*>(&clearText),
  840. 0,
  841. 0,
  842. 0,
  843. 0,
  844. CRYPTPROTECT_UI_FORBIDDEN,
  845. &cypherText);
  846. #endif
  847. if (!succeeded)
  848. {
  849. hr = GetLastErrorAsHresult();
  850. break;
  851. }
  852. } while(false);
  853. #pragma warning( pop )
  854. if (FAILED(hr))
  855. {
  856. // Ensure that any clear text copied to the cypher blob is
  857. // scratched out and then freed.
  858. DestroyBlob(cypherText);
  859. // should have a null result on failure, but just in case...
  860. ASSERT(!cypherText.cbData && !cypherText.pbData);
  861. }
  862. // we don't assert success, as the API may have run out of memory.
  863. return hr;
  864. }
  865. //
  866. // DecryptData:
  867. //
  868. // Decrypts cypherText into clearText using data protection API.
  869. // This function does not release any memory tied to clearText, so
  870. // check that no memory in clearText needs to be released before calling.
  871. // To release the memory allocated to clearText in this function call
  872. // LocalFree().
  873. //
  874. // History:
  875. // 2002/04/01 artm Created.
  876. // 2002/06/08 artm Changed implementation to include CryptUnprotectMemory().
  877. // NTRAID#NTBUG9-635046
  878. //
  879. inline HRESULT
  880. EncryptedString::DecryptData(const DATA_BLOB& cypherText, DATA_BLOB& clearText)
  881. {
  882. ASSERT(cypherText.cbData);
  883. ASSERT(cypherText.pbData);
  884. ::ZeroMemory(&clearText, sizeof clearText);
  885. HRESULT hr = S_OK;
  886. BOOL succeeded = FALSE;
  887. // Tell compiler that false while loop is okay.
  888. #pragma warning( push )
  889. #pragma warning( disable : 4127 )
  890. do // false while loop
  891. {
  892. #ifndef ENCRYPT_WITH_CRYPTPROTECTDATA
  893. // Our cypher blob should always be a multiple of the block size;
  894. // if it isn't then it is a bug in the implementation.
  895. ASSERT( (cypherText.cbData % CRYPTPROTECTMEMORY_BLOCK_SIZE) == 0);
  896. hr = CopyBlob(clearText, cypherText);
  897. if (FAILED(hr))
  898. {
  899. break;
  900. }
  901. succeeded =
  902. ::CryptUnprotectMemory(
  903. clearText.pbData,
  904. clearText.cbData,
  905. CRYPTPROTECTMEMORY_SAME_PROCESS);
  906. #else
  907. succeeded =
  908. ::CryptUnprotectData(
  909. const_cast<DATA_BLOB*>(&cypherText),
  910. 0,
  911. 0,
  912. 0,
  913. 0,
  914. CRYPTPROTECT_UI_FORBIDDEN,
  915. &clearText);
  916. #endif
  917. if (!succeeded)
  918. {
  919. hr = GetLastErrorAsHresult();
  920. break;
  921. }
  922. } while(false);
  923. #pragma warning( pop )
  924. if (FAILED(hr))
  925. {
  926. // Scratch out and free anything we put in clearText.
  927. DestroyBlob(clearText);
  928. // should have a null result on failure, but just in case...
  929. ASSERT(!clearText.cbData && !clearText.pbData);
  930. }
  931. // we don't assert success, as the API may have run out of memory.
  932. return hr;
  933. }
  934. #endif // _PUBLIC_ENCRYPTEDSTRING_