Skip to content
github-actions edited this page Mar 15, 2026 · 3 revisions

title: src/Messenger/Cryptor.cpp


src/Messenger/Cryptor.cpp

Namespaces

Name
aasdk
aasdk::messenger

Attributes

Name
bool enabled
int sampleEvery

Attributes Documentation

variable enabled

bool enabled {false};

variable sampleEvery

int sampleEvery {1};

Source code

// This file is part of aasdk library project.
// Copyright (C) 2018 f1x.studio (Michal Szwaj)
// Copyright (C) 2024 CubeOne (Simon Dean - simon.dean@cubeone.co.uk)
// Copyright (C) 2024 OpenCarDev (Matthew Hilton - matthilton2005@gmail.com)
// Copyright (C) 2026 OpenCarDev (Matthew Hilton - matthilton2005@gmail.com)
//
// aasdk is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// aasdk is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with aasdk. If not, see <http://www.gnu.org/licenses/>.

#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <functional>
#include <fstream>
#include <sstream>
#include <aasdk/Messenger/Cryptor.hpp>
#include <aasdk/Error/Error.hpp>
#include <aasdk/Common/Log.hpp>
#include <aasdk/Common/ModernLogger.hpp>

namespace aasdk {
  namespace messenger {

    namespace {

      struct RuntimeTraceConfig {
        bool enabled{false};
        int sampleEvery{1};
      };

      static auto parseEnvBool(const char* value, bool defaultValue) -> bool {
        if (value == nullptr) {
          return defaultValue;
        }

        const std::string token(value);
        if (token.empty()) {
          return defaultValue;
        }

        if (token == "1" || token == "true" || token == "TRUE" || token == "on" ||
            token == "ON" || token == "yes" || token == "YES") {
          return true;
        }

        if (token == "0" || token == "false" || token == "FALSE" || token == "off" ||
            token == "OFF" || token == "no" || token == "NO") {
          return false;
        }

        return defaultValue;
      }

      static auto parseEnvInt(const char* value, int defaultValue, int minValue, int maxValue) -> int {
        if (value == nullptr) {
          return defaultValue;
        }

        try {
          const int parsed = std::stoi(value);
          return std::max(minValue, std::min(maxValue, parsed));
        } catch (...) {
          return defaultValue;
        }
      }

      static auto readRuntimeTraceConfig() -> RuntimeTraceConfig {
        RuntimeTraceConfig cfg;
        cfg.enabled = parseEnvBool(std::getenv("AASDK_TRACE_CRYPTOR"), false);
        cfg.sampleEvery = parseEnvInt(std::getenv("AASDK_TRACE_CRYPTOR_SAMPLE_EVERY"), 1, 1, 1000);
        return cfg;
      }

      static auto getRuntimeTraceConfig() -> RuntimeTraceConfig {
        static RuntimeTraceConfig cached = readRuntimeTraceConfig();
        static auto lastRefresh = std::chrono::steady_clock::now();

        const auto now = std::chrono::steady_clock::now();
        if (now - lastRefresh > std::chrono::seconds(1)) {
          cached = readRuntimeTraceConfig();
          lastRefresh = now;
        }

        return cached;
      }

      static auto shouldEmitTraceSample() -> bool {
        static std::atomic<uint64_t> counter{0};
        const RuntimeTraceConfig cfg = getRuntimeTraceConfig();
        if (!cfg.enabled) {
          return false;
        }

        const uint64_t current = ++counter;
        return (current % static_cast<uint64_t>(cfg.sampleEvery)) == 0;
      }

    } // namespace

