Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cmake-linux-fedora.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
container: fedora:${{ matrix.container }}
strategy:
matrix:
container: [42, 43]
container: [42, 43, 44]

steps:
- name: Install Deps
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/cmake-linux-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@ jobs:
container: ubuntu:${{matrix.container}}
strategy:
matrix:
container: ['22.04', '24.04', '25.04', '25.10']
container: ['22.04', '24.04', '25.10', '26.04']
arch: ['amd64', 'arm64']

steps:
- name: Install dependencies
run: apt update -qq && apt install --no-install-recommends -y git lsb-release fakeroot build-essential devscripts debhelper lintian pkg-config cmake libpcsclite-dev libssl-dev libgtest-dev libgl-dev libqt6svg6-dev qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools
run: |
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
[ -f "$f" ] && sed -i 's|http://archive.ubuntu.com|http://azure.archive.ubuntu.com|g; s|http://security.ubuntu.com|http://azure.archive.ubuntu.com|g' "$f"
done
echo 'path-exclude=/usr/share/man/*' > /etc/dpkg/dpkg.cfg.d/99-nodocs
echo 'path-exclude=/usr/share/doc/*' >> /etc/dpkg/dpkg.cfg.d/99-nodocs
echo 'path-exclude=/usr/share/doc-base/*' >> /etc/dpkg/dpkg.cfg.d/99-nodocs
apt update -qq && apt install --no-install-recommends -y git lsb-release fakeroot build-essential devscripts debhelper lintian pkg-config cmake libpcsclite-dev libssl-dev libgtest-dev libgl-dev libqt6svg6-dev qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools

