Files
fn_registry/cpp/functions/infra/secret_store.cpp
T
egutierrez 90115270d2 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>
2026-05-22 21:52:37 +02:00

168 lines
5.3 KiB
C++

// 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