    // Embedded certificate constants
    static const std::string cCertificate = "-----BEGIN CERTIFICATE-----\n\
MIIDKjCCAhICARswDQYJKoZIhvcNAQELBQAwWzELMAkGA1UEBhMCVVMxEzARBgNV\n\
BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxHzAdBgNVBAoM\n\
Fkdvb2dsZSBBdXRvbW90aXZlIExpbmswJhcRMTQwNzA0MDAwMDAwLTA3MDAXETQ1\n\
MDQyOTE0MjgzOC0wNzAwMFMxCzAJBgNVBAYTAkpQMQ4wDAYDVQQIDAVUb2t5bzER\n\
MA8GA1UEBwwISGFjaGlvamkxFDASBgNVBAoMC0pWQyBLZW53b29kMQswCQYDVQQL\n\
DAIwMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM911mNnUfx+WJtx\n\
uk06GO7kXRW/gXUVNQBkbAFZmVdVNvLoEQNthi2X8WCOwX6n6oMPxU2MGJnvicP3\n\
6kBqfHhfQ2Fvqlf7YjjhgBHh0lqKShVPxIvdatBjVQ76aym5H3GpkigLGkmeyiVo\n\
VO8oc3cJ1bO96wFRmk7kJbYcEjQyakODPDu4QgWUTwp1Z8Dn41ARMG5OFh6otITL\n\
XBzj9REkUPkxfS03dBXGr5/LIqvSsnxib1hJ47xnYJXROUsBy3e6T+fYZEEzZa7y\n\
7tFioHIQ8G/TziPmvFzmQpaWMGiYfoIgX8WoR3GD1diYW+wBaZTW+4SFUZJmRKgq\n\
TbMNFkMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAsGdH5VFn78WsBElMXaMziqFC\n\
zmilkvr85/QpGCIztI0FdF6xyMBJk/gYs2thwvF+tCCpXoO8mjgJuvJZlwr6fHzK\n\
Ox5hNUb06AeMtsUzUfFjSZXKrSR+XmclVd+Z6/ie33VhGePOPTKYmJ/PPfTT9wvT\n\
93qswcxhA+oX5yqLbU3uDPF1ZnJaEeD/YN45K/4eEA4/0SDXaWW14OScdS2LV0Bc\n\
YmsbkPVNYZn37FlY7e2Z4FUphh0A7yME2Eh/e57QxWrJ1wubdzGnX8mrABc67ADU\n\
U5r9tlTRqMs7FGOk6QS2Cxp4pqeVQsrPts4OEwyPUyb3LfFNo3+sP111D9zEow==\n\
-----END CERTIFICATE-----\n";

    static const std::string cPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n\
MIIEowIBAAKCAQEAz3XWY2dR/H5Ym3G6TToY7uRdFb+BdRU1AGRsAVmZV1U28ugR\n\
A22GLZfxYI7Bfqfqgw/FTYwYme+Jw/fqQGp8eF9DYW+qV/tiOOGAEeHSWopKFU/E\n\
i91q0GNVDvprKbkfcamSKAsaSZ7KJWhU7yhzdwnVs73rAVGaTuQlthwSNDJqQ4M8\n\
O7hCBZRPCnVnwOfjUBEwbk4WHqi0hMtcHOP1ESRQ+TF9LTd0Fcavn8siq9KyfGJv\n\
WEnjvGdgldE5SwHLd7pP59hkQTNlrvLu0WKgchDwb9POI+a8XOZClpYwaJh+giBf\n\
xahHcYPV2Jhb7AFplNb7hIVRkmZEqCpNsw0WQwIDAQABAoIBAB2u7ZLheKCY71Km\n\
bhKYqnKb6BmxgfNfqmq4858p07/kKG2O+Mg1xooFgHrhUhwuKGbCPee/kNGNrXeF\n\
pFW9JrwOXVS2pnfaNw6ObUWhuvhLaxgrhqLAdoUEgWoYOHcKzs3zhj8Gf6di+edq\n\
SyTA8+xnUtVZ6iMRKvP4vtCUqaIgBnXdmQbGINP+/4Qhb5R7XzMt/xPe6uMyAIyC\n\
y5Fm9HnvekaepaeFEf3bh4NV1iN/R8px6cFc6ELYxIZc/4Xbm91WGqSdB0iSriaZ\n\
TjgrmaFjSO40tkCaxI9N6DGzJpmpnMn07ifhl2VjnGOYwtyuh6MKEnyLqTrTg9x0\n\
i3mMwskCgYEA9IyljPRerXxHUAJt+cKOayuXyNt80q9PIcGbyRNvn7qIY6tr5ut+\n\
ZbaFgfgHdSJ/4nICRq02HpeDJ8oj9BmhTAhcX6c1irH5ICjRlt40qbPwemIcpybt\n\
mb+DoNYbI8O4dUNGH9IPfGK8dRpOok2m+ftfk94GmykWbZF5CnOKIp8CgYEA2Syc\n\
5xlKB5Qk2ZkwXIzxbzozSfunHhWWdg4lAbyInwa6Y5GB35UNdNWI8TAKZsN2fKvX\n\
RFgCjbPreUbREJaM3oZ92o5X4nFxgjvAE1tyRqcPVbdKbYZgtcqqJX06sW/g3r/3\n\
RH0XPj2SgJIHew9sMzjGWDViMHXLmntI8rVA7d0CgYBOr36JFwvrqERN0ypNpbMr\n\
epBRGYZVSAEfLGuSzEUrUNqXr019tKIr2gmlIwhLQTmCxApFcXArcbbKs7jTzvde\n\
PoZyZJvOr6soFNozP/YT8Ijc5/quMdFbmgqhUqLS5CPS3z2N+YnwDNj0mO1aPcAP\n\
STmcm2DmxdaolJksqrZ0owKBgQCD0KJDWoQmaXKcaHCEHEAGhMrQot/iULQMX7Vy\n\
gl5iN5E2EgFEFZIfUeRWkBQgH49xSFPWdZzHKWdJKwSGDvrdrcABwdfx520/4MhK\n\
d3y7CXczTZbtN1zHuoTfUE0pmYBhcx7AATT0YCblxrynosrHpDQvIefBBh5YW3AB\n\
cKZCOQKBgEM/ixzI/OVSZ0Py2g+XV8+uGQyC5XjQ6cxkVTX3Gs0ZXbemgUOnX8co\n\
eCXS4VrhEf4/HYMWP7GB5MFUOEVtlLiLM05ruUL7CrphdfgayDXVcTPfk75lLhmu\n\
KAwp3tIHPoJOQiKNQ3/qks5km/9dujUGU2ARiU3qmxLMdgegFz8e\n\
-----END RSA PRIVATE KEY-----\n";

