changed bash scripts key/cert generation to openssl lib
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,4 +8,5 @@
|
|||||||
/toolchain/sysroot/
|
/toolchain/sysroot/
|
||||||
/toolchain/build/
|
/toolchain/build/
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
check/
|
||||||
@@ -37,6 +37,7 @@ set( HEADERS
|
|||||||
src/Services/DeviceControlService.h
|
src/Services/DeviceControlService.h
|
||||||
src/Services/EnrollmentService.h
|
src/Services/EnrollmentService.h
|
||||||
src/Security/TlsKeyUtil.h
|
src/Security/TlsKeyUtil.h
|
||||||
|
src/Security/SslCertUtil.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable( ${PROJECT_NAME} ${SOURCES} ${HEADERS} )
|
add_executable( ${PROJECT_NAME} ${SOURCES} ${HEADERS} )
|
||||||
|
|||||||
512
src/Security/SslCertUtil.h
Normal file
512
src/Security/SslCertUtil.h
Normal file
@@ -0,0 +1,512 @@
|
|||||||
|
// src/Security/SslCertUtil.h
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <sys/stat.h> // chmod
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/ec.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/x509v3.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
namespace snoop
|
||||||
|
{
|
||||||
|
namespace device_sec
|
||||||
|
{
|
||||||
|
|
||||||
|
class SslCertUtil
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Generates:
|
||||||
|
// - EC P-256 private key at keyPath (PEM, chmod 600)
|
||||||
|
// - CSR at csrPath with:
|
||||||
|
// CN = guid
|
||||||
|
// subjectAltName = URI:urn:device:<guid>
|
||||||
|
static void GenerateEcKeyAndCsr(const std::string &guid,
|
||||||
|
const std::filesystem::path &keyPath,
|
||||||
|
const std::filesystem::path &csrPath)
|
||||||
|
{
|
||||||
|
spdlog::info("SslCertUtil: generating EC key + CSR for GUID={}", guid);
|
||||||
|
|
||||||
|
EVP_PKEY *pkey = nullptr;
|
||||||
|
EC_KEY *ec = nullptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// ----- 1) Generate EC key (prime256v1) -----
|
||||||
|
ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||||
|
if (!ec)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EC_KEY_new_by_curve_name failed");
|
||||||
|
}
|
||||||
|
if (EC_KEY_generate_key(ec) != 1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EC_KEY_generate_key failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
pkey = EVP_PKEY_new();
|
||||||
|
if (!pkey)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EVP_PKEY_new failed");
|
||||||
|
}
|
||||||
|
if (EVP_PKEY_assign_EC_KEY(pkey, ec) != 1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EVP_PKEY_assign_EC_KEY failed");
|
||||||
|
}
|
||||||
|
// pkey now owns ec
|
||||||
|
ec = nullptr; // prevent double free
|
||||||
|
|
||||||
|
// ----- 5) Write key + CSR to disk -----
|
||||||
|
SavePrivateKeyPem(pkey, keyPath);
|
||||||
|
BuildAndSaveCsr(pkey, guid, csrPath);
|
||||||
|
|
||||||
|
spdlog::info("SslCertUtil: key written to '{}', csr written to '{}'",
|
||||||
|
keyPath.string(), csrPath.string());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
if (pkey)
|
||||||
|
{
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
}
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
EC_KEY_free(ec);
|
||||||
|
}
|
||||||
|
throw; // rethrow
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSR from an existing key
|
||||||
|
static void GenerateCsrFromExistingKey(const std::string &guid,
|
||||||
|
const std::filesystem::path &keyPath,
|
||||||
|
const std::filesystem::path &csrPath)
|
||||||
|
{
|
||||||
|
spdlog::info("SslCertUtil: generating CSR from existing key '{}'", keyPath.string());
|
||||||
|
|
||||||
|
FILE *kf = fopen(keyPath.string().c_str(), "rb");
|
||||||
|
if (!kf)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("GenerateCsrFromExistingKey: cannot open key file: " +
|
||||||
|
keyPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_PKEY *pkey = PEM_read_PrivateKey(kf, nullptr, nullptr, nullptr);
|
||||||
|
fclose(kf);
|
||||||
|
|
||||||
|
if (!pkey)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("GenerateCsrFromExistingKey: PEM_read_PrivateKey failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BuildAndSaveCsr(pkey, guid, csrPath);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
spdlog::info("SslCertUtil: CSR written to '{}'", csrPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt file with AES-256-CBC + PBKDF2 + salt.
|
||||||
|
//
|
||||||
|
// Equivalent to:
|
||||||
|
// openssl enc -aes-256-cbc -pbkdf2 -salt -pass pass:<password> -in in -out out
|
||||||
|
//
|
||||||
|
// NOTE: Does NOT shred or delete the input file; caller should do that
|
||||||
|
// (you’ll still call `shred -u` from EnrollmentService).
|
||||||
|
static void EncryptFileAes256CbcPbkdf2(const std::filesystem::path &inPath,
|
||||||
|
const std::filesystem::path &outPath,
|
||||||
|
const std::string &password)
|
||||||
|
{
|
||||||
|
spdlog::info("SslCertUtil: encrypting '{}' -> '{}' (AES-256-CBC + PBKDF2)",
|
||||||
|
inPath.string(), outPath.string());
|
||||||
|
|
||||||
|
// ----- 1) Read input file -----
|
||||||
|
std::ifstream fin(inPath, std::ios::binary);
|
||||||
|
if (!fin)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: cannot open input file: " +
|
||||||
|
inPath.string());
|
||||||
|
}
|
||||||
|
std::vector<unsigned char> plaintext(
|
||||||
|
(std::istreambuf_iterator<char>(fin)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
fin.close();
|
||||||
|
|
||||||
|
// ----- 2) Prepare salt, key, iv -----
|
||||||
|
unsigned char salt[8];
|
||||||
|
if (RAND_bytes(salt, sizeof(salt)) != 1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: RAND_bytes failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
|
||||||
|
const int keyLen = EVP_CIPHER_key_length(cipher);
|
||||||
|
const int ivLen = EVP_CIPHER_iv_length(cipher);
|
||||||
|
|
||||||
|
std::vector<unsigned char> keyiv(keyLen + ivLen);
|
||||||
|
|
||||||
|
const int iterations = 10000; // PBKDF2 rounds
|
||||||
|
if (PKCS5_PBKDF2_HMAC(password.c_str(),
|
||||||
|
static_cast<int>(password.size()),
|
||||||
|
salt, sizeof(salt),
|
||||||
|
iterations,
|
||||||
|
EVP_sha256(),
|
||||||
|
keyLen + ivLen,
|
||||||
|
keyiv.data()) != 1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: PKCS5_PBKDF2_HMAC failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *key = keyiv.data();
|
||||||
|
unsigned char *iv = keyiv.data() + keyLen;
|
||||||
|
|
||||||
|
// ----- 3) Encrypt -----
|
||||||
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||||
|
if (!ctx)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: EVP_CIPHER_CTX_new failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> ciphertext(plaintext.size() + EVP_CIPHER_block_size(cipher));
|
||||||
|
int outLen1 = 0;
|
||||||
|
int outLen2 = 0;
|
||||||
|
|
||||||
|
if (EVP_EncryptInit_ex(ctx, cipher, nullptr, key, iv) != 1)
|
||||||
|
{
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: EVP_EncryptInit_ex failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_EncryptUpdate(ctx,
|
||||||
|
ciphertext.data(), &outLen1,
|
||||||
|
plaintext.data(), static_cast<int>(plaintext.size())) != 1)
|
||||||
|
{
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: EVP_EncryptUpdate failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_EncryptFinal_ex(ctx,
|
||||||
|
ciphertext.data() + outLen1, &outLen2) != 1)
|
||||||
|
{
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: EVP_EncryptFinal_ex failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
ciphertext.resize(outLen1 + outLen2);
|
||||||
|
|
||||||
|
// ----- 4) Write output: [salt][ciphertext] -----
|
||||||
|
std::ofstream fout(outPath, std::ios::binary | std::ios::trunc);
|
||||||
|
if (!fout)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: cannot open output file: " +
|
||||||
|
outPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just raw salt followed by ciphertext
|
||||||
|
fout.write(reinterpret_cast<const char *>(salt), sizeof(salt));
|
||||||
|
fout.write(reinterpret_cast<const char *>(ciphertext.data()),
|
||||||
|
static_cast<std::streamsize>(ciphertext.size()));
|
||||||
|
fout.close();
|
||||||
|
|
||||||
|
spdlog::info("SslCertUtil: encryption finished, wrote {}", outPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute HMAC-SHA256 and return lowercase hex string.
|
||||||
|
// Equivalent to:
|
||||||
|
// printf %s "<data>" | openssl dgst -sha256 -hmac "<key>" | awk '{print $2}'
|
||||||
|
static std::string ComputeHmacSha256Hex(const std::string &key,
|
||||||
|
const std::string &data)
|
||||||
|
{
|
||||||
|
unsigned char mac[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int macLen = 0;
|
||||||
|
|
||||||
|
if (!HMAC(EVP_sha256(),
|
||||||
|
key.data(), static_cast<int>(key.size()),
|
||||||
|
reinterpret_cast<const unsigned char *>(data.data()),
|
||||||
|
data.size(),
|
||||||
|
mac, &macLen))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("ComputeHmacSha256Hex: HMAC(EVP_sha256) failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToHexLower(mac, macLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt file with AES-256-CBC + PBKDF2 + salt.
|
||||||
|
// Layout: [8-byte salt][ciphertext]
|
||||||
|
// Password is KEK (hex string from HMAC).
|
||||||
|
static void DecryptFileAes256CbcPbkdf2(const std::filesystem::path &inPath,
|
||||||
|
const std::filesystem::path &outPath,
|
||||||
|
const std::string &password)
|
||||||
|
{
|
||||||
|
spdlog::info("SslCertUtil: decrypting '{}' -> '{}' (AES-256-CBC + PBKDF2)",
|
||||||
|
inPath.string(), outPath.string());
|
||||||
|
|
||||||
|
// ----- 1) Read input file -----
|
||||||
|
std::ifstream fin(inPath, std::ios::binary);
|
||||||
|
if (!fin)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: cannot open input file: " +
|
||||||
|
inPath.string());
|
||||||
|
}
|
||||||
|
std::vector<unsigned char> enc(
|
||||||
|
(std::istreambuf_iterator<char>(fin)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
fin.close();
|
||||||
|
|
||||||
|
if (enc.size() < 8)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: encrypted file too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
// First 8 bytes = salt, rest = ciphertext
|
||||||
|
unsigned char salt[8];
|
||||||
|
std::copy(enc.begin(), enc.begin() + 8, salt);
|
||||||
|
std::vector<unsigned char> ciphertext(enc.begin() + 8, enc.end());
|
||||||
|
|
||||||
|
// ----- 2) Derive key/iv -----
|
||||||
|
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
|
||||||
|
const int keyLen = EVP_CIPHER_key_length(cipher);
|
||||||
|
const int ivLen = EVP_CIPHER_iv_length(cipher);
|
||||||
|
|
||||||
|
std::vector<unsigned char> keyiv(keyLen + ivLen);
|
||||||
|
|
||||||
|
const int iterations = 10000;
|
||||||
|
if (PKCS5_PBKDF2_HMAC(password.c_str(),
|
||||||
|
static_cast<int>(password.size()),
|
||||||
|
salt, sizeof(salt),
|
||||||
|
iterations,
|
||||||
|
EVP_sha256(),
|
||||||
|
keyLen + ivLen,
|
||||||
|
keyiv.data()) != 1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: PKCS5_PBKDF2_HMAC failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *key = keyiv.data();
|
||||||
|
unsigned char *iv = keyiv.data() + keyLen;
|
||||||
|
|
||||||
|
// ----- 3) Decrypt -----
|
||||||
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||||
|
if (!ctx)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: EVP_CIPHER_CTX_new failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> plaintext(ciphertext.size() + EVP_CIPHER_block_size(cipher));
|
||||||
|
int outLen1 = 0;
|
||||||
|
int outLen2 = 0;
|
||||||
|
|
||||||
|
if (EVP_DecryptInit_ex(ctx, cipher, nullptr, key, iv) != 1)
|
||||||
|
{
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: EVP_DecryptInit_ex failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_DecryptUpdate(ctx,
|
||||||
|
plaintext.data(), &outLen1,
|
||||||
|
ciphertext.data(), static_cast<int>(ciphertext.size())) != 1)
|
||||||
|
{
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: EVP_DecryptUpdate failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_DecryptFinal_ex(ctx,
|
||||||
|
plaintext.data() + outLen1, &outLen2) != 1)
|
||||||
|
{
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: EVP_DecryptFinal_ex failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
plaintext.resize(outLen1 + outLen2);
|
||||||
|
|
||||||
|
// ----- 4) Write plaintext -----
|
||||||
|
std::filesystem::create_directories(outPath.parent_path());
|
||||||
|
std::ofstream fout(outPath, std::ios::binary | std::ios::trunc);
|
||||||
|
if (!fout)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: cannot open output file: " +
|
||||||
|
outPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fout.write(reinterpret_cast<const char *>(plaintext.data()),
|
||||||
|
static_cast<std::streamsize>(plaintext.size()));
|
||||||
|
fout.close();
|
||||||
|
|
||||||
|
spdlog::info("SslCertUtil: decryption finished, wrote {}", outPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void BuildAndSaveCsr(EVP_PKEY *pkey,
|
||||||
|
const std::string &guid,
|
||||||
|
const std::filesystem::path &csrPath)
|
||||||
|
{
|
||||||
|
X509_REQ *req = X509_REQ_new();
|
||||||
|
if (!req)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_new failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Subject: CN = GUID
|
||||||
|
X509_NAME *name = X509_NAME_new();
|
||||||
|
if (!name)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: X509_NAME_new failed");
|
||||||
|
}
|
||||||
|
if (X509_NAME_add_entry_by_NID(
|
||||||
|
name,
|
||||||
|
NID_commonName,
|
||||||
|
MBSTRING_ASC,
|
||||||
|
reinterpret_cast<const unsigned char *>(guid.c_str()),
|
||||||
|
-1, -1, 0) != 1)
|
||||||
|
{
|
||||||
|
X509_NAME_free(name);
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: X509_NAME_add_entry_by_NID(CN) failed");
|
||||||
|
}
|
||||||
|
if (X509_REQ_set_subject_name(req, name) != 1)
|
||||||
|
{
|
||||||
|
X509_NAME_free(name);
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_set_subject_name failed");
|
||||||
|
}
|
||||||
|
X509_NAME_free(name);
|
||||||
|
|
||||||
|
// Public key
|
||||||
|
if (X509_REQ_set_pubkey(req, pkey) != 1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_set_pubkey failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// subjectAltName = URI:urn:device:<GUID>
|
||||||
|
X509V3_CTX ctx;
|
||||||
|
X509V3_set_ctx_nodb(&ctx);
|
||||||
|
X509V3_set_ctx(&ctx, nullptr, nullptr, req, nullptr, 0);
|
||||||
|
|
||||||
|
std::string sanStr = "URI:urn:device:" + guid;
|
||||||
|
X509_EXTENSION *ext = X509V3_EXT_conf_nid(
|
||||||
|
nullptr, &ctx, NID_subject_alt_name,
|
||||||
|
const_cast<char *>(sanStr.c_str()));
|
||||||
|
if (!ext)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: X509V3_EXT_conf_nid(subjectAltName) failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
STACK_OF(X509_EXTENSION) *exts = sk_X509_EXTENSION_new_null();
|
||||||
|
if (!exts)
|
||||||
|
{
|
||||||
|
X509_EXTENSION_free(ext);
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: sk_X509_EXTENSION_new_null failed");
|
||||||
|
}
|
||||||
|
sk_X509_EXTENSION_push(exts, ext);
|
||||||
|
if (X509_REQ_add_extensions(req, exts) != 1)
|
||||||
|
{
|
||||||
|
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_add_extensions failed");
|
||||||
|
}
|
||||||
|
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||||
|
|
||||||
|
// Sign with SHA-256
|
||||||
|
if (X509_REQ_sign(req, pkey, EVP_sha256()) <= 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_sign failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveCsrPem(req, csrPath);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
X509_REQ_free(req);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
X509_REQ_free(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SavePrivateKeyPem(EVP_PKEY *pkey, const std::filesystem::path &keyPath)
|
||||||
|
{
|
||||||
|
const auto parent = keyPath.parent_path();
|
||||||
|
if (!parent.empty())
|
||||||
|
{
|
||||||
|
std::filesystem::create_directories(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *f = fopen(keyPath.string().c_str(), "wb");
|
||||||
|
if (!f)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("SavePrivateKeyPem: fopen failed: " + keyPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr) != 1)
|
||||||
|
{
|
||||||
|
fclose(f);
|
||||||
|
throw std::runtime_error("SavePrivateKeyPem: PEM_write_PrivateKey failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
// chmod 600
|
||||||
|
::chmod(keyPath.string().c_str(), 0600);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SaveCsrPem(X509_REQ *req, const std::filesystem::path &csrPath)
|
||||||
|
{
|
||||||
|
const auto parent = csrPath.parent_path();
|
||||||
|
if (!parent.empty())
|
||||||
|
{
|
||||||
|
std::filesystem::create_directories(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *f = fopen(csrPath.string().c_str(), "wb");
|
||||||
|
if (!f)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("SaveCsrPem: fopen failed: " + csrPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PEM_write_X509_REQ(f, req) != 1)
|
||||||
|
{
|
||||||
|
fclose(f);
|
||||||
|
throw std::runtime_error("SaveCsrPem: PEM_write_X509_REQ failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ToHexLower(const unsigned char *buf, size_t len)
|
||||||
|
{
|
||||||
|
static const char *hex = "0123456789abcdef";
|
||||||
|
std::string out;
|
||||||
|
out.reserve(len * 2);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
unsigned char b = buf[i];
|
||||||
|
out.push_back(hex[b >> 4]);
|
||||||
|
out.push_back(hex[b & 0x0F]);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace device_sec
|
||||||
|
} // namespace snoop
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include "SslCertUtil.h"
|
||||||
|
|
||||||
namespace snoop
|
namespace snoop
|
||||||
{
|
{
|
||||||
@@ -119,5 +120,100 @@ namespace snoop
|
|||||||
|
|
||||||
return std::vector<uint8_t>(bytes.begin(), bytes.end());
|
return std::vector<uint8_t>(bytes.begin(), bytes.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string GetCpuSerial()
|
||||||
|
{
|
||||||
|
std::ifstream f("/proc/cpuinfo");
|
||||||
|
if (!f)
|
||||||
|
{
|
||||||
|
spdlog::warn("GetCpuSerial: /proc/cpuinfo not available, using fallback");
|
||||||
|
return std::string(12, '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
std::regex re(R"(^\s*Serial\s*:\s*([0-9A-Fa-f]+)\s*$)");
|
||||||
|
while (std::getline(f, line))
|
||||||
|
{
|
||||||
|
std::smatch m;
|
||||||
|
if (std::regex_match(line, m, re) && m.size() == 2)
|
||||||
|
{
|
||||||
|
auto serial = Trim(m[1].str());
|
||||||
|
if (!serial.empty())
|
||||||
|
{
|
||||||
|
return serial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spdlog::warn("GetCpuSerial: Serial not found, using fallback");
|
||||||
|
return std::string(12, '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsClientKeyInKernelKeyring()
|
||||||
|
{
|
||||||
|
std::string id = Trim(Exec("keyctl search @s user iot-client-key | tail -n1"));
|
||||||
|
return !id.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that iot-client-key exists in kernel keyring.
|
||||||
|
// - If already present: no-op, returns false (nothing loaded).
|
||||||
|
// - If not present: decrypts /etc/iot/keys/device.key.enc using KEK
|
||||||
|
// derived from (GUID, CPU_SERIAL), padds into keyring, returns true.
|
||||||
|
inline bool EnsureClientKeyInKernelKeyring(const std::string &guid)
|
||||||
|
{
|
||||||
|
if (IsClientKeyInKernelKeyring())
|
||||||
|
{
|
||||||
|
spdlog::info("EnsureClientKeyInKernelKeyring: key already present, nothing to do");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::filesystem::path encPath = "/etc/iot/keys/device.key.enc";
|
||||||
|
if (!std::filesystem::exists(encPath))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EnsureClientKeyInKernelKeyring: encrypted key not found at " +
|
||||||
|
encPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive KEK = HMAC-SHA256(cpuSerial, key=GUID)
|
||||||
|
const std::string cpuSerial = GetCpuSerial();
|
||||||
|
spdlog::info("EnsureClientKeyInKernelKeyring: CPU_SERIAL = {}", cpuSerial);
|
||||||
|
|
||||||
|
const std::string kek =
|
||||||
|
snoop::device_sec::SslCertUtil::ComputeHmacSha256Hex(guid, cpuSerial);
|
||||||
|
|
||||||
|
if (kek.empty())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EnsureClientKeyInKernelKeyring: empty KEK");
|
||||||
|
}
|
||||||
|
spdlog::debug("EnsureClientKeyInKernelKeyring: KEK (hex) = {}", kek);
|
||||||
|
|
||||||
|
// Decrypt into tmpfs: /run/iot/device.key
|
||||||
|
const std::filesystem::path runDir = "/run/iot";
|
||||||
|
const std::filesystem::path plainPath = runDir / "device.key";
|
||||||
|
std::filesystem::create_directories(runDir);
|
||||||
|
|
||||||
|
snoop::device_sec::SslCertUtil::DecryptFileAes256CbcPbkdf2(
|
||||||
|
encPath,
|
||||||
|
plainPath,
|
||||||
|
kek);
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(plainPath))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("EnsureClientKeyInKernelKeyring: decrypted key not created");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load into kernel keyring
|
||||||
|
const std::string cmd = "keyctl padd user iot-client-key @s < " + plainPath.string();
|
||||||
|
spdlog::info("EnsureClientKeyInKernelKeyring: loading key into kernel keyring...");
|
||||||
|
auto out = Exec(cmd);
|
||||||
|
spdlog::debug("keyctl padd output:\n{}", out);
|
||||||
|
|
||||||
|
// Securely erase plaintext
|
||||||
|
const std::string shredCmd = "shred -u " + plainPath.string();
|
||||||
|
spdlog::info("EnsureClientKeyInKernelKeyring: shredding plaintext key with '{}'", shredCmd);
|
||||||
|
auto shredOut = Exec(shredCmd);
|
||||||
|
spdlog::debug("shred output:\n{}", shredOut);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,8 @@
|
|||||||
#include <openssl/asn1.h>
|
#include <openssl/asn1.h>
|
||||||
|
|
||||||
#include "ConfigService.h"
|
#include "ConfigService.h"
|
||||||
|
#include "Security/SslCertUtil.h"
|
||||||
|
#include "Security/TlsKeyUtil.h"
|
||||||
|
|
||||||
namespace snoop
|
namespace snoop
|
||||||
{
|
{
|
||||||
@@ -125,61 +127,181 @@ namespace snoop
|
|||||||
|
|
||||||
spdlog::info("Starting first-run enrollment...");
|
spdlog::info("Starting first-run enrollment...");
|
||||||
|
|
||||||
// 1) Run gen_device_csr.sh <GUID>
|
// // 1) Run gen_device_csr.sh <GUID>
|
||||||
{
|
// {
|
||||||
// Assumes script is in the working dir or in PATH
|
// // Assumes script is in the working dir or in PATH
|
||||||
const std::string cmd = "bash ./gen_device_csr.sh " + guid;
|
// const std::string cmd = "bash ./gen_device_csr.sh " + guid;
|
||||||
spdlog::info("Executing: {}", cmd);
|
// spdlog::info("Executing: {}", cmd);
|
||||||
auto out = Exec(cmd);
|
// auto out = Exec(cmd);
|
||||||
spdlog::debug("gen_device_csr.sh output:\n{}", out);
|
// spdlog::debug("gen_device_csr.sh output:\n{}", out);
|
||||||
}
|
// }
|
||||||
const std::string keyName = "device_" + guid + ".key";
|
const std::string keyName = "device_" + guid + ".key";
|
||||||
const std::string csrName = "device_" + guid + ".csr";
|
const std::string csrName = "device_" + guid + ".csr";
|
||||||
|
|
||||||
if (!std::filesystem::exists(keyName) || !std::filesystem::exists(csrName))
|
// if (!std::filesystem::exists(keyName) || !std::filesystem::exists(csrName))
|
||||||
{
|
// {
|
||||||
throw std::runtime_error("CSR or key was not generated by gen_device_csr.sh");
|
// throw std::runtime_error("CSR or key was not generated by gen_device_csr.sh");
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 2) CPU serial (awk '/Serial/ {print $3}' /proc/cpuinfo), if empty -> 9 * 12
|
// 1) Generate key + CSR via OpenSSL (no external script)
|
||||||
|
// {
|
||||||
|
// spdlog::info("Generating device key + CSR via SslCertUtil");
|
||||||
|
// snoop::device_sec::SslCertUtil::GenerateEcKeyAndCsr(
|
||||||
|
// guid,
|
||||||
|
// std::filesystem::path(keyName),
|
||||||
|
// std::filesystem::path(csrName));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!std::filesystem::exists(keyName) || !std::filesystem::exists(csrName))
|
||||||
|
// {
|
||||||
|
// throw std::runtime_error("CSR or key was not generated");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 1) CPU serial (awk '/Serial/ {print $3}' /proc/cpuinfo), if empty -> 9 * 12
|
||||||
const std::string cpuSerial = ParseCpuSerial();
|
const std::string cpuSerial = ParseCpuSerial();
|
||||||
spdlog::info("CPU_SERIAL = {}", cpuSerial);
|
spdlog::info("CPU_SERIAL = {}", cpuSerial);
|
||||||
|
|
||||||
// 3) KEK = HMAC-SHA256(cpuSerial, key=GUID) (bash equiv used)
|
// 3) KEK = HMAC-SHA256(cpuSerial, key=GUID) (bash equiv used)
|
||||||
|
// std::string kek;
|
||||||
|
// {
|
||||||
|
// // careful to avoid trailing newline
|
||||||
|
// const std::string cmd = "printf %s \"" + cpuSerial + "\""
|
||||||
|
// " | openssl dgst -sha256 -hmac \"" +
|
||||||
|
// guid + "\" | awk '{print $2}'";
|
||||||
|
// spdlog::info("Deriving KEK with HMAC-SHA256");
|
||||||
|
// kek = Trim(Exec(cmd));
|
||||||
|
// if (kek.empty())
|
||||||
|
// throw std::runtime_error("Failed to derive KEK");
|
||||||
|
// spdlog::debug("KEK (hex) = {}", kek);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 2) KEK = HMAC-SHA256(cpuSerial, key=GUID)
|
||||||
std::string kek;
|
std::string kek;
|
||||||
{
|
{
|
||||||
// careful to avoid trailing newline
|
spdlog::info("Deriving KEK with HMAC-SHA256 (OpenSSL API)");
|
||||||
const std::string cmd = "printf %s \"" + cpuSerial + "\""
|
kek = snoop::device_sec::SslCertUtil::ComputeHmacSha256Hex(guid, cpuSerial);
|
||||||
" | openssl dgst -sha256 -hmac \"" +
|
|
||||||
guid + "\" | awk '{print $2}'";
|
|
||||||
spdlog::info("Deriving KEK with HMAC-SHA256");
|
|
||||||
kek = Trim(Exec(cmd));
|
|
||||||
if (kek.empty())
|
if (kek.empty())
|
||||||
|
{
|
||||||
throw std::runtime_error("Failed to derive KEK");
|
throw std::runtime_error("Failed to derive KEK");
|
||||||
|
}
|
||||||
spdlog::debug("KEK (hex) = {}", kek);
|
spdlog::debug("KEK (hex) = {}", kek);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4) Encrypt the private key to /etc/iot/keys/device.key.enc using KEK; shred original
|
// // 4) Encrypt the private key to /etc/iot/keys/device.key.enc using KEK; shred original
|
||||||
|
// {
|
||||||
|
// std::filesystem::create_directories(keystoreDir);
|
||||||
|
// const std::string cmd =
|
||||||
|
// "openssl enc -aes-256-cbc -pbkdf2 -salt "
|
||||||
|
// "-pass pass:" +
|
||||||
|
// kek + " "
|
||||||
|
// "-in " +
|
||||||
|
// keyName + " "
|
||||||
|
// "-out " +
|
||||||
|
// encKeyPath.string() + " "
|
||||||
|
// "&& shred -u " +
|
||||||
|
// keyName;
|
||||||
|
// spdlog::info("Encrypting private key and shredding plaintext...");
|
||||||
|
// auto out = Exec(cmd);
|
||||||
|
// spdlog::debug("openssl enc output:\n{}", out);
|
||||||
|
|
||||||
|
// if (!std::filesystem::exists(encKeyPath))
|
||||||
|
// {
|
||||||
|
// throw std::runtime_error("Encrypted key not created: " + encKeyPath.string());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 3) Generate key + CSR via OpenSSL (no external script)
|
||||||
|
if (!std::filesystem::exists(encKeyPath))
|
||||||
{
|
{
|
||||||
|
// ---- Fresh key: generate + encrypt ----
|
||||||
|
spdlog::info("No encrypted key yet -> generating new EC key + CSR");
|
||||||
|
snoop::device_sec::SslCertUtil::GenerateEcKeyAndCsr(
|
||||||
|
guid,
|
||||||
|
std::filesystem::path(keyName),
|
||||||
|
std::filesystem::path(csrName));
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(keyName) || !std::filesystem::exists(csrName))
|
||||||
|
throw std::runtime_error("CSR or key was not generated");
|
||||||
|
|
||||||
std::filesystem::create_directories(keystoreDir);
|
std::filesystem::create_directories(keystoreDir);
|
||||||
const std::string cmd =
|
|
||||||
"openssl enc -aes-256-cbc -pbkdf2 -salt "
|
spdlog::info("Encrypting private key via SslCertUtil (AES-256-CBC + PBKDF2)...");
|
||||||
"-pass pass:" +
|
snoop::device_sec::SslCertUtil::EncryptFileAes256CbcPbkdf2(
|
||||||
kek + " "
|
std::filesystem::path(keyName),
|
||||||
"-in " +
|
encKeyPath,
|
||||||
keyName + " "
|
kek);
|
||||||
"-out " +
|
|
||||||
encKeyPath.string() + " "
|
|
||||||
"&& shred -u " +
|
|
||||||
keyName;
|
|
||||||
spdlog::info("Encrypting private key and shredding plaintext...");
|
|
||||||
auto out = Exec(cmd);
|
|
||||||
spdlog::debug("openssl enc output:\n{}", out);
|
|
||||||
|
|
||||||
if (!std::filesystem::exists(encKeyPath))
|
if (!std::filesystem::exists(encKeyPath))
|
||||||
{
|
|
||||||
throw std::runtime_error("Encrypted key not created: " + encKeyPath.string());
|
throw std::runtime_error("Encrypted key not created: " + encKeyPath.string());
|
||||||
|
|
||||||
|
// shred plaintext key
|
||||||
|
const std::string shredCmd = "shred -u " + keyName;
|
||||||
|
spdlog::info("Shredding plaintext private key with '{}'", shredCmd);
|
||||||
|
auto out = Exec(shredCmd);
|
||||||
|
spdlog::debug("shred output:\n{}", out);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ---- Key already exists: reuse it ----
|
||||||
|
spdlog::info("Encrypted key already exists -> reusing key, regenerating CSR only");
|
||||||
|
|
||||||
|
// Decrypt to temp file (in /run/iot)
|
||||||
|
snoop::device_sec::TempFile tf(std::filesystem::path("/run/iot"));
|
||||||
|
snoop::device_sec::SslCertUtil::DecryptFileAes256CbcPbkdf2(
|
||||||
|
encKeyPath,
|
||||||
|
tf.path,
|
||||||
|
kek);
|
||||||
|
|
||||||
|
// Generate CSR from existing key
|
||||||
|
snoop::device_sec::SslCertUtil::GenerateCsrFromExistingKey(
|
||||||
|
guid,
|
||||||
|
tf.path,
|
||||||
|
std::filesystem::path(csrName));
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(csrName))
|
||||||
|
throw std::runtime_error("CSR was not generated from existing key");
|
||||||
|
|
||||||
|
// TempFile destructor will securely delete the decrypted key file
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 4) Encrypt the private key to /etc/iot/keys/device.key.enc using KEK; shred original
|
||||||
|
// {
|
||||||
|
// std::filesystem::create_directories(keystoreDir);
|
||||||
|
|
||||||
|
// spdlog::info("Encrypting private key via SslCertUtil (AES-256-CBC + PBKDF2)...");
|
||||||
|
// snoop::device_sec::SslCertUtil::EncryptFileAes256CbcPbkdf2(
|
||||||
|
// std::filesystem::path(keyName),
|
||||||
|
// encKeyPath,
|
||||||
|
// kek);
|
||||||
|
|
||||||
|
// if (!std::filesystem::exists(encKeyPath))
|
||||||
|
// {
|
||||||
|
// throw std::runtime_error("Encrypted key not created: " + encKeyPath.string());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // still use shred to securely remove plaintext key, as requested
|
||||||
|
// const std::string shredCmd = "shred -u " + keyName;
|
||||||
|
// spdlog::info("Shredding plaintext private key with '{}'", shredCmd);
|
||||||
|
// auto out = Exec(shredCmd);
|
||||||
|
// spdlog::debug("shred output:\n{}", out);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 4.5) Ensure key is loaded into kernel keyring (first creation)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool loaded = snoop::device_sec::EnsureClientKeyInKernelKeyring(guid);
|
||||||
|
if (loaded)
|
||||||
|
{
|
||||||
|
spdlog::info("EnsureEnrolled: client key loaded into kernel keyring");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spdlog::info("EnsureEnrolled: client key was already in keyring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
spdlog::warn("EnsureEnrolled: failed to load key into keyring: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5) Send CSR to /enroll/:guid as multipart form, field `json: {"csr":"<...>"}`
|
// 5) Send CSR to /enroll/:guid as multipart form, field `json: {"csr":"<...>"}`
|
||||||
@@ -287,13 +409,31 @@ namespace snoop
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----- 2) re-generate CSR via your bash script -----
|
// ----- 2) re-generate CSR via your bash script -----
|
||||||
{
|
// {
|
||||||
const std::string cmd = "bash ./gen_device_csr.sh " + guid;
|
// const std::string cmd = "bash ./gen_device_csr.sh " + guid;
|
||||||
spdlog::info("Executing (renew): {}", cmd);
|
// spdlog::info("Executing (renew): {}", cmd);
|
||||||
auto out = Exec(cmd);
|
// auto out = Exec(cmd);
|
||||||
spdlog::debug("gen_device_csr.sh (renew) output:\n{}", out);
|
// spdlog::debug("gen_device_csr.sh (renew) output:\n{}", out);
|
||||||
}
|
// }
|
||||||
|
// const std::string csrName = "device_" + guid + ".csr";
|
||||||
|
// if (!std::filesystem::exists(csrName))
|
||||||
|
// {
|
||||||
|
// throw std::runtime_error("Renew: CSR was not generated");
|
||||||
|
// }
|
||||||
|
// std::string csrPem = ReadFile(csrName);
|
||||||
|
|
||||||
|
// ----- 2) re-generate CSR via OpenSSL (no bash) -----
|
||||||
const std::string csrName = "device_" + guid + ".csr";
|
const std::string csrName = "device_" + guid + ".csr";
|
||||||
|
{
|
||||||
|
spdlog::info("RenewCertificate: generating new CSR via SslCertUtil");
|
||||||
|
// We must reuse the existing key or generate a new one?
|
||||||
|
// Current flow (bash script) always regenerated key+csr, so we mimic that:
|
||||||
|
snoop::device_sec::SslCertUtil::GenerateEcKeyAndCsr(
|
||||||
|
guid,
|
||||||
|
std::filesystem::path("device_" + guid + ".key"),
|
||||||
|
std::filesystem::path(csrName));
|
||||||
|
}
|
||||||
|
|
||||||
if (!std::filesystem::exists(csrName))
|
if (!std::filesystem::exists(csrName))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Renew: CSR was not generated");
|
throw std::runtime_error("Renew: CSR was not generated");
|
||||||
@@ -461,7 +601,7 @@ namespace snoop
|
|||||||
if (days == 0 && secs <= 0)
|
if (days == 0 && secs <= 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return days <= daysThreshold;
|
return days <= daysThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
39
src/main.cpp
39
src/main.cpp
@@ -10,6 +10,7 @@
|
|||||||
#include "Services/ConfigService.h"
|
#include "Services/ConfigService.h"
|
||||||
#include "Services/EnrollmentService.h"
|
#include "Services/EnrollmentService.h"
|
||||||
#include "Services/DeviceControlService.h"
|
#include "Services/DeviceControlService.h"
|
||||||
|
#include "Security/TlsKeyUtil.h"
|
||||||
|
|
||||||
#ifdef USE_ALSA_ADAPTER
|
#ifdef USE_ALSA_ADAPTER
|
||||||
#include "AudioAdapters/AlsaAudioAdapter.h"
|
#include "AudioAdapters/AlsaAudioAdapter.h"
|
||||||
@@ -35,22 +36,40 @@ namespace snoop
|
|||||||
auto configService = std::make_shared<ConfigService>("config.json");
|
auto configService = std::make_shared<ConfigService>("config.json");
|
||||||
|
|
||||||
// ---- FIRST-RUN ENROLLMENT ----
|
// ---- FIRST-RUN ENROLLMENT ----
|
||||||
|
// EnrollmentService enroll(configService);
|
||||||
|
// const bool didEnroll = enroll.EnsureEnrolled();
|
||||||
|
// if (didEnroll)
|
||||||
|
// {
|
||||||
|
// spdlog::info("First-run enrollment completed.");
|
||||||
|
// }
|
||||||
|
auto enrollSvc = std::make_shared<EnrollmentService>(configService);
|
||||||
|
enrollSvc->EnsureEnrolled();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
EnrollmentService enroll(configService);
|
enrollSvc->RenewCertificate(false);
|
||||||
const bool didEnroll = enroll.EnsureEnrolled();
|
}
|
||||||
if (didEnroll)
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
spdlog::warn("Auto-renew check failed: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure client key is in kernel keyring for this session
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool loaded = snoop::device_sec::EnsureClientKeyInKernelKeyring(configService->GetGuid());
|
||||||
|
if (loaded)
|
||||||
{
|
{
|
||||||
spdlog::info("First-run enrollment completed.");
|
spdlog::info("Main: client key loaded into kernel keyring for this session");
|
||||||
}
|
}
|
||||||
try
|
else
|
||||||
{
|
{
|
||||||
enroll.RenewCertificate(false);
|
spdlog::info("Main: client key already present in kernel keyring");
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
spdlog::warn("Auto-renew check failed: {}", e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
spdlog::warn("Main: failed to ensure client key in keyring: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
auto writerService = std::make_shared<AudioWriterService>(configService, "records");
|
auto writerService = std::make_shared<AudioWriterService>(configService, "records");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user