2025-11-28 00:35:46 +09:00

219 lines
6.5 KiB
C++

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Security.DataProtection.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
#include <iostream>
namespace winrt
{
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Security::DataProtection;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Storage::Streams;
}
void usage()
{
std::wcout << LR"(PersonalDataEncryption sample usage.
PersonalDataEncryption.exe file <level> <path>
Protect a file to a protection level.
Path must be a fully-qualified path.
PersonalDataEncryption.exe folder <level> <path>
Protect a folder to a protection level.
Path must be a fully-qualified path.
PersonalDataEncryption.exe memory <level> <string>
Protect a memory buffer to a protection level.
For both options, the following protection levels are supported:
Level 0: No protection (always available).
Level 1: Available after first unlock.
Level 2: Avaialble when unlocked.
Examples:
Protect a file to level 1:
PersonalDataEncryption.exe file 1 C:\Users\Abby\Documents\file.txt
Remove protection from a folder:
PersonalDataEncryption.exe folder 1 C:\Users\Abby\Documents\Party
Protect a string to level 2:
PersonalDataEncryption.exe memory 2 "hello, world!"
)";
}
void ProtectItem(winrt::UserDataProtectionManager const& manager, winrt::UserDataAvailability availability, winrt::IStorageItem const& item)
{
auto status = manager.ProtectStorageItemAsync(item, availability).get();
wchar_t const* message;
switch (status)
{
case winrt::UserDataStorageItemProtectionStatus::Succeeded:
message = L"Succeeded";
break;
case winrt::UserDataStorageItemProtectionStatus::NotProtectable:
message = L"Not protectable";
break;
case winrt::UserDataStorageItemProtectionStatus::DataUnavailable:
message = L"Data unavailable";
break;
default:
message = L"Unknown failure";
break;
}
std::wcout <<
L"Protecting " << std::wstring_view(item.Path()) <<
L" to level " << static_cast<int>(availability) <<
L": " << message << L"\n";
}
void ProtectFile(winrt::UserDataProtectionManager const& manager, winrt::hstring const& path, winrt::UserDataAvailability availability)
{
winrt::IStorageItem item;
try
{
item = winrt::StorageFile::GetFileFromPathAsync(path).get();
}
catch (...)
{
std::wcout << L"Error getting StorageFile: " << std::wstring_view(winrt::to_message()) << L"\n";
}
ProtectItem(manager, availability, item);
}
// Note that setting the availability of a folder establishes the availability for any new items
// created in that folder, but it does not affect the availability of existing items in the folder.
// If you want to change the availability of an entire folder tree, you should first call
// ProtectStorageItemAsync to protect the folder (so that any new items are suitably protected),
// and then recursively protect all the files and subfolders inside that folder.
void ProtectFolder(winrt::UserDataProtectionManager const& manager, winrt::hstring const& path, winrt::UserDataAvailability availability)
{
winrt::IStorageItem item;
try
{
item = winrt::StorageFolder::GetFolderFromPathAsync(path).get();
}
catch (...)
{
std::wcout << L"Error getting StorageFile: " << std::wstring_view(winrt::to_message()) << L"\n";
}
ProtectItem(manager, availability, item);
}
void ProtectMemory(winrt::UserDataProtectionManager const& manager, winrt::hstring const& message, winrt::UserDataAvailability availability)
{
// Convert the string to a Buffer.
auto bufferSize = message.size() * static_cast<uint32_t>(sizeof(wchar_t));
auto buffer = winrt::Buffer(bufferSize);
memcpy_s(buffer.data(), bufferSize, message.c_str(), bufferSize);
buffer.Length(bufferSize);
// Protect the buffer.
auto protectedBuffer = manager.ProtectBufferAsync(buffer, availability).get();
// Zero out the original buffer to ensure it holds no data.
memset(buffer.data(), 0, bufferSize);
// Free the original buffer and retain the protected buffer.
buffer = nullptr;
std::wcout << L"Memory buffer has been protected.\n"
<< L"Press Enter to unprotect it.\n";
std::cin.get();
// Now unprotect the buffer to recover the string.
auto result = manager.UnprotectBufferAsync(protectedBuffer).get();
winrt::UserDataBufferUnprotectStatus status = result.Status();
winrt::IBuffer unprotectedBuffer = result.UnprotectedBuffer();
switch (status)
{
case winrt::UserDataBufferUnprotectStatus::Succeeded:
// Extract the string from the unprotected buffer.
std::wcout << L"Original message: "
<< std::wstring_view(reinterpret_cast<wchar_t*>(unprotectedBuffer.data()), unprotectedBuffer.Length() / sizeof(wchar_t))
<< L"\n";
break;
case winrt::UserDataBufferUnprotectStatus::Unavailable:
std::wcout << L"Buffer cannot be unprotected at this time.\n";
break;
default:
std::wcout << L"Unexpected error when unprotecting buffer.\n";
break;
}
}
void Run(int argc, wchar_t** argv)
{
if (argc != 4)
{
usage();
return;
}
wchar_t* end;
auto availability = static_cast<winrt::UserDataAvailability>(wcstoul(argv[2], &end, 10));
if (*end != L'\0')
{
// Not an integer.
usage();
return;
}
if (availability != winrt::UserDataAvailability::Always &&
availability != winrt::UserDataAvailability::AfterFirstUnlock &&
availability != winrt::UserDataAvailability::WhileUnlocked)
{
// Not a valid integer.
usage();
return;
}
winrt::UserDataProtectionManager manager = winrt::UserDataProtectionManager::TryGetDefault();
if (!manager)
{
std::wcout << L"Personal Data Encryption is not enabled.\n";
return;
}
if (wcscmp(argv[1], L"file") == 0)
{
ProtectFile(manager, argv[3], availability);
}
else if (wcscmp(argv[1], L"folder") == 0)
{
ProtectFolder(manager, argv[3], availability);
}
else if (wcscmp(argv[1], L"memory") == 0)
{
ProtectMemory(manager, argv[3], availability);
}
else
{
usage();
}
}
int wmain(int argc, wchar_t** argv)
{
winrt::init_apartment();
Run(argc, argv);
winrt::uninit_apartment();
return 0;
}