    // Helper function to read file contents
    static std::string readFileContents(const std::string& filepath) {
      std::ifstream file(filepath);
      if (!file.is_open()) {
        AASDK_LOG(error) << "[Cryptor] Failed to open file: " << filepath;
        return "";
      }
      
      std::stringstream buffer;
      buffer << file.rdbuf();
      return buffer.str();
    }

    // Helper to try multiple certificate paths
    static std::string loadCertificate() {
      // Try paths in order of preference
      std::vector<std::string> certPaths = {
        "/etc/aasdk/headunit.crt",              // Installed system path
        "/etc/openauto/headunit.crt",           // Legacy path (backward compatibility)
        "/usr/share/aasdk/cert/headunit.crt",   // Alternative system path
        "./cert/headunit.crt",                   // Development path
        "../cert/headunit.crt"                   // Alternative development path
      };
      
      for (const auto& path : certPaths) {
        std::string content = readFileContents(path);
        if (!content.empty()) {
          AASDK_LOG(info) << "[Cryptor] Loaded certificate from: " << path;
          return content;
        }
      }
      
      AASDK_LOG(warning) << "[Cryptor] Could not load certificate from file, using embedded fallback";
      return cCertificate;  // Fallback to embedded certificate
    }

    // Helper to try multiple private key paths
    static std::string loadPrivateKey() {
      // Try paths in order of preference
      std::vector<std::string> keyPaths = {
        "/etc/aasdk/headunit.key",              // Installed system path
        "/etc/openauto/headunit.key",           // Legacy path (backward compatibility)
        "/usr/share/aasdk/cert/headunit.key",   // Alternative system path
        "./cert/headunit.key",                   // Development path
        "../cert/headunit.key"                   // Alternative development path
      };
      
      for (const auto& path : keyPaths) {
        std::string content = readFileContents(path);
        if (!content.empty()) {
          AASDK_LOG(info) << "[Cryptor] Loaded private key from: " << path;
          return content;
        }
      }
      
      AASDK_LOG(warning) << "[Cryptor] Could not load private key from file, using embedded fallback";
      return cPrivateKey;  // Fallback to embedded key
    }

    Cryptor::Cryptor(transport::ISSLWrapper::Pointer sslWrapper)
        : sslWrapper_(std::move(sslWrapper)), maxBufferSize_(1024 * 20), certificate_(nullptr), privateKey_(nullptr),
          context_(nullptr), ssl_(nullptr), isActive_(false) {

    }

