Skip to content

Commit be8c356

Browse files
committed
feat: add secret Provider to windows, WIP
1 parent 7370bb0 commit be8c356

File tree

2 files changed

+172
-16
lines changed

2 files changed

+172
-16
lines changed

src/lobby/credentials/secret.cpp

Lines changed: 166 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,15 @@ secret::SecretStorage::SecretStorage(KeyringType type) : m_type{ type } {
5656
// 1 stands for, create if not exists, as a bool
5757
this->m_ring_id = keyctl_get_keyring_ID(key_type, 1);
5858

59-
if (m_ring_id < 0) {
59+
if (this->m_ring_id < 0) {
6060
throw std::runtime_error(fmt::format("Error while getting the requested keyring: {}", strerror(errno)));
6161
}
6262
}
6363

64+
secret::SecretStorage::~SecretStorage() = default;
6465

6566
[[nodiscard]] helper::expected<std::string, std::string> secret::SecretStorage::load(const std::string& key) const {
6667

67-
if (m_ring_id < 0) {
68-
return helper::unexpected<std::string>{ "Error while loading a key, ring_id is invalid" };
69-
}
70-
7168
auto key_id = get_id_from_name(m_ring_id, key);
7269

7370
if (key_id < 0) {
@@ -87,18 +84,14 @@ secret::SecretStorage::SecretStorage(KeyringType type) : m_type{ type } {
8784
};
8885
}
8986

90-
auto result_string = std::string{ static_cast<char*>(buffer) };
87+
auto result_string = std::string{ static_cast<char*>(buffer), static_cast<char*>(buffer) + result };
9188
free(buffer);
9289
return result_string;
9390
}
9491

9592
[[nodiscard]] std::optional<std::string>
9693
secret::SecretStorage::store(const std::string& key, const std::string& value, bool update) const {
9794

98-
if (m_ring_id < 0) {
99-
return "Error while storing a key, ring_id is invalid";
100-
}
101-
10295
auto key_id = get_id_from_name(m_ring_id, key);
10396

10497

@@ -127,10 +120,6 @@ secret::SecretStorage::store(const std::string& key, const std::string& value, b
127120

128121
[[nodiscard]] std::optional<std::string> secret::SecretStorage::remove(const std::string& key) const {
129122

130-
if (m_ring_id < 0) {
131-
return fmt::format("Error while remove a key, ring_id is invalid");
132-
}
133-
134123
auto key_id = get_id_from_name(m_ring_id, key);
135124

136125
if (key_id < 0) {
@@ -150,22 +139,181 @@ secret::SecretStorage::store(const std::string& key, const std::string& value, b
150139
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
151140

152141

153-
//TODO: use https://learn.microsoft.com/en-us/windows/win32/seccng/cng-key-storage-functions
142+
namespace {
143+
144+
namespace constants {
145+
constexpr const char* property_name = "OOPetris Payload";
146+
147+
constexpr const char* key_name_prefix = "OOPetris_key__";
148+
149+
constexpr const char* used_algorithm = BCRYPT_AES_ALGORITHM;
150+
} // namespace constants
151+
152+
std::string get_key_name(const std::string& key) {
153+
return constants::key_name_prefix + key;
154+
}
155+
156+
helper::expected<NCRYPT_KEY_HANDLE, std::string>
157+
get_handle_from_name(KeyringType type, NCRYPT_PROV_HANDLE phProvider, const std::string& key) {
158+
159+
160+
NCRYPT_KEY_HANDLE key_handle{};
161+
162+
auto key_name = get_key_name(key);
163+
164+
// the key is of type user, if not specified otherwise, session mode is not supported
165+
DWORD flags = (type == KeyringType::Persistent ? NCRYPT_MACHINE_KEY_FLAG : 0);
154166

155-
secret::SecretStorage::SecretStorage(KeyringType type) : m_type{ type } { }
167+
// 0 means no dwLegacyKeySpec
168+
auto result = NCryptOpenKey(phProvider, &key_handle, key_name.c_str(), 0, flags);
169+
170+
171+
if (result != ERROR_SUCCESS) {
172+
return helper::unexpected<std::string>{ fmt::format("Error while opening a key: {}", result) };
173+
}
174+
175+
return key_handle;
176+
}
177+
178+
} // namespace
179+
180+
181+
secret::SecretStorage::SecretStorage(KeyringType type) : m_type{ type }, m_phProvider{} {
182+
183+
if (type == KeyringType::Session) {
184+
spdlog::warning("KeyringType Session is not supported, using KeyringType User");
185+
m_type = KeyringType::User;
186+
}
187+
188+
// there are no flags available, so using 0
189+
auto result = NCryptOpenStorageProvider(&this->m_phProvider, MS_KEY_STORAGE_PROVIDER, 0);
190+
191+
if (result != ERROR_SUCCESS) {
192+
throw std::runtime_error(fmt::format("Error while getting a storage provider handle: {}", result));
193+
}
194+
}
195+
196+
secret::SecretStorage::~SecretStorage() {
197+
// ignore return code, as it only indicates, if we passed a valid or invalid handle
198+
NCryptFreeObject(m_phProvider);
199+
}
156200

157201

158202
[[nodiscard]] helper::expected<std::string, std::string> secret::SecretStorage::load(const std::string& key) const {
203+
204+
auto maybe_key_handle = get_handle_from_name(m_type, m_phProvider, key);
205+
206+
if (not maybe_key_handle.has_value()) {
207+
return return helper::unexpected<std::string>{ maybe_key_handle.error() };
208+
}
209+
210+
auto key_handle = maybe_key_handle.value();
211+
212+
DWORD bytes_needed{};
213+
214+
// no flags needed, so using 0
215+
auto result = NCryptGetProperty(key_handle, constants::property_name, nullptr, 0, &bytes_needed, 0);
216+
217+
if (result != ERROR_SUCCESS) {
218+
NCryptFreeObject(key_handle);
219+
return helper::unexpected<std::string>{
220+
fmt::format("Error while loading a key, getting the property size failed: {}", result)
221+
};
222+
}
223+
224+
char* buffer = new char[bytes_needed];
225+
226+
DOWRD bytes_written{};
227+
228+
auto result = NCryptGetProperty(key_handle, constants::property_name, &buffer, bytes_needed, &bytes_written, 0);
229+
230+
if (result != ERROR_SUCCESS) {
231+
NCryptFreeObject(key_handle);
232+
delete[] buffer;
233+
return helper::unexpected<std::string>{
234+
fmt::format("Error while loading a key, getting the property failed: {}", result)
235+
};
236+
}
237+
238+
if (bytes_written != bytes_needed) {
239+
NCryptFreeObject(key_handle);
240+
delete[] buffer;
241+
return helper::unexpected<std::string>{
242+
fmt::format("Error while loading a key, getting the property failed: mismatching sizes reported")
243+
};
244+
}
245+
246+
NCryptFreeObject(key_handle);
247+
248+
auto result_string = std::string{ static_cast<char*>(buffer), static_cast<char*>(buffer) + bytes_needed };
249+
250+
delete[] buffer;
251+
159252
return result_string;
160253
}
161254

162255
[[nodiscard]] std::optional<std::string>
163256
secret::SecretStorage::store(const std::string& key, const std::string& value, bool update) const {
257+
258+
NCRYPT_KEY_HANDLE key_handle{};
259+
260+
auto key_name = get_key_name(key);
261+
262+
// the key is of type user, if not specified otherwise, session mode is not supported, also prefer VBS, but not require it, take the update flag also in consideration
263+
DWORD flags = (m_type == KeyringType::Persistent ? NCRYPT_MACHINE_KEY_FLAG : 0)
264+
| (update ? NCRYPT_OVERWRITE_KEY_FLAG : 0) | NCRYPT_PREFER_VBS_FLAG;
265+
266+
// 0 means no dwLegacyKeySpec
267+
auto result = NCryptCreatePersistedKey(
268+
this->m_phProvider, &key_handle, constants::used_algorithm, key_name.c_str(), 0, flags
269+
);
270+
271+
if (result != ERROR_SUCCESS) {
272+
return fmt::format("Error while storing a key, creating a key failed: {}", result);
273+
}
274+
275+
DWORD flags2 = m_type == KeyringType::Persistent ? NCRYPT_PERSIST_FLAG : 0;
276+
277+
auto result2 = NCryptSetProperty(key_handle, constants::property_name, value.c_str(), value.size(), flags2);
278+
279+
if (result2 != ERROR_SUCCESS) {
280+
NCryptFreeObject(key_handle);
281+
return fmt::format("Error while storing a key, setting the key data failed: {}", result);
282+
}
283+
284+
// no specific flags are needed, so using 0
285+
auto result3 = NCryptFinalizeKey(key_handle, 0);
286+
287+
288+
if (result3 != ERROR_SUCCESS) {
289+
NCryptFreeObject(key_handle);
290+
return fmt::format("Error while storing a key, finalizing the key failed: {}", result);
291+
}
292+
293+
// ignoring return value
294+
NCryptFreeObject(key_handle);
164295
return std::nullopt;
165296
}
166297

167298

168299
[[nodiscard]] std::optional<std::string> secret::SecretStorage::remove(const std::string& key) const {
300+
301+
auto maybe_key_handle = get_handle_from_name(m_type, m_phProvider, key);
302+
303+
if (not maybe_key_handle.has_value()) {
304+
return maybe_key_handle.error();
305+
}
306+
307+
auto key_handle = maybe_key_handle.value();
308+
309+
// no flags needed, so using 0
310+
auto result = NCryptDeleteKey(key_handle, 0);
311+
312+
if (result != ERROR_SUCCESS) {
313+
NCryptFreeObject(key_handle);
314+
return fmt::format("Error while removing a key, deleting the key failed: {}", result);
315+
}
316+
169317
return std::nullopt;
170318
}
171319

@@ -200,6 +348,8 @@ secret::SecretStorage::SecretStorage(KeyringType type) : m_type{ type } {
200348
m_file_path = utils::get_root_folder() / secrets_constants::store_file_name;
201349
}
202350

351+
secret::SecretStorage::~SecretStorage() = default;
352+
203353

204354
[[nodiscard]] helper::expected<std::string, std::string> secret::SecretStorage::load(const std::string& key) const {
205355

src/lobby/credentials/secret.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#if defined(__linux__)
1111

1212
#include <keyutils.h>
13+
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
14+
#include <ncrypt.h>
1315
#endif
1416

1517
namespace secret {
@@ -24,12 +26,16 @@ namespace secret {
2426
key_serial_t m_ring_id;
2527
#elif defined(__CONSOLE__) || defined(__APPLE__)
2628
std::string m_file_path;
29+
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
30+
NCRYPT_PROV_HANDLE m_phProvider;
2731
#endif
2832

2933

3034
public:
3135
explicit SecretStorage(KeyringType type);
3236

37+
~SecretStorage();
38+
3339
[[nodiscard]] helper::expected<std::string, std::string> load(const std::string& key) const;
3440

3541
[[nodiscard]] std::optional<std::string>

0 commit comments

Comments
 (0)