LCOV - code coverage report
Current view: top level - src/connectivity/security - tlsvalidator.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 0.0 % 377 0
Test Date: 2026-06-13 09:18:46 Functions: 0.0 % 71 0

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  *  This program is free software: you can redistribute it and/or modify
       5              :  *  it under the terms of the GNU General Public License as published by
       6              :  *  the Free Software Foundation, either version 3 of the License, or
       7              :  *  (at your option) any later version.
       8              :  *
       9              :  *  This program is distributed in the hope that it will be useful,
      10              :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  *  GNU General Public License for more details.
      13              :  *
      14              :  *  You should have received a copy of the GNU General Public License
      15              :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : 
      18              : #include "tlsvalidator.h"
      19              : 
      20              : #ifdef HAVE_CONFIG_H
      21              : #include "config.h"
      22              : #endif
      23              : 
      24              : #include <opendht/infohash.h> // for toHex
      25              : #include <dhtnet/certstore.h>
      26              : 
      27              : #include "fileutils.h"
      28              : #include "logger.h"
      29              : #include "security_const.h"
      30              : #include "string_utils.h"
      31              : 
      32              : #include <sstream>
      33              : 
      34              : #include <cstdio>
      35              : #include <cerrno>
      36              : #include <cassert>
      37              : #include <ctime>
      38              : 
      39              : #include <sys/types.h>
      40              : #include <sys/stat.h>
      41              : 
      42              : #ifndef _MSC_VER
      43              : #include <libgen.h>
      44              : #endif
      45              : 
      46              : #ifndef _WIN32
      47              : #include <sys/socket.h>
      48              : #include <netinet/tcp.h>
      49              : #include <netinet/in.h>
      50              : #include <netdb.h>
      51              : #else
      52              : #ifndef _MSC_VER
      53              : #define close(x) closesocket(x)
      54              : #endif
      55              : #endif
      56              : #include <unistd.h>
      57              : #include <fcntl.h>
      58              : 
      59              : #ifdef _MSC_VER
      60              : #include "windirent.h"
      61              : #endif
      62              : 
      63              : #include <filesystem>
      64              : 
      65              : namespace jami {
      66              : namespace tls {
      67              : 
      68              : // Map the internal ring Enum class of the exported names
      69              : 
      70              : const EnumClassNames<TlsValidator::CheckValues> TlsValidator::CheckValuesNames = {{
      71              :     /* CheckValues                        Name                         */
      72              :     /* PASSED      */ libjami::Certificate::CheckValuesNames::PASSED,
      73              :     /* FAILED      */ libjami::Certificate::CheckValuesNames::FAILED,
      74              :     /* UNSUPPORTED */ libjami::Certificate::CheckValuesNames::UNSUPPORTED,
      75              :     /* ISO_DATE    */ libjami::Certificate::CheckValuesNames::ISO_DATE,
      76              :     /* CUSTOM      */ libjami::Certificate::CheckValuesNames::CUSTOM,
      77              :     /* CUSTOM      */ libjami::Certificate::CheckValuesNames::DATE,
      78              : }};
      79              : 
      80              : const CallbackMatrix1D<TlsValidator::CertificateCheck, TlsValidator, TlsValidator::CheckResult>
      81              :     TlsValidator::checkCallback = {{
      82              :         /*      CertificateCheck                       Callback                            */
      83              :         /*HAS_PRIVATE_KEY                  */ &TlsValidator::hasPrivateKey,
      84              :         /*EXPIRED                          */ &TlsValidator::notExpired,
      85              :         /*STRONG_SIGNING                   */ &TlsValidator::strongSigning,
      86              :         /*NOT_SELF_SIGNED                  */ &TlsValidator::notSelfSigned,
      87              :         /*KEY_MATCH                        */ &TlsValidator::keyMatch,
      88              :         /*PRIVATE_KEY_STORAGE_PERMISSION   */ &TlsValidator::privateKeyStoragePermissions,
      89              :         /*PUBLIC_KEY_STORAGE_PERMISSION    */ &TlsValidator::publicKeyStoragePermissions,
      90              :         /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ &TlsValidator::privateKeyDirectoryPermissions,
      91              :         /*PUBLICKEY_DIRECTORY_PERMISSIONS  */ &TlsValidator::publicKeyDirectoryPermissions,
      92              :         /*PRIVATE_KEY_STORAGE_LOCATION     */ &TlsValidator::privateKeyStorageLocation,
      93              :         /*PUBLIC_KEY_STORAGE_LOCATION      */ &TlsValidator::publicKeyStorageLocation,
      94              :         /*PRIVATE_KEY_SELINUX_ATTRIBUTES   */ &TlsValidator::privateKeySelinuxAttributes,
      95              :         /*PUBLIC_KEY_SELINUX_ATTRIBUTES    */ &TlsValidator::publicKeySelinuxAttributes,
      96              :         /*EXIST                            */ &TlsValidator::exist,
      97              :         /*VALID                            */ &TlsValidator::valid,
      98              :         /*VALID_AUTHORITY                  */ &TlsValidator::validAuthority,
      99              :         /*KNOWN_AUTHORITY                  */ &TlsValidator::knownAuthority,
     100              :         /*NOT_REVOKED                      */ &TlsValidator::notRevoked,
     101              :         /*AUTHORITY_MISMATCH               */ &TlsValidator::authorityMatch,
     102              :         /*UNEXPECTED_OWNER                 */ &TlsValidator::expectedOwner,
     103              :         /*NOT_ACTIVATED                    */ &TlsValidator::activated,
     104              :     }};
     105              : 
     106              : const CallbackMatrix1D<TlsValidator::CertificateDetails, TlsValidator, TlsValidator::CheckResult>
     107              :     TlsValidator::getterCallback = {{
     108              :         /* EXPIRATION_DATE              */ &TlsValidator::getExpirationDate,
     109              :         /* ACTIVATION_DATE              */ &TlsValidator::getActivationDate,
     110              :         /* REQUIRE_PRIVATE_KEY_PASSWORD */ &TlsValidator::requirePrivateKeyPassword,
     111              :         /* PUBLIC_SIGNATURE             */ &TlsValidator::getPublicSignature,
     112              :         /* VERSION_NUMBER               */ &TlsValidator::getVersionNumber,
     113              :         /* SERIAL_NUMBER                */ &TlsValidator::getSerialNumber,
     114              :         /* ISSUER                       */ &TlsValidator::getIssuer,
     115              :         /* SUBJECT_KEY_ALGORITHM        */ &TlsValidator::getSubjectKeyAlgorithm,
     116              :         /* SUBJECT_KEY                  */ &TlsValidator::getSubjectKey,
     117              :         /* CN                           */ &TlsValidator::getCN,
     118              :         /* UID                          */ &TlsValidator::getUID,
     119              :         /* N                            */ &TlsValidator::getN,
     120              :         /* O                            */ &TlsValidator::getO,
     121              :         /* SIGNATURE_ALGORITHM          */ &TlsValidator::getSignatureAlgorithm,
     122              :         /* MD5_FINGERPRINT              */ &TlsValidator::getMd5Fingerprint,
     123              :         /* SHA1_FINGERPRINT             */ &TlsValidator::getSha1Fingerprint,
     124              :         /* PUBLIC_KEY_ID                */ &TlsValidator::getPublicKeyId,
     125              :         /* ISSUER_DN                    */ &TlsValidator::getIssuerDN,
     126              :         /* ISSUER_CN                    */ &TlsValidator::getIssuerCN,
     127              :         /* ISSUER_UID                   */ &TlsValidator::getIssuerUID,
     128              :         /* ISSUER_N                     */ &TlsValidator::getIssuerN,
     129              :         /* ISSUER_O                     */ &TlsValidator::getIssuerO,
     130              :         /* NEXT_EXPECTED_UPDATE_DATE    */ &TlsValidator::getIssuerDN, // TODO
     131              :         /* OUTGOING_SERVER              */ &TlsValidator::outgoingServer,
     132              :         /* IS_CA                        */ &TlsValidator::isCA,
     133              :     }};
     134              : 
     135              : const Matrix1D<TlsValidator::CertificateCheck, TlsValidator::CheckValuesType> TlsValidator::enforcedCheckType = {{
     136              :     /*      CertificateCheck                    Callback        */
     137              :     /*HAS_PRIVATE_KEY                  */ CheckValuesType::BOOLEAN,
     138              :     /*EXPIRED                          */ CheckValuesType::BOOLEAN,
     139              :     /*STRONG_SIGNING                   */ CheckValuesType::BOOLEAN,
     140              :     /*NOT_SELF_SIGNED                  */ CheckValuesType::BOOLEAN,
     141              :     /*KEY_MATCH                        */ CheckValuesType::BOOLEAN,
     142              :     /*PRIVATE_KEY_STORAGE_PERMISSION   */ CheckValuesType::BOOLEAN,
     143              :     /*PUBLIC_KEY_STORAGE_PERMISSION    */ CheckValuesType::BOOLEAN,
     144              :     /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ CheckValuesType::BOOLEAN,
     145              :     /*PUBLICKEY_DIRECTORY_PERMISSIONS  */ CheckValuesType::BOOLEAN,
     146              :     /*PRIVATE_KEY_STORAGE_LOCATION     */ CheckValuesType::BOOLEAN,
     147              :     /*PUBLIC_KEY_STORAGE_LOCATION      */ CheckValuesType::BOOLEAN,
     148              :     /*PRIVATE_KEY_SELINUX_ATTRIBUTES   */ CheckValuesType::BOOLEAN,
     149              :     /*PUBLIC_KEY_SELINUX_ATTRIBUTES    */ CheckValuesType::BOOLEAN,
     150              :     /*EXIST                            */ CheckValuesType::BOOLEAN,
     151              :     /*VALID                            */ CheckValuesType::BOOLEAN,
     152              :     /*VALID_AUTHORITY                  */ CheckValuesType::BOOLEAN,
     153              :     /*KNOWN_AUTHORITY                  */ CheckValuesType::BOOLEAN,
     154              :     /*NOT_REVOKED                      */ CheckValuesType::BOOLEAN,
     155              :     /*AUTHORITY_MISMATCH               */ CheckValuesType::BOOLEAN,
     156              :     /*UNEXPECTED_OWNER                 */ CheckValuesType::BOOLEAN,
     157              :     /*NOT_ACTIVATED                    */ CheckValuesType::BOOLEAN,
     158              : }};
     159              : 
     160              : const EnumClassNames<TlsValidator::CertificateCheck> TlsValidator::CertificateCheckNames = {{
     161              :     /*      CertificateCheck                                   Name */
     162              :     /*HAS_PRIVATE_KEY                  */ libjami::Certificate::ChecksNames::HAS_PRIVATE_KEY,
     163              :     /*EXPIRED                          */ libjami::Certificate::ChecksNames::EXPIRED,
     164              :     /*STRONG_SIGNING                   */ libjami::Certificate::ChecksNames::STRONG_SIGNING,
     165              :     /*NOT_SELF_SIGNED                  */ libjami::Certificate::ChecksNames::NOT_SELF_SIGNED,
     166              :     /*KEY_MATCH                        */ libjami::Certificate::ChecksNames::KEY_MATCH,
     167              :     /*PRIVATE_KEY_STORAGE_PERMISSION   */ libjami::Certificate::ChecksNames::PRIVATE_KEY_STORAGE_PERMISSION,
     168              :     /*PUBLIC_KEY_STORAGE_PERMISSION    */ libjami::Certificate::ChecksNames::PUBLIC_KEY_STORAGE_PERMISSION,
     169              :     /*PRIVATEKEY_DIRECTORY_PERMISSIONS */ libjami::Certificate::ChecksNames::PRIVATE_KEY_DIRECTORY_PERMISSIONS,
     170              :     /*PUBLICKEY_DIRECTORY_PERMISSIONS  */ libjami::Certificate::ChecksNames::PUBLIC_KEY_DIRECTORY_PERMISSIONS,
     171              :     /*PRIVATE_KEY_STORAGE_LOCATION     */ libjami::Certificate::ChecksNames::PRIVATE_KEY_STORAGE_LOCATION,
     172              :     /*PUBLIC_KEY_STORAGE_LOCATION      */ libjami::Certificate::ChecksNames::PUBLIC_KEY_STORAGE_LOCATION,
     173              :     /*PRIVATE_KEY_SELINUX_ATTRIBUTES   */ libjami::Certificate::ChecksNames::PRIVATE_KEY_SELINUX_ATTRIBUTES,
     174              :     /*PUBLIC_KEY_SELINUX_ATTRIBUTES    */ libjami::Certificate::ChecksNames::PUBLIC_KEY_SELINUX_ATTRIBUTES,
     175              :     /*EXIST                            */ libjami::Certificate::ChecksNames::EXIST,
     176              :     /*VALID                            */ libjami::Certificate::ChecksNames::VALID,
     177              :     /*VALID_AUTHORITY                  */ libjami::Certificate::ChecksNames::VALID_AUTHORITY,
     178              :     /*KNOWN_AUTHORITY                  */ libjami::Certificate::ChecksNames::KNOWN_AUTHORITY,
     179              :     /*NOT_REVOKED                      */ libjami::Certificate::ChecksNames::NOT_REVOKED,
     180              :     /*AUTHORITY_MISMATCH               */ libjami::Certificate::ChecksNames::AUTHORITY_MISMATCH,
     181              :     /*UNEXPECTED_OWNER                 */ libjami::Certificate::ChecksNames::UNEXPECTED_OWNER,
     182              :     /*NOT_ACTIVATED                    */ libjami::Certificate::ChecksNames::NOT_ACTIVATED,
     183              : }};
     184              : 
     185              : const EnumClassNames<TlsValidator::CertificateDetails> TlsValidator::CertificateDetailsNames = {{
     186              :     /* EXPIRATION_DATE              */ libjami::Certificate::DetailsNames::EXPIRATION_DATE,
     187              :     /* ACTIVATION_DATE              */ libjami::Certificate::DetailsNames::ACTIVATION_DATE,
     188              :     /* REQUIRE_PRIVATE_KEY_PASSWORD */ libjami::Certificate::DetailsNames::REQUIRE_PRIVATE_KEY_PASSWORD,
     189              :     /* PUBLIC_SIGNATURE             */ libjami::Certificate::DetailsNames::PUBLIC_SIGNATURE,
     190              :     /* VERSION_NUMBER               */ libjami::Certificate::DetailsNames::VERSION_NUMBER,
     191              :     /* SERIAL_NUMBER                */ libjami::Certificate::DetailsNames::SERIAL_NUMBER,
     192              :     /* ISSUER                       */ libjami::Certificate::DetailsNames::ISSUER,
     193              :     /* SUBJECT_KEY_ALGORITHM        */ libjami::Certificate::DetailsNames::SUBJECT_KEY_ALGORITHM,
     194              :     /* SUBJECT_KEY                  */ libjami::Certificate::DetailsNames::SUBJECT_KEY,
     195              :     /* CN                           */ libjami::Certificate::DetailsNames::CN,
     196              :     /* UID                          */ libjami::Certificate::DetailsNames::UID,
     197              :     /* N                            */ libjami::Certificate::DetailsNames::N,
     198              :     /* O                            */ libjami::Certificate::DetailsNames::O,
     199              :     /* SIGNATURE_ALGORITHM          */ libjami::Certificate::DetailsNames::SIGNATURE_ALGORITHM,
     200              :     /* MD5_FINGERPRINT              */ libjami::Certificate::DetailsNames::MD5_FINGERPRINT,
     201              :     /* SHA1_FINGERPRINT             */ libjami::Certificate::DetailsNames::SHA1_FINGERPRINT,
     202              :     /* PUBLIC_KEY_ID                */ libjami::Certificate::DetailsNames::PUBLIC_KEY_ID,
     203              :     /* ISSUER_DN                    */ libjami::Certificate::DetailsNames::ISSUER_DN,
     204              :     /* ISSUER_CN                    */ libjami::Certificate::DetailsNames::ISSUER_CN,
     205              :     /* ISSUER_UID                   */ libjami::Certificate::DetailsNames::ISSUER_UID,
     206              :     /* ISSUER_N                     */ libjami::Certificate::DetailsNames::ISSUER_N,
     207              :     /* ISSUER_O                     */ libjami::Certificate::DetailsNames::ISSUER_O,
     208              :     /* NEXT_EXPECTED_UPDATE_DATE    */ libjami::Certificate::DetailsNames::NEXT_EXPECTED_UPDATE_DATE,
     209              :     /* OUTGOING_SERVER              */ libjami::Certificate::DetailsNames::OUTGOING_SERVER,
     210              :     /* IS_CA                        */ libjami::Certificate::DetailsNames::IS_CA,
     211              : 
     212              : }};
     213              : 
     214              : const EnumClassNames<const TlsValidator::CheckValuesType> TlsValidator::CheckValuesTypeNames = {{
     215              :     /*   Type                            Name                          */
     216              :     /* BOOLEAN  */ libjami::Certificate::ChecksValuesTypesNames::BOOLEAN,
     217              :     /* ISO_DATE */ libjami::Certificate::ChecksValuesTypesNames::ISO_DATE,
     218              :     /* CUSTOM   */ libjami::Certificate::ChecksValuesTypesNames::CUSTOM,
     219              :     /* NUMBER   */ libjami::Certificate::ChecksValuesTypesNames::NUMBER,
     220              : }};
     221              : 
     222              : const Matrix2D<TlsValidator::CheckValuesType, TlsValidator::CheckValues, bool> TlsValidator::acceptedCheckValuesResult = {
     223              :     {
     224              :         /*   Type          PASSED    FAILED   UNSUPPORTED   ISO_DATE    CUSTOM    NUMBER */
     225              :         /* BOOLEAN  */ {{true, true, true, false, false, false}},
     226              :         /* ISO_DATE */ {{false, false, true, true, false, false}},
     227              :         /* CUSTOM   */ {{false, false, true, false, true, false}},
     228              :         /* NUMBER   */ {{false, false, true, false, false, true}},
     229              :     }};
     230              : 
     231            0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
     232            0 :                            const std::vector<std::vector<uint8_t>>& crtChain)
     233            0 :     : TlsValidator(certStore, std::make_shared<dht::crypto::Certificate>(crtChain.begin(), crtChain.end()))
     234            0 : {}
     235              : 
     236            0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
     237              :                            const std::string& certificate,
     238              :                            const std::string& privatekey,
     239              :                            const std::string& privatekeyPasswd,
     240            0 :                            const std::string& caList)
     241            0 :     : certStore_(certStore)
     242            0 :     , certificatePath_(certificate)
     243            0 :     , privateKeyPath_(privatekey)
     244            0 :     , caListPath_(caList)
     245            0 :     , certificateFound_(false)
     246              : {
     247            0 :     std::vector<uint8_t> certificate_raw;
     248              :     try {
     249            0 :         certificate_raw = fileutils::loadFile(certificatePath_);
     250            0 :         certificateFileFound_ = true;
     251            0 :     } catch (const std::exception& e) {
     252            0 :     }
     253              : 
     254            0 :     if (not certificate_raw.empty()) {
     255              :         try {
     256            0 :             x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
     257            0 :             certificateContent_ = x509crt_->getPacked();
     258            0 :             certificateFound_ = true;
     259            0 :         } catch (const std::exception& e) {
     260            0 :         }
     261              :     }
     262              : 
     263              :     try {
     264            0 :         auto privateKeyContent = fileutils::loadFile(privateKeyPath_);
     265            0 :         dht::crypto::PrivateKey key_tmp(privateKeyContent, privatekeyPasswd);
     266            0 :         privateKeyFound_ = true;
     267            0 :         privateKeyPassword_ = not privatekeyPasswd.empty();
     268            0 :         privateKeyMatch_ = key_tmp.getPublicKey().getId() == x509crt_->getId();
     269            0 :     } catch (const dht::crypto::DecryptError& d) {
     270              :         // If we encounter a DecryptError, it means the private key exists and is encrypted,
     271              :         // otherwise we would get some other exception.
     272            0 :         JAMI_WARNING("decryption error: {}", d.what());
     273            0 :         privateKeyFound_ = true;
     274            0 :         privateKeyPassword_ = true;
     275            0 :     } catch (const std::exception& e) {
     276            0 :         JAMI_WARNING("creation failed: {}", e.what());
     277            0 :     }
     278            0 : }
     279              : 
     280            0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<uint8_t>& certificate_raw)
     281            0 :     : certStore_(certStore)
     282              : {
     283              :     try {
     284            0 :         x509crt_ = std::make_shared<dht::crypto::Certificate>(certificate_raw);
     285            0 :         certificateContent_ = x509crt_->getPacked();
     286            0 :         certificateFound_ = true;
     287            0 :     } catch (const std::exception& e) {
     288            0 :         throw TlsValidatorException("Unable to load certificate");
     289            0 :     }
     290            0 : }
     291              : 
     292            0 : TlsValidator::TlsValidator(const dhtnet::tls::CertificateStore& certStore,
     293            0 :                            const std::shared_ptr<dht::crypto::Certificate>& crt)
     294            0 :     : certStore_(certStore)
     295            0 :     , certificateFound_(true)
     296              : {
     297              :     try {
     298            0 :         if (not crt)
     299            0 :             throw std::invalid_argument("Certificate must be set");
     300            0 :         x509crt_ = crt;
     301            0 :         certificateContent_ = x509crt_->getPacked();
     302            0 :     } catch (const std::exception& e) {
     303            0 :         throw TlsValidatorException("Unable to load certificate");
     304            0 :     }
     305            0 : }
     306              : 
     307            0 : TlsValidator::~TlsValidator() {}
     308              : 
     309              : /**
     310              :  * This method convert results into validated strings
     311              :  *
     312              :  * @todo The date should be validated, this is currently not an issue
     313              :  */
     314              : std::string
     315            0 : TlsValidator::getStringValue([[maybe_unused]] const TlsValidator::CertificateCheck check,
     316              :                              const TlsValidator::CheckResult& result)
     317              : {
     318            0 :     assert(acceptedCheckValuesResult[enforcedCheckType[check]][result.first]);
     319              : 
     320            0 :     switch (result.first) {
     321            0 :     case CheckValues::PASSED:
     322              :     case CheckValues::FAILED:
     323              :     case CheckValues::UNSUPPORTED:
     324            0 :         return std::string(CheckValuesNames[result.first]);
     325            0 :     case CheckValues::ISO_DATE:
     326              :         // TODO validate date
     327              :         // return CheckValues::FAILED;
     328            0 :         return result.second;
     329            0 :     case CheckValues::NUMBER:
     330              :         // TODO Validate numbers
     331              :     case CheckValues::CUSTOM:
     332            0 :         return result.second;
     333            0 :     default:
     334              :         // Consider any other case (such as forced int->CheckValues casting) as failed
     335            0 :         return std::string(CheckValuesNames[CheckValues::FAILED]);
     336              :     };
     337              : }
     338              : 
     339              : /**
     340              :  * Check if all boolean check passed
     341              :  * return true if there was no ::FAILED checks
     342              :  *
     343              :  * Checks functions are not "const", so this function isn't
     344              :  */
     345              : bool
     346            0 : TlsValidator::isValid(bool verbose)
     347              : {
     348            0 :     for (const CertificateCheck check : Matrix0D<CertificateCheck>()) {
     349            0 :         if (enforcedCheckType[check] == CheckValuesType::BOOLEAN) {
     350            0 :             if (((this->*(checkCallback[check]))()).first == CheckValues::FAILED) {
     351            0 :                 if (verbose)
     352            0 :                     JAMI_WARNING("Check failed: {}", CertificateCheckNames[check]);
     353            0 :                 return false;
     354              :             }
     355              :         }
     356              :     }
     357            0 :     return true;
     358              : }
     359              : 
     360              : /**
     361              :  * Convert all checks results into a string map
     362              :  */
     363              : std::map<std::string, std::string>
     364            0 : TlsValidator::getSerializedChecks()
     365              : {
     366            0 :     std::map<std::string, std::string> ret;
     367            0 :     if (not certificateFound_) {
     368              :         // Instead of checking `certificateFound` everywhere, handle it once
     369            0 :         ret[std::string(CertificateCheckNames[CertificateCheck::EXIST])] = getStringValue(CertificateCheck::EXIST,
     370            0 :                                                                                           exist());
     371              :     } else {
     372            0 :         for (const CertificateCheck check : Matrix0D<CertificateCheck>())
     373            0 :             ret[std::string(CertificateCheckNames[check])] = getStringValue(check, (this->*(checkCallback[check]))());
     374              :     }
     375              : 
     376            0 :     return ret;
     377            0 : }
     378              : 
     379              : /**
     380              :  * Get a map with all common certificate details
     381              :  */
     382              : std::map<std::string, std::string>
     383            0 : TlsValidator::getSerializedDetails()
     384              : {
     385            0 :     std::map<std::string, std::string> ret;
     386            0 :     if (certificateFound_) {
     387            0 :         for (const CertificateDetails& det : Matrix0D<CertificateDetails>()) {
     388            0 :             const CheckResult r = (this->*(getterCallback[det]))();
     389            0 :             std::string val;
     390              :             // TODO move this to a fuction
     391            0 :             switch (r.first) {
     392            0 :             case CheckValues::PASSED:
     393              :             case CheckValues::FAILED:
     394              :             case CheckValues::UNSUPPORTED:
     395            0 :                 val = CheckValuesNames[r.first];
     396            0 :                 break;
     397            0 :             case CheckValues::ISO_DATE:
     398              :                 // TODO validate date
     399              :             case CheckValues::NUMBER:
     400              :                 // TODO Validate numbers
     401              :             case CheckValues::CUSTOM:
     402              :             default:
     403            0 :                 val = r.second;
     404            0 :                 break;
     405              :             }
     406            0 :             ret[std::string(CertificateDetailsNames[det])] = val;
     407            0 :         }
     408              :     }
     409            0 :     return ret;
     410            0 : }
     411              : 
     412              : /**
     413              :  * Helper method to return UNSUPPORTED when an error is detected
     414              :  */
     415              : static TlsValidator::CheckResult
     416            0 : checkError(int err, char* copy_buffer, size_t size)
     417              : {
     418            0 :     return TlsValidator::TlsValidator::CheckResult(err == GNUTLS_E_SUCCESS ? TlsValidator::CheckValues::CUSTOM
     419              :                                                                            : TlsValidator::CheckValues::UNSUPPORTED,
     420            0 :                                                    err == GNUTLS_E_SUCCESS ? std::string(copy_buffer, size) : "");
     421              : }
     422              : 
     423              : /**
     424              :  * Convert a time_t to an ISO date string
     425              :  */
     426              : static TlsValidator::CheckResult
     427            0 : formatDate(const time_t time)
     428              : {
     429              :     char buffer[12];
     430            0 :     struct tm* timeinfo = localtime(&time);
     431            0 :     strftime(buffer, sizeof(buffer), "%F", timeinfo);
     432            0 :     return TlsValidator::CheckResult(TlsValidator::CheckValues::ISO_DATE, buffer);
     433              : }
     434              : 
     435              : /**
     436              :  * Helper method to return UNSUPPORTED when an error is detected
     437              :  *
     438              :  * This method also convert the output to binary
     439              :  */
     440              : static TlsValidator::CheckResult
     441            0 : checkBinaryError(int err, char* copy_buffer, size_t resultSize)
     442              : {
     443            0 :     if (err == GNUTLS_E_SUCCESS)
     444            0 :         return TlsValidator::CheckResult(TlsValidator::CheckValues::CUSTOM,
     445            0 :                                          dht::toHex(reinterpret_cast<uint8_t*>(copy_buffer), resultSize));
     446              :     else
     447            0 :         return TlsValidator::CheckResult(TlsValidator::CheckValues::UNSUPPORTED, "");
     448              : }
     449              : 
     450              : /**
     451              :  * Check if a certificate has been signed with the authority
     452              :  */
     453              : unsigned int
     454            0 : TlsValidator::compareToCa()
     455              : {
     456              :     // Don't check unless the certificate changed
     457            0 :     if (caChecked_)
     458            0 :         return caValidationOutput_;
     459              : 
     460              :     // build the CA trusted list
     461              :     gnutls_x509_trust_list_t trust;
     462            0 :     gnutls_x509_trust_list_init(&trust, 0);
     463              : 
     464            0 :     auto root_cas = certStore_.getTrustedCertificates();
     465            0 :     auto err = gnutls_x509_trust_list_add_cas(trust, root_cas.data(), root_cas.size(), 0);
     466            0 :     if (err)
     467            0 :         JAMI_WARNING("gnutls_x509_trust_list_add_cas failed: {}", gnutls_strerror(err));
     468              : 
     469            0 :     if (not caListPath_.empty()) {
     470            0 :         if (std::filesystem::is_directory(caListPath_))
     471            0 :             gnutls_x509_trust_list_add_trust_dir(trust, caListPath_.c_str(), nullptr, GNUTLS_X509_FMT_PEM, 0, 0);
     472              :         else
     473            0 :             gnutls_x509_trust_list_add_trust_file(trust, caListPath_.c_str(), nullptr, GNUTLS_X509_FMT_PEM, 0, 0);
     474              :     }
     475              : 
     476              :     // build the certificate chain
     477            0 :     auto crts = x509crt_->getChain();
     478            0 :     err = gnutls_x509_trust_list_verify_crt2(trust,
     479              :                                              crts.data(),
     480            0 :                                              crts.size(),
     481              :                                              nullptr,
     482              :                                              0,
     483              :                                              GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_MEDIUM),
     484              :                                              &caValidationOutput_,
     485              :                                              nullptr);
     486              : 
     487            0 :     gnutls_x509_trust_list_deinit(trust, true);
     488              : 
     489            0 :     if (err) {
     490            0 :         JAMI_WARNING("gnutls_x509_trust_list_verify_crt2 failed: {}", gnutls_strerror(err));
     491            0 :         return GNUTLS_CERT_SIGNER_NOT_FOUND;
     492              :     }
     493              : 
     494            0 :     caChecked_ = true;
     495            0 :     return caValidationOutput_;
     496            0 : }
     497              : 
     498              : #if 0 // disabled, see .h for reason
     499              : /**
     500              :  * Verify if a hostname is valid
     501              :  *
     502              :  * @warning This function is blocking
     503              :  *
     504              :  * Mainly based on Fedora Defensive Coding tutorial
     505              :  * https://docs.fedoraproject.org/en-US/Fedora_Security_Team/1/html/Defensive_Coding/sect-Defensive_Coding-TLS-Client-GNUTLS.html
     506              :  */
     507              : int TlsValidator::verifyHostnameCertificate(const std::string& host, const uint16_t port)
     508              : {
     509              :     int err, arg, res = -1;
     510              :     unsigned int status = (unsigned) -1;
     511              :     const char *errptr = nullptr;
     512              :     gnutls_session_t session = nullptr;
     513              :     gnutls_certificate_credentials_t cred = nullptr;
     514              :     unsigned int certslen = 0;
     515              :     const gnutls_datum_t *certs = nullptr;
     516              :     gnutls_x509_crt_t cert = nullptr;
     517              : 
     518              :     char buf[4096];
     519              :     int sockfd;
     520              :     struct sockaddr_in name;
     521              :     struct hostent *hostinfo;
     522              :     const int one = 1;
     523              :     fd_set fdset;
     524              :     struct timeval tv;
     525              : 
     526              :     if (!host.size() || !port) {
     527              :         JAMI_ERROR("Wrong parameters used - host {}, port {}.", host, port);
     528              :         return res;
     529              :     }
     530              : 
     531              :     /* Create the socket. */
     532              :     sockfd = socket (PF_INET, SOCK_STREAM, 0);
     533              :     if (sockfd < 0) {
     534              :         JAMI_ERROR("Unable to create socket.");
     535              :         return res;
     536              :     }
     537              :     /* Set non-blocking so we can dected timeouts. */
     538              :     arg = fcntl(sockfd, F_GETFL, nullptr);
     539              :     if (arg < 0)
     540              :         goto out;
     541              :     arg |= O_NONBLOCK;
     542              :     if (fcntl(sockfd, F_SETFL, arg) < 0)
     543              :         goto out;
     544              : 
     545              :     /* Give the socket a name. */
     546              :     memset(&name, 0, sizeof(name));
     547              :     name.sin_family = AF_INET;
     548              :     name.sin_port = htons(port);
     549              :     hostinfo = gethostbyname(host.c_str());
     550              :     if (hostinfo == nullptr) {
     551              :         JAMI_ERROR("Unknown host {}.", host);
     552              :         goto out;
     553              :     }
     554              :     name.sin_addr = *(struct in_addr *)hostinfo->h_addr;
     555              :     /* Connect to the address specified in name struct. */
     556              :     err = connect(sockfd, (struct sockaddr *)&name, sizeof(name));
     557              :     if (err < 0) {
     558              :         /* Connection in progress, use select to see if timeout is reached. */
     559              :         if (errno == EINPROGRESS) {
     560              :             do {
     561              :                 FD_ZERO(&fdset);
     562              :                 FD_SET(sockfd, &fdset);
     563              :                 tv.tv_sec = 10;     // 10 second timeout
     564              :                 tv.tv_usec = 0;
     565              :                 err = select(sockfd + 1, nullptr, &fdset, nullptr, &tv);
     566              :                 if (err < 0 && errno != EINTR) {
     567              :                     JAMI_ERROR("Unable to connect to hostname {} at port {}", host, port);
     568              :                     goto out;
     569              :                 } else if (err > 0) {
     570              :                     /* Select returned, if so_error is clean we are ready. */
     571              :                     int so_error;
     572              :                     socklen_t len = sizeof(so_error);
     573              :                     getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len);
     574              : 
     575              :                     if (so_error) {
     576              :                         JAMI_ERROR("Connection delayed.");
     577              :                         goto out;
     578              :                     }
     579              :                     break;  // exit do-while loop
     580              :                 } else {
     581              :                     JAMI_ERROR("Connection timeout.");
     582              :                     goto out;
     583              :                 }
     584              :             } while(1);
     585              :         } else {
     586              :             JAMI_ERROR("Unable to connect to hostname {} at port {}", host, port);
     587              :             goto out;
     588              :         }
     589              :     }
     590              :     /* Set the socked blocking again. */
     591              :     arg = fcntl(sockfd, F_GETFL, nullptr);
     592              :     if (arg < 0)
     593              :         goto out;
     594              :     arg &= ~O_NONBLOCK;
     595              :     if (fcntl(sockfd, F_SETFL, arg) < 0)
     596              :         goto out;
     597              : 
     598              :     /* Disable Nagle algorithm that slows down the SSL handshake. */
     599              :     err = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
     600              :     if (err < 0) {
     601              :         JAMI_ERROR("Unable to set TCP_NODELAY.");
     602              :         goto out;
     603              :     }
     604              : 
     605              : 
     606              :     /* Load the trusted CA certificates. */
     607              :     err = gnutls_certificate_allocate_credentials(&cred);
     608              :     if (err != GNUTLS_E_SUCCESS) {
     609              :         JAMI_ERROR("Unable to allocate credentials - {}", gnutls_strerror(err));
     610              :         goto out;
     611              :     }
     612              :     err = gnutls_certificate_set_x509_system_trust(cred);
     613              :     if (err != GNUTLS_E_SUCCESS) {
     614              :         JAMI_ERROR("Unable to load credentials.");
     615              :         goto out;
     616              :     }
     617              : 
     618              :     /* Create the session object. */
     619              :     err = gnutls_init(&session, GNUTLS_CLIENT);
     620              :     if (err != GNUTLS_E_SUCCESS) {
     621              :         JAMI_ERROR("Unable to init session -{}", gnutls_strerror(err));
     622              :         goto out;
     623              :     }
     624              : 
     625              :     /* Configure the cipher preferences. The default set should be good enough. */
     626              :     err = gnutls_priority_set_direct(session, "NORMAL", &errptr);
     627              :     if (err != GNUTLS_E_SUCCESS) {
     628              :         JAMI_ERROR("Unable to set up ciphers - {} ({})", gnutls_strerror(err), errptr);
     629              :         goto out;
     630              :     }
     631              : 
     632              :     /* Install the trusted certificates. */
     633              :     err = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
     634              :     if (err != GNUTLS_E_SUCCESS) {
     635              :         JAMI_ERROR("Unable to set up credentials - {}", gnutls_strerror(err));
     636              :         goto out;
     637              :     }
     638              : 
     639              :     /* Associate the socket with the session object and set the server name. */
     640              :     gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) (uintptr_t) sockfd);
     641              :     err = gnutls_server_name_set(session, GNUTLS_NAME_DNS, host.c_str(), host.size());
     642              :     if (err != GNUTLS_E_SUCCESS) {
     643              :         JAMI_ERROR("Unable to set server name - {}", gnutls_strerror(err));
     644              :         goto out;
     645              :     }
     646              : 
     647              :     /* Establish the connection. */
     648              :     err = gnutls_handshake(session);
     649              :     if (err != GNUTLS_E_SUCCESS) {
     650              :         JAMI_ERROR("Handshake failed - {}", gnutls_strerror(err));
     651              :         goto out;
     652              :     }
     653              :     /* Obtain the server certificate chain. The server certificate
     654              :      * itself is stored in the first element of the array. */
     655              :     certs = gnutls_certificate_get_peers(session, &certslen);
     656              :     if (certs == nullptr || certslen == 0) {
     657              :         JAMI_ERROR("Unable to obtain peer certificate - {}", gnutls_strerror(err));
     658              :         goto out;
     659              :     }
     660              : 
     661              :     /* Validate the certificate chain. */
     662              :     err = gnutls_certificate_verify_peers2(session, &status);
     663              :     if (err != GNUTLS_E_SUCCESS) {
     664              :         JAMI_ERROR("Unable to verify the certificate chain - {}", gnutls_strerror(err));
     665              :         goto out;
     666              :     }
     667              :     if (status != 0) {
     668              :         gnutls_datum_t msg;
     669              : #if GNUTLS_VERSION_AT_LEAST_3_1_4
     670              :         int type = gnutls_certificate_type_get(session);
     671              :         err = gnutls_certificate_verification_status_print(status, type, &out, 0);
     672              : #else
     673              :         err = -1;
     674              : #endif
     675              :         if (err == 0) {
     676              :             JAMI_ERROR("Certificate validation failed - {}", msg.data);
     677              :             gnutls_free(msg.data);
     678              :             goto out;
     679              :         } else {
     680              :             JAMI_ERROR("Certificate validation failed with code 0x{:x}.", status);
     681              :             goto out;
     682              :         }
     683              :     }
     684              : 
     685              :     /* Match the peer certificate against the hostname.
     686              :      * We can only obtain a set of DER-encoded certificates from the
     687              :      * session object, so we have to re-parse the peer certificate into
     688              :      * a certificate object. */
     689              : 
     690              :     err = gnutls_x509_crt_init(&cert);
     691              :     if (err != GNUTLS_E_SUCCESS) {
     692              :         JAMI_ERROR("Unable to init certificate - {}", gnutls_strerror(err));
     693              :         goto out;
     694              :     }
     695              : 
     696              :     /* The peer certificate is the first certificate in the list. */
     697              :     err = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM);
     698              :     if (err != GNUTLS_E_SUCCESS)
     699              :         err = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER);
     700              :     if (err != GNUTLS_E_SUCCESS) {
     701              :         JAMI_ERROR("Unable to read peer certificate - {}", gnutls_strerror(err));
     702              :         goto out;
     703              :     }
     704              :     /* Finally check if the hostnames match. */
     705              :     err = gnutls_x509_crt_check_hostname(cert, host.c_str());
     706              :     if (err == 0) {
     707              :         JAMI_ERROR("Hostname {} does not match certificate.", host);
     708              :         goto out;
     709              :     }
     710              : 
     711              :     /* Try sending and receiving some data through. */
     712              :     snprintf(buf, sizeof(buf), "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", host.c_str());
     713              :     err = gnutls_record_send(session, buf, strlen(buf));
     714              :     if (err < 0) {
     715              :         JAMI_ERROR("Send failed - {}", gnutls_strerror(err));
     716              :         goto out;
     717              :     }
     718              :     err = gnutls_record_recv(session, buf, sizeof(buf));
     719              :     if (err < 0) {
     720              :         JAMI_ERROR("Recv failed - {}", gnutls_strerror(err));
     721              :         goto out;
     722              :     }
     723              : 
     724              :     JAMI_LOG("Hostname {} seems to point to a valid server.", host);
     725              :     res = 0;
     726              : out:
     727              :     if (session) {
     728              :         gnutls_bye(session, GNUTLS_SHUT_RDWR);
     729              :         gnutls_deinit(session);
     730              :     }
     731              :     if (cert)
     732              :         gnutls_x509_crt_deinit(cert);
     733              :     if (cred)
     734              :         gnutls_certificate_free_credentials(cred);
     735              :     close(sockfd);
     736              :     return res;
     737              : }
     738              : #endif
     739              : 
     740              : /**
     741              :  * Check if the Validator have access to a private key
     742              :  */
     743              : TlsValidator::CheckResult
     744            0 : TlsValidator::hasPrivateKey()
     745              : {
     746            0 :     if (privateKeyFound_)
     747            0 :         return TlsValidator::CheckResult(CheckValues::PASSED, "");
     748              : 
     749              :     try {
     750            0 :         dht::crypto::PrivateKey key_tmp(certificateContent_);
     751            0 :     } catch (const std::exception& e) {
     752            0 :         return CheckResult(CheckValues::FAILED, e.what());
     753            0 :     }
     754              : 
     755            0 :     JAMI_LOG("Key from {} seems valid.", certificatePath_);
     756            0 :     return CheckResult(CheckValues::PASSED, "");
     757              : }
     758              : 
     759              : /**
     760              :  * Check if the certificate is not expired
     761              :  *
     762              :  * The double negative is used because all boolean checks need to have
     763              :  * a consistent return value semantic
     764              :  *
     765              :  * @fixme Handle both "with ca" and "without ca" case
     766              :  */
     767              : TlsValidator::CheckResult
     768            0 : TlsValidator::notExpired()
     769              : {
     770            0 :     if (exist().first == CheckValues::FAILED)
     771            0 :         TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     772              : 
     773              :     // time_t expirationTime = gnutls_x509_crt_get_expiration_time(cert);
     774            0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_EXPIRED ? CheckValues::FAILED : CheckValues::PASSED,
     775            0 :                                      "");
     776              : }
     777              : 
     778              : /**
     779              :  * If the activation value is in the past
     780              :  *
     781              :  * @fixme Handle both "with ca" and "without ca" case
     782              :  */
     783              : TlsValidator::CheckResult
     784            0 : TlsValidator::activated()
     785              : {
     786            0 :     if (exist().first == CheckValues::FAILED)
     787            0 :         TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     788              : 
     789              :     // time_t activationTime = gnutls_x509_crt_get_activation_time(cert);
     790            0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_NOT_ACTIVATED ? CheckValues::FAILED
     791              :                                                                                : CheckValues::PASSED,
     792            0 :                                      "");
     793              : }
     794              : 
     795              : /**
     796              :  * If the algorithm used to sign the certificate is considered weak by modern
     797              :  * standard
     798              :  */
     799              : TlsValidator::CheckResult
     800            0 : TlsValidator::strongSigning()
     801              : {
     802            0 :     if (exist().first == CheckValues::FAILED)
     803            0 :         TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     804              : 
     805              :     // Doesn't seem to have the same value as
     806              :     // certtool  --infile /home/etudiant/Téléchargements/mynsauser.pem --key-inf
     807              :     // TODO figure out why
     808            0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_INSECURE_ALGORITHM ? CheckValues::FAILED
     809              :                                                                                     : CheckValues::PASSED,
     810            0 :                                      "");
     811              : }
     812              : 
     813              : /**
     814              :  * The certificate is not self signed
     815              :  */
     816              : TlsValidator::CheckResult
     817            0 : TlsValidator::notSelfSigned()
     818              : {
     819            0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     820              : }
     821              : 
     822              : /**
     823              :  * The provided key can be used along with the certificate
     824              :  */
     825              : TlsValidator::CheckResult
     826            0 : TlsValidator::keyMatch()
     827              : {
     828            0 :     if (exist().first == CheckValues::FAILED)
     829            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     830              : 
     831            0 :     if (not privateKeyFound_)
     832            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     833            0 :     return TlsValidator::CheckResult(privateKeyMatch_ ? CheckValues::PASSED : CheckValues::FAILED, "");
     834              : }
     835              : 
     836              : TlsValidator::CheckResult
     837            0 : TlsValidator::privateKeyStoragePermissions()
     838              : {
     839              :     struct stat statbuf;
     840            0 :     int err = stat(privateKeyPath_.c_str(), &statbuf);
     841            0 :     if (err)
     842            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     843              : 
     844              :     // clang-format off
     845              :     return TlsValidator::CheckResult(
     846            0 :         (statbuf.st_mode & S_IFREG) && /* Regular file only */
     847              :         /*                          READ                      WRITE                            EXECUTE          */
     848            0 :         /* Owner */    ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */     && !(statbuf.st_mode & S_IXUSR))
     849            0 :         /* Group */ && (!(statbuf.st_mode & S_IRGRP) && !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
     850            0 :         /* Other */ && (!(statbuf.st_mode & S_IROTH) && !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
     851            0 :         ? CheckValues::PASSED:CheckValues::FAILED, "");
     852              :     // clang-format on
     853              : }
     854              : 
     855              : TlsValidator::CheckResult
     856            0 : TlsValidator::publicKeyStoragePermissions()
     857              : {
     858              :     struct stat statbuf;
     859            0 :     int err = stat(certificatePath_.c_str(), &statbuf);
     860            0 :     if (err)
     861            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     862              : 
     863              :     // clang-format off
     864              :     return TlsValidator::CheckResult(
     865            0 :         (statbuf.st_mode & S_IFREG) && /* Regular file only */
     866              :         /*                          READ                      WRITE                            EXECUTE          */
     867            0 :         /* Owner */    ( (statbuf.st_mode & S_IRUSR) /* write is not relevant */   && !(statbuf.st_mode & S_IXUSR))
     868            0 :         /* Group */ && ( /* read is not relevant */   !(statbuf.st_mode & S_IWGRP) && !(statbuf.st_mode & S_IXGRP))
     869            0 :         /* Other */ && ( /* read is not relevant */   !(statbuf.st_mode & S_IWOTH) && !(statbuf.st_mode & S_IXOTH))
     870            0 :         ? CheckValues::PASSED:CheckValues::FAILED, "");
     871              :     // clang-format on
     872              : }
     873              : 
     874              : TlsValidator::CheckResult
     875            0 : TlsValidator::privateKeyDirectoryPermissions()
     876              : {
     877              :     namespace fs = std::filesystem;
     878              : 
     879            0 :     if (privateKeyPath_.empty())
     880            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     881              : 
     882            0 :     fs::path dir = fs::path(privateKeyPath_).parent_path();
     883            0 :     if (dir.empty())
     884            0 :         dir = fs::path("."); // mimic dirname() behavior when no separator
     885              : 
     886            0 :     std::error_code ec;
     887            0 :     auto st = fs::status(dir, ec);
     888            0 :     if (ec)
     889            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     890              : 
     891            0 :     if (!fs::is_directory(st))
     892            0 :         return TlsValidator::CheckResult(CheckValues::FAILED, "");
     893              : 
     894            0 :     auto perm = st.permissions();
     895              : 
     896            0 :     bool ownerRead = (perm & fs::perms::owner_read) != fs::perms::none;
     897            0 :     bool ownerExec = (perm & fs::perms::owner_exec) != fs::perms::none;
     898            0 :     bool groupAny = (perm & (fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec))
     899            0 :                     != fs::perms::none;
     900            0 :     bool othersAny = (perm & (fs::perms::others_read | fs::perms::others_write | fs::perms::others_exec))
     901            0 :                      != fs::perms::none;
     902              : 
     903            0 :     bool ok = ownerRead && ownerExec && !groupAny && !othersAny;
     904            0 :     return TlsValidator::CheckResult(ok ? CheckValues::PASSED : CheckValues::FAILED, "");
     905            0 : }
     906              : 
     907              : TlsValidator::CheckResult
     908            0 : TlsValidator::publicKeyDirectoryPermissions()
     909              : {
     910              :     namespace fs = std::filesystem;
     911              : 
     912            0 :     if (certificatePath_.empty())
     913            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     914              : 
     915            0 :     fs::path dir = fs::path(certificatePath_).parent_path();
     916            0 :     if (dir.empty())
     917            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     918              : 
     919            0 :     std::error_code ec;
     920            0 :     auto st = fs::status(dir, ec);
     921            0 :     if (ec)
     922            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     923              : 
     924            0 :     if (!fs::is_directory(st))
     925            0 :         return TlsValidator::CheckResult(CheckValues::FAILED, "");
     926              : 
     927            0 :     auto perm = st.permissions();
     928              : 
     929            0 :     bool ownerRead = (perm & fs::perms::owner_read) != fs::perms::none;
     930            0 :     bool ownerExec = (perm & fs::perms::owner_exec) != fs::perms::none;
     931            0 :     bool groupAny = (perm & (fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec))
     932            0 :                     != fs::perms::none;
     933            0 :     bool othersAny = (perm & (fs::perms::others_read | fs::perms::others_write | fs::perms::others_exec))
     934            0 :                      != fs::perms::none;
     935              : 
     936            0 :     bool ok = ownerRead && ownerExec && !groupAny && !othersAny;
     937            0 :     return TlsValidator::CheckResult(ok ? CheckValues::PASSED : CheckValues::FAILED, "");
     938            0 : }
     939              : 
     940              : /**
     941              :  * Certificate should be located in specific path on some operating systems
     942              :  */
     943              : TlsValidator::CheckResult
     944            0 : TlsValidator::privateKeyStorageLocation()
     945              : {
     946              :     // TODO
     947            0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     948              : }
     949              : 
     950              : /**
     951              :  * Certificate should be located in specific path on some operating systems
     952              :  */
     953              : TlsValidator::CheckResult
     954            0 : TlsValidator::publicKeyStorageLocation()
     955              : {
     956              :     // TODO
     957            0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     958              : }
     959              : 
     960              : /**
     961              :  * SELinux provide additional key protection mechanism
     962              :  */
     963              : TlsValidator::CheckResult
     964            0 : TlsValidator::privateKeySelinuxAttributes()
     965              : {
     966              :     // TODO
     967            0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     968              : }
     969              : 
     970              : /**
     971              :  * SELinux provide additional key protection mechanism
     972              :  */
     973              : TlsValidator::CheckResult
     974            0 : TlsValidator::publicKeySelinuxAttributes()
     975              : {
     976              :     // TODO
     977            0 :     return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
     978              : }
     979              : 
     980              : /**
     981              :  * If the key need decryption
     982              :  *
     983              :  * Double factor authentication is recommended
     984              :  */
     985              : TlsValidator::CheckResult
     986            0 : TlsValidator::requirePrivateKeyPassword()
     987              : {
     988            0 :     return TlsValidator::CheckResult(privateKeyPassword_ ? CheckValues::PASSED : CheckValues::FAILED, "");
     989              : }
     990              : /**
     991              :  * The CA and certificate provide conflicting ownership information
     992              :  */
     993              : TlsValidator::CheckResult
     994            0 : TlsValidator::expectedOwner()
     995              : {
     996            0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_UNEXPECTED_OWNER ? CheckValues::FAILED
     997              :                                                                                   : CheckValues::PASSED,
     998            0 :                                      "");
     999              : }
    1000              : 
    1001              : /**
    1002              :  * The file has been found
    1003              :  */
    1004              : TlsValidator::CheckResult
    1005            0 : TlsValidator::exist()
    1006              : {
    1007            0 :     return TlsValidator::CheckResult((certificateFound_ or certificateFileFound_) ? CheckValues::PASSED
    1008              :                                                                                   : CheckValues::FAILED,
    1009            0 :                                      "");
    1010              : }
    1011              : 
    1012              : /**
    1013              :  * The certificate is invalid compared to the authority
    1014              :  *
    1015              :  * @todo Handle case when there is facultative authority, such as DHT
    1016              :  */
    1017              : TlsValidator::CheckResult
    1018            0 : TlsValidator::valid()
    1019              : {
    1020            0 :     return TlsValidator::CheckResult(certificateFound_ ? CheckValues::PASSED : CheckValues::FAILED, "");
    1021              : }
    1022              : 
    1023              : /**
    1024              :  * The provided authority is invalid
    1025              :  */
    1026              : TlsValidator::CheckResult
    1027            0 : TlsValidator::validAuthority()
    1028              : {
    1029              :     // TODO Merge with either above or below
    1030            0 :     return TlsValidator::CheckResult((compareToCa() & GNUTLS_CERT_SIGNER_NOT_FOUND) ? CheckValues::FAILED
    1031              :                                                                                     : CheckValues::PASSED,
    1032            0 :                                      "");
    1033              : }
    1034              : 
    1035              : /**
    1036              :  * Check if the authority match the certificate
    1037              :  */
    1038              : TlsValidator::CheckResult
    1039            0 : TlsValidator::authorityMatch()
    1040              : {
    1041            0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_SIGNER_NOT_CA ? CheckValues::FAILED
    1042              :                                                                                : CheckValues::PASSED,
    1043            0 :                                      "");
    1044              : }
    1045              : 
    1046              : /**
    1047              :  * When an account require an authority known by the system (like /usr/share/ssl/certs)
    1048              :  * then the whole chain of trust need be to checked
    1049              :  *
    1050              :  * @fixme port crypto_cert_load_trusted
    1051              :  * @fixme add account settings
    1052              :  * @todo implement the check
    1053              :  */
    1054              : TlsValidator::CheckResult
    1055            0 : TlsValidator::knownAuthority()
    1056              : {
    1057              :     // TODO need a new boolean account setting "require trusted authority" or something defaulting
    1058              :     // to true using GNUTLS_CERT_SIGNER_NOT_FOUND is a temporary placeholder as it is close enough
    1059            0 :     return TlsValidator::CheckResult(compareToCa() & GNUTLS_CERT_SIGNER_NOT_FOUND ? CheckValues::FAILED
    1060              :                                                                                   : CheckValues::PASSED,
    1061            0 :                                      "");
    1062              : }
    1063              : 
    1064              : /**
    1065              :  * Check if the certificate has been revoked
    1066              :  */
    1067              : TlsValidator::CheckResult
    1068            0 : TlsValidator::notRevoked()
    1069              : {
    1070            0 :     return TlsValidator::CheckResult((compareToCa() & GNUTLS_CERT_REVOKED)
    1071            0 :                                              || (compareToCa() & GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE)
    1072            0 :                                          ? CheckValues::FAILED
    1073              :                                          : CheckValues::PASSED,
    1074            0 :                                      "");
    1075              : }
    1076              : 
    1077              : /**
    1078              :  * A certificate authority has been provided
    1079              :  */
    1080              : bool
    1081            0 : TlsValidator::hasCa() const
    1082              : {
    1083            0 :     return (x509crt_ and x509crt_->issuer); /* or
    1084              :             (caCert_ != nullptr and caCert_->certificateFound_);*/
    1085              : }
    1086              : 
    1087              : //
    1088              : // Certificate details
    1089              : //
    1090              : 
    1091              : // TODO gnutls_x509_crl_get_this_update
    1092              : 
    1093              : /**
    1094              :  * An hexadecimal representation of the signature
    1095              :  */
    1096              : TlsValidator::CheckResult
    1097            0 : TlsValidator::getPublicSignature()
    1098              : {
    1099            0 :     size_t resultSize = sizeof(copy_buffer);
    1100            0 :     int err = gnutls_x509_crt_get_signature(x509crt_->cert, copy_buffer, &resultSize);
    1101            0 :     return checkBinaryError(err, copy_buffer, resultSize);
    1102              : }
    1103              : 
    1104              : /**
    1105              :  * Return the certificate version
    1106              :  */
    1107              : TlsValidator::CheckResult
    1108            0 : TlsValidator::getVersionNumber()
    1109              : {
    1110            0 :     int version = gnutls_x509_crt_get_version(x509crt_->cert);
    1111            0 :     if (version < 0)
    1112            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1113              : 
    1114            0 :     std::ostringstream convert;
    1115            0 :     convert << version;
    1116              : 
    1117            0 :     return TlsValidator::CheckResult(CheckValues::NUMBER, convert.str());
    1118            0 : }
    1119              : 
    1120              : /**
    1121              :  * Return the certificate serial number
    1122              :  */
    1123              : TlsValidator::CheckResult
    1124            0 : TlsValidator::getSerialNumber()
    1125              : {
    1126              :     // gnutls_x509_crl_iter_crt_serial
    1127              :     // gnutls_x509_crt_get_authority_key_gn_serial
    1128            0 :     size_t resultSize = sizeof(copy_buffer);
    1129            0 :     int err = gnutls_x509_crt_get_serial(x509crt_->cert, copy_buffer, &resultSize);
    1130            0 :     return checkBinaryError(err, copy_buffer, resultSize);
    1131              : }
    1132              : 
    1133              : /**
    1134              :  * If the certificate is not self signed, return the issuer
    1135              :  */
    1136              : TlsValidator::CheckResult
    1137            0 : TlsValidator::getIssuer()
    1138              : {
    1139            0 :     if (not x509crt_->issuer) {
    1140            0 :         auto icrt = certStore_.findIssuer(x509crt_);
    1141            0 :         if (icrt)
    1142            0 :             return TlsValidator::CheckResult(CheckValues::CUSTOM, icrt->getId().toString());
    1143            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1144            0 :     }
    1145            0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, x509crt_->issuer->getId().toString());
    1146              : }
    1147              : 
    1148              : /**
    1149              :  * The algorithm used to sign the certificate details (rather than the certificate itself)
    1150              :  */
    1151              : TlsValidator::CheckResult
    1152            0 : TlsValidator::getSubjectKeyAlgorithm()
    1153              : {
    1154            0 :     unsigned key_length = 0;
    1155            0 :     gnutls_pk_algorithm_t algo = (gnutls_pk_algorithm_t) gnutls_x509_crt_get_pk_algorithm(x509crt_->cert, &key_length);
    1156              : 
    1157            0 :     if (algo < 0)
    1158            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1159              : 
    1160            0 :     const char* name = gnutls_pk_get_name(algo);
    1161              : 
    1162            0 :     if (!name)
    1163            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1164              : 
    1165            0 :     if (key_length)
    1166            0 :         return TlsValidator::CheckResult(CheckValues::CUSTOM, fmt::format("{} ({} bits)", name, key_length));
    1167              :     else
    1168            0 :         return TlsValidator::CheckResult(CheckValues::CUSTOM, name);
    1169              : }
    1170              : 
    1171              : /**
    1172              :  * The subject public key
    1173              :  */
    1174              : TlsValidator::CheckResult
    1175            0 : TlsValidator::getSubjectKey()
    1176              : {
    1177              :     try {
    1178            0 :         std::vector<uint8_t> data;
    1179            0 :         x509crt_->getPublicKey().pack(data);
    1180            0 :         return TlsValidator::CheckResult(CheckValues::CUSTOM, dht::toHex(data));
    1181            0 :     } catch (const dht::crypto::CryptoException& e) {
    1182            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, e.what());
    1183            0 :     }
    1184              : }
    1185              : 
    1186              : /**
    1187              :  * The 'CN' section of a DN (RFC4514)
    1188              :  */
    1189              : TlsValidator::CheckResult
    1190            0 : TlsValidator::getCN()
    1191              : {
    1192              :     // TODO split, cache
    1193            0 :     size_t resultSize = sizeof(copy_buffer);
    1194            0 :     int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, copy_buffer, &resultSize);
    1195            0 :     return checkError(err, copy_buffer, resultSize);
    1196              : }
    1197              : 
    1198              : /**
    1199              :  * The 'UID' section of a DN (RFC4514)
    1200              :  */
    1201              : TlsValidator::CheckResult
    1202            0 : TlsValidator::getUID()
    1203              : {
    1204            0 :     size_t resultSize = sizeof(copy_buffer);
    1205            0 :     int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert, GNUTLS_OID_LDAP_UID, 0, 0, copy_buffer, &resultSize);
    1206            0 :     return checkError(err, copy_buffer, resultSize);
    1207              : }
    1208              : 
    1209              : /**
    1210              :  * The 'N' section of a DN (RFC4514)
    1211              :  */
    1212              : TlsValidator::CheckResult
    1213            0 : TlsValidator::getN()
    1214              : {
    1215              :     // TODO split, cache
    1216            0 :     size_t resultSize = sizeof(copy_buffer);
    1217            0 :     int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert, GNUTLS_OID_X520_NAME, 0, 0, copy_buffer, &resultSize);
    1218            0 :     return checkError(err, copy_buffer, resultSize);
    1219              : }
    1220              : 
    1221              : /**
    1222              :  * The 'O' section of a DN (RFC4514)
    1223              :  */
    1224              : TlsValidator::CheckResult
    1225            0 : TlsValidator::getO()
    1226              : {
    1227              :     // TODO split, cache
    1228            0 :     size_t resultSize = sizeof(copy_buffer);
    1229            0 :     int err = gnutls_x509_crt_get_dn_by_oid(x509crt_->cert,
    1230              :                                             GNUTLS_OID_X520_ORGANIZATION_NAME,
    1231              :                                             0,
    1232              :                                             0,
    1233            0 :                                             copy_buffer,
    1234              :                                             &resultSize);
    1235            0 :     return checkError(err, copy_buffer, resultSize);
    1236              : }
    1237              : 
    1238              : /**
    1239              :  * Return the algorithm used to sign the Key
    1240              :  *
    1241              :  * For example: RSA
    1242              :  */
    1243              : TlsValidator::CheckResult
    1244            0 : TlsValidator::getSignatureAlgorithm()
    1245              : {
    1246            0 :     gnutls_sign_algorithm_t algo = (gnutls_sign_algorithm_t) gnutls_x509_crt_get_signature_algorithm(x509crt_->cert);
    1247              : 
    1248            0 :     if (algo < 0)
    1249            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1250              : 
    1251            0 :     const char* algoName = gnutls_sign_get_name(algo);
    1252            0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, algoName);
    1253              : }
    1254              : 
    1255              : /**
    1256              :  *Compute the key fingerprint
    1257              :  *
    1258              :  * This need to be used along with getSha1Fingerprint() to avoid collisions
    1259              :  */
    1260              : TlsValidator::CheckResult
    1261            0 : TlsValidator::getMd5Fingerprint()
    1262              : {
    1263            0 :     size_t resultSize = sizeof(copy_buffer);
    1264            0 :     int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert, GNUTLS_DIG_MD5, copy_buffer, &resultSize);
    1265            0 :     return checkBinaryError(err, copy_buffer, resultSize);
    1266              : }
    1267              : 
    1268              : /**
    1269              :  * Compute the key fingerprint
    1270              :  *
    1271              :  * This need to be used along with getMd5Fingerprint() to avoid collisions
    1272              :  */
    1273              : TlsValidator::CheckResult
    1274            0 : TlsValidator::getSha1Fingerprint()
    1275              : {
    1276            0 :     size_t resultSize = sizeof(copy_buffer);
    1277            0 :     int err = gnutls_x509_crt_get_fingerprint(x509crt_->cert, GNUTLS_DIG_SHA1, copy_buffer, &resultSize);
    1278            0 :     return checkBinaryError(err, copy_buffer, resultSize);
    1279              : }
    1280              : 
    1281              : /**
    1282              :  * Return an hexadecimal identifier
    1283              :  */
    1284              : TlsValidator::CheckResult
    1285            0 : TlsValidator::getPublicKeyId()
    1286              : {
    1287              :     static unsigned char unsigned_copy_buffer[4096];
    1288            0 :     size_t resultSize = sizeof(unsigned_copy_buffer);
    1289            0 :     int err = gnutls_x509_crt_get_key_id(x509crt_->cert, 0, unsigned_copy_buffer, &resultSize);
    1290              : 
    1291              :     // TODO check for GNUTLS_E_SHORT_MEMORY_BUFFER and increase the buffer size
    1292              :     // TODO get rid of the cast, display a HEX or something, need research
    1293              : 
    1294            0 :     return checkBinaryError(err, (char*) unsigned_copy_buffer, resultSize);
    1295              : }
    1296              : // gnutls_x509_crt_get_authority_key_id
    1297              : 
    1298              : /**
    1299              :  *  If the certificate is not self signed, return the issuer DN (RFC4514)
    1300              :  */
    1301              : TlsValidator::CheckResult
    1302            0 : TlsValidator::getIssuerDN()
    1303              : {
    1304            0 :     size_t resultSize = sizeof(copy_buffer);
    1305            0 :     int err = gnutls_x509_crt_get_issuer_dn(x509crt_->cert, copy_buffer, &resultSize);
    1306            0 :     return checkError(err, copy_buffer, resultSize);
    1307              : }
    1308              : 
    1309              : /**
    1310              :  *  If the certificate is not self signed, return the issuer CN
    1311              :  */
    1312              : TlsValidator::CheckResult
    1313            0 : TlsValidator::getIssuerCN()
    1314              : {
    1315            0 :     size_t resultSize = sizeof(copy_buffer);
    1316            0 :     int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
    1317              :                                                    GNUTLS_OID_X520_COMMON_NAME,
    1318              :                                                    0,
    1319              :                                                    0,
    1320            0 :                                                    copy_buffer,
    1321              :                                                    &resultSize);
    1322            0 :     return checkError(err, copy_buffer, resultSize);
    1323              : }
    1324              : 
    1325              : /**
    1326              :  *  If the certificate is not self signed, return the issuer UID
    1327              :  */
    1328              : TlsValidator::CheckResult
    1329            0 : TlsValidator::getIssuerUID()
    1330              : {
    1331            0 :     size_t resultSize = sizeof(copy_buffer);
    1332            0 :     int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert, GNUTLS_OID_LDAP_UID, 0, 0, copy_buffer, &resultSize);
    1333            0 :     return checkError(err, copy_buffer, resultSize);
    1334              : }
    1335              : 
    1336              : /**
    1337              :  *  If the certificate is not self signed, return the issuer N
    1338              :  */
    1339              : TlsValidator::CheckResult
    1340            0 : TlsValidator::getIssuerN()
    1341              : {
    1342            0 :     size_t resultSize = sizeof(copy_buffer);
    1343            0 :     int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert, GNUTLS_OID_X520_NAME, 0, 0, copy_buffer, &resultSize);
    1344            0 :     return checkError(err, copy_buffer, resultSize);
    1345              : }
    1346              : 
    1347              : /**
    1348              :  *  If the certificate is not self signed, return the issuer O
    1349              :  */
    1350              : TlsValidator::CheckResult
    1351            0 : TlsValidator::getIssuerO()
    1352              : {
    1353            0 :     size_t resultSize = sizeof(copy_buffer);
    1354            0 :     int err = gnutls_x509_crt_get_issuer_dn_by_oid(x509crt_->cert,
    1355              :                                                    GNUTLS_OID_X520_ORGANIZATION_NAME,
    1356              :                                                    0,
    1357              :                                                    0,
    1358            0 :                                                    copy_buffer,
    1359              :                                                    &resultSize);
    1360            0 :     return checkError(err, copy_buffer, resultSize);
    1361              : }
    1362              : 
    1363              : /**
    1364              :  * Get the expiration date
    1365              :  *
    1366              :  * @todo Move to "certificateDetails()" method once completed
    1367              :  */
    1368              : TlsValidator::CheckResult
    1369            0 : TlsValidator::getExpirationDate()
    1370              : {
    1371            0 :     if (not certificateFound_)
    1372            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1373              : 
    1374            0 :     time_t expiration = gnutls_x509_crt_get_expiration_time(x509crt_->cert);
    1375              : 
    1376            0 :     return formatDate(expiration);
    1377              : }
    1378              : 
    1379              : /**
    1380              :  * Get the activation date
    1381              :  *
    1382              :  * @todo Move to "certificateDetails()" method once completed
    1383              :  */
    1384              : TlsValidator::CheckResult
    1385            0 : TlsValidator::getActivationDate()
    1386              : {
    1387            0 :     if (not certificateFound_)
    1388            0 :         return TlsValidator::CheckResult(CheckValues::UNSUPPORTED, "");
    1389              : 
    1390            0 :     time_t expiration = gnutls_x509_crt_get_activation_time(x509crt_->cert);
    1391              : 
    1392            0 :     return formatDate(expiration);
    1393              : }
    1394              : 
    1395              : /**
    1396              :  * The expected outgoing server domain
    1397              :  *
    1398              :  * @todo Move to "certificateDetails()" method once completed
    1399              :  * @todo extract information for the certificate
    1400              :  */
    1401              : TlsValidator::CheckResult
    1402            0 : TlsValidator::outgoingServer()
    1403              : {
    1404              :     // TODO
    1405            0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, "");
    1406              : }
    1407              : 
    1408              : /**
    1409              :  * If the certificate is not self signed, return the issuer
    1410              :  */
    1411              : TlsValidator::CheckResult
    1412            0 : TlsValidator::isCA()
    1413              : {
    1414            0 :     return TlsValidator::CheckResult(CheckValues::CUSTOM, x509crt_->isCA() ? TRUE_STR : FALSE_STR);
    1415              : }
    1416              : 
    1417              : } // namespace tls
    1418              : } // namespace jami
        

Generated by: LCOV version 2.0-1