    void Cryptor::init() {
      std::lock_guard<decltype(mutex_)> lock(mutex_);

      // Load certificate from file or fallback to embedded
      std::string certContent = loadCertificate();
      certificate_ = sslWrapper_->readCertificate(certContent);

      if (certificate_ == nullptr) {
        throw error::Error(error::ErrorCode::SSL_READ_CERTIFICATE);
      }

      // Load private key from file or fallback to embedded
      std::string keyContent = loadPrivateKey();
      privateKey_ = sslWrapper_->readPrivateKey(keyContent);

      if (privateKey_ == nullptr) {
        throw error::Error(error::ErrorCode::SSL_READ_PRIVATE_KEY);
      }

      auto method = sslWrapper_->getMethod();

      if (method == nullptr) {
        throw error::Error(error::ErrorCode::SSL_METHOD);
      }

      context_ = sslWrapper_->createContext(method);

      if (context_ == nullptr) {
        throw error::Error(error::ErrorCode::SSL_CONTEXT_CREATION);
      }

      if (!sslWrapper_->useCertificate(context_, certificate_)) {
        throw error::Error(error::ErrorCode::SSL_USE_CERTIFICATE);
      }

      if (!sslWrapper_->usePrivateKey(context_, privateKey_)) {
        throw error::Error(error::ErrorCode::SSL_USE_PRIVATE_KEY);
      }

      ssl_ = sslWrapper_->createInstance(context_);

      if (ssl_ == nullptr) {
        throw error::Error(error::ErrorCode::SSL_HANDLER_CREATION);
      }

      bIOs_ = sslWrapper_->createBIOs();

      if (bIOs_.first == nullptr) {
        throw error::Error(error::ErrorCode::SSL_READ_BIO_CREATION);
      }

      if (bIOs_.second == nullptr) {
        throw error::Error(error::ErrorCode::SSL_WRITE_BIO_CREATION);
      }

      sslWrapper_->setBIOs(ssl_, bIOs_, maxBufferSize_);

      sslWrapper_->setConnectState(ssl_);
    }

    void Cryptor::deinit() {
      std::lock_guard<decltype(mutex_)> lock(mutex_);

      if (ssl_ != nullptr) {
        sslWrapper_->free(ssl_);
        ssl_ = nullptr;
      }

      bIOs_ = {nullptr, nullptr};

      if (context_ != nullptr) {
        sslWrapper_->free(context_);
        context_ = nullptr;
      }

      if (certificate_ != nullptr) {
        sslWrapper_->free(certificate_);
        certificate_ = nullptr;
      }

      if (privateKey_ != nullptr) {
        sslWrapper_->free(privateKey_);
        privateKey_ = nullptr;
      }
    }

    bool Cryptor::doHandshake() {
      std::lock_guard<decltype(mutex_)> lock(mutex_);

      auto result = sslWrapper_->doHandshake(ssl_);
      if (result == SSL_ERROR_WANT_READ) {
        return false;
      } else if (result == SSL_ERROR_NONE) {
        isActive_ = true;
        return true;
      } else {
        throw error::Error(error::ErrorCode::SSL_HANDSHAKE, result);
      }
    }

    size_t Cryptor::encrypt(common::Data &output, const common::DataConstBuffer &buffer) {
      std::lock_guard<decltype(mutex_)> lock(mutex_);

      size_t totalWrittenBytes = 0;

      while (totalWrittenBytes < buffer.size) {
        const common::DataConstBuffer currentBuffer(buffer.cdata, buffer.size, totalWrittenBytes);
        const auto writeSize = sslWrapper_->sslWrite(ssl_, currentBuffer.cdata, currentBuffer.size);

        if (writeSize <= 0) {
          throw error::Error(error::ErrorCode::SSL_WRITE, sslWrapper_->getError(ssl_, writeSize));
        }

        totalWrittenBytes += writeSize;
      }

      return this->read(output);
    }

