@@ -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>
9693secret::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>
163256secret::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
0 commit comments