chore: auto-commit (3 archivos)
- cpp/functions/infra/secret_store.cpp - cpp/functions/infra/secret_store.h - cpp/functions/infra/secret_store.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
// secret_store.cpp — implementation of fn_secret (issue 0129).
|
||||
//
|
||||
// See secret_store.h for API docs and platform notes.
|
||||
|
||||
#include "infra/secret_store.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
# include <wincrypt.h>
|
||||
# pragma comment(lib, "crypt32.lib")
|
||||
#endif
|
||||
|
||||
namespace fn_secret {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Base64 helpers (no external deps, RFC 4648 alphabet)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static const char kB64Chars[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
static std::string base64_encode(const uint8_t* data, size_t len) {
|
||||
std::string out;
|
||||
out.reserve(((len + 2) / 3) * 4);
|
||||
for (size_t i = 0; i < len; i += 3) {
|
||||
uint32_t b = (uint32_t)data[i] << 16;
|
||||
if (i + 1 < len) b |= (uint32_t)data[i + 1] << 8;
|
||||
if (i + 2 < len) b |= (uint32_t)data[i + 2];
|
||||
out += kB64Chars[(b >> 18) & 63];
|
||||
out += kB64Chars[(b >> 12) & 63];
|
||||
out += (i + 1 < len) ? kB64Chars[(b >> 6) & 63] : '=';
|
||||
out += (i + 2 < len) ? kB64Chars[(b) & 63] : '=';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> base64_decode(const std::string& s) {
|
||||
auto decode_char = [](char c) -> int {
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
||||
if (c >= '0' && c <= '9') return c - '0' + 52;
|
||||
if (c == '+') return 62;
|
||||
if (c == '/') return 63;
|
||||
return -1;
|
||||
};
|
||||
std::vector<uint8_t> out;
|
||||
out.reserve(s.size() * 3 / 4);
|
||||
for (size_t i = 0; i + 3 < s.size(); i += 4) {
|
||||
int a = decode_char(s[i]);
|
||||
int b = decode_char(s[i + 1]);
|
||||
int c = decode_char(s[i + 2]);
|
||||
int d = decode_char(s[i + 3]);
|
||||
if (a < 0 || b < 0) break;
|
||||
out.push_back((uint8_t)((a << 2) | (b >> 4)));
|
||||
if (c >= 0) out.push_back((uint8_t)((b << 4) | (c >> 2)));
|
||||
if (d >= 0) out.push_back((uint8_t)((c << 2) | d));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Linux fallback: XOR with a stable per-user key
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#ifndef _WIN32
|
||||
static std::vector<uint8_t> linux_key() {
|
||||
// Key = first 32 bytes of SHA-256-like mixing of LOGNAME + HOSTNAME.
|
||||
// Good enough to prevent casual plaintext inspection; NOT crypto-secure.
|
||||
const char* user = getenv("LOGNAME");
|
||||
const char* host = getenv("HOSTNAME");
|
||||
if (!user) user = "user";
|
||||
if (!host) host = "localhost";
|
||||
std::string seed = std::string(user) + "@" + host + ":fn_agents_dashboard_key_v1";
|
||||
std::vector<uint8_t> key(32, 0);
|
||||
for (size_t i = 0; i < seed.size(); i++) {
|
||||
key[i % 32] ^= (uint8_t)seed[i];
|
||||
key[(i + 7) % 32] += (uint8_t)(seed[i] * 31 + i);
|
||||
key[(i + 13) % 32] ^= (uint8_t)(seed[i] + i * 7);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool is_strong() {
|
||||
#ifdef _WIN32
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encrypt(const std::string& plaintext) {
|
||||
#ifdef _WIN32
|
||||
DATA_BLOB in_blob { (DWORD)plaintext.size(),
|
||||
(BYTE*)const_cast<char*>(plaintext.data()) };
|
||||
DATA_BLOB out_blob {};
|
||||
if (!CryptProtectData(&in_blob, L"fn_agents_dashboard", nullptr,
|
||||
nullptr, nullptr, 0, &out_blob)) {
|
||||
return {};
|
||||
}
|
||||
std::vector<uint8_t> result(out_blob.pbData,
|
||||
out_blob.pbData + out_blob.cbData);
|
||||
LocalFree(out_blob.pbData);
|
||||
return result;
|
||||
#else
|
||||
// Linux: 1-byte magic + XOR
|
||||
std::vector<uint8_t> key = linux_key();
|
||||
std::vector<uint8_t> out;
|
||||
out.reserve(1 + plaintext.size());
|
||||
out.push_back(0xAF); // magic marker
|
||||
for (size_t i = 0; i < plaintext.size(); i++) {
|
||||
out.push_back((uint8_t)plaintext[i] ^ key[i % key.size()]);
|
||||
}
|
||||
return out;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string decrypt(const std::vector<uint8_t>& blob) {
|
||||
if (blob.empty()) return {};
|
||||
#ifdef _WIN32
|
||||
DATA_BLOB in_blob { (DWORD)blob.size(),
|
||||
(BYTE*)const_cast<uint8_t*>(blob.data()) };
|
||||
DATA_BLOB out_blob {};
|
||||
if (!CryptUnprotectData(&in_blob, nullptr, nullptr,
|
||||
nullptr, nullptr, 0, &out_blob)) {
|
||||
return {};
|
||||
}
|
||||
std::string result(reinterpret_cast<char*>(out_blob.pbData),
|
||||
out_blob.cbData);
|
||||
LocalFree(out_blob.pbData);
|
||||
return result;
|
||||
#else
|
||||
// Linux: check magic, XOR decode
|
||||
if (blob[0] != 0xAF) return {};
|
||||
std::vector<uint8_t> key = linux_key();
|
||||
std::string out;
|
||||
out.reserve(blob.size() - 1);
|
||||
for (size_t i = 1; i < blob.size(); i++) {
|
||||
out += (char)(blob[i] ^ key[(i - 1) % key.size()]);
|
||||
}
|
||||
return out;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string encrypt_b64(const std::string& plaintext) {
|
||||
auto blob = encrypt(plaintext);
|
||||
if (blob.empty()) return {};
|
||||
return base64_encode(blob.data(), blob.size());
|
||||
}
|
||||
|
||||
std::string decrypt_b64(const std::string& b64) {
|
||||
auto blob = base64_decode(b64);
|
||||
return decrypt(blob);
|
||||
}
|
||||
|
||||
} // namespace fn_secret
|
||||
Reference in New Issue
Block a user