    size_t Cryptor::decrypt(common::Data &output, const common::DataConstBuffer &buffer, int frameLength) {
      std::lock_guard<decltype(mutex_)> lock(mutex_);

      const bool traceSample = shouldEmitTraceSample();
      const int pendingBeforeWrite = sslWrapper_->getAvailableBytes(ssl_);

      this->write(buffer);
      const size_t beginOffset = output.size();

      size_t totalReadSize = 0;
      while (true) {
        const size_t readBytes = 2048;
        output.resize(beginOffset + totalReadSize + readBytes);

        const auto &currentBuffer = common::DataBuffer(output, totalReadSize + beginOffset);
        const auto readSize = sslWrapper_->sslRead(ssl_, currentBuffer.data, currentBuffer.size);

        if (readSize <= 0) {
          const auto nativeError = sslWrapper_->getError(ssl_, readSize);
          const int pendingAfterRead = sslWrapper_->getAvailableBytes(ssl_);

          if (nativeError == SSL_ERROR_WANT_READ || nativeError == SSL_ERROR_WANT_WRITE) {
            if (traceSample) {
              AASDK_LOG(info) << "[CryptorTrace] decrypt-drained"
                              << " frameLength=" << frameLength
                              << " encryptedBytesIn=" << buffer.size
                              << " totalReadSize=" << totalReadSize
                              << " requestedReadBytes=" << readBytes
                              << " sslError=" << nativeError
                              << " pendingBeforeWrite=" << pendingBeforeWrite
                              << " pendingAfterRead=" << pendingAfterRead;
            }
            AASDK_LOG(debug) << "[Cryptor] SSL decrypt drained"
                             << " frameLength=" << frameLength
                             << " totalReadSize=" << totalReadSize
                             << " requestedReadBytes=" << readBytes
                             << " sslError=" << nativeError;
            output.resize(beginOffset + totalReadSize);
            return totalReadSize;
          }

          const std::string info = "decrypt sslRead<=0"
                                   " frameLength=" + std::to_string(frameLength) +
                                   " totalReadSize=" + std::to_string(totalReadSize) +
                                   " requestedReadBytes=" + std::to_string(readBytes) +
                                   " returnCode=" + std::to_string(readSize);
          throw error::Error(error::ErrorCode::SSL_READ, nativeError, info);
        }

        totalReadSize += static_cast<size_t>(readSize);
        if (traceSample) {
          AASDK_LOG(info) << "[CryptorTrace] decrypt-read"
                          << " frameLength=" << frameLength
                          << " encryptedBytesIn=" << buffer.size
                          << " readSize=" << readSize
                          << " totalReadSize=" << totalReadSize
                          << " pendingBeforeWrite=" << pendingBeforeWrite;
        }
      }
    }

    common::Data Cryptor::readHandshakeBuffer() {
      std::lock_guard<decltype(mutex_)> lock(mutex_);

      common::Data output;
      this->read(output);
      return output;
    }

    void Cryptor::writeHandshakeBuffer(const common::DataConstBuffer &buffer) {
      std::lock_guard<decltype(mutex_)> lock(mutex_);

      this->write(buffer);
    }

    size_t Cryptor::read(common::Data &output) {
      const auto pendingSize = sslWrapper_->bioCtrlPending(bIOs_.second);

      size_t beginOffset = output.size();
      output.resize(beginOffset + pendingSize);
      size_t totalReadSize = 0;

      while (totalReadSize < pendingSize) {
        const auto &currentBuffer = common::DataBuffer(output, totalReadSize + beginOffset);
        const auto readSize = sslWrapper_->bioRead(bIOs_.second, currentBuffer.data, currentBuffer.size);

        if (readSize <= 0) {
          const auto nativeError = sslWrapper_->getError(ssl_, readSize);
          const std::string info = "read bioRead<=0"
                                   " pendingSize=" + std::to_string(pendingSize) +
                                   " totalReadSize=" + std::to_string(totalReadSize) +
                                   " currentBufferSize=" + std::to_string(currentBuffer.size) +
                                   " returnCode=" + std::to_string(readSize);
          throw error::Error(error::ErrorCode::SSL_BIO_READ, nativeError, info);
        }

        totalReadSize += readSize;
      }

      return totalReadSize;
    }

    void Cryptor::write(const common::DataConstBuffer &buffer) {
      size_t totalWrittenBytes = 0;

      while (totalWrittenBytes < buffer.size) {
        const common::DataConstBuffer currentBuffer(buffer.cdata, buffer.size, totalWrittenBytes);
        const auto writeSize = sslWrapper_->bioWrite(bIOs_.first, currentBuffer.cdata, currentBuffer.size);

        if (writeSize <= 0) {
          throw error::Error(error::ErrorCode::SSL_BIO_WRITE, sslWrapper_->getError(ssl_, writeSize));
        }

        totalWrittenBytes += writeSize;
      }
    }

    bool Cryptor::isActive() const {
      std::lock_guard<decltype(mutex_)> lock(mutex_);

      return isActive_;
    }

  }
}

Updated on 2026-03-15 at 09:02:41 +0000

Clone this wiki locally