Skip to content

Commit eadff24

Browse files
authored
Harden winrt_encryption slightly and avoid a potential reallocation. (#896)
1 parent b211145 commit eadff24

File tree

2 files changed

+36
-27
lines changed

2 files changed

+36
-27
lines changed

Release/src/json/json_parsing.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,7 @@ std::unique_ptr<web::json::details::_Value> JSON_Parser<CharType>::_ParseObject(
10261026
::std::sort(elems.begin(), elems.end(), json::object::compare_pairs);
10271027
}
10281028

1029-
return std::move(obj);
1029+
return obj;
10301030

10311031
error:
10321032
if (!tkn.m_error)

Release/src/utilities/web_utilities.cpp

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
****/
1313

1414
#include "stdafx.h"
15+
#include <assert.h>
1516

1617
#if defined(_WIN32) && !defined(__cplusplus_winrt)
1718
#include <Wincrypt.h>
@@ -92,23 +93,28 @@ plaintext_string winrt_encryption::decrypt() const
9293
win32_encryption::win32_encryption(const std::wstring &data) :
9394
m_numCharacters(data.size())
9495
{
95-
// Early return because CryptProtectMemory crashs with empty string
96+
// Early return because CryptProtectMemory crashes with empty string
9697
if (m_numCharacters == 0)
9798
{
9899
return;
99100
}
100101

101-
const auto dataNumBytes = data.size() * sizeof(std::wstring::value_type);
102-
m_buffer.resize(dataNumBytes);
103-
memcpy_s(m_buffer.data(), m_buffer.size(), data.c_str(), dataNumBytes);
104-
105-
// Buffer must be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
106-
const auto mod = m_buffer.size() % CRYPTPROTECTMEMORY_BLOCK_SIZE;
107-
if (mod != 0)
102+
if (data.size() > (std::numeric_limits<DWORD>::max)() / sizeof(wchar_t))
108103
{
109-
m_buffer.resize(m_buffer.size() + CRYPTPROTECTMEMORY_BLOCK_SIZE - mod);
104+
throw std::length_error("Encryption string too long");
110105
}
111-
if (!CryptProtectMemory(m_buffer.data(), static_cast<DWORD>(m_buffer.size()), CRYPTPROTECTMEMORY_SAME_PROCESS))
106+
107+
const auto dataSizeDword = static_cast<DWORD>(data.size() * sizeof(wchar_t));
108+
109+
// Round up dataSizeDword to be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
110+
static_assert(CRYPTPROTECTMEMORY_BLOCK_SIZE == 16, "Power of 2 assumptions in this bit masking violated");
111+
const auto mask = static_cast<DWORD>(CRYPTPROTECTMEMORY_BLOCK_SIZE - 1u);
112+
const auto dataNumBytes = (dataSizeDword & ~mask) + ((dataSizeDword & mask) != 0) * CRYPTPROTECTMEMORY_BLOCK_SIZE;
113+
assert((dataNumBytes % CRYPTPROTECTMEMORY_BLOCK_SIZE) == 0);
114+
assert(dataNumBytes >= dataSizeDword);
115+
m_buffer.resize(dataNumBytes);
116+
memcpy_s(m_buffer.data(), m_buffer.size(), data.c_str(), dataNumBytes);
117+
if (!CryptProtectMemory(m_buffer.data(), dataNumBytes, CRYPTPROTECTMEMORY_SAME_PROCESS))
112118
{
113119
throw ::utility::details::create_system_error(GetLastError());
114120
}
@@ -121,20 +127,25 @@ win32_encryption::~win32_encryption()
121127

122128
plaintext_string win32_encryption::decrypt() const
123129
{
124-
if (m_buffer.empty())
125-
return plaintext_string(new std::wstring());
126-
127130
// Copy the buffer and decrypt to avoid having to re-encrypt.
128-
auto data = plaintext_string(new std::wstring(reinterpret_cast<const std::wstring::value_type *>(m_buffer.data()), m_buffer.size() / 2));
129-
if (!CryptUnprotectMemory(
130-
const_cast<std::wstring::value_type *>(data->c_str()),
131-
static_cast<DWORD>(m_buffer.size()),
132-
CRYPTPROTECTMEMORY_SAME_PROCESS))
133-
{
134-
throw ::utility::details::create_system_error(GetLastError());
131+
auto result = plaintext_string(new std::wstring(
132+
reinterpret_cast<const std::wstring::value_type *>(m_buffer.data()), m_buffer.size() / sizeof(wchar_t)));
133+
auto& data = *result;
134+
if (!m_buffer.empty()) {
135+
if (!CryptUnprotectMemory(
136+
&data[0],
137+
static_cast<DWORD>(m_buffer.size()),
138+
CRYPTPROTECTMEMORY_SAME_PROCESS))
139+
{
140+
throw ::utility::details::create_system_error(GetLastError());
141+
}
142+
143+
assert(m_numCharacters <= m_buffer.size());
144+
SecureZeroMemory(&data[m_numCharacters], data.size() - m_numCharacters);
145+
data.erase(m_numCharacters);
135146
}
136-
data->resize(m_numCharacters);
137-
return std::move(data);
147+
148+
return result;
138149
}
139150
#endif
140151
#endif
@@ -143,12 +154,10 @@ void zero_memory_deleter::operator()(::utility::string_t *data) const
143154
{
144155
CASABLANCA_UNREFERENCED_PARAMETER(data);
145156
#if defined(_WIN32)
146-
SecureZeroMemory(
147-
const_cast<::utility::string_t::value_type *>(data->data()),
148-
data->size() * sizeof(::utility::string_t::value_type));
157+
SecureZeroMemory(&(*data)[0], data->size() * sizeof(::utility::string_t::value_type));
149158
delete data;
150159
#endif
151160
}
152161
}
153162

154-
}
163+
}

0 commit comments

Comments
 (0)