- uses: actions/checkout@v6
with:
Expand Down
1 change: 1 addition & 0 deletions src/controller/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ add_library(controller STATIC
utils/erasedata.hpp
utils/observer_ptr.hpp
utils/qdisablecopymove.hpp
utils/qt_comp.hpp
utils/utils.hpp
writeresponse.cpp
writeresponse.hpp
Expand Down
79 changes: 40 additions & 39 deletions src/controller/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "certandpininfo.hpp"
#include "logging.hpp"
#include "retriableerror.hpp"
#include "utils/qt_comp.hpp"

#include <QCommandLineParser>
#include <QDir>
Expand All @@ -36,6 +37,11 @@
#include <QStyleHints>
#include <QTranslator>

#include <algorithm>
#include <array>

using namespace Qt::Literals::StringLiterals;

inline CommandWithArguments::second_type parseArgumentJson(const QString& argumentStr)
{
const auto argumentJson = QJsonDocument::fromJson(argumentStr.toUtf8());
Expand All @@ -51,26 +57,26 @@ Application::Application(int& argc, char** argv, const QString& name) :
QApplication(argc, argv), translator(new QTranslator(this))
{
setApplicationName(name);
setApplicationDisplayName(QStringLiteral("Web eID"));
setApplicationDisplayName(u"Web eID"_s);
setApplicationVersion(QStringLiteral(PROJECT_VERSION));
setOrganizationDomain(QStringLiteral("web-eid.eu"));
setOrganizationName(QStringLiteral("RIA"));
setOrganizationDomain(u"web-eid.eu"_s);
setOrganizationName(u"RIA"_s);
setQuitOnLastWindowClosed(false);

installTranslator(translator);
loadTranslations();

auto list = QUrl::idnWhitelist();
list.append({
QStringLiteral("fi"),
QStringLiteral("ee"),
QStringLiteral("lt"),
QStringLiteral("lv"),
u"fi"_s,
u"ee"_s,
u"lt"_s,
u"lv"_s,
});
QUrl::setIdnWhitelist(list);

for (const QString& font : QDir(QStringLiteral(":/fonts")).entryList()) {
QFontDatabase::addApplicationFont(QStringLiteral(":/fonts/%1").arg(font));
for (const QString& font : QDir(u":/fonts"_s).entryList()) {
QFontDatabase::addApplicationFont(u":/fonts/%1"_s.arg(font));
}

registerMetatypes();
Expand All @@ -93,8 +99,7 @@ bool Application::isDarkTheme()
// supported OS-s.
static const bool isDarkTheme = [] {
QProcess p;
p.start(QStringLiteral("gsettings"),
{"get", "org.gnome.desktop.interface", "color-scheme"});
p.start(u"gsettings"_s, {u"get"_s, u"org.gnome.desktop.interface"_s, u"color-scheme"_s});
if (p.waitForFinished()) {
return p.readAllStandardOutput().contains("dark");
}
Expand All @@ -108,56 +113,52 @@ bool Application::isDarkTheme()

void Application::loadTranslations(const QString& lang)
{
static const QStringList SUPPORTED_LANGS {
QStringLiteral("en"), QStringLiteral("et"), QStringLiteral("fi"), QStringLiteral("hr"),
QStringLiteral("ru"), QStringLiteral("de"), QStringLiteral("fr"), QStringLiteral("nl"),
QStringLiteral("cs"), QStringLiteral("sk")};
static constexpr auto SUPPORTED_LANGS = std::to_array<QStringView>(
{u"en", u"et", u"fi", u"hr", u"ru", u"de", u"fr", u"nl", u"cs", u"sk"});
QLocale locale;
QString langSetting = QSettings().value(QStringLiteral("lang"), lang).toString();
if (SUPPORTED_LANGS.contains(langSetting)) {
QString langSetting = QSettings().value(u"lang"_s, lang).toString();
if (std::ranges::find(SUPPORTED_LANGS, langSetting) != SUPPORTED_LANGS.cend()) {
locale = QLocale(langSetting);
}
void(translator->load(locale, QStringLiteral(":/translations/")));
void(translator->load(locale, u":/translations/"_s));
}

CommandWithArgumentsPtr Application::parseArgs()
{
// On Windows Chrome, the native messaging host is also passed a command line argument with a
// handle to the calling Chrome native window: --parent-window=<decimal handle value>.
// We don't use it, but need to support it to avoid unknown option errors.
QCommandLineOption parentWindow(QStringLiteral("parent-window"),
QStringLiteral("Parent window handle (unused)"),
QStringLiteral("parent-window"));
QCommandLineOption parentWindow(u"parent-window"_s, u"Parent window handle (unused)"_s,
u"parent-window"_s);

QCommandLineOption aboutArgument(QStringLiteral("about"),
QStringLiteral("Show Web-eID about window"));
QCommandLineOption aboutArgument(u"about"_s, u"Show Web-eID about window"_s);
QCommandLineOption commandLineMode(
{u"c"_s, u"command-line-mode"_s},
u"Command-line mode, read commands from command line arguments instead of "
"standard input."_s);

QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral(
"Application that communicates with the Web eID browser extension via standard input and "
parser.setApplicationDescription(
u"Application that communicates with the Web eID browser extension via standard input and "
"output, but also works standalone in command-line mode. Performs PKI cryptographic "
"operations with eID smart cards for signing and authentication purposes."));
"operations with eID smart cards for signing and authentication purposes."_s);

parser.addHelpOption();
parser.addOptions({{{"c", "command-line-mode"},
"Command-line mode, read commands from command line arguments instead of "
"standard input."},
aboutArgument,
parentWindow});
parser.addVersionOption();
parser.addOptions({commandLineMode, aboutArgument, parentWindow});

static const auto COMMANDS = "'" + CMDLINE_GET_SIGNING_CERTIFICATE + "', '"
+ CMDLINE_AUTHENTICATE + "', '" + CMDLINE_SIGN + "'.";
static const auto COMMANDS = u"'%1', '%2', '%3'."_s.arg(CMDLINE_GET_SIGNING_CERTIFICATE,
CMDLINE_AUTHENTICATE, CMDLINE_SIGN);

parser.addPositionalArgument(
QStringLiteral("command"),
QStringLiteral("The command to execute in command-line mode, any of ") + COMMANDS);
parser.addPositionalArgument(
QStringLiteral("arguments"),
QStringLiteral("Arguments to the given command as a JSON-encoded string."));
u"command"_s, u"The command to execute in command-line mode, any of "_s + COMMANDS,
u"(%1|%2|%3)"_s.arg(CMDLINE_GET_SIGNING_CERTIFICATE, CMDLINE_AUTHENTICATE, CMDLINE_SIGN));
parser.addPositionalArgument(u"arguments"_s,
u"Arguments to the given command as a JSON-encoded string."_s);

parser.process(arguments());

if (parser.isSet(QStringLiteral("command-line-mode"))) {
if (parser.isSet(commandLineMode)) {
const auto args = parser.positionalArguments();
if (args.size() != 2) {
throw ArgumentError("Provide two positional arguments in command-line mode.");
Expand Down
10 changes: 4 additions & 6 deletions src/controller/command-handlers/authenticate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ namespace
// Use common base64-encoding defaults.
constexpr auto BASE64_OPTIONS = QByteArray::Base64Encoding | QByteArray::KeepTrailingEquals;

QVariantMap createAuthenticationToken(const QString& signatureAlgorithm,
QVariantMap createAuthenticationToken(std::string_view signatureAlgorithm,
const QByteArray& certificateDer, const QByteArray& signature)
{
return QVariantMap {
{"unverifiedCertificate", QString(certificateDer.toBase64(BASE64_OPTIONS))},
{"algorithm", signatureAlgorithm},
{"algorithm", QLatin1String(signatureAlgorithm.data(), signatureAlgorithm.size())},
{"signature", QString(signature)},
{"format", QStringLiteral("web-eid:1.0")},
{"appVersion",
Expand Down Expand Up @@ -123,8 +123,6 @@ QVariantMap Authenticate::onConfirm(WebEidUI* window,
const EidCertificateAndPinInfo& certAndPinInfo)
{
try {
const auto signatureAlgorithm =
QString::fromStdString(certAndPinInfo.eid->authSignatureAlgorithm());
pcsc_cpp::byte_vector pin;
// Reserve space for APDU overhead (5 bytes) + PIN padding (16 bytes) to prevent PIN memory
// reallocation. The 16-byte limit comes from the max PIN length of 12 bytes across all card
Expand All @@ -133,8 +131,8 @@ QVariantMap Authenticate::onConfirm(WebEidUI* window,
getPin(pin, *certAndPinInfo.eid, window);
const auto signature =
createSignature(origin.url(), challengeNonce, *certAndPinInfo.eid, std::move(pin));
return createAuthenticationToken(signatureAlgorithm, certAndPinInfo.certificateBytesInDer,
signature);
return createAuthenticationToken(certAndPinInfo.eid->authSignatureAlgorithm(),
certAndPinInfo.certificateBytesInDer, signature);

} catch (const VerifyPinFailed& failure) {
switch (failure.status()) {
Expand Down
7 changes: 2 additions & 5 deletions src/controller/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,12 @@
#include <stdexcept>
#include <map>

const QString CMDLINE_GET_SIGNING_CERTIFICATE = QStringLiteral("get-signing-certificate");
const QString CMDLINE_AUTHENTICATE = QStringLiteral("authenticate");
const QString CMDLINE_SIGN = QStringLiteral("sign");
// A special command for stdin mode for quitting the application after sending the version.
const QString STDINMODE_QUIT = QStringLiteral("quit");
constexpr QStringView STDINMODE_QUIT {u"quit"};

CommandType::CommandType(const QString& cmdName)
{
static const std::map<QString, CommandType> SUPPORTED_COMMANDS {
static const std::map<QStringView, CommandType> SUPPORTED_COMMANDS {
{CMDLINE_GET_SIGNING_CERTIFICATE, CommandType::GET_SIGNING_CERTIFICATE},
{CMDLINE_AUTHENTICATE, CommandType::AUTHENTICATE},
{CMDLINE_SIGN, CommandType::SIGN},
Expand Down
6 changes: 3 additions & 3 deletions src/controller/commands.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ class CommandType
CommandTypeEnum value;
};

extern const QString CMDLINE_GET_SIGNING_CERTIFICATE;
extern const QString CMDLINE_AUTHENTICATE;
extern const QString CMDLINE_SIGN;
constexpr QStringView CMDLINE_GET_SIGNING_CERTIFICATE {u"get-signing-certificate"};
constexpr QStringView CMDLINE_AUTHENTICATE {u"authenticate"};
constexpr QStringView CMDLINE_SIGN {u"sign"};

using CommandWithArguments = std::pair<CommandType, QVariantMap>;
using CommandWithArgumentsPtr = std::unique_ptr<CommandWithArguments>;
2 changes: 2 additions & 0 deletions src/controller/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#include "logging.hpp"

#include "utils/qt_comp.hpp"

#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
Expand Down
8 changes: 0 additions & 8 deletions src/controller/logging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@

void setupLogging();

#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
template <typename... Args>
inline QDebug operator<<(QDebug out, const std::basic_string<char, Args...>& s)
{
return out << QUtf8StringView(s);
}
#endif

inline QDebug operator<<(QDebug out, const std::exception& e)
{
out << e.what();
Expand Down
1 change: 1 addition & 0 deletions src/controller/threads/controllerchildthread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "commandhandler.hpp"
#include "logging.hpp"
#include "retriableerror.hpp"
#include "utils/qt_comp.hpp"

#include <QCoreApplication>
#include <QMutexLocker>
Expand Down
31 changes: 31 additions & 0 deletions src/controller/utils/qt_comp.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Estonian Information System Authority
// SPDX-License-Identifier: MIT

#pragma once

#include <QString>

#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
namespace Qt::Literals::StringLiterals
{

constexpr inline QLatin1String operator"" _L1(const char* str, size_t size) noexcept
{
return QLatin1String(str, qsizetype(size));
}

inline QString operator""_s(const char16_t* str, size_t size) noexcept
{
return QString(QStringPrivate(nullptr, const_cast<char16_t*>(str), qsizetype(size)));
}

} // namespace Qt::Literals::StringLiterals
#endif

#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
template <typename... Args>
inline QDebug operator<<(QDebug out, const std::basic_string<char, Args...>& s)
{
return out << QUtf8StringView(s);
}
#endif
13 changes: 1 addition & 12 deletions src/ui/webeiddialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "webeiddialog.hpp"
#include "application.hpp"
#include "languageselect.hpp"
#include "utils/qt_comp.hpp"

#include "ui_dialog.h"

Expand All @@ -43,19 +44,7 @@
#include <unistd.h>
#endif

#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
constexpr inline QLatin1String operator"" _L1(const char* str, size_t size) noexcept
{
return QLatin1String(str, int(size));
}

inline QString operator""_s(const char16_t* str, size_t size) noexcept
{
return QString(QStringPrivate(nullptr, const_cast<char16_t*>(str), qsizetype(size)));
}
#else
using namespace Qt::Literals::StringLiterals;
#endif

using namespace electronic_id;

Expand Down
Loading