<commit_before>/*
 * Chrome Token Signing Native Host
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#pragma once

#include "pkcs11.h"
#include "Logger.h"

#include <openssl/x509.h>

#include <cstring>
#include <dlfcn.h>
#include <stdexcept>
#include <vector>

#define BINARY_SHA1_LENGTH 20
#define BINARY_SHA224_LENGTH 28
#define BINARY_SHA256_LENGTH 32
#define BINARY_SHA384_LENGTH 48
#define BINARY_SHA512_LENGTH 64

#ifndef PKCS11_MODULE
#ifdef _WIN32
#define PKCS11_MODULE "opensc-pkcs11.dll"
#elif defined(__APPLE__)
#define PKCS11_MODULE "/Library/EstonianIDCard/lib/esteid-pkcs11.so"
#else
#define PKCS11_MODULE "opensc-pkcs11.so"
#endif
#endif

#define C(API, ...) Call(__FILE__, __LINE__, "C_"#API, fl->C_##API, __VA_ARGS__)

class UserCanceledError : public std::runtime_error {
    public:
     UserCanceledError() : std::runtime_error("User canceled"){}
};

class AuthenticationError : public std::runtime_error {
public:
    AuthenticationError() : std::runtime_error("Authentication error"){}
};

class AuthenticationBadInput : public std::runtime_error {
public:
    AuthenticationBadInput() : std::runtime_error("Authentication Bad Input"){}
};

class PKCS11CardManager {
private:
    void *library = nullptr;
    CK_FUNCTION_LIST_PTR fl = nullptr;
    CK_TOKEN_INFO tokenInfo;
    CK_SESSION_HANDLE session = 0;
    X509 *cert = nullptr;
    std::vector<unsigned char> signCert;

    template <typename Func, typename... Args>
    void Call(const char *file, int line, const char *function, Func func, Args... args) const
    {
        CK_RV rv = func(args...);
        Logger::writeLog(function, file, line, "return value %u", rv);
        switch (rv) {
            case CKR_OK:
                break;
            case CKR_FUNCTION_CANCELED:
                throw UserCanceledError();
            case CKR_PIN_INCORRECT:
                throw AuthenticationError();
            case CKR_PIN_LEN_RANGE:
                throw AuthenticationBadInput();
            default:
                throw std::runtime_error("PKCS11 method failed.");
        }
    }

    std::vector<CK_OBJECT_HANDLE> findObject(CK_OBJECT_CLASS objectClass, CK_ULONG max = 1) const {
        if (!fl) {
            throw std::runtime_error("PKCS11 is not loaded");
        }
        CK_ATTRIBUTE searchAttribute = {CKA_CLASS, &objectClass, sizeof(objectClass)};
        C(FindObjectsInit, session, &searchAttribute, 1);
        CK_ULONG objectCount = max;
        std::vector<CK_OBJECT_HANDLE> objectHandle(objectCount);
        C(FindObjects, session, &objectHandle[0], objectHandle.size(), &objectCount);
        C(FindObjectsFinal, session);
        objectHandle.resize(objectCount);
        return objectHandle;
    }

    std::string getFromX509Name(const std::string &subjectField) const {
        if (!cert) {
            throw std::runtime_error("Could not parse cert");
        }
        X509_NAME *name = X509_get_subject_name(cert);
        std::string X509Value(1024, 0);
        int length = X509_NAME_get_text_by_NID(name, OBJ_txt2nid(subjectField.c_str()), &X509Value[0], int(X509Value.size()));
        X509Value.resize(std::max(0, length));
        _log("%s length is %i, %s", subjectField.c_str(), length, X509Value.c_str());
        return X509Value;
    }

    PKCS11CardManager(CK_SLOT_ID slotID, CK_FUNCTION_LIST_PTR fl)
    : fl(fl)
    {
        C(GetTokenInfo, slotID, &tokenInfo);
        C(OpenSession, slotID, CKF_SERIAL_SESSION, nullptr, nullptr, &session);
        std::vector<CK_OBJECT_HANDLE> objectHandle = findObject(CKO_CERTIFICATE);
        if (objectHandle.empty()) {
            throw std::runtime_error("Could not read cert");
        }

        CK_ATTRIBUTE attribute = {CKA_VALUE, nullptr, 0};
        C(GetAttributeValue, session, objectHandle[0], &attribute, 1);
        signCert.resize(attribute.ulValueLen, 0);
        attribute.pValue = &signCert[0];
        C(GetAttributeValue, session, objectHandle[0], &attribute, 1);

        const unsigned char *p = signCert.data();
        cert = d2i_X509(NULL, &p, signCert.size());
    }

    PKCS11CardManager(const std::string &module) {
        library = dlopen(module.c_str(), RTLD_LOCAL | RTLD_NOW);
        if (!library) {
            throw std::runtime_error("PKCS11 is not loaded");
        }

        CK_C_GetFunctionList C_GetFunctionList = (CK_C_GetFunctionList) dlsym(library, "C_GetFunctionList");
        if (dlerror()) {
            dlclose(library);
            library = nullptr;
            throw std::runtime_error("PKCS11 is not loaded");
        }
        Call(__FILE__, __LINE__, "C_GetFunctionList", C_GetFunctionList, &fl);
        C(Initialize, nullptr);
    }
    
public:

    static PKCS11CardManager* instance(const std::string &module = PKCS11_MODULE) {
        static PKCS11CardManager instance(module);
        return &instance;
    }

    ~PKCS11CardManager() {
        if (session)
            C(CloseSession, session);
        if (cert)
            X509_free(cert);
        if (!library)
            return;
        C(Finalize, nullptr);
        dlclose(library);
    }

    std::vector<CK_SLOT_ID> getAvailableTokens() const {
        if (!fl) {
            throw std::runtime_error("PKCS11 is not loaded");
        }
        CK_ULONG slotCount = 0;
        C(GetSlotList, CK_TRUE, nullptr, &slotCount);
        _log("slotCount = %i", slotCount);
        std::vector<CK_SLOT_ID> slotIDs(slotCount, 0);
        C(GetSlotList, CK_TRUE, &slotIDs[0], &slotCount);

        std::vector<CK_SLOT_ID> signingSlotIDs;
        for (CK_ULONG i = 0; i < slotCount; ++i) {
            _log("slotID: %i", slotIDs[i]);
            if (i & 1) {
                _log("Found signing slotID: %i", slotIDs[i]);
                signingSlotIDs.push_back(slotIDs[i]);
            }
        }
        return signingSlotIDs;
    }

    PKCS11CardManager *getManagerForReader(CK_SLOT_ID slotId) {
        if (!fl) {
            throw std::runtime_error("PKCS11 is not loaded");
        }<commit_msg>Resolve dll symbols in windows<commit_after>/*
 * Chrome Token Signing Native Host
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#pragma once

#include "pkcs11.h"
#include "Logger.h"

#include <openssl/x509.h>

#include <algorithm>
#include <cstring>
#include <stdexcept>
#include <vector>

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#else
#include <dlfcn.h>
#endif

#define BINARY_SHA1_LENGTH 20
#define BINARY_SHA224_LENGTH 28
#define BINARY_SHA256_LENGTH 32
#define BINARY_SHA384_LENGTH 48
#define BINARY_SHA512_LENGTH 64

#ifndef PKCS11_MODULE
#ifdef _WIN32
#define PKCS11_MODULE "opensc-pkcs11.dll"
#elif defined(__APPLE__)
#define PKCS11_MODULE "/Library/EstonianIDCard/lib/esteid-pkcs11.so"
#else
#define PKCS11_MODULE "opensc-pkcs11.so"
#endif
#endif

#define C(API, ...) Call(__FILE__, __LINE__, "C_"#API, fl->C_##API, __VA_ARGS__)

class UserCanceledError : public std::runtime_error {
    public:
     UserCanceledError() : std::runtime_error("User canceled"){}
};

class AuthenticationError : public std::runtime_error {
public:
    AuthenticationError() : std::runtime_error("Authentication error"){}
};

class AuthenticationBadInput : public std::runtime_error {
public:
    AuthenticationBadInput() : std::runtime_error("Authentication Bad Input"){}
};

class PKCS11CardManager {
private:
#ifdef _WIN32
    HINSTANCE library = 0;
#else
    void *library = nullptr;
#endif
    CK_FUNCTION_LIST_PTR fl = nullptr;
    CK_TOKEN_INFO tokenInfo;
    CK_SESSION_HANDLE session = 0;
    X509 *cert = nullptr;
    std::vector<unsigned char> signCert;

    template <typename Func, typename... Args>
    void Call(const char *file, int line, const char *function, Func func, Args... args) const
    {
        CK_RV rv = func(args...);
        Logger::writeLog(function, file, line, "return value %u", rv);
        switch (rv) {
            case CKR_OK:
                break;
            case CKR_FUNCTION_CANCELED:
                throw UserCanceledError();
            case CKR_PIN_INCORRECT:
                throw AuthenticationError();
            case CKR_PIN_LEN_RANGE:
                throw AuthenticationBadInput();
            default:
                throw std::runtime_error("PKCS11 method failed.");
        }
    }

    std::vector<CK_OBJECT_HANDLE> findObject(CK_OBJECT_CLASS objectClass, CK_ULONG max = 1) const {
        if (!fl) {
            throw std::runtime_error("PKCS11 is not loaded");
        }
        CK_ATTRIBUTE searchAttribute = {CKA_CLASS, &objectClass, sizeof(objectClass)};
        C(FindObjectsInit, session, &searchAttribute, 1);
        CK_ULONG objectCount = max;
        std::vector<CK_OBJECT_HANDLE> objectHandle(objectCount);
        C(FindObjects, session, &objectHandle[0], objectHandle.size(), &objectCount);
        C(FindObjectsFinal, session);
        objectHandle.resize(objectCount);
        return objectHandle;
    }

    std::string getFromX509Name(const std::string &subjectField) const {
        if (!cert) {
            throw std::runtime_error("Could not parse cert");
        }
        X509_NAME *name = X509_get_subject_name(cert);
        std::string X509Value(1024, 0);
        int length = X509_NAME_get_text_by_NID(name, OBJ_txt2nid(subjectField.c_str()), &X509Value[0], int(X509Value.size()));
        X509Value.resize(std::max(0, length));
        _log("%s length is %i, %s", subjectField.c_str(), length, X509Value.c_str());
        return X509Value;
    }

    PKCS11CardManager(CK_SLOT_ID slotID, CK_FUNCTION_LIST_PTR fl)
    : fl(fl)
    {
        C(GetTokenInfo, slotID, &tokenInfo);
        C(OpenSession, slotID, CKF_SERIAL_SESSION, nullptr, nullptr, &session);
        std::vector<CK_OBJECT_HANDLE> objectHandle = findObject(CKO_CERTIFICATE);
        if (objectHandle.empty()) {
            throw std::runtime_error("Could not read cert");
        }

        CK_ATTRIBUTE attribute = {CKA_VALUE, nullptr, 0};
        C(GetAttributeValue, session, objectHandle[0], &attribute, 1);
        signCert.resize(attribute.ulValueLen, 0);
        attribute.pValue = &signCert[0];
        C(GetAttributeValue, session, objectHandle[0], &attribute, 1);

        const unsigned char *p = signCert.data();
        cert = d2i_X509(NULL, &p, signCert.size());
    }

    PKCS11CardManager(const std::string &module) {
        CK_C_GetFunctionList C_GetFunctionList = nullptr;
#ifdef _WIN32
        library = LoadLibraryA(module.c_str());
        if (library)
            C_GetFunctionList = (CK_C_GetFunctionList) GetProcAddress(library, "C_GetFunctionList");
#else
        library = dlopen(module.c_str(), RTLD_LOCAL | RTLD_NOW);
        if (library)
            C_GetFunctionList = (CK_C_GetFunctionList) dlsym(library, "C_GetFunctionList");
#endif

        if (!C_GetFunctionList) {
            throw std::runtime_error("PKCS11 is not loaded");
        }
        Call(__FILE__, __LINE__, "C_GetFunctionList", C_GetFunctionList, &fl);
        C(Initialize, nullptr);
    }
    
public:

    static PKCS11CardManager* instance(const std::string &module = PKCS11_MODULE) {
        static PKCS11CardManager instance(module);
        return &instance;
    }

    ~PKCS11CardManager() {
        if (session)
            C(CloseSession, session);
        if (cert)
            X509_free(cert);
        if (!library)
            return;
        C(Finalize, nullptr);
#ifdef _WIN32
        FreeLibrary(library);
#else
        dlclose(library);
#endif
    }

    std::vector<CK_SLOT_ID> getAvailableTokens() const {
        if (!fl) {
            throw std::runtime_error("PKCS11 is not loaded");
        }
        CK_ULONG slotCount = 0;
        C(GetSlotList, CK_TRUE, nullptr, &slotCount);
        _log("slotCount = %i", slotCount);
        std::vector<CK_SLOT_ID> slotIDs(slotCount, 0);
        C(GetSlotList, CK_TRUE, &slotIDs[0], &slotCount);

        std::vector<CK_SLOT_ID> signingSlotIDs;
        for (CK_ULONG i = 0; i < slotCount; ++i) {
            _log("slotID: %i", slotIDs[i]);
            if (i & 1) {
                _log("Found signing slotID: %i", slotIDs[i]);
                signingSlotIDs.push_back(slotIDs[i]);
            }
        }
        return signingSlotIDs;
    }

    PKCS11CardManager *getManagerForReader(CK_SLOT_ID slotId) {
        if (!fl) {
            throw std::runtime_error("PKCS11 is not loaded");
        }
{'fraction_non_alphanumeric': 0.10950924712392603, 'fraction_numerical': 0.022353283821173728, 'mean_word_length': 3.0125620800467425, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3844110', 'n_tokens_mistral': 4476, 'n_tokens_neox': 4208, 'n_words